From 4414c91f1e8d44128af51a1d414f39e209af4349 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 23 Jan 2014 18:05:04 -0800 Subject: [PATCH 001/153] Working on streaming edits. --- interface/src/MetavoxelSystem.cpp | 13 +++++++------ interface/src/MetavoxelSystem.h | 2 +- interface/src/ui/MetavoxelEditor.cpp | 2 +- libraries/metavoxels/src/Bitstream.cpp | 18 ++++++++++++++++++ libraries/metavoxels/src/Bitstream.h | 4 ++++ libraries/metavoxels/src/MetavoxelData.cpp | 3 +++ libraries/metavoxels/src/MetavoxelData.h | 3 +++ libraries/metavoxels/src/MetavoxelMessages.h | 13 +++++++++++++ 8 files changed, 50 insertions(+), 8 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 258db6da00..cf0c29ff15 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -48,13 +48,12 @@ void MetavoxelSystem::processData(const QByteArray& data, const HifiSockAddr& se void MetavoxelSystem::simulate(float deltaTime) { // simulate the clients - foreach (MetavoxelClient* client, _clients) { - client->simulate(deltaTime); - } - _points.clear(); _data.guide(_pointVisitor); - + foreach (MetavoxelClient* client, _clients) { + client->simulate(deltaTime, _pointVisitor); + } + _buffer.bind(); int bytes = _points.size() * sizeof(Point); if (_buffer.size() < bytes) { @@ -188,11 +187,13 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : _receiveRecords.append(record); } -void MetavoxelClient::simulate(float deltaTime) { +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) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 708b4d0839..7692f0d30d 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -90,7 +90,7 @@ public: const QUuid& getSessionID() const { return _sessionID; } - void simulate(float deltaTime); + void simulate(float deltaTime, MetavoxelVisitor& visitor); void receivedData(const QByteArray& data, const HifiSockAddr& sender); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index d3e4494ac9..71fcffdd08 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -375,7 +375,7 @@ bool Applier::visit(MetavoxelInfo& info) { return false; // entirely contained } if (info.size <= _granularity) { - if (volume > 0.5f) { + if (volume >= 0.5f) { info.outputValues[0] = _value; } return false; // reached granularity limit; take best guess diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index e1ab79c974..77273fea9c 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -237,6 +237,24 @@ Bitstream& Bitstream::operator>>(QVariant& value) { return *this; } +Bitstream& Bitstream::operator<<(const AttributeValue& value) { + _attributeStreamer << value.getAttribute(); + if (value.getAttribute()) { + value.getAttribute()->write(*this, value.getValue(), true); + } + return *this; +} + +Bitstream& Bitstream::operator>>(AttributeValue& value) { + AttributePointer attribute; + _attributeStreamer >> attribute; + if (attribute) { + void* value; + attribute->read(*this, value, true); + } + 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..c8793f9673 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -23,6 +23,7 @@ struct QMetaObject; class QObject; class Attribute; +class AttributeValue; class Bitstream; class TypeStreamer; @@ -231,6 +232,9 @@ public: Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); + Bitstream& operator<<(const AttributeValue& value); + Bitstream& operator>>(AttributeValue& value); + template Bitstream& operator<<(const QList& list); template Bitstream& operator>>(QList& list); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index ffbe5b4ff8..a88c5cd922 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -334,6 +334,9 @@ MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, cons _outputs(outputs) { } +MetavoxelVisitor::~MetavoxelVisitor() { +} + PolymorphicData* DefaultMetavoxelGuide::clone() const { return new DefaultMetavoxelGuide(); } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index a25e0bc9a8..8628958052 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -124,6 +124,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 +143,8 @@ protected: QVector _outputs; }; +typedef QSharedPointer MetavoxelVisitorPointer; + /// Interface for objects that guide metavoxel visitors. class MetavoxelGuide : public PolymorphicData { public: diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 3951e16d22..e3550acaa5 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -29,4 +29,17 @@ class MetavoxelDeltaMessage { DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaMessage) +/// A simple streamable edit. +class MetavoxelEdit { + STREAMABLE + +public: + + glm::vec3 minimum; + glm::vec3 maximum; + float granularity; +}; + +DECLARE_STREAMABLE_METATYPE(MetavoxelEdit) + #endif /* defined(__interface__MetavoxelMessages__) */ From 52511b2baf4fc74689ba8fc478930d53f1d99dbc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 13:59:03 -0800 Subject: [PATCH 002/153] Adding AABox::touches(box) --- libraries/octree/src/AABox.cpp | 7 +++++++ libraries/octree/src/AABox.h | 1 + 2 files changed, 8 insertions(+) diff --git a/libraries/octree/src/AABox.cpp b/libraries/octree/src/AABox.cpp index 670e2b8a6b..18b45becf5 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 totalScale = _scale + otherBox._scale; + return fabs(relativeCenter.x) <= totalScale && + fabs(relativeCenter.y) <= totalScale && + fabs(relativeCenter.z) <= totalScale; +} // 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; From f6a49c96a72156ff09ea7b705b9e0a99abc02fa6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 13:59:35 -0800 Subject: [PATCH 003/153] Adding ParticleTreeElement::findParticles(box, QVector) --- libraries/particles/src/ParticleTreeElement.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index 8493d59bb8..d8bbbb3a54 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -93,6 +93,10 @@ public: bool removeParticleWithID(uint32_t id); + /// finds all particles that touch a box + /// \param box the query box + /// \param particles[out] vector of Particle pointers + void findParticles(const AABox& box, QVector& foundParticles); protected: virtual void init(unsigned char * octalCode); From 998b772be442f1fc1863dc4dea758dbde38c6111 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 14:01:03 -0800 Subject: [PATCH 004/153] Adding ParticleTreeElement::findParticles(box, QVector) --- libraries/particles/src/ParticleTreeElement.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index 7994909004..2f7919b104 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -211,7 +211,21 @@ bool ParticleTreeElement::removeParticleWithID(uint32_t id) { return foundParticle; } - +void ParticleTreeElement::findParticles(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(); + particleBox.setBox(particle->getPosition() - glm::vec3(radius), 2.f * radius); + // TODO: replace particleBox-box query with sphere-box + if (particleBox.touches(_box)) { + foundParticles.push_back(particle); + } + ++particleItr; + } +} int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { From 5cc2b029cffca882fad8baef93d3e7cbebe49543 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 14:01:34 -0800 Subject: [PATCH 005/153] Adding ParticleTree::findParticles(box, QVector) method --- libraries/particles/src/ParticleTree.cpp | 28 ++++++++++++++++++++++++ libraries/particles/src/ParticleTree.h | 6 +++++ 2 files changed, 34 insertions(+) diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index 73e8dd6711..ea70d13c16 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -458,3 +458,31 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi recurseTreeWithOperation(findAndDeleteOperation, &args); } } + +class FindParticlesArgs { +public: + FindParticlesArgs(const AABox& box) + : _box(box), _foundParticles() { + } + + AABox _box; + QVector _foundParticles; +}; + +bool findOperation(OctreeElement* element, void* extraData) { + FindParticlesArgs* args = static_cast< FindParticlesArgs*>(extraData); + const AABox& elementBox = element->getAABox(); + if (elementBox.touches(args->_box)) { + ParticleTreeElement* particleTreeElement = static_cast(element); + particleTreeElement->findParticles(args->_box, args->_foundParticles); + return true; + } + return false; +} + +void ParticleTree::findParticles(const AABox& box, QVector foundParticles) { + FindParticlesArgs args(box); + recurseTreeWithOperation(findOperation, &args); + // swap the two lists of particle pointers instead of copy + foundParticles.swap(args._foundParticles); +} diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 33e5d5fe75..e0679f192b 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -53,6 +53,12 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode); + /// finds all particles that touch a box + /// \param box the query box + /// \param particles[out] vector of Particle pointer + /// \remark Side effect: any initial contents in particles will be lost + void findParticles(const AABox& box, QVector particles); + private: static bool updateOperation(OctreeElement* element, void* extraData); From 7f20750e80980d9918688d1e684e90e3c7699dc0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 24 Jan 2014 16:03:25 -0800 Subject: [PATCH 006/153] Working on sending edits. --- cmake/macros/AutoMTC.cmake | 4 +- interface/src/MetavoxelSystem.cpp | 15 +++- interface/src/MetavoxelSystem.h | 5 +- interface/src/ui/MetavoxelEditor.cpp | 50 +------------ libraries/metavoxels/src/Bitstream.cpp | 17 +++-- libraries/metavoxels/src/Bitstream.h | 5 +- .../metavoxels/src/DatagramSequencer.cpp | 71 ++++++++++++++----- libraries/metavoxels/src/DatagramSequencer.h | 19 +++++ .../metavoxels/src/MetavoxelMessages.cpp | 54 ++++++++++++++ libraries/metavoxels/src/MetavoxelMessages.h | 11 ++- 10 files changed, 172 insertions(+), 79 deletions(-) create mode 100644 libraries/metavoxels/src/MetavoxelMessages.cpp 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/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index cf0c29ff15..66d9444ed0 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -10,7 +10,6 @@ #include -#include #include #include "Application.h" @@ -42,6 +41,12 @@ void MetavoxelSystem::init() { _buffer.create(); } +void MetavoxelSystem::applyEdit(const MetavoxelEdit& 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)); } @@ -187,6 +192,14 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : _receiveRecords.append(record); } +void MetavoxelClient::applyEdit(const MetavoxelEdit& 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() }; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 7692f0d30d..f758b6e938 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -19,6 +19,7 @@ #include #include +#include #include "renderer/ProgramObject.h" @@ -34,7 +35,7 @@ public: void init(); - MetavoxelData& getData() { return _data; } + void applyEdit(const MetavoxelEdit& edit); void processData(const QByteArray& data, const HifiSockAddr& sender); @@ -90,6 +91,8 @@ public: const QUuid& getSessionID() const { return _sessionID; } + void applyEdit(const MetavoxelEdit& edit); + void simulate(float deltaTime, MetavoxelVisitor& visitor); void receivedData(const QByteArray& data, const HifiSockAddr& sender); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 9a8b951522..df77deb714 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -339,60 +339,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); + MetavoxelEdit edit = { minimum, maximum, _gridSpacing->value(), value }; + Application::getInstance()->getMetavoxels()->applyEdit(edit); } QVariant MetavoxelEditor::getValue() const { diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 77273fea9c..4f23c79d78 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -237,20 +237,25 @@ Bitstream& Bitstream::operator>>(QVariant& value) { return *this; } -Bitstream& Bitstream::operator<<(const AttributeValue& value) { - _attributeStreamer << value.getAttribute(); - if (value.getAttribute()) { - value.getAttribute()->write(*this, value.getValue(), true); +Bitstream& Bitstream::operator<<(const AttributeValue& attributeValue) { + _attributeStreamer << attributeValue.getAttribute(); + if (attributeValue.getAttribute()) { + attributeValue.getAttribute()->write(*this, attributeValue.getValue(), true); } return *this; } -Bitstream& Bitstream::operator>>(AttributeValue& value) { +Bitstream& Bitstream::operator>>(OwnedAttributeValue& attributeValue) { AttributePointer attribute; _attributeStreamer >> attribute; if (attribute) { - void* value; + void* value = attribute->create(); attribute->read(*this, value, true); + attributeValue = AttributeValue(attribute, value); + attribute->destroy(value); + + } else { + attributeValue = AttributeValue(); } return *this; } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index c8793f9673..4d6ea8efe5 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -25,6 +25,7 @@ class QObject; class Attribute; class AttributeValue; class Bitstream; +class OwnedAttributeValue; class TypeStreamer; typedef QSharedPointer AttributePointer; @@ -232,8 +233,8 @@ public: Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); - Bitstream& operator<<(const AttributeValue& value); - Bitstream& operator>>(AttributeValue& 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..1e246b9818 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,12 +137,26 @@ 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); } @@ -138,10 +166,19 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { QList::iterator it = qBinaryFind(_receiveRecords.begin(), _receiveRecords.end(), compare); if (it != _receiveRecords.end()) { _inputStream.persistReadMappings(it->mappings); + _receivedHighPriorityMessages -= it->newHighPriorityMessages; emit receiveAcknowledged(it - _receiveRecords.begin()); _receiveRecords.erase(_receiveRecords.begin(), it + 1); } _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..a7159bd9de 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -31,6 +31,12 @@ 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); + /// Starts a new packet for transmission. /// \return a reference to the Bitstream to use for writing to the packet Bitstream& startPacket(); @@ -50,6 +56,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,10 +80,17 @@ private: public: int packetNumber; Bitstream::ReadMappings mappings; + int newHighPriorityMessages; bool operator<(const ReceiveRecord& other) const { return packetNumber < other.packetNumber; } }; + class HighPriorityMessage { + public: + QVariant data; + int firstPacketNumber; + }; + /// Notes that the described send was acknowledged by the other party. void sendRecordAcknowledged(const SendRecord& record); @@ -104,6 +120,9 @@ private: Bitstream _inputStream; QSet _offsetsReceived; int _remainingBytes; + + QList _highPriorityMessages; + int _receivedHighPriorityMessages; }; #endif /* defined(__interface__DatagramSequencer__) */ diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp new file mode 100644 index 0000000000..46ec4fd781 --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -0,0 +1,54 @@ +// +// MetavoxelMessages.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 1/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "MetavoxelMessages.h" + +class EditVisitor : public MetavoxelVisitor { +public: + + EditVisitor(const MetavoxelEdit& edit); + + virtual bool visit(MetavoxelInfo& info); + +private: + + const MetavoxelEdit& _edit; +}; + +EditVisitor::EditVisitor(const MetavoxelEdit& 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.minimum); + glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _edit.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 MetavoxelEdit::apply(MetavoxelDataPointer& data) const { + //data.detach(); + EditVisitor visitor(*this); + data->guide(visitor); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index e3550acaa5..9b711737e0 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -9,7 +9,9 @@ #ifndef __interface__MetavoxelMessages__ #define __interface__MetavoxelMessages__ +#include "AttributeRegistry.h" #include "Bitstream.h" +#include "MetavoxelData.h" /// A message containing the state of a client. class ClientStateMessage { @@ -35,9 +37,12 @@ class MetavoxelEdit { public: - glm::vec3 minimum; - glm::vec3 maximum; - float granularity; + STREAM glm::vec3 minimum; + STREAM glm::vec3 maximum; + STREAM float granularity; + STREAM OwnedAttributeValue value; + + void apply(MetavoxelDataPointer& data) const; }; DECLARE_STREAMABLE_METATYPE(MetavoxelEdit) From 2f02ee0b315a8c0ff2c3a58f924a615fbb0ef00c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 16:29:44 -0800 Subject: [PATCH 007/153] Moving applyHardCollision to the Particle class. Adding daming and elasticity to CollisionInfo. --- libraries/particles/src/Particle.cpp | 43 +++++++++++++++ libraries/particles/src/Particle.h | 3 + .../particles/src/ParticleCollisionSystem.cpp | 55 ++++--------------- .../particles/src/ParticleCollisionSystem.h | 2 +- libraries/shared/src/CollisionInfo.h | 10 +++- 5 files changed, 68 insertions(+), 45 deletions(-) diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 5cf5e9248d..206f075df4 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -668,7 +668,50 @@ 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); diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index e534c7b418..96e58f0693 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -240,6 +241,8 @@ public: static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew); + void applyHardCollision(const CollisionInfo& collisionInfo); + void update(const uint64_t& now); void collisionWithParticle(Particle* other); void collisionWithVoxel(VoxelDetail* voxel); diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 7e8866e7d4..d2eb291a9d 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -73,6 +73,8 @@ 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)) { @@ -81,7 +83,8 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { 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 } @@ -161,6 +164,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { if (_selfAvatar) { AvatarData* avatar = (AvatarData*)_selfAvatar; 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,7 +190,8 @@ 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); } } } @@ -224,51 +230,14 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // } } -// 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); + properties.setPosition(particle->getPosition() * (float)TREE_SCALE); + properties.setVelocity(particle->getVelocity() * (float)TREE_SCALE); _packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); - - // change the local particle too... - particle->setPosition(position); - particle->setVelocity(velocity); } diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index cf52d01a7a..b96d3f07f3 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -47,7 +47,7 @@ public: 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); private: 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; }; From ef39d019aca3f69678b846f595ae230532f40da6 Mon Sep 17 00:00:00 2001 From: stojce Date: Sun, 26 Jan 2014 16:28:21 +0100 Subject: [PATCH 008/153] Voxel import fix --- interface/src/ImportDialog.cpp | 77 ++++++++++++++++----------------- interface/src/VoxelImporter.cpp | 17 +++++++- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/interface/src/ImportDialog.cpp b/interface/src/ImportDialog.cpp index c3f499b038..ec64b84e63 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"); } @@ -99,7 +98,7 @@ ImportDialog::ImportDialog(QWidget* parent) : QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, NULL), _importButton(IMPORT_BUTTON_NAME, this), _cancelButton(CANCEL_BUTTON_NAME, this) { - + setOption(QFileDialog::DontUseNativeDialog, true); setFileMode(QFileDialog::ExistingFile); setViewMode(QFileDialog::Detail); @@ -111,16 +110,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))); } @@ -131,7 +132,6 @@ ImportDialog::~ImportDialog() { void ImportDialog::import() { emit accepted(); - close(); } void ImportDialog::accept() { @@ -163,72 +163,72 @@ 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 +251,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 +272,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/VoxelImporter.cpp b/interface/src/VoxelImporter.cpp index 49b28c850f..0011b0abb7 100644 --- a/interface/src/VoxelImporter.cpp +++ b/interface/src/VoxelImporter.cpp @@ -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; } From dcdbf729c9656d17469bbc2d56ea35b55b2fcfea Mon Sep 17 00:00:00 2001 From: stojce Date: Sun, 26 Jan 2014 17:57:21 +0100 Subject: [PATCH 009/153] importing file w/ double click --- interface/src/ImportDialog.cpp | 11 +++++++++-- interface/src/ImportDialog.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/interface/src/ImportDialog.cpp b/interface/src/ImportDialog.cpp index ec64b84e63..b992b216bc 100644 --- a/interface/src/ImportDialog.cpp +++ b/interface/src/ImportDialog.cpp @@ -97,7 +97,8 @@ 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); @@ -131,11 +132,17 @@ ImportDialog::~ImportDialog() { } void ImportDialog::import() { + fileAccepted = true; emit accepted(); } void ImportDialog::accept() { - QFileDialog::accept(); + if (!fileAccepted) { + fileAccepted = true; + emit accepted(); + } else { + QFileDialog::accept(); + } } void ImportDialog::reject() { 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__) */ From 1ef6f5f7b3ccb87f6505134f59662824d4aa7afc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 26 Jan 2014 13:23:10 -0800 Subject: [PATCH 010/153] MetavoxelDataPointer -> MetavoxelData, grid spacing as power of 2. --- .../src/metavoxels/MetavoxelServer.cpp | 19 +++++++------- .../src/metavoxels/MetavoxelServer.h | 10 ++++---- interface/src/MetavoxelSystem.cpp | 10 +++----- interface/src/MetavoxelSystem.h | 5 ++-- interface/src/ui/MetavoxelEditor.cpp | 25 +++++++++++-------- interface/src/ui/MetavoxelEditor.h | 1 + libraries/metavoxels/src/MetavoxelData.cpp | 22 ---------------- libraries/metavoxels/src/MetavoxelData.h | 9 +------ .../metavoxels/src/MetavoxelMessages.cpp | 6 ++--- libraries/metavoxels/src/MetavoxelMessages.h | 5 ++-- 10 files changed, 43 insertions(+), 69 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index af982abf9b..db67fe27ed 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -18,8 +18,7 @@ const int SEND_INTERVAL = 50; MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes), - _data(new MetavoxelData()) { + ThreadedAssignment(dataBuffer, numBytes) { _sendTimer.setSingleShot(true); connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas())); @@ -94,9 +93,10 @@ 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); } @@ -114,7 +114,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); + //writeDelta(_server->getData(), _sendRecords.first().data, out); _sequencer.endPacket(); // record the send @@ -134,25 +134,26 @@ 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) { ClientStateMessage state = message.value(); _position = state.position; - } else if (userType == MetavoxelDeltaMessage::Type) { + } else if (userType == MetavoxelEdit::Type) { + MetavoxelEdit edit = message.value(); + qDebug() << "got edit " << edit.granularity; - } 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..3853ac5cf0 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -30,7 +30,7 @@ public: MetavoxelServer(const unsigned char* dataBuffer, int numBytes); - const MetavoxelDataPointer& getData() const { return _data; } + const MetavoxelData& getData() const { return _data; } void removeSession(const QUuid& sessionId); @@ -51,7 +51,7 @@ private: QHash _sessions; - MetavoxelDataPointer _data; + MetavoxelData _data; }; /// Contains the state of a single client session. @@ -76,14 +76,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/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 66d9444ed0..c56087bcc3 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -54,7 +54,6 @@ void MetavoxelSystem::processData(const QByteArray& data, const HifiSockAddr& se void MetavoxelSystem::simulate(float deltaTime) { // simulate the clients _points.clear(); - _data.guide(_pointVisitor); foreach (MetavoxelClient* client, _clients) { client->simulate(deltaTime, _pointVisitor); } @@ -180,8 +179,7 @@ static QByteArray createDatagramHeader(const QUuid& sessionID) { MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : _address(address), _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&))); @@ -197,7 +195,7 @@ void MetavoxelClient::applyEdit(const MetavoxelEdit& edit) { edit.apply(_data); // start sending it out - _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); + // _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); } void MetavoxelClient::simulate(float deltaTime, MetavoxelVisitor& visitor) { @@ -206,7 +204,7 @@ void MetavoxelClient::simulate(float deltaTime, MetavoxelVisitor& visitor) { out << QVariant::fromValue(state); _sequencer.endPacket(); - _data->guide(visitor); + _data.guide(visitor); } void MetavoxelClient::receivedData(const QByteArray& data, const HifiSockAddr& sender) { @@ -238,7 +236,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); + // readDelta(_data, _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 f758b6e938..c3198fa681 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -72,7 +72,6 @@ private: static ProgramObject _program; static int _pointScaleLocation; - MetavoxelData _data; QVector _points; PointVisitor _pointVisitor; QOpenGLBuffer _buffer; @@ -112,7 +111,7 @@ private: class ReceiveRecord { public: int packetNumber; - MetavoxelDataPointer data; + MetavoxelData data; }; HifiSockAddr _address; @@ -120,7 +119,7 @@ private: DatagramSequencer _sequencer; - MetavoxelDataPointer _data; + MetavoxelData _data; QList _receiveRecords; }; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index df77deb714..b71ff52add 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -60,15 +60,16 @@ MetavoxelEditor::MetavoxelEditor() : _gridPlane->setCurrentIndex(GRID_PLANE_XZ); formLayout->addRow("Grid Spacing:", _gridSpacing = new QDoubleSpinBox()); - _gridSpacing->setValue(0.1); + _gridSpacing->setMinimum(-FLT_MAX); _gridSpacing->setMaximum(FLT_MAX); - _gridSpacing->setSingleStep(0.01); + _gridSpacing->setPrefix("2^"); + _gridSpacing->setValue(-3.0); connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(updateGridPosition())); formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox()); - _gridPosition->setSingleStep(0.1); _gridPosition->setMinimum(-FLT_MAX); _gridPosition->setMaximum(FLT_MAX); + updateGridPosition(); _value = new QGroupBox(); _value->setTitle("Value"); @@ -119,7 +120,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)); @@ -183,11 +184,9 @@ void MetavoxelEditor::createNewAttribute() { void MetavoxelEditor::updateGridPosition() { // 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 +208,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 +317,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()) { @@ -345,7 +348,7 @@ void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maxi return; } OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue())); - MetavoxelEdit edit = { minimum, maximum, _gridSpacing->value(), value }; + MetavoxelEdit edit = { minimum, maximum, getGridSpacing(), value }; Application::getInstance()->getMetavoxels()->applyEdit(edit); } diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 21c8478d95..3782007e38 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -40,6 +40,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/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index a88c5cd922..fc8b270551 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -176,28 +176,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++) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 8628958052..4a9867dd7e 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 @@ -28,7 +27,7 @@ class MetavoxelVisitation; class MetavoxelVisitor; /// The base metavoxel representation shared between server and client. -class MetavoxelData : public QSharedData { +class MetavoxelData { public: MetavoxelData(); @@ -56,12 +55,6 @@ private: 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: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 46ec4fd781..961667deaa 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -6,6 +6,7 @@ // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // +#include "MetavoxelData.h" #include "MetavoxelMessages.h" class EditVisitor : public MetavoxelVisitor { @@ -47,8 +48,7 @@ bool EditVisitor::visit(MetavoxelInfo& info) { return true; // subdivide } -void MetavoxelEdit::apply(MetavoxelDataPointer& data) const { - //data.detach(); +void MetavoxelEdit::apply(MetavoxelData& data) const { EditVisitor visitor(*this); - data->guide(visitor); + data.guide(visitor); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 9b711737e0..0872f4b6c3 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -11,7 +11,8 @@ #include "AttributeRegistry.h" #include "Bitstream.h" -#include "MetavoxelData.h" + +class MetavoxelData; /// A message containing the state of a client. class ClientStateMessage { @@ -42,7 +43,7 @@ public: STREAM float granularity; STREAM OwnedAttributeValue value; - void apply(MetavoxelDataPointer& data) const; + void apply(MetavoxelData& data) const; }; DECLARE_STREAMABLE_METATYPE(MetavoxelEdit) From adc747fa93d1006ed24fd3c6e35411735022ab3e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 26 Jan 2014 16:06:11 -0800 Subject: [PATCH 011/153] Streaming fix. --- interface/src/MetavoxelSystem.cpp | 2 +- libraries/metavoxels/src/Bitstream.h | 17 +++++++++++------ libraries/metavoxels/src/DatagramSequencer.cpp | 15 +++++++-------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index c56087bcc3..fdb3ef5fbf 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -195,7 +195,7 @@ void MetavoxelClient::applyEdit(const MetavoxelEdit& edit) { edit.apply(_data); // start sending it out - // _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); + _sequencer.sendHighPriorityMessage(QVariant::fromValue(edit)); } void MetavoxelClient::simulate(float deltaTime, MetavoxelVisitor& visitor) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 4d6ea8efe5..5764cf2cdf 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -91,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); } @@ -108,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); } diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 1e246b9818..83ef641b39 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -161,14 +161,13 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { } 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); - _receivedHighPriorityMessages -= it->newHighPriorityMessages; - 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); From 31a2e467f4a00d8577d724fb168f7ff6f18fadc9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 26 Jan 2014 16:14:11 -0800 Subject: [PATCH 012/153] more efficient ParticleTree::getParticles() and adding ParticleTree::getParticlesForUpdate() --- libraries/particles/src/ParticleTree.cpp | 74 +++++++++---------- libraries/particles/src/ParticleTree.h | 20 +++-- .../particles/src/ParticleTreeElement.cpp | 47 ++++++------ libraries/particles/src/ParticleTreeElement.h | 16 ++-- .../src/ParticlesScriptingInterface.cpp | 3 +- 5 files changed, 85 insertions(+), 75 deletions(-) diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index d0e5060b4c..3e224be48b 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -170,16 +170,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 +185,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 { @@ -495,31 +523,3 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi recurseTreeWithOperation(findAndDeleteOperation, &args); } } - -class FindParticlesArgs { -public: - FindParticlesArgs(const AABox& box) - : _box(box), _foundParticles() { - } - - AABox _box; - QVector _foundParticles; -}; - -bool findOperation(OctreeElement* element, void* extraData) { - FindParticlesArgs* args = static_cast< FindParticlesArgs*>(extraData); - const AABox& elementBox = element->getAABox(); - if (elementBox.touches(args->_box)) { - ParticleTreeElement* particleTreeElement = static_cast(element); - particleTreeElement->findParticles(args->_box, args->_foundParticles); - return true; - } - return false; -} - -void ParticleTree::findParticles(const AABox& box, QVector foundParticles) { - FindParticlesArgs args(box); - recurseTreeWithOperation(findOperation, &args); - // swap the two lists of particle pointers instead of copy - foundParticles.swap(args._foundParticles); -} diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 44db78814c..a19bad9892 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -42,7 +42,19 @@ public: void storeParticle(const Particle& particle, Node* senderNode = NULL); 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); @@ -54,12 +66,6 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode); - /// finds all particles that touch a box - /// \param box the query box - /// \param particles[out] vector of Particle pointer - /// \remark Side effect: any initial contents in particles will be lost - void findParticles(const AABox& box, QVector particles); - private: static bool updateOperation(OctreeElement* element, void* extraData); diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index 0878436e31..72d3e1b7b0 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -185,21 +185,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,22 +241,6 @@ bool ParticleTreeElement::removeParticleWithID(uint32_t id) { return foundParticle; } -void ParticleTreeElement::findParticles(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(); - particleBox.setBox(particle->getPosition() - glm::vec3(radius), 2.f * radius); - // TODO: replace particleBox-box query with sphere-box - if (particleBox.touches(_box)) { - foundParticles.push_back(particle); - } - ++particleItr; - } -} - 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 e967d9bd23..1d30bfbdfd 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -89,15 +89,21 @@ public: bool containsParticle(const Particle& particle) const; bool updateParticle(const Particle& particle); const Particle* getClosestParticle(glm::vec3 position) const; - QVector getParticles(glm::vec3 position, float radius) const; - const Particle* getParticleWithID(uint32_t id) const; - bool removeParticleWithID(uint32_t id); + /// 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 Particle pointers - void findParticles(const AABox& box, QVector& foundParticles); + /// \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..2bbade964d 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -150,7 +150,8 @@ 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); + QVector particles; + _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE, particles); foreach (const Particle* particle, particles) { ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true); From 62b7e6e58d38670b036562d2926df06614b1c406 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 27 Jan 2014 09:00:34 -0800 Subject: [PATCH 013/153] implement local edits in ParticleScriptingInterface --- interface/src/Application.cpp | 8 ++-- libraries/particles/src/Particle.cpp | 22 ++++++++++ libraries/particles/src/Particle.h | 1 + libraries/particles/src/ParticleTree.cpp | 41 +++++++++++++++++- libraries/particles/src/ParticleTree.h | 3 ++ .../particles/src/ParticleTreeElement.cpp | 40 +++++++++++------ libraries/particles/src/ParticleTreeElement.h | 3 +- .../src/ParticlesScriptingInterface.cpp | 43 +++++++++---------- 8 files changed, 120 insertions(+), 41 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 285a395c07..94c1d42b71 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -110,6 +110,7 @@ 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), @@ -153,8 +154,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _swatch(NULL), _pasteMode(false), _logger(new FileLogger()), - _persistThread(NULL), - _statsExpanded(false) + _persistThread(NULL) { _applicationStartupTime = startup_time; @@ -252,7 +252,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 +281,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() { diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index ecda364e85..6e13749aac 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -1065,44 +1065,66 @@ void ParticleProperties::copyFromScriptValue(const QScriptValue &object) { } 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 (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); } } diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index ebd21a6b52..ed865c670d 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -201,6 +201,7 @@ public: /// The last edited time of this particle from the time perspective of the authoritative server/source uint64_t getLastEdited() const { return _lastEdited; } + void setLastEdited(uint64_t lastEdited) { _lastEdited = lastEdited; } /// lifetime of the particle in seconds float getAge() const { return static_cast(usecTimestampNow() - _created) / static_cast(USECS_PER_SECOND); } diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index 7e83c56a42..a6f04a5174 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -81,8 +81,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 +106,43 @@ 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 + } + 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::deleteParticle(const ParticleID& particleID) { + if (particleID.isKnownID) { + FindAndDeleteParticlesArgs args; + args._idsToDelete.push_back(particleID.id); + recurseTreeWithOperation(findAndDeleteOperation, &args); + } +} + + class FindNearPointArgs { public: glm::vec3 position; diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 5fa25b2c2d..3fd6c3eca3 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -40,6 +40,8 @@ public: virtual void update(); void storeParticle(const Particle& particle, Node* senderNode = NULL); + void updateParticle(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); @@ -58,6 +60,7 @@ 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); diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index 1454eadcc9..43b40f8f9f 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,34 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) { return false; } +bool ParticleTreeElement::updateParticle(const ParticleID& particleID, const ParticleProperties& properties) { + // currently only known IDs are in the tree... in the future we might support local storage of creatorTokenID particles + if (particleID.isKnownID) { + uint16_t numberOfParticles = _particles->size(); + uint32_t searchID = particleID.id; + 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]; + if (thisParticle.getID() == searchID) { + 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; +} + + const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) const { const Particle* closestParticle = NULL; float closestParticleDistance = FLT_MAX; diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index 2705ad4292..f8ac341400 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -86,8 +86,9 @@ 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); + const Particle* getClosestParticle(glm::vec3 position) const; QVector getParticles(glm::vec3 position, float radius) const; const Particle* getParticleWithID(uint32_t id) const; diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index 0641cdba7c..73c316de37 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -57,8 +57,10 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID return results; } if (_particleTree) { + _particleTree->lockForRead(); const Particle* particle = _particleTree->findParticleByID(identity.id); results.copyFromParticle(*particle); + _particleTree->unlock(); } return results; @@ -77,29 +79,14 @@ ParticleID ParticlesScriptingInterface::editParticle(ParticleID particleID, cons 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(); - } - } 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->updateParticle(particleID, properties); + _particleTree->unlock(); + } return particleID; } @@ -130,14 +117,22 @@ void ParticlesScriptingInterface::deleteParticle(ParticleID particleID) { //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 +145,9 @@ ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& cen QVector ParticlesScriptingInterface::findParticles(const glm::vec3& center, float radius) const { QVector result; if (_particleTree) { + _particleTree->lockForRead(); QVector particles = _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE); + _particleTree->unlock(); foreach (const Particle* particle, particles) { ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true); From 74801da07f9e2ab51c2afe13c735ab06d5b48eed Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 27 Jan 2014 18:07:03 +0100 Subject: [PATCH 014/153] #19491 - Implement in-app snapshots --- interface/resources/sounds/snap.wav | Bin 0 -> 13274 bytes interface/src/Application.cpp | 18 +++++++++++++ interface/src/Application.h | 1 + interface/src/FileLogger.cpp | 14 +++++----- interface/src/ui/Snapshot.cpp | 38 ++++++++++++++++++++++++++++ interface/src/ui/Snapshot.h | 27 ++++++++++++++++++++ libraries/shared/src/FileUtils.cpp | 27 ++++++++++++++++++-- libraries/shared/src/FileUtils.h | 3 ++- 8 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 interface/resources/sounds/snap.wav create mode 100644 interface/src/ui/Snapshot.cpp create mode 100644 interface/src/ui/Snapshot.h diff --git a/interface/resources/sounds/snap.wav b/interface/resources/sounds/snap.wav new file mode 100644 index 0000000000000000000000000000000000000000..3e22124abf576cfcd7481f80ef1db3e6af4b4607 GIT binary patch literal 13274 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 literal 0 HcmV?d00001 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 285a395c07..d4d9f5d949 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -47,6 +47,9 @@ #include #include #include +#include +#include + #include #include @@ -70,6 +73,7 @@ #include "renderer/ProgramObject.h" #include "ui/TextRenderer.h" #include "InfoView.h" +#include "ui/Snapshot.h" using namespace std; @@ -695,6 +699,7 @@ void Application::keyPressEvent(QKeyEvent* event) { bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier); bool isMeta = event->modifiers().testFlag(Qt::ControlModifier); + bool isControl = event->modifiers().testFlag(Qt::MetaModifier); switch (event->key()) { break; case Qt::Key_Shift: @@ -773,6 +778,8 @@ void Application::keyPressEvent(QKeyEvent* event) { _voxels.collectStatsForTreesAndVBOs(); } else if (isShifted && isMeta) { Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings); + } else if (!isShifted && !isMeta && isControl) { + takeSnapshot(); } else if (_nudgeStarted) { if (_lookingAlongX) { if (_lookingAwayFromOrigin) { @@ -4341,3 +4348,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..21aa26bd47 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -505,6 +505,7 @@ private: void checkVersion(); void displayUpdateDialog(); bool shouldSkipVersion(QString latestVersion); + void takeSnapshot(); }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp index 4c96dfcfa5..3c98b285a3 100644 --- a/interface/src/FileLogger.cpp +++ b/interface/src/FileLogger.cpp @@ -14,17 +14,17 @@ #include #include +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() : _logData(NULL) { setExtraDebugging(false); - _fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - QDir logDir(_fileName); - if (!logDir.exists(_fileName)) { - logDir.mkdir(_fileName); - } + _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 +40,5 @@ void FileLogger::addMessage(QString message) { } void FileLogger::locateLog() { - FileUtils::LocateFile(_fileName); + FileUtils::locateFile(_fileName); } diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp new file mode 100644 index 0000000000..b024890d12 --- /dev/null +++ b/interface/src/ui/Snapshot.cpp @@ -0,0 +1,38 @@ +// +// Snapshot.cpp +// hifi +// +// Created by Stojce Slavkovski on 1/26/14. +// +// + +#include "Snapshot.h" + +#include + +#include +#include + +// filename format: hifi-snap_username_current-coordinates_date_time-with-seconds.jpg +// %1 <= username, %2 <= current location, %3 <= date and time +const QString FILENAME_PATH_FORMAT = "hifi-snap_%1_%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); + QDateTime now = QDateTime::currentDateTime(); + + QString fileName = FileUtils::standardPath(SNAPSHOTS_DIRECTORY); + fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, formattedLocation, now.toString(DATETIME_FORMAT)))); + shot.save(fileName); +} + + diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h new file mode 100644 index 0000000000..39548cdff8 --- /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__ + +#import +#import +#import + +#include + +class Snapshot { + +public: + static void saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location); + +private: + QString _username; +}; + +#endif /* defined(__hifi__Snapshot__) */ 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); }; From c5ba92d73e15a4ff34cdadc2dfeeb30d16556f64 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 27 Jan 2014 11:10:59 -0800 Subject: [PATCH 015/153] first part of getting local particle tree in agent --- assignment-client/src/Agent.cpp | 6 ++++++ assignment-client/src/Agent.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 4f81c42046..85a2fd3a65 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -43,6 +43,9 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& dataByteArray.size()); break; } + } else if (dataByteArray[0] == PACKET_TYPE_PARTICLE_ADD_RESPONSE) { + // this will keep creatorTokenIDs to IDs mapped correctly + Particle::handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size()); } else { NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); } @@ -88,6 +91,9 @@ 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; diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 980885fcf3..6ab2924ada 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -32,6 +33,7 @@ signals: void willSendVisualDataCallback(); private: ScriptEngine _scriptEngine; + ParticleTree _particleTree; }; #endif /* defined(__hifi__Agent__) */ From 13a92bc3c9dc9c47da0102fa697806b04cb394dc Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 27 Jan 2014 11:11:20 -0800 Subject: [PATCH 016/153] tweaks to editParticle example --- examples/editParticleExample.js | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/editParticleExample.js b/examples/editParticleExample.js index 4eb5dfe907..61e32c4d55 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,17 +43,17 @@ var positionDelta = { x: 0.05, y: 0, z: 0 }; var particleID = Particles.addParticle(originalProperties); function moveParticle() { - if (count >= 100) { + if (count >= moveUntil) { //Agent.stop(); // delete it... - if (count == 100) { + if (count == moveUntil) { print("calling Particles.deleteParticle()"); Particles.deleteParticle(particleID); } // stop it... - if (count >= 200) { + if (count >= stopAfter) { print("calling Agent.stop()"); Agent.stop(); } @@ -77,16 +82,6 @@ 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:"); - } } From 40ea552981f24f2ea3fa4969ebae28defe9e27a6 Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 27 Jan 2014 20:35:06 +0100 Subject: [PATCH 017/153] filename format changes - new wav file - changed filename format --- interface/resources/sounds/snap.wav | Bin 13274 -> 182658 bytes interface/src/Application.cpp | 3 +-- interface/src/ui/Snapshot.cpp | 22 +++++++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/interface/resources/sounds/snap.wav b/interface/resources/sounds/snap.wav index 3e22124abf576cfcd7481f80ef1db3e6af4b4607..bb562e1db909662f08de66fb8e03c6fb31191ec2 100644 GIT binary patch literal 182658 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%0A1dJj(tMi(4$Mso{_DS#fPtjoKMDeM~(Yyj0bmO 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 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d4d9f5d949..e660446a10 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -699,7 +699,6 @@ void Application::keyPressEvent(QKeyEvent* event) { bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier); bool isMeta = event->modifiers().testFlag(Qt::ControlModifier); - bool isControl = event->modifiers().testFlag(Qt::MetaModifier); switch (event->key()) { break; case Qt::Key_Shift: @@ -778,7 +777,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _voxels.collectStatsForTreesAndVBOs(); } else if (isShifted && isMeta) { Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings); - } else if (!isShifted && !isMeta && isControl) { + } else if (!isShifted && isMeta) { takeSnapshot(); } else if (_nudgeStarted) { if (_lookingAlongX) { diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index b024890d12..a7ba9bd2a1 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -11,12 +11,14 @@ #include #include -#include +#include +#include -// filename format: hifi-snap_username_current-coordinates_date_time-with-seconds.jpg -// %1 <= username, %2 <= current location, %3 <= date and time -const QString FILENAME_PATH_FORMAT = "hifi-snap_%1_%2_%3.jpg"; -const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; +// 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) { @@ -27,11 +29,17 @@ void Snapshot::saveSnapshot(QGLWidget* widget, QString username, glm::vec3 locat 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); + 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, formattedLocation, now.toString(DATETIME_FORMAT)))); + fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation))); shot.save(fileName); } From c82a8a152ebbca41938970fc94826499b8c08c74 Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 27 Jan 2014 20:59:46 +0100 Subject: [PATCH 018/153] Set JPG quality to 100 --- interface/src/ui/Snapshot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index a7ba9bd2a1..f0fef33cee 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -40,7 +40,7 @@ void Snapshot::saveSnapshot(QGLWidget* widget, QString username, glm::vec3 locat QString fileName = FileUtils::standardPath(SNAPSHOTS_DIRECTORY); fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation))); - shot.save(fileName); + shot.save(fileName, 0, 100); } From 4d1992485fd642af93129f59fee75053f74d764f Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 27 Jan 2014 21:17:17 +0100 Subject: [PATCH 019/153] removed unused QSound reference --- interface/src/Application.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e660446a10..f67e233118 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -47,10 +47,8 @@ #include #include #include -#include #include - #include #include #include From 3fde4078c516a2400a3ed690adf7cd8a2d7080d6 Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 27 Jan 2014 21:48:26 +0100 Subject: [PATCH 020/153] ignore double-click on disabled items --- interface/src/ImportDialog.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/ImportDialog.cpp b/interface/src/ImportDialog.cpp index b992b216bc..28e39f1abd 100644 --- a/interface/src/ImportDialog.cpp +++ b/interface/src/ImportDialog.cpp @@ -137,6 +137,11 @@ void ImportDialog::import() { } void ImportDialog::accept() { + // do nothing if import is not enable + if (!_importButton.isEnabled()) { + return; + } + if (!fileAccepted) { fileAccepted = true; emit accepted(); From 5e4813ab1165a220dbfdcfc50f31cc8c5f7b6067 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 27 Jan 2014 13:50:05 -0800 Subject: [PATCH 021/153] more work on allowing for local particle trees --- assignment-client/src/Agent.cpp | 3 + interface/src/DatagramProcessor.cpp | 1 + libraries/octree/src/Octree.cpp | 1 + libraries/octree/src/Octree.h | 6 ++ libraries/octree/src/OctreeRenderer.cpp | 4 + libraries/particles/src/Particle.cpp | 29 ++++++- libraries/particles/src/Particle.h | 3 + libraries/particles/src/ParticleTree.cpp | 76 +++++++++++++++++++ libraries/particles/src/ParticleTree.h | 3 + .../particles/src/ParticleTreeElement.cpp | 67 +++++++++++----- libraries/particles/src/ParticleTreeElement.h | 12 +++ .../src/ParticlesScriptingInterface.cpp | 24 ++++-- 12 files changed, 201 insertions(+), 28 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 85a2fd3a65..db200c415c 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -46,6 +46,9 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& } else if (dataByteArray[0] == PACKET_TYPE_PARTICLE_ADD_RESPONSE) { // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size()); + + // also give our local particle tree a chance to remap any internal locally created particles + _particleTree.handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size()); } else { NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); } diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index b78bd0c309..29c073fdba 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -57,6 +57,7 @@ void DatagramProcessor::processDatagrams() { case PACKET_TYPE_PARTICLE_ADD_RESPONSE: // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse(incomingPacket, bytesReceived); + application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket, bytesReceived); break; case PACKET_TYPE_PARTICLE_DATA: diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 5d25f0c164..1fa583b2be 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() { diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 959c9272cf..b01a8b1d63 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -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/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 7bbeb065d6..de0d09ef74 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -42,6 +42,10 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Hifi if(command == expectedType) { PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PACKET_TYPE",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; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 6e13749aac..0d111c4e01 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -78,6 +78,33 @@ Particle::Particle() { 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,10 +113,8 @@ 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(); _lastEdited = now; diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index ed865c670d..e37db04a06 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -161,6 +161,8 @@ class Particle { public: Particle(); + + Particle(const ParticleID& particleID, const ParticleProperties& properties); /// 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, @@ -207,6 +209,7 @@ public: 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; } diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index a6f04a5174..7f8f168e5d 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -121,6 +121,12 @@ bool ParticleTree::findAndUpdateWithIDandPropertiesOperation(OctreeElement* elem args->found = true; return false; // stop searching } + + // if we've found our particle stop searching + if (args->found) { + return false; + } + return true; } @@ -134,6 +140,21 @@ void ParticleTree::updateParticle(const ParticleID& particleID, const ParticlePr } } +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; @@ -142,6 +163,61 @@ void ParticleTree::deleteParticle(const ParticleID& particleID) { } } +// 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(unsigned char* packetData , int packetLength) { + unsigned char* dataAt = packetData; + int numBytesPacketHeader = numBytesForPacketHeader(packetData); + dataAt += 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: diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 3fd6c3eca3..54286e7c4f 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -41,6 +41,7 @@ public: 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); @@ -55,6 +56,7 @@ public: void forgetParticlesDeletedBefore(uint64_t sinceTime); void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode); + void handleAddParticleResponse(unsigned char* packetData , int packetLength); private: @@ -66,6 +68,7 @@ private: 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); diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index 43b40f8f9f..7e3f769daa 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -161,32 +161,61 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) { } bool ParticleTreeElement::updateParticle(const ParticleID& particleID, const ParticleProperties& properties) { - // currently only known IDs are in the tree... in the future we might support local storage of creatorTokenID particles - if (particleID.isKnownID) { - uint16_t numberOfParticles = _particles->size(); - uint32_t searchID = particleID.id; - 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]; - if (thisParticle.getID() == searchID) { - thisParticle.setProperties(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(); + 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; - } + 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; diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index f8ac341400..b810efef97 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... @@ -88,6 +99,7 @@ public: 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; diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index 73c316de37..d2f5edce26 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -31,6 +31,13 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr // queue the packet queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 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; } @@ -70,18 +77,21 @@ 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; - queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + // 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(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + } - // If we have a local particle tree set, then also update it. + // 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); From 62e8cbedbd9c3bc1da4ab3a7f1d567ecfd361b8f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 27 Jan 2014 13:51:20 -0800 Subject: [PATCH 022/153] Proper detachment/reference counting for edits. --- libraries/metavoxels/src/MetavoxelData.cpp | 93 ++++++++++++++-------- libraries/metavoxels/src/MetavoxelData.h | 5 +- 2 files changed, 63 insertions(+), 35 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index fc8b270551..c52aaddd4b 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -34,7 +34,7 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { 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), + MetavoxelVisitation firstVisitation = { NULL, visitor, QVector(inputs.size() + 1), QVector(outputs.size()), { glm::vec3(), TOP_LEVEL_SIZE, QVector(inputs.size() + 1), QVector(outputs.size()) } }; for (int i = 0; i < inputs.size(); i++) { @@ -54,12 +54,19 @@ 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()); } } } @@ -183,6 +190,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(); @@ -327,19 +343,15 @@ 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]; + const AttributeValue& value = visitation.info.outputValues[i]; if (value.getAttribute()) { - MetavoxelNode*& node = visitation.outputNodes[i]; - if (!node) { - node = visitation.createOutputNode(i); - } - node->setAttributeValue(value); + visitation.outputNodes[i] = 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()) } }; @@ -360,15 +372,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++) { @@ -488,20 +524,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 4a9867dd7e..56c786efbf 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -62,6 +62,7 @@ public: static const int CHILD_COUNT = 8; MetavoxelNode(const AttributeValue& attributeValue); + MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy); void setAttributeValue(const AttributeValue& attributeValue); @@ -191,16 +192,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__) */ From d5695395c44ece9ef4f2cb59057669bd52c653a0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 27 Jan 2014 15:49:50 -0800 Subject: [PATCH 023/153] Basic editing with delta streaming up and running. --- .../src/metavoxels/MetavoxelServer.cpp | 27 ++++-- .../src/metavoxels/MetavoxelServer.h | 6 +- interface/src/MetavoxelSystem.cpp | 20 +++- interface/src/MetavoxelSystem.h | 5 +- interface/src/ui/MetavoxelEditor.cpp | 2 +- libraries/metavoxels/src/DatagramSequencer.h | 15 +-- libraries/metavoxels/src/MetavoxelData.cpp | 91 ++++++++++--------- libraries/metavoxels/src/MetavoxelData.h | 1 + .../metavoxels/src/MetavoxelMessages.cpp | 8 +- libraries/metavoxels/src/MetavoxelMessages.h | 11 ++- 10 files changed, 118 insertions(+), 68 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index db67fe27ed..f18fbb6c87 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -24,6 +24,10 @@ MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) 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); } @@ -74,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); @@ -98,6 +104,8 @@ MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& session // insert the baseline send record 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 @@ -143,13 +151,16 @@ void MetavoxelSession::clearSendRecordsBefore(int index) { 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 == MetavoxelEdit::Type) { - MetavoxelEdit edit = message.value(); - qDebug() << "got edit " << edit.granularity; + } else if (userType == MetavoxelEditMessage::Type) { + _server->applyEdit(message.value()); } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 3853ac5cf0..c866a8871c 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. @@ -30,6 +31,8 @@ public: MetavoxelServer(const unsigned char* dataBuffer, int numBytes); + void applyEdit(const MetavoxelEditMessage& edit); + const MetavoxelData& getData() const { return _data; } void removeSession(const QUuid& sessionId); @@ -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); diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index fdb3ef5fbf..546b8c9b5c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -41,7 +41,7 @@ void MetavoxelSystem::init() { _buffer.create(); } -void MetavoxelSystem::applyEdit(const MetavoxelEdit& edit) { +void MetavoxelSystem::applyEdit(const MetavoxelEditMessage& edit) { foreach (MetavoxelClient* client, _clients) { client->applyEdit(edit); } @@ -190,7 +190,14 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : _receiveRecords.append(record); } -void MetavoxelClient::applyEdit(const MetavoxelEdit& edit) { +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); @@ -227,6 +234,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) { @@ -236,7 +250,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 c3198fa681..2387a6c67d 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -35,7 +35,7 @@ public: void init(); - void applyEdit(const MetavoxelEdit& edit); + void applyEdit(const MetavoxelEditMessage& edit); void processData(const QByteArray& data, const HifiSockAddr& sender); @@ -87,10 +87,11 @@ class MetavoxelClient : public QObject { public: MetavoxelClient(const HifiSockAddr& address); + virtual ~MetavoxelClient(); const QUuid& getSessionID() const { return _sessionID; } - void applyEdit(const MetavoxelEdit& edit); + void applyEdit(const MetavoxelEditMessage& edit); void simulate(float deltaTime, MetavoxelVisitor& visitor); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index b71ff52add..12f727f139 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -348,7 +348,7 @@ void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maxi return; } OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue())); - MetavoxelEdit edit = { minimum, maximum, getGridSpacing(), value }; + MetavoxelEditMessage edit = { minimum, maximum, getGridSpacing(), value }; Application::getInstance()->getMetavoxels()->applyEdit(edit); } diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index a7159bd9de..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. @@ -37,6 +43,9 @@ public: /// 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(); @@ -85,12 +94,6 @@ private: bool operator<(const ReceiveRecord& other) const { return packetNumber < other.packetNumber; } }; - class HighPriorityMessage { - public: - QVariant data; - int firstPacketNumber; - }; - /// Notes that the described send was acknowledged by the other party. void sendRecordAcknowledged(const SendRecord& record); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index c52aaddd4b..c9136d8cab 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -72,28 +72,20 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { } void MetavoxelData::read(Bitstream& in) { - // save the old roots and clear - QHash oldRoots = _roots; + // clear out any existing roots + decrementRootReferenceCounts(); _roots.clear(); - - // 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 { @@ -105,22 +97,25 @@ void MetavoxelData::write(Bitstream& out) const { } void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { + // shallow copy the reference + *this = reference; + 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; @@ -128,7 +123,7 @@ 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); } } @@ -231,22 +226,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); } } @@ -262,20 +251,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(); + } } } } @@ -292,7 +289,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); + } } } } @@ -343,9 +345,16 @@ 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++) { - const AttributeValue& value = visitation.info.outputValues[i]; - if (value.getAttribute()) { - visitation.outputNodes[i] = new MetavoxelNode(value); + AttributeValue& value = visitation.info.outputValues[i]; + 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) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 56c786efbf..83821e47ad 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -67,6 +67,7 @@ public: void setAttributeValue(const AttributeValue& attributeValue); AttributeValue getAttributeValue(const AttributePointer& attribute) const; + void* getAttributeValue() const { return _attributeValue; } void mergeChildren(const AttributePointer& attribute); diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 961667deaa..02b040e5cc 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -12,16 +12,16 @@ class EditVisitor : public MetavoxelVisitor { public: - EditVisitor(const MetavoxelEdit& edit); + EditVisitor(const MetavoxelEditMessage& edit); virtual bool visit(MetavoxelInfo& info); private: - const MetavoxelEdit& _edit; + const MetavoxelEditMessage& _edit; }; -EditVisitor::EditVisitor(const MetavoxelEdit& edit) : +EditVisitor::EditVisitor(const MetavoxelEditMessage& edit) : MetavoxelVisitor(QVector(), QVector() << edit.value.getAttribute()), _edit(edit) { } @@ -48,7 +48,7 @@ bool EditVisitor::visit(MetavoxelInfo& info) { return true; // subdivide } -void MetavoxelEdit::apply(MetavoxelData& data) const { +void MetavoxelEditMessage::apply(MetavoxelData& data) const { EditVisitor visitor(*this); data.guide(visitor); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 0872f4b6c3..fb8db64506 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -14,6 +14,13 @@ class MetavoxelData; +/// Requests to close the session. +class CloseSessionMessage { + STREAMABLE +}; + +DECLARE_STREAMABLE_METATYPE(CloseSessionMessage) + /// A message containing the state of a client. class ClientStateMessage { STREAMABLE @@ -33,7 +40,7 @@ class MetavoxelDeltaMessage { DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaMessage) /// A simple streamable edit. -class MetavoxelEdit { +class MetavoxelEditMessage { STREAMABLE public: @@ -46,6 +53,6 @@ public: void apply(MetavoxelData& data) const; }; -DECLARE_STREAMABLE_METATYPE(MetavoxelEdit) +DECLARE_STREAMABLE_METATYPE(MetavoxelEditMessage) #endif /* defined(__interface__MetavoxelMessages__) */ From 1e4bfffdd8ebcd319b4b2cdebe7d2845928d2e5b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 27 Jan 2014 16:41:25 -0800 Subject: [PATCH 024/153] Retain a shared node pointer, use the active socket. Fixed a compile warning. --- interface/src/DatagramProcessor.cpp | 2 +- interface/src/MetavoxelSystem.cpp | 27 ++++++++++++++------------- interface/src/MetavoxelSystem.h | 8 ++++---- interface/src/ui/Snapshot.h | 6 +++--- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index b78bd0c309..9dc583a312 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -128,4 +128,4 @@ void DatagramProcessor::processDatagrams() { } } } -} \ No newline at end of file +} diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 546b8c9b5c..46617edab1 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -6,6 +6,7 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include #include #include @@ -111,8 +112,7 @@ 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())); + QMetaObject::invokeMethod(this, "addClient", Q_ARG(const SharedNodePointer&, node)); } } @@ -122,9 +122,9 @@ void MetavoxelSystem::nodeKilled(SharedNodePointer node) { } } -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); } @@ -142,7 +142,7 @@ void MetavoxelSystem::receivedData(const QByteArray& data, const HifiSockAddr& s } MetavoxelClient* client = _clientsBySessionID.value(sessionID); if (client) { - client->receivedData(data, sender); + client->receivedData(data); } } @@ -176,8 +176,8 @@ static QByteArray createDatagramHeader(const QUuid& sessionID) { return header; } -MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : - _address(address), +MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) : + _node(node), _sessionID(QUuid::createUuid()), _sequencer(createDatagramHeader(_sessionID)) { @@ -214,16 +214,17 @@ void MetavoxelClient::simulate(float deltaTime, MetavoxelVisitor& visitor) { _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) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 2387a6c67d..3a8fa3378c 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -49,7 +49,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); @@ -86,7 +86,7 @@ class MetavoxelClient : public QObject { public: - MetavoxelClient(const HifiSockAddr& address); + MetavoxelClient(const SharedNodePointer& node); virtual ~MetavoxelClient(); const QUuid& getSessionID() const { return _sessionID; } @@ -95,7 +95,7 @@ public: void simulate(float deltaTime, MetavoxelVisitor& visitor); - void receivedData(const QByteArray& data, const HifiSockAddr& sender); + void receivedData(const QByteArray& data); private slots: @@ -115,7 +115,7 @@ private: MetavoxelData data; }; - HifiSockAddr _address; + SharedNodePointer _node; QUuid _sessionID; DatagramSequencer _sequencer; diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 39548cdff8..26315678f9 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -9,9 +9,9 @@ #ifndef __hifi__Snapshot__ #define __hifi__Snapshot__ -#import -#import -#import +#include +#include +#include #include From 9a892ea51a54a2b925cfd4c610d9d37d52f63f12 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 27 Jan 2014 17:03:35 -0800 Subject: [PATCH 025/153] Let's actually close our sessios on exit. --- assignment-client/src/metavoxels/MetavoxelServer.cpp | 2 +- interface/src/MetavoxelSystem.cpp | 6 ++++++ interface/src/MetavoxelSystem.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index f18fbb6c87..0ee0ab51a9 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -29,7 +29,7 @@ void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) { } void MetavoxelServer::removeSession(const QUuid& sessionId) { - delete _sessions.take(sessionId); + _sessions.take(sessionId)->deleteLater(); } const char METAVOXEL_SERVER_LOGGING_NAME[] = "metavoxel-server"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 46617edab1..309eef3a95 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -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(); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 3a8fa3378c..0956a9d0d3 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -32,6 +32,7 @@ class MetavoxelSystem : public QObject { public: MetavoxelSystem(); + ~MetavoxelSystem(); void init(); From decad397683bfc4e271eeb855942ae7bf336fbe1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 27 Jan 2014 17:16:02 -0800 Subject: [PATCH 026/153] Handle the case of completely identical data. --- libraries/metavoxels/src/MetavoxelData.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index c9136d8cab..df59206b6b 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -100,6 +100,12 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { // shallow copy the reference *this = reference; + bool changed; + in >> changed; + if (!changed) { + return; + } + int changedCount; in >> changedCount; for (int i = 0; i < changedCount; i++) { @@ -128,6 +134,13 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { } void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const { + // first things first: there might be no change whatsoever + if (_roots == reference._roots) { + out << false; + return; + } + out << true; + // count the number of roots added/changed, then write int changedCount = 0; for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { From 4deb9936ba7d89895886ca9ab0493f3a846a6d46 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 27 Jan 2014 17:44:13 -0800 Subject: [PATCH 027/153] Don't add the specular alpha in the model fragment shaders. --- interface/resources/shaders/model.frag | 2 +- interface/resources/shaders/model_normal_map.frag | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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); } From 2fa3fc523ddea4335364db8b2e670110c62ef430 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Jan 2014 17:51:05 -0800 Subject: [PATCH 028/153] Fix for AABox::touches(otherBox) --- libraries/octree/src/AABox.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/octree/src/AABox.cpp b/libraries/octree/src/AABox.cpp index 18b45becf5..1313111765 100644 --- a/libraries/octree/src/AABox.cpp +++ b/libraries/octree/src/AABox.cpp @@ -119,10 +119,10 @@ bool AABox::contains(const AABox& otherBox) const { bool AABox::touches(const AABox& otherBox) const { glm::vec3 relativeCenter = _corner - otherBox._corner + (glm::vec3(_scale - otherBox._scale) * 0.5f); - float totalScale = _scale + otherBox._scale; - return fabs(relativeCenter.x) <= totalScale && - fabs(relativeCenter.y) <= totalScale && - fabs(relativeCenter.z) <= totalScale; + 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 From 2b5675b077682beae29ba75002c07ea0910d674c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Jan 2014 18:01:53 -0800 Subject: [PATCH 029/153] Fix issue #1684 : particles collide against avatars again. This a quick and dirty fix to resurrect particle-avatar collisions. We rebuild and pass a list of AvatarData* to the ParticleCollisionManager every call to update(). --- interface/src/Application.cpp | 9 +++- interface/src/avatar/AvatarManager.cpp | 9 +++- interface/src/avatar/AvatarManager.h | 2 + .../particles/src/ParticleCollisionSystem.cpp | 47 +++---------------- .../particles/src/ParticleCollisionSystem.h | 12 +++-- 5 files changed, 31 insertions(+), 48 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 285a395c07..258ce6037d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1796,7 +1796,7 @@ void Application::init() { _metavoxels.init(); - _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_myAvatar); + _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio); _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); @@ -2310,7 +2310,12 @@ void Application::update(float deltaTime) { updateCursor(deltaTime); // Handle cursor updates _particles.update(); // update the particles... - _particleCollisionSystem.update(); // handle collisions for the particles... + + // collide the particles... + QVector avatars; + avatars.push_back(&_myAvatar); + _avatarManager.getAvatarBasePointers(avatars); + _particleCollisionSystem.update(avatars); } void Application::updateAvatar(float deltaTime) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index dd1686a0d2..5ac521d838 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -230,4 +230,11 @@ void AvatarManager::clearHash() { while (removeAvatar != _avatarHash.end()) { removeAvatar = removeAvatarAtHashIterator(removeAvatar); } -} \ No newline at end of file +} + +void AvatarManager::getAvatarBasePointers(QVector& avatars) { + AvatarHash::iterator avatarItr = _avatarHash.begin(); + while (avatarItr != _avatarHash.end()) { + avatars.push_back( static_cast(avatarItr.value().data()) ); + } +} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 7d0f11646d..82fe279254 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -36,6 +36,8 @@ public: void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); void clearHash(); + + void getAvatarBasePointers(QVector& avatars); public slots: void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList); diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index d2eb291a9d..d53898ac05 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -20,17 +20,16 @@ #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) { + init(packetSender, particles, voxels, audio); } void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender, - ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, AvatarData* selfAvatar) { + ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio) { _packetSender = packetSender; _particles = particles; _voxels = voxels; _audio = audio; - _selfAvatar = selfAvatar; } ParticleCollisionSystem::~ParticleCollisionSystem() { @@ -52,8 +51,9 @@ bool ParticleCollisionSystem::updateOperation(OctreeElement* element, void* extr } -void ParticleCollisionSystem::update() { +void ParticleCollisionSystem::update(QVector& avatars) { // update all particles + _avatars.swap(avatars); _particles->lockForWrite(); _particles->recurseTreeWithOperation(updateOperation, this); _particles->unlock(); @@ -161,8 +161,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { glm::vec3 penetration; // first check the selfAvatar if set... - if (_selfAvatar) { - AvatarData* avatar = (AvatarData*)_selfAvatar; + for (int i = 0; i < _avatars.size(); ++i) { + AvatarData* avatar = _avatars[i]; CollisionInfo collisionInfo; collisionInfo._damping = DAMPING; collisionInfo._elasticity = ELASTICITY; @@ -195,39 +195,6 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* 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); -// } -// } -// } -// } } void ParticleCollisionSystem::queueParticlePropertiesUpdate(Particle* particle) { diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index b96d3f07f3..b4293071bb 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -34,15 +34,17 @@ class ParticleCollisionSystem { public: ParticleCollisionSystem(ParticleEditPacketSender* packetSender = NULL, ParticleTree* particles = NULL, VoxelTree* voxels = NULL, - AbstractAudioInterface* audio = NULL, - AvatarData* selfAvatar = NULL); + AbstractAudioInterface* audio = NULL); void init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, - AbstractAudioInterface* audio = NULL, AvatarData* selfAvatar = NULL); + AbstractAudioInterface* audio = NULL); ~ParticleCollisionSystem(); - void update(); + /// Compute collisions for particles against: other particles, voxels, and avatars + /// \param avatars the list of avatars to collide against during this frame + void update(QVector& avatars); + void checkParticle(Particle* particle); void updateCollisionWithVoxels(Particle* particle); void updateCollisionWithParticles(Particle* particle); @@ -58,7 +60,7 @@ private: ParticleTree* _particles; VoxelTree* _voxels; AbstractAudioInterface* _audio; - AvatarData* _selfAvatar; + QVector _avatars; // only used during update() }; #endif /* defined(__hifi__ParticleCollisionSystem__) */ From 568d5bc05de5f493875fe3e85446601fc0203a50 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 09:26:47 -0800 Subject: [PATCH 030/153] first cut at particle meshes --- interface/src/ParticleTreeRenderer.cpp | 82 ++++++++++++++-- interface/src/ParticleTreeRenderer.h | 7 ++ libraries/octree/src/OctreeRenderer.h | 2 +- libraries/particles/src/Particle.cpp | 99 +++++++++++++------- libraries/particles/src/Particle.h | 71 +++++++++++--- libraries/shared/src/RegisteredMetaTypes.cpp | 16 ++++ libraries/shared/src/RegisteredMetaTypes.h | 4 + 7 files changed, 229 insertions(+), 52 deletions(-) diff --git a/interface/src/ParticleTreeRenderer.cpp b/interface/src/ParticleTreeRenderer.cpp index aa4978891f..e0229bddaa 100644 --- a/interface/src/ParticleTreeRenderer.cpp +++ b/interface/src/ParticleTreeRenderer.cpp @@ -7,6 +7,8 @@ // // +#include + #include "InterfaceConfig.h" #include "ParticleTreeRenderer.h" @@ -16,8 +18,18 @@ 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; @@ -27,6 +39,39 @@ void ParticleTreeRenderer::update() { } } +void ParticleTreeRenderer::render() { + OctreeRenderer::render(); +} + +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/lotus.fbx")); +//_testModel->setURL(QUrl("http://www.fungibleinsight.com/faces/tie.fbx")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Angie1.fbx")); +//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/orb_model.fbx")); +//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/space_frigate_6.FBX")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/soccer_ball.fbx")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/top%20fbx.FBX")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/golfball_FBX2010.fbx")); +//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/Combat_tank_V01.FBX")); + +//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/orc.fbx")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX")); + +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(); + qDebug() << "calling model->setURL()"; + model->setURL(QUrl(url)); + qDebug() << "after calling setURL()"; + } 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,8 +81,6 @@ 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 @@ -45,18 +88,41 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg glColor3ub(particle.getColor()[RED_INDEX],particle.getColor()[GREEN_INDEX],particle.getColor()[BLUE_INDEX]); float sphereRadius = particle.getRadius() * (float)TREE_SCALE; + bool drawAsModel = particle.hasModel(); + args->_renderedItems++; - if (drawAsSphere) { + if (drawAsModel) { + glPushMatrix(); + 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); + + // glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f) + // set the rotation + glm::quat rotation = particle.getModelRotation(); + model->setRotation(rotation); + + // scale + const float MODEL_SCALE = 0.0006f; // need to figure out correct scale adjust + glm::vec3 scale(1.0f,1.0f,1.0f); + model->setScale(scale * MODEL_SCALE); + + model->simulate(0.0f); + model->render(alpha); + //qDebug() << "called _testModel->render(alpha);"; + glPopMatrix(); + } else { glPushMatrix(); glTranslatef(position.x, position.y, position.z); glutSolidSphere(sphereRadius, 15, 15); glPopMatrix(); - } else { - glPointSize(sphereRadius); - glBegin(GL_POINTS); - glVertex3f(position.x, position.y, position.z); - glEnd(); } } } diff --git a/interface/src/ParticleTreeRenderer.h b/interface/src/ParticleTreeRenderer.h index 1c76cf623d..608d701f23 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 { @@ -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/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index f321ed2e3e..10d655ba17 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -46,7 +46,7 @@ public: 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/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 0d111c4e01..cb3d70ac27 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -391,49 +391,49 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe // 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); @@ -441,21 +441,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); @@ -556,7 +556,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // radius - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_RADIUS) == PACKET_CONTAINS_RADIUS)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) { float radius = properties.getRadius() / (float) TREE_SCALE; memcpy(copyAt, &radius, sizeof(radius)); copyAt += sizeof(radius); @@ -564,7 +564,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // position - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) { glm::vec3 position = properties.getPosition() / (float)TREE_SCALE; memcpy(copyAt, &position, sizeof(position)); copyAt += sizeof(position); @@ -572,7 +572,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // color - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_COLOR) == PACKET_CONTAINS_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); @@ -580,7 +580,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // velocity - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) { glm::vec3 velocity = properties.getVelocity() / (float)TREE_SCALE; memcpy(copyAt, &velocity, sizeof(velocity)); copyAt += sizeof(velocity); @@ -588,7 +588,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // gravity - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_GRAVITY) == PACKET_CONTAINS_GRAVITY)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) { glm::vec3 gravity = properties.getGravity() / (float)TREE_SCALE; memcpy(copyAt, &gravity, sizeof(gravity)); copyAt += sizeof(gravity); @@ -596,7 +596,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // damping - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_DAMPING) == PACKET_CONTAINS_DAMPING)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { float damping = properties.getDamping(); memcpy(copyAt, &damping, sizeof(damping)); copyAt += sizeof(damping); @@ -604,7 +604,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // lifetime - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_LIFETIME) == PACKET_CONTAINS_LIFETIME)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { float lifetime = properties.getLifetime(); memcpy(copyAt, &lifetime, sizeof(lifetime)); copyAt += sizeof(lifetime); @@ -612,7 +612,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // inHand - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { bool inHand = properties.getInHand(); memcpy(copyAt, &inHand, sizeof(inHand)); copyAt += sizeof(inHand); @@ -620,7 +620,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // shoulDie - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SHOULDDIE) == PACKET_CONTAINS_SHOULDDIE)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { bool shouldDie = properties.getShouldDie(); memcpy(copyAt, &shouldDie, sizeof(shouldDie)); copyAt += sizeof(shouldDie); @@ -628,7 +628,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // script - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SCRIPT) == PACKET_CONTAINS_SCRIPT)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) { uint16_t scriptLength = properties.getScript().size() + 1; memcpy(copyAt, &scriptLength, sizeof(scriptLength)); copyAt += sizeof(scriptLength); @@ -857,10 +857,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), @@ -871,6 +876,10 @@ ParticleProperties::ParticleProperties() : _scriptChanged(false), _inHandChanged(false), _shouldDieChanged(false), + _modelURLChanged(false), + _modelTranslationChanged(false), + _modelRotationChanged(false), + _modelScaleChanged(false), _defaultSettings(true) { } @@ -879,44 +888,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; @@ -945,7 +969,18 @@ 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)); diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index e37db04a06..535d77d6af 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -31,16 +31,20 @@ 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 +52,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,6 +83,10 @@ 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; } uint16_t getChangedBits() const; @@ -93,7 +105,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 +128,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; + bool _positionChanged; bool _colorChanged; bool _radiusChanged; @@ -123,6 +147,10 @@ private: bool _scriptChanged; bool _inHandChanged; bool _shouldDieChanged; + bool _modelURLChanged; + bool _modelTranslationChanged; + bool _modelRotationChanged; + bool _modelScaleChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ParticleProperties); @@ -196,6 +224,14 @@ 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; } + ParticleProperties getProperties() const; /// The last updated/simulated time of this particle from the time perspective of the authoritative server/source @@ -238,6 +274,13 @@ 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; @@ -300,6 +343,12 @@ protected: QString _script; bool _inHand; + // model related items + QString _modelURL; + glm::vec3 _modelTranslation; + glm::quat _modelRotation; + float _modelScale; + uint32_t _creatorTokenID; bool _newlyCreated; 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..de667c9ed8 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -19,6 +19,7 @@ Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::vec2) +Q_DECLARE_METATYPE(glm::quat) Q_DECLARE_METATYPE(xColor) void registerMetaTypes(QScriptEngine* engine); @@ -29,6 +30,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); From ade02c0259d18a085bdc548807813c7659dffb48 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jan 2014 09:32:58 -0800 Subject: [PATCH 031/153] Bug fix: update ALL avatars in the AvatarManager (rather than just one). Reviewed with Birarda and pushed directly to upstream. --- interface/src/avatar/AvatarManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index dd1686a0d2..91a356c952 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -63,7 +63,7 @@ void AvatarManager::updateAvatars(float deltaTime) { // simulate avatars AvatarHash::iterator avatar = _avatarHash.begin(); - if (avatar != _avatarHash.end()) { + while (avatar != _avatarHash.end()) { if (avatar->data()->getOwningAvatarMixer()) { // this avatar's mixer is still around, go ahead and simulate it avatar->data()->simulate(deltaTime, NULL); @@ -72,6 +72,7 @@ void AvatarManager::updateAvatars(float deltaTime) { avatar->data()->setMouseRay(applicationInstance->getMouseRayOrigin(), applicationInstance->getMouseRayDirection()); + avatar++; } else { // the mixer that owned this avatar is gone, give it to the vector of fades and kill it avatar = removeAvatarAtHashIterator(avatar); @@ -230,4 +231,4 @@ void AvatarManager::clearHash() { while (removeAvatar != _avatarHash.end()) { removeAvatar = removeAvatarAtHashIterator(removeAvatar); } -} \ No newline at end of file +} From 4103cc1cde0d710021fed890cd61288f608a4e03 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 10:36:08 -0800 Subject: [PATCH 032/153] packet header changes and beginning of conversion to QByteArray --- animation-server/src/AnimationServer.cpp | 42 ++- assignment-client/src/Agent.cpp | 18 +- assignment-client/src/Agent.h | 2 +- assignment-client/src/AssignmentClient.cpp | 71 ++--- assignment-client/src/AssignmentFactory.cpp | 18 +- assignment-client/src/AssignmentFactory.h | 2 +- assignment-client/src/audio/AudioMixer.cpp | 24 +- assignment-client/src/audio/AudioMixer.h | 2 +- .../src/audio/AudioMixerClientData.cpp | 15 +- .../src/audio/AudioMixerClientData.h | 2 +- .../src/audio/AvatarAudioRingBuffer.cpp | 6 +- .../src/audio/AvatarAudioRingBuffer.h | 2 +- assignment-client/src/avatars/AvatarMixer.cpp | 82 ++--- assignment-client/src/avatars/AvatarMixer.h | 2 +- .../src/metavoxels/MetavoxelServer.cpp | 8 +- .../src/metavoxels/MetavoxelServer.h | 2 +- data-server/src/DataServer.cpp | 118 +++----- domain-server/src/DomainServer.cpp | 186 +++++------- domain-server/src/DomainServer.h | 2 - interface/src/Application.cpp | 61 ++-- interface/src/Application.h | 10 +- interface/src/Audio.cpp | 19 +- interface/src/DatagramProcessor.cpp | 86 +++--- interface/src/Environment.cpp | 18 +- interface/src/Environment.h | 2 +- interface/src/MetavoxelSystem.cpp | 3 +- interface/src/ParticleTreeRenderer.h | 4 +- interface/src/VoxelPacketProcessor.cpp | 45 +-- interface/src/VoxelPacketProcessor.h | 2 +- interface/src/VoxelSystem.cpp | 27 +- interface/src/VoxelSystem.h | 2 +- interface/src/avatar/Avatar.cpp | 5 +- interface/src/avatar/Avatar.h | 2 +- interface/src/avatar/AvatarManager.cpp | 19 +- interface/src/avatar/MyAvatar.cpp | 15 +- interface/src/devices/Transmitter.cpp | 2 +- libraries/audio/src/AudioInjector.cpp | 49 ++- libraries/audio/src/AudioRingBuffer.cpp | 6 +- libraries/audio/src/AudioRingBuffer.h | 2 +- .../audio/src/InjectedAudioRingBuffer.cpp | 26 +- libraries/audio/src/InjectedAudioRingBuffer.h | 2 +- .../audio/src/PositionalAudioRingBuffer.cpp | 31 +- .../audio/src/PositionalAudioRingBuffer.h | 6 +- libraries/avatars/src/AvatarData.cpp | 130 ++++---- libraries/avatars/src/AvatarData.h | 4 +- libraries/avatars/src/HandData.cpp | 43 ++- libraries/avatars/src/HandData.h | 2 +- libraries/metavoxels/src/MetavoxelUtil.cpp | 2 +- .../src/OctreeInboundPacketProcessor.cpp | 31 +- .../src/OctreeInboundPacketProcessor.h | 2 +- .../octree-server/src/OctreeQueryNode.cpp | 2 +- libraries/octree-server/src/OctreeQueryNode.h | 2 +- .../octree-server/src/OctreeSendThread.cpp | 2 +- libraries/octree-server/src/OctreeServer.cpp | 36 +-- libraries/octree-server/src/OctreeServer.h | 4 +- libraries/octree/src/JurisdictionListener.cpp | 10 +- libraries/octree/src/JurisdictionListener.h | 8 +- libraries/octree/src/JurisdictionMap.cpp | 10 +- libraries/octree/src/JurisdictionMap.h | 4 +- libraries/octree/src/JurisdictionSender.cpp | 12 +- libraries/octree/src/JurisdictionSender.h | 4 +- libraries/octree/src/Octree.cpp | 19 +- libraries/octree/src/Octree.h | 8 +- .../octree/src/OctreeEditPacketSender.cpp | 22 +- libraries/octree/src/OctreeEditPacketSender.h | 12 +- libraries/octree/src/OctreePacketData.h | 6 +- libraries/octree/src/OctreeQuery.cpp | 8 +- libraries/octree/src/OctreeQuery.h | 2 +- libraries/octree/src/OctreeRenderer.cpp | 8 +- libraries/octree/src/OctreeRenderer.h | 6 +- libraries/octree/src/OctreeSceneStats.cpp | 18 +- libraries/octree/src/OctreeSceneStats.h | 5 +- .../particle-server/src/ParticleNodeData.h | 2 +- .../particle-server/src/ParticleServer.cpp | 6 +- .../particle-server/src/ParticleServer.h | 4 +- libraries/particles/src/Particle.cpp | 14 +- libraries/particles/src/Particle.h | 8 +- .../particles/src/ParticleCollisionSystem.cpp | 6 +- .../src/ParticleEditPacketSender.cpp | 4 +- .../particles/src/ParticleEditPacketSender.h | 4 +- libraries/particles/src/ParticleTree.cpp | 38 +-- libraries/particles/src/ParticleTree.h | 8 +- .../src/ParticlesScriptingInterface.cpp | 12 +- .../src/ParticlesScriptingInterface.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 16 +- libraries/shared/src/Assignment.cpp | 129 ++------ libraries/shared/src/Assignment.h | 29 +- libraries/shared/src/DataServerClient.cpp | 106 ++----- libraries/shared/src/DataServerClient.h | 8 +- libraries/shared/src/HifiSockAddr.cpp | 32 +- libraries/shared/src/HifiSockAddr.h | 4 +- libraries/shared/src/NetworkPacket.cpp | 31 +- libraries/shared/src/NetworkPacket.h | 15 +- libraries/shared/src/Node.cpp | 19 +- libraries/shared/src/Node.h | 17 +- libraries/shared/src/NodeData.h | 2 +- libraries/shared/src/NodeList.cpp | 285 ++++++++---------- libraries/shared/src/NodeList.h | 18 +- libraries/shared/src/NodeTypes.h | 31 -- libraries/shared/src/PacketHeaders.cpp | 164 +++++----- libraries/shared/src/PacketHeaders.h | 104 ++++--- libraries/shared/src/PacketSender.cpp | 14 +- libraries/shared/src/PacketSender.h | 2 +- .../shared/src/ReceivedPacketProcessor.cpp | 8 +- .../shared/src/ReceivedPacketProcessor.h | 4 +- libraries/shared/src/SharedUtil.cpp | 111 +------ libraries/shared/src/SharedUtil.h | 23 +- libraries/shared/src/ThreadedAssignment.cpp | 4 +- libraries/shared/src/ThreadedAssignment.h | 2 +- libraries/voxel-server/src/VoxelNodeData.h | 2 +- libraries/voxel-server/src/VoxelServer.cpp | 4 +- libraries/voxel-server/src/VoxelServer.h | 4 +- libraries/voxels/src/EnvironmentData.cpp | 4 +- libraries/voxels/src/EnvironmentData.h | 2 +- .../voxels/src/VoxelEditPacketSender.cpp | 103 ++++++- libraries/voxels/src/VoxelEditPacketSender.h | 6 +- libraries/voxels/src/VoxelPacketData.h | 6 +- libraries/voxels/src/VoxelTree.cpp | 38 +-- libraries/voxels/src/VoxelTree.h | 8 +- .../voxels/src/VoxelsScriptingInterface.cpp | 8 +- .../voxels/src/VoxelsScriptingInterface.h | 2 +- 121 files changed, 1331 insertions(+), 1612 deletions(-) delete mode 100644 libraries/shared/src/NodeTypes.h diff --git a/animation-server/src/AnimationServer.cpp b/animation-server/src/AnimationServer.cpp index 6d493b62e5..c436fd1d4b 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++) { @@ -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] == NODE_TYPE_VOXEL_SERVER && ::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..38985a916e 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -22,29 +22,27 @@ #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... + if (packetTypeForPacket(dataByteArray) == PacketTypeJurisdiction) { + int headerBytes = numBytesForPacketHeader(dataByteArray); + // PacketType_JURISDICTION, first byte is the node type... switch (dataByteArray[headerBytes]) { case NODE_TYPE_VOXEL_SERVER: _scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, - (unsigned char*) dataByteArray.data(), - dataByteArray.size()); + dataByteArray); break; case NODE_TYPE_PARTICLE_SERVER: _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, - (unsigned char*) dataByteArray.data(), - dataByteArray.size()); + dataByteArray); break; } } else { - NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); + NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray); } } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 980885fcf3..bc026084c1 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -21,7 +21,7 @@ class Agent : public ThreadedAssignment { Q_OBJECT public: - Agent(const unsigned char* dataBuffer, int numBytes); + Agent(const QByteArray& packet); public slots: void run(); diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index fc8d2deffd..998ff46217 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -102,63 +102,58 @@ 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) { - nodeList->setDomainSockAddr(senderSockAddr); - nodeList->setOwnerUUID(_currentAssignment->getUUID()); - - qDebug() << "Destination IP for assignment is" << nodeList->getDomainIP().toString(); - - // start the deployed assignment - QThread* workerThread = new QThread(this); - - connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); - - connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); - connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); - connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); - connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); - - _currentAssignment->moveToThread(workerThread); - - // move the NodeList to the thread used for the _current assignment - nodeList->moveToThread(workerThread); - - // Starts an event loop, and emits workerThread->started() - workerThread->start(); - } else { - qDebug("Received a bad destination socket for assignment."); - } + + nodeList->setDomainSockAddr(senderSockAddr); + nodeList->setOwnerUUID(_currentAssignment->getUUID()); + + qDebug() << "Destination IP for assignment is" << nodeList->getDomainIP().toString(); + + // start the deployed assignment + QThread* workerThread = new QThread(this); + + connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); + + connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); + connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); + connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); + connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); + + _currentAssignment->moveToThread(workerThread); + + // move the NodeList to the thread used for the _current assignment + nodeList->moveToThread(workerThread); + + // Starts an event loop, and emits workerThread->started() + workerThread->start(); } } else { // have the NodeList attempt to handle it - nodeList->processNodeData(senderSockAddr, packetData, receivedBytes); + nodeList->processNodeData(senderSockAddr, receivedPacket); } } } diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index b32a909bfe..0b8e089c01 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -18,25 +18,25 @@ #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) { + int headerBytes = numBytesForPacketHeader(packet); Assignment::Type assignmentType = Assignment::AllTypes; - memcpy(&assignmentType, dataBuffer + headerBytes, sizeof(Assignment::Type)); + memcpy(&assignmentType, packet.data() + headerBytes, sizeof(Assignment::Type)); switch (assignmentType) { 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..6a721824f3 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,7 +231,7 @@ 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); } } @@ -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) { 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..bb5654ddb7 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,11 +48,10 @@ 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(); @@ -72,45 +60,39 @@ void broadcastAvatarData() { if (node->getLinkedData() && node->getType() == NODE_TYPE_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()); } @@ -122,13 +104,10 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { && 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, + NodeList::getInstance()->broadcastToNodes(killPacket, QSet() << NODE_TYPE_AGENT); } } @@ -137,29 +116,28 @@ 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; } 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..9fee6f3e2c 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -17,8 +17,8 @@ const int SEND_INTERVAL = 50; -MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes), +MetavoxelServer::MetavoxelServer(const QByteArray& packet) : + ThreadedAssignment(packet), _data(new MetavoxelData()) { _sendTimer.setSingleShot(true); @@ -40,12 +40,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; } } diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 407c520116..862d5a8d18 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -28,7 +28,7 @@ class MetavoxelServer : public ThreadedAssignment { public: - MetavoxelServer(const unsigned char* dataBuffer, int numBytes); + MetavoxelServer(const QByteArray& packet); const MetavoxelDataPointer& getData() const { return _data; } diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp index c59749ed08..173f42f33a 100644 --- a/data-server/src/DataServer.cpp +++ b/data-server/src/DataServer.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include "DataServer.h" @@ -51,66 +52,59 @@ 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()); + + 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,9 +116,12 @@ 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, + // which is a reply packet with the sequence number + QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm); + + replyPacket.append(sequenceNumber); + + _socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } @@ -132,66 +129,49 @@ void DataServer::readPendingDatagrams() { } else { // setup a send packet with the returned data // leverage the packetData sent by overwriting and appending - int numSendPacketBytes = receivedBytes; + QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend); - packetData[0] = PACKET_TYPE_DATA_SERVER_SEND; - - if (strcmp((char*) packetData + readBytes, "uuid") != 0) { + if (!receivedPacket.mid(numReceivedHeaderBytes).startsWith("uuid")) { 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'); - + // pull the key that specifies the data the user is putting/getting, null terminate it + QString keyListString(receivedPacket.mid(numReceivedHeaderBytes)); + QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR); + + 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; + sendPacket.append(reply->str, reply->len); } else { // didn't find a value - insert a space - packetData[numSendPacketBytes++] = ' '; + sendPacket.append(' '); } // add the multi-value separator - packetData[numSendPacketBytes++] = MULTI_KEY_VALUE_SEPARATOR; + sendPacket.append(MULTI_KEY_VALUE_SEPARATOR); freeReplyObject(reply); } // null terminate the packet we're sending back (erases the trailing separator) - packetData[(numSendPacketBytes - 1)] = '\0'; + sendPacket[sendPacket.size() - 1] = '\0'; } 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'); + sendPacket.append(uuidStringWithoutCurlyBraces(parsedUUID)); + sendPacket.append('\0'); } // reply back with the send packet - _socket.writeDatagram(reinterpret_cast(packetData), numSendPacketBytes, - senderSockAddr.getAddress(), senderSockAddr.getPort()); - - + _socket.writeDatagram(sendPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } } } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 0ee7cca467..073f788f86 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -102,39 +102,39 @@ 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; + NODE_TYPE 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,20 +143,14 @@ 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 - }; - + + const QSet STATICALLY_ASSIGNED_NODES = QSet() << NODE_TYPE_AUDIO_MIXER + << NODE_TYPE_AVATAR_MIXER << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER + << NODE_TYPE_METAVOXEL_SERVER; + Assignment* matchingStaticAssignment = NULL; - - if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL + + if (!STATICALLY_ASSIGNED_NODES.contains(nodeType) || ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType)) || checkInWithUUIDMatchesExistingNode(nodePublicAddress, nodeLocalAddress, @@ -166,80 +160,81 @@ void DomainServer::readAvailableDatagrams() { 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 - + if (_hasCompletedRestartHold) { // remove the matching assignment from the assignment queue so we don't take the next check in removeAssignmentFromQueue(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; + + NODE_TYPE* 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(); 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) { - + } else if (requestType == PacketTypeRequestAssignment) { + if (_assignmentQueue.size() > 0) { // construct the requested assignment from the packet data - Assignment requestAssignment(packetData, receivedBytes); - + Assignment requestAssignment(receivedPacket); + 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, + assignmentPacket.resize(numAssignmentPacketHeaderBytes); + QDataStream assignmentStream(&assignmentPacket, QIODevice::Append); + + assignmentStream << *assignmentToDeploy; + + nodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); - + if (assignmentToDeploy->getNumberOfInstances() == 0) { // there are no more instances of this script to send out, delete it delete assignmentToDeploy; } } - + } else { qDebug() << "Received an invalid assignment request from" << senderSockAddr.getAddress(); } @@ -278,8 +273,8 @@ 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; @@ -386,8 +381,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& 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 @@ -538,21 +533,6 @@ void DomainServer::nodeKilled(SharedNodePointer node) { } } -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; @@ -595,9 +575,8 @@ void DomainServer::prepopulateStaticAssignmentFile() { 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); + + voxelServerAssignment.setPayload(config.toUtf8()); freshStaticAssignments[numFreshStaticAssignments++] = voxelServerAssignment; } @@ -637,9 +616,8 @@ void DomainServer::prepopulateStaticAssignmentFile() { 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); + + particleServerAssignment.setPayload(config.toLocal8Bit()); freshStaticAssignments[numFreshStaticAssignments++] = particleServerAssignment; } @@ -652,7 +630,7 @@ void DomainServer::prepopulateStaticAssignmentFile() { Assignment& metavoxelAssignment = (freshStaticAssignments[numFreshStaticAssignments++] = Assignment(Assignment::CreateCommand, Assignment::MetavoxelServerType)); if (_metavoxelServerConfig) { - metavoxelAssignment.setPayload((const unsigned char*)_metavoxelServerConfig, strlen(_metavoxelServerConfig)); + metavoxelAssignment.setPayload(QByteArray(_metavoxelServerConfig)); } qDebug() << "Adding" << numFreshStaticAssignments << "static assignments to fresh file."; @@ -708,10 +686,8 @@ Assignment* DomainServer::deployableAssignmentForRequest(Assignment& requestAssi while (assignment != _assignmentQueue.end()) { 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 nietherHasPool = (*assignment)->getPool().isEmpty() && requestAssignment.getPool().isEmpty(); + bool assignmentPoolsMatch = (*assignment)->getPool() == requestAssignment.getPool(); if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index fed5aaaa43..1c662bcb73 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -47,8 +47,6 @@ private: const QUuid& checkInUUI); void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment); - unsigned char* addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd); - HTTPManager _HTTPManager; QMutex _assignmentQueueMutex; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e2778fd6a8..556bb40bcf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -49,7 +49,6 @@ #include #include -#include #include #include #include @@ -640,7 +639,7 @@ void Application::resetProfile(const QString& username) { updateWindowTitle(); } -void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, +void Application::controlledBroadcastToNodes(const QByteArray& packet, const QSet& destinationNodeTypes) { foreach(NODE_TYPE type, destinationNodeTypes) { // Intercept data to voxel server when voxels are disabled @@ -649,8 +648,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ } // Perform the broadcast for one type - int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, - QSet() << type); + int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(packet, QSet() << type); // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise) BandwidthMeter::ChannelIndex channel; @@ -665,7 +663,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ default: continue; } - _bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); + _bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * packet.size()); } } @@ -1319,11 +1317,9 @@ 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 + QByteArray pingPacket = NodeList::getInstance()->constructPingPacket(); + getInstance()->controlledBroadcastToNodes(pingPacket, QSet() << NODE_TYPE_VOXEL_SERVER + << NODE_TYPE_PARTICLE_SERVER << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER << NODE_TYPE_METAVOXEL_SERVER); } @@ -1463,7 +1459,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); @@ -1484,7 +1480,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 @@ -1554,7 +1550,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; @@ -2350,18 +2346,10 @@ void Application::updateAvatar(float deltaTime) { _myAvatar.getHead().setAudioLoudness(_audio.getLastInputLoudness()); // send head/hand data to the avatar mixer and voxel server - unsigned char broadcastString[MAX_PACKET_SIZE]; - unsigned char* endOfBroadcastStringWrite = broadcastString; + QByteArray avatarData = byteArrayWithPopluatedHeader(PacketTypeAvatarData); + avatarData.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(avatarData, QSet() << NODE_TYPE_AVATAR_MIXER); // Update _viewFrustum with latest camera and view frustum data... // NOTE: we get this from the view frustum, to make it simpler, since the @@ -2372,11 +2360,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(NODE_TYPE_VOXEL_SERVER, PacketTypeVoxelQuery, _voxelServerJurisdictions); + queryOctree(NODE_TYPE_PARTICLE_SERVER, PacketTypeParticleQuery, _particleServerJurisdictions); } -void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions) { +void Application::queryOctree(NODE_TYPE serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) { // if voxels are disabled, then don't send this at all... if (!Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { @@ -2544,10 +2532,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); @@ -3658,7 +3643,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); @@ -3830,8 +3815,7 @@ void Application::nodeKilled(SharedNodePointer node) { } } -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); @@ -3842,13 +3826,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); @@ -3856,7 +3840,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) { @@ -3865,7 +3849,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; } diff --git a/interface/src/Application.h b/interface/src/Application.h index defe4dc8aa..db93f7c67f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -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 QSet& destinationNodeTypes); void setupWorldLight(); @@ -299,7 +298,7 @@ private: void renderHighlightVoxel(VoxelDetail voxel); void updateAvatar(float deltaTime); - void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions); + void queryOctree(NODE_TYPE serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); glm::vec3 getSunDirection(); @@ -477,9 +476,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; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 47ab8b0aba..82c7360d77 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -285,8 +284,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); @@ -375,16 +374,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)); @@ -433,7 +426,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()); diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index b78bd0c309..e2437e4441 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -26,52 +26,51 @@ 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()); _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->_myTransmitter.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); 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,50 +79,47 @@ 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; } } 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/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 258db6da00..5bd83d969c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -167,8 +167,7 @@ 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; } diff --git a/interface/src/ParticleTreeRenderer.h b/interface/src/ParticleTreeRenderer.h index 1c76cf623d..20cb41b207 100644 --- a/interface/src/ParticleTreeRenderer.h +++ b/interface/src/ParticleTreeRenderer.h @@ -29,8 +29,8 @@ public: 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 PacketType getMyQueryMessageType() const { return PacketTypeParticleQuery; } + virtual PacketType getExpectedPacketType() const { return PacketTypeParticleData; } virtual void renderElement(OctreeElement* element, RenderArgs* args); void update(); 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..029690e56a 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() { diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 728106a922..915b5a05b4 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) { } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 5e41619997..61d2ddb8a0 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -336,11 +335,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; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 10a3760f6e..b8f27a0edf 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -114,7 +114,7 @@ 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); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index dd1686a0d2..8228ec9e64 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -164,15 +164,11 @@ 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 numDummyByteArrayHeaderBytes = dummyAvatarByteArray.size(); // enumerate over all of the avatars in this packet // only add them if mixerWeakPointer points to something (meaning that mixer is still around) @@ -197,18 +193,17 @@ 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(numDummyByteArrayHeaderBytes); + dummyAvatarByteArray += 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); } } 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); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ca7a3b863b..cee524c171 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -473,18 +472,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, QSet() << NODE_TYPE_AVATAR_MIXER); } void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { diff --git a/interface/src/devices/Transmitter.cpp b/interface/src/devices/Transmitter.cpp index 8ae6bdc405..e6da39447c 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) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 60ff777452..fd8bdc1a89 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -45,45 +45,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 +74,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); 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()); } 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..d7b40f63ea 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -26,29 +26,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..fa515a5bf7 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -35,23 +35,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 source UUID + packetStream.skipRawData(NUM_BYTES_RFC4122_UUID); + + 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 +60,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..f52af7cff8 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -52,9 +52,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 +66,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 +88,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 +154,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) { @@ -166,67 +170,68 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { _handData = new HandData(this); } - // increment to push past the packet header - int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); - sourceBuffer += numBytesPacketHeader; + QDataStream packetStream(packet); + packetStream.skipRawData(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; + packetStream.readRawData(reinterpret_cast(&_position), sizeof(_position)); // Body rotation (NOTE: This needs to become a quaternion to save two bytes) - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyYaw); - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyPitch); - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll); - - // Body scale - sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _targetScale); + uint16_t twoByteHolder; + packetStream >> twoByteHolder; + unpackFloatAngleFromTwoByte(&twoByteHolder, &_bodyYaw); + + packetStream >> twoByteHolder; + unpackFloatAngleFromTwoByte(&twoByteHolder, &_bodyPitch); + + packetStream >> twoByteHolder; + unpackFloatAngleFromTwoByte(&twoByteHolder, &_bodyRoll); + + // body scale + packetStream >> twoByteHolder; + unpackFloatRatioFromTwoByte(reinterpret_cast(&twoByteHolder), _targetScale); // Head rotation (NOTE: This needs to become a quaternion to save two bytes) float headYaw, headPitch, headRoll; - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw); - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch); - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll); + + packetStream >> twoByteHolder; + unpackFloatAngleFromTwoByte(&twoByteHolder, &headYaw); + + packetStream >> twoByteHolder; + unpackFloatAngleFromTwoByte(&twoByteHolder, &headPitch); + + packetStream >> twoByteHolder; + unpackFloatAngleFromTwoByte(&twoByteHolder, &headRoll); _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); + packetStream >> _headData->_leanSideways; + packetStream >> _headData->_leanForward; // Hand Position - is relative to body position glm::vec3 handPositionRelative; - memcpy(&handPositionRelative, sourceBuffer, sizeof(float) * 3); + packetStream.readRawData(reinterpret_cast(&handPositionRelative), sizeof(handPositionRelative)); _handPosition = _position + handPositionRelative; - sourceBuffer += sizeof(float) * 3; - - // Lookat Position - memcpy(&_headData->_lookAtPosition, sourceBuffer, sizeof(_headData->_lookAtPosition)); - sourceBuffer += sizeof(_headData->_lookAtPosition); + + packetStream.readRawData(reinterpret_cast(&_headData->_lookAtPosition), 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); + packetStream >> _headData->_audioLoudness; // the rest is a chat message - int chatMessageSize = *sourceBuffer++; - _chatMessage = string((char*)sourceBuffer, chatMessageSize); - sourceBuffer += chatMessageSize * sizeof(char); + + quint8 chatMessageSize; + packetStream >> chatMessageSize; + _chatMessage = string(packet.data() + packetStream.device()->pos(), chatMessageSize); + packetStream.skipRawData(chatMessageSize); // voxel sending features... unsigned char bitItems = 0; - bitItems = (unsigned char)*sourceBuffer++; - + packetStream >> bitItems; + // key state, stored as a semi-nibble in the bitItems _keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT); @@ -239,34 +244,31 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { // 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); + packetStream >> _headData->_leftEyeBlink; + packetStream >> _headData->_rightEyeBlink; + packetStream >> _headData->_averageLoudness; + packetStream >> _headData->_browAudioLift; - _headData->_blendshapeCoefficients.resize(*sourceBuffer++); - memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer, - _headData->_blendshapeCoefficients.size() * sizeof(float)); - sourceBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float); + quint8 numBlendshapeCoefficients; + packetStream >> numBlendshapeCoefficients; + + _headData->_blendshapeCoefficients.resize(numBlendshapeCoefficients); + packetStream.readRawData(reinterpret_cast(_headData->_blendshapeCoefficients.data()), + numBlendshapeCoefficients * sizeof(float)); } // pupil dilation - sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f); + quint8 pupilByte; + packetStream >> pupilByte; + unpackFloatFromByte(&pupilByte, _headData->_pupilDilation, 1.0f); // leap hand data - if (sourceBuffer - startPosition < numBytes) { + if (packetStream.device()->pos() < packet.size()) { // check passed, bytes match - sourceBuffer += _handData->decodeRemoteData(sourceBuffer); + packetStream.skipRawData(_handData->decodeRemoteData(packet.mid(packetStream.device()->pos()))); } - return sourceBuffer - startPosition; + return packetStream.device()->pos(); } void AvatarData::setClampedTargetScale(float targetScale) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fc9bad7f02..bc16f2baaa 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -79,8 +79,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; } diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 81d4469486..9342c2c6ed 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -157,10 +157,11 @@ int HandData::encodeRemoteData(unsigned char* destinationBuffer) { return destinationBuffer - startPosition; } -int HandData::decodeRemoteData(unsigned char* sourceBuffer) { - const unsigned char* startPosition = sourceBuffer; - - unsigned int numHands = *sourceBuffer++; +int HandData::decodeRemoteData(const QByteArray& dataByteArray) { + QDataStream packetStream(dataByteArray); + + quint8 numHands; + packetStream >> numHands; for (unsigned int handIndex = 0; handIndex < numHands; ++handIndex) { if (handIndex >= getNumPalms()) @@ -169,9 +170,18 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) { glm::vec3 handPosition; glm::vec3 handNormal; - sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, handPosition, fingerVectorRadix); - sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, handNormal, fingerVectorRadix); - unsigned int numFingers = *sourceBuffer++; + qint16 twoByteHolder; + + packetStream >> twoByteHolder; + unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast(&twoByteHolder), + handPosition, fingerVectorRadix); + + packetStream >> twoByteHolder; + unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast(&twoByteHolder), + handNormal, fingerVectorRadix); + + quint8 numFingers; + packetStream >> numFingers; palm.setRawPosition(handPosition); palm.setRawNormal(handNormal); @@ -186,8 +196,14 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) { glm::vec3 tipPosition; glm::vec3 rootPosition; - sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, tipPosition, fingerVectorRadix); - sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, rootPosition, fingerVectorRadix); + + packetStream >> twoByteHolder; + unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast(&twoByteHolder), tipPosition, + fingerVectorRadix); + + packetStream >> twoByteHolder; + unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast(&twoByteHolder), rootPosition, + fingerVectorRadix); finger.setRawTipPosition(tipPosition); finger.setRawRootPosition(rootPosition); @@ -207,11 +223,14 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) { } // One byte for error checking safety. - unsigned char requiredLength = (unsigned char)(sourceBuffer - startPosition); - unsigned char checkLength = *sourceBuffer++; + unsigned char requiredLength = packetStream.device()->pos(); + + unsigned char checkLength; + packetStream >> checkLength; + assert(checkLength == requiredLength); - return sourceBuffer - startPosition; + return packetStream.device()->pos(); } void HandData::setFingerTrailLength(unsigned int length) { diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 0f1e393db9..5c0a01540d 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 diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index 29b8c57168..1ea76d168f 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; diff --git a/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp index e537838649..fd5e9f1749 100644 --- a/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp +++ b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp @@ -39,25 +39,26 @@ 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)))); @@ -69,26 +70,26 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA 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(); _myServer->getOctree()->lockForWrite(); uint64_t 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(); @@ -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,7 +126,7 @@ 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); } } diff --git a/libraries/octree-server/src/OctreeInboundPacketProcessor.h b/libraries/octree-server/src/OctreeInboundPacketProcessor.h index 51214b2617..a4d40e8ae1 100644 --- a/libraries/octree-server/src/OctreeInboundPacketProcessor.h +++ b/libraries/octree-server/src/OctreeInboundPacketProcessor.h @@ -63,7 +63,7 @@ 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, diff --git a/libraries/octree-server/src/OctreeQueryNode.cpp b/libraries/octree-server/src/OctreeQueryNode.cpp index f220180980..e454658f07 100644 --- a/libraries/octree-server/src/OctreeQueryNode.cpp +++ b/libraries/octree-server/src/OctreeQueryNode.cpp @@ -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; diff --git a/libraries/octree-server/src/OctreeQueryNode.h b/libraries/octree-server/src/OctreeQueryNode.h index 6fc3c1a07b..c151bb0697 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 diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp index a12a633e58..7c63d4b0cd 100644 --- a/libraries/octree-server/src/OctreeSendThread.cpp +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -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); diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index 438f71dd16..9a3ce9aebc 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), @@ -432,7 +432,7 @@ void OctreeServer::setArguments(int argc, char** argv) { void OctreeServer::parsePayload() { - if (getNumPayloadBytes() > 0) { + if (getPayload().size() > 0) { QString config((const char*) _payload); // Now, parse the config @@ -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 NODE_TYPE_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(); } diff --git a/libraries/octree-server/src/OctreeServer.h b/libraries/octree-server/src/OctreeServer.h index 86ad134df0..b17beb4a7e 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 @@ -51,7 +51,7 @@ public: 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; diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index e761a15e76..e28fb92cdb 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -39,7 +39,7 @@ 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(); @@ -48,7 +48,7 @@ bool JurisdictionListener::queueJurisdictionRequest() { 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 +63,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..d4cd396a48 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 { @@ -43,13 +43,13 @@ public slots: 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; diff --git a/libraries/octree/src/JurisdictionMap.cpp b/libraries/octree/src/JurisdictionMap.cpp index bf64e19959..df980917e8 100644 --- a/libraries/octree/src/JurisdictionMap.cpp +++ b/libraries/octree/src/JurisdictionMap.cpp @@ -265,7 +265,7 @@ bool JurisdictionMap::writeToFile(const char* filename) { int JurisdictionMap::packEmptyJurisdictionIntoMessage(NODE_TYPE 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,7 +283,7 @@ 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 @@ -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..5380a0167a 100644 --- a/libraries/octree/src/JurisdictionMap.h +++ b/libraries/octree/src/JurisdictionMap.h @@ -16,7 +16,7 @@ #include #include -#include +#include class JurisdictionMap { public: @@ -56,7 +56,7 @@ 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 diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp index e1bbf78506..f3ee5def2e 100644 --- a/libraries/octree/src/JurisdictionSender.cpp +++ b/libraries/octree/src/JurisdictionSender.cpp @@ -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(); @@ -66,7 +66,7 @@ bool JurisdictionSender::process() { if (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..7c8f288728 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 { @@ -37,7 +37,7 @@ public: void setNodeType(NODE_TYPE 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(); } diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 5d25f0c164..d204d6487c 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -443,7 +443,7 @@ 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)))); @@ -1322,13 +1322,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 +1368,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..8ca7dec0e6 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -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 diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index d4c1051747..b8f38e3b0b 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,12 +93,12 @@ 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(); @@ -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(uint64_t); 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,18 +289,18 @@ 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 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/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 87b55ace96..1a2b160c81 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -28,7 +28,9 @@ typedef uint16_t OCTREE_PACKET_SEQUENCE; typedef uint64_t 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) + +// 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 = (sizeof(PacketType) + sizeof(PacketVersion) + 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); diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index ec474beb3d..51b73a9ed2 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -95,13 +95,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; + const unsigned char* startPosition = reinterpret_cast(packet.data()); + const unsigned char* sourceBuffer = startPosition + numBytesPacketHeader; // push past the node session UUID sourceBuffer += NUM_BYTES_RFC4122_UUID; diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index c306c5a851..98e27e48bd 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -55,7 +55,7 @@ public: virtual ~OctreeQuery(); int getBroadcastData(unsigned char* destinationBuffer); - int parseData(unsigned char* sourceBuffer, int numBytes); + int parseData(const QByteArray& packet); QUuid& getUUID() { return _uuid; } void setUUID(const QUuid& uuid) { _uuid = uuid; } diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 7bbeb065d6..e53bd1a606 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -36,14 +36,14 @@ 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); - 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..c1d3e85aef 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" @@ -38,8 +38,8 @@ public: virtual Octree* createTree() = 0; virtual NODE_TYPE getMyNodeType() const = 0; - virtual PACKET_TYPE getMyQueryMessageType() const = 0; - virtual PACKET_TYPE getExpectedPacketType() const = 0; + virtual PacketType getMyQueryMessageType() const = 0; + virtual PacketType getExpectedPacketType() const = 0; virtual void renderElement(OctreeElement* element, RenderArgs* args) = 0; /// process incoming data diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 35fe8a5d16..6a57fbf303 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)); @@ -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..350470b9c1 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; } diff --git a/libraries/particle-server/src/ParticleNodeData.h b/libraries/particle-server/src/ParticleNodeData.h index 1758b2e9a3..da5a9b0b71 100644 --- a/libraries/particle-server/src/ParticleNodeData.h +++ b/libraries/particle-server/src/ParticleNodeData.h @@ -19,7 +19,7 @@ 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; } diff --git a/libraries/particle-server/src/ParticleServer.cpp b/libraries/particle-server/src/ParticleServer.cpp index 6df12e5ff2..94bc3c3a16 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; @@ -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(), diff --git a/libraries/particle-server/src/ParticleServer.h b/libraries/particle-server/src/ParticleServer.h index 55e1b77285..4420edf947 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 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 5cf5e9248d..aef62ccf07 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -45,9 +45,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; @@ -64,8 +64,6 @@ void Particle::handleAddParticleResponse(unsigned char* packetData , int packetL _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) { @@ -292,12 +290,12 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef } -Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree) { +Particle Particle::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree) { //qDebug() << "Particle::fromEditPacket() length=" << length; 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... @@ -471,7 +469,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 diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index e534c7b418..fab528595d 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -161,8 +161,8 @@ public: 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); - /// 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); + /// creates an NEW particle from an PacketType_PARTICLE_ADD_OR_EDIT edit data buffer + static Particle fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree); virtual ~Particle(); virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, @@ -235,7 +235,7 @@ public: 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); @@ -262,7 +262,7 @@ 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; diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 7e8866e7d4..e9484e99d9 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -122,7 +122,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 +130,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(); @@ -264,7 +264,7 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, float elast 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); + _packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, particleID, properties); // change the local particle too... particle->setPosition(position); 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..f406ba625a 100644 --- a/libraries/particles/src/ParticleEditPacketSender.h +++ b/libraries/particles/src/ParticleEditPacketSender.h @@ -20,13 +20,13 @@ 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; } diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index cd59e0e3a7..0c90a03573 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 { @@ -207,29 +208,30 @@ 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: { - //qDebug() << " got PACKET_TYPE_PARTICLE_ADD_OR_EDIT... "; + case PacketTypeParticleAddOrEdit: { + //qDebug() << " got PacketType_PARTICLE_ADD_OR_EDIT... "; Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this); storeParticle(newParticle, senderNode); if (newParticle.isNewlyCreated()) { notifyNewlyCreatedParticle(newParticle, senderNode); } - //qDebug() << " DONE... PACKET_TYPE_PARTICLE_ADD_OR_EDIT... "; - } break; + //qDebug() << " DONE... PacketType_PARTICLE_ADD_OR_EDIT... "; + return processedBytes; + } - // 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: { - processedBytes = 0; - } break; + // TODO: wire in support here for server to get PacketType_PARTICLE_ERASE messages + // instead of using PacketType_PARTICLE_ADD_OR_EDIT messages to delete particles + case PacketTypeParticleErase: + return 0; + default: + return 0; } - return processedBytes; } void ParticleTree::notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode) { @@ -331,7 +333,7 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha 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; @@ -424,7 +426,7 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi 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; diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 2b7efbbe78..c89bd01b3b 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -32,10 +32,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 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(); diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index 14388b6ac4..987fdf789c 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,7 +29,7 @@ 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); return id; } @@ -70,13 +70,13 @@ void ParticlesScriptingInterface::editParticle(ParticleID particleID, const Part qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getInHand()=" << properties.getInHand(); } } - queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + queueParticleMessage(PacketTypeParticleAddOrEdit, particleID, properties); } -// 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) { @@ -99,7 +99,7 @@ void ParticlesScriptingInterface::deleteParticle(ParticleID particleID) { particleID.isKnownID = true; //qDebug() << "ParticlesScriptingInterface::deleteParticle(), queueParticleMessage......"; - queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + queueParticleMessage(PacketTypeParticleAddOrEdit, particleID, properties); } ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& center, float radius) const { diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h index d61f8289f4..624ce8a7b1 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.h +++ b/libraries/particles/src/ParticlesScriptingInterface.h @@ -34,7 +34,7 @@ public slots: ParticleID findClosestParticle(const glm::vec3& center, float radius) const; 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/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f77b428764..f511299fcd 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -236,21 +236,19 @@ 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, QSet() << NODE_TYPE_AVATAR_MIXER); } if (willSendVisualDataCallBack) { diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 8e67e7a178..0040fd47a3 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -48,19 +48,18 @@ Assignment::Assignment() : _type(Assignment::AllTypes), _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) : _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 @@ -70,45 +69,33 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const setPool(pool); } -Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) : +Assignment::Assignment(const QByteArray& packet) : _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); + packetStream.readRawData(reinterpret_cast(&_type), 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; + packetStream >> _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++; - } + packetStream >> _pool; - if (numBytes > numBytesRead) { - setPayload(dataBuffer + numBytesRead, numBytes - numBytesRead); + if (!packetStream.atEnd()) { + _payload = packet.mid(packetStream.device()->pos()); } } @@ -124,10 +111,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 +129,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 +154,21 @@ const char* Assignment::getTypeName() const { } } -int Assignment::packToBuffer(unsigned char* buffer) { - int numPackedBytes = 0; - - 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 (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; -} - 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 << (char) assignment._type; + + // pack the UUID for this assignment, if this is an assignment create or deploy + if (assignment._command != Assignment::RequestCommand) { + out << assignment._uuid; + } + + out << assignment._pool << assignment._payload; + + return out; +} diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index 3f73748251..2cf026682a 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -22,6 +22,8 @@ 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 { Q_OBJECT @@ -53,7 +55,7 @@ public: 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 +65,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 +75,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; } - 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,27 +87,20 @@ 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); - friend QDataStream& operator>>(QDataStream &in, Assignment& assignment); 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/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/HifiSockAddr.cpp b/libraries/shared/src/HifiSockAddr.cpp index 4c9772b1c8..a658c75a44 100644 --- a/libraries/shared/src/HifiSockAddr.cpp +++ b/libraries/shared/src/HifiSockAddr.cpp @@ -59,31 +59,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..37628fb649 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -16,7 +16,6 @@ #endif #include "Node.h" -#include "NodeTypes.h" #include "SharedUtil.h" #include @@ -135,6 +134,24 @@ 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 << " " << node.getUUID().toString().toLocal8Bit().constData() << " "; diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h index 352498cd61..eacbc93892 100644 --- a/libraries/shared/src/Node.h +++ b/libraries/shared/src/Node.h @@ -27,6 +27,18 @@ #include "NodeData.h" #include "SimpleMovingAverage.h" +typedef quint8 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; + class Node : public QObject { Q_OBJECT public: @@ -75,13 +87,16 @@ 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; + NODE_TYPE _type; QUuid _uuid; uint64_t _wakeMicrostamp; uint64_t _lastHeardMicrostamp; 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..4d43159f52 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -17,7 +17,6 @@ #include "HifiSockAddr.h" #include "Logging.h" #include "NodeList.h" -#include "NodeTypes.h" #include "PacketHeaders.h" #include "SharedUtil.h" #include "UUID.h" @@ -109,79 +108,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.device()->seek(numBytesForPacketHeader(packet)); + + qint64 ourOriginalTime, othersReplyTime; + + packetStream >> ourOriginalTime >> othersReplyTime; + + qint64 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; + + 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 +198,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 @@ -323,7 +326,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 +336,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 +350,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 +368,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 +392,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 +419,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 +460,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()) + << (char) _nodeTypesOfInterest.size(); + // copy over the bytes for node types of interest, if required foreach (NODE_TYPE nodeTypeOfInterest, _nodeTypesOfInterest) { - *(packetPosition++) = nodeTypeOfInterest; + 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 +489,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.atEnd()) { + 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 +518,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,9 +538,7 @@ 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) { @@ -582,43 +547,36 @@ int NodeList::packOwnerUUID(unsigned char* packetData) { return rfcUUID.size(); } -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::constructPingPacket() { + QByteArray pingPacket = byteArrayWithPopluatedHeader(PacketTypePing); + + QDataStream packetStream(&pingPacket, QIODevice::Append); + packetStream << usecTimestampNow(); + + return pingPacket; } -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; +QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) { + QByteArray replyPacket; + + uint64_t timeFromOriginalPing; + memcpy(&timeFromOriginalPing, pingPacket.data() + numBytesForPacketHeader(pingPacket), sizeof(timeFromOriginalPing)); + + QDataStream packetStream(replyPacket); + + packetStream.device()->seek(populatePacketHeader(replyPacket, PacketTypePingReply)); + + packetStream << timeFromOriginalPing << usecTimestampNow(); + + return replyPacket; } - 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, @@ -669,7 +627,7 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, } } -unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes, +unsigned NodeList::broadcastToNodes(const QByteArray& packet, const QSet& destinationNodeTypes) { unsigned n = 0; @@ -678,8 +636,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..b987cd0e65 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -29,7 +29,6 @@ #include #include "Node.h" -#include "NodeTypes.h" const int MAX_NUM_NODES = 10000; const int NODES_PER_BUCKET = 100; @@ -94,15 +93,15 @@ public: void addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd); void addSetOfNodeTypesToNodeInterestSet(const QSet& 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 +109,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 QSet& destinationNodeTypes); SharedNodePointer soloNodeOfType(char nodeType); void loadData(QSettings* settings); @@ -141,7 +139,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); @@ -160,7 +158,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..529f2639d7 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -6,105 +6,117 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // -#include +#include #include +#include "NodeList.h" +#include "UUID.h" + #include "PacketHeaders.h" -PACKET_VERSION versionForPacketType(PACKET_TYPE type) { +int arithmeticCodingValueFromBuffer(const char* checkValue) { + if (((int) *checkValue) < 255) { + return *checkValue; + } else { + return 255 + arithmeticCodingValueFromBuffer(checkValue + 1); + } +} + +int numBytesArithmeticCodingFromBuffer(const char* checkValue) { + if (((int) *checkValue) < 255) { + return *checkValue; + } else { + return 1 + numBytesArithmeticCodingFromBuffer(checkValue + 1); + } +} + +int packArithmeticallyCodedValue(int value, char* destination) { + if (value < 255) { + // less than 255, just pack our value + destination[0] = (char) 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; + populatePacketHeader(freshByteArray, type, connectionUUID); + return freshByteArray; +} + +int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID) { + 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, 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)) || packet[0] == 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) + 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(arithmeticCodingValueFromBuffer(packet.data()) + sizeof(PacketVersion))); +} + +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..e194406d58 100644 --- a/libraries/shared/src/PacketHeaders.h +++ b/libraries/shared/src/PacketHeaders.h @@ -12,61 +12,67 @@ #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'; +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 +}; -typedef char PACKET_VERSION; +typedef char PacketVersion; -PACKET_VERSION versionForPacketType(PACKET_TYPE type); +PacketVersion versionForPacketType(PacketType type); -bool packetVersionMatch(unsigned char* packetHeader, const HifiSockAddr& senderSockAddr); +const QUuid nullUUID = QUuid(); -int populateTypeAndVersion(unsigned char* destinationHeader, PACKET_TYPE type); -int numBytesForPacketHeader(const unsigned char* packetHeader); +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); -const int MAX_PACKET_HEADER_BYTES = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION); +bool packetVersionMatch(const QByteArray& packet); -// These are supported Z-Command -#define ERASE_ALL_COMMAND "erase all" -#define ADD_SCENE_COMMAND "add scene" -#define TEST_COMMAND "a message" +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..0cc37f356e 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -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) { @@ -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..415e084141 100644 --- a/libraries/shared/src/PacketSender.h +++ b/libraries/shared/src/PacketSender.h @@ -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; } diff --git a/libraries/shared/src/ReceivedPacketProcessor.cpp b/libraries/shared/src/ReceivedPacketProcessor.cpp index d61db2b184..a34e41006e 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(); } @@ -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/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 9433fe1236..9430fcde20 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -231,105 +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 +473,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 +491,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 +508,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 +538,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 +569,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 +591,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; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index a77663bbda..6498962fad 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]; @@ -107,13 +109,6 @@ struct VoxelDetail { 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); @@ -147,33 +142,33 @@ 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 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); #endif /* defined(__hifi__SharedUtil__) */ diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index 7c7e07863a..148f06ec85 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) { diff --git a/libraries/shared/src/ThreadedAssignment.h b/libraries/shared/src/ThreadedAssignment.h index 301a81bcf3..1f59aa4623 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: 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..481a6c436b 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(); diff --git a/libraries/voxel-server/src/VoxelServer.h b/libraries/voxel-server/src/VoxelServer.h index 06cea132c0..9e4a1d23df 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; } @@ -37,7 +37,7 @@ public: 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 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/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index af3ae63377..c0477b683b 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 + 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; +} -void VoxelEditPacketSender::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail) { +/// 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..d99c176d92 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -18,19 +18,19 @@ 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 diff --git a/libraries/voxels/src/VoxelPacketData.h b/libraries/voxels/src/VoxelPacketData.h index 200bd7d30c..5cdc314503 100644 --- a/libraries/voxels/src/VoxelPacketData.h +++ b/libraries/voxels/src/VoxelPacketData.h @@ -31,14 +31,16 @@ typedef uint16_t VOXEL_PACKET_SEQUENCE; typedef uint64_t 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) + +// this is overly conservative - uses 8 bytes for PacketType which could be as compact as a single byte +const int VOXEL_PACKET_HEADER_SIZE = (sizeof(PacketType) + sizeof(PacketVersion) + 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/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..bef3b9782e 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -54,7 +54,7 @@ public slots: void eraseVoxel(float x, float y, float z, float scale); private: - void queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails); + void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails); }; class VoxelDetailScriptObject : public QObject { From 3854f75abd32f719da4104849be1215cf6998b1c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 10:41:40 -0800 Subject: [PATCH 033/153] cleanup some UUID packing --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 4 ++-- libraries/shared/src/NodeList.cpp | 6 ------ libraries/shared/src/NodeList.h | 2 -- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index fa515a5bf7..5434966c00 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -38,8 +38,8 @@ PositionalAudioRingBuffer::~PositionalAudioRingBuffer() { int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { QDataStream packetStream(packet); - // skip the source UUID - packetStream.skipRawData(NUM_BYTES_RFC4122_UUID); + // 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(), diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 4d43159f52..83e8d9824c 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -541,12 +541,6 @@ void NodeList::sendAssignment(Assignment& assignment) { _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); diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index b987cd0e65..3f811dbad4 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -97,8 +97,6 @@ public: void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); - - int packOwnerUUID(unsigned char* packetData); QByteArray constructPingPacket(); QByteArray constructPingReplyPacket(const QByteArray& pingPacket); From 85ebad979f08cdb2ccb8fdaa9a1bb3d0f3f3689e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 11:15:38 -0800 Subject: [PATCH 034/153] repair assignment flow errors from packet changes --- assignment-client/src/AssignmentClient.cpp | 56 +++++++++++---------- assignment-client/src/AssignmentFactory.cpp | 8 +-- domain-server/src/DomainServer.cpp | 1 + libraries/shared/src/Assignment.cpp | 9 ++-- libraries/shared/src/PacketHeaders.cpp | 18 ++++--- 5 files changed, 54 insertions(+), 38 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 998ff46217..8725c6ca56 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -124,32 +124,36 @@ void AssignmentClient::readPendingDatagrams() { // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket); - 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()); - - qDebug() << "Destination IP for assignment is" << nodeList->getDomainIP().toString(); - - // start the deployed assignment - QThread* workerThread = new QThread(this); - - connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); - - connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); - connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); - connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); - connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); - - _currentAssignment->moveToThread(workerThread); - - // move the NodeList to the thread used for the _current assignment - nodeList->moveToThread(workerThread); - - // Starts an event loop, and emits workerThread->started() - workerThread->start(); + 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()); + + qDebug() << "Destination IP for assignment is" << nodeList->getDomainIP().toString(); + + // start the deployed assignment + QThread* workerThread = new QThread(this); + + connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); + + connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); + connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); + connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); + connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); + + _currentAssignment->moveToThread(workerThread); + + // move the NodeList to the thread used for the _current assignment + nodeList->moveToThread(workerThread); + + // Starts an event loop, and emits workerThread->started() + workerThread->start(); + } else { + qDebug() << "Received an assignment that could not be unpacked. Re-requesting."; + } } } else { // have the NodeList attempt to handle it diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index 0b8e089c01..ef587c9b6d 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -21,10 +21,12 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) { int headerBytes = numBytesForPacketHeader(packet); - Assignment::Type assignmentType = Assignment::AllTypes; - memcpy(&assignmentType, packet.data() + headerBytes, sizeof(Assignment::Type)); + quint8 packedType; + memcpy(&packedType, packet.data() + headerBytes, sizeof(packedType)); - switch (assignmentType) { + Assignment::Type unpackedType = (Assignment::Type) packedType; + + switch (unpackedType) { case Assignment::AudioMixerType: return new AudioMixer(packet); case Assignment::AvatarMixerType: diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 073f788f86..665ef45eb1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -222,6 +222,7 @@ void DomainServer::readAvailableDatagrams() { if (assignmentToDeploy) { // 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; diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 0040fd47a3..ca6a6adca7 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -70,6 +70,7 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const } Assignment::Assignment(const QByteArray& packet) : + _pool(), _location(GlobalLocation), _numberOfInstances(1), _payload() @@ -85,7 +86,9 @@ Assignment::Assignment(const QByteArray& packet) : QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); - packetStream.readRawData(reinterpret_cast(&_type), sizeof(Assignment::Type)); + uchar assignmentType; + packetStream >> assignmentType; + _type = (Assignment::Type) assignmentType; if (_command != Assignment::RequestCommand) { // read the GUID for this assignment @@ -155,13 +158,13 @@ const char* Assignment::getTypeName() const { } QDebug operator<<(QDebug debug, const Assignment &assignment) { - debug.nospace() << "UUID: " << assignment.getUUID().toString().toStdString().c_str() << + debug.nospace() << "UUID: " << qPrintable(assignment.getUUID().toString()) << ", Type: " << assignment.getType(); return debug.nospace(); } QDataStream& operator<<(QDataStream &out, const Assignment& assignment) { - out << (char) assignment._type; + out << (quint8) assignment._type; // pack the UUID for this assignment, if this is an assignment create or deploy if (assignment._command != Assignment::RequestCommand) { diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 529f2639d7..fc9511622f 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -16,7 +16,7 @@ #include "PacketHeaders.h" int arithmeticCodingValueFromBuffer(const char* checkValue) { - if (((int) *checkValue) < 255) { + if (((uchar) *checkValue) < 255) { return *checkValue; } else { return 255 + arithmeticCodingValueFromBuffer(checkValue + 1); @@ -24,8 +24,8 @@ int arithmeticCodingValueFromBuffer(const char* checkValue) { } int numBytesArithmeticCodingFromBuffer(const char* checkValue) { - if (((int) *checkValue) < 255) { - return *checkValue; + if (((uchar) *checkValue) < 255) { + return 1; } else { return 1 + numBytesArithmeticCodingFromBuffer(checkValue + 1); } @@ -50,13 +50,19 @@ PacketVersion versionForPacketType(PacketType type) { } } +const int MAX_HEADER_BYTES = sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; + QByteArray byteArrayWithPopluatedHeader(PacketType type, const QUuid& connectionUUID) { - QByteArray freshByteArray; - populatePacketHeader(freshByteArray, type, connectionUUID); + QByteArray freshByteArray(MAX_HEADER_BYTES, 0); + freshByteArray.resize(populatePacketHeader(freshByteArray, type, connectionUUID)); return freshByteArray; } int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID) { + if (packet.size() < MAX_HEADER_BYTES) { + packet.resize(numBytesForPacketHeaderGivenPacketType(type)); + } + return populatePacketHeader(packet.data(), type, connectionUUID); } @@ -110,7 +116,7 @@ int numBytesForPacketHeaderGivenPacketType(PacketType type) { } void deconstructPacketHeader(const QByteArray& packet, QUuid& senderUUID) { - senderUUID = QUuid::fromRfc4122(packet.mid(arithmeticCodingValueFromBuffer(packet.data()) + sizeof(PacketVersion))); + senderUUID = QUuid::fromRfc4122(packet.mid(numBytesArithmeticCodingFromBuffer(packet.data()) + sizeof(PacketVersion))); } PacketType packetTypeForPacket(const QByteArray& packet) { From 427abd450963a3df7b604f73f9e0776215d6cf6b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 11:18:37 -0800 Subject: [PATCH 035/153] add model properties to particles --- libraries/octree/src/OctreePacketData.cpp | 13 + libraries/octree/src/OctreePacketData.h | 3 + libraries/particles/src/Particle.cpp | 446 +++++++++++++++------- libraries/particles/src/Particle.h | 6 - libraries/shared/src/PacketHeaders.cpp | 4 +- libraries/shared/src/SharedUtil.cpp | 2 +- libraries/shared/src/SharedUtil.h | 4 +- 7 files changed, 329 insertions(+), 149 deletions(-) diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 9e7d88416b..59216918b1 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -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) { diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 87b55ace96..27f2ee5090 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -130,6 +130,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); diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index cb3d70ac27..2dbf59efbe 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -65,13 +65,6 @@ void Particle::handleAddParticleResponse(unsigned char* packetData , int packetL } - -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), @@ -182,7 +175,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); @@ -190,6 +182,28 @@ 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; } @@ -210,21 +224,6 @@ int Particle::expectedBytes() { return expectedBytes; } -int Particle::expectedEditMessageBytes() { - int expectedBytes = sizeof(uint32_t) // id - + 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::readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { int bytesRead = 0; if (bytesLeftToRead >= expectedBytes()) { @@ -311,6 +310,31 @@ 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; @@ -466,6 +490,39 @@ 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()..."); @@ -512,138 +569,166 @@ 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 - uint64_t lastEdited = properties.getLastEdited(); - memcpy(copyAt, &lastEdited, sizeof(lastEdited)); - copyAt += sizeof(lastEdited); - sizeOut += sizeof(lastEdited); + // lastEdited + uint64_t 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); - } + // 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); + } - // 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); - } + // 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); + } - // 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); - } + // 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); + } - // 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); - } + // 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); + } - // 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); - } + // 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); + } - // 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); - } + // 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); + } - // damping - if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { - float damping = properties.getDamping(); - memcpy(copyAt, &damping, sizeof(damping)); - copyAt += sizeof(damping); - sizeOut += sizeof(damping); - } + // damping + if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { + float damping = properties.getDamping(); + memcpy(copyAt, &damping, sizeof(damping)); + copyAt += sizeof(damping); + sizeOut += sizeof(damping); + } - // lifetime - if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { - float lifetime = properties.getLifetime(); - memcpy(copyAt, &lifetime, sizeof(lifetime)); - copyAt += sizeof(lifetime); - sizeOut += sizeof(lifetime); - } + // lifetime + if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { + float lifetime = properties.getLifetime(); + memcpy(copyAt, &lifetime, sizeof(lifetime)); + copyAt += sizeof(lifetime); + sizeOut += sizeof(lifetime); + } - // inHand - if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { - bool inHand = properties.getInHand(); - memcpy(copyAt, &inHand, sizeof(inHand)); - copyAt += sizeof(inHand); - sizeOut += sizeof(inHand); - } + // inHand + if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { + bool inHand = properties.getInHand(); + memcpy(copyAt, &inHand, sizeof(inHand)); + copyAt += sizeof(inHand); + sizeOut += sizeof(inHand); + } - // shoulDie - if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { - bool shouldDie = properties.getShouldDie(); - memcpy(copyAt, &shouldDie, sizeof(shouldDie)); - copyAt += sizeof(shouldDie); - sizeOut += sizeof(shouldDie); - } + // shoulDie + if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { + bool shouldDie = properties.getShouldDie(); + memcpy(copyAt, &shouldDie, sizeof(shouldDie)); + copyAt += sizeof(shouldDie); + sizeOut += sizeof(shouldDie); + } - // 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; - } + // 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; + } - bool wantDebugging = false; - if (wantDebugging) { - printf("encodeParticleEditMessageDetails()....\n"); - printf("Particle id :%u\n", id.id); - printf(" nextID:%u\n", _nextID); - } + // 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; + } + + // 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); + } + + // 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 @@ -1121,6 +1206,63 @@ 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(); } @@ -1175,6 +1317,26 @@ void ParticleProperties::copyToParticle(Particle& particle) const { 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; @@ -1199,6 +1361,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; @@ -1213,6 +1379,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 535d77d6af..fa9ea14d0e 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -192,11 +192,6 @@ public: Particle(const ParticleID& particleID, const ParticleProperties& properties); - /// 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); - /// 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); @@ -286,7 +281,6 @@ public: 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, unsigned char* bufferOut, int sizeIn, int& sizeOut); diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 24a34f618e..0f81110d01 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -45,10 +45,10 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { return 1; case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: - return 4; + return 5; case PACKET_TYPE_PARTICLE_DATA: - return 8; + return 9; case PACKET_TYPE_PING_REPLY: return 1; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 9433fe1236..00e11e2b19 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -607,7 +607,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)); diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index a77663bbda..174742fcf3 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -152,7 +152,7 @@ int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPo // 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 @@ -172,7 +172,7 @@ int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy); int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix); int unpackFloatScalarFromSignedTwoByteFixed(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); From bd0631838dfeb5f5e8086cfbab4fcc7b9ec3fee0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 11:19:04 -0800 Subject: [PATCH 036/153] example of setting particle model with JS --- examples/particleModelExample.js | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 examples/particleModelExample.js diff --git a/examples/particleModelExample.js b/examples/particleModelExample.js new file mode 100644 index 0000000000..cc4bc517fb --- /dev/null +++ b/examples/particleModelExample.js @@ -0,0 +1,49 @@ +// +// 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: 0, y: 1, z: 0 }, + velocity: { x: 0.5, y: 0, z: 0.5 }, + gravity: { x: 0, y: 0, z: 0 }, + damping: 0, + radius : 0.1, + modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX", +}; + +var ballProperties = { + position: { x: 0, y: 0.75, z: 0 }, + velocity: { x: 0.5, y: 0, z: 0.5 }, + gravity: { x: 0, y: 0, z: 0 }, + damping: 0, + radius : 0.1, + color: { red: 255, green: 0, blue: 0 }, +}; + +var modelParticleID = Particles.addParticle(modelProperties); +var ballParticleID = Particles.addParticle(ballProperties); + +function endAfterAWhile() { + // stop it... + if (count >= stopAfter) { + print("calling Agent.stop()"); + Agent.stop(); + } + + print("count =" + count); + count++; +} + + +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(endAfterAWhile); + From 75e2fa491a08ed2110a8ef6b78e4844a5329f6f5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 11:26:36 -0800 Subject: [PATCH 037/153] some type fixes for marshalling/un-marshalling of data --- domain-server/src/DomainServer.cpp | 1 - libraries/shared/src/NodeList.cpp | 4 ++-- libraries/shared/src/NodeList.h | 2 +- libraries/shared/src/PacketHeaders.cpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 665ef45eb1..62ee855ddb 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -128,7 +128,6 @@ void DomainServer::readAvailableDatagrams() { deconstructPacketHeader(receivedPacket, nodeUUID); packetStream >> nodeType; - packetStream >> nodePublicAddress >> nodeLocalAddress; if (nodePublicAddress.getAddress().isNull()) { diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 83e8d9824c..527840e0cd 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -464,11 +464,11 @@ void NodeList::sendDomainServerCheckIn() { // check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination 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()) - << (char) _nodeTypesOfInterest.size(); + << (quint8) _nodeTypesOfInterest.size(); // copy over the bytes for node types of interest, if required foreach (NODE_TYPE nodeTypeOfInterest, _nodeTypesOfInterest) { diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 3f811dbad4..f978c5479e 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -146,7 +146,7 @@ private: QString _domainHostname; HifiSockAddr _domainSockAddr; QUdpSocket _nodeSocket; - char _ownerType; + NODE_TYPE _ownerType; QSet _nodeTypesOfInterest; QUuid _ownerUUID; int _numNoReplyDomainCheckIns; diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index fc9511622f..a725198748 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -34,7 +34,7 @@ int numBytesArithmeticCodingFromBuffer(const char* checkValue) { int packArithmeticallyCodedValue(int value, char* destination) { if (value < 255) { // less than 255, just pack our value - destination[0] = (char) value; + destination[0] = (uchar) value; return 1; } else { // pack 255 and then recursively pack on From dc258684033b230c675a2bf98c481feeb45d7779 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 11:26:55 -0800 Subject: [PATCH 038/153] cleanup --- interface/src/ParticleTreeRenderer.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/interface/src/ParticleTreeRenderer.cpp b/interface/src/ParticleTreeRenderer.cpp index e0229bddaa..11ba832d07 100644 --- a/interface/src/ParticleTreeRenderer.cpp +++ b/interface/src/ParticleTreeRenderer.cpp @@ -43,19 +43,6 @@ void ParticleTreeRenderer::render() { OctreeRenderer::render(); } -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/lotus.fbx")); -//_testModel->setURL(QUrl("http://www.fungibleinsight.com/faces/tie.fbx")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Angie1.fbx")); -//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/orb_model.fbx")); -//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/space_frigate_6.FBX")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/soccer_ball.fbx")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/top%20fbx.FBX")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/golfball_FBX2010.fbx")); -//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/Combat_tank_V01.FBX")); - -//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/orc.fbx")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX")); - Model* ParticleTreeRenderer::getModel(const QString& url) { Model* model = NULL; @@ -63,9 +50,8 @@ Model* ParticleTreeRenderer::getModel(const QString& url) { if (_particleModels.find(url) == _particleModels.end()) { model = new Model(); model->init(); - qDebug() << "calling model->setURL()"; model->setURL(QUrl(url)); - qDebug() << "after calling setURL()"; + _particleModels[url] = model; } else { model = _particleModels[url]; } From 64afd17d147d8279ffb552b05bf3b587c701b85c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 11:49:11 -0800 Subject: [PATCH 039/153] include particle radius in model scaling --- examples/particleModelExample.js | 10 ++++++---- interface/src/ParticleTreeRenderer.cpp | 14 +++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/particleModelExample.js b/examples/particleModelExample.js index cc4bc517fb..9f19069ee9 100644 --- a/examples/particleModelExample.js +++ b/examples/particleModelExample.js @@ -12,21 +12,23 @@ var count = 0; var stopAfter = 100; var modelProperties = { - position: { x: 0, y: 1, z: 0 }, + 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.1, + radius : 0.25, modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX", + lifetime: 20 }; var ballProperties = { - position: { x: 0, y: 0.75, z: 0 }, + 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.1, + radius : 0.25, color: { red: 255, green: 0, blue: 0 }, + lifetime: 20 }; var modelParticleID = Particles.addParticle(modelProperties); diff --git a/interface/src/ParticleTreeRenderer.cpp b/interface/src/ParticleTreeRenderer.cpp index 11ba832d07..7c82f76aab 100644 --- a/interface/src/ParticleTreeRenderer.cpp +++ b/interface/src/ParticleTreeRenderer.cpp @@ -72,7 +72,7 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg // 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(); @@ -90,24 +90,24 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg glm::vec3 translation(position.x, position.y, position.z); model->setTranslation(translation + translationAdjustment); - // glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f) // set the rotation glm::quat rotation = particle.getModelRotation(); model->setRotation(rotation); // scale - const float MODEL_SCALE = 0.0006f; // need to figure out correct scale adjust + // 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); + model->setScale(scale * MODEL_SCALE * radius * particle.getModelScale()); model->simulate(0.0f); - model->render(alpha); - //qDebug() << "called _testModel->render(alpha);"; + model->render(alpha); // TODO: should we allow particles to have alpha on their models? + glPopMatrix(); } else { glPushMatrix(); glTranslatef(position.x, position.y, position.z); - glutSolidSphere(sphereRadius, 15, 15); + glutSolidSphere(radius, 15, 15); glPopMatrix(); } } From ffefc529f3c5a7d9afec4c8703bc8b81e6a0f5f4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 11:53:09 -0800 Subject: [PATCH 040/153] ensure UUID pulled from packet header is exactly 16 bytes --- domain-server/src/DomainServer.cpp | 1 + libraries/shared/src/NodeList.cpp | 2 +- libraries/shared/src/PacketHeaders.cpp | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 62ee855ddb..5cffc724f3 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -127,6 +127,7 @@ void DomainServer::readAvailableDatagrams() { packetStream.skipRawData(numBytesForPacketHeader(receivedPacket)); deconstructPacketHeader(receivedPacket, nodeUUID); + packetStream >> nodeType; packetStream >> nodePublicAddress >> nodeLocalAddress; diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 527840e0cd..a2ad00125e 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -474,7 +474,7 @@ void NodeList::sendDomainServerCheckIn() { foreach (NODE_TYPE nodeTypeOfInterest, _nodeTypesOfInterest) { packetStream << nodeTypeOfInterest; } - + _nodeSocket.writeDatagram(domainServerPacket, _domainSockAddr.getAddress(), _domainSockAddr.getPort()); const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index a725198748..545c779fea 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -59,7 +59,7 @@ QByteArray byteArrayWithPopluatedHeader(PacketType type, const QUuid& connection } int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID) { - if (packet.size() < MAX_HEADER_BYTES) { + if (packet.size() < numBytesForPacketHeaderGivenPacketType(type)) { packet.resize(numBytesForPacketHeaderGivenPacketType(type)); } @@ -77,7 +77,6 @@ int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionU // return the number of bytes written for pointer pushing return numTypeBytes + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; - } bool packetVersionMatch(const QByteArray& packet) { @@ -112,11 +111,12 @@ int numBytesForPacketHeader(const char* packet) { } int numBytesForPacketHeaderGivenPacketType(PacketType type) { - return (int) ceilf((float)type) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; + return (int) ceilf((float)type / 255) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; } void deconstructPacketHeader(const QByteArray& packet, QUuid& senderUUID) { - senderUUID = QUuid::fromRfc4122(packet.mid(numBytesArithmeticCodingFromBuffer(packet.data()) + sizeof(PacketVersion))); + senderUUID = QUuid::fromRfc4122(packet.mid(numBytesArithmeticCodingFromBuffer(packet.data()) + sizeof(PacketVersion), + NUM_BYTES_RFC4122_UUID)); } PacketType packetTypeForPacket(const QByteArray& packet) { From 3edb81baf451f1f15ba5ef241d995c2cb1944cb3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 28 Jan 2014 12:01:32 -0800 Subject: [PATCH 041/153] Check for texture translucency, render translucent mesh parts after opaque ones, without alpha testing, and with back face culling enabled. --- interface/src/renderer/GeometryCache.cpp | 14 + interface/src/renderer/GeometryCache.h | 4 + interface/src/renderer/Model.cpp | 378 ++++++++++++----------- interface/src/renderer/Model.h | 1 + interface/src/renderer/TextureCache.cpp | 19 +- interface/src/renderer/TextureCache.h | 5 + 6 files changed, 239 insertions(+), 182 deletions(-) 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..c25e513b3b 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 and no depth writes + + 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. From cf05cf6178ae122fa3a6e89d023a025df7255b17 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 28 Jan 2014 12:03:41 -0800 Subject: [PATCH 042/153] Didn't need to disable depth writes after all (for now). --- interface/src/renderer/Model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c25e513b3b..6d61b2df68 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -272,7 +272,7 @@ bool Model::render(float alpha) { glDisable(GL_ALPHA_TEST); - // render translucent meshes afterwards, with back face culling and no depth writes + // render translucent meshes afterwards, with back face culling glEnable(GL_CULL_FACE); From b009cd8e6766302330d87dde2d6749cb603f05ab Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 12:25:44 -0800 Subject: [PATCH 043/153] repair more node communication problems after refactor --- interface/src/DatagramProcessor.cpp | 3 ++- libraries/shared/src/Assignment.cpp | 5 ++--- libraries/shared/src/Node.cpp | 4 ++-- libraries/shared/src/Node.h | 4 ++-- libraries/shared/src/NodeList.cpp | 11 ++++------- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index e2437e4441..3e4347fc98 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -34,7 +34,8 @@ void DatagramProcessor::processDatagrams() { while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams()) { incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); - nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size()); + nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(), + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); _packetCount++; _byteCount += incomingPacket.size(); diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index ca6a6adca7..5432278b17 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -46,11 +46,12 @@ Assignment::Assignment() : _uuid(), _command(Assignment::RequestCommand), _type(Assignment::AllTypes), + _pool(), _location(Assignment::LocalLocation), _numberOfInstances(1), _payload() { - setPool(NULL); + } Assignment::Assignment(Assignment::Command command, Assignment::Type type, const QString& pool, Assignment::Location location) : @@ -65,8 +66,6 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const // this is a newly created assignment, generate a random UUID _uuid = QUuid::createUuid(); } - - setPool(pool); } Assignment::Assignment(const QByteArray& packet) : diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index 37628fb649..18367019ad 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -134,7 +134,7 @@ float Node::getAverageKilobitsPerSecond() { } } -QDataStream& operator>>(QDataStream& out, const Node& node) { +QDataStream& operator<<(QDataStream& out, const Node& node) { out << node._type; out << node._uuid; out << node._publicSocket; @@ -143,7 +143,7 @@ QDataStream& operator>>(QDataStream& out, const Node& node) { return out; } -QDataStream& operator<<(QDataStream& in, Node& node) { +QDataStream& operator>>(QDataStream& in, Node& node) { in >> node._type; in >> node._uuid; in >> node._publicSocket; diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h index eacbc93892..15dfd633b1 100644 --- a/libraries/shared/src/Node.h +++ b/libraries/shared/src/Node.h @@ -88,8 +88,8 @@ public: 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); + 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 diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index a2ad00125e..f2710965f5 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -506,7 +506,7 @@ int NodeList::processDomainServerList(const QByteArray& packet) { QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); - while(!packetStream.atEnd()) { + 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 @@ -551,14 +551,11 @@ QByteArray NodeList::constructPingPacket() { } QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) { - QByteArray replyPacket; - uint64_t timeFromOriginalPing; memcpy(&timeFromOriginalPing, pingPacket.data() + numBytesForPacketHeader(pingPacket), sizeof(timeFromOriginalPing)); - QDataStream packetStream(replyPacket); - - packetStream.device()->seek(populatePacketHeader(replyPacket, PacketTypePingReply)); + QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypePingReply); + QDataStream packetStream(&replyPacket, QIODevice::Append); packetStream << timeFromOriginalPing << usecTimestampNow(); @@ -574,7 +571,7 @@ void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) { } 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); From 3a706a2759e55bb0af1f6a8bd1f1147f28712cca Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 12:31:25 -0800 Subject: [PATCH 044/153] repair the count of avatars and servers --- interface/src/Application.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cda34f727c..4b293f6f46 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3148,10 +3148,8 @@ 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++; - } + int totalAvatars = _avatarManager.size(); + int totalServers = NodeList::getInstance()->size(); if (mirrorEnabled) { horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; From 7b4921e7616c21e8197b996d56ca44d605b3368b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 12:34:08 -0800 Subject: [PATCH 045/153] use quint64 in place of uint64_t for QDataStream --- libraries/shared/src/NodeList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index f2710965f5..5fcd4d4460 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -551,7 +551,7 @@ QByteArray NodeList::constructPingPacket() { } QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) { - uint64_t timeFromOriginalPing; + quint64 timeFromOriginalPing; memcpy(&timeFromOriginalPing, pingPacket.data() + numBytesForPacketHeader(pingPacket), sizeof(timeFromOriginalPing)); QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypePingReply); From cda06d1cb36ffe36866b0e40a86c0dca0530cc84 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 12:35:42 -0800 Subject: [PATCH 046/153] replace uint64_t with quint64 for Qt friendlyness --- animation-server/src/AnimationServer.cpp | 10 +-- domain-server/src/DomainServer.cpp | 2 +- interface/src/Application.cpp | 4 +- interface/src/Application.h | 2 +- interface/src/VoxelHideShowThread.cpp | 16 ++-- interface/src/VoxelSystem.cpp | 20 ++--- interface/src/VoxelSystem.h | 8 +- interface/src/avatar/Profile.cpp | 6 +- interface/src/avatar/Profile.h | 2 +- interface/src/devices/Faceshift.cpp | 2 +- interface/src/devices/Faceshift.h | 2 +- interface/src/devices/SixenseManager.h | 2 +- interface/src/starfield/Config.h | 2 +- libraries/avatars/src/AvatarData.h | 2 +- libraries/avatars/src/HandData.h | 2 +- .../src/OctreeInboundPacketProcessor.cpp | 24 +++--- .../src/OctreeInboundPacketProcessor.h | 52 ++++++------- .../octree-server/src/OctreeQueryNode.cpp | 4 +- libraries/octree-server/src/OctreeQueryNode.h | 8 +- .../octree-server/src/OctreeSendThread.cpp | 34 ++++---- .../octree-server/src/OctreeSendThread.h | 10 +-- libraries/octree-server/src/OctreeServer.cpp | 34 ++++---- libraries/octree-server/src/OctreeServer.h | 4 +- libraries/octree/src/Octree.cpp | 2 +- libraries/octree/src/Octree.h | 6 +- libraries/octree/src/OctreeConstants.h | 2 +- .../octree/src/OctreeEditPacketSender.cpp | 14 ++-- libraries/octree/src/OctreeElement.cpp | 62 +++++++-------- libraries/octree/src/OctreeElement.h | 78 +++++++++---------- libraries/octree/src/OctreePacketData.cpp | 18 ++--- libraries/octree/src/OctreePacketData.h | 30 +++---- libraries/octree/src/OctreePersistThread.cpp | 14 ++-- libraries/octree/src/OctreePersistThread.h | 6 +- libraries/octree/src/OctreeQuery.h | 2 +- libraries/octree/src/OctreeSceneStats.cpp | 2 +- libraries/octree/src/OctreeSceneStats.h | 14 ++-- .../particle-server/src/ParticleNodeData.h | 6 +- .../particle-server/src/ParticleServer.cpp | 10 +-- libraries/particles/src/Particle.cpp | 20 ++--- libraries/particles/src/Particle.h | 16 ++-- libraries/particles/src/ParticleTree.cpp | 18 ++--- libraries/particles/src/ParticleTree.h | 8 +- libraries/shared/src/Node.h | 12 +-- libraries/shared/src/NodeList.cpp | 2 +- libraries/shared/src/NodeList.h | 6 +- libraries/shared/src/PacketSender.cpp | 24 +++--- libraries/shared/src/PacketSender.h | 30 +++---- libraries/shared/src/PerfStat.cpp | 4 +- libraries/shared/src/PerfStat.h | 8 +- libraries/shared/src/Radix2IntegerScanner.h | 2 +- .../shared/src/ReceivedPacketProcessor.cpp | 2 +- libraries/shared/src/SharedUtil.cpp | 4 +- libraries/shared/src/SharedUtil.h | 10 +-- libraries/shared/src/SimpleMovingAverage.h | 2 +- libraries/voxels/src/VoxelConstants.h | 2 +- .../voxels/src/VoxelEditPacketSender.cpp | 4 +- libraries/voxels/src/VoxelPacketData.h | 2 +- 57 files changed, 347 insertions(+), 347 deletions(-) diff --git a/animation-server/src/AnimationServer.cpp b/animation-server/src/AnimationServer.cpp index c436fd1d4b..52c281c0d9 100644 --- a/animation-server/src/AnimationServer.cpp +++ b/animation-server/src/AnimationServer.cpp @@ -591,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; @@ -622,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 @@ -676,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; } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 5cffc724f3..908f0bacdf 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -201,7 +201,7 @@ void DomainServer::readAvailableDatagrams() { } // update last receive to now - uint64_t timeNow = usecTimestampNow(); + quint64 timeNow = usecTimestampNow(); checkInNode->setLastHeardMicrostamp(timeNow); // send the constructed list back to this node diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4b293f6f46..892b97521a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2256,7 +2256,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)) { @@ -3057,7 +3057,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) && diff --git a/interface/src/Application.h b/interface/src/Application.h index 3645355b89..dae7d83da5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -403,7 +403,7 @@ private: int _mouseY; int _mouseDragStartedX; int _mouseDragStartedY; - uint64_t _lastMouseMove; + quint64 _lastMouseMove; bool _mouseHidden; bool _seenMouseMove; 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/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 029690e56a..a663c27ce2 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -638,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)) { @@ -685,7 +685,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { _bufferWriteLock.unlock(); - uint64_t end = usecTimestampNow(); + quint64 end = usecTimestampNow(); int elapsedmsec = (end - start) / 1000; _setupNewVoxelsForDrawingLastFinished = end; _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; @@ -702,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 && @@ -728,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; @@ -737,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 @@ -766,7 +766,7 @@ void VoxelSystem::checkForCulling() { hideOutOfView(forceFullFrustum); if (forceFullFrustum) { - uint64_t endViewCulling = usecTimestampNow(); + quint64 endViewCulling = usecTimestampNow(); _lastViewCullingElapsed = (endViewCulling - start) / 1000; } @@ -775,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)) { diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 915b5a05b4..ca90424b70 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -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/Profile.cpp b/interface/src/avatar/Profile.cpp index 83105f3f41..74f47bd658 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -87,7 +87,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 +115,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/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/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index bc16f2baaa..828f0a4bea 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 diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 5c0a01540d..550c62e829 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -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/octree-server/src/OctreeInboundPacketProcessor.cpp b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp index fd5e9f1749..f9523851b4 100644 --- a/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp +++ b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp @@ -61,12 +61,12 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA 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 @@ -84,19 +84,19 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA 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, 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; @@ -130,8 +130,8 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA } } -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 a4d40e8ae1..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(); @@ -66,17 +66,17 @@ protected: 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 e454658f07..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. @@ -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 c151bb0697..6d4dee300a 100644 --- a/libraries/octree-server/src/OctreeQueryNode.h +++ b/libraries/octree-server/src/OctreeQueryNode.h @@ -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 7c63d4b0cd..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; @@ -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 9a3ce9aebc..9a4b0a51b3 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -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;
 
diff --git a/libraries/octree-server/src/OctreeServer.h b/libraries/octree-server/src/OctreeServer.h
index b17beb4a7e..48a74a5e9e 100644
--- a/libraries/octree-server/src/OctreeServer.h
+++ b/libraries/octree-server/src/OctreeServer.h
@@ -45,7 +45,7 @@ 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;
@@ -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/Octree.cpp b/libraries/octree/src/Octree.cpp
index d204d6487c..5e80fc83ff 100644
--- a/libraries/octree/src/Octree.cpp
+++ b/libraries/octree/src/Octree.cpp
@@ -445,7 +445,7 @@ void Octree::processRemoveOctreeElementsBitstream(const unsigned char* 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);
 
diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h
index 8ca7dec0e6..43a695f2ce 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) :
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 b8f38e3b0b..5b84b819cc 100644
--- a/libraries/octree/src/OctreeEditPacketSender.cpp
+++ b/libraries/octree/src/OctreeEditPacketSender.cpp
@@ -100,9 +100,9 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c
                 if (wantDebugging) {
                     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 <<
@@ -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(reinterpret_cast(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
@@ -307,10 +307,10 @@ void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, Pa
     _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/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..44c302dc59 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);
@@ -334,8 +334,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 1a2b160c81..9ef7399fe0 100644
--- a/libraries/octree/src/OctreePacketData.h
+++ b/libraries/octree/src/OctreePacketData.h
@@ -25,7 +25,7 @@
 
 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;
 
@@ -124,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);
@@ -170,11 +170,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
@@ -210,15 +210,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.h b/libraries/octree/src/OctreeQuery.h
index 98e27e48bd..acb4b7cb32 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 
diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp
index 6a57fbf303..59287e3c5c 100644
--- a/libraries/octree/src/OctreeSceneStats.cpp
+++ b/libraries/octree/src/OctreeSceneStats.cpp
@@ -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: {
diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h
index 350470b9c1..bf65e85ad4 100644
--- a/libraries/octree/src/OctreeSceneStats.h
+++ b/libraries/octree/src/OctreeSceneStats.h
@@ -172,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/particle-server/src/ParticleNodeData.h b/libraries/particle-server/src/ParticleNodeData.h
index da5a9b0b71..4ab16cb33a 100644
--- a/libraries/particle-server/src/ParticleNodeData.h
+++ b/libraries/particle-server/src/ParticleNodeData.h
@@ -21,11 +21,11 @@ public:
 
     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 94bc3c3a16..9344d8c4ae 100644
--- a/libraries/particle-server/src/ParticleServer.cpp
+++ b/libraries/particle-server/src/ParticleServer.cpp
@@ -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;
@@ -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/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp
index 340a560439..3ed551b506 100644
--- a/libraries/particles/src/Particle.cpp
+++ b/libraries/particles/src/Particle.cpp
@@ -89,7 +89,7 @@ void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3
         _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()
@@ -169,8 +169,8 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const {
 int Particle::expectedBytes() {
     int expectedBytes = sizeof(uint32_t) // id
                 + sizeof(float) // age
-                + sizeof(uint64_t) // last updated
-                + sizeof(uint64_t) // lasted edited
+                + sizeof(quint64) // last updated
+                + sizeof(quint64) // lasted edited
                 + sizeof(float) // radius
                 + sizeof(glm::vec3) // position
                 + sizeof(rgbColor) // color
@@ -185,7 +185,7 @@ int Particle::expectedBytes() {
 
 int Particle::expectedEditMessageBytes() {
     int expectedBytes = sizeof(uint32_t) // id
-                + sizeof(uint64_t) // lasted edited
+                + sizeof(quint64) // lasted edited
                 + sizeof(float) // radius
                 + sizeof(glm::vec3) // position
                 + sizeof(rgbColor) // color
@@ -514,7 +514,7 @@ bool Particle::encodeParticleEditMessageDetails(PacketType command, ParticleID i
         }
 
         // lastEdited
-        uint64_t lastEdited = properties.getLastEdited();
+        quint64 lastEdited = properties.getLastEdited();
         memcpy(copyAt, &lastEdited, sizeof(lastEdited));
         copyAt += sizeof(lastEdited);
         sizeOut += sizeof(lastEdited);
@@ -646,9 +646,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) {
@@ -663,14 +663,14 @@ void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssiz
 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
+    const quint64 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);
@@ -799,7 +799,7 @@ void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) {
 
 
 void Particle::setAge(float age) {
-    uint64_t ageInUsecs = age * USECS_PER_SECOND;
+    quint64 ageInUsecs = age * USECS_PER_SECOND;
     _created = usecTimestampNow() - ageInUsecs;
 }
 
diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h
index 6046ca39de..5b30e4f031 100644
--- a/libraries/particles/src/Particle.h
+++ b/libraries/particles/src/Particle.h
@@ -76,7 +76,7 @@ public:
     bool getInHand() const { return _inHand; }
     bool getShouldDie() const { return _shouldDie; }
 
-    uint64_t getLastEdited() const { return _lastEdited; }
+    quint64 getLastEdited() const { return _lastEdited; }
     uint16_t getChangedBits() const;
 
     /// set position in meter units
@@ -112,7 +112,7 @@ private:
 
     uint32_t _id;
     bool _idSet;
-    uint64_t _lastEdited;
+    quint64 _lastEdited;
     bool _positionChanged;
     bool _colorChanged;
     bool _radiusChanged;
@@ -198,10 +198,10 @@ public:
     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; }
 
     /// lifetime of the particle in seconds
     float getAge() const { return static_cast(usecTimestampNow() - _created) / static_cast(USECS_PER_SECOND); }
@@ -247,7 +247,7 @@ public:
 
     static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
 
-    void update(const uint64_t& now);
+    void update(const quint64& now);
     void collisionWithParticle(Particle* other);
     void collisionWithVoxel(VoxelDetail* voxel);
 
@@ -300,11 +300,11 @@ protected:
     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;
diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp
index a23c393f44..933a29775f 100644
--- a/libraries/particles/src/ParticleTree.cpp
+++ b/libraries/particles/src/ParticleTree.cpp
@@ -336,7 +336,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();
@@ -348,12 +348,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) {
@@ -367,7 +367,7 @@ 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;
@@ -386,7 +386,7 @@ 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) {
@@ -431,12 +431,12 @@ 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();
+    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();
@@ -448,7 +448,7 @@ void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) {
     }
 
     // Now run through the keysToRemove and remove them    
-    foreach (uint64_t value, keysToRemove) {
+    foreach (quint64 value, keysToRemove) {
         //qDebug() << "removing the key, _recentlyDeletedParticleIDs.remove(value); time/key:" << value;
         _recentlyDeletedParticleIDs.remove(value);
     }
diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h
index af15666ff7..12ac693f32 100644
--- a/libraries/particles/src/ParticleTree.h
+++ b/libraries/particles/src/ParticleTree.h
@@ -48,9 +48,9 @@ public:
     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);
 
@@ -71,7 +71,7 @@ private:
 
 
     QReadWriteLock _recentlyDeletedParticlesLock;
-    QMultiMap _recentlyDeletedParticleIDs;
+    QMultiMap _recentlyDeletedParticleIDs;
 };
 
 #endif /* defined(__hifi__ParticleTree__) */
diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h
index 15dfd633b1..60d14a9dd3 100644
--- a/libraries/shared/src/Node.h
+++ b/libraries/shared/src/Node.h
@@ -55,11 +55,11 @@ public:
     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);
@@ -98,8 +98,8 @@ private:
 
     NODE_TYPE _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/NodeList.cpp b/libraries/shared/src/NodeList.cpp
index 5fcd4d4460..e26185b8ef 100644
--- a/libraries/shared/src/NodeList.cpp
+++ b/libraries/shared/src/NodeList.cpp
@@ -128,7 +128,7 @@ void NodeList::timePingReply(const QByteArray& packet) {
         
         // 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;
+        quint64 othersExprectedReply = ourOriginalTime + oneWayFlightTime;
         int clockSkew = othersReplyTime - othersExprectedReply;
         
         matchingNode->setPingMs(pingTime / 1000);
diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h
index f978c5479e..69c7303c7b 100644
--- a/libraries/shared/src/NodeList.h
+++ b/libraries/shared/src/NodeList.h
@@ -35,9 +35,9 @@ 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];
 
diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp
index 0cc37f356e..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;
 
@@ -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
diff --git a/libraries/shared/src/PacketSender.h b/libraries/shared/src/PacketSender.h
index 415e084141..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;
 
@@ -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 a34e41006e..5faec02ea1 100644
--- a/libraries/shared/src/ReceivedPacketProcessor.cpp
+++ b/libraries/shared/src/ReceivedPacketProcessor.cpp
@@ -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) {
diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp
index 9430fcde20..44239c7c07 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;
diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h
index 6498962fad..e95aaf980c 100644
--- a/libraries/shared/src/SharedUtil.h
+++ b/libraries/shared/src/SharedUtil.h
@@ -57,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();
diff --git a/libraries/shared/src/SimpleMovingAverage.h b/libraries/shared/src/SimpleMovingAverage.h
index 9788aafe58..2f2b45100c 100644
--- a/libraries/shared/src/SimpleMovingAverage.h
+++ b/libraries/shared/src/SimpleMovingAverage.h
@@ -26,7 +26,7 @@ public:
     float getAverageSampleValuePerSecond();
 private:
     int _numSamples;
-    uint64_t _lastEventTimestamp;
+    quint64 _lastEventTimestamp;
     float _average;
     float _eventDeltaAverage;
     
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/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp
index c0477b683b..3ea4ca3626 100644
--- a/libraries/voxels/src/VoxelEditPacketSender.cpp
+++ b/libraries/voxels/src/VoxelEditPacketSender.cpp
@@ -44,8 +44,8 @@ bool createVoxelEditMessage(PacketType command, short int sequence,
     *sequenceAt = sequence;
     
     // pack in timestamp
-    uint64_t now = usecTimestampNow();
-    uint64_t* timeAt = (uint64_t*)&messageBuffer[numBytesPacketHeader + sizeof(sequence)];
+    quint64 now = usecTimestampNow();
+    quint64* timeAt = (quint64*)&messageBuffer[numBytesPacketHeader + sizeof(sequence)];
     *timeAt = now;
     
     unsigned char* copyAt = &messageBuffer[numBytesPacketHeader + sizeof(sequence) + sizeof(now)];
diff --git a/libraries/voxels/src/VoxelPacketData.h b/libraries/voxels/src/VoxelPacketData.h
index 5cdc314503..088a69b970 100644
--- a/libraries/voxels/src/VoxelPacketData.h
+++ b/libraries/voxels/src/VoxelPacketData.h
@@ -28,7 +28,7 @@
 
 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;
 

From 8384ff79dfbb8b7fdc465b2f46b8c34adc794841 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Tue, 28 Jan 2014 12:36:43 -0800
Subject: [PATCH 047/153] remove one Qt type replacement

---
 libraries/shared/src/SimpleMovingAverage.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/shared/src/SimpleMovingAverage.h b/libraries/shared/src/SimpleMovingAverage.h
index 2f2b45100c..9788aafe58 100644
--- a/libraries/shared/src/SimpleMovingAverage.h
+++ b/libraries/shared/src/SimpleMovingAverage.h
@@ -26,7 +26,7 @@ public:
     float getAverageSampleValuePerSecond();
 private:
     int _numSamples;
-    quint64 _lastEventTimestamp;
+    uint64_t _lastEventTimestamp;
     float _average;
     float _eventDeltaAverage;
     

From fe410ae3e98513d61850e6f6c7db1b47a4ee0d33 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Tue, 28 Jan 2014 12:56:05 -0800
Subject: [PATCH 048/153] fix packet header size constraints

---
 libraries/octree/src/OctreePacketData.h | 4 ++--
 libraries/shared/src/PacketHeaders.cpp  | 7 ++-----
 libraries/shared/src/PacketHeaders.h    | 4 ++++
 libraries/voxels/src/VoxelPacketData.h  | 4 ++--
 4 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h
index 9ef7399fe0..448c350d3a 100644
--- a/libraries/octree/src/OctreePacketData.h
+++ b/libraries/octree/src/OctreePacketData.h
@@ -30,8 +30,8 @@ typedef uint16_t OCTREE_PACKET_INTERNAL_SECTION_SIZE;
 const int MAX_OCTREE_PACKET_SIZE = MAX_PACKET_SIZE;
 
 // 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 = (sizeof(PacketType) + sizeof(PacketVersion) + sizeof(OCTREE_PACKET_FLAGS)
-                + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME));
+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;
             
diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp
index 545c779fea..5d2dd52a2f 100644
--- a/libraries/shared/src/PacketHeaders.cpp
+++ b/libraries/shared/src/PacketHeaders.cpp
@@ -11,7 +11,6 @@
 #include 
 
 #include "NodeList.h"
-#include "UUID.h"
 
 #include "PacketHeaders.h"
 
@@ -50,10 +49,8 @@ PacketVersion versionForPacketType(PacketType type) {
     }
 }
 
-const int MAX_HEADER_BYTES = sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
-
 QByteArray byteArrayWithPopluatedHeader(PacketType type, const QUuid& connectionUUID) {
-    QByteArray freshByteArray(MAX_HEADER_BYTES, 0);
+    QByteArray freshByteArray(MAX_PACKET_HEADER_BYTES, 0);
     freshByteArray.resize(populatePacketHeader(freshByteArray, type, connectionUUID));
     return freshByteArray;
 }
@@ -73,7 +70,7 @@ int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionU
     QUuid packUUID = connectionUUID.isNull() ? NodeList::getInstance()->getOwnerUUID() : connectionUUID;
     
     QByteArray rfcUUID = packUUID.toRfc4122();
-    memcpy(packet + numTypeBytes + sizeof(PacketVersion), rfcUUID, NUM_BYTES_RFC4122_UUID);
+    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;
diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h
index e194406d58..543dce0504 100644
--- a/libraries/shared/src/PacketHeaders.h
+++ b/libraries/shared/src/PacketHeaders.h
@@ -14,6 +14,8 @@
 
 #include 
 
+#include "UUID.h"
+
 enum PacketType {
     PacketTypeUnknown,
     PacketTypeStunResponse,
@@ -54,6 +56,8 @@ enum PacketType {
 
 typedef char PacketVersion;
 
+const int MAX_PACKET_HEADER_BYTES = sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;;
+
 PacketVersion versionForPacketType(PacketType type);
 
 const QUuid nullUUID = QUuid();
diff --git a/libraries/voxels/src/VoxelPacketData.h b/libraries/voxels/src/VoxelPacketData.h
index 088a69b970..bf2a759225 100644
--- a/libraries/voxels/src/VoxelPacketData.h
+++ b/libraries/voxels/src/VoxelPacketData.h
@@ -33,8 +33,8 @@ typedef uint16_t VOXEL_PACKET_INTERNAL_SECTION_SIZE;
 const int MAX_VOXEL_PACKET_SIZE = MAX_PACKET_SIZE;
 
 // this is overly conservative - uses 8 bytes for PacketType which could be as compact as a single byte
-const int VOXEL_PACKET_HEADER_SIZE = (sizeof(PacketType) + sizeof(PacketVersion) + sizeof(VOXEL_PACKET_FLAGS) 
-                + sizeof(VOXEL_PACKET_SEQUENCE) + sizeof(VOXEL_PACKET_SENT_TIME));
+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;
             

From 4de270b8323d518ceb8ef4cf2a5a41cbad6777bb Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Tue, 28 Jan 2014 13:38:33 -0800
Subject: [PATCH 049/153] cleanup Node types, closes #1726

---
 animation-server/src/AnimationServer.cpp      |  6 +-
 assignment-client/src/Agent.cpp               |  8 +--
 assignment-client/src/AssignmentClient.cpp    |  4 +-
 assignment-client/src/audio/AudioMixer.cpp    |  6 +-
 assignment-client/src/avatars/AvatarMixer.cpp | 10 +--
 .../src/metavoxels/MetavoxelServer.cpp        |  2 +-
 domain-server/src/DomainServer.cpp            | 16 ++---
 domain-server/src/DomainServer.h              |  2 +-
 interface/src/Application.cpp                 | 54 ++++++++--------
 interface/src/Application.h                   |  4 +-
 interface/src/Audio.cpp                       |  2 +-
 interface/src/MetavoxelSystem.cpp             |  4 +-
 interface/src/ParticleTreeRenderer.h          |  2 +-
 interface/src/VoxelSystem.cpp                 |  6 +-
 interface/src/avatar/MyAvatar.cpp             |  2 +-
 interface/src/ui/VoxelStatsDialog.cpp         |  8 +--
 interface/src/ui/VoxelStatsDialog.h           |  2 +-
 libraries/audio/src/AudioInjector.cpp         |  2 +-
 libraries/octree-server/src/OctreeServer.cpp  |  4 +-
 libraries/octree/src/JurisdictionListener.cpp |  4 +-
 libraries/octree/src/JurisdictionListener.h   |  8 +--
 libraries/octree/src/JurisdictionMap.cpp      |  6 +-
 libraries/octree/src/JurisdictionMap.h        | 10 +--
 libraries/octree/src/JurisdictionSender.cpp   |  2 +-
 libraries/octree/src/JurisdictionSender.h     |  8 +--
 libraries/octree/src/OctreeRenderer.h         |  2 +-
 .../octree/src/OctreeScriptingInterface.h     |  2 +-
 .../particle-server/src/ParticleServer.h      |  2 +-
 .../particles/src/ParticleCollisionSystem.cpp |  2 +-
 .../particles/src/ParticleEditPacketSender.h  |  2 +-
 .../src/ParticlesScriptingInterface.h         |  2 +-
 libraries/script-engine/src/ScriptEngine.cpp  |  2 +-
 libraries/shared/src/Assignment.cpp           | 14 ++---
 libraries/shared/src/Assignment.h             |  2 +-
 libraries/shared/src/Node.cpp                 | 63 +++++++------------
 libraries/shared/src/Node.h                   | 32 ++++++----
 libraries/shared/src/NodeList.cpp             | 20 +++---
 libraries/shared/src/NodeList.h               | 21 +++----
 libraries/shared/src/ThreadedAssignment.cpp   |  2 +-
 libraries/shared/src/ThreadedAssignment.h     |  2 +-
 libraries/voxel-server/src/VoxelServer.cpp    |  2 +-
 libraries/voxel-server/src/VoxelServer.h      |  2 +-
 libraries/voxels/src/VoxelEditPacketSender.h  |  2 +-
 .../voxels/src/VoxelsScriptingInterface.h     |  2 +-
 44 files changed, 176 insertions(+), 184 deletions(-)

diff --git a/animation-server/src/AnimationServer.cpp b/animation-server/src/AnimationServer.cpp
index 52c281c0d9..24ea7ede9c 100644
--- a/animation-server/src/AnimationServer.cpp
+++ b/animation-server/src/AnimationServer.cpp
@@ -694,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
@@ -806,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()));
@@ -834,7 +834,7 @@ void AnimationServer::readPendingDatagrams() {
             if (packetTypeForPacket(receivedPacket) == PacketTypeJurisdiction) {
                 int headerBytes = numBytesForPacketHeader(receivedPacket);
                 // PacketType_JURISDICTION, first byte is the node type...
-                if (receivedPacket.data()[headerBytes] == NODE_TYPE_VOXEL_SERVER && ::jurisdictionListener) {
+                if (receivedPacket.data()[headerBytes] == NodeType::VoxelServer && ::jurisdictionListener) {
                     ::jurisdictionListener->queueReceivedPacket(nodeSockAddr, receivedPacket);
                 }
             }
diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp
index 38985a916e..85d996511e 100644
--- a/assignment-client/src/Agent.cpp
+++ b/assignment-client/src/Agent.cpp
@@ -32,11 +32,11 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr&
         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,
                                                                                                             dataByteArray);
                 break;
-            case NODE_TYPE_PARTICLE_SERVER:
+            case NodeType::ParticleServer:
                 _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
                                                                                                                dataByteArray);
                 break;
@@ -48,9 +48,9 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr&
 
 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");
diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp
index 8725c6ca56..cdf0da43de 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";
@@ -174,6 +174,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/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index 6a721824f3..90e2810f27 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -237,11 +237,11 @@ void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSock
 
 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;
 
@@ -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/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index bb5654ddb7..014008d35e 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -57,7 +57,7 @@ void broadcastAvatarData() {
     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
             mixedAvatarByteArray.resize(numPacketHeaderBytes);
@@ -100,7 +100,7 @@ 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
@@ -108,7 +108,7 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
         killPacket += killedNode->getUUID().toRfc4122();
         
         NodeList::getInstance()->broadcastToNodes(killPacket,
-                                                  QSet() << NODE_TYPE_AGENT);
+                                                  NodeSet() << NodeType::Agent);
     }
 }
 
@@ -144,10 +144,10 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc
 }
 
 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/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp
index 9fee6f3e2c..59b08a7185 100644
--- a/assignment-client/src/metavoxels/MetavoxelServer.cpp
+++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp
@@ -32,7 +32,7 @@ void MetavoxelServer::removeSession(const QUuid& sessionId) {
 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);
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 908f0bacdf..c861e678a0 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -66,7 +66,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
         _metavoxelServerConfig = getCmdOption(argc, (const char**) argv, metavoxelConfigOption.constData());
     }
 
-    NodeList* nodeList = NodeList::createInstance(NODE_TYPE_DOMAIN, domainServerPort);
+    NodeList* nodeList = NodeList::createInstance(NodeType::DomainServer, domainServerPort);
 
     connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), this, SLOT(nodeKilled(SharedNodePointer)));
 
@@ -110,7 +110,7 @@ void DomainServer::readAvailableDatagrams() {
     static int numAssignmentPacketHeaderBytes = assignmentPacket.size();
     
     QByteArray receivedPacket;
-    NODE_TYPE nodeType;
+    NodeType_t nodeType;
     QUuid nodeUUID;
 
     while (nodeList->getNodeSocket().hasPendingDatagrams()) {
@@ -144,9 +144,9 @@ void DomainServer::readAvailableDatagrams() {
                     }
                 }
                 
-                const QSet STATICALLY_ASSIGNED_NODES = QSet() << NODE_TYPE_AUDIO_MIXER
-                    << NODE_TYPE_AVATAR_MIXER << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER
-                    << NODE_TYPE_METAVOXEL_SERVER;
+                const QSet STATICALLY_ASSIGNED_NODES = QSet() << NodeType::AudioMixer
+                    << NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer
+                    << NodeType::MetavoxelServer;
                 
                 Assignment* matchingStaticAssignment = NULL;
                 
@@ -183,7 +183,7 @@ void DomainServer::readAvailableDatagrams() {
                     quint8 numInterestTypes = 0;
                     packetStream >> numInterestTypes;
                     
-                    NODE_TYPE* nodeTypesOfInterest = reinterpret_cast(receivedPacket.data()
+                    NodeType_t* nodeTypesOfInterest = reinterpret_cast(receivedPacket.data()
                                                                                   + packetStream.device()->pos());
                     
                     if (numInterestTypes > 0) {
@@ -262,7 +262,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(' ', '-');
 
@@ -642,7 +642,7 @@ void DomainServer::prepopulateStaticAssignmentFile() {
     _staticAssignmentFile.close();
 }
 
-Assignment* DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType) {
+Assignment* DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) {
     // pull the UUID passed with the check in
 
     if (_hasCompletedRestartHold) {
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index 1c662bcb73..26cdc765ba 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -39,7 +39,7 @@ private:
     QString readServerAssignmentConfig(QJsonObject jsonObj, const char* nodeName);
 
     void prepopulateStaticAssignmentFile();
-    Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType);
+    Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
     Assignment* deployableAssignmentForRequest(Assignment& requestAssignment);
     void removeAssignmentFromQueue(Assignment* removableAssignment);
     bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 892b97521a..eccf2dae10 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -182,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);
@@ -229,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(QSet() << 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);
@@ -649,24 +649,24 @@ void Application::resetProfile(const QString& username) {
 }
 
 void Application::controlledBroadcastToNodes(const QByteArray& packet,
-                                             const QSet& destinationNodeTypes) {
-    foreach(NODE_TYPE type, destinationNodeTypes) {
+                                             const QSet& 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(packet, QSet() << type);
+        int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(packet, QSet() << 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:
@@ -1332,10 +1332,10 @@ void Application::wheelEvent(QWheelEvent* event) {
 
 void Application::sendPingPackets() {
     QByteArray pingPacket = NodeList::getInstance()->constructPingPacket();
-    getInstance()->controlledBroadcastToNodes(pingPacket, QSet() << NODE_TYPE_VOXEL_SERVER
-                                              << NODE_TYPE_PARTICLE_SERVER
-                                              << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER
-                                              << NODE_TYPE_METAVOXEL_SERVER);
+    getInstance()->controlledBroadcastToNodes(pingPacket, QSet() << NodeType::VoxelServer
+                                              << NodeType::ParticleServer
+                                              << NodeType::AudioMixer << NodeType::AvatarMixer
+                                              << NodeType::MetavoxelServer);
 }
 
 //  Every second, check the frame rates and other stuff
@@ -2363,7 +2363,7 @@ void Application::updateAvatar(float deltaTime) {
     QByteArray avatarData = byteArrayWithPopluatedHeader(PacketTypeAvatarData);
     avatarData.append(_myAvatar.toByteArray());
 
-    controlledBroadcastToNodes(avatarData, QSet() << NODE_TYPE_AVATAR_MIXER);
+    controlledBroadcastToNodes(avatarData, QSet() << 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
@@ -2374,11 +2374,11 @@ void Application::updateAvatar(float deltaTime) {
     loadViewFrustum(_myCamera, _viewFrustum);
 
     // Update my voxel servers with my current voxel query...
-    queryOctree(NODE_TYPE_VOXEL_SERVER, PacketTypeVoxelQuery, _voxelServerJurisdictions);
-    queryOctree(NODE_TYPE_PARTICLE_SERVER, PacketTypeParticleQuery, _particleServerJurisdictions);
+    queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions);
+    queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions);
 }
 
-void Application::queryOctree(NODE_TYPE serverType, PacketType 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)) {
@@ -3192,8 +3192,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;
@@ -3203,7 +3203,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()) {
@@ -3274,7 +3274,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()),
@@ -3952,7 +3952,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()) {
@@ -3983,7 +3983,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()) {
@@ -4014,7 +4014,7 @@ 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();
     }
@@ -4066,7 +4066,7 @@ int Application::parseOctreeStats(const QByteArray& packet, const HifiSockAddr&
 
         // 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;
diff --git a/interface/src/Application.h b/interface/src/Application.h
index dae7d83da5..8daad301bf 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -175,7 +175,7 @@ public:
     Profile* getProfile() { return &_profile; }
     void resetProfile(const QString& username);
 
-    void controlledBroadcastToNodes(const QByteArray& packet, const QSet& destinationNodeTypes);
+    void controlledBroadcastToNodes(const QByteArray& packet, const QSet& destinationNodeTypes);
 
     void setupWorldLight();
 
@@ -298,7 +298,7 @@ private:
     void renderHighlightVoxel(VoxelDetail voxel);
 
     void updateAvatar(float deltaTime);
-    void queryOctree(NODE_TYPE serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
+    void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
     void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
 
     glm::vec3 getSunDirection();
diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index 752ba64910..e42947bf8c 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -365,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();
diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp
index 5bd83d969c..0196499a39 100644
--- a/interface/src/MetavoxelSystem.cpp
+++ b/interface/src/MetavoxelSystem.cpp
@@ -107,14 +107,14 @@ void MetavoxelSystem::render() {
 }
 
 void MetavoxelSystem::nodeAdded(SharedNodePointer node) {
-    if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) {
+    if (node->getType() == NodeType::MetavoxelServer) {
         QMetaObject::invokeMethod(this, "addClient", Q_ARG(const QUuid&, node->getUUID()),
             Q_ARG(const HifiSockAddr&, node->getLocalSocket()));
     }
 }
 
 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()));
     }
 }
diff --git a/interface/src/ParticleTreeRenderer.h b/interface/src/ParticleTreeRenderer.h
index 20cb41b207..147456757c 100644
--- a/interface/src/ParticleTreeRenderer.h
+++ b/interface/src/ParticleTreeRenderer.h
@@ -28,7 +28,7 @@ public:
     virtual ~ParticleTreeRenderer();
 
     virtual Octree* createTree() { return new ParticleTree(true); }
-    virtual NODE_TYPE getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
+    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);
diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp
index a663c27ce2..3297750fb9 100644
--- a/interface/src/VoxelSystem.cpp
+++ b/interface/src/VoxelSystem.cpp
@@ -1591,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];
@@ -2663,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++;
     }
@@ -2684,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/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index cee524c171..e07c3b1715 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -473,7 +473,7 @@ void MyAvatar::loadData(QSettings* settings) {
 
 void MyAvatar::sendKillAvatar() {
     QByteArray killPacket = byteArrayWithPopluatedHeader(PacketTypeKillAvatar);
-    NodeList::getInstance()->broadcastToNodes(killPacket, QSet() << NODE_TYPE_AVATAR_MIXER);
+    NodeList::getInstance()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer);
 }
 
 void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
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/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp
index fd8bdc1a89..8bcf90784e 100644
--- a/libraries/audio/src/AudioInjector.cpp
+++ b/libraries/audio/src/AudioInjector.cpp
@@ -89,7 +89,7 @@ void AudioInjector::injectAudio() {
             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
diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp
index 9a4b0a51b3..73522565f9 100644
--- a/libraries/octree-server/src/OctreeServer.cpp
+++ b/libraries/octree-server/src/OctreeServer.cpp
@@ -469,7 +469,7 @@ void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSo
             qDebug() << "Got PacketType_VOXEL_QUERY at" << usecTimestampNow();
         }
        
-        // If we got a PacketType_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;
         deconstructPacketHeader(dataByteArray, nodeUUID);
@@ -552,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/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp
index e28fb92cdb..fc8ea008d1 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);
diff --git a/libraries/octree/src/JurisdictionListener.h b/libraries/octree/src/JurisdictionListener.h
index d4cd396a48..712749aae7 100644
--- a/libraries/octree/src/JurisdictionListener.h
+++ b/libraries/octree/src/JurisdictionListener.h
@@ -28,15 +28,15 @@ 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.
@@ -53,7 +53,7 @@ protected:
 
 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 df980917e8..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,7 +262,7 @@ 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 = populatePacketHeader(reinterpret_cast(destinationBuffer), PacketTypeJurisdiction);
@@ -287,7 +287,7 @@ int JurisdictionMap::packIntoMessage(unsigned char* destinationBuffer, int avail
     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);
 
diff --git a/libraries/octree/src/JurisdictionMap.h b/libraries/octree/src/JurisdictionMap.h
index 5380a0167a..219429712e 100644
--- a/libraries/octree/src/JurisdictionMap.h
+++ b/libraries/octree/src/JurisdictionMap.h
@@ -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
@@ -60,12 +60,12 @@ public:
     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 f3ee5def2e..52990397db 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)
diff --git a/libraries/octree/src/JurisdictionSender.h b/libraries/octree/src/JurisdictionSender.h
index 7c8f288728..19ce727a57 100644
--- a/libraries/octree/src/JurisdictionSender.h
+++ b/libraries/octree/src/JurisdictionSender.h
@@ -26,15 +26,15 @@ 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, const QByteArray& packet);
@@ -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/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h
index c1d3e85aef..b65e2889e6 100644
--- a/libraries/octree/src/OctreeRenderer.h
+++ b/libraries/octree/src/OctreeRenderer.h
@@ -37,7 +37,7 @@ public:
     virtual ~OctreeRenderer();
 
     virtual Octree* createTree() = 0;
-    virtual NODE_TYPE getMyNodeType() 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;
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/ParticleServer.h b/libraries/particle-server/src/ParticleServer.h
index 4420edf947..1c1e3b5be9 100644
--- a/libraries/particle-server/src/ParticleServer.h
+++ b/libraries/particle-server/src/ParticleServer.h
@@ -26,7 +26,7 @@ public:
     // Subclasses must implement these methods
     virtual OctreeQueryNode* createOctreeQueryNode();
     virtual Octree* createTree();
-    virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
+    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; }
diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp
index e9484e99d9..d06d13795b 100644
--- a/libraries/particles/src/ParticleCollisionSystem.cpp
+++ b/libraries/particles/src/ParticleCollisionSystem.cpp
@@ -193,7 +193,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* 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) {
+//        if (node->getLinkedData() && node->getType() == NodeType_t_AGENT) {
 //            AvatarData* avatar = static_cast(node->getLinkedData());
 //            CollisionInfo collisionInfo;
 //            if (avatar->findSphereCollision(center, radius, collisionInfo)) {
diff --git a/libraries/particles/src/ParticleEditPacketSender.h b/libraries/particles/src/ParticleEditPacketSender.h
index f406ba625a..3169c5629d 100644
--- a/libraries/particles/src/ParticleEditPacketSender.h
+++ b/libraries/particles/src/ParticleEditPacketSender.h
@@ -29,7 +29,7 @@ public:
     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/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h
index a85997ee05..dc5793ae30 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; }
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 937162e0bf..d16ddcd56d 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -249,7 +249,7 @@ void ScriptEngine::run() {
             avatarPacket.resize(numAvatarHeaderBytes);
             avatarPacket.append(_avatarData->toByteArray());
             
-            nodeList->broadcastToNodes(avatarPacket, QSet() << NODE_TYPE_AVATAR_MIXER);
+            nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer);
         }
 
         if (willSendVisualDataCallBack) {
diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp
index 5432278b17..2bff4ddcd0 100644
--- a/libraries/shared/src/Assignment.cpp
+++ b/libraries/shared/src/Assignment.cpp
@@ -17,19 +17,19 @@
 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;
diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h
index 2cf026682a..418ea7fe09 100644
--- a/libraries/shared/src/Assignment.h
+++ b/libraries/shared/src/Assignment.h
@@ -50,7 +50,7 @@ public:
         LocalLocation
     };
 
-    static Assignment::Type typeForNodeType(NODE_TYPE nodeType);
+    static Assignment::Type typeForNodeType(NodeType_t nodeType);
 
     Assignment();
     Assignment(Assignment::Command command,
diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp
index 18367019ad..adcb7357bf 100644
--- a/libraries/shared/src/Node.cpp
+++ b/libraries/shared/src/Node.cpp
@@ -20,6 +20,29 @@
 
 #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),
@@ -44,44 +67,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
@@ -153,7 +138,7 @@ QDataStream& operator>>(QDataStream& in, Node& node) {
 }
 
 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 60d14a9dd3..952e1b1be2 100644
--- a/libraries/shared/src/Node.h
+++ b/libraries/shared/src/Node.h
@@ -27,17 +27,23 @@
 #include "NodeData.h"
 #include "SimpleMovingAverage.h"
 
-typedef quint8 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;
+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
@@ -50,7 +56,7 @@ 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; }
@@ -96,7 +102,7 @@ private:
     Node(const Node &otherNode);
     Node& operator=(Node otherNode);
 
-    NODE_TYPE _type;
+    NodeType_t _type;
     QUuid _uuid;
     quint64 _wakeMicrostamp;
     quint64 _lastHeardMicrostamp;
diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp
index e26185b8ef..aedfef9a3d 100644
--- a/libraries/shared/src/NodeList.cpp
+++ b/libraries/shared/src/NodeList.cpp
@@ -22,8 +22,8 @@
 #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";
@@ -33,6 +33,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
@@ -257,11 +259,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 QSet& setOfNodeTypes) {
     _nodeTypesOfInterest.unite(setOfNodeTypes);
 }
 
@@ -471,7 +473,7 @@ void NodeList::sendDomainServerCheckIn() {
             << (quint8) _nodeTypesOfInterest.size();
         
         // copy over the bytes for node types of interest, if required
-        foreach (NODE_TYPE nodeTypeOfInterest, _nodeTypesOfInterest) {
+        foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) {
             packetStream << nodeTypeOfInterest;
         }
         
@@ -595,9 +597,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());
@@ -619,7 +621,7 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
 }
 
 unsigned NodeList::broadcastToNodes(const QByteArray& packet,
-                                    const QSet& destinationNodeTypes) {
+                                    const QSet& destinationNodeTypes) {
     unsigned n = 0;
 
     foreach (const SharedNodePointer& node, getNodeHash()) {
diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h
index 69c7303c7b..b3ac019a42 100644
--- a/libraries/shared/src/NodeList.h
+++ b/libraries/shared/src/NodeList.h
@@ -30,9 +30,6 @@
 
 #include "Node.h"
 
-const int MAX_NUM_NODES = 10000;
-const int NODES_PER_BUCKET = 100;
-
 const int MAX_PACKET_SIZE = 1500;
 
 const quint64 NODE_SILENCE_THRESHOLD_USECS = 2 * 1000 * 1000;
@@ -53,6 +50,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)
@@ -62,8 +61,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);
@@ -89,9 +88,9 @@ 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(const QByteArray& packet);
 
@@ -112,7 +111,7 @@ public:
 
     int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, const QByteArray& packet);
 
-    unsigned broadcastToNodes(const QByteArray& packet, const QSet& destinationNodeTypes);
+    unsigned broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes);
     SharedNodePointer soloNodeOfType(char nodeType);
 
     void loadData(QSettings* settings);
@@ -146,8 +145,8 @@ private:
     QString _domainHostname;
     HifiSockAddr _domainSockAddr;
     QUdpSocket _nodeSocket;
-    NODE_TYPE _ownerType;
-    QSet _nodeTypesOfInterest;
+    NodeType_t _ownerType;
+    NodeSet _nodeTypesOfInterest;
     QUuid _ownerUUID;
     int _numNoReplyDomainCheckIns;
     HifiSockAddr _assignmentServerSocket;
diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp
index 148f06ec85..2c1110491b 100644
--- a/libraries/shared/src/ThreadedAssignment.cpp
+++ b/libraries/shared/src/ThreadedAssignment.cpp
@@ -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 1f59aa4623..7c7aa9a63e 100644
--- a/libraries/shared/src/ThreadedAssignment.h
+++ b/libraries/shared/src/ThreadedAssignment.h
@@ -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/VoxelServer.cpp b/libraries/voxel-server/src/VoxelServer.cpp
index 481a6c436b..b71bcd3dc2 100644
--- a/libraries/voxel-server/src/VoxelServer.cpp
+++ b/libraries/voxel-server/src/VoxelServer.cpp
@@ -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 9e4a1d23df..183224b2a7 100644
--- a/libraries/voxel-server/src/VoxelServer.h
+++ b/libraries/voxel-server/src/VoxelServer.h
@@ -36,7 +36,7 @@ public:
     // Subclasses must implement these methods
     virtual OctreeQueryNode* createOctreeQueryNode();
     virtual Octree* createTree();
-    virtual unsigned char getMyNodeType() const { return NODE_TYPE_VOXEL_SERVER; }
+    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; }
diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h
index d99c176d92..79d9f8c757 100644
--- a/libraries/voxels/src/VoxelEditPacketSender.h
+++ b/libraries/voxels/src/VoxelEditPacketSender.h
@@ -44,6 +44,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/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h
index bef3b9782e..dc53840cb1 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:

From 423e1598f076541585eb3f265c7dbba4849a1df9 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Tue, 28 Jan 2014 13:41:39 -0800
Subject: [PATCH 050/153] use NodeSet in place of QSet where
 possible

---
 domain-server/src/DomainServer.cpp |  2 +-
 interface/src/Application.cpp      | 11 +++++------
 interface/src/Application.h        |  2 +-
 libraries/shared/src/NodeList.cpp  |  4 ++--
 4 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index c861e678a0..6e4a4cdff4 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -144,7 +144,7 @@ void DomainServer::readAvailableDatagrams() {
                     }
                 }
                 
-                const QSet STATICALLY_ASSIGNED_NODES = QSet() << NodeType::AudioMixer
+                const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
                     << NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer
                     << NodeType::MetavoxelServer;
                 
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index eccf2dae10..62ca9a2af4 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -229,7 +229,7 @@ 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() << NodeType::AudioMixer << NodeType::AvatarMixer
+    nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
                                                  << NodeType::VoxelServer << NodeType::ParticleServer
                                                  << NodeType::MetavoxelServer);
     
@@ -648,8 +648,7 @@ void Application::resetProfile(const QString& username) {
     updateWindowTitle();
 }
 
-void Application::controlledBroadcastToNodes(const QByteArray& packet,
-                                             const QSet& 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 == NodeType::VoxelServer && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
@@ -657,7 +656,7 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet,
         }
         
         // Perform the broadcast for one type
-        int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(packet, 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;
@@ -1332,7 +1331,7 @@ void Application::wheelEvent(QWheelEvent* event) {
 
 void Application::sendPingPackets() {
     QByteArray pingPacket = NodeList::getInstance()->constructPingPacket();
-    getInstance()->controlledBroadcastToNodes(pingPacket, QSet() << NodeType::VoxelServer
+    getInstance()->controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer
                                               << NodeType::ParticleServer
                                               << NodeType::AudioMixer << NodeType::AvatarMixer
                                               << NodeType::MetavoxelServer);
@@ -2363,7 +2362,7 @@ void Application::updateAvatar(float deltaTime) {
     QByteArray avatarData = byteArrayWithPopluatedHeader(PacketTypeAvatarData);
     avatarData.append(_myAvatar.toByteArray());
 
-    controlledBroadcastToNodes(avatarData, QSet() << NodeType::AvatarMixer);
+    controlledBroadcastToNodes(avatarData, 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
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 8daad301bf..d38c3675d7 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -175,7 +175,7 @@ public:
     Profile* getProfile() { return &_profile; }
     void resetProfile(const QString& username);
 
-    void controlledBroadcastToNodes(const QByteArray& packet, const QSet& destinationNodeTypes);
+    void controlledBroadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes);
 
     void setupWorldLight();
 
diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp
index aedfef9a3d..9a1317de88 100644
--- a/libraries/shared/src/NodeList.cpp
+++ b/libraries/shared/src/NodeList.cpp
@@ -263,7 +263,7 @@ void NodeList::addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd) {
     _nodeTypesOfInterest << nodeTypeToAdd;
 }
 
-void NodeList::addSetOfNodeTypesToNodeInterestSet(const QSet& setOfNodeTypes) {
+void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes) {
     _nodeTypesOfInterest.unite(setOfNodeTypes);
 }
 
@@ -621,7 +621,7 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
 }
 
 unsigned NodeList::broadcastToNodes(const QByteArray& packet,
-                                    const QSet& destinationNodeTypes) {
+                                    const NodeSet& destinationNodeTypes) {
     unsigned n = 0;
 
     foreach (const SharedNodePointer& node, getNodeHash()) {

From 4bb72b9f3f21d1cdcf4d03d66fe42b7966dc071d Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Tue, 28 Jan 2014 13:44:47 -0800
Subject: [PATCH 051/153] remove MAX_HOSTNAME_BYTES

---
 libraries/shared/src/NodeList.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h
index b3ac019a42..6b8acd6e99 100644
--- a/libraries/shared/src/NodeList.h
+++ b/libraries/shared/src/NodeList.h
@@ -38,8 +38,6 @@ 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;
 

From 167acb8873a0023db7f2ddb395e05c120a57049a Mon Sep 17 00:00:00 2001
From: Andrew Meadows 
Date: Tue, 28 Jan 2014 14:16:34 -0800
Subject: [PATCH 052/153] Adding avatars back into ParticleCollisionSystem.

Had to add Application::_myAvatar to the AvatarManager list so that
particles would properly collide.
---
 interface/src/Application.cpp                 |  10 +-
 interface/src/avatar/AvatarManager.cpp        | 174 +++++++++++-------
 interface/src/avatar/AvatarManager.h          |  27 +--
 interface/src/avatar/Hand.cpp                 |  13 +-
 interface/src/avatar/MyAvatar.cpp             |  13 +-
 libraries/avatars/src/AvatarHashMap.cpp       |  30 +++
 libraries/avatars/src/AvatarHashMap.h         |  37 ++++
 .../particles/src/ParticleCollisionSystem.cpp |  15 +-
 .../particles/src/ParticleCollisionSystem.h   |   9 +-
 9 files changed, 216 insertions(+), 112 deletions(-)
 create mode 100644 libraries/avatars/src/AvatarHashMap.cpp
 create mode 100644 libraries/avatars/src/AvatarHashMap.h

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 838c5c1763..8bc845ff24 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1754,6 +1754,7 @@ void Application::init() {
     _myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
     _myCamera.setModeShiftRate(1.0f);
     _myAvatar.setDisplayingLookatVectors(false);
+    _avatarManager.setMyAvatar(&_myAvatar);
 
     _mirrorCamera.setMode(CAMERA_MODE_MIRROR);
     _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT);
@@ -1800,7 +1801,7 @@ void Application::init() {
 
     _metavoxels.init();
 
-    _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio);
+    _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager);
 
     _palette.init(_glWidget->width(), _glWidget->height());
     _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0);
@@ -2314,12 +2315,7 @@ void Application::update(float deltaTime) {
     updateCursor(deltaTime); // Handle cursor updates
 
     _particles.update(); // update the particles...
-
-    // collide the particles...
-    QVector avatars;
-    avatars.push_back(&_myAvatar);
-    _avatarManager.getAvatarBasePointers(avatars);
-    _particleCollisionSystem.update(avatars);
+    _particleCollisionSystem.update(); // collide the particles...
 }
 
 void Application::updateAvatar(float deltaTime) {
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 5ac521d838..417cdcb2ac 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -16,17 +16,28 @@
 
 #include "AvatarManager.h"
 
+
 AvatarManager::AvatarManager(QObject* parent) :
     _lookAtTargetAvatar(),
     _lookAtOtherPosition(),
     _lookAtIndicatorScale(1.0f),
-    _avatarHash(),
-    _avatarFades()
+    _avatarFades(),
+    _myAvatar(NULL)
 {
     // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
     qRegisterMetaType >("NodeWeakPointer");
 }
 
+void AvatarManager::setMyAvatar(MyAvatar* myAvatar) {
+    if (!_myAvatar) {
+        // can only ever set this once
+        _myAvatar = myAvatar;
+        // add _myAvatar to the list
+        AvatarSharedPointer myPointer = AvatarSharedPointer(_myAvatar);
+        _avatarHash.insert(MY_AVATAR_KEY, myPointer);
+    }
+}
+
 void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
     bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
     PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()");
@@ -34,22 +45,26 @@ void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
     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;
+        glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin();
+        glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection();
+
+        foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) {
+            Avatar* avatar = static_cast(avatarPointer.data());
+            if (avatar != static_cast(_myAvatar)) {
+                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();
+                    
+                    _lookAtIndicatorScale = avatar->getHead().getScale();
+                    _lookAtOtherPosition = avatar->getHead().getPosition();
+                    
+                    _lookAtTargetAvatar = avatarPointer;
+                    
+                    // found the look at target avatar, return
+                    return;
+                }
             }
         }
         
@@ -61,20 +76,28 @@ void AvatarManager::updateAvatars(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()) {
+        if (MY_AVATAR_KEY == avatarIterator.key()) {
+            // for now skip updates to _myAvatar because it is done explicitly in Application
+            // TODO: update _myAvatar in this context
+            ++avatarIterator;
+            continue;
+        }
+        Avatar* avatar = static_cast(avatarIterator.value().data());
+        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, NULL);
+            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 +111,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)) {
+                avatar->render(forceRenderHead);
+            } else {
+                avatar->render(false);
+            }
+            avatar->setDisplayingLookatVectors(renderLookAtVectors);
         }
-        
         renderAvatarFades();
+    } else if (_myAvatar) {
+        // Render my own Avatar
+        _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, NULL);
+            ++fadingIterator;
         }
     }
 }
@@ -132,29 +158,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])));
                     }
                 }
@@ -178,15 +211,17 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const
     // only add them if mixerWeakPointer points to something (meaning that mixer is still around)
     while (bytesRead < datagram.size() && mixerWeakPointer.data()) {
         QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
+        // TODO: skip the data if nodeUUID is same as MY_AVATAR_KEY
         
         AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID);
         
         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
@@ -213,28 +248,27 @@ void AvatarManager::processKillAvatar(const QByteArray& datagram) {
     // 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
     AvatarHash::iterator removeAvatar =  _avatarHash.begin();
-    
     while (removeAvatar != _avatarHash.end()) {
-        removeAvatar = removeAvatarAtHashIterator(removeAvatar);
+        removeAvatar = erase(removeAvatar);
     }
 }
 
-void AvatarManager::getAvatarBasePointers(QVector& avatars) {
-    AvatarHash::iterator avatarItr =  _avatarHash.begin();
-    while (avatarItr != _avatarHash.end()) {
-        avatars.push_back( static_cast(avatarItr.value().data()) );
-    }
-}
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index 82fe279254..f092789ded 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -13,32 +13,32 @@
 #include 
 #include 
 
+#include 
 #include 
 
 #include "Avatar.h"
 
-typedef QSharedPointer AvatarSharedPointer;
-typedef QHash AvatarHash;
+class MyAvatar;
 
-class AvatarManager : public QObject, public DataServerCallbackObject {
+const QUuid MY_AVATAR_KEY;  // NULL key
+
+class AvatarManager : public QObject, public DataServerCallbackObject, public AvatarHashMap {
     Q_OBJECT
 public:
     AvatarManager(QObject* parent = 0);
+
+    void setMyAvatar(MyAvatar* myAvatar);
     
-    const AvatarHash& getAvatarHash() { return _avatarHash; }
-    int size() const { return _avatarHash.size(); }
-    
-    Avatar* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
+    AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
     
     void updateLookAtTargetAvatar(glm::vec3& eyePosition);
     
     void updateAvatars(float deltaTime);
     void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
     
+    // virtual override
     void clearHash();
 
-    void getAvatarBasePointers(QVector& avatars);
-    
 public slots:
     void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList);
     
@@ -46,17 +46,20 @@ 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;
+    QWeakPointer _lookAtTargetAvatar;
     glm::vec3 _lookAtOtherPosition;
     float _lookAtIndicatorScale;
     
-    AvatarHash _avatarHash;
     QVector _avatarFades;
+    MyAvatar* _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/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index ca7a3b863b..6910826524 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -791,14 +791,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);
     }
diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp
new file mode 100644
index 0000000000..b1bb42edbf
--- /dev/null
+++ b/libraries/avatars/src/AvatarHashMap.cpp
@@ -0,0 +1,30 @@
+//
+//  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);
+}
+
+void AvatarHashMap::clearHash() {
+    AvatarHash::iterator removeAvatar =  _avatarHash.begin();
+    while (removeAvatar != _avatarHash.end()) {
+        removeAvatar = erase(removeAvatar);
+    }
+}
+
+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..37e9004d6f
--- /dev/null
+++ b/libraries/avatars/src/AvatarHashMap.h
@@ -0,0 +1,37 @@
+//
+//  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);
+    virtual void clearHash();
+
+protected:
+    virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
+
+    AvatarHash _avatarHash;
+};
+
+#endif /* defined(__hifi__AvatarHashMap__) */
diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp
index d53898ac05..4f5bc85d5a 100644
--- a/libraries/particles/src/ParticleCollisionSystem.cpp
+++ b/libraries/particles/src/ParticleCollisionSystem.cpp
@@ -25,11 +25,13 @@ ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packe
 }
 
 void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender,
-    ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio) {
+    ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio,
+    AvatarHashMap* avatars) {
     _packetSender = packetSender;
     _particles = particles;
     _voxels = voxels;
     _audio = audio;
+    _avatars = avatars;
 }
 
 ParticleCollisionSystem::~ParticleCollisionSystem() {
@@ -51,9 +53,8 @@ bool ParticleCollisionSystem::updateOperation(OctreeElement* element, void* extr
 }
 
 
-void ParticleCollisionSystem::update(QVector& avatars) {
+void ParticleCollisionSystem::update() {
     // update all particles
-    _avatars.swap(avatars);
     _particles->lockForWrite();
     _particles->recurseTreeWithOperation(updateOperation, this);
     _particles->unlock();
@@ -147,9 +148,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;
     }
 
@@ -160,9 +160,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
     const float COLLISION_FREQUENCY = 0.5f;
     glm::vec3 penetration;
 
-    // first check the selfAvatar if set...
-    for (int i = 0; i < _avatars.size(); ++i) {
-        AvatarData* avatar = _avatars[i];
+    foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) {
+        AvatarData* avatar = avatarPointer.data();
         CollisionInfo collisionInfo;
         collisionInfo._damping = DAMPING;
         collisionInfo._elasticity = ELASTICITY;
diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h
index b4293071bb..352864134e 100644
--- a/libraries/particles/src/ParticleCollisionSystem.h
+++ b/libraries/particles/src/ParticleCollisionSystem.h
@@ -16,6 +16,7 @@
 #include 
 #include 
 
+#include 
 #include 
 #include 
 #include 
@@ -37,13 +38,11 @@ public:
                                 AbstractAudioInterface* audio = NULL);
 
     void init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, 
-                                AbstractAudioInterface* audio = NULL);
+                                AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL);
                                 
     ~ParticleCollisionSystem();
 
-    /// Compute collisions for particles against: other particles, voxels, and avatars
-    /// \param avatars the list of avatars to collide against during this frame
-    void update(QVector& avatars);
+    void update();
 
     void checkParticle(Particle* particle);
     void updateCollisionWithVoxels(Particle* particle);
@@ -60,7 +59,7 @@ private:
     ParticleTree* _particles;
     VoxelTree* _voxels;
     AbstractAudioInterface* _audio;
-    QVector _avatars;  // only used during update() 
+    AvatarHashMap* _avatars;
 };
 
 #endif /* defined(__hifi__ParticleCollisionSystem__) */

From 1cc992a7f819905b1519b6136d0ba15e240d97fc Mon Sep 17 00:00:00 2001
From: Andrew Meadows 
Date: Tue, 28 Jan 2014 14:18:21 -0800
Subject: [PATCH 053/153] import --> include

---
 interface/src/ui/Snapshot.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h
index 39548cdff8..26315678f9 100644
--- a/interface/src/ui/Snapshot.h
+++ b/interface/src/ui/Snapshot.h
@@ -9,9 +9,9 @@
 #ifndef __hifi__Snapshot__
 #define __hifi__Snapshot__
 
-#import 
-#import 
-#import 
+#include 
+#include 
+#include 
 
 #include 
 

From 714dbf84937de6da8e7f89c6a37a205dfe3212eb Mon Sep 17 00:00:00 2001
From: Andrew Meadows 
Date: Tue, 28 Jan 2014 14:37:32 -0800
Subject: [PATCH 054/153] Moving MY_AVATAR_KEY back into AvatarManager.cpp

---
 interface/src/avatar/AvatarManager.cpp | 2 ++
 interface/src/avatar/AvatarManager.h   | 2 --
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 20b1bc151d..fc3ac73044 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -16,6 +16,8 @@
 
 #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(),
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index f092789ded..369444bcc7 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -20,8 +20,6 @@
 
 class MyAvatar;
 
-const QUuid MY_AVATAR_KEY;  // NULL key
-
 class AvatarManager : public QObject, public DataServerCallbackObject, public AvatarHashMap {
     Q_OBJECT
 public:

From e13bc71f39b6172812c693ac0b1c2ced824ab003 Mon Sep 17 00:00:00 2001
From: Andrew Meadows 
Date: Tue, 28 Jan 2014 14:43:36 -0800
Subject: [PATCH 055/153] Adding avatar info back into ParticleCollisionSystem
 ctor arguments.

---
 libraries/particles/src/ParticleCollisionSystem.cpp | 5 +++--
 libraries/particles/src/ParticleCollisionSystem.h   | 4 ++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp
index 4f5bc85d5a..a2394935b1 100644
--- a/libraries/particles/src/ParticleCollisionSystem.cpp
+++ b/libraries/particles/src/ParticleCollisionSystem.cpp
@@ -20,8 +20,9 @@
 #include "ParticleTree.h"
 
 ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender,
-    ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio) {
-    init(packetSender, particles, voxels, audio);
+    ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio,
+    AvatarHashMap* avatars) {
+    init(packetSender, particles, voxels, audio, avatars);
 }
 
 void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender,
diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h
index 352864134e..4a61693fa6 100644
--- a/libraries/particles/src/ParticleCollisionSystem.h
+++ b/libraries/particles/src/ParticleCollisionSystem.h
@@ -34,8 +34,8 @@ const glm::vec3 NO_ADDED_VELOCITY = glm::vec3(0);
 class ParticleCollisionSystem {
 public:
     ParticleCollisionSystem(ParticleEditPacketSender* packetSender = NULL, ParticleTree* particles = NULL, 
-                                VoxelTree* voxels = NULL, 
-                                AbstractAudioInterface* audio = NULL);
+                                VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL, 
+                                AvatarHashMap* avatars = NULL);
 
     void init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, 
                                 AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL);

From 5addc35cd8a7f68e82229c5b2635e7aaa8a3575f Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Tue, 28 Jan 2014 14:58:08 -0800
Subject: [PATCH 056/153] hacking on controller events

---
 interface/src/Application.cpp                 | 17 +++++++++++++
 interface/src/ControllerScriptingInterface.h  | 25 +++++++++++++++++++
 .../AbstractControllerScriptingInterface.h    | 19 ++++++++++++++
 3 files changed, 61 insertions(+)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 228a8bdd2f..fd365d67e8 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -681,6 +681,9 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_
 }
 
 void Application::keyPressEvent(QKeyEvent* event) {
+
+    _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
+
     if (activeWindow() == _window) {
         if (_chatEntryOn) {
             if (_chatEntry.keyPressEvent(event)) {
@@ -1092,6 +1095,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
 }
 
 void Application::keyReleaseEvent(QKeyEvent* event) {
+
+    _controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts
+
     if (activeWindow() == _window) {
         if (_chatEntryOn) {
             _myAvatar.setKeyState(NO_KEY_DOWN);
@@ -1154,6 +1160,8 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
 }
 
 void Application::mouseMoveEvent(QMouseEvent* event) {
+    _controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts
+
     _lastMouseMove = usecTimestampNow();
     if (_mouseHidden) {
         getGLWidget()->setCursor(Qt::ArrowCursor);
@@ -1200,6 +1208,7 @@ 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 (activeWindow() == _window) {
         if (event->button() == Qt::LeftButton) {
             _mouseX = event->x();
@@ -1267,6 +1276,7 @@ void Application::mousePressEvent(QMouseEvent* event) {
 }
 
 void Application::mouseReleaseEvent(QMouseEvent* event) {
+    _controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts
     if (activeWindow() == _window) {
         if (event->button() == Qt::LeftButton) {
             _mouseX = event->x();
@@ -1283,6 +1293,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
 }
 
 void Application::touchUpdateEvent(QTouchEvent* event) {
+    _controllerScriptingInterface.emitTouchUpdateEvent(event); // send events to any registered scripts
     bool validTouch = false;
     if (activeWindow() == _window) {
         const QList& tPoints = event->touchPoints();
@@ -1307,12 +1318,15 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
 }
 
 void Application::touchBeginEvent(QTouchEvent* event) {
+    _controllerScriptingInterface.emitTouchBeginEvent(event); // send events to any registered scripts
+
     touchUpdateEvent(event);
     _lastTouchAvgX = _touchAvgX;
     _lastTouchAvgY = _touchAvgY;
 }
 
 void Application::touchEndEvent(QTouchEvent* event) {
+    _controllerScriptingInterface.emitTouchEndEvent(event); // send events to any registered scripts
     _touchDragStartedAvgX = _touchAvgX;
     _touchDragStartedAvgY = _touchAvgY;
     _isTouchPressed = false;
@@ -1320,6 +1334,9 @@ void Application::touchEndEvent(QTouchEvent* event) {
 
 const bool USE_MOUSEWHEEL = false;
 void Application::wheelEvent(QWheelEvent* event) {
+
+    _controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts
+    
     //  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()) {
diff --git a/interface/src/ControllerScriptingInterface.h b/interface/src/ControllerScriptingInterface.h
index 69daefa3fb..ca14729151 100644
--- a/interface/src/ControllerScriptingInterface.h
+++ b/interface/src/ControllerScriptingInterface.h
@@ -18,6 +18,25 @@ class PalmData;
 class ControllerScriptingInterface : public AbstractControllerScriptingInterface {
     Q_OBJECT
 
+public:    
+    void emitKeyPressEvent(QKeyEvent* x) {
+            KeyEvent event(x);
+            emit keyPressEvent(event);
+    }
+    
+    /**
+    void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(*event); }
+
+    void emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(*event); }
+    void emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(*event); }
+    void emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(*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); }
+     **/
+
 public slots:
     virtual bool isPrimaryButtonPressed() const;
     virtual glm::vec2 getPrimaryJoystickPosition() const;
@@ -36,6 +55,12 @@ public slots:
     virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const;
     virtual glm::vec3 getSpatialControlNormal(int controlIndex) const;
 
+// The following signals are defined by AbstractControllerScriptingInterface
+//
+// signals:
+//      void keyPressEvent();
+//      void keyPressEvent();
+
 private:
     const PalmData* getPrimaryPalm() const;
     const PalmData* getPalm(int palmIndex) const;
diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h
index 5c791af0a4..b3f482c74e 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,21 @@ 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;
+
+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__) */

From 7882d50c5fc81aa17860263e03e9724a89f294a5 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Tue, 28 Jan 2014 14:58:20 -0800
Subject: [PATCH 057/153] hacking on controller events

---
 libraries/script-engine/src/EventTypes.cpp | 86 ++++++++++++++++++++++
 libraries/script-engine/src/EventTypes.h   | 62 ++++++++++++++++
 2 files changed, 148 insertions(+)
 create mode 100644 libraries/script-engine/src/EventTypes.cpp
 create mode 100644 libraries/script-engine/src/EventTypes.h

diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp
new file mode 100644
index 0000000000..e11fa72495
--- /dev/null
+++ b/libraries/script-engine/src/EventTypes.cpp
@@ -0,0 +1,86 @@
+//
+//  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 "EventTypes.h"
+
+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("isAutoRepeat", event.isAutoRepeat());
+
+    bool isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
+    bool isMeta = event.modifiers().testFlag(Qt::ControlModifier);
+
+    obj.setProperty("isShifted", isShifted);
+    obj.setProperty("isMeta", isMeta);
+     */
+
+    return obj;
+}
+
+void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) {
+    // nothing for now...
+}
+
+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();
+
+    // 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);
+    }
+    
+    obj.setProperty("averageX", touchAvgX);
+    obj.setProperty("averageY", touchAvgY);
+    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..5ed014f09a
--- /dev/null
+++ b/libraries/script-engine/src/EventTypes.h
@@ -0,0 +1,62 @@
+//
+//  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(QKeyEvent* other) { }; 
+};
+
+class MouseEvent {
+public:
+    MouseEvent() { };
+};
+
+class TouchEvent {
+public:
+    TouchEvent() { };
+};
+
+class WheelEvent {
+public:
+    WheelEvent() { };
+};
+
+
+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__

From f435f614675e4a31f6b97be85a1d54f522c7a34a Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka 
Date: Tue, 28 Jan 2014 15:46:02 -0800
Subject: [PATCH 058/153] Start the grid position off roughly at our feet.

---
 interface/src/ui/MetavoxelEditor.cpp | 18 ++++++++++++++----
 interface/src/ui/MetavoxelEditor.h   |  3 ++-
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp
index 12f727f139..fc7abe28fd 100644
--- a/interface/src/ui/MetavoxelEditor.cpp
+++ b/interface/src/ui/MetavoxelEditor.cpp
@@ -58,19 +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->setMinimum(-FLT_MAX);
     _gridSpacing->setMaximum(FLT_MAX);
     _gridSpacing->setPrefix("2^");
     _gridSpacing->setValue(-3.0);
-    connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(updateGridPosition()));
+    connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(alignGridPosition()));
 
     formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox());
     _gridPosition->setMinimum(-FLT_MAX);
     _gridPosition->setMaximum(FLT_MAX);
-    updateGridPosition();
-
+    alignGridPosition();
+    centerGridPosition();
+    
     _value = new QGroupBox();
     _value->setTitle("Value");
     topLayout->addWidget(_value);
@@ -182,7 +184,15 @@ void MetavoxelEditor::createNewAttribute() {
     updateAttributes(nameText);
 }
 
-void MetavoxelEditor::updateGridPosition() {
+void MetavoxelEditor::centerGridPosition() {
+    const float CENTER_OFFSET = 0.5f;
+    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 = getGridSpacing();
     _gridPosition->setSingleStep(step);
diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h
index 3782007e38..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();
     

From 674bc702236ed371f8f0d9ea1b3bdf63448b6181 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka 
Date: Tue, 28 Jan 2014 15:50:13 -0800
Subject: [PATCH 059/153] Offset adjustment.

---
 interface/src/ui/MetavoxelEditor.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp
index fc7abe28fd..2d09b0d95c 100644
--- a/interface/src/ui/MetavoxelEditor.cpp
+++ b/interface/src/ui/MetavoxelEditor.cpp
@@ -185,7 +185,7 @@ void MetavoxelEditor::createNewAttribute() {
 }
 
 void MetavoxelEditor::centerGridPosition() {
-    const float CENTER_OFFSET = 0.5f;
+    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();

From d624654a29a0925ecadb20d838835435fd7780ca Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Tue, 28 Jan 2014 16:25:04 -0800
Subject: [PATCH 060/153] serve a 301 redirect when trailing slash is missing,
 closes #1712

---
 .../embedded-webserver/src/HTTPConnection.cpp |  2 ++
 .../embedded-webserver/src/HTTPConnection.h   |  4 +++-
 .../embedded-webserver/src/HTTPManager.cpp    | 22 ++++++++++++++-----
 3 files changed, 21 insertions(+), 7 deletions(-)

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..849bf593cd 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,14 +55,10 @@ 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);
@@ -99,8 +107,10 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p
             }
         }
         
-        connection->respond(HTTPConnection::StatusCode200, localFileString.toLocal8Bit(), qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
+        connection->respond(HTTPConnection::StatusCode200, localFileString.toLocal8Bit(),
+                            qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
     } else {
+        
         // respond with a 404
         connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
     }

From 677341d207681b555a0749744b6c4e6026a15489 Mon Sep 17 00:00:00 2001
From: Andrew Meadows 
Date: Tue, 28 Jan 2014 18:04:42 -0800
Subject: [PATCH 061/153] fix for crash bug #1728.

Moving _myAvatr to live in AvatarManager.  Application still has a
pointer to it.  Unfortunately the order of Application initialization
and destruction matters very much.
---
 interface/src/Application.cpp           | 224 ++++++++++++------------
 interface/src/Application.h             |   8 +-
 interface/src/avatar/AvatarManager.cpp  |  40 +++--
 interface/src/avatar/AvatarManager.h    |  11 +-
 libraries/avatars/src/AvatarHashMap.cpp |   7 -
 libraries/avatars/src/AvatarHashMap.h   |   1 -
 6 files changed, 147 insertions(+), 144 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 8bc845ff24..c88e21318c 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -121,6 +121,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
         _wantToKillLocalVoxels(false),
         _audioScope(256, 200, true),
         _avatarManager(),
+        _myAvatar(NULL),
         _profile(QString()),
         _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
         _mouseX(0),
@@ -158,6 +159,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
         _persistThread(NULL),
         _statsExpanded(false)
 {
+    _myAvatar = _avatarManager.getMyAvatar();
+
     _applicationStartupTime = startup_time;
 
     switchToResourcesParentIfRequired();
@@ -326,6 +329,9 @@ Application::~Application() {
     VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
     Menu::getInstance()->deleteLater();
 
+    _avatarManager.clear();
+    _myAvatar = NULL;
+
     delete _logger;
     delete _settings;
     delete _glWidget;
@@ -440,25 +446,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
@@ -515,22 +521,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
@@ -547,27 +553,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);
             }
@@ -682,12 +688,12 @@ void Application::keyPressEvent(QKeyEvent* event) {
     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);
@@ -731,10 +737,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;
 
@@ -746,7 +752,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
                 if (_nudgeStarted) {
                     _nudgeGuidePosition.y -= _mouseVoxel.s;
                 } else {
-                    _myAvatar.setDriveKeys(DOWN, 1);
+                    _myAvatar->setDriveKeys(DOWN, 1);
                 }
                 break;
 
@@ -766,7 +772,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
                         }
                     }
                 } else {
-                    _myAvatar.setDriveKeys(FWD, 1);
+                    _myAvatar->setDriveKeys(FWD, 1);
                 }
                 break;
 
@@ -792,7 +798,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
                         }
                     }
                 } else {
-                    _myAvatar.setDriveKeys(BACK, 1);
+                    _myAvatar->setDriveKeys(BACK, 1);
                 }
                 break;
 
@@ -826,7 +832,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
                         }
                     }
                 } else {
-                    _myAvatar.setDriveKeys(ROT_LEFT, 1);
+                    _myAvatar->setDriveKeys(ROT_LEFT, 1);
                 }
                 break;
 
@@ -846,7 +852,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
                         }
                     }
                 } else {
-                    _myAvatar.setDriveKeys(ROT_RIGHT, 1);
+                    _myAvatar->setDriveKeys(ROT_RIGHT, 1);
                 }
                 break;
 
@@ -856,8 +862,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;
@@ -880,7 +886,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;
 
@@ -902,7 +908,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;
 
@@ -922,7 +928,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
                         }
                     }
                 } else {
-                    _myAvatar.setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1);
+                    _myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1);
                 }
                 break;
 
@@ -942,7 +948,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
                         }
                     }
                 } else {
-                    _myAvatar.setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1);
+                    _myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1);
                 }
                 break;
 
@@ -1060,13 +1066,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:
@@ -1092,7 +1098,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
 void Application::keyReleaseEvent(QKeyEvent* event) {
     if (activeWindow() == _window) {
         if (_chatEntryOn) {
-            _myAvatar.setKeyState(NO_KEY_DOWN);
+            _myAvatar->setKeyState(NO_KEY_DOWN);
             return;
         }
 
@@ -1101,47 +1107,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:
@@ -1168,11 +1174,11 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
         // orbit behavior
         if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) {
             if (_avatarManager.getLookAtTargetAvatar()) {
-                _myAvatar.orbit(_avatarManager.getLookAtTargetAvatar()->getPosition(), deltaX, deltaY);
+                _myAvatar->orbit(_avatarManager.getLookAtTargetAvatar()->getPosition(), deltaX, deltaY);
                 return;
             }
             if (_isHoverVoxel) {
-                _myAvatar.orbit(getMouseVoxelWorldCoordinates(_hoverVoxel), deltaX, deltaY);
+                _myAvatar->orbit(getMouseVoxelWorldCoordinates(_hoverVoxel), deltaX, deltaY);
                 return;
             }
         }
@@ -1247,14 +1253,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);
                 }
             }
 
@@ -1367,8 +1373,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) {
@@ -1663,7 +1669,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) {
@@ -1749,12 +1755,10 @@ void Application::init() {
     _headMouseY = _mouseY = _glWidget->height() / 2;
     QCursor::setPos(_headMouseX, _headMouseY);
 
-    _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);
-    _avatarManager.setMyAvatar(&_myAvatar);
 
     _mirrorCamera.setMode(CAMERA_MODE_MIRROR);
     _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT);
@@ -1859,9 +1863,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;
@@ -1899,10 +1903,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() {
@@ -1915,7 +1919,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());
     }
 }
 
@@ -1939,14 +1943,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) {
@@ -2004,9 +2008,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) {
@@ -2138,16 +2142,16 @@ void Application::updateMyAvatarSimulation(float deltaTime) {
     PerformanceWarning warn(showWarnings, "Application::updateMyAvatarSimulation()");
 
     if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) {
-        _myAvatar.setGravity(_environment.getGravity(_myAvatar.getPosition()));
+        _myAvatar->setGravity(_environment.getGravity(_myAvatar->getPosition()));
     }
     else {
-        _myAvatar.setGravity(glm::vec3(0.0f, 0.0f, 0.0f));
+        _myAvatar->setGravity(glm::vec3(0.0f, 0.0f, 0.0f));
     }
 
     if (Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) {
-        _myAvatar.simulate(deltaTime, &_myTransmitter);
+        _myAvatar->simulate(deltaTime, &_myTransmitter);
     } else {
-        _myAvatar.simulate(deltaTime, NULL);
+        _myAvatar->simulate(deltaTime, NULL);
     }
 }
 
@@ -2175,8 +2179,8 @@ void Application::updateTransmitter(float deltaTime) {
 
     // no transmitter drive implies transmitter pick
     if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) {
-        _transmitterPickStart = _myAvatar.getChestPosition();
-        glm::vec3 direction = _myAvatar.getOrientation() *
+        _transmitterPickStart = _myAvatar->getChestPosition();
+        glm::vec3 direction = _myAvatar->getOrientation() *
             glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT;
 
         // check against voxels, avatars
@@ -2250,8 +2254,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) {
@@ -2323,19 +2327,19 @@ void Application::updateAvatar(float deltaTime) {
     PerformanceWarning warn(showWarnings, "Application::updateAvatar()");
 
     // rotate body yaw for yaw received from multitouch
-    _myAvatar.setOrientation(_myAvatar.getOrientation()
+    _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() +
+    _myAvatar->getHead().setMousePitch(_myAvatar->getHead().getMousePitch() +
+                                      _myAvatar->getHand().getPitchUpdate() +
                                       _pitchFromTouch);
-    _myAvatar.getHand().setPitchUpdate(0.f);
+    _myAvatar->getHand().setPitchUpdate(0.f);
     _pitchFromTouch = 0.0f;
 
     // Update my avatar's state from gyros
-    _myAvatar.updateFromGyros(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead));
+    _myAvatar->updateFromGyros(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead));
 
     // Update head mouse from faceshift if active
     if (_faceshift.isActive()) {
@@ -2356,13 +2360,13 @@ void Application::updateAvatar(float deltaTime) {
         float yaw, pitch, roll;
         OculusManager::getEulerAngles(yaw, pitch, roll);
 
-        _myAvatar.getHead().setYaw(yaw);
-        _myAvatar.getHead().setPitch(pitch);
-        _myAvatar.getHead().setRoll(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->getHead().setAudioLoudness(_audio.getLastInputLoudness());
 
     // send head/hand data to the avatar mixer and voxel server
     unsigned char broadcastString[MAX_PACKET_SIZE];
@@ -2373,7 +2377,7 @@ void Application::updateAvatar(float deltaTime) {
     // pack the NodeList owner UUID
     endOfBroadcastStringWrite += NodeList::getInstance()->packOwnerUUID(endOfBroadcastStringWrite);
 
-    endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite);
+    endOfBroadcastStringWrite += _myAvatar->getBroadcastData(endOfBroadcastStringWrite);
 
     controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString,
                                QSet() << NODE_TYPE_AVATAR_MIXER);
@@ -3264,7 +3268,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);
@@ -3279,9 +3283,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;
@@ -3902,10 +3906,10 @@ void Application::resetSensors() {
     }
 
     QCursor::setPos(_headMouseX, _headMouseY);
-    _myAvatar.reset();
+    _myAvatar->reset();
     _myTransmitter.resetLevels();
-    _myAvatar.setVelocity(glm::vec3(0,0,0));
-    _myAvatar.setThrust(glm::vec3(0,0,0));
+    _myAvatar->setVelocity(glm::vec3(0,0,0));
+    _myAvatar->setThrust(glm::vec3(0,0,0));
 
     QMetaObject::invokeMethod(&_audio, "reset", Qt::QueuedConnection);
 }
@@ -4034,7 +4038,7 @@ void Application::nodeKilled(SharedNodePointer node) {
 
     } else if (node->getType() == NODE_TYPE_AVATAR_MIXER) {
         // our avatar mixer has gone away - clear the hash of avatars
-        _avatarManager.clearHash();
+        _avatarManager.clearMixedAvatars();
     }
 }
 
@@ -4149,7 +4153,7 @@ 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();
@@ -4187,7 +4191,7 @@ 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);
 
@@ -4354,6 +4358,6 @@ void Application::takeSnapshot() {
     player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
     player->play();
 
-    Snapshot::saveSnapshot(_glWidget, _profile.getUsername(), _myAvatar.getPosition());
+    Snapshot::saveSnapshot(_glWidget, _profile.getUsername(), _myAvatar->getPosition());
 }
 
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 21aa26bd47..4d0646235d 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; }
@@ -375,10 +375,10 @@ 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
+    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
 
-    Transmitter _myTransmitter;        // Gets UDP data from transmitter app used to animate the avatar
+    Transmitter _myTransmitter;     // Gets UDP data from transmitter app used to animate the avatar
 
     Faceshift _faceshift;
 
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index fc3ac73044..4d292b07bf 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -23,21 +23,24 @@ AvatarManager::AvatarManager(QObject* parent) :
     _lookAtTargetAvatar(),
     _lookAtOtherPosition(),
     _lookAtIndicatorScale(1.0f),
-    _avatarFades(),
-    _myAvatar(NULL)
-{
+    _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::setMyAvatar(MyAvatar* myAvatar) {
-    if (!_myAvatar) {
-        // can only ever set this once
-        _myAvatar = myAvatar;
-        // add _myAvatar to the list
-        AvatarSharedPointer myPointer = AvatarSharedPointer(_myAvatar);
-        _avatarHash.insert(MY_AVATAR_KEY, myPointer);
-    }
+void AvatarManager::clear() {
+    _lookAtTargetAvatar.clear();
+    _avatarFades.clear();
+    _avatarHash.clear();
+    _myAvatar.clear();
+}
+
+void AvatarManager::init() {
+    _myAvatar->init();
+    _myAvatar->setPosition(START_LOCATION);
+    _myAvatar->setDisplayingLookatVectors(false);
+    _avatarHash.insert(MY_AVATAR_KEY, _myAvatar);
 }
 
 void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
@@ -52,7 +55,7 @@ void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
 
         foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) {
             Avatar* avatar = static_cast(avatarPointer.data());
-            if (avatar != static_cast(_myAvatar)) {
+            if (avatar != static_cast(_myAvatar.data())) {
                 float distance;
                 if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) {
                     // rescale to compensate for head embiggening
@@ -86,7 +89,7 @@ void AvatarManager::updateAvatars(float deltaTime) {
     AvatarHash::iterator avatarIterator = _avatarHash.begin();
     while (avatarIterator != _avatarHash.end()) {
         Avatar* avatar = static_cast(avatarIterator.value().data());
-        if (avatar == static_cast(_myAvatar)) {
+        if (avatar == static_cast(_myAvatar.data())) {
             // for now skip updates to _myAvatar because it is done explicitly in Application
             // TODO: update _myAvatar in this context
             ++avatarIterator;
@@ -122,7 +125,7 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
             if (!avatar->isInitialized()) {
                 avatar->init();
             }
-            if (avatar == static_cast(_myAvatar)) {
+            if (avatar == static_cast(_myAvatar.data())) {
                 avatar->render(forceRenderHead);
             } else {
                 avatar->render(false);
@@ -130,8 +133,8 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
             avatar->setDisplayingLookatVectors(renderLookAtVectors);
         }
         renderAvatarFades();
-    } else if (_myAvatar) {
-        // Render my own Avatar
+    } else {
+        // just render myAvatar
         _myAvatar->render(forceRenderHead);
         _myAvatar->setDisplayingLookatVectors(renderLookAtVectors);
     }
@@ -266,10 +269,11 @@ AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator)
     }
 }
 
-void AvatarManager::clearHash() {
-    // clear the AvatarManager hash - typically happens on the removal of the avatar-mixer
+void AvatarManager::clearMixedAvatars() {
+    // clear any avatars that came from an avatar-mixer
     AvatarHash::iterator removeAvatar =  _avatarHash.begin();
     while (removeAvatar != _avatarHash.end()) {
         removeAvatar = erase(removeAvatar);
     }
+    _lookAtTargetAvatar.clear();
 }
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index 369444bcc7..ca436b324d 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -25,7 +25,11 @@ class AvatarManager : public QObject, public DataServerCallbackObject, public Av
 public:
     AvatarManager(QObject* parent = 0);
 
-    void setMyAvatar(MyAvatar* myAvatar);
+    void clear();
+
+    void init();
+
+    MyAvatar* getMyAvatar() { return _myAvatar.data(); }
     
     AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
     
@@ -34,8 +38,7 @@ public:
     void updateAvatars(float deltaTime);
     void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
     
-    // virtual override
-    void clearHash();
+    void clearMixedAvatars();
 
 public slots:
     void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList);
@@ -57,7 +60,7 @@ private:
     float _lookAtIndicatorScale;
     
     QVector _avatarFades;
-    MyAvatar* _myAvatar;
+    QSharedPointer _myAvatar;
 };
 
 #endif /* defined(__hifi__AvatarManager__) */
diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp
index b1bb42edbf..72ada7d421 100644
--- a/libraries/avatars/src/AvatarHashMap.cpp
+++ b/libraries/avatars/src/AvatarHashMap.cpp
@@ -17,13 +17,6 @@ void AvatarHashMap::insert(const QUuid& id, AvatarSharedPointer avatar) {
     _avatarHash.insert(id, avatar);
 }
 
-void AvatarHashMap::clearHash() {
-    AvatarHash::iterator removeAvatar =  _avatarHash.begin();
-    while (removeAvatar != _avatarHash.end()) {
-        removeAvatar = erase(removeAvatar);
-    }
-}
-
 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
index 37e9004d6f..19b6623402 100644
--- a/libraries/avatars/src/AvatarHashMap.h
+++ b/libraries/avatars/src/AvatarHashMap.h
@@ -26,7 +26,6 @@ public:
     int size() const { return _avatarHash.size(); }
 
     virtual void insert(const QUuid& id, AvatarSharedPointer avatar);
-    virtual void clearHash();
 
 protected:
     virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator);

From febb306a629fb7484eb2c9b7a18c74feb56b4032 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Tue, 28 Jan 2014 19:46:59 -0800
Subject: [PATCH 062/153] cleaned up some registration of global objects

---
 libraries/script-engine/src/ScriptEngine.cpp | 30 +++++++-------------
 1 file changed, 11 insertions(+), 19 deletions(-)

diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 1bf8dbf696..6ca59137be 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -116,19 +116,12 @@ void ScriptEngine::init() {
 
     // register meta-type for glm::vec3 conversions
     registerMetaTypes(&_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 +129,12 @@ void ScriptEngine::init() {
     QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject();
     _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue);
 
-    QScriptValue audioScriptingInterfaceValue = _engine.newQObject(&_audioScriptingInterface);
-    _engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue);
-    
+    registerGlobalObject("Agent", this);
+    registerGlobalObject("Audio", &_audioScriptingInterface);
+    registerGlobalObject("Controller", _controllerScriptingInterface);
     registerGlobalObject("Data", &_dataServerScriptingInterface);
-
-    if (_controllerScriptingInterface) {
-        QScriptValue controllerScripterValue =  _engine.newQObject(_controllerScriptingInterface);
-        _engine.globalObject().setProperty("Controller", controllerScripterValue);
-    }
+    registerGlobalObject("Particles", &_particlesScriptingInterface);
+    registerGlobalObject("Voxels", &_voxelsScriptingInterface);
 
     QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE));
     _engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
@@ -157,8 +147,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() {

From 305e69def64d3648ad15f89aa68c1ec0990e8ca4 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Tue, 28 Jan 2014 19:47:45 -0800
Subject: [PATCH 063/153] really get controller key, mouse, etc scripting
 working

---
 interface/src/Application.cpp                 |  57 ++++++++++
 .../src/ControllerScriptingInterface.cpp      |  35 ++++++
 interface/src/ControllerScriptingInterface.h  |  49 ++++++---
 libraries/particles/src/ParticleTree.cpp      |  14 +--
 .../AbstractControllerScriptingInterface.h    |  13 +++
 libraries/script-engine/src/EventTypes.cpp    | 103 +++++++++++-------
 libraries/script-engine/src/EventTypes.h      |  27 ++++-
 7 files changed, 226 insertions(+), 72 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index fd365d67e8..1a673da70c 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -683,6 +683,11 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, 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) {
@@ -1098,6 +1103,12 @@ 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);
@@ -1162,6 +1173,12 @@ 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);
@@ -1209,6 +1226,13 @@ 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();
@@ -1277,6 +1301,12 @@ 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();
@@ -1294,6 +1324,12 @@ 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();
@@ -1321,21 +1357,42 @@ 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)) {
diff --git a/interface/src/ControllerScriptingInterface.cpp b/interface/src/ControllerScriptingInterface.cpp
index fd27eb2428..491c38a9de 100644
--- a/interface/src/ControllerScriptingInterface.cpp
+++ b/interface/src/ControllerScriptingInterface.cpp
@@ -179,5 +179,40 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex
     return glm::vec3(0); // bad index
 }
 
+bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const {
+qDebug() << "ControllerScriptingInterface::isKeyCaptured() event=" << event;
+    return isKeyCaptured(KeyEvent(*event));
+}
 
+bool ControllerScriptingInterface::isKeyCaptured(const KeyEvent& event) const {
+
+qDebug() << "ControllerScriptingInterface::isKeyCaptured() event.key=" << event.key;
+
+    // if we've captured some combination of this key it will be in the map
+    if (_capturedKeys.contains(event.key, event)) {
+qDebug() << "ControllerScriptingInterface::isKeyCaptured() event.key=" << event.key << " returning TRUE";
+        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 ca14729151..6b09d34909 100644
--- a/interface/src/ControllerScriptingInterface.h
+++ b/interface/src/ControllerScriptingInterface.h
@@ -1,3 +1,4 @@
+
 //
 //  ControllerScriptingInterface.h
 //  hifi
@@ -19,23 +20,24 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface
     Q_OBJECT
 
 public:    
-    void emitKeyPressEvent(QKeyEvent* x) {
-            KeyEvent event(x);
-            emit keyPressEvent(event);
-    }
-    
-    /**
-    void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(*event); }
+    void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
+    void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
 
-    void emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(*event); }
-    void emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(*event); }
-    void emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(*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; }
 
-    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); }
-     **/
 
 public slots:
     virtual bool isPrimaryButtonPressed() const;
@@ -55,6 +57,18 @@ 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; }
+
 // The following signals are defined by AbstractControllerScriptingInterface
 //
 // signals:
@@ -66,6 +80,11 @@ private:
     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/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp
index 7f8f168e5d..6cb46b3340 100644
--- a/libraries/particles/src/ParticleTree.cpp
+++ b/libraries/particles/src/ParticleTree.cpp
@@ -498,11 +498,9 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha
     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);
@@ -540,16 +538,14 @@ 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) {
-    //qDebug() << "forgetParticlesDeletedBefore()";
     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    
     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;
@@ -557,18 +553,15 @@ void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) {
 
     // Now run through the keysToRemove and remove them    
     foreach (uint64_t 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;
@@ -583,14 +576,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
             }
 
@@ -599,12 +589,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/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h
index b3f482c74e..4fad5f6edc 100644
--- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h
+++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h
@@ -38,6 +38,19 @@ public slots:
     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);
diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp
index e11fa72495..5c4ab7f2a7 100644
--- a/libraries/script-engine/src/EventTypes.cpp
+++ b/libraries/script-engine/src/EventTypes.cpp
@@ -10,47 +10,28 @@
 
 #include "EventTypes.h"
 
-void registerEventTypes(QScriptEngine* engine) {
-    qScriptRegisterMetaType(engine, keyEventToScriptValue, keyEventFromScriptValue);
-    qScriptRegisterMetaType(engine, mouseEventToScriptValue, mouseEventFromScriptValue);
-    qScriptRegisterMetaType(engine, touchEventToScriptValue, touchEventFromScriptValue);
-    qScriptRegisterMetaType(engine, wheelEventToScriptValue, wheelEventFromScriptValue);
+
+KeyEvent::KeyEvent() {
+    key = 0;
+    isShifted = false;
+    isMeta = false;
+    isValid = false;
 }
 
-QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) {
-    QScriptValue obj = engine->newObject();
-    /*
-    obj.setProperty("key", event.key());
-    obj.setProperty("isAutoRepeat", event.isAutoRepeat());
 
-    bool isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
-    bool isMeta = event.modifiers().testFlag(Qt::ControlModifier);
-
-    obj.setProperty("isShifted", isShifted);
-    obj.setProperty("isMeta", isMeta);
-     */
-
-    return obj;
+KeyEvent::KeyEvent(const QKeyEvent& event) {
+    key = event.key();
+    isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
+    isMeta = event.modifiers().testFlag(Qt::ControlModifier);
+    isValid = true;
 }
 
-void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) {
-    // nothing for now...
+MouseEvent::MouseEvent(const QMouseEvent& event) {
+    x = event.x();
+    y = event.y();
 }
 
-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();
-
+TouchEvent::TouchEvent(const QTouchEvent& event) {
     // convert the touch points into an average    
     const QList& tPoints = event.touchPoints();
     float touchAvgX = 0.0f;
@@ -64,9 +45,53 @@ QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& ev
         touchAvgX /= (float)(numTouches);
         touchAvgY /= (float)(numTouches);
     }
-    
-    obj.setProperty("averageX", touchAvgX);
-    obj.setProperty("averageY", touchAvgY);
+    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("isShifted", event.isShifted);
+    obj.setProperty("isMeta", event.isMeta);
+    return obj;
+}
+
+void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) {
+    event.key = object.property("key").toVariant().toInt();
+    event.isShifted = object.property("isShifted").toVariant().toBool();
+    event.isMeta = object.property("isMeta").toVariant().toBool();
+    event.isValid = object.property("key").isValid();
+}
+
+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;
 }
 
@@ -76,8 +101,8 @@ void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event) {
 
 QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event) {
     QScriptValue obj = engine->newObject();
-    obj.setProperty("x", event.x());
-    obj.setProperty("y", event.y());
+    obj.setProperty("x", event.x);
+    obj.setProperty("y", event.y);
     return obj;
 }
 
diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h
index 5ed014f09a..c3764b2619 100644
--- a/libraries/script-engine/src/EventTypes.h
+++ b/libraries/script-engine/src/EventTypes.h
@@ -18,25 +18,42 @@
 #include 
 #include 
 
+
 class KeyEvent {
 public:
-    KeyEvent() { }; 
-    KeyEvent(QKeyEvent* other) { }; 
+    KeyEvent();
+    KeyEvent(const QKeyEvent& event);
+    inline bool operator==(const KeyEvent& other) const { 
+                            return other.key == key && other.isShifted == isShifted && other.isMeta == isMeta; }
+    int key;
+    bool isShifted;
+    bool isMeta;
+    bool isValid;
 };
 
+
 class MouseEvent {
 public:
-    MouseEvent() { };
+    MouseEvent() : x(0), y(0) { }; 
+    MouseEvent(const QMouseEvent& event);
+    int x;
+    int y;
 };
 
 class TouchEvent {
 public:
-    TouchEvent() { };
+    TouchEvent() : x(0), y(0) { };
+    TouchEvent(const QTouchEvent& event);
+    float x;
+    float y;
 };
 
 class WheelEvent {
 public:
-    WheelEvent() { };
+    WheelEvent() : x(0), y(0)  { }; 
+    WheelEvent(const QWheelEvent& event);
+    int x;
+    int y;
 };
 
 

From a73733e57bfcf287535fbc92c9ac8f38995a5596 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Tue, 28 Jan 2014 19:48:06 -0800
Subject: [PATCH 064/153] controller scripting example

---
 examples/controllerExample.js | 105 ++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)
 create mode 100644 examples/controllerExample.js

diff --git a/examples/controllerExample.js b/examples/controllerExample.js
new file mode 100644
index 0000000000..c1b33b24a5
--- /dev/null
+++ b/examples/controllerExample.js
@@ -0,0 +1,105 @@
+//
+//  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);
+    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
+Agent.willSendVisualDataCallback.connect(checkController);
+
+// Map keyPress and mouse move events to our callbacks
+Controller.keyPressEvent.connect(keyPressEvent);
+var AKeyEvent = {
+    key: "A".charCodeAt(0),
+    isShifted: false,
+    isMeta: false
+};
+
+// prevent the A key from going through to the application
+Controller.captureKeyEvents(AKeyEvent);
+
+
+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();
+}
+
+Agent.scriptEnding.connect(scriptEnding);

From bcc7cfb15ec6e21abaf1fee507b4dd46934a2e5f Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Tue, 28 Jan 2014 20:38:47 -0800
Subject: [PATCH 065/153] removed some debug

---
 interface/src/ControllerScriptingInterface.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/interface/src/ControllerScriptingInterface.cpp b/interface/src/ControllerScriptingInterface.cpp
index 491c38a9de..799a57b951 100644
--- a/interface/src/ControllerScriptingInterface.cpp
+++ b/interface/src/ControllerScriptingInterface.cpp
@@ -180,17 +180,12 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex
 }
 
 bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const {
-qDebug() << "ControllerScriptingInterface::isKeyCaptured() event=" << event;
     return isKeyCaptured(KeyEvent(*event));
 }
 
 bool ControllerScriptingInterface::isKeyCaptured(const KeyEvent& event) const {
-
-qDebug() << "ControllerScriptingInterface::isKeyCaptured() event.key=" << event.key;
-
     // if we've captured some combination of this key it will be in the map
     if (_capturedKeys.contains(event.key, event)) {
-qDebug() << "ControllerScriptingInterface::isKeyCaptured() event.key=" << event.key << " returning TRUE";
         return true;
     }
     return false;

From 434756a40f19528b40386c056658f98137b1ef41 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Tue, 28 Jan 2014 20:40:00 -0800
Subject: [PATCH 066/153] removed some dead comments

---
 interface/src/ControllerScriptingInterface.h | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/interface/src/ControllerScriptingInterface.h b/interface/src/ControllerScriptingInterface.h
index 6b09d34909..b6d5c81a05 100644
--- a/interface/src/ControllerScriptingInterface.h
+++ b/interface/src/ControllerScriptingInterface.h
@@ -69,12 +69,6 @@ public slots:
     virtual void captureWheelEvents() { _wheelCaptured = true; }
     virtual void releaseWheelEvents() { _wheelCaptured = false; }
 
-// The following signals are defined by AbstractControllerScriptingInterface
-//
-// signals:
-//      void keyPressEvent();
-//      void keyPressEvent();
-
 private:
     const PalmData* getPrimaryPalm() const;
     const PalmData* getPalm(int palmIndex) const;

From 98cdc656c44f01286af1011fc3bb3aae9d1e0800 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Wed, 29 Jan 2014 00:56:08 -0800
Subject: [PATCH 067/153] more scripting support, including adding a Quat
 helper class that makes JS quat math easier

---
 interface/src/Util.cpp                       | 28 -------------------
 interface/src/Util.h                         |  2 --
 interface/src/world.h                        |  3 +-
 libraries/avatars/src/AvatarData.cpp         |  7 +++++
 libraries/avatars/src/AvatarData.h           |  4 +++
 libraries/script-engine/src/Quat.cpp         | 20 ++++++++++++++
 libraries/script-engine/src/Quat.h           | 29 ++++++++++++++++++++
 libraries/script-engine/src/ScriptEngine.cpp |  2 ++
 libraries/script-engine/src/ScriptEngine.h   |  2 ++
 libraries/shared/src/SharedUtil.cpp          | 28 +++++++++++++++++++
 libraries/shared/src/SharedUtil.h            |  6 ++++
 11 files changed, 100 insertions(+), 31 deletions(-)
 create mode 100644 libraries/script-engine/src/Quat.cpp
 create mode 100644 libraries/script-engine/src/Quat.h

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/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/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 8f548869ec..44f71c7aec 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -276,3 +276,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..7300da041e 100755
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -69,6 +69,9 @@ 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)
+
 public:
     AvatarData();
     ~AvatarData();
@@ -91,6 +94,7 @@ 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);
 
     //  Scale
     float getTargetScale() const { return _targetScale; }
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 6ca59137be..28cb49f6f7 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -134,6 +134,8 @@ void ScriptEngine::init() {
     registerGlobalObject("Controller", _controllerScriptingInterface);
     registerGlobalObject("Data", &_dataServerScriptingInterface);
     registerGlobalObject("Particles", &_particlesScriptingInterface);
+    registerGlobalObject("Quat", &_quatLibrary);
+
     registerGlobalObject("Voxels", &_voxelsScriptingInterface);
 
     QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE));
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index b36e2425fe..ef55e14109 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -25,6 +25,7 @@ class ParticlesScriptingInterface;
 
 #include "AbstractControllerScriptingInterface.h"
 #include "DataServerScriptingInterface.h"
+#include "Quat.h"
 
 const QString NO_SCRIPT("");
 
@@ -94,6 +95,7 @@ private:
     QString _fileNameString;
     AbstractMenuInterface* _menu;
     static int _scriptNumber;
+    Quat _quatLibrary;
 };
 
 #endif /* defined(__hifi__ScriptEngine__) */
diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp
index 00e11e2b19..54a4291c25 100644
--- a/libraries/shared/src/SharedUtil.cpp
+++ b/libraries/shared/src/SharedUtil.cpp
@@ -715,3 +715,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 174742fcf3..399bf40204 100644
--- a/libraries/shared/src/SharedUtil.h
+++ b/libraries/shared/src/SharedUtil.h
@@ -176,4 +176,10 @@ int unpackFloatScalarFromSignedTwoByteFixed(int16_t* byteFixedPointer, float* de
 int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
 int unpackFloatVec3FromSignedTwoByteFixed(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__) */

From ecd2477007c9b5c6d8cc5f41d6b7784ca6a4e209 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Wed, 29 Jan 2014 01:01:41 -0800
Subject: [PATCH 068/153] adding lookWithMouse.js example that uses mouse
 instead of multi-touch to add the look around feature

---
 examples/lookWithMouse.js | 84 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 examples/lookWithMouse.js

diff --git a/examples/lookWithMouse.js b/examples/lookWithMouse.js
new file mode 100644
index 0000000000..90ea81bf8c
--- /dev/null
+++ b/examples/lookWithMouse.js
@@ -0,0 +1,84 @@
+//
+//  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 isMouseDown = false;
+var lastX = 0;
+var lastY = 0;
+var yawFromMouse = 0;
+var pitchFromMouse = 0;
+
+function mousePressEvent(event) {
+    print("mousePressEvent event.x,y=" + event.x + ", " + event.y);
+    isMouseDown = true;
+    lastX = event.x;
+    lastY = event.y;
+}
+
+function mouseReleaseEvent(event) {
+    print("mouseReleaseEvent event.x,y=" + event.x + ", " + event.y);
+    isMouseDown = false;
+}
+
+function mouseMoveEvent(event) {
+    print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
+
+    if (isMouseDown) {
+        print("isMouseDown... attempting to change pitch...");
+        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() {
+
+    print("isMouseDown... attempting to change pitch...");
+
+    // rotate body yaw for yaw received from mouse
+    MyAvatar.orientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } ));
+    yawFromMouse = 0;
+
+    // apply pitch from mouse
+    /**
+    _myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() +
+                                      _myAvatar.getHand().getPitchUpdate() +
+                                      pitchFromMouse);
+    **/
+    
+    //_myAvatar.getHand().setPitchUpdate(0.f);
+    
+    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
+Agent.willSendVisualDataCallback.connect(update);
+Agent.scriptEnding.connect(scriptEnding);

From b36cc0bcf9bdfcf4952e6e895f647ebd4f75c964 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Wed, 29 Jan 2014 02:09:49 -0800
Subject: [PATCH 069/153] removed old cruft around mousePitch in Head and
 unified pitch

---
 examples/lookWithMouse.js          | 12 +-----------
 interface/src/Application.cpp      |  5 +----
 interface/src/avatar/Head.cpp      | 12 +-----------
 interface/src/avatar/Head.h        |  5 -----
 interface/src/avatar/MyAvatar.cpp  | 17 ++++++++---------
 libraries/avatars/src/AvatarData.h |  5 +++++
 6 files changed, 16 insertions(+), 40 deletions(-)

diff --git a/examples/lookWithMouse.js b/examples/lookWithMouse.js
index 90ea81bf8c..79fad76a1b 100644
--- a/examples/lookWithMouse.js
+++ b/examples/lookWithMouse.js
@@ -43,22 +43,12 @@ function mouseMoveEvent(event) {
 }
 
 function update() {
-
-    print("isMouseDown... attempting to change pitch...");
-
     // rotate body yaw for yaw received from mouse
     MyAvatar.orientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } ));
     yawFromMouse = 0;
 
     // apply pitch from mouse
-    /**
-    _myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() +
-                                      _myAvatar.getHand().getPitchUpdate() +
-                                      pitchFromMouse);
-    **/
-    
-    //_myAvatar.getHand().setPitchUpdate(0.f);
-    
+    MyAvatar.headPitch = MyAvatar.headPitch + pitchFromMouse;
     pitchFromMouse = 0;
 }
 
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index a0d9c9617e..6578618e5f 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -2404,10 +2404,7 @@ void Application::updateAvatar(float deltaTime) {
     _yawFromTouch = 0.f;
 
     // apply pitch from touch
-    _myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() +
-                                      _myAvatar.getHand().getPitchUpdate() +
-                                      _pitchFromTouch);
-    _myAvatar.getHand().setPitchUpdate(0.f);
+    _myAvatar.getHead().setPitch(_myAvatar.getHead().getPitch() + _pitchFromTouch);
     _pitchFromTouch = 0.0f;
 
     // Update my avatar's state from gyros
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 6910826524..7448d33415 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -238,7 +238,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 +284,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));
@@ -434,7 +432,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 +454,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);
@@ -497,9 +495,10 @@ 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));
 }
 
@@ -549,7 +548,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]) {
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index 7300da041e..e3bb8f08d5 100755
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -71,6 +71,7 @@ class AvatarData : public NodeData {
     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();
@@ -96,6 +97,10 @@ public:
     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; }
     void setTargetScale(float targetScale) { _targetScale = targetScale; }

From 154deccbc65c8c802a3276229c561e2b14c664e9 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka 
Date: Wed, 29 Jan 2014 11:14:10 -0800
Subject: [PATCH 070/153] Pass in the starting minimum/size.

---
 libraries/metavoxels/src/MetavoxelData.cpp | 5 ++---
 libraries/metavoxels/src/MetavoxelData.h   | 4 +++-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp
index df59206b6b..66bba15de2 100644
--- a/libraries/metavoxels/src/MetavoxelData.cpp
+++ b/libraries/metavoxels/src/MetavoxelData.cpp
@@ -29,13 +29,12 @@ MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) {
     return *this;
 }
 
-void MetavoxelData::guide(MetavoxelVisitor& visitor) {
+void MetavoxelData::guide(MetavoxelVisitor& visitor, const glm::vec3& minimum, float size) {
     // 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 = { NULL, visitor, QVector(inputs.size() + 1),
-        QVector(outputs.size()), { glm::vec3(), TOP_LEVEL_SIZE,
+        QVector(outputs.size()), { minimum, size,
             QVector(inputs.size() + 1), QVector(outputs.size()) } };
     for (int i = 0; i < inputs.size(); i++) {
         MetavoxelNode* node = _roots.value(inputs.at(i));
diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h
index 83821e47ad..60ee4e7634 100644
--- a/libraries/metavoxels/src/MetavoxelData.h
+++ b/libraries/metavoxels/src/MetavoxelData.h
@@ -37,7 +37,9 @@ public:
     MetavoxelData& operator=(const MetavoxelData& other);
 
     /// Applies the specified visitor to the contained voxels.
-    void guide(MetavoxelVisitor& visitor);
+    /// \param minimum the top-level minimum
+    /// \param size the top-level size
+    void guide(MetavoxelVisitor& visitor, const glm::vec3& minimum = glm::vec3(), float size = 1.0f);
 
     void read(Bitstream& in);
     void write(Bitstream& out) const;

From 616cfac2939789d164db6a657c347d482ba998a3 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Wed, 29 Jan 2014 11:21:45 -0800
Subject: [PATCH 071/153] fix init bug

---
 interface/src/ControllerScriptingInterface.cpp | 8 ++++++++
 interface/src/ControllerScriptingInterface.h   | 1 +
 2 files changed, 9 insertions(+)

diff --git a/interface/src/ControllerScriptingInterface.cpp b/interface/src/ControllerScriptingInterface.cpp
index 799a57b951..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;
 
diff --git a/interface/src/ControllerScriptingInterface.h b/interface/src/ControllerScriptingInterface.h
index b6d5c81a05..e84039bcb0 100644
--- a/interface/src/ControllerScriptingInterface.h
+++ b/interface/src/ControllerScriptingInterface.h
@@ -20,6 +20,7 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface
     Q_OBJECT
 
 public:    
+    ControllerScriptingInterface();
     void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
     void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
 

From 2dd71435eef709a10da279de9e9109755ae71c3a Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 12:25:05 -0800
Subject: [PATCH 072/153] fix build breakers for new packet type

---
 assignment-client/src/Agent.cpp | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp
index 7120f82bfc..84714259e5 100644
--- a/assignment-client/src/Agent.cpp
+++ b/assignment-client/src/Agent.cpp
@@ -28,7 +28,8 @@ Agent::Agent(const QByteArray& packet) :
 }
 
 void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
-    if (packetTypeForPacket(dataByteArray) == PacketTypeJurisdiction) {
+    PacketType datagramPacketType = packetTypeForPacket(dataByteArray);
+    if (datagramPacketType == PacketTypeJurisdiction) {
         int headerBytes = numBytesForPacketHeader(dataByteArray);
         // PacketType_JURISDICTION, first byte is the node type...
         switch (dataByteArray[headerBytes]) {
@@ -41,12 +42,12 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr&
                                                                                                                dataByteArray);
                 break;
         }
-    } else if (dataByteArray[0] == PACKET_TYPE_PARTICLE_ADD_RESPONSE) {
+    } else if (datagramPacketType == PacketTypeParticleAddResponse) {
         // this will keep creatorTokenIDs to IDs mapped correctly
-        Particle::handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size());
+        Particle::handleAddParticleResponse(dataByteArray);
         
         // also give our local particle tree a chance to remap any internal locally created particles
-        _particleTree.handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size());
+        _particleTree.handleAddParticleResponse(dataByteArray);
     } else {
         NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray);
     }

From 7d8b7a79acccf9c5d76a33a10533f3f735b1ba1f Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 12:30:20 -0800
Subject: [PATCH 073/153] fix Particle update signature

---
 libraries/particles/src/Particle.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h
index 6cb26aba47..debb073ebd 100644
--- a/libraries/particles/src/Particle.h
+++ b/libraries/particles/src/Particle.h
@@ -290,7 +290,7 @@ public:
     
     void applyHardCollision(const CollisionInfo& collisionInfo);
 
-    void update(const uint64_t& now);
+    void update(const quint64& now);
     void collisionWithParticle(Particle* other);
     void collisionWithVoxel(VoxelDetail* voxel);
 

From 4c70af51e8837833fedb2a970ff14842d2b14259 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka 
Date: Wed, 29 Jan 2014 13:30:54 -0800
Subject: [PATCH 074/153] Expand data automatically to encompass edits.

---
 interface/src/ui/MetavoxelEditor.cpp          |   2 +-
 libraries/metavoxels/src/MetavoxelData.cpp    | 106 +++++++++++++++---
 libraries/metavoxels/src/MetavoxelData.h      |  13 ++-
 .../metavoxels/src/MetavoxelMessages.cpp      |   9 +-
 libraries/metavoxels/src/MetavoxelMessages.h  |   5 +-
 libraries/metavoxels/src/MetavoxelUtil.cpp    |   6 +
 libraries/metavoxels/src/MetavoxelUtil.h      |  16 +++
 7 files changed, 133 insertions(+), 24 deletions(-)

diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp
index 2d09b0d95c..d35b44ffef 100644
--- a/interface/src/ui/MetavoxelEditor.cpp
+++ b/interface/src/ui/MetavoxelEditor.cpp
@@ -358,7 +358,7 @@ void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maxi
         return;
     }
     OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue()));
-    MetavoxelEditMessage edit = { minimum, maximum, getGridSpacing(), value };
+    MetavoxelEditMessage edit = { { minimum, maximum }, getGridSpacing(), value };
     Application::getInstance()->getMetavoxels()->applyEdit(edit);
 }
 
diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp
index 66bba15de2..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,17 +28,24 @@ MetavoxelData::~MetavoxelData() {
 
 MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) {
     decrementRootReferenceCounts();
+    _size = other._size;
     _roots = other._roots;
     incrementRootReferenceCounts();
     return *this;
 }
 
-void MetavoxelData::guide(MetavoxelVisitor& visitor, const glm::vec3& minimum, float size) {
+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 QVector& inputs = visitor.getInputs();
     const QVector& outputs = visitor.getOutputs();
     MetavoxelVisitation firstVisitation = { NULL, visitor, QVector(inputs.size() + 1),
-        QVector(outputs.size()), { minimum, size,
+        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));
@@ -70,11 +81,49 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor, const glm::vec3& minimum, f
     }
 }
 
+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) {
     // clear out any existing roots
     decrementRootReferenceCounts();
     _roots.clear();
 
+    in >> _size;
+    
     // read in the new roots
     int rootCount;
     in >> rootCount;
@@ -88,6 +137,7 @@ void MetavoxelData::read(Bitstream& in) {
 }
 
 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();
@@ -105,6 +155,16 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) {
         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++) {
@@ -134,23 +194,38 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) {
 
 void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const {
     // first things first: there might be no change whatsoever
-    if (_roots == reference._roots) {
+    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) {
@@ -163,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() {
@@ -349,10 +429,6 @@ 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);
diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h
index 60ee4e7634..0114f1b4d6 100644
--- a/libraries/metavoxels/src/MetavoxelData.h
+++ b/libraries/metavoxels/src/MetavoxelData.h
@@ -22,6 +22,7 @@
 
 class QScriptContext;
 
+class Box;
 class MetavoxelNode;
 class MetavoxelVisitation;
 class MetavoxelVisitor;
@@ -36,10 +37,15 @@ public:
 
     MetavoxelData& operator=(const MetavoxelData& other);
 
+    float getSize() const { return _size; }
+
+    Box getBounds() const;
+
     /// Applies the specified visitor to the contained voxels.
-    /// \param minimum the top-level minimum
-    /// \param size the top-level size
-    void guide(MetavoxelVisitor& visitor, const glm::vec3& minimum = glm::vec3(), float size = 1.0f);
+    void guide(MetavoxelVisitor& visitor);
+    
+    /// Expands the tree, increasing its capacity in all dimensions.
+    void expand();
 
     void read(Bitstream& in);
     void write(Bitstream& out) const;
@@ -54,6 +60,7 @@ private:
     void incrementRootReferenceCounts();
     void decrementRootReferenceCounts();
     
+    float _size;
     QHash _roots;
 };
 
diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp
index 02b040e5cc..380df4cac1 100644
--- a/libraries/metavoxels/src/MetavoxelMessages.cpp
+++ b/libraries/metavoxels/src/MetavoxelMessages.cpp
@@ -28,8 +28,8 @@ EditVisitor::EditVisitor(const MetavoxelEditMessage& edit) :
 
 bool EditVisitor::visit(MetavoxelInfo& info) {
     // find the intersection between volume and voxel
-    glm::vec3 minimum = glm::max(info.minimum, _edit.minimum);
-    glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _edit.maximum);
+    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
@@ -49,6 +49,11 @@ bool EditVisitor::visit(MetavoxelInfo& info) {
 }
 
 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 fb8db64506..25f4ffe422 100644
--- a/libraries/metavoxels/src/MetavoxelMessages.h
+++ b/libraries/metavoxels/src/MetavoxelMessages.h
@@ -10,7 +10,7 @@
 #define __interface__MetavoxelMessages__
 
 #include "AttributeRegistry.h"
-#include "Bitstream.h"
+#include "MetavoxelUtil.h"
 
 class MetavoxelData;
 
@@ -45,8 +45,7 @@ class MetavoxelEditMessage {
 
 public:
     
-    STREAM glm::vec3 minimum;
-    STREAM glm::vec3 maximum;
+    STREAM Box region;
     STREAM float granularity;
     STREAM OwnedAttributeValue value;
     
diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp
index 29b8c57168..32f91f30f3 100644
--- a/libraries/metavoxels/src/MetavoxelUtil.cpp
+++ b/libraries/metavoxels/src/MetavoxelUtil.cpp
@@ -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__) */

From 1e9e298a75fb84bdd16a5738ad1933f072e7827f Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka 
Date: Wed, 29 Jan 2014 13:47:13 -0800
Subject: [PATCH 075/153] Missing includes.

---
 data-server/src/DataServer.cpp                    | 4 +++-
 libraries/audio/src/AudioInjector.cpp             | 4 +++-
 libraries/audio/src/InjectedAudioRingBuffer.cpp   | 1 +
 libraries/audio/src/PositionalAudioRingBuffer.cpp | 2 ++
 libraries/avatars/src/AvatarData.cpp              | 2 ++
 libraries/avatars/src/HandData.cpp                | 2 ++
 libraries/shared/src/HifiSockAddr.cpp             | 1 +
 libraries/shared/src/Node.cpp                     | 1 +
 libraries/shared/src/NodeList.cpp                 | 1 +
 9 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp
index 173f42f33a..9044e6cf6a 100644
--- a/data-server/src/DataServer.cpp
+++ b/data-server/src/DataServer.cpp
@@ -6,6 +6,8 @@
 //  Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
 //
 
+#include 
+#include 
 #include 
 
 #include 
@@ -176,4 +178,4 @@ void DataServer::readPendingDatagrams() {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp
index 8bcf90784e..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 
@@ -115,4 +117,4 @@ void AudioInjector::injectAudio() {
     }
     
     emit finished();
-}
\ No newline at end of file
+}
diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp
index d7b40f63ea..f8205a846f 100644
--- a/libraries/audio/src/InjectedAudioRingBuffer.cpp
+++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp
@@ -8,6 +8,7 @@
 
 #include 
 
+#include 
 #include 
 
 #include 
diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp
index 5434966c00..27b62a8a56 100644
--- a/libraries/audio/src/PositionalAudioRingBuffer.cpp
+++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp
@@ -8,6 +8,8 @@
 
 #include 
 
+#include 
+
 #include 
 #include 
 #include 
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 8c9e645db3..a8906f9167 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -10,6 +10,8 @@
 #include 
 #include 
 
+#include 
+
 #include 
 #include 
 #include 
diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp
index 9342c2c6ed..d947b894aa 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 
diff --git a/libraries/shared/src/HifiSockAddr.cpp b/libraries/shared/src/HifiSockAddr.cpp
index a658c75a44..44eedc6b17 100644
--- a/libraries/shared/src/HifiSockAddr.cpp
+++ b/libraries/shared/src/HifiSockAddr.cpp
@@ -8,6 +8,7 @@
 
 #include "HifiSockAddr.h"
 
+#include 
 #include 
 #include 
 
diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp
index adcb7357bf..8efbf5782a 100644
--- a/libraries/shared/src/Node.cpp
+++ b/libraries/shared/src/Node.cpp
@@ -18,6 +18,7 @@
 #include "Node.h"
 #include "SharedUtil.h"
 
+#include 
 #include 
 
 const QString UNKNOWN_NodeType_t_NAME = "Unknown";
diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp
index 9a1317de88..c5ad709fa4 100644
--- a/libraries/shared/src/NodeList.cpp
+++ b/libraries/shared/src/NodeList.cpp
@@ -10,6 +10,7 @@
 #include 
 #include 
 
+#include 
 #include 
 #include 
 

From 15ba5f7ab1cc15204b8a17f5aa48c49f9e7acf32 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 13:48:31 -0800
Subject: [PATCH 076/153] correct OctreeServer config parsing

---
 libraries/octree-server/src/OctreeServer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp
index 73522565f9..6e58a0a987 100644
--- a/libraries/octree-server/src/OctreeServer.cpp
+++ b/libraries/octree-server/src/OctreeServer.cpp
@@ -433,7 +433,7 @@ void OctreeServer::setArguments(int argc, char** argv) {
 void OctreeServer::parsePayload() {
 
     if (getPayload().size() > 0) {
-        QString config((const char*) _payload);
+        QString config(_payload);
 
         // Now, parse the config
         QStringList configList = config.split(" ");

From 7d278300647a66805e510dfb20c1498fdf72a6ba Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 14:23:39 -0800
Subject: [PATCH 077/153] domain-server patches while looking for payload/pool
 errors

---
 domain-server/src/DomainServer.cpp  | 8 ++++----
 libraries/shared/src/Assignment.cpp | 8 +-------
 libraries/shared/src/Assignment.h   | 3 +--
 3 files changed, 6 insertions(+), 13 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 6e4a4cdff4..b4ec326d37 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -575,7 +575,7 @@ void DomainServer::prepopulateStaticAssignmentFile() {
 
             Assignment voxelServerAssignment(Assignment::CreateCommand,
                                              Assignment::VoxelServerType,
-                                             (assignmentPool.isEmpty() ? NULL : assignmentPool.toLocal8Bit().constData()));
+                                             assignmentPool);
             
             voxelServerAssignment.setPayload(config.toUtf8());
 
@@ -615,8 +615,8 @@ void DomainServer::prepopulateStaticAssignmentFile() {
             }
 
             Assignment particleServerAssignment(Assignment::CreateCommand,
-                                             Assignment::ParticleServerType,
-                                             (assignmentPool.isEmpty() ? NULL : assignmentPool.toLocal8Bit().constData()));
+                                                Assignment::ParticleServerType,
+                                                assignmentPool);
             
             particleServerAssignment.setPayload(config.toLocal8Bit());
 
@@ -689,7 +689,7 @@ Assignment* DomainServer::deployableAssignmentForRequest(Assignment& requestAssi
         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;
diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp
index 2bff4ddcd0..76ee4b04cf 100644
--- a/libraries/shared/src/Assignment.cpp
+++ b/libraries/shared/src/Assignment.cpp
@@ -14,9 +14,6 @@
 
 #include "Assignment.h"
 
-const char IPv4_ADDRESS_DESIGNATOR = 4;
-const char IPv6_ADDRESS_DESIGNATOR = 6;
-
 Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) {
     switch (nodeType) {
         case NodeType::AudioMixer:
@@ -95,10 +92,7 @@ Assignment::Assignment(const QByteArray& packet) :
     }
     
     packetStream >> _pool;
-    
-    if (!packetStream.atEnd()) {
-        _payload = packet.mid(packetStream.device()->pos());
-    }
+    packetStream >> _payload;
 }
 
 #ifdef WIN32
diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h
index 418ea7fe09..fb69271db3 100644
--- a/libraries/shared/src/Assignment.h
+++ b/libraries/shared/src/Assignment.h
@@ -20,7 +20,6 @@
 #include "NodeList.h"
 
 const int MAX_PAYLOAD_BYTES = 1024;
-const int MAX_ASSIGNMENT_POOL_BYTES = 64 + sizeof('\0');
 
 const QString emptyPool = QString();
 
@@ -76,7 +75,7 @@ public:
     Assignment::Location getLocation() const { return _location; }
 
     const QByteArray& getPayload() const { return _payload; }
-    void setPayload(const QByteArray& payload) { _payload = payload; }
+    void setPayload(const QByteArray& payload) { _payload = payload.left(MAX_PAYLOAD_BYTES); }
 
     void setPool(const QString& pool) { _pool = pool; };
     const QString& getPool() const { return _pool; }

From dbecb96a58b65895249fcfbbdd9d196722a315e7 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 15:08:50 -0800
Subject: [PATCH 078/153] remove assignmentQueueMutex now that DS concurrency
 is gone

---
 domain-server/src/DomainServer.cpp | 27 ++++-----------------------
 domain-server/src/DomainServer.h   |  1 -
 2 files changed, 4 insertions(+), 24 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index b4ec326d37..a124af4b40 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -38,7 +38,6 @@ 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(),
     _assignmentQueue(),
     _staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())),
     _staticAssignmentFileData(NULL),
@@ -467,9 +466,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
             
             // 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();
         }
     } else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
         if (path.startsWith(URI_NODE)) {
@@ -514,9 +511,7 @@ void DomainServer::addReleasedAssignmentBackToQueue(Assignment* releasedAssignme
             _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
@@ -646,22 +641,18 @@ Assignment* DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkI
     // pull the UUID passed with the check in
 
     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;
             } else {
                 // no match, push deque iterator forwards
                 assignment++;
             }
         }
-
-        _assignmentQueueMutex.unlock();
+        
     } else {
         for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
             if (_staticAssignments[i].getUUID() == checkInUUID) {
@@ -678,8 +669,6 @@ Assignment* DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkI
 }
 
 Assignment* DomainServer::deployableAssignmentForRequest(Assignment& requestAssignment) {
-    _assignmentQueueMutex.lock();
-
     // 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();
@@ -713,22 +702,18 @@ Assignment* DomainServer::deployableAssignmentForRequest(Assignment& requestAssi
             }
 
             // stop looping, we've handed out an assignment
-            _assignmentQueueMutex.unlock();
             return deployableAssignment;
         } else {
             // push forward the iterator to check the next assignment
             assignment++;
         }
     }
-
-    _assignmentQueueMutex.unlock();
+    
     return NULL;
 }
 
 void DomainServer::removeAssignmentFromQueue(Assignment* removableAssignment) {
 
-    _assignmentQueueMutex.lock();
-
     std::deque::iterator assignment = _assignmentQueue.begin();
 
     while (assignment != _assignmentQueue.end()) {
@@ -740,8 +725,6 @@ void DomainServer::removeAssignmentFromQueue(Assignment* removableAssignment) {
             assignment++;
         }
     }
-
-    _assignmentQueueMutex.unlock();
 }
 
 bool DomainServer::checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
@@ -796,10 +779,8 @@ void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
             _staticAssignments[i].resetUUID();
 
             qDebug() << "Adding static assignment to queue -" << _staticAssignments[i];
-
-            _assignmentQueueMutex.lock();
-            _assignmentQueue.push_back(&_staticAssignments[i]);
-            _assignmentQueueMutex.unlock();
+            
+            _assignmentQueue.push_back(&_staticAssignments[i]);gs
         }
     }
 }
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index 26cdc765ba..ca83436c35 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -49,7 +49,6 @@ private:
     
     HTTPManager _HTTPManager;
     
-    QMutex _assignmentQueueMutex;
     std::deque _assignmentQueue;
     
     QFile _staticAssignmentFile;

From d6431a3e7a5596fedf88e7be4bccd7212107d561 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 15:09:46 -0800
Subject: [PATCH 079/153] remove an extra gs

---
 domain-server/src/DomainServer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index a124af4b40..603041be54 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -780,7 +780,7 @@ void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
 
             qDebug() << "Adding static assignment to queue -" << _staticAssignments[i];
             
-            _assignmentQueue.push_back(&_staticAssignments[i]);gs
+            _assignmentQueue.push_back(&_staticAssignments[i]);
         }
     }
 }

From 810877b8e411d7c69fa524008631e3bf1afe65a3 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 16:05:21 -0800
Subject: [PATCH 080/153] fix some silly data-server mistakes

---
 data-server/src/DataServer.cpp            | 19 ++++++++++++-------
 data-server/src/DataServer.h              |  2 ++
 libraries/shared/src/DataServerClient.cpp |  2 +-
 3 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp
index 9044e6cf6a..b174ea293c 100644
--- a/data-server/src/DataServer.cpp
+++ b/data-server/src/DataServer.cpp
@@ -24,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);
     
@@ -59,7 +60,8 @@ void DataServer::readPendingDatagrams() {
     
     while (_socket.hasPendingDatagrams()) {
         receivedPacket.resize(_socket.pendingDatagramSize());
-        _socket.readDatagram(receivedPacket.data(), _socket.pendingDatagramSize());
+        _socket.readDatagram(receivedPacket.data(), _socket.pendingDatagramSize(),
+                             senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
         
         PacketType requestType = packetTypeForPacket(receivedPacket);
         
@@ -119,11 +121,11 @@ 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 a reply packet with the sequence number
-                        QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm);
+                        QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm, _uuid);
                         
                         replyPacket.append(sequenceNumber);
                         
-                        _socket.writeDatagram(replyPacket,
+                        qint64 size = _socket.writeDatagram(replyPacket,
                                               senderSockAddr.getAddress(), senderSockAddr.getPort());
                     }
                     
@@ -131,14 +133,17 @@ void DataServer::readPendingDatagrams() {
                 } else {
                     // setup a send packet with the returned data
                     // leverage the packetData sent by overwriting and appending
-                    QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend);
+                    QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend, _uuid);
+                    sendPacket.append(sequenceNumber);
                     
-                    if (!receivedPacket.mid(numReceivedHeaderBytes).startsWith("uuid")) {
+                    if (!receivedPacket.mid(numReceivedHeaderBytes + sizeof(sequenceNumber)).startsWith("uuid")) {
                         
                         const char MULTI_KEY_VALUE_SEPARATOR = '|';
                         
                         // pull the key that specifies the data the user is putting/getting, null terminate it
-                        QString keyListString(receivedPacket.mid(numReceivedHeaderBytes));
+                        QString keyListString;
+                        packetStream >> keyListString;
+                        
                         QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR);
                         
                         foreach (const QString& dataKey, keyList) {
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/libraries/shared/src/DataServerClient.cpp b/libraries/shared/src/DataServerClient.cpp
index fd003aa3bb..7ca951ba29 100644
--- a/libraries/shared/src/DataServerClient.cpp
+++ b/libraries/shared/src/DataServerClient.cpp
@@ -21,7 +21,7 @@ quint8 DataServerClient::_sequenceNumber = 0;
 
 const char MULTI_KEY_VALUE_SEPARATOR = '|';
 
-const char DATA_SERVER_HOSTNAME[] = "data.highfidelity.io";
+const char DATA_SERVER_HOSTNAME[] = "localhost";
 const unsigned short DATA_SERVER_PORT = 3282;
 
 const HifiSockAddr& DataServerClient::dataServerSockAddr() {

From 2f6961f4558df5acd35d099a7846b5527c98f6d4 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 16:06:12 -0800
Subject: [PATCH 081/153] remove debug and wrong data-server address

---
 data-server/src/DataServer.cpp            | 3 +--
 libraries/shared/src/DataServerClient.cpp | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp
index b174ea293c..91d2b7f496 100644
--- a/data-server/src/DataServer.cpp
+++ b/data-server/src/DataServer.cpp
@@ -125,8 +125,7 @@ void DataServer::readPendingDatagrams() {
                         
                         replyPacket.append(sequenceNumber);
                         
-                        qint64 size = _socket.writeDatagram(replyPacket,
-                                              senderSockAddr.getAddress(), senderSockAddr.getPort());
+                        _socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort());
                     }
                     
                     freeReplyObject(reply);
diff --git a/libraries/shared/src/DataServerClient.cpp b/libraries/shared/src/DataServerClient.cpp
index 7ca951ba29..fd003aa3bb 100644
--- a/libraries/shared/src/DataServerClient.cpp
+++ b/libraries/shared/src/DataServerClient.cpp
@@ -21,7 +21,7 @@ quint8 DataServerClient::_sequenceNumber = 0;
 
 const char MULTI_KEY_VALUE_SEPARATOR = '|';
 
-const char DATA_SERVER_HOSTNAME[] = "localhost";
+const char DATA_SERVER_HOSTNAME[] = "data.highfidelity.io";
 const unsigned short DATA_SERVER_PORT = 3282;
 
 const HifiSockAddr& DataServerClient::dataServerSockAddr() {

From fe192ca2dece38dc8435f8c4feba79142a674d30 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 14:23:39 -0800
Subject: [PATCH 082/153] domain-server patches while looking for payload/pool
 errors


From 740d11027ccfb5605258731ff0adb9c128099b14 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Wed, 29 Jan 2014 17:20:32 -0800
Subject: [PATCH 083/153] add QDataStream >> operator to Assignment

---
 libraries/shared/src/Assignment.cpp | 32 ++++++++++++-----------------
 libraries/shared/src/Assignment.h   |  1 +
 2 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp
index 76ee4b04cf..f8dcf3edca 100644
--- a/libraries/shared/src/Assignment.cpp
+++ b/libraries/shared/src/Assignment.cpp
@@ -52,6 +52,7 @@ Assignment::Assignment() :
 }
 
 Assignment::Assignment(Assignment::Command command, Assignment::Type type, const QString& pool, Assignment::Location location) :
+    _uuid(),
     _command(command),
     _type(type),
     _pool(pool),
@@ -82,17 +83,7 @@ Assignment::Assignment(const QByteArray& packet) :
     QDataStream packetStream(packet);
     packetStream.skipRawData(numBytesForPacketHeader(packet));
     
-    uchar assignmentType;
-    packetStream >> assignmentType;
-    _type = (Assignment::Type) assignmentType;
-    
-    if (_command != Assignment::RequestCommand) {
-        // read the GUID for this assignment
-        packetStream >> _uuid;
-    }
-    
-    packetStream >> _pool;
-    packetStream >> _payload;
+    packetStream >> *this;
 }
 
 #ifdef WIN32
@@ -157,14 +148,17 @@ QDebug operator<<(QDebug debug, const Assignment &assignment) {
 }
 
 QDataStream& operator<<(QDataStream &out, const Assignment& assignment) {
-    out << (quint8) assignment._type;
-    
-    // pack the UUID for this assignment, if this is an assignment create or deploy
-    if (assignment._command != Assignment::RequestCommand) {
-        out << assignment._uuid;
-    }
-    
-    out << assignment._pool << assignment._payload;
+    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 fb69271db3..dd34751b57 100644
--- a/libraries/shared/src/Assignment.h
+++ b/libraries/shared/src/Assignment.h
@@ -91,6 +91,7 @@ public:
 
     friend QDebug operator<<(QDebug debug, const Assignment& assignment);
     friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment);
+    friend QDataStream& operator>>(QDataStream &in, Assignment& assignment);
 
 protected:
     QUuid _uuid; /// the 16 byte UUID for this assignment

From 08b06cc59c048fa8553bab91067dce77cc049bc4 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Wed, 29 Jan 2014 17:52:17 -0800
Subject: [PATCH 084/153] support for global collision callbacks in JS

---
 examples/gameoflife.js                        | 133 ++++++++++++++++++
 examples/globalCollisionsExample.js           |  26 ++++
 examples/gun.js                               |   8 +-
 examples/paintGun.js                          |   6 +-
 interface/src/Application.cpp                 |  11 ++
 libraries/particles/src/Particle.cpp          | 120 +++++-----------
 libraries/particles/src/Particle.h            |  27 ++--
 .../particles/src/ParticleCollisionSystem.cpp |  15 ++
 .../particles/src/ParticleCollisionSystem.h   |  10 +-
 .../src/ParticlesScriptingInterface.h         |  12 ++
 libraries/script-engine/src/ScriptEngine.cpp  |   7 +-
 libraries/script-engine/src/ScriptEngine.h    |   4 +-
 libraries/shared/src/RegisteredMetaTypes.h    |   1 +
 libraries/shared/src/SharedUtil.cpp           | 100 -------------
 libraries/shared/src/SharedUtil.h             |  18 ---
 libraries/voxels/src/VoxelDetail.cpp          |  37 +++++
 libraries/voxels/src/VoxelDetail.h            |  36 +++++
 .../voxels/src/VoxelEditPacketSender.cpp      | 100 +++++++++++++
 libraries/voxels/src/VoxelEditPacketSender.h  |   1 +
 libraries/voxels/src/VoxelTreeElement.h       |   1 -
 .../voxels/src/VoxelsScriptingInterface.h     |  16 ---
 21 files changed, 448 insertions(+), 241 deletions(-)
 create mode 100644 examples/gameoflife.js
 create mode 100644 examples/globalCollisionsExample.js
 create mode 100644 libraries/voxels/src/VoxelDetail.cpp
 create mode 100644 libraries/voxels/src/VoxelDetail.h

diff --git a/examples/gameoflife.js b/examples/gameoflife.js
new file mode 100644
index 0000000000..09fae07204
--- /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");
+Agent.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..3f8eefe3e2 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'); " +
                          " } " +
diff --git a/examples/paintGun.js b/examples/paintGun.js
index 0cbe3ff366..b78e6abb0b 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'); " +
                          " } " +
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 6578618e5f..f300c79c67 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1879,6 +1879,17 @@ void Application::init() {
 
     _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);
     _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1);
diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp
index 933bf36830..557d4ca87f 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
@@ -831,7 +833,7 @@ void Particle::update(const uint64_t& now) {
     bool shouldDie = (getAge() > getLifetime()) || getShouldDie() || (!isInHand && isStopped && isReallyOld);
     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) {
@@ -853,106 +855,62 @@ 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;
     _created = usecTimestampNow() - ageInUsecs;
diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h
index 456eb7ef4f..ec2e389cb5 100644
--- a/libraries/particles/src/Particle.h
+++ b/libraries/particles/src/Particle.h
@@ -20,13 +20,16 @@
 #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;
@@ -227,7 +230,8 @@ public:
     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
@@ -318,11 +322,9 @@ 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);
 
@@ -366,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(); }
@@ -414,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 a2394935b1..143b1ddcbd 100644
--- a/libraries/particles/src/ParticleCollisionSystem.cpp
+++ b/libraries/particles/src/ParticleCollisionSystem.cpp
@@ -68,6 +68,17 @@ void ParticleCollisionSystem::checkParticle(Particle* particle) {
     updateCollisionWithAvatars(particle);
 }
 
+void ParticleCollisionSystem::emitGlobalParicleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails) {
+    ParticleID particleID = particle->getParticleID();
+    emit particleCollisionWithVoxel(particleID, *voxelDetails);
+}
+
+void ParticleCollisionSystem::emitGlobalParicleCollisionWithParticle(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);
@@ -83,6 +94,9 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
         // 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
+        emitGlobalParicleCollisionWithVoxel(particle, voxelDetails);
+
         updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
         collisionInfo._penetration /= (float)(TREE_SCALE);
         particle->applyHardCollision(collisionInfo);
@@ -110,6 +124,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
         if (glm::dot(relativeVelocity, penetration) > 0.0f) {
             particleA->collisionWithParticle(particleB);
             particleB->collisionWithParticle(particleA);
+            emitGlobalParicleCollisionWithParticle(particleA, particleB);
 
             glm::vec3 axis = glm::normalize(penetration);
             glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis;
diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h
index 4a61693fa6..9baf9bfd05 100644
--- a/libraries/particles/src/ParticleCollisionSystem.h
+++ b/libraries/particles/src/ParticleCollisionSystem.h
@@ -31,7 +31,8 @@ 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, 
@@ -51,9 +52,14 @@ public:
     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 emitGlobalParicleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails);
+    void emitGlobalParicleCollisionWithParticle(Particle* particleA, Particle* particleB);
 
     ParticleEditPacketSender* _packetSender;
     ParticleTree* _particles;
diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h
index 9a3ceb51af..2a6a5e24f0 100644
--- a/libraries/particles/src/ParticlesScriptingInterface.h
+++ b/libraries/particles/src/ParticlesScriptingInterface.h
@@ -53,7 +53,19 @@ 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);
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 28cb49f6f7..9db1254f80 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 
@@ -114,10 +115,12 @@ 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);
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index ef55e14109..c2188cca63 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -41,10 +41,10 @@ public:
     ~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; }
diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h
index de667c9ed8..b198add7c2 100644
--- a/libraries/shared/src/RegisteredMetaTypes.h
+++ b/libraries/shared/src/RegisteredMetaTypes.h
@@ -22,6 +22,7 @@ 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);
diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp
index 54a4291c25..e2c5b912c0 100644
--- a/libraries/shared/src/SharedUtil.cpp
+++ b/libraries/shared/src/SharedUtil.cpp
@@ -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);
 }
diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h
index 399bf40204..c25d2c5f0d 100644
--- a/libraries/shared/src/SharedUtil.h
+++ b/libraries/shared/src/SharedUtil.h
@@ -94,27 +94,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
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..e776a8f665 100644
--- a/libraries/voxels/src/VoxelEditPacketSender.cpp
+++ b/libraries/voxels/src/VoxelEditPacketSender.cpp
@@ -14,6 +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(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;
+}
+
+
 
 void VoxelEditPacketSender::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail) {
     // allows app to disable sending if for example voxels have been disabled
diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h
index ec9b74dff8..c09c3b533a 100644
--- a/libraries/voxels/src/VoxelEditPacketSender.h
+++ b/libraries/voxels/src/VoxelEditPacketSender.h
@@ -12,6 +12,7 @@
 #define __shared__VoxelEditPacketSender__
 
 #include 
+#include "VoxelDetail.h"
 
 /// Utility for processing, packing, queueing and sending of outbound edit voxel messages.
 class VoxelEditPacketSender :  public OctreeEditPacketSender {
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.h b/libraries/voxels/src/VoxelsScriptingInterface.h
index 97bdfb2c59..f5f5114154 100644
--- a/libraries/voxels/src/VoxelsScriptingInterface.h
+++ b/libraries/voxels/src/VoxelsScriptingInterface.h
@@ -57,20 +57,4 @@ 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;
-};
-
 #endif /* defined(__hifi__VoxelsScriptingInterface__) */

From a04879936735013c40b7ac983805541159b7d0f3 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Wed, 29 Jan 2014 20:54:22 -0800
Subject: [PATCH 085/153] fix header comment

---
 examples/findParticleExample.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/findParticleExample.js b/examples/findParticleExample.js
index f582ee6469..bd20e6ded7 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.

From 01c369716eadc8ad55cb13b909dfedb621ecdd40 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Wed, 29 Jan 2014 20:55:12 -0800
Subject: [PATCH 086/153] implement a basic exampple of ride along with a
 particle

---
 examples/rideAlongWithAParticleExample.js | 50 +++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 examples/rideAlongWithAParticleExample.js

diff --git a/examples/rideAlongWithAParticleExample.js b/examples/rideAlongWithAParticleExample.js
new file mode 100644
index 0000000000..eca9fe7f4c
--- /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 {
+        Agent.stop();
+    }
+                              
+    iteration++;
+    print("iteration="+iteration);
+}
+
+
+// register the call back so it fires before each data send
+Agent.willSendVisualDataCallback.connect(rideWithParticle);
+

From 84ef757af43ed022919b4fc37437b4aa43e63783 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Thu, 30 Jan 2014 09:45:09 -0800
Subject: [PATCH 087/153] fix avatar count, HandData crash (closes #1754)

---
 interface/src/Application.cpp          | 3 ++-
 interface/src/avatar/AvatarManager.cpp | 6 +++++-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 2efcd5b121..b30fc37591 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -3225,7 +3225,8 @@ void Application::displayStats() {
 
     glPointSize(1.0f);
 
-    int totalAvatars = _avatarManager.size();
+    // 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) {
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 06b550b9d6..57f187603b 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -234,7 +234,11 @@ 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
         dummyAvatarByteArray.resize(numDummyByteArrayHeaderBytes);
-        dummyAvatarByteArray += datagram.mid(bytesRead);
+        
+        // make this Avatar's UUID the UUID in the packet and tack the remaining data onto the end
+        dummyAvatarByteArray.replace(numDummyByteArrayHeaderBytes - NUM_BYTES_RFC4122_UUID,
+                                     NUM_BYTES_RFC4122_UUID + datagram.size() - bytesRead,
+                                     datagram.mid(bytesRead));
         
         // have the matching (or new) avatar parse the data from the packet
         bytesRead += matchingAvatar->parseData(dummyAvatarByteArray);

From 49d04ce14e02bf1030e8836e1d83b9e88ae90407 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Thu, 30 Jan 2014 10:42:29 -0800
Subject: [PATCH 088/153] data-server repairs for get responses

---
 data-server/src/DataServer.cpp | 36 ++++++++++++++++++++++------------
 1 file changed, 23 insertions(+), 13 deletions(-)

diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp
index 91d2b7f496..98b3f9c298 100644
--- a/data-server/src/DataServer.cpp
+++ b/data-server/src/DataServer.cpp
@@ -133,17 +133,26 @@ void DataServer::readPendingDatagrams() {
                     // setup a send packet with the returned data
                     // leverage the packetData sent by overwriting and appending
                     QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend, _uuid);
-                    sendPacket.append(sequenceNumber);
+                    QDataStream sendPacketStream(&sendPacket, QIODevice::Append);
                     
-                    if (!receivedPacket.mid(numReceivedHeaderBytes + sizeof(sequenceNumber)).startsWith("uuid")) {
+                    sendPacketStream << sequenceNumber;
+                    
+                    // 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 = '|';
                         
-                        // pull the key that specifies the data the user is putting/getting, null terminate it
-                        QString keyListString;
-                        packetStream >> keyListString;
+                        // append the keyListString back to the sendPacket
+                        sendPacketStream << keyListString;
                         
                         QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR);
+                        QString valueList;
                         
                         foreach (const QString& dataKey, keyList) {
                             qDebug("Sending command to redis: GET uuid:%s:%s",
@@ -155,25 +164,26 @@ void DataServer::readPendingDatagrams() {
                             
                             if (reply->len) {
                                 // copy the value that redis returned
-                                sendPacket.append(reply->str, reply->len);
-                                
+                                valueList.append(reply->str);
                             } else {
                                 // didn't find a value - insert a space
-                                sendPacket.append(' ');
+                                valueList.append(' ');
                             }
                             
                             // add the multi-value separator
-                            sendPacket.append(MULTI_KEY_VALUE_SEPARATOR);
+                            valueList.append(MULTI_KEY_VALUE_SEPARATOR);
                             
                             freeReplyObject(reply);
                         }
                         
                         // null terminate the packet we're sending back (erases the trailing separator)
-                        sendPacket[sendPacket.size() - 1] = '\0';
+                        valueList[valueList.size() - 1] = '\0';
+                        sendPacketStream << valueList;
                     } else {
-                        // user is asking for a UUID matching username, copy the UUID we found
-                        sendPacket.append(uuidStringWithoutCurlyBraces(parsedUUID));
-                        sendPacket.append('\0');
+                        // user was asking for their UUID
+                        sendPacketStream << userString;
+                        sendPacketStream << QString("uuid");
+                        sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID);
                     }
                     
                     // reply back with the send packet

From 2294b3a2cd398ce8795be0f020818abde4ae4c39 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Thu, 30 Jan 2014 12:14:28 -0800
Subject: [PATCH 089/153] use a proper string list in DataServer for values

---
 data-server/src/DataServer.cpp | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp
index 98b3f9c298..48462040d5 100644
--- a/data-server/src/DataServer.cpp
+++ b/data-server/src/DataServer.cpp
@@ -152,7 +152,7 @@ void DataServer::readPendingDatagrams() {
                         sendPacketStream << keyListString;
                         
                         QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR);
-                        QString valueList;
+                        QStringList valueList;
                         
                         foreach (const QString& dataKey, keyList) {
                             qDebug("Sending command to redis: GET uuid:%s:%s",
@@ -164,21 +164,17 @@ void DataServer::readPendingDatagrams() {
                             
                             if (reply->len) {
                                 // copy the value that redis returned
-                                valueList.append(reply->str);
+                                valueList << reply->str;
                             } else {
                                 // didn't find a value - insert a space
-                                valueList.append(' ');
+                                valueList << QChar(' ');
                             }
                             
-                            // add the multi-value separator
-                            valueList.append(MULTI_KEY_VALUE_SEPARATOR);
-                            
                             freeReplyObject(reply);
                         }
                         
-                        // null terminate the packet we're sending back (erases the trailing separator)
-                        valueList[valueList.size() - 1] = '\0';
-                        sendPacketStream << valueList;
+                        // append the value QStringList using the right separator
+                        sendPacketStream << valueList.join(MULTI_KEY_VALUE_SEPARATOR);
                     } else {
                         // user was asking for their UUID
                         sendPacketStream << userString;

From da5e0f55b6d8bf4ed9b53860599f03928f8cebd5 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Thu, 30 Jan 2014 12:26:50 -0800
Subject: [PATCH 090/153] add easier support for key codes in JS by adding text
 property and auto-detecting isShifted

---
 examples/controllerExample.js              | 101 +++++++++++++-
 libraries/script-engine/src/EventTypes.cpp | 155 ++++++++++++++++++++-
 libraries/script-engine/src/EventTypes.h   |  11 +-
 3 files changed, 260 insertions(+), 7 deletions(-)

diff --git a/examples/controllerExample.js b/examples/controllerExample.js
index c1b33b24a5..95561dc9dc 100644
--- a/examples/controllerExample.js
+++ b/examples/controllerExample.js
@@ -48,6 +48,15 @@ function checkController() {
 
 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");
     }
@@ -77,14 +86,102 @@ Agent.willSendVisualDataCallback.connect(checkController);
 
 // Map keyPress and mouse move events to our callbacks
 Controller.keyPressEvent.connect(keyPressEvent);
-var AKeyEvent = {
+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
-Controller.captureKeyEvents(AKeyEvent);
+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);
diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp
index 5c4ab7f2a7..ff27282b73 100644
--- a/libraries/script-engine/src/EventTypes.cpp
+++ b/libraries/script-engine/src/EventTypes.cpp
@@ -8,22 +8,72 @@
 //  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::ControlModifier);
+    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) {
@@ -65,16 +115,113 @@ void registerEventTypes(QScriptEngine* engine) {
 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.key = object.property("key").toVariant().toInt();
-    event.isShifted = object.property("isShifted").toVariant().toBool();
+
+    event.isValid = false; // assume the worst
     event.isMeta = object.property("isMeta").toVariant().toBool();
-    event.isValid = object.property("key").isValid();
+    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) {
diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h
index c3764b2619..81cba03cea 100644
--- a/libraries/script-engine/src/EventTypes.h
+++ b/libraries/script-engine/src/EventTypes.h
@@ -24,10 +24,19 @@ public:
     KeyEvent();
     KeyEvent(const QKeyEvent& event);
     inline bool operator==(const KeyEvent& other) const { 
-                            return other.key == key && other.isShifted == isShifted && other.isMeta == isMeta; }
+                            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;
 };
 

From 8b9f6813ca085246564a3d6e5557bc3bebb6b48b Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Thu, 30 Jan 2014 12:26:50 -0800
Subject: [PATCH 091/153] add easier support for key codes in JS by adding text
 property and auto-detecting isShifted

---
 examples/controllerExample.js              | 101 +++++++++++++-
 libraries/script-engine/src/EventTypes.cpp | 155 ++++++++++++++++++++-
 libraries/script-engine/src/EventTypes.h   |  11 +-
 3 files changed, 260 insertions(+), 7 deletions(-)

diff --git a/examples/controllerExample.js b/examples/controllerExample.js
index c1b33b24a5..95561dc9dc 100644
--- a/examples/controllerExample.js
+++ b/examples/controllerExample.js
@@ -48,6 +48,15 @@ function checkController() {
 
 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");
     }
@@ -77,14 +86,102 @@ Agent.willSendVisualDataCallback.connect(checkController);
 
 // Map keyPress and mouse move events to our callbacks
 Controller.keyPressEvent.connect(keyPressEvent);
-var AKeyEvent = {
+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
-Controller.captureKeyEvents(AKeyEvent);
+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);
diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp
index 5c4ab7f2a7..ff27282b73 100644
--- a/libraries/script-engine/src/EventTypes.cpp
+++ b/libraries/script-engine/src/EventTypes.cpp
@@ -8,22 +8,72 @@
 //  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::ControlModifier);
+    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) {
@@ -65,16 +115,113 @@ void registerEventTypes(QScriptEngine* engine) {
 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.key = object.property("key").toVariant().toInt();
-    event.isShifted = object.property("isShifted").toVariant().toBool();
+
+    event.isValid = false; // assume the worst
     event.isMeta = object.property("isMeta").toVariant().toBool();
-    event.isValid = object.property("key").isValid();
+    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) {
diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h
index c3764b2619..81cba03cea 100644
--- a/libraries/script-engine/src/EventTypes.h
+++ b/libraries/script-engine/src/EventTypes.h
@@ -24,10 +24,19 @@ public:
     KeyEvent();
     KeyEvent(const QKeyEvent& event);
     inline bool operator==(const KeyEvent& other) const { 
-                            return other.key == key && other.isShifted == isShifted && other.isMeta == isMeta; }
+                            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;
 };
 

From a6934605a456bd261e3196256428742045a56581 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Wed, 29 Jan 2014 20:55:12 -0800
Subject: [PATCH 092/153] implement a basic exampple of ride along with a
 particle

---
 examples/rideAlongWithAParticleExample.js | 50 +++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 examples/rideAlongWithAParticleExample.js

diff --git a/examples/rideAlongWithAParticleExample.js b/examples/rideAlongWithAParticleExample.js
new file mode 100644
index 0000000000..eca9fe7f4c
--- /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 {
+        Agent.stop();
+    }
+                              
+    iteration++;
+    print("iteration="+iteration);
+}
+
+
+// register the call back so it fires before each data send
+Agent.willSendVisualDataCallback.connect(rideWithParticle);
+

From ac780df8a63be17a705bb88f85a83fa6ff0bce92 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Thu, 30 Jan 2014 12:44:43 -0800
Subject: [PATCH 093/153] fix appending of QString value to valueList

---
 data-server/src/DataServer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp
index 48462040d5..43fc52fb06 100644
--- a/data-server/src/DataServer.cpp
+++ b/data-server/src/DataServer.cpp
@@ -164,7 +164,7 @@ void DataServer::readPendingDatagrams() {
                             
                             if (reply->len) {
                                 // copy the value that redis returned
-                                valueList << reply->str;
+                                valueList << QString(reply->str);
                             } else {
                                 // didn't find a value - insert a space
                                 valueList << QChar(' ');

From 181670f5d7bca20397bb9255c2ec346f621119c7 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka 
Date: Thu, 30 Jan 2014 12:53:02 -0800
Subject: [PATCH 094/153] Fixes for parsing FBX text encoding.

---
 interface/src/renderer/FBXReader.cpp | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp
index 8a7f839b70..1a5171d100 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;

From d2464d89e3ea6d33b4492b16d94d957577b49eda Mon Sep 17 00:00:00 2001
From: stojce 
Date: Thu, 30 Jan 2014 22:23:31 +0100
Subject: [PATCH 095/153] #19493 - @ function can jump to locations as well as
 avatars

---
 interface/src/Application.cpp |  2 +-
 interface/src/Menu.cpp        | 89 ++++++++++++++++++++++++++---------
 interface/src/Menu.h          |  5 +-
 3 files changed, 71 insertions(+), 25 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 2efcd5b121..f420b2be45 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1093,7 +1093,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();
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";

From 78d9b798056044d379adca595ab8a08716a1fba4 Mon Sep 17 00:00:00 2001
From: Andrzej Kapolka 
Date: Thu, 30 Jan 2014 13:24:30 -0800
Subject: [PATCH 096/153] Another fix for Blender exports.  Closes #1762.

---
 interface/src/renderer/FBXReader.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp
index 1a5171d100..d128206c80 100644
--- a/interface/src/renderer/FBXReader.cpp
+++ b/interface/src/renderer/FBXReader.cpp
@@ -1121,6 +1121,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
     QVector modelIDs;
     QSet remainingModels;
     for (QHash::const_iterator model = models.constBegin(); model != models.constEnd(); model++) {
+        // 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()) {

From 356222ba15dd17b3a3d01b14f1e791cebcdd5778 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Thu, 30 Jan 2014 14:33:36 -0800
Subject: [PATCH 097/153] removal of persistence in DS for static assignments

---
 domain-server/src/DomainServer.cpp  | 590 +++++++++++-----------------
 domain-server/src/DomainServer.h    |  41 +-
 libraries/shared/src/Assignment.cpp |   2 +-
 3 files changed, 252 insertions(+), 381 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 603041be54..f191fdd3ac 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,64 +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),
+    _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(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);
@@ -93,8 +68,145 @@ 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);
+    }
+}
+
+// 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;
+    
+    QString multiConfig(configString);
+    QStringList multiConfigList = multiConfig.split(";");
+    
+    // read each config to a payload for this type of assignment
+    for (int i = 0; i < multiConfigList.size(); i++) {
+        QString config = multiConfigList.at(i);
+        
+        qDebug("type %d config[%d] = %s", type, i, config.toLocal8Bit().constData());
+        
+        // Now, parse the config to check for a pool
+        const QString 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);
+        }
+        
+        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 (Assignment::Type defaultedType = Assignment::AudioMixerType; defaultedType != Assignment::AllTypes; defaultedType++) {
+        if (!excludedTypes.contains(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, defaultedType);
+            addStaticAssignmentToAssignmentHash(newAssignment);
+        }
+    }
 }
 
 void DomainServer::readAvailableDatagrams() {
@@ -147,13 +259,11 @@ void DomainServer::readAvailableDatagrams() {
                     << NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer
                     << NodeType::MetavoxelServer;
                 
-                Assignment* matchingStaticAssignment = NULL;
+                SharedAssignmentPointer matchingStaticAssignment;
                 
                 if (!STATICALLY_ASSIGNED_NODES.contains(nodeType)
-                    || ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
-                        || checkInWithUUIDMatchesExistingNode(nodePublicAddress,
-                                                              nodeLocalAddress,
-                                                              nodeUUID)))
+                    || (matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
+                    || checkInWithUUIDMatchesExistingNode(nodePublicAddress, nodeLocalAddress, nodeUUID))
                 {
                     SharedNodePointer checkInNode = nodeList->addOrUpdateNode(nodeUUID,
                                                                               nodeType,
@@ -166,19 +276,13 @@ void DomainServer::readAvailableDatagrams() {
                     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);
                     }
                     
-                    
                     quint8 numInterestTypes = 0;
                     packetStream >> numInterestTypes;
                     
@@ -216,7 +320,7 @@ void DomainServer::readAvailableDatagrams() {
                     qDebug("Received a request for assignment type %i from %s.",
                            requestAssignment.getType(), qPrintable(senderSockAddr.getAddress().toString()));
                     
-                    Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
+                    SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
                     
                     if (assignmentToDeploy) {
                         // give this assignment out, either the type matches or the requestor said they will take any
@@ -224,15 +328,10 @@ void DomainServer::readAvailableDatagrams() {
                         
                         QDataStream assignmentStream(&assignmentPacket, QIODevice::Append);
                         
-                        assignmentStream << *assignmentToDeploy;
+                        assignmentStream << assignmentToDeploy;
                         
                         nodeList->getNodeSocket().writeDatagram(assignmentPacket,
                                                                 senderSockAddr.getAddress(), senderSockAddr.getPort());
-                        
-                        if (assignmentToDeploy->getNumberOfInstances() == 0) {
-                            // there are no more instances of this script to send out, delete it
-                            delete assignmentToDeploy;
-                        }
                     }
                     
                 } else {
@@ -280,70 +379,6 @@ QJsonObject jsonObjectForNode(Node* node) {
     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";
     
@@ -372,24 +407,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)->getPool().isEmpty()) {
-                    queuedAssignmentJSON[JSON_KEY_POOL] = (*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;
@@ -427,11 +457,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";
@@ -465,8 +492,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
-            _assignmentQueue.push_back(scriptAssignment);
+            _assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment));
         }
     } else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
         if (path.startsWith(URI_NODE)) {
@@ -501,228 +527,99 @@ 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
-            _assignmentQueue.push_back(&_staticAssignments[i]);
-
-        } 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);
+    _assignmentQueue.enqueue(assignment);
 }
 
 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);
     }
 }
 
-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);
-            
-            voxelServerAssignment.setPayload(config.toUtf8());
-
-            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);
-            
-            particleServerAssignment.setPayload(config.toLocal8Bit());
-
-            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(QByteArray(_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, NodeType_t nodeType) {
-    // pull the UUID passed with the check in
-
+SharedAssignmentPointer DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) {
     if (_hasCompletedRestartHold) {
-        // 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
-                return *assignment;
-            } else {
-                // no match, push deque iterator forwards
-                assignment++;
+        // look for a match in the assignment hash
+        
+        QQueue::iterator i = _assignmentQueue.begin();
+        
+        while (i != _assignmentQueue.end()) {
+            if (i->data()->getType() == nodeType && i->data()->getUUID() == checkInUUID) {
+                return _assignmentQueue.takeAt(i - _assignmentQueue.begin());
             }
         }
-        
     } 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) {
+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)->getPool().isEmpty() && requestAssignment.getPool().isEmpty();
-        bool assignmentPoolsMatch = (*assignment)->getPool() == requestAssignment.getPool();
+        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
-            return deployableAssignment;
         } else {
             // push forward the iterator to check the next assignment
-            assignment++;
+            ++sharedAssignment;
         }
     }
     
-    return NULL;
+    return SharedAssignmentPointer();
 }
 
-void DomainServer::removeAssignmentFromQueue(Assignment* removableAssignment) {
-
-    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;
         }
     }
 }
@@ -749,43 +646,24 @@ 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::iterator staticAssignment = _staticAssignmentHash.begin();
+    while (staticAssignment != _staticAssignmentHash.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];
-            
-            _assignmentQueue.push_back(&_staticAssignments[i]);
+            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 ca83436c35..06ae3c2d0e 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,36 +34,30 @@ 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, NodeType_t nodeType);
-    Assignment* deployableAssignmentForRequest(Assignment& requestAssignment);
-    void removeAssignmentFromQueue(Assignment* removableAssignment);
+    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);
+    
+    SharedAssignmentPointer matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
+    SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
+    void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment);
     bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
                                             const HifiSockAddr& nodeLocalSocket,
                                             const QUuid& checkInUUI);
-    void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
+    void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
     
     HTTPManager _HTTPManager;
     
-    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/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp
index f8dcf3edca..a7dd5c36f8 100644
--- a/libraries/shared/src/Assignment.cpp
+++ b/libraries/shared/src/Assignment.cpp
@@ -144,7 +144,7 @@ const char* Assignment::getTypeName() const {
 QDebug operator<<(QDebug debug, const Assignment &assignment) {
     debug.nospace() << "UUID: " << qPrintable(assignment.getUUID().toString()) <<
         ", Type: " << assignment.getType();
-    return debug.nospace();
+    return debug.space();
 }
 
 QDataStream& operator<<(QDataStream &out, const Assignment& assignment) {

From 55049eeb6b358925ca95c36e54cf4a67b2fc7af1 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Thu, 30 Jan 2014 14:54:52 -0800
Subject: [PATCH 098/153] fix assignment packing and unpacking from DS to AC

---
 assignment-client/src/AssignmentFactory.cpp |  7 ++-
 domain-server/src/DomainServer.cpp          | 62 ++++++++-------------
 domain-server/src/DomainServer.h            |  3 -
 3 files changed, 28 insertions(+), 44 deletions(-)

diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp
index ef587c9b6d..5bf0417f22 100644
--- a/assignment-client/src/AssignmentFactory.cpp
+++ b/assignment-client/src/AssignmentFactory.cpp
@@ -19,10 +19,11 @@
 #include "metavoxels/MetavoxelServer.h"
 
 ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) {
-    int headerBytes = numBytesForPacketHeader(packet);
-    
+    QDataStream packetStream(packet);
+    packetStream.skipRawData(numBytesForPacketHeader(packet));
+
     quint8 packedType;
-    memcpy(&packedType, packet.data() + headerBytes, sizeof(packedType));
+    packetStream >> packedType;    
     
     Assignment::Type unpackedType = (Assignment::Type) packedType;
     
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index f191fdd3ac..0ab75c0088 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -261,9 +261,11 @@ void DomainServer::readAvailableDatagrams() {
                 
                 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))
-                    || checkInWithUUIDMatchesExistingNode(nodePublicAddress, nodeLocalAddress, nodeUUID))
+                    || nodeList->getInstance()->nodeWithUUID(nodeUUID))
                 {
                     SharedNodePointer checkInNode = nodeList->addOrUpdateNode(nodeUUID,
                                                                               nodeType,
@@ -313,29 +315,29 @@ void DomainServer::readAvailableDatagrams() {
                 }
             } else if (requestType == PacketTypeRequestAssignment) {
                 
-                if (_assignmentQueue.size() > 0) {
-                    // construct the requested assignment from the packet data
-                    Assignment requestAssignment(receivedPacket);
+                // 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;
                     
-                    qDebug("Received a request for assignment type %i from %s.",
-                           requestAssignment.getType(), qPrintable(senderSockAddr.getAddress().toString()));
+                    // give this assignment out, either the type matches or the requestor said they will take any
+                    assignmentPacket.resize(numAssignmentPacketHeaderBytes);
                     
-                    SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
+                    QDataStream assignmentStream(&assignmentPacket, QIODevice::Append);
                     
-                    if (assignmentToDeploy) {
-                        // 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;
-                        
-                        nodeList->getNodeSocket().writeDatagram(assignmentPacket,
-                                                                senderSockAddr.getAddress(), senderSockAddr.getPort());
-                    }
+                    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;
                 }
             }
         }
@@ -551,8 +553,10 @@ SharedAssignmentPointer DomainServer::matchingStaticAssignmentForCheckIn(const Q
         QQueue::iterator i = _assignmentQueue.begin();
         
         while (i != _assignmentQueue.end()) {
-            if (i->data()->getType() == nodeType && i->data()->getUUID() == checkInUUID) {
+            if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) {
                 return _assignmentQueue.takeAt(i - _assignmentQueue.begin());
+            } else {
+                ++i;
             }
         }
     } else {
@@ -624,24 +628,6 @@ void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPoint
     }
 }
 
-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;
 
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index 06ae3c2d0e..60251b3bb4 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -44,9 +44,6 @@ private:
     SharedAssignmentPointer matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
     SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
     void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment);
-    bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
-                                            const HifiSockAddr& nodeLocalSocket,
-                                            const QUuid& checkInUUI);
     void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
     
     HTTPManager _HTTPManager;

From 984fb1a5df0674acb9bf51548d662634159e1982 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Thu, 30 Jan 2014 15:22:03 -0800
Subject: [PATCH 099/153] fix config parsing from command line and json

---
 domain-server/src/DomainServer.cpp  | 26 +++++++++++++-------------
 libraries/shared/src/Assignment.cpp |  5 +++++
 2 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 0ab75c0088..2c1cb55d2e 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -82,11 +82,10 @@ void DomainServer::parseCommandLineTypeConfigs(const QStringList& argumentList,
             Assignment::Type assignmentType = (Assignment::Type) clConfigType;
             createStaticAssignmentsForTypeGivenConfigString((Assignment::Type) assignmentType,
                                                             argumentList.value(clConfigIndex + 2));
-            
             excludedTypes.insert(assignmentType);
         }
         
-        clConfigIndex = argumentList.indexOf(CONFIG_TYPE_OPTION, clConfigIndex);
+        clConfigIndex = argumentList.indexOf(CONFIG_TYPE_OPTION, clConfigIndex + 1);
     }
 }
 
@@ -167,28 +166,29 @@ void DomainServer::createStaticAssignmentsForTypeGivenConfigString(Assignment::T
     // we have a string for config for this type
     qDebug() << "Parsing command line config for assignment type" << type;
     
-    QString multiConfig(configString);
-    QStringList multiConfigList = multiConfig.split(";");
+    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);
         
-        qDebug("type %d config[%d] = %s", type, i, config.toLocal8Bit().constData());
-        
-        // Now, parse the config to check for a pool
-        const QString ASSIGNMENT_CONFIG_POOL_OPTION = "--pool";
+        // check the config string for a pool
         QString assignmentPool;
         
-        int poolIndex = config.indexOf(ASSIGNMENT_CONFIG_POOL_OPTION);
+        int poolIndex = poolRegex.indexIn(config);
         
-        if (poolIndex >= 0) {
-            int spaceBeforePoolIndex = config.indexOf(' ', poolIndex);
-            int spaceAfterPoolIndex = config.indexOf(' ', spaceBeforePoolIndex);
+        if (poolIndex != -1) {
+            assignmentPool = poolRegex.cap(1);
             
-            assignmentPool = config.mid(spaceBeforePoolIndex + 1, spaceAfterPoolIndex);
+            // 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());
diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp
index a7dd5c36f8..83296991f3 100644
--- a/libraries/shared/src/Assignment.cpp
+++ b/libraries/shared/src/Assignment.cpp
@@ -144,6 +144,11 @@ const char* Assignment::getTypeName() const {
 QDebug operator<<(QDebug debug, const Assignment &assignment) {
     debug.nospace() << "UUID: " << qPrintable(assignment.getUUID().toString()) <<
         ", Type: " << assignment.getType();
+    
+    if (!assignment.getPool().isEmpty()) {
+        debug << ", Pool: " << assignment.getPool();
+    }
+
     return debug.space();
 }
 

From f853e34ab19b27f1734f7985bf39fde0c3b53ed3 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Thu, 30 Jan 2014 15:49:05 -0800
Subject: [PATCH 100/153] fix unix complaint for no postfix for enum

---
 domain-server/src/DomainServer.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 2c1cb55d2e..715e750391 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -199,11 +199,11 @@ void DomainServer::createStaticAssignmentsForTypeGivenConfigString(Assignment::T
 
 void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet& excludedTypes) {
     // enumerate over all assignment types and see if we've already excluded it
-    for (Assignment::Type defaultedType = Assignment::AudioMixerType; defaultedType != Assignment::AllTypes; defaultedType++) {
-        if (!excludedTypes.contains(defaultedType)) {
+    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, defaultedType);
+            Assignment* newAssignment = new Assignment(Assignment::CreateCommand, (Assignment::Type) defaultedType);
             addStaticAssignmentToAssignmentHash(newAssignment);
         }
     }

From 3b17550d92594c48fbe84e4ca1b8210517487cf2 Mon Sep 17 00:00:00 2001
From: ZappoMan 
Date: Thu, 30 Jan 2014 16:36:26 -0800
Subject: [PATCH 101/153] make sure to flush edit queue on script shutdown

---
 libraries/particles/src/Particle.cpp          | 14 +------------
 .../src/ParticlesScriptingInterface.cpp       | 19 +++++++----------
 libraries/script-engine/src/ScriptEngine.cpp  | 21 +++++++++++++++++++
 3 files changed, 30 insertions(+), 24 deletions(-)

diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp
index 557d4ca87f..c7afc49668 100644
--- a/libraries/particles/src/Particle.cpp
+++ b/libraries/particles/src/Particle.cpp
@@ -60,8 +60,6 @@ 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;
 }
@@ -362,8 +360,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
@@ -412,7 +408,6 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
         memcpy(&packetContainsBits, dataAt, sizeof(packetContainsBits));
         dataAt += sizeof(packetContainsBits);
         processedBytes += sizeof(packetContainsBits);
-        //qDebug() << "packetContainsBits:" << packetContainsBits;
     }
 
 
@@ -529,7 +524,6 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
     if (wantDebugging) {
         qDebug("Particle::fromEditPacket()...");
         qDebug() << "   Particle id in packet:" << editID;
-        //qDebug() << "    position: " << newParticle._position;
         newParticle.debugDump();
     }
 
@@ -736,8 +730,6 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID
     // cleanup
     delete[] octcode;
     
-    //qDebug() << "encoding... sizeOut:" << sizeOut;
-
     return success;
 }
 
@@ -825,12 +817,8 @@ void Particle::update(const uint64_t& now) {
     _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);
 
     executeUpdateScripts(); // allow the javascript to alter our state
diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp
index 0ef8f82b00..7f4c29cb55 100644
--- a/libraries/particles/src/ParticlesScriptingInterface.cpp
+++ b/libraries/particles/src/ParticlesScriptingInterface.cpp
@@ -112,22 +112,19 @@ 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(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 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();
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 9db1254f80..b69f69f5f9 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -261,6 +261,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

From d0a4d6a614087c535ba6602971ec9f10a5defa2b Mon Sep 17 00:00:00 2001
From: Andrew Meadows 
Date: Thu, 30 Jan 2014 17:10:00 -0800
Subject: [PATCH 102/153] Moving MyAvatar specific code out of Avatar and into
 MyAvatar.

---
 interface/src/avatar/Avatar.cpp   | 25 -------------------------
 interface/src/avatar/Avatar.h     |  8 --------
 interface/src/avatar/MyAvatar.cpp | 26 ++++++++++++++++++++++++++
 interface/src/avatar/MyAvatar.h   | 11 ++++++++++-
 4 files changed, 36 insertions(+), 34 deletions(-)

diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 61d2ddb8a0..ab62720410 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -72,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)
@@ -395,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 b8f27a0edf..6732db264e 100755
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -118,13 +118,6 @@ public:
 
     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;
 
 
@@ -142,7 +135,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/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index c6c3c9113d..a874fd60a7 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -49,6 +49,7 @@ MyAvatar::MyAvatar() :
     _elapsedTimeSinceCollision(0.0f),
     _lastCollisionPosition(0, 0, 0),
     _speedBrakes(false),
+    _isCollisionsOn(true),
     _isThrustOn(false),
     _thrustMultiplier(1.0f),
     _moveTarget(0,0,0),
@@ -887,3 +888,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..b7f359ca1d 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -23,6 +23,8 @@ enum AvatarHandState
 };
 
 class MyAvatar : public Avatar {
+    Q_OBJECT
+
 public:
 	MyAvatar();
     
@@ -39,7 +41,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
@@ -73,6 +74,13 @@ public:
 
     void orbit(const glm::vec3& position, int deltaX, int deltaY);
 
+public slots:
+    void goHome();
+    void setWantCollisionsOn(bool wantCollisionsOn) { _isCollisionsOn = wantCollisionsOn; }
+    void increaseSize();
+    void decreaseSize();
+    void resetSize();
+
 private:
     bool _mousePressed;
     float _bodyPitchDelta;
@@ -86,6 +94,7 @@ private:
     float _elapsedTimeSinceCollision;
     glm::vec3 _lastCollisionPosition;
     bool _speedBrakes;
+    bool _isCollisionsOn;
     bool _isThrustOn;
     float _thrustMultiplier;
     float _collisionRadius;

From a8bdc874ee7358706048b2f700ba0073327d8c7b Mon Sep 17 00:00:00 2001
From: Andrew Meadows 
Date: Thu, 30 Jan 2014 17:13:39 -0800
Subject: [PATCH 103/153] MyAvatar doesn't need to be a friend of its base
 class Avatar.

---
 interface/src/avatar/Avatar.h | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index 6732db264e..6aaf10b7b7 100755
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -118,9 +118,6 @@ public:
 
     static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
 
-    friend class MyAvatar;
-
-
 protected:
     Head _head;
     Hand _hand;

From 703b654728734b0d58dcf69d08a79b6a22d06e11 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Thu, 30 Jan 2014 17:39:37 -0800
Subject: [PATCH 104/153] make sure UUID key changes in static assignment hash,
 closes #1770

---
 domain-server/src/DomainServer.cpp | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 715e750391..37626e8a43 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -534,13 +534,22 @@ void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer&
     assignment->resetUUID();
     
     qDebug() << "Reset UUID for assignment -" << *assignment.data() << "- and added to queue. Old UUID was"
-        << uuidStringWithoutCurlyBraces( oldUUID);
+        << 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'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);
     }
@@ -633,8 +642,9 @@ void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
 
     // if the domain-server has just restarted,
     // check if there are static assignments that we need to throw into the assignment queue
-    QHash::iterator staticAssignment = _staticAssignmentHash.begin();
-    while (staticAssignment != _staticAssignmentHash.end()) {
+    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;
         

From 1b99bc90cfa2b8de8d1a2843493cb6dfec56c641 Mon Sep 17 00:00:00 2001
From: Andrew Meadows 
Date: Thu, 30 Jan 2014 18:02:04 -0800
Subject: [PATCH 105/153] Removing AvatarManager::clear() as unecessary during
 class destruction.

---
 interface/src/Application.cpp          | 1 -
 interface/src/avatar/AvatarManager.cpp | 7 -------
 interface/src/avatar/AvatarManager.h   | 2 --
 3 files changed, 10 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index b30fc37591..a178288fb2 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -330,7 +330,6 @@ Application::~Application() {
     VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
     Menu::getInstance()->deleteLater();
 
-    _avatarManager.clear();
     _myAvatar = NULL;
 
     delete _logger;
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 57f187603b..3540d465a8 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -29,13 +29,6 @@ AvatarManager::AvatarManager(QObject* parent) :
     _myAvatar = QSharedPointer(new MyAvatar());
 }
 
-void AvatarManager::clear() {
-    _lookAtTargetAvatar.clear();
-    _avatarFades.clear();
-    _avatarHash.clear();
-    _myAvatar.clear();
-}
-
 void AvatarManager::init() {
     _myAvatar->init();
     _myAvatar->setPosition(START_LOCATION);
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index ca436b324d..3f8e8571cc 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -25,8 +25,6 @@ class AvatarManager : public QObject, public DataServerCallbackObject, public Av
 public:
     AvatarManager(QObject* parent = 0);
 
-    void clear();
-
     void init();
 
     MyAvatar* getMyAvatar() { return _myAvatar.data(); }

From dc70c7473338c44cd40c563f22c7a90d2f7dd3fb Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 09:30:26 -0800
Subject: [PATCH 106/153] fix a double push in added particle response

---
 libraries/particles/src/ParticleTree.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp
index a7472d98d1..51150722d0 100644
--- a/libraries/particles/src/ParticleTree.cpp
+++ b/libraries/particles/src/ParticleTree.cpp
@@ -190,7 +190,6 @@ void ParticleTree::handleAddParticleResponse(const QByteArray& packet) {
     int numBytesPacketHeader = numBytesForPacketHeader(packet);
     
     const unsigned char* dataAt = reinterpret_cast(packet.data()) + numBytesPacketHeader;
-    dataAt += numBytesPacketHeader;
 
     uint32_t creatorTokenID;
     memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));

From c989a64e90d0ade8e2a5d2e3cf0e22a438ea0255 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 09:54:52 -0800
Subject: [PATCH 107/153] repair ping packet parsing in creation of reply

---
 libraries/shared/src/NodeList.cpp      | 13 +++++++++----
 libraries/shared/src/PacketHeaders.cpp |  2 +-
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp
index c5ad709fa4..8776337f6b 100644
--- a/libraries/shared/src/NodeList.cpp
+++ b/libraries/shared/src/NodeList.cpp
@@ -119,13 +119,15 @@ void NodeList::timePingReply(const QByteArray& packet) {
     
     if (matchingNode) {
         QDataStream packetStream(packet);
-        packetStream.device()->seek(numBytesForPacketHeader(packet));
+        packetStream.skipRawData(numBytesForPacketHeader(packet));
         
-        qint64 ourOriginalTime, othersReplyTime;
+        quint64 ourOriginalTime, othersReplyTime;
         
         packetStream >> ourOriginalTime >> othersReplyTime;
         
-        qint64 now = usecTimestampNow();
+        qDebug() << "OT:" << ourOriginalTime << "OR:" << othersReplyTime;
+        
+        quint64 now = usecTimestampNow();
         int pingTime = now - ourOriginalTime;
         int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
         
@@ -554,8 +556,11 @@ QByteArray NodeList::constructPingPacket() {
 }
 
 QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
+    QDataStream pingPacketStream(pingPacket);
+    pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
+    
     quint64 timeFromOriginalPing;
-    memcpy(&timeFromOriginalPing, pingPacket.data() + numBytesForPacketHeader(pingPacket), sizeof(timeFromOriginalPing));
+    pingPacketStream >> timeFromOriginalPing;
     
     QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypePingReply);
     QDataStream packetStream(&replyPacket, QIODevice::Append);
diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp
index 5d2dd52a2f..3fd51949f9 100644
--- a/libraries/shared/src/PacketHeaders.cpp
+++ b/libraries/shared/src/PacketHeaders.cpp
@@ -80,7 +80,7 @@ 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 (packet[1] == versionForPacketType(packetTypeForPacket(packet)) || packet[0] == PacketTypeStunResponse) {
+    if (packet[1] == versionForPacketType(packetTypeForPacket(packet)) || packetTypeForPacket(packet) == PacketTypeStunResponse) {
         return true;
     } else {
         PacketType mismatchType = packetTypeForPacket(packet);

From ddbcfbd02f7719233e221fa078ac5bb4979aed4f Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 09:56:18 -0800
Subject: [PATCH 108/153] remove an extra debug for ping packets

---
 libraries/shared/src/NodeList.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp
index 8776337f6b..c0c05196dc 100644
--- a/libraries/shared/src/NodeList.cpp
+++ b/libraries/shared/src/NodeList.cpp
@@ -125,8 +125,6 @@ void NodeList::timePingReply(const QByteArray& packet) {
         
         packetStream >> ourOriginalTime >> othersReplyTime;
         
-        qDebug() << "OT:" << ourOriginalTime << "OR:" << othersReplyTime;
-        
         quint64 now = usecTimestampNow();
         int pingTime = now - ourOriginalTime;
         int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight

From a5b0675f2479674bcef4b502b0bb5ce49d808539 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 09:58:29 -0800
Subject: [PATCH 109/153] sendPingPackets method need not be static

---
 interface/src/Application.cpp | 8 ++++----
 interface/src/Application.h   | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index b30fc37591..16475fcbb0 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1413,10 +1413,10 @@ void Application::wheelEvent(QWheelEvent* event) {
 
 void Application::sendPingPackets() {
     QByteArray pingPacket = NodeList::getInstance()->constructPingPacket();
-    getInstance()->controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer
-                                              << NodeType::ParticleServer
-                                              << NodeType::AudioMixer << NodeType::AvatarMixer
-                                              << NodeType::MetavoxelServer);
+    controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer
+                               << NodeType::ParticleServer
+                               << NodeType::AudioMixer << NodeType::AvatarMixer
+                               << NodeType::MetavoxelServer);
 }
 
 //  Every second, check the frame rates and other stuff
diff --git a/interface/src/Application.h b/interface/src/Application.h
index a0b52365cf..d43b624e59 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -264,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();

From a07511ed2ca2fe5b520d911b9723c0962f4f2703 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 10:05:46 -0800
Subject: [PATCH 110/153] remove an unecessary line

---
 libraries/shared/src/NodeList.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp
index c0c05196dc..cd9356ab15 100644
--- a/libraries/shared/src/NodeList.cpp
+++ b/libraries/shared/src/NodeList.cpp
@@ -624,8 +624,7 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
     }
 }
 
-unsigned NodeList::broadcastToNodes(const QByteArray& packet,
-                                    const NodeSet& destinationNodeTypes) {
+unsigned NodeList::broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
     unsigned n = 0;
 
     foreach (const SharedNodePointer& node, getNodeHash()) {

From 59a528f096f8748823966140abe9829fd8e91c55 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 10:06:26 -0800
Subject: [PATCH 111/153] remove another unneeded line

---
 libraries/octree/src/JurisdictionListener.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp
index fc8ea008d1..d2c24021ac 100644
--- a/libraries/octree/src/JurisdictionListener.cpp
+++ b/libraries/octree/src/JurisdictionListener.cpp
@@ -45,8 +45,7 @@ bool JurisdictionListener::queueJurisdictionRequest() {
     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, QByteArray(reinterpret_cast(bufferOut), sizeOut));
             nodeCount++;

From 79c8f91c78cc9f332c8caffd207520e820530a40 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 10:15:17 -0800
Subject: [PATCH 112/153] instantiate isAvatar in ScriptEngine to false, closes
 #1779

---
 libraries/script-engine/src/ScriptEngine.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 71e450d4ad..303f53aaa1 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -41,6 +41,7 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng
 
 ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const QString& fileNameString, AbstractMenuInterface* menu,
                            AbstractControllerScriptingInterface* controllerScriptingInterface) :
+    _isAvatar(false),
     _dataServerScriptingInterface(),
     _avatarData(NULL)
 {

From 29abd2500fac41e311adc254beb51bd5ccbfe925 Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 10:34:11 -0800
Subject: [PATCH 113/153] revert to old parsing of AvatarData

---
 libraries/avatars/src/AvatarData.cpp | 117 +++++++++++++--------------
 1 file changed, 57 insertions(+), 60 deletions(-)

diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index a8906f9167..c337f2ab95 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -172,105 +172,102 @@ int AvatarData::parseData(const QByteArray& packet) {
         _handData = new HandData(this);
     }
     
-    QDataStream packetStream(packet);
-    packetStream.skipRawData(numBytesForPacketHeader(packet));
+    // increment to push past the packet header
+    const unsigned char* sourceBuffer = reinterpret_cast(packet.data());
+    const unsigned char* startPosition = sourceBuffer + numBytesForPacketHeader(packet);
     
-    packetStream.readRawData(reinterpret_cast(&_position), sizeof(_position));
+    // Body world position
+    memcpy(&_position, sourceBuffer, sizeof(float) * 3);
+    sourceBuffer += sizeof(float) * 3;
     
     // Body rotation (NOTE: This needs to become a quaternion to save two bytes)
-    uint16_t twoByteHolder;
-    packetStream >> twoByteHolder;
-    unpackFloatAngleFromTwoByte(&twoByteHolder, &_bodyYaw);
+    sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyYaw);
+    sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyPitch);
+    sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll);
     
-    packetStream >> twoByteHolder;
-    unpackFloatAngleFromTwoByte(&twoByteHolder, &_bodyPitch);
+    // Body scale
+    sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _targetScale);
     
-    packetStream >> twoByteHolder;
-    unpackFloatAngleFromTwoByte(&twoByteHolder, &_bodyRoll);
-    
-    // body scale
-    packetStream >> twoByteHolder;
-    unpackFloatRatioFromTwoByte(reinterpret_cast(&twoByteHolder), _targetScale);
-
     // Head rotation (NOTE: This needs to become a quaternion to save two bytes)
     float headYaw, headPitch, headRoll;
-    
-    packetStream >> twoByteHolder;
-    unpackFloatAngleFromTwoByte(&twoByteHolder, &headYaw);
-    
-    packetStream >> twoByteHolder;
-    unpackFloatAngleFromTwoByte(&twoByteHolder, &headPitch);
-    
-    packetStream >> twoByteHolder;
-    unpackFloatAngleFromTwoByte(&twoByteHolder, &headRoll);
+    sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw);
+    sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch);
+    sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll);
     
     _headData->setYaw(headYaw);
     _headData->setPitch(headPitch);
     _headData->setRoll(headRoll);
-
+    
     //  Head position relative to pelvis
-    packetStream >> _headData->_leanSideways;
-    packetStream >> _headData->_leanForward;
-
+    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;
-    packetStream.readRawData(reinterpret_cast(&handPositionRelative), sizeof(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);
     
-    packetStream.readRawData(reinterpret_cast(&_headData->_lookAtPosition), sizeof(_headData->_lookAtPosition));
-
     // Instantaneous audio loudness (used to drive facial animation)
     //sourceBuffer += unpackFloatFromByte(sourceBuffer, _audioLoudness, MAX_AUDIO_LOUDNESS);
-    packetStream >> _headData->_audioLoudness;
-
-    // the rest is a chat message
+    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);
     
-    quint8 chatMessageSize;
-    packetStream >> chatMessageSize;
-    _chatMessage = string(packet.data() + packetStream.device()->pos(), chatMessageSize);
-    packetStream.skipRawData(chatMessageSize);
-
     // voxel sending features...
     unsigned char bitItems = 0;
-    packetStream >> bitItems;
+    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) {
-        packetStream >> _headData->_leftEyeBlink;
-        packetStream >> _headData->_rightEyeBlink;
-        packetStream >> _headData->_averageLoudness;
-        packetStream >> _headData->_browAudioLift;
+        memcpy(&_headData->_leftEyeBlink, sourceBuffer, sizeof(float));
+        sourceBuffer += sizeof(float);
         
-        quint8 numBlendshapeCoefficients;
-        packetStream >> numBlendshapeCoefficients;
+        memcpy(&_headData->_rightEyeBlink, sourceBuffer, sizeof(float));
+        sourceBuffer += sizeof(float);
         
-        _headData->_blendshapeCoefficients.resize(numBlendshapeCoefficients);
-        packetStream.readRawData(reinterpret_cast(_headData->_blendshapeCoefficients.data()),
-                                 numBlendshapeCoefficients * 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));
+        sourceBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
     }
     
     // pupil dilation
-    quint8 pupilByte;
-    packetStream >> pupilByte;
-    unpackFloatFromByte(&pupilByte, _headData->_pupilDilation, 1.0f);
+    sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f);
     
     // leap hand data
-    if (packetStream.device()->pos() < packet.size()) {
+    if (sourceBuffer - startPosition < packet.size()) {
         // check passed, bytes match
-        packetStream.skipRawData(_handData->decodeRemoteData(packet.mid(packetStream.device()->pos())));
+        sourceBuffer += _handData->decodeRemoteData(packet.mid(sourceBuffer - startPosition));
     }
-
-    return packetStream.device()->pos();
+    
+    return sourceBuffer - startPosition;
 }
 
 void AvatarData::setClampedTargetScale(float targetScale) {

From c70633e5d14d8368ece8f9188977a4dd70712c6a Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 10:40:10 -0800
Subject: [PATCH 114/153] don't render any audio stats if no _audioOutput,
 closes #1783

---
 interface/src/Audio.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index e42947bf8c..9fede84a93 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -539,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);

From 217135a8d6b628067858f9d48d19dc5a867bd2dc Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 11:00:44 -0800
Subject: [PATCH 115/153] repairs to VoxelImporter to remove QCoreApplication
 warning

---
 interface/src/Application.cpp   | 14 ++++++++------
 interface/src/Application.h     |  2 +-
 interface/src/ImportDialog.cpp  |  4 ----
 interface/src/VoxelImporter.cpp | 18 +++++++++---------
 4 files changed, 18 insertions(+), 20 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 66f9f8e5b7..070a20ee65 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -117,11 +117,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
         _frameCount(0),
         _fps(120.0f),
         _justStarted(true),
-        _voxelImporter(_window),
+        _voxelImporter(NULL),
         _wantToKillLocalVoxels(false),
         _audioScope(256, 200, true),
-        _avatarManager(),
-        _myAvatar(NULL),
+        _myAvatar(),
         _profile(QString()),
         _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
         _mouseX(0),
@@ -1673,7 +1672,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.");
@@ -1813,8 +1817,6 @@ void Application::init() {
     _sharedVoxelSystem.changeTree(&_clipboard);
     delete tmpTree;
 
-    _voxelImporter.init(_settings);
-
     _environment.init();
 
     _glowEffect.init();
diff --git a/interface/src/Application.h b/interface/src/Application.h
index d43b624e59..c83437949c 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -355,7 +355,7 @@ private:
 
     VoxelSystem _voxels;
     VoxelTree _clipboard; // if I copy/paste
-    VoxelImporter _voxelImporter;
+    VoxelImporter* _voxelImporter;
     VoxelSystem _sharedVoxelSystem;
     ViewFrustum _sharedVoxelSystemViewFrustum;
 
diff --git a/interface/src/ImportDialog.cpp b/interface/src/ImportDialog.cpp
index 28e39f1abd..ac7853629c 100644
--- a/interface/src/ImportDialog.cpp
+++ b/interface/src/ImportDialog.cpp
@@ -231,10 +231,6 @@ void ImportDialog::setLayout() {
     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)) {
diff --git a/interface/src/VoxelImporter.cpp b/interface/src/VoxelImporter.cpp
index 0011b0abb7..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) {

From 4ed748752f736293f8d5a994e2f6d18f40f5c2dc Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 11:20:36 -0800
Subject: [PATCH 116/153] fix incorrect packet position in AvatarData, closes
 #1786

---
 libraries/avatars/src/AvatarData.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index c337f2ab95..98afa76107 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -173,8 +173,8 @@ int AvatarData::parseData(const QByteArray& packet) {
     }
     
     // increment to push past the packet header
-    const unsigned char* sourceBuffer = reinterpret_cast(packet.data());
-    const unsigned char* startPosition = sourceBuffer + numBytesForPacketHeader(packet);
+    const unsigned char* startPosition = reinterpret_cast(packet.data());
+    const unsigned char* sourceBuffer = startPosition + numBytesForPacketHeader(packet);
     
     // Body world position
     memcpy(&_position, sourceBuffer, sizeof(float) * 3);

From 4bf8f3a465ed369df0d5e2f8b0c34d1c91165d6f Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 11:29:15 -0800
Subject: [PATCH 117/153] reinstate old HandData parsing

---
 libraries/avatars/src/HandData.cpp | 43 +++++++++---------------------
 1 file changed, 12 insertions(+), 31 deletions(-)

diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp
index d947b894aa..2e24c48cfa 100644
--- a/libraries/avatars/src/HandData.cpp
+++ b/libraries/avatars/src/HandData.cpp
@@ -162,29 +162,21 @@ int HandData::encodeRemoteData(unsigned char* destinationBuffer) {
 int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
     QDataStream packetStream(dataByteArray);
     
-    quint8 numHands;
-    packetStream >> numHands;
+    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;
-        qint16 twoByteHolder;
+        sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, handPosition, fingerVectorRadix);
+        sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, handNormal, fingerVectorRadix);
+        unsigned int numFingers = *sourceBuffer++;
         
-        packetStream >> twoByteHolder;
-        unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast(&twoByteHolder),
-                                              handPosition, fingerVectorRadix);
-        
-        packetStream >> twoByteHolder;
-        unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast(&twoByteHolder),
-                                              handNormal, fingerVectorRadix);
-        
-        quint8 numFingers;
-        packetStream >> numFingers;
-
         palm.setRawPosition(handPosition);
         palm.setRawNormal(handNormal);
         palm.setActive(true);
@@ -195,18 +187,12 @@ int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
         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);
                 
-                packetStream >> twoByteHolder;
-                unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast(&twoByteHolder), tipPosition,
-                                                        fingerVectorRadix);
-                
-                packetStream >> twoByteHolder;
-                unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast(&twoByteHolder), rootPosition,
-                                                      fingerVectorRadix);
-
                 finger.setRawTipPosition(tipPosition);
                 finger.setRawRootPosition(rootPosition);
                 finger.setActive(true);
@@ -225,14 +211,9 @@ int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
     }
     
     // One byte for error checking safety.
-    unsigned char requiredLength = packetStream.device()->pos();
-    
-    unsigned char checkLength;
-    packetStream >> checkLength;
-    
+    unsigned char requiredLength = (unsigned char)(sourceBuffer - startPosition);
+    unsigned char checkLength = *sourceBuffer++;
     assert(checkLength == requiredLength);
-
-    return packetStream.device()->pos();
 }
 
 void HandData::setFingerTrailLength(unsigned int length) {

From 9d8234799433e678fcb7034dd013db11ef75df1e Mon Sep 17 00:00:00 2001
From: Stephen Birarda 
Date: Fri, 31 Jan 2014 11:32:23 -0800
Subject: [PATCH 118/153] add a missing return

---
 libraries/avatars/src/HandData.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp
index 2e24c48cfa..cca8ad45cb 100644
--- a/libraries/avatars/src/HandData.cpp
+++ b/libraries/avatars/src/HandData.cpp
@@ -214,6 +214,8 @@ int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
     unsigned char requiredLength = (unsigned char)(sourceBuffer - startPosition);
     unsigned char checkLength = *sourceBuffer++;
     assert(checkLength == requiredLength);
+    
+    return sourceBuffer - startPosition;
 }
 
 void HandData::setFingerTrailLength(unsigned int length) {

From bd1d0201e085d34a23e3e88ebbce7d88217d6f7a Mon Sep 17 00:00:00 2001
From: Lucas Crisman 
Date: Fri, 31 Jan 2014 16:52:20 -0300
Subject: [PATCH 119/153] Updated Ubuntu package requirements

---
 README.md | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index bc32998113..afa7877c23 100644
--- a/README.md
+++ b/README.md
@@ -71,8 +71,16 @@ 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 that you set the variable automatically on each shell instance to save this task in the future, such as: + + echo 'export QT_CMAKE_PREFIX_PATH=~/Qt5.2.0/5.2.0/gcc_64/lib/cmake' >> ~/.bashrc Running Interface ----- From 73aa73fd5865cae1bb07ca7d9b988ad0a02fce1a Mon Sep 17 00:00:00 2001 From: Lucas Crisman Date: Fri, 31 Jan 2014 17:00:27 -0300 Subject: [PATCH 120/153] joining paragraphs --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index afa7877c23..7679054630 100644 --- a/README.md +++ b/README.md @@ -76,9 +76,9 @@ 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 that you set the variable automatically on each shell instance to save this task in the future, such as: +and set your `QT_CMAKE_PREFIX_PATH` environment variable as described above in the CMake section. It's +recommended that you set the variable automatically on each shell instance to save this task in the future, +such as: echo 'export QT_CMAKE_PREFIX_PATH=~/Qt5.2.0/5.2.0/gcc_64/lib/cmake' >> ~/.bashrc From 4573eaebc679989bf916eed18f94d1a69e9a78c2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 12:02:24 -0800 Subject: [PATCH 121/153] fix multiple avatar parsing in AvatarManager --- assignment-client/src/avatars/AvatarMixer.cpp | 1 - interface/src/avatar/AvatarManager.cpp | 4 ++-- libraries/avatars/src/HandData.cpp | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 014008d35e..6dc63a6dd7 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -70,7 +70,6 @@ void broadcastAvatarData() { QByteArray avatarByteArray; avatarByteArray.append(otherNode->getUUID().toRfc4122()); - AvatarData *nodeData = (AvatarData *)otherNode->getLinkedData(); avatarByteArray.append(nodeData->toByteArray()); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 57f187603b..ba9dddde07 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -206,7 +206,7 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const int bytesRead = numBytesForPacketHeader(datagram); QByteArray dummyAvatarByteArray = byteArrayWithPopluatedHeader(PacketTypeAvatarData); - int numDummyByteArrayHeaderBytes = dummyAvatarByteArray.size(); + int numDummyByteArrayHeaderBytes = dummyAvatarByteArray.size(); // enumerate over all of the avatars in this packet // only add them if mixerWeakPointer points to something (meaning that mixer is still around) @@ -237,7 +237,7 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const // make this Avatar's UUID the UUID in the packet and tack the remaining data onto the end dummyAvatarByteArray.replace(numDummyByteArrayHeaderBytes - NUM_BYTES_RFC4122_UUID, - NUM_BYTES_RFC4122_UUID + datagram.size() - bytesRead, + datagram.size() - bytesRead, datagram.mid(bytesRead)); // have the matching (or new) avatar parse the data from the packet diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index cca8ad45cb..5a923eea93 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -160,8 +160,6 @@ int HandData::encodeRemoteData(unsigned char* destinationBuffer) { } int HandData::decodeRemoteData(const QByteArray& dataByteArray) { - QDataStream packetStream(dataByteArray); - const unsigned char* startPosition; const unsigned char* sourceBuffer = startPosition = reinterpret_cast(dataByteArray.data()); unsigned int numHands = *sourceBuffer++; From 18ce012d7aba237b3238c18b29b52ad0c3a9117a Mon Sep 17 00:00:00 2001 From: Lucas Crisman Date: Fri, 31 Jan 2014 17:03:57 -0300 Subject: [PATCH 122/153] simplifying text --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 7679054630..a42bbd1626 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,7 @@ On a fresh Ubuntu 13.10 install, get these requirements from Ubuntu repositories 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 that you set the variable automatically on each shell instance to save this task in the future, -such as: +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 From 2cc6d2218144cb5ceac5e36c15b1c46e59ea67c5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 12:44:09 -0800 Subject: [PATCH 123/153] fix push bytesRead when creating dummy Avatar packets, closes #1794 --- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- interface/src/avatar/AvatarManager.cpp | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 6dc63a6dd7..358e507fc4 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -70,7 +70,7 @@ void broadcastAvatarData() { QByteArray avatarByteArray; avatarByteArray.append(otherNode->getUUID().toRfc4122()); - AvatarData *nodeData = (AvatarData *)otherNode->getLinkedData(); + AvatarData* nodeData = (AvatarData*) otherNode->getLinkedData(); avatarByteArray.append(nodeData->toByteArray()); if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ba9dddde07..24431cf3f3 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -206,7 +206,8 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const int bytesRead = numBytesForPacketHeader(datagram); QByteArray dummyAvatarByteArray = byteArrayWithPopluatedHeader(PacketTypeAvatarData); - int numDummyByteArrayHeaderBytes = dummyAvatarByteArray.size(); + 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) @@ -233,15 +234,13 @@ 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 - dummyAvatarByteArray.resize(numDummyByteArrayHeaderBytes); + dummyAvatarByteArray.resize(numDummyHeaderBytesWithoutUUID); // make this Avatar's UUID the UUID in the packet and tack the remaining data onto the end - dummyAvatarByteArray.replace(numDummyByteArrayHeaderBytes - NUM_BYTES_RFC4122_UUID, - datagram.size() - bytesRead, - datagram.mid(bytesRead)); + dummyAvatarByteArray.append(datagram.mid(bytesRead)); // have the matching (or new) avatar parse the data from the packet - bytesRead += matchingAvatar->parseData(dummyAvatarByteArray); + bytesRead += matchingAvatar->parseData(dummyAvatarByteArray) - numDummyHeaderBytesWithoutUUID; } } From b16d03c42a8ed2a6a551e2aeadce3399a857587d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 12:46:03 -0800 Subject: [PATCH 124/153] remove an unneeded todo --- interface/src/avatar/AvatarManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 24431cf3f3..6a44eb98aa 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -213,7 +213,6 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const // only add them if mixerWeakPointer points to something (meaning that mixer is still around) while (bytesRead < datagram.size() && mixerWeakPointer.data()) { QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID)); - // TODO: skip the data if nodeUUID is same as MY_AVATAR_KEY AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); From a7ffb7df1d1dd13c30914e55152f66482ac86255 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 31 Jan 2014 13:17:12 -0800 Subject: [PATCH 125/153] Moved LookAtTargetAvatar stuff from AvatarManager to MyAvatar. --- interface/src/Application.cpp | 8 +++--- interface/src/avatar/AvatarManager.cpp | 39 +------------------------- interface/src/avatar/AvatarManager.h | 8 ------ interface/src/avatar/MyAvatar.cpp | 36 +++++++++++++++++++++++- interface/src/avatar/MyAvatar.h | 8 ++++++ 5 files changed, 48 insertions(+), 51 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a178288fb2..916109c290 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1196,8 +1196,8 @@ 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) { @@ -1256,7 +1256,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); } @@ -2363,7 +2363,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 diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 3540d465a8..c7cf83c20a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -20,9 +20,6 @@ const QUuid MY_AVATAR_KEY; // NULL key AvatarManager::AvatarManager(QObject* parent) : - _lookAtTargetAvatar(), - _lookAtOtherPosition(), - _lookAtIndicatorScale(1.0f), _avatarFades() { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); @@ -36,40 +33,6 @@ void AvatarManager::init() { _avatarHash.insert(MY_AVATAR_KEY, _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()) { - glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin(); - glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection(); - - foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) { - Avatar* avatar = static_cast(avatarPointer.data()); - if (avatar != static_cast(_myAvatar.data())) { - 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(); - - _lookAtIndicatorScale = avatar->getHead().getScale(); - _lookAtOtherPosition = avatar->getHead().getPosition(); - - _lookAtTargetAvatar = avatarPointer; - - // found the look at target avatar, return - return; - } - } - } - - _lookAtTargetAvatar.clear(); - } -} - void AvatarManager::updateAvatars(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); @@ -267,5 +230,5 @@ void AvatarManager::clearMixedAvatars() { while (removeAvatar != _avatarHash.end()) { removeAvatar = erase(removeAvatar); } - _lookAtTargetAvatar.clear(); + _myAvatar->clearLookAtTargetAvatar(); } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 3f8e8571cc..e9ff77281f 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -29,10 +29,6 @@ public: MyAvatar* getMyAvatar() { return _myAvatar.data(); } - AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } - - void updateLookAtTargetAvatar(glm::vec3& eyePosition); - void updateAvatars(float deltaTime); void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); @@ -53,10 +49,6 @@ private: // virtual override AvatarHash::iterator erase(const AvatarHash::iterator& iterator); - QWeakPointer _lookAtTargetAvatar; - glm::vec3 _lookAtOtherPosition; - float _lookAtIndicatorScale; - QVector _avatarFades; QSharedPointer _myAvatar; }; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a874fd60a7..c1822e5df8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -53,13 +53,18 @@ MyAvatar::MyAvatar() : _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() { _head.reset(); _hand.reset(); @@ -492,6 +497,35 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { 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()); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b7f359ca1d..cf25517fb8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -27,6 +27,7 @@ class MyAvatar : public Avatar { public: MyAvatar(); + ~MyAvatar(); void reset(); void simulate(float deltaTime, Transmitter* transmitter); @@ -74,6 +75,12 @@ 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; } @@ -100,6 +107,7 @@ private: float _collisionRadius; glm::vec3 _moveTarget; int _moveTargetStepCounter; + QWeakPointer _lookAtTargetAvatar; // private methods void renderBody(bool forceRenderHead); From 5caaf0dc7f1710b97c0bce05abd80fb9ff2746cd Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 31 Jan 2014 13:28:56 -0800 Subject: [PATCH 126/153] check against NULL node from nodeWithUUID() --- libraries/octree/src/JurisdictionSender.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp index 52990397db..854496e40f 100644 --- a/libraries/octree/src/JurisdictionSender.cpp +++ b/libraries/octree/src/JurisdictionSender.cpp @@ -64,7 +64,7 @@ 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, QByteArray(reinterpret_cast(bufferOut), sizeOut)); nodeCount++; From 254a0c0aed17c62b24cc51b1ac036a8ff6533d59 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 13:44:24 -0800 Subject: [PATCH 127/153] remove a double push on UUID in OctreeQuery --- libraries/octree/src/OctreeQuery.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index 51b73a9ed2..ed8bfd0da4 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -103,9 +103,6 @@ int OctreeQuery::parseData(const QByteArray& packet) { const unsigned char* startPosition = reinterpret_cast(packet.data()); const unsigned char* sourceBuffer = startPosition + numBytesPacketHeader; - // 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; From 9b549723d3e302b9fbb928309af4836e496dc31e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 13:47:59 -0800 Subject: [PATCH 128/153] remove unused UUID from OctreeQuery --- libraries/octree/src/OctreeQuery.cpp | 10 ---------- libraries/octree/src/OctreeQuery.h | 5 ----- 2 files changed, 15 deletions(-) diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index ed8bfd0da4..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); @@ -103,10 +97,6 @@ int OctreeQuery::parseData(const QByteArray& packet) { const unsigned char* startPosition = reinterpret_cast(packet.data()); const unsigned char* sourceBuffer = startPosition + numBytesPacketHeader; - // user UUID - _uuid = QUuid::fromRfc4122(QByteArray((char*) sourceBuffer, NUM_BYTES_RFC4122_UUID)); - sourceBuffer += NUM_BYTES_RFC4122_UUID; - // camera details memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition)); sourceBuffer += sizeof(_cameraPosition); diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index acb4b7cb32..800d30b7cd 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -57,9 +57,6 @@ public: int getBroadcastData(unsigned char* destinationBuffer); int parseData(const QByteArray& packet); - QUuid& getUUID() { return _uuid; } - void setUUID(const QUuid& uuid) { _uuid = uuid; } - // getters for camera details const glm::vec3& getCameraPosition() const { return _cameraPosition; } const glm::quat& getCameraOrientation() const { return _cameraOrientation; } @@ -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; From 4b18e37dfb100020df4ff77939282074d1fc1bd4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 14:13:05 -0800 Subject: [PATCH 129/153] fix for ttf return in DS, closes #1769 --- libraries/embedded-webserver/src/HTTPManager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 849bf593cd..a217555a78 100755 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -63,7 +63,7 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p QFile localFile(filePath); localFile.open(QIODevice::ReadOnly); - QString localFileString(localFile.readAll()); + QByteArray localFileData = localFile.readAll(); QFileInfo localFileInfo(filePath); @@ -77,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 @@ -105,9 +106,11 @@ 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(), + connection->respond(HTTPConnection::StatusCode200, localFileData, qPrintable(mimeDatabase.mimeTypeForFile(filePath).name())); } else { From dd83ff05380277fb3398984f7d00fa0c1a2b5ce4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 14:36:50 -0800 Subject: [PATCH 130/153] move isAvatar property access to Agent object --- assignment-client/src/Agent.cpp | 3 +++ assignment-client/src/Agent.h | 5 +++++ libraries/script-engine/src/ScriptEngine.cpp | 2 +- libraries/script-engine/src/ScriptEngine.h | 2 -- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 84714259e5..2694bf83e2 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -101,6 +101,9 @@ void Agent::run() { // 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 146cb71df4..8b2038a8b0 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -21,9 +21,14 @@ class Agent : public ThreadedAssignment { Q_OBJECT + + Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) public: Agent(const QByteArray& packet); + void setIsAvatar(bool isAvatar) { _scriptEngine.setIsAvatar(isAvatar); } + bool isAvatar() const { return _scriptEngine.isAvatar(); } + public slots: void run(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 6ea3742592..9a7a9197b0 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -133,7 +133,7 @@ void ScriptEngine::init() { QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject(); _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); - registerGlobalObject("Agent", this); + registerGlobalObject("Script", this); registerGlobalObject("Audio", &_audioScriptingInterface); registerGlobalObject("Controller", _controllerScriptingInterface); registerGlobalObject("Data", &_dataServerScriptingInterface); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index c2188cca63..8f29379266 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -31,8 +31,6 @@ 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, From 9015b24aa5ec5af9da0508aa5c831c6e3a4399a2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 14:41:24 -0800 Subject: [PATCH 131/153] update the script examples for new nomenclature --- examples/clap.js | 2 +- examples/collidingParticles.js | 6 +++--- examples/controllerExample.js | 4 ++-- examples/count.js | 4 ++-- examples/drumStick.js | 2 +- examples/editParticleExample.js | 8 ++++---- examples/findParticleExample.js | 8 ++++---- examples/fountain.js | 4 ++-- examples/gameoflife.js | 2 +- examples/gun.js | 2 +- examples/lookWithMouse.js | 4 ++-- examples/movingVoxel.js | 2 +- examples/paintGun.js | 2 +- examples/particleBird.js | 4 ++-- examples/particleModelExample.js | 6 +++--- examples/playSound.js | 2 +- examples/rideAlongWithAParticleExample.js | 4 ++-- examples/toyball.js | 2 +- examples/voxelBird.js | 2 +- 19 files changed, 35 insertions(+), 35 deletions(-) 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..d202b31d97 100644 --- a/examples/collidingParticles.js +++ b/examples/collidingParticles.js @@ -1,4 +1,4 @@ -// +Script// // collidingParticles.js // hifi // @@ -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 index 95561dc9dc..43eb516cee 100644 --- a/examples/controllerExample.js +++ b/examples/controllerExample.js @@ -82,7 +82,7 @@ function touchEndEvent(event) { } // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(checkController); +Script.willSendVisualDataCallback.connect(checkController); // Map keyPress and mouse move events to our callbacks Controller.keyPressEvent.connect(keyPressEvent); @@ -199,4 +199,4 @@ function scriptEnding() { Controller.releaseTouchEvents(); } -Agent.scriptEnding.connect(scriptEnding); +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 61e32c4d55..5774eda689 100644 --- a/examples/editParticleExample.js +++ b/examples/editParticleExample.js @@ -44,7 +44,7 @@ var particleID = Particles.addParticle(originalProperties); function moveParticle() { if (count >= moveUntil) { - //Agent.stop(); + //Script.stop(); // delete it... if (count == moveUntil) { @@ -54,8 +54,8 @@ function moveParticle() { // stop it... if (count >= stopAfter) { - print("calling Agent.stop()"); - Agent.stop(); + print("calling Script.stop()"); + Script.stop(); } count++; @@ -86,5 +86,5 @@ function moveParticle() { // 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 bd20e6ded7..5eb257d502 100644 --- a/examples/findParticleExample.js +++ b/examples/findParticleExample.js @@ -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 index 09fae07204..6779941dc7 100644 --- a/examples/gameoflife.js +++ b/examples/gameoflife.js @@ -128,6 +128,6 @@ print("step()..."); } print("here"); -Agent.willSendVisualDataCallback.connect(step); +Script.willSendVisualDataCallback.connect(step); Voxels.setPacketsPerSecond(200); print("now here"); diff --git a/examples/gun.js b/examples/gun.js index 3f8eefe3e2..e7cd2973e2 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -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 index 79fad76a1b..f404019bc1 100644 --- a/examples/lookWithMouse.js +++ b/examples/lookWithMouse.js @@ -70,5 +70,5 @@ MyAvatar.bodyPitch = 0; MyAvatar.bodyRoll = 0; // would be nice to change to update -Agent.willSendVisualDataCallback.connect(update); -Agent.scriptEnding.connect(scriptEnding); +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 b78e6abb0b..56e916a183 100644 --- a/examples/paintGun.js +++ b/examples/paintGun.js @@ -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 6a4cf79a40..c1c26058e6 100644 --- a/examples/particleBird.js +++ b/examples/particleBird.js @@ -95,7 +95,7 @@ function moveBird() { var nowTimeInSeconds = new Date().getTime() / 1000; if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) { print("our bird is dying, stop our script"); - Agent.stop(); + Script.stop(); return; } @@ -171,4 +171,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 index 9f19069ee9..e95cc0c2bf 100644 --- a/examples/particleModelExample.js +++ b/examples/particleModelExample.js @@ -37,8 +37,8 @@ var ballParticleID = Particles.addParticle(ballProperties); function endAfterAWhile() { // stop it... if (count >= stopAfter) { - print("calling Agent.stop()"); - Agent.stop(); + print("calling Script.stop()"); + Script.stop(); } print("count =" + count); @@ -47,5 +47,5 @@ function endAfterAWhile() { // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(endAfterAWhile); +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 index eca9fe7f4c..b2b6627063 100644 --- a/examples/rideAlongWithAParticleExample.js +++ b/examples/rideAlongWithAParticleExample.js @@ -37,7 +37,7 @@ function rideWithParticle() { y: propertiesA.position.y + 2, z: propertiesA.position.z }; } else { - Agent.stop(); + Script.stop(); } iteration++; @@ -46,5 +46,5 @@ function rideWithParticle() { // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(rideWithParticle); +Script.willSendVisualDataCallback.connect(rideWithParticle); 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 From 8fc55bd196fa8b70fdc4da7d4cacf4ef1fae2391 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 14:56:04 -0800 Subject: [PATCH 132/153] add user UUID to application window title --- interface/src/Application.cpp | 16 +++++----------- interface/src/avatar/Profile.cpp | 5 ++++- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4947fb008f..bf17736368 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4003,18 +4003,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); diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index 74f47bd658..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) { From dba7fbceac46799b4e9495158cd6e307ead97038 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 15:00:35 -0800 Subject: [PATCH 133/153] remove extra Script and commented out line --- examples/collidingParticles.js | 2 +- examples/editParticleExample.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/collidingParticles.js b/examples/collidingParticles.js index d202b31d97..81ccfe108b 100644 --- a/examples/collidingParticles.js +++ b/examples/collidingParticles.js @@ -1,4 +1,4 @@ -Script// +// // collidingParticles.js // hifi // diff --git a/examples/editParticleExample.js b/examples/editParticleExample.js index 5774eda689..152bb18fca 100644 --- a/examples/editParticleExample.js +++ b/examples/editParticleExample.js @@ -44,7 +44,6 @@ var particleID = Particles.addParticle(originalProperties); function moveParticle() { if (count >= moveUntil) { - //Script.stop(); // delete it... if (count == moveUntil) { From ff2afca50e386d27fbb622cabf07c593365ba457 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 31 Jan 2014 15:24:54 -0800 Subject: [PATCH 134/153] Special handling for blendshapes in Blender exports. They're under the model, like the geometry. --- interface/src/renderer/FBXReader.cpp | 103 ++++++++++++++++----------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index d128206c80..c07eb8469d 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -738,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)); @@ -760,6 +776,33 @@ 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; + } + } + } + } +} + FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) { QHash meshes; QVector blendshapes; @@ -799,7 +842,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]; @@ -827,22 +869,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") { @@ -900,6 +927,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; @@ -969,9 +998,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); @@ -1084,29 +1127,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 From 00e98ee42b9c278cfc04d147e10b803324bd8d94 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 31 Jan 2014 15:44:48 -0800 Subject: [PATCH 135/153] make tryLock actually return a bool --- libraries/octree/src/Octree.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 94acf90ab7..9b8921208b 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -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(); From 19f218f37c3683a8449744b5b0a399bc0c63d176 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 31 Jan 2014 15:45:31 -0800 Subject: [PATCH 136/153] fix typo --- libraries/particles/src/ParticleCollisionSystem.cpp | 8 ++++---- libraries/particles/src/ParticleCollisionSystem.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index b5fe827ebb..e69632f379 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -68,12 +68,12 @@ void ParticleCollisionSystem::checkParticle(Particle* particle) { updateCollisionWithAvatars(particle); } -void ParticleCollisionSystem::emitGlobalParicleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails) { +void ParticleCollisionSystem::emitGlobalParticleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails) { ParticleID particleID = particle->getParticleID(); emit particleCollisionWithVoxel(particleID, *voxelDetails); } -void ParticleCollisionSystem::emitGlobalParicleCollisionWithParticle(Particle* particleA, Particle* particleB) { +void ParticleCollisionSystem::emitGlobalParticleCollisionWithParticle(Particle* particleA, Particle* particleB) { ParticleID idA = particleA->getParticleID(); ParticleID idB = particleB->getParticleID(); emit particleCollisionWithParticle(idA, idB); @@ -95,7 +95,7 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { particle->collisionWithVoxel(voxelDetails); // let the global script run their collision scripts for particles if they have them - emitGlobalParicleCollisionWithVoxel(particle, voxelDetails); + emitGlobalParticleCollisionWithVoxel(particle, voxelDetails); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); collisionInfo._penetration /= (float)(TREE_SCALE); @@ -124,7 +124,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) if (glm::dot(relativeVelocity, penetration) > 0.0f) { particleA->collisionWithParticle(particleB); particleB->collisionWithParticle(particleA); - emitGlobalParicleCollisionWithParticle(particleA, particleB); + emitGlobalParticleCollisionWithParticle(particleA, particleB); glm::vec3 axis = glm::normalize(penetration); glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index 9baf9bfd05..c525d3ddfc 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -58,8 +58,8 @@ signals: private: static bool updateOperation(OctreeElement* element, void* extraData); - void emitGlobalParicleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails); - void emitGlobalParicleCollisionWithParticle(Particle* particleA, Particle* particleB); + void emitGlobalParticleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails); + void emitGlobalParticleCollisionWithParticle(Particle* particleA, Particle* particleB); ParticleEditPacketSender* _packetSender; ParticleTree* _particles; From 5b825ecd5b02ffe5999f0e1f448b85536b2570c7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 31 Jan 2014 15:46:19 -0800 Subject: [PATCH 137/153] fix isKnownID --- libraries/particles/src/Particle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index a5ee38072f..12b59d28c9 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -1056,7 +1056,7 @@ QScriptValue ParticleProperties::copyToScriptValue(QScriptEngine* engine) const if (_idSet) { properties.setProperty("id", _id); - properties.setProperty("isKnownID", (_id == UNKNOWN_PARTICLE_ID)); + properties.setProperty("isKnownID", (_id != UNKNOWN_PARTICLE_ID)); } return properties; From d2fd4bf445b22d1d5383bf591f9cefc5fe85c58a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 16:36:44 -0800 Subject: [PATCH 138/153] expose QTimer to the script engine --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 6ea3742592..25467389de 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -105,6 +105,7 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents) { } Q_SCRIPT_DECLARE_QMETAOBJECT(AudioInjectorOptions, QObject*) +Q_SCRIPT_DECLARE_QMETAOBJECT(QTimer, QObject*) void ScriptEngine::init() { if (_isInitialized) { @@ -132,6 +133,9 @@ void ScriptEngine::init() { QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject(); _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); + + QScriptValue timerValue = _engine.scriptValueFromQMetaObject(); + _engine.globalObject().setProperty("Timer", timerValue); registerGlobalObject("Agent", this); registerGlobalObject("Audio", &_audioScriptingInterface); From a7f240650584b3597a8af8f0a2303b5c919c0882 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 16:40:24 -0800 Subject: [PATCH 139/153] add an example timer script --- examples/timer.js | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 examples/timer.js diff --git a/examples/timer.js b/examples/timer.js new file mode 100644 index 0000000000..7ce6418c24 --- /dev/null +++ b/examples/timer.js @@ -0,0 +1,5 @@ +var timer = new Timer(); +timer.interval = 1000; +timer.singleShot = true; // set this is you only want the timer to fire once +timer.timeout.connect(function() { print("TIMER FIRED!"); }); +timer.start(); From c754663582609b2c3ac15eaa1f67195fb78b04cc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 17:09:08 -0800 Subject: [PATCH 140/153] force queued connection for readyRead on node socket in case it moves, closes #1685 --- assignment-client/src/AssignmentClient.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index cdf0da43de..b2a5555e36 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -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() { From 289be04f0f7fb58fd79a31454e2f300f9b29f1bc Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 31 Jan 2014 17:12:50 -0800 Subject: [PATCH 141/153] use tryLock in some cases where we don't want to block and pass already locked to the findParticleByID() in scripting interface --- interface/src/ParticleTreeRenderer.cpp | 9 +++++---- libraries/particles/src/ParticleCollisionSystem.cpp | 7 ++++--- .../particles/src/ParticlesScriptingInterface.cpp | 11 ++++++++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/interface/src/ParticleTreeRenderer.cpp b/interface/src/ParticleTreeRenderer.cpp index 7c82f76aab..d96ac2beaa 100644 --- a/interface/src/ParticleTreeRenderer.cpp +++ b/interface/src/ParticleTreeRenderer.cpp @@ -32,10 +32,11 @@ void ParticleTreeRenderer::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(); + } } } diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index e69632f379..c718ab3ddc 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -56,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(); + } } diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index 73aaad4cc5..a25dde1b9e 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -43,6 +43,7 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr ParticleID ParticlesScriptingInterface::identifyParticle(ParticleID particleID) { uint32_t actualID = particleID.id; + if (!particleID.isKnownID) { actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID); if (actualID == UNKNOWN_PARTICLE_ID) { @@ -65,8 +66,12 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID } if (_particleTree) { _particleTree->lockForRead(); - const Particle* particle = _particleTree->findParticleByID(identity.id); - results.copyFromParticle(*particle); + const Particle* particle = _particleTree->findParticleByID(identity.id, true); + if (particle) { + results.copyFromParticle(*particle); + } else { + results.setIsUnknownID(); + } _particleTree->unlock(); } @@ -123,7 +128,7 @@ void ParticlesScriptingInterface::deleteParticle(ParticleID particleID) { if (actualID != UNKNOWN_PARTICLE_ID) { particleID.id = actualID; particleID.isKnownID = true; - queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + queueParticleMessage(PacketTypeParticleAddOrEdit, particleID, properties); } // If we have a local particle tree set, then also update it. From d65a3ee1747b160805e45bda12131f7b1e23c38b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 17:38:53 -0800 Subject: [PATCH 142/153] spacing cleanup in Application and ScriptEngine --- interface/src/Application.cpp | 51 ++++++++++---------- libraries/script-engine/src/ScriptEngine.cpp | 3 +- libraries/script-engine/src/ScriptEngine.h | 4 +- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bf17736368..289e7e2430 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -321,7 +321,7 @@ Application::~Application() { _persistThread->deleteLater(); _persistThread = NULL; } - + storeSizeAndPosition(); saveScripts(); _sharedVoxelSystem.changeTree(new VoxelTree); @@ -4186,33 +4186,33 @@ 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); } @@ -4244,7 +4244,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 diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 9a7a9197b0..a18fb58531 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -40,7 +40,8 @@ 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(), diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 8f29379266..a07f18f5f7 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -33,8 +33,8 @@ class ScriptEngine : public QObject { Q_OBJECT 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(); From ed93d8db392889a79f4d324122e9a6f8ea69dac3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 31 Jan 2014 17:42:48 -0800 Subject: [PATCH 143/153] Moving headMouse, transmitter, and touchYaw/Pitch stuff into MyAvatar. --- interface/src/Application.cpp | 227 +++++-------------------- interface/src/Application.h | 13 +- interface/src/DatagramProcessor.cpp | 2 +- interface/src/avatar/Avatar.cpp | 2 +- interface/src/avatar/Avatar.h | 3 +- interface/src/avatar/AvatarManager.cpp | 14 +- interface/src/avatar/AvatarManager.h | 4 +- interface/src/avatar/MyAvatar.cpp | 177 ++++++++++++++++++- interface/src/avatar/MyAvatar.h | 19 ++- interface/src/devices/Transmitter.cpp | 4 +- interface/src/devices/Transmitter.h | 4 +- 11 files changed, 240 insertions(+), 229 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 916109c290..09e4c1ca62 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -132,8 +132,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), @@ -1821,9 +1819,9 @@ 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); // TODO: move _myAvatar out of Application. Move relevant code to MyAvataar or AvatarManager _avatarManager.init(); @@ -2164,11 +2162,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; } @@ -2207,24 +2200,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()"); @@ -2243,32 +2218,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()"); @@ -2376,13 +2325,11 @@ 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 @@ -2392,54 +2339,17 @@ void Application::update(float deltaTime) { _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().setPitch(_myAvatar->getHead().getPitch() + _pitchFromTouch); - _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 - QByteArray avatarData = byteArrayWithPopluatedHeader(PacketTypeAvatarData); - avatarData.append(_myAvatar->toByteArray()); + QByteArray packet = byteArrayWithPopluatedHeader(PacketTypeAvatarData); + packet.append(_myAvatar->toByteArray()); - controlledBroadcastToNodes(avatarData, NodeSet() << NodeType::AvatarMixer); + 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 @@ -2991,29 +2901,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(); } @@ -3037,71 +2926,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); @@ -3951,8 +3807,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(); @@ -3960,11 +3816,8 @@ void Application::resetSensors() { OculusManager::reset(); } - QCursor::setPos(_headMouseX, _headMouseY); + QCursor::setPos(_mouseX, _mouseY); _myAvatar->reset(); - _myTransmitter.resetLevels(); - _myAvatar->setVelocity(glm::vec3(0,0,0)); - _myAvatar->setThrust(glm::vec3(0,0,0)); QMetaObject::invokeMethod(&_audio, "reset", Qt::QueuedConnection); } @@ -4093,7 +3946,7 @@ void Application::nodeKilled(SharedNodePointer node) { } else if (node->getType() == NodeType::AvatarMixer) { // our avatar mixer has gone away - clear the hash of avatars - _avatarManager.clearMixedAvatars(); + _avatarManager.clearOtherAvatars(); } } @@ -4291,7 +4144,7 @@ void Application::toggleLogDialog() { } void Application::initAvatarAndViewFrustum() { - updateAvatar(0.f); + updateMyAvatar(0.f); } QString Application::getLocalVoxelCacheFileName() { diff --git a/interface/src/Application.h b/interface/src/Application.h index a0b52365cf..111110296f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -282,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); @@ -297,7 +295,7 @@ private: void renderLookatIndicator(glm::vec3 pointOfInterest); void renderHighlightVoxel(VoxelDetail voxel); - void updateAvatar(float deltaTime); + void updateMyAvatar(float deltaTime); void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); @@ -377,8 +375,6 @@ private: 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 - Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar - Faceshift _faceshift; SixenseManager _sixenseManager; @@ -397,8 +393,6 @@ private: Environment _environment; - int _headMouseX, _headMouseY; - int _mouseX; int _mouseY; int _mouseDragStartedX; @@ -417,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) @@ -443,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 diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 32d57352d9..2938be80ea 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -45,7 +45,7 @@ void DatagramProcessor::processDatagrams() { switch (packetTypeForPacket(incomingPacket)) { case PacketTypeTransmitterData: // V2 = IOS transmitter app - application->_myTransmitter.processIncomingData(reinterpret_cast(incomingPacket.data()), + application->getAvatar()->getTransmitter().processIncomingData(reinterpret_cast(incomingPacket.data()), incomingPacket.size()); break; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ab62720410..4b4c189729 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -106,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); } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 6aaf10b7b7..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 diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c7cf83c20a..78b8bad3ed 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -33,10 +33,10 @@ void AvatarManager::init() { _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(); @@ -46,14 +46,14 @@ void AvatarManager::updateAvatars(float deltaTime) { while (avatarIterator != _avatarHash.end()) { Avatar* avatar = static_cast(avatarIterator.value().data()); if (avatar == static_cast(_myAvatar.data())) { - // for now skip updates to _myAvatar because it is done explicitly in Application - // TODO: update _myAvatar in this context + // 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->simulate(deltaTime, NULL); + avatar->simulate(deltaTime); avatar->setMouseRay(mouseOrigin, mouseDirection); ++avatarIterator; } else { @@ -108,7 +108,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { if (avatar->getTargetScale() < MIN_FADE_SCALE) { fadingIterator = _avatarFades.erase(fadingIterator); } else { - avatar->simulate(deltaTime, NULL); + avatar->simulate(deltaTime); ++fadingIterator; } } @@ -224,7 +224,7 @@ AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) } } -void AvatarManager::clearMixedAvatars() { +void AvatarManager::clearOtherAvatars() { // clear any avatars that came from an avatar-mixer AvatarHash::iterator removeAvatar = _avatarHash.begin(); while (removeAvatar != _avatarHash.end()) { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index e9ff77281f..6605f5c234 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -29,10 +29,10 @@ public: MyAvatar* getMyAvatar() { return _myAvatar.data(); } - void updateAvatars(float deltaTime); + void updateOtherAvatars(float deltaTime); void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); - void clearMixedAvatars(); + void clearOtherAvatars(); public slots: void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c1822e5df8..0bdc5e9748 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -16,10 +16,14 @@ #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" @@ -66,8 +70,15 @@ MyAvatar::~MyAvatar() { } 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) { @@ -75,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(); @@ -97,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(); @@ -430,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"); @@ -547,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 // @@ -595,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; @@ -615,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; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cf25517fb8..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 @@ -30,10 +32,16 @@ public: ~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; } @@ -53,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; @@ -76,9 +85,7 @@ 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: @@ -109,9 +116,13 @@ private: 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/devices/Transmitter.cpp b/interface/src/devices/Transmitter.cpp index e6da39447c..5fca045cfd 100644 --- a/interface/src/devices/Transmitter.cpp +++ b/interface/src/devices/Transmitter.cpp @@ -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; }; From 40a963cf927500ab371918ba672cc1038b475ad1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jan 2014 17:50:51 -0800 Subject: [PATCH 144/153] some application destructor cleanup --- interface/src/AbstractLoggerInterface.h | 1 + interface/src/Application.cpp | 9 ++------- interface/src/FileLogger.cpp | 5 ++++- interface/src/FileLogger.h | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) 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 289e7e2430..d0d0fc4c40 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -154,7 +154,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _resetRecentMaxPacketsSoon(true), _swatch(NULL), _pasteMode(false), - _logger(new FileLogger()), + _logger(new FileLogger(this)), _persistThread(NULL) { _myAvatar = _avatarManager.getMyAvatar(); @@ -328,12 +328,7 @@ Application::~Application() { VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown Menu::getInstance()->deleteLater(); - - _avatarManager.clear(); - _myAvatar = NULL; - - delete _logger; - delete _settings; + delete _glWidget; } diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp index 3c98b285a3..81c626a46e 100644 --- a/interface/src/FileLogger.cpp +++ b/interface/src/FileLogger.cpp @@ -18,7 +18,10 @@ 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() : _logData(NULL) { +FileLogger::FileLogger(QObject* parent) : + AbstractLoggerInterface(parent), + _logData(NULL) +{ setExtraDebugging(false); _fileName = FileUtils::standardPath(LOGS_DIRECTORY); 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; }; From e1350f8e242736fa40cc404e7a6a4b524d8ea8b2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 31 Jan 2014 19:00:24 -0800 Subject: [PATCH 145/153] Trying out a transform tweak. --- interface/src/renderer/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index c07eb8469d..fd844e61ec 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1321,7 +1321,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) qDebug() << "Joint not in model list: " << jointID; fbxCluster.jointIndex = 0; } - fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink); extracted.mesh.clusters.append(fbxCluster); // override the bind rotation with the transform link From 2b4876181a93b1ba23b8ee76f7123a1336c9a591 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 31 Jan 2014 19:38:45 -0800 Subject: [PATCH 146/153] Different take on the transform fix. --- interface/src/renderer/FBXReader.cpp | 48 +++++++++++++++++++--------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index fd844e61ec..e2c3bfafdd 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -803,6 +803,22 @@ void addBlendshapes(const ExtractedBlendshape& extracted, const QList& 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; @@ -1142,6 +1158,20 @@ 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())) { @@ -1150,20 +1180,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) 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 @@ -1321,7 +1339,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) qDebug() << "Joint not in model list: " << jointID; fbxCluster.jointIndex = 0; } - fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink); + fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; extracted.mesh.clusters.append(fbxCluster); // override the bind rotation with the transform link From 9dd8c11e1e1efdca60d800734fc996b23b123929 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Feb 2014 09:59:16 -0800 Subject: [PATCH 147/153] change script timer setup to match HTML dom standard --- examples/timer.js | 13 ++++--- libraries/script-engine/src/ScriptEngine.cpp | 40 +++++++++++++++++--- libraries/script-engine/src/ScriptEngine.h | 9 ++++- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/examples/timer.js b/examples/timer.js index 7ce6418c24..db9a51ef6f 100644 --- a/examples/timer.js +++ b/examples/timer.js @@ -1,5 +1,8 @@ -var timer = new Timer(); -timer.interval = 1000; -timer.singleShot = true; // set this is you only want the timer to fire once -timer.timeout.connect(function() { print("TIMER FIRED!"); }); -timer.start(); +var one_timer = Script.setTimeout(function() { print("One time timer fired!"); }, 1000); +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/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index d040e5b68e..8e45c319c2 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -105,9 +105,6 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents) { return true; } -Q_SCRIPT_DECLARE_QMETAOBJECT(AudioInjectorOptions, QObject*) -Q_SCRIPT_DECLARE_QMETAOBJECT(QTimer, QObject*) - void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once @@ -134,9 +131,6 @@ void ScriptEngine::init() { QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject(); _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); - - QScriptValue timerValue = _engine.scriptValueFromQMetaObject(); - _engine.globalObject().setProperty("Timer", timerValue); registerGlobalObject("Script", this); registerGlobalObject("Audio", &_audioScriptingInterface); @@ -302,4 +296,38 @@ 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; + } +} + +void 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); + connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired); + _timerFunctionMap.insert(newTimer, function); + + newTimer->setSingleShot(isSingleShot); + + newTimer->start(intervalMS); +} + +void ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) { + setupTimerWithInterval(function, intervalMS, false); +} + +void ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) { + setupTimerWithInterval(function, timeoutMS, true); +} diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index a07f18f5f7..d706ed7bb0 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -59,12 +59,17 @@ public: bool isAvatar() const { return _isAvatar; } void setAvatarData(AvatarData* avatarData, const QString& objectName); + + void timerFired(); 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 setInterval(const QScriptValue& function, int intervalMS); + void setTimeout(const QScriptValue& function, int timeoutMS); signals: void willSendAudioDataCallback(); @@ -73,15 +78,17 @@ signals: void finished(const QString& fileNameString); protected: - QString _scriptContents; bool _isFinished; bool _isRunning; bool _isInitialized; QScriptEngine _engine; bool _isAvatar; + QHash _timerFunctionMap; private: + void setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); + static VoxelsScriptingInterface _voxelsScriptingInterface; static ParticlesScriptingInterface _particlesScriptingInterface; AbstractControllerScriptingInterface* _controllerScriptingInterface; From 906d913d713529d051a478d4342b934af85b14ad Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Feb 2014 10:05:07 -0800 Subject: [PATCH 148/153] added more examples, updated lookWithMouse.js to support alwaysLook --- examples/lookWithMouse.js | 35 ++++- examples/lookWithTouch.js | 86 ++++++++++++ examples/spaceInvadersExample.js | 224 +++++++++++++++++++++++++++++++ 3 files changed, 338 insertions(+), 7 deletions(-) create mode 100644 examples/lookWithTouch.js create mode 100644 examples/spaceInvadersExample.js diff --git a/examples/lookWithMouse.js b/examples/lookWithMouse.js index f404019bc1..460663c054 100644 --- a/examples/lookWithMouse.js +++ b/examples/lookWithMouse.js @@ -9,29 +9,36 @@ // // +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) { - print("mousePressEvent event.x,y=" + event.x + ", " + event.y); + if (wantDebugging) { + print("mousePressEvent event.x,y=" + event.x + ", " + event.y); + } isMouseDown = true; lastX = event.x; lastY = event.y; } function mouseReleaseEvent(event) { - print("mouseReleaseEvent event.x,y=" + event.x + ", " + event.y); + if (wantDebugging) { + print("mouseReleaseEvent event.x,y=" + event.x + ", " + event.y); + } isMouseDown = false; } function mouseMoveEvent(event) { - print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y); + if (wantDebugging) { + print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y); + } - if (isMouseDown) { - print("isMouseDown... attempting to change pitch..."); + if (alwaysLook || isMouseDown) { var MOUSE_YAW_SCALE = -0.25; var MOUSE_PITCH_SCALE = -12.5; var FIXED_MOUSE_TIMESTEP = 0.016; @@ -43,12 +50,26 @@ function mouseMoveEvent(event) { } function update() { + if (wantDebugging) { + print("update()..."); + } // rotate body yaw for yaw received from mouse - MyAvatar.orientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } )); + 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 - MyAvatar.headPitch = MyAvatar.headPitch + pitchFromMouse; + var newPitch = MyAvatar.headPitch + pitchFromMouse; + if (wantDebugging) { + print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch); + } + MyAvatar.headPitch = newPitch; pitchFromMouse = 0; } 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/spaceInvadersExample.js b/examples/spaceInvadersExample.js new file mode 100644 index 0000000000..c575cbcd76 --- /dev/null +++ b/examples/spaceInvadersExample.js @@ -0,0 +1,224 @@ +// +// 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 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: 60 * 5 // 5 minutes + }; + myShip = Particles.addParticle(myShipProperties); +} + +function initializeInvaders() { + for (var row = 0; row < numberOfRows; row++) { + invaders[row] = new Array(); + for (var column = 0; column < invadersPerRow; column++) { + invaderPosition = { + x: invadersBottomCorner.x + (column * columnWidth), + y: invadersBottomCorner.y + (row * rowHeight), + z: invadersBottomCorner.z }; + + + 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: 60 * 5 // 5 minutes + }); + + print("invaders[row][column].creatorTokenID=" + invaders[row][column].creatorTokenID); + } + } +} + +var invadersMovingRight = true; +function moveInvaders() { + print("moveInvaders()..."); + if (invadersMovingRight) { + invaderMoveX = 0.05; + } else { + invaderMoveX = -0.05; + } + for (var row = 0; row < numberOfRows; row++) { + for (var column = 0; column < invadersPerRow; column++) { + props = Particles.getParticleProperties(invaders[row][column]); + if (props.isKnownID) { + newPosition = { x: props.position.x + invaderMoveX, + y: props.position.y, + z: props.position.z }; + Particles.editParticle(invaders[row][column], { position: newPosition }); + } + } + } +} + +function update() { + print("updating space invaders... iteration="+iteration); + iteration++; + if ((iteration % 60) == 0) { + invadersMovingRight = !invadersMovingRight; + } + moveInvaders(); +} + +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(update); + +function endGame() { + print("ending game..."); + Particles.deleteParticle(myShip); + print("endGame() ... 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("endGame() ... Particles.deleteParticle(invaders[row][column])... invaders[row][column].id="+invaders[row][column].id); + } + } + + // clean up our missile + if (missileFired) { + Particles.deleteParticle(myMissile); + } + Agent.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(); + + From 8b2f18593706d1bbac2d323d3404b4e0c3d59ec4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Feb 2014 10:05:26 -0800 Subject: [PATCH 149/153] always emit update even when no servers --- libraries/script-engine/src/ScriptEngine.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index cf9b582e2c..cf49a9cddb 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -208,11 +208,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(); @@ -223,9 +219,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(); @@ -251,9 +244,7 @@ void ScriptEngine::run() { nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer); } - if (willSendVisualDataCallBack) { - emit willSendVisualDataCallback(); - } + emit willSendVisualDataCallback(); if (_engine.hasUncaughtException()) { int line = _engine.uncaughtExceptionLineNumber(); From e2842ab7a472733cef92eef101674994028e345f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Feb 2014 10:10:58 -0800 Subject: [PATCH 150/153] stop timers when the script does, add cleanup methods --- examples/timer.js | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 29 ++++++++++++++------ libraries/script-engine/src/ScriptEngine.h | 11 +++++--- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/examples/timer.js b/examples/timer.js index db9a51ef6f..c7ad0290ab 100644 --- a/examples/timer.js +++ b/examples/timer.js @@ -1,4 +1,4 @@ -var one_timer = Script.setTimeout(function() { print("One time timer fired!"); }, 1000); +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 diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8e45c319c2..3b5cedb3ad 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -312,22 +312,35 @@ void ScriptEngine::timerFired() { } } -void ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot) { +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); - connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired); - _timerFunctionMap.insert(newTimer, function); - 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; } -void ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) { - setupTimerWithInterval(function, intervalMS, false); +QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) { + return setupTimerWithInterval(function, intervalMS, false); } -void ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) { - setupTimerWithInterval(function, timeoutMS, true); +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 d706ed7bb0..12909a16eb 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -68,9 +68,11 @@ public slots: void stop(); void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller - void setInterval(const QScriptValue& function, int intervalMS); - void setTimeout(const QScriptValue& function, int timeoutMS); - + 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(); @@ -87,7 +89,8 @@ protected: QHash _timerFunctionMap; private: - void setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); + QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); + void stopTimer(QTimer* timer); static VoxelsScriptingInterface _voxelsScriptingInterface; static ParticlesScriptingInterface _particlesScriptingInterface; From 028eec71645171f323932f6ba826691214f773a0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Feb 2014 10:33:31 -0800 Subject: [PATCH 151/153] don't allow calls to init, run, evaluate from JS --- interface/src/Application.cpp | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 4 ++-- libraries/script-engine/src/ScriptEngine.h | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d0d0fc4c40..2ecb58eaf2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4255,7 +4255,7 @@ void Application::loadScript(const QString& fileNameString) { 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())); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 3b5cedb3ad..477d0be5b4 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -106,6 +106,7 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents) { } void ScriptEngine::init() { + qDebug() << "Init called!"; if (_isInitialized) { return; // only initialize once } @@ -342,5 +343,4 @@ void ScriptEngine::stopTimer(QTimer *timer) { _timerFunctionMap.remove(timer); delete timer; } -} - +} \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 12909a16eb..98d5860332 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -60,13 +60,14 @@ public: void setAvatarData(AvatarData* avatarData, const QString& objectName); + void init(); + void run(); /// runs continuously until Agent.stop() is called + void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller + void timerFired(); 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 QObject* setInterval(const QScriptValue& function, int intervalMS); QObject* setTimeout(const QScriptValue& function, int timeoutMS); From f41d2a3c02d0a4aa6d5cf1f7edebe3f5c2c48daa Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Feb 2014 10:35:25 -0800 Subject: [PATCH 152/153] change Agent to Script --- examples/spaceInvadersExample.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/spaceInvadersExample.js b/examples/spaceInvadersExample.js index c575cbcd76..5f9d14ebdd 100644 --- a/examples/spaceInvadersExample.js +++ b/examples/spaceInvadersExample.js @@ -10,6 +10,7 @@ var iteration = 0; +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); @@ -40,7 +41,7 @@ function initializeMyShip() { damping: 0, radius: shipSize, color: { red: 0, green: 255, blue: 0 }, - lifetime: 60 * 5 // 5 minutes + lifetime: itemLifetimes }; myShip = Particles.addParticle(myShipProperties); } @@ -62,7 +63,7 @@ function initializeInvaders() { damping: 0, radius: shipSize, color: { red: 255, green: 0, blue: 0 }, - lifetime: 60 * 5 // 5 minutes + lifetime: itemLifetimes }); print("invaders[row][column].creatorTokenID=" + invaders[row][column].creatorTokenID); @@ -101,7 +102,7 @@ function update() { } // register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(update); +Script.willSendVisualDataCallback.connect(update); function endGame() { print("ending game..."); @@ -118,7 +119,7 @@ function endGame() { if (missileFired) { Particles.deleteParticle(myMissile); } - Agent.stop(); + Script.stop(); } function moveShipTo(position) { From f33f4e9cc22f10d690235e9304808a9984f4bf7c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Feb 2014 11:09:30 -0800 Subject: [PATCH 153/153] change invaders to use absolute positions --- examples/spaceInvadersExample.js | 65 +++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/examples/spaceInvadersExample.js b/examples/spaceInvadersExample.js index 5f9d14ebdd..3a13a1c52f 100644 --- a/examples/spaceInvadersExample.js +++ b/examples/spaceInvadersExample.js @@ -10,6 +10,10 @@ 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 }; @@ -46,16 +50,28 @@ function initializeMyShip() { 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 = { - x: invadersBottomCorner.x + (column * columnWidth), - y: invadersBottomCorner.y + (row * rowHeight), - z: invadersBottomCorner.z }; - - + invaderPosition = getInvaderPosition(row, column); invaders[row][column] = Particles.addParticle({ position: invaderPosition, velocity: { x: 0, y: 0, z: 0 }, @@ -71,22 +87,14 @@ function initializeInvaders() { } } -var invadersMovingRight = true; function moveInvaders() { print("moveInvaders()..."); - if (invadersMovingRight) { - invaderMoveX = 0.05; - } else { - invaderMoveX = -0.05; - } for (var row = 0; row < numberOfRows; row++) { for (var column = 0; column < invadersPerRow; column++) { props = Particles.getParticleProperties(invaders[row][column]); if (props.isKnownID) { - newPosition = { x: props.position.x + invaderMoveX, - y: props.position.y, - z: props.position.z }; - Particles.editParticle(invaders[row][column], { position: newPosition }); + invaderPosition = getInvaderPosition(row, column); + Particles.editParticle(invaders[row][column], { position: invaderPosition }); } } } @@ -95,8 +103,14 @@ function moveInvaders() { function update() { print("updating space invaders... iteration="+iteration); iteration++; - if ((iteration % 60) == 0) { - invadersMovingRight = !invadersMovingRight; + invaderStepOfCycle++; + if (invaderStepOfCycle > invaderStepsPerCycle) { + invaderStepOfCycle = 0; + if (invaderMoveDirection > 0) { + invaderMoveDirection = -1; + } else { + invaderMoveDirection = 1; + } } moveInvaders(); } @@ -104,14 +118,15 @@ function update() { // register the call back so it fires before each data send Script.willSendVisualDataCallback.connect(update); -function endGame() { - print("ending game..."); +function cleanupGame() { + print("cleaning up game..."); Particles.deleteParticle(myShip); - print("endGame() ... Particles.deleteParticle(myShip)... myShip.id="+myShip.id); + 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("endGame() ... Particles.deleteParticle(invaders[row][column])... invaders[row][column].id="+invaders[row][column].id); + print("cleanupGame() ... Particles.deleteParticle(invaders[row][column])... invaders[row][column].id=" + +invaders[row][column].id); } } @@ -121,6 +136,12 @@ function endGame() { } Script.stop(); } +Script.scriptEnding.connect(cleanupGame); + +function endGame() { + print("ending game..."); + Script.stop(); +} function moveShipTo(position) { myShip = Particles.identifyParticle(myShip);