From 9affd60e044941d6ba70e765f731b4e010df9996 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 18 Dec 2013 13:04:27 -0800 Subject: [PATCH 01/66] Stubbed out metavoxel server. --- assignment-client/src/AssignmentFactory.cpp | 14 ++++--- .../src/metavoxels/MetavoxelServer.cpp | 41 +++++++++++++++++++ .../src/metavoxels/MetavoxelServer.h | 27 ++++++++++++ domain-server/src/DomainServer.cpp | 18 ++++++-- domain-server/src/DomainServer.h | 1 + libraries/shared/src/Assignment.cpp | 4 ++ libraries/shared/src/Assignment.h | 1 + libraries/shared/src/Node.cpp | 3 ++ libraries/shared/src/NodeTypes.h | 1 + 9 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 assignment-client/src/metavoxels/MetavoxelServer.cpp create mode 100644 assignment-client/src/metavoxels/MetavoxelServer.h diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index 815186c4f5..b32a909bfe 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -8,13 +8,15 @@ #include -#include "Agent.h" -#include "audio/AudioMixer.h" -#include "avatars/AvatarMixer.h" -#include #include +#include + +#include "Agent.h" #include "AssignmentFactory.h" +#include "audio/AudioMixer.h" +#include "avatars/AvatarMixer.h" +#include "metavoxels/MetavoxelServer.h" ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) { int headerBytes = numBytesForPacketHeader(dataBuffer); @@ -33,7 +35,9 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dat return new VoxelServer(dataBuffer, numBytes); case Assignment::ParticleServerType: return new ParticleServer(dataBuffer, numBytes); + case Assignment::MetavoxelServerType: + return new MetavoxelServer(dataBuffer, numBytes); default: return NULL; } -} \ No newline at end of file +} diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp new file mode 100644 index 0000000000..cef93007c5 --- /dev/null +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -0,0 +1,41 @@ +// +// MetavoxelServer.cpp +// hifi +// +// Created by Andrzej Kapolka on 12/18/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include + +#include "MetavoxelServer.h" + +MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) : + ThreadedAssignment(dataBuffer, numBytes) { +} + +void MetavoxelServer::run() { + // change the logging target name while AvatarMixer is running + Logging::setTargetName("metavoxel-server"); + + NodeList* nodeList = NodeList::getInstance(); + nodeList->setOwnerType(NODE_TYPE_METAVOXEL_SERVER); + + QTimer* domainServerTimer = new QTimer(this); + connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); + domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); + + QTimer* pingNodesTimer = new QTimer(this); + connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); + pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); + + QTimer* silentNodeTimer = new QTimer(this); + connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); +} + +void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { + NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*)dataByteArray.data(), dataByteArray.size()); +} diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h new file mode 100644 index 0000000000..1f0c18117d --- /dev/null +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -0,0 +1,27 @@ +// +// MetavoxelServer.h +// hifi +// +// Created by Andrzej Kapolka on 12/28/2013. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__MetavoxelServer__ +#define __hifi__MetavoxelServer__ + +#include + +/// Maintains a shared metavoxel system, accepting change requests and broadcasting updates. +class MetavoxelServer : public ThreadedAssignment { + Q_OBJECT + +public: + + MetavoxelServer(const unsigned char* dataBuffer, int numBytes); + + virtual void run(); + + virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); +}; + +#endif /* defined(__hifi__MetavoxelServer__) */ diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ec955f5fb4..e5bfca16a1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -37,6 +37,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())), _staticAssignmentFileData(NULL), _voxelServerConfig(NULL), + _metavoxelServerConfig(NULL), _hasCompletedRestartHold(false) { DomainServer::setDomainServerInstance(this); @@ -55,6 +56,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : const char PARTICLE_CONFIG_OPTION[] = "--particleServerConfig"; _particleServerConfig = getCmdOption(argc, (const char**) argv, PARTICLE_CONFIG_OPTION); + const char METAVOXEL_CONFIG_OPTION[] = "--metavoxelServerConfig"; + _metavoxelServerConfig = getCmdOption(argc, (const char**)argv, METAVOXEL_CONFIG_OPTION); + // setup the mongoose web server struct mg_callbacks callbacks = {}; @@ -152,10 +156,11 @@ void DomainServer::readAvailableDatagrams() { int numBytesPublicSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodeLocalAddress); packetIndex += numBytesPublicSocket; - const char STATICALLY_ASSIGNED_NODES[3] = { + const char STATICALLY_ASSIGNED_NODES[] = { NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, - NODE_TYPE_VOXEL_SERVER + NODE_TYPE_VOXEL_SERVER, + NODE_TYPE_METAVOXEL_SERVER }; Assignment* matchingStaticAssignment = NULL; @@ -610,6 +615,13 @@ void DomainServer::prepopulateStaticAssignmentFile() { freshStaticAssignments[numFreshStaticAssignments++] = rootParticleServerAssignment; } + // handle metavoxel configuration command line argument + Assignment& metavoxelAssignment = (freshStaticAssignments[numFreshStaticAssignments++] = + Assignment(Assignment::CreateCommand, Assignment::MetavoxelServerType)); + if (_metavoxelServerConfig) { + metavoxelAssignment.setPayload((const unsigned char*)_metavoxelServerConfig, strlen(_metavoxelServerConfig)); + } + qDebug() << "Adding" << numFreshStaticAssignments << "static assignments to fresh file.\n"; _staticAssignmentFile.open(QIODevice::WriteOnly); @@ -785,4 +797,4 @@ void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() { void DomainServer::cleanup() { _staticAssignmentFile.unmap(_staticAssignmentFileData); _staticAssignmentFile.close(); -} \ No newline at end of file +} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index e6047fed9a..10cb58b786 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -62,6 +62,7 @@ private: const char* _voxelServerConfig; const char* _particleServerConfig; + const char* _metavoxelServerConfig; bool _hasCompletedRestartHold; private slots: diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 4943098812..a0168e4bf0 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -29,6 +29,8 @@ Assignment::Type Assignment::typeForNodeType(NODE_TYPE nodeType) { return Assignment::VoxelServerType; case NODE_TYPE_PARTICLE_SERVER: return Assignment::ParticleServerType; + case NODE_TYPE_METAVOXEL_SERVER: + return Assignment::MetavoxelServerType; default: return Assignment::AllTypes; } @@ -180,6 +182,8 @@ const char* Assignment::getTypeName() const { return "voxel-server"; case Assignment::ParticleServerType: return "particle-server"; + case Assignment::MetavoxelServerType: + return "metavoxel-server"; default: return "unknown"; } diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index 1aac273e36..5a7a21c112 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -29,6 +29,7 @@ public: AgentType, VoxelServerType, ParticleServerType, + MetavoxelServerType, AllTypes }; diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index e681a20277..c92bc57fd7 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -51,6 +51,7 @@ Node::~Node() { 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"; @@ -67,6 +68,8 @@ const char* Node::getTypeName() const { 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: diff --git a/libraries/shared/src/NodeTypes.h b/libraries/shared/src/NodeTypes.h index c9723f4477..37e0503bab 100644 --- a/libraries/shared/src/NodeTypes.h +++ b/libraries/shared/src/NodeTypes.h @@ -20,6 +20,7 @@ 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'; From 368f609416fe0c2773f7e9aace394ddc26e80de6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 18 Dec 2013 13:52:47 -0800 Subject: [PATCH 02/66] More metavoxel server bits. --- assignment-client/CMakeLists.txt | 3 ++- assignment-client/src/metavoxels/MetavoxelServer.cpp | 2 +- assignment-client/src/metavoxels/MetavoxelServer.h | 6 ++++++ interface/src/Application.cpp | 6 +++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 464728f7e0..153be279f4 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -13,7 +13,7 @@ find_package(Qt5Network REQUIRED) include(${MACRO_DIR}/SetupHifiProject.cmake) setup_hifi_project(${TARGET_NAME} TRUE) -qt5_use_modules(${TARGET_NAME} Network) +qt5_use_modules(${TARGET_NAME} Gui Network Script) # include glm include(${MACRO_DIR}/IncludeGLM.cmake) @@ -27,6 +27,7 @@ link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(metavoxels ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particle-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR}) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index cef93007c5..e18903197f 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -17,7 +17,7 @@ MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) } void MetavoxelServer::run() { - // change the logging target name while AvatarMixer is running + // change the logging target name while the metavoxel server is running Logging::setTargetName("metavoxel-server"); NodeList* nodeList = NodeList::getInstance(); diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 1f0c18117d..85ca05d0c9 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -11,6 +11,8 @@ #include +#include + /// Maintains a shared metavoxel system, accepting change requests and broadcasting updates. class MetavoxelServer : public ThreadedAssignment { Q_OBJECT @@ -22,6 +24,10 @@ public: virtual void run(); virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); + +private: + + MetavoxelData _data; }; #endif /* defined(__hifi__MetavoxelServer__) */ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d5c874ba6b..4aacc50291 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -211,7 +211,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // tell the NodeList instance who to tell the domain server we care about const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER, - NODE_TYPE_PARTICLE_SERVER}; + NODE_TYPE_PARTICLE_SERVER, NODE_TYPE_METAVOXEL_SERVER}; nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); QTimer* silentNodeTimer = new QTimer(this); @@ -1249,8 +1249,8 @@ void Application::wheelEvent(QWheelEvent* event) { void Application::sendPingPackets() { - const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, - NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER}; + const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, + NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_METAVOXEL_SERVER}; uint64_t currentTime = usecTimestampNow(); unsigned char pingPacket[numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_PING) + sizeof(currentTime)]; From 130ec7488ae6de69b631a5c4db061dfc6111382b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 18 Dec 2013 17:10:53 -0800 Subject: [PATCH 03/66] More work on streaming. --- libraries/metavoxels/src/AttributeRegistry.h | 37 +++++++++++++--- libraries/metavoxels/src/Bitstream.cpp | 15 ++++++- libraries/metavoxels/src/Bitstream.h | 13 ++++++ libraries/metavoxels/src/MetavoxelData.cpp | 46 ++++++++++++++++++++ libraries/metavoxels/src/MetavoxelData.h | 6 +++ 5 files changed, 110 insertions(+), 7 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index a5f9c08f8b..559828eecb 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -140,8 +140,8 @@ public: virtual void* create(void* copy) const = 0; virtual void destroy(void* value) const = 0; - virtual bool read(Bitstream& in, void*& value) const = 0; - virtual bool write(Bitstream& out, void* value) const = 0; + virtual void read(Bitstream& in, void*& value, bool isLeaf) const = 0; + virtual void write(Bitstream& out, void* value, bool isLeaf) const = 0; virtual bool equal(void* first, void* second) const = 0; @@ -163,8 +163,8 @@ public: virtual void* create(void* copy) const { void* value; new (&value) T(*(T*)©); return value; } virtual void destroy(void* value) const { ((T*)&value)->~T(); } - virtual bool read(Bitstream& in, void*& value) const { value = getDefaultValue(); in.read(&value, bits); return false; } - virtual bool write(Bitstream& out, void* value) const { out.write(&value, bits); return false; } + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; virtual bool equal(void* first, void* second) const { return decodeInline(first) == decodeInline(second); } @@ -175,6 +175,19 @@ private: T _defaultValue; }; +template inline void InlineAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (isLeaf) { + value = getDefaultValue(); + in.read(&value, bits); + } +} + +template inline void InlineAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (isLeaf) { + out.write(&value, bits); + } +} + /// Provides merging using the =, ==, += and /= operators. template class SimpleInlineAttribute : public InlineAttribute { public: @@ -216,8 +229,8 @@ public: virtual void* create(void* copy) const { new T(*static_cast(copy)); } virtual void destroy(void* value) const { delete static_cast(value); } - virtual bool read(Bitstream& in, void*& value) const { in >> *static_cast(value); return true; } - virtual bool write(Bitstream& out, void* value) const { out << *static_cast(value); return true; } + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; virtual bool equal(void* first, void* second) const { return *static_cast(first) == *static_cast(second); } @@ -228,6 +241,18 @@ private: T _defaultValue; }; +template inline void PointerAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (isLeaf) { + in.read(value, sizeof(T) * 8); + } +} + +template inline void PointerAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (isLeaf) { + out.write(value, sizeof(T) * 8); + } +} + /// Provides merging using the =, ==, += and /= operators. template class SimplePointerAttribute : public PointerAttribute { public: diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index ac91bdf767..032ff27068 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -10,6 +10,12 @@ #include "Bitstream.h" +QHash Bitstream::_metaObjects; + +void Bitstream::registerMetaObject(const QByteArray& name, const QMetaObject* metaObject) { + _metaObjects.insert(name, metaObject); +} + Bitstream::Bitstream(QDataStream& underlying) : _underlying(underlying), _byte(0), _position(0) { } @@ -60,7 +66,6 @@ void Bitstream::flush() { } } - Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); @@ -79,3 +84,11 @@ Bitstream& Bitstream::operator>>(bool& value) { _position = (_position + 1) & LAST_BIT_POSITION; return *this; } + +Bitstream& Bitstream::operator<<(qint32 value) { + return write(&value, 32, 0); +} + +Bitstream& Bitstream::operator>>(qint32& value) { + return read(&value, 32, 0); +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 12a1b88886..74a5d76775 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -9,14 +9,20 @@ #ifndef __interface__Bitstream__ #define __interface__Bitstream__ +#include #include +class QByteArray; class QDataStream; +class QMetaObject; /// A stream for bit-aligned data. class Bitstream { public: + /// Registers a metaobject under its name so that instances of it can be streamed. + static void registerMetaObject(const QByteArray& name, const QMetaObject* metaObject); + Bitstream(QDataStream& underlying); /// Writes a set of bits to the underlying stream. @@ -35,11 +41,18 @@ public: Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); + Bitstream& operator<<(qint32 value); + Bitstream& operator>>(qint32& value); + + + private: QDataStream& _underlying; quint8 _byte; int _position; + + static QHash _metaObjects; }; #endif /* defined(__interface__Bitstream__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 636982b63a..e464399508 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -64,6 +64,21 @@ AttributeValue MetavoxelData::getAttributeValue(const MetavoxelPath& path, const return node->getAttributeValue(attribute); } +void MetavoxelData::read(Bitstream& in) { + qint32 rootCount; + in >> rootCount; + for (int i = 0; i < rootCount; i++) { + + } +} + +void MetavoxelData::write(Bitstream& out) const { + out << (qint32)_roots.size(); + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + it.value()->write(it.key(), out); + } +} + MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) { _attributeValue = attributeValue.copy(); for (int i = 0; i < CHILD_COUNT; i++) { @@ -118,6 +133,37 @@ bool MetavoxelNode::isLeaf() const { return true; } +void MetavoxelNode::read(const AttributePointer& attribute, Bitstream& in) { + bool leaf; + in >> leaf; + attribute->read(in, _attributeValue, leaf); + if (leaf) { + clearChildren(attribute); + + } else { + void* childValues[CHILD_COUNT]; + for (int i = 0; i < CHILD_COUNT; i++) { + if (!_children[i]) { + _children[i] = new MetavoxelNode(attribute); + } + _children[i]->read(attribute, in); + childValues[i] = _children[i]->_attributeValue; + } + attribute->merge(_attributeValue, childValues); + } +} + +void MetavoxelNode::write(const AttributePointer& attribute, Bitstream& out) const { + bool leaf = isLeaf(); + out << leaf; + attribute->write(out, _attributeValue, leaf); + if (!leaf) { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->write(attribute, out); + } + } +} + void MetavoxelNode::destroy(const AttributePointer& attribute) { attribute->destroy(_attributeValue); for (int i = 0; i < CHILD_COUNT; i++) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index fc7045cff4..32c66c789c 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -41,6 +41,9 @@ public: /// Retrieves the attribute value corresponding to the specified path. AttributeValue getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const; + void read(Bitstream& in); + void write(Bitstream& out) const; + private: QHash _roots; @@ -69,6 +72,9 @@ public: bool isLeaf() const; + void read(const AttributePointer& attribute, Bitstream& in); + void write(const AttributePointer& attribute, Bitstream& out) const; + void destroy(const AttributePointer& attribute); private: From b7f0057fe97031653fb3a07985b3d008e7023af7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 19 Dec 2013 16:53:38 -0800 Subject: [PATCH 04/66] More streaming work. --- .../metavoxels/src/AttributeRegistry.cpp | 9 +- libraries/metavoxels/src/AttributeRegistry.h | 11 ++- libraries/metavoxels/src/Bitstream.cpp | 94 +++++++++++++++---- libraries/metavoxels/src/Bitstream.h | 38 ++++++-- libraries/metavoxels/src/MetavoxelData.cpp | 24 ++++- 5 files changed, 142 insertions(+), 34 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 5fb03035da..9a1f220034 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -11,7 +11,12 @@ #include "AttributeRegistry.h" #include "MetavoxelData.h" -AttributeRegistry AttributeRegistry::_instance; +REGISTER_META_OBJECT(QRgbAttribute) + +AttributeRegistry* AttributeRegistry::getInstance() { + static AttributeRegistry registry; + return ®istry; +} AttributeRegistry::AttributeRegistry() : _guideAttribute(registerAttribute(new PolymorphicAttribute("guide", PolymorphicDataPointer(new DefaultMetavoxelGuide())))), @@ -36,7 +41,7 @@ AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute } QScriptValue AttributeRegistry::getAttribute(QScriptContext* context, QScriptEngine* engine) { - return engine->newQObject(_instance.getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership, + return engine->newQObject(getInstance()->getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 559828eecb..04a7a43a7f 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -32,7 +32,7 @@ class AttributeRegistry { public: /// Returns a pointer to the singleton registry instance. - static AttributeRegistry* getInstance() { return &_instance; } + static AttributeRegistry* getInstance(); AttributeRegistry(); @@ -65,8 +65,6 @@ private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); - static AttributeRegistry _instance; - QHash _attributes; AttributePointer _guideAttribute; AttributePointer _colorAttribute; @@ -170,7 +168,7 @@ public: virtual void* getDefaultValue() const { return encodeInline(_defaultValue); } -private: +protected: T _defaultValue; }; @@ -211,9 +209,12 @@ template inline bool SimpleInlineAttribute::merge(vo /// Provides appropriate averaging for RGBA values. class QRgbAttribute : public InlineAttribute { + Q_OBJECT + Q_PROPERTY(int defaultValue MEMBER _defaultValue) + public: - QRgbAttribute(const QString& name, QRgb defaultValue = QRgb()); + Q_INVOKABLE QRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb()); virtual bool merge(void*& parent, void* children[]) const; diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index cf575a9912..744177a5c5 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -12,21 +12,30 @@ #include #include +#include "AttributeRegistry.h" #include "Bitstream.h" -QHash Bitstream::_metaObjects; -QHash Bitstream::_typeStreamers; +REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) +REGISTER_SIMPLE_TYPE_STREAMER(QString) +REGISTER_SIMPLE_TYPE_STREAMER(bool) +REGISTER_SIMPLE_TYPE_STREAMER(int) -void Bitstream::registerMetaObject(const QByteArray& name, const QMetaObject* metaObject) { - _metaObjects.insert(name, metaObject); +int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { + getMetaObjects().insert(className, metaObject); + return 0; } -void Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { - _typeStreamers.insert(type, streamer); +int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { + getTypeStreamers().insert(type, streamer); + return 0; } Bitstream::Bitstream(QDataStream& underlying) : - _underlying(underlying), _byte(0), _position(0), _classNameStreamer(*this) { + _underlying(underlying), + _byte(0), + _position(0), + _classNameStreamer(*this), + _attributeStreamer(*this) { } const int BITS_IN_BYTE = 8; @@ -94,37 +103,63 @@ Bitstream& Bitstream::operator>>(bool& value) { return *this; } -Bitstream& Bitstream::operator<<(qint32 value) { +Bitstream& Bitstream::operator<<(int value) { return write(&value, 32); } -Bitstream& Bitstream::operator>>(qint32& value) { - return read(&value, 32); +Bitstream& Bitstream::operator>>(int& value) { + qint32 sizedValue; + read(&sizedValue, 32); + value = sizedValue; + return *this; } Bitstream& Bitstream::operator<<(const QByteArray& string) { - *this << (qint32)string.size(); + *this << string.size(); return write(string.constData(), string.size() * BITS_IN_BYTE); } Bitstream& Bitstream::operator>>(QByteArray& string) { - qint32 size; + int size; *this >> size; return read(string.data(), size * BITS_IN_BYTE); } Bitstream& Bitstream::operator<<(const QString& string) { - *this << (qint32)string.size(); + *this << string.size(); return write(string.constData(), string.size() * sizeof(QChar) * BITS_IN_BYTE); } Bitstream& Bitstream::operator>>(QString& string) { - qint32 size; + int size; *this >> size; return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE); } +Bitstream& Bitstream::operator<<(const QVariant& value) { + *this << value.userType(); + TypeStreamer* streamer = getTypeStreamers().value(value.userType()); + if (streamer) { + streamer->write(*this, value); + } + return *this; +} + +Bitstream& Bitstream::operator>>(QVariant& value) { + int type; + *this >> type; + TypeStreamer* streamer = getTypeStreamers().value(type); + if (streamer) { + value = streamer->read(*this); + } + return *this; +} + Bitstream& Bitstream::operator<<(QObject* object) { + if (!object) { + _classNameStreamer << QByteArray(); + return *this; + } const QMetaObject* metaObject = object->metaObject(); _classNameStreamer << QByteArray::fromRawData(metaObject->className(), strlen(metaObject->className())); for (int i = 0; i < metaObject->propertyCount(); i++) { @@ -132,7 +167,7 @@ Bitstream& Bitstream::operator<<(QObject* object) { if (!property.isStored(object)) { continue; } - TypeStreamer* streamer = _typeStreamers.value(property.userType()); + TypeStreamer* streamer = getTypeStreamers().value(property.userType()); if (streamer) { streamer->write(*this, property.read(object)); } @@ -143,7 +178,11 @@ Bitstream& Bitstream::operator<<(QObject* object) { Bitstream& Bitstream::operator>>(QObject*& object) { QByteArray className; _classNameStreamer >> className; - const QMetaObject* metaObject = _metaObjects.value(className); + if (className.isEmpty()) { + object = NULL; + return *this; + } + const QMetaObject* metaObject = getMetaObjects().value(className); if (metaObject) { object = metaObject->newInstance(); for (int i = 0; i < metaObject->propertyCount(); i++) { @@ -151,7 +190,7 @@ Bitstream& Bitstream::operator>>(QObject*& object) { if (!property.isStored(object)) { continue; } - TypeStreamer* streamer = _typeStreamers.value(property.userType()); + TypeStreamer* streamer = getTypeStreamers().value(property.userType()); if (streamer) { property.write(object, streamer->read(*this)); } @@ -162,6 +201,27 @@ Bitstream& Bitstream::operator>>(QObject*& object) { return *this; } +Bitstream& Bitstream::operator<<(const AttributePointer& attribute) { + return *this << (QObject*)attribute.data(); +} + +Bitstream& Bitstream::operator>>(AttributePointer& attribute) { + QObject* object; + *this >> object; + attribute = AttributeRegistry::getInstance()->registerAttribute(static_cast(object)); + return *this; +} + +QHash& Bitstream::getMetaObjects() { + static QHash metaObjects; + return metaObjects; +} + +QHash& Bitstream::getTypeStreamers() { + static QHash typeStreamers; + return typeStreamers; +} + IDStreamer::IDStreamer(Bitstream& stream) : _stream(stream), _bits(1) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index a3cfb07e65..6f04f6a82c 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -10,6 +10,8 @@ #define __interface__Bitstream__ #include +#include +#include #include class QByteArray; @@ -17,9 +19,12 @@ class QDataStream; class QMetaObject; class QObject; +class Attribute; class Bitstream; class TypeStreamer; +typedef QSharedPointer AttributePointer; + /// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that /// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows /// us to use the minimum number of bits to encode the IDs. @@ -87,10 +92,12 @@ class Bitstream { public: /// Registers a metaobject under its name so that instances of it can be streamed. - static void registerMetaObject(const QByteArray& name, const QMetaObject* metaObject); + /// \return zero; the function only returns a value so that it can be used in static initialization + static int registerMetaObject(const char* className, const QMetaObject* metaObject); /// Registers a streamer for the specified Qt-registered type. - static void registerTypeStreamer(int type, TypeStreamer* streamer); + /// \return zero; the function only returns a value so that it can be used in static initialization + static int registerTypeStreamer(int type, TypeStreamer* streamer); /// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both. Bitstream(QDataStream& underlying); @@ -108,11 +115,14 @@ public: /// Flushes any unwritten bits to the underlying stream. void flush(); + /// Returns a reference to the attribute streamer. + RepeatedValueStreamer& getAttributeStreamer() { return _attributeStreamer; } + Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); - Bitstream& operator<<(qint32 value); - Bitstream& operator>>(qint32& value); + Bitstream& operator<<(int value); + Bitstream& operator>>(int& value); Bitstream& operator<<(const QByteArray& string); Bitstream& operator>>(QByteArray& string); @@ -120,9 +130,15 @@ public: Bitstream& operator<<(const QString& string); Bitstream& operator>>(QString& string); + Bitstream& operator<<(const QVariant& value); + Bitstream& operator>>(QVariant& value); + Bitstream& operator<<(QObject* object); Bitstream& operator>>(QObject*& object); + Bitstream& operator<<(const AttributePointer& attribute); + Bitstream& operator>>(AttributePointer& attribute); + private: QDataStream& _underlying; @@ -130,11 +146,15 @@ private: int _position; RepeatedValueStreamer _classNameStreamer; + RepeatedValueStreamer _attributeStreamer; - static QHash _metaObjects; - static QHash _typeStreamers; + static QHash& getMetaObjects(); + static QHash& getTypeStreamers(); }; +/// Macro for registering streamable meta-objects. +#define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); + /// Interface for objects that can write values to and read values from bitstreams. class TypeStreamer { public: @@ -144,11 +164,15 @@ public: }; /// A streamer that works with Bitstream's operators. -template class SimpleTypeStreamer { +template class SimpleTypeStreamer : public TypeStreamer { public: virtual void write(Bitstream& out, const QVariant& value) const { out << value.value(); } virtual QVariant read(Bitstream& in) const { T value; in >> value; return QVariant::fromValue(value); } }; +/// Macro for registering simple type streamers. +#define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ + Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); + #endif /* defined(__interface__Bitstream__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 672acb4489..612900e658 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -65,17 +65,35 @@ AttributeValue MetavoxelData::getAttributeValue(const MetavoxelPath& path, const } void MetavoxelData::read(Bitstream& in) { + // save the old roots and clear + QHash oldRoots = _roots; + _roots.clear(); + + // read in the new roots, reusing old ones where appropriate qint32 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); + root->read(attribute, in); + } + + // clear out the remaining old roots + for (QHash::const_iterator it = oldRoots.constBegin(); it != oldRoots.constEnd(); it++) { + it.value()->destroy(it.key()); + delete it.value(); + } } void MetavoxelData::write(Bitstream& out) const { out << (qint32)_roots.size(); for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - + out.getAttributeStreamer() << it.key(); it.value()->write(it.key(), out); } } From 11e729c3cc3cdf6956dc3d624381c137154b480a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 19 Dec 2013 18:03:49 -0800 Subject: [PATCH 05/66] Starting on delta encoding. --- libraries/metavoxels/src/AttributeRegistry.h | 3 ++ libraries/metavoxels/src/MetavoxelData.cpp | 53 ++++++++++++++++++++ libraries/metavoxels/src/MetavoxelData.h | 6 +++ 3 files changed, 62 insertions(+) diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 04a7a43a7f..4f2f1d79b2 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -141,6 +141,9 @@ public: virtual void read(Bitstream& in, void*& value, bool isLeaf) const = 0; virtual void write(Bitstream& out, void* value, bool isLeaf) const = 0; + virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } + virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } + virtual bool equal(void* first, void* second) const = 0; /// Merges the value of a parent and its children. diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 612900e658..f7879e8b6d 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -98,6 +98,12 @@ void MetavoxelData::write(Bitstream& out) const { } } +void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { +} + +void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const { +} + MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) { _attributeValue = attributeValue.copy(); for (int i = 0; i < CHILD_COUNT; i++) { @@ -183,6 +189,53 @@ void MetavoxelNode::write(const AttributePointer& attribute, Bitstream& out) con } } +void MetavoxelNode::readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in) { + bool different; + in >> different; + if (!different) { + return; + } + bool leaf; + in >> leaf; + attribute->readDelta(in, _attributeValue, reference._attributeValue, leaf); + if (leaf) { + clearChildren(attribute); + + } else { + if (reference.isLeaf()) { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->read(attribute, in); + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->readDelta(attribute, *reference._children[i], in); + } + } + } +} + +void MetavoxelNode::writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const { + if (this == &reference) { + out << false; + return; + } + out << true; + bool leaf = isLeaf(); + out << leaf; + attribute->writeDelta(out, _attributeValue, reference._attributeValue, leaf); + if (!leaf) { + if (reference.isLeaf()) { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->write(attribute, out); + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->writeDelta(attribute, *reference._children[i], out); + } + } + } +} + void MetavoxelNode::destroy(const AttributePointer& attribute) { attribute->destroy(_attributeValue); for (int i = 0; i < CHILD_COUNT; i++) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 32c66c789c..be6b8c2288 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -44,6 +44,9 @@ public: void read(Bitstream& in); void write(Bitstream& out) const; + void readDelta(const MetavoxelData& reference, Bitstream& in); + void writeDelta(const MetavoxelData& reference, Bitstream& out) const; + private: QHash _roots; @@ -75,6 +78,9 @@ public: void read(const AttributePointer& attribute, Bitstream& in); void write(const AttributePointer& attribute, Bitstream& out) const; + void readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in); + void writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const; + void destroy(const AttributePointer& attribute); private: From 775f32b94c6900bea9fcbb6bf2dfb6a8816f80e1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 20 Dec 2013 14:33:11 -0800 Subject: [PATCH 06/66] First pass at datagram sequencer. --- .../metavoxels/src/DatagramSequencer.cpp | 102 ++++++++++++++++++ libraries/metavoxels/src/DatagramSequencer.h | 51 +++++++++ 2 files changed, 153 insertions(+) create mode 100644 libraries/metavoxels/src/DatagramSequencer.cpp create mode 100644 libraries/metavoxels/src/DatagramSequencer.h diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp new file mode 100644 index 0000000000..26be4f5f26 --- /dev/null +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -0,0 +1,102 @@ +// +// DatagramSequencer.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 12/20/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include "DatagramSequencer.h" + +const int MAX_DATAGRAM_SIZE = 1500; + +DatagramSequencer::DatagramSequencer() : + _datagramStream(&_datagramBuffer), + _outgoingPacketNumber(0), + _outgoingDatagram(MAX_DATAGRAM_SIZE, 0), + _incomingPacketNumber(0) { + + _datagramStream.setByteOrder(QDataStream::LittleEndian); +} + +/// Simple RAII-style object to keep a device open when in scope. +class QIODeviceOpener { +public: + + QIODeviceOpener(QIODevice* device, QIODevice::OpenMode mode) : _device(device) { _device->open(mode); } + ~QIODeviceOpener() { _device->close(); } + +private: + + QIODevice* _device; +}; + +void DatagramSequencer::sendPacket(const QByteArray& packet) { + _datagramBuffer.setBuffer(&_outgoingDatagram); + QIODeviceOpener opener(&_datagramBuffer, QIODevice::WriteOnly); + + // increment the packet number + _outgoingPacketNumber++; + + // write the sequence number and size, which are the same between all fragments + _datagramStream << (quint32)_outgoingPacketNumber; + _datagramStream << (quint32)packet.size(); + int initialPosition = _datagramBuffer.pos(); + + // break the packet into MTU-sized datagrams + int offset = 0; + do { + _datagramBuffer.seek(initialPosition); + _datagramStream << (quint32)offset; + + int payloadSize = qMin((int)(_outgoingDatagram.size() - _datagramBuffer.pos()), packet.size() - offset); + memcpy(_outgoingDatagram.data() + _datagramBuffer.pos(), packet.constData() + offset, payloadSize); + + emit readyToWrite(QByteArray::fromRawData(_outgoingDatagram.constData(), _datagramBuffer.pos() + payloadSize)); + + offset += payloadSize; + + } while(offset < packet.size()); +} + +void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { + _datagramBuffer.setBuffer(const_cast(&datagram)); + QIODeviceOpener opener(&_datagramBuffer, QIODevice::ReadOnly); + + // read the sequence number + quint32 sequenceNumber; + _datagramStream >> sequenceNumber; + + // if it's less than the last, ignore + if (sequenceNumber < _incomingPacketNumber) { + return; + } + + // read the size and offset + quint32 packetSize, offset; + _datagramStream >> packetSize >> offset; + + // if it's greater, reset + if (sequenceNumber > _incomingPacketNumber) { + _incomingPacketNumber = sequenceNumber; + _incomingPacketData.resize(packetSize); + _offsetsReceived.clear(); + _remainingBytes = packetSize; + } + + // make sure it's not a duplicate + if (_offsetsReceived.contains(offset)) { + return; + } + _offsetsReceived.insert(offset); + + // copy in the data + memcpy(_incomingPacketData.data() + offset, datagram.constData() + _datagramBuffer.pos(), _datagramBuffer.bytesAvailable()); + + // see if we're done + if ((_remainingBytes -= _datagramBuffer.bytesAvailable()) == 0) { + emit readyToRead(_incomingPacketData); + } +} diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h new file mode 100644 index 0000000000..b0aa187faa --- /dev/null +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -0,0 +1,51 @@ +// +// DatagramSequencer.h +// metavoxels +// +// Created by Andrzej Kapolka on 12/20/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__DatagramSequencer__ +#define __interface__DatagramSequencer__ + +#include +#include +#include +#include + +/// Performs simple datagram sequencing, packet fragmentation and reassembly. +class DatagramSequencer : public QObject { + Q_OBJECT + +public: + + DatagramSequencer(); + + void sendPacket(const QByteArray& packet); + + void receivedDatagram(const QByteArray& datagram); + +signals: + + /// Emitted when a datagram is ready to be transmitted. + void readyToWrite(const QByteArray& datagram); + + /// Emitted when a packet is available to read. + void readyToRead(const QByteArray& packet); + +private: + + QBuffer _datagramBuffer; + QDataStream _datagramStream; + + int _outgoingPacketNumber; + QByteArray _outgoingDatagram; + + int _incomingPacketNumber; + QByteArray _incomingPacketData; + QSet _offsetsReceived; + int _remainingBytes; +}; + +#endif /* defined(__interface__DatagramSequencer__) */ From 7ab1a4b849c9f46924950e53cb9f6a3d3daf95ce Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 20 Dec 2013 17:54:20 -0800 Subject: [PATCH 07/66] More work on datagrams. --- interface/src/Application.cpp | 2 + libraries/metavoxels/src/Bitstream.cpp | 45 ++++---- libraries/metavoxels/src/Bitstream.h | 3 + .../metavoxels/src/DatagramSequencer.cpp | 109 +++++++++++------- libraries/metavoxels/src/DatagramSequencer.h | 30 ++++- libraries/shared/src/PacketHeaders.h | 1 + 6 files changed, 129 insertions(+), 61 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 379fe823ff..b1eeaec636 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4375,6 +4375,8 @@ void* Application::networkReceive(void* args) { app->_voxelProcessor.queueReceivedPacket(senderSockAddr, app->_incomingPacket, bytesReceived); break; } + case PACKET_TYPE_METAVOXEL_DATA: + break; case PACKET_TYPE_BULK_AVATAR_DATA: NodeList::getInstance()->processBulkNodeData(senderSockAddr, app->_incomingPacket, diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 744177a5c5..737a829596 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -20,6 +20,25 @@ REGISTER_SIMPLE_TYPE_STREAMER(QString) REGISTER_SIMPLE_TYPE_STREAMER(bool) REGISTER_SIMPLE_TYPE_STREAMER(int) +IDStreamer::IDStreamer(Bitstream& stream) : + _stream(stream), + _bits(1) { +} + +IDStreamer& IDStreamer::operator<<(int value) { + _stream.write(&value, _bits); + if (value == (1 << _bits) - 1) { + _bits++; + } +} + +IDStreamer& IDStreamer::operator>>(int& value) { + _stream.read(&value, _bits); + if (value == (1 << _bits) - 1) { + _bits++; + } +} + int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { getMetaObjects().insert(className, metaObject); return 0; @@ -79,11 +98,15 @@ Bitstream& Bitstream::read(void* data, int bits, int offset) { void Bitstream::flush() { if (_position != 0) { _underlying << _byte; - _byte = 0; - _position = 0; + reset(); } } +void Bitstream::reset() { + _byte = 0; + _position = 0; +} + Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); @@ -222,21 +245,3 @@ QHash& Bitstream::getTypeStreamers() { return typeStreamers; } -IDStreamer::IDStreamer(Bitstream& stream) : - _stream(stream), - _bits(1) { -} - -IDStreamer& IDStreamer::operator<<(int value) { - _stream.write(&value, _bits); - if (value == (1 << _bits) - 1) { - _bits++; - } -} - -IDStreamer& IDStreamer::operator>>(int& value) { - _stream.read(&value, _bits); - if (value == (1 << _bits) - 1) { - _bits++; - } -} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 6f04f6a82c..ab159dba8f 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -115,6 +115,9 @@ public: /// Flushes any unwritten bits to the underlying stream. void flush(); + /// Resets to the initial state. + void reset(); + /// Returns a reference to the attribute streamer. RepeatedValueStreamer& getAttributeStreamer() { return _attributeStreamer; } diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 26be4f5f26..a70b14207e 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -13,10 +13,16 @@ const int MAX_DATAGRAM_SIZE = 1500; DatagramSequencer::DatagramSequencer() : + _outgoingPacketStream(&_outgoingPacketData, QIODevice::WriteOnly), + _outputStream(_outgoingPacketStream), _datagramStream(&_datagramBuffer), _outgoingPacketNumber(0), _outgoingDatagram(MAX_DATAGRAM_SIZE, 0), - _incomingPacketNumber(0) { + _lastAcknowledgedPacketNumber(0), + _incomingPacketNumber(0), + _incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly), + _inputStream(_incomingPacketStream), + _lastReceivedPacketNumber(0) { _datagramStream.setByteOrder(QDataStream::LittleEndian); } @@ -33,6 +39,66 @@ private: QIODevice* _device; }; +Bitstream& DatagramSequencer::startPacket() { + 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) { + _datagramBuffer.setBuffer(const_cast(&datagram)); + QIODeviceOpener opener(&_datagramBuffer, QIODevice::ReadOnly); + + // read the sequence number + quint32 sequenceNumber; + _datagramStream >> sequenceNumber; + + // if it's less than the last, ignore + if (sequenceNumber < _incomingPacketNumber) { + return; + } + + // read the acknowledgement, size and offset + quint32 acknowledgedPacketNumber, packetSize, offset; + _datagramStream >> acknowledgedPacketNumber >> packetSize >> offset; + + // if it's greater, reset + if (sequenceNumber > _incomingPacketNumber) { + _incomingPacketNumber = sequenceNumber; + _incomingPacketData.resize(packetSize); + _offsetsReceived.clear(); + _offsetsReceived.insert(offset); + _remainingBytes = packetSize; + + // handle new acknowledgement, if any + if (_lastAcknowledgedPacketNumber != acknowledgedPacketNumber) { + packetAcknowledged(_lastAcknowledgedPacketNumber = acknowledgedPacketNumber); + } + } else { + // make sure it's not a duplicate + if (_offsetsReceived.contains(offset)) { + return; + } + _offsetsReceived.insert(offset); + } + + // copy in the data + memcpy(_incomingPacketData.data() + offset, datagram.constData() + _datagramBuffer.pos(), _datagramBuffer.bytesAvailable()); + + // see if we're done + if ((_remainingBytes -= _datagramBuffer.bytesAvailable()) == 0) { + _lastReceivedPacketNumber = _incomingPacketNumber; + + emit readyToRead(_inputStream); + _incomingPacketStream.device()->seek(0); + _inputStream.reset(); + } +} + void DatagramSequencer::sendPacket(const QByteArray& packet) { _datagramBuffer.setBuffer(&_outgoingDatagram); QIODeviceOpener opener(&_datagramBuffer, QIODevice::WriteOnly); @@ -40,8 +106,9 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { // increment the packet number _outgoingPacketNumber++; - // write the sequence number and size, which are the same between all fragments + // write the sequence number, acknowledgement, and size, which are the same between all fragments _datagramStream << (quint32)_outgoingPacketNumber; + _datagramStream << (quint32)_lastReceivedPacketNumber; _datagramStream << (quint32)packet.size(); int initialPosition = _datagramBuffer.pos(); @@ -61,42 +128,6 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { } while(offset < packet.size()); } -void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { - _datagramBuffer.setBuffer(const_cast(&datagram)); - QIODeviceOpener opener(&_datagramBuffer, QIODevice::ReadOnly); - - // read the sequence number - quint32 sequenceNumber; - _datagramStream >> sequenceNumber; +void DatagramSequencer::packetAcknowledged(int packetNumber) { - // if it's less than the last, ignore - if (sequenceNumber < _incomingPacketNumber) { - return; - } - - // read the size and offset - quint32 packetSize, offset; - _datagramStream >> packetSize >> offset; - - // if it's greater, reset - if (sequenceNumber > _incomingPacketNumber) { - _incomingPacketNumber = sequenceNumber; - _incomingPacketData.resize(packetSize); - _offsetsReceived.clear(); - _remainingBytes = packetSize; - } - - // make sure it's not a duplicate - if (_offsetsReceived.contains(offset)) { - return; - } - _offsetsReceived.insert(offset); - - // copy in the data - memcpy(_incomingPacketData.data() + offset, datagram.constData() + _datagramBuffer.pos(), _datagramBuffer.bytesAvailable()); - - // see if we're done - if ((_remainingBytes -= _datagramBuffer.bytesAvailable()) == 0) { - emit readyToRead(_incomingPacketData); - } } diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index b0aa187faa..cf702f60eb 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -14,6 +14,8 @@ #include #include +#include "Bitstream.h" + /// Performs simple datagram sequencing, packet fragmentation and reassembly. class DatagramSequencer : public QObject { Q_OBJECT @@ -22,8 +24,15 @@ public: DatagramSequencer(); - void sendPacket(const QByteArray& packet); + /// Starts a new packet for transmission. + /// \return a reference to the Bitstream to use for writing to the packet + Bitstream& startPacket(); + /// Sends the packet currently being written. + void endPacket(); + + /// Processes a datagram received from the other party, emitting readyToRead when the entire packet + /// has been successfully assembled. void receivedDatagram(const QByteArray& datagram); signals: @@ -32,20 +41,37 @@ signals: void readyToWrite(const QByteArray& datagram); /// Emitted when a packet is available to read. - void readyToRead(const QByteArray& packet); + void readyToRead(Bitstream& input); private: + /// Sends a packet to the other party, fragmenting it into multiple datagrams (and emitting + /// readyToWrite) as necessary. + void sendPacket(const QByteArray& packet); + + /// Handles the acknowledgement of a sent packet. + void packetAcknowledged(int packetNumber); + + QByteArray _outgoingPacketData; + QDataStream _outgoingPacketStream; + Bitstream _outputStream; + QBuffer _datagramBuffer; QDataStream _datagramStream; int _outgoingPacketNumber; QByteArray _outgoingDatagram; + int _lastAcknowledgedPacketNumber; + int _incomingPacketNumber; QByteArray _incomingPacketData; + QDataStream _incomingPacketStream; + Bitstream _inputStream; QSet _offsetsReceived; int _remainingBytes; + + int _lastReceivedPacketNumber; }; #endif /* defined(__interface__DatagramSequencer__) */ diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h index a59ae5e5e5..d1a2b501f6 100644 --- a/libraries/shared/src/PacketHeaders.h +++ b/libraries/shared/src/PacketHeaders.h @@ -51,6 +51,7 @@ 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'; typedef char PACKET_VERSION; From 71ce244a0ac39cd447b377f8251188af587ba8d7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 23 Dec 2013 13:15:14 -0800 Subject: [PATCH 08/66] More work on datagram sequencing. --- .../metavoxels/src/DatagramSequencer.cpp | 64 +++++++++++++------ libraries/metavoxels/src/DatagramSequencer.h | 16 ++++- 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index a70b14207e..17511cf912 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -18,11 +18,9 @@ DatagramSequencer::DatagramSequencer() : _datagramStream(&_datagramBuffer), _outgoingPacketNumber(0), _outgoingDatagram(MAX_DATAGRAM_SIZE, 0), - _lastAcknowledgedPacketNumber(0), _incomingPacketNumber(0), _incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly), - _inputStream(_incomingPacketStream), - _lastReceivedPacketNumber(0) { + _inputStream(_incomingPacketStream) { _datagramStream.setByteOrder(QDataStream::LittleEndian); } @@ -40,6 +38,11 @@ private: }; Bitstream& DatagramSequencer::startPacket() { + // start with the list of acknowledgements + _outgoingPacketStream << (quint32)_receivedPacketNumbers.size(); + foreach (int packetNumber, _receivedPacketNumbers) { + _outgoingPacketStream << (quint32)packetNumber; + } return _outputStream; } @@ -62,9 +65,9 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { return; } - // read the acknowledgement, size and offset - quint32 acknowledgedPacketNumber, packetSize, offset; - _datagramStream >> acknowledgedPacketNumber >> packetSize >> offset; + // read the size and offset + quint32 packetSize, offset; + _datagramStream >> packetSize >> offset; // if it's greater, reset if (sequenceNumber > _incomingPacketNumber) { @@ -73,11 +76,7 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { _offsetsReceived.clear(); _offsetsReceived.insert(offset); _remainingBytes = packetSize; - - // handle new acknowledgement, if any - if (_lastAcknowledgedPacketNumber != acknowledgedPacketNumber) { - packetAcknowledged(_lastAcknowledgedPacketNumber = acknowledgedPacketNumber); - } + } else { // make sure it's not a duplicate if (_offsetsReceived.contains(offset)) { @@ -90,13 +89,35 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { memcpy(_incomingPacketData.data() + offset, datagram.constData() + _datagramBuffer.pos(), _datagramBuffer.bytesAvailable()); // see if we're done - if ((_remainingBytes -= _datagramBuffer.bytesAvailable()) == 0) { - _lastReceivedPacketNumber = _incomingPacketNumber; - - emit readyToRead(_inputStream); - _incomingPacketStream.device()->seek(0); - _inputStream.reset(); - } + if ((_remainingBytes -= _datagramBuffer.bytesAvailable()) > 0) { + return; + } + _receivedPacketNumbers.append(_incomingPacketNumber); + + // read the list of acknowledged packets + quint32 acknowledgementCount; + _incomingPacketStream >> acknowledgementCount; + for (int i = 0; i < acknowledgementCount; i++) { + quint32 packetNumber; + _incomingPacketStream >> packetNumber; + for (QList::iterator it = _sendRecords.begin(); it != _sendRecords.end(); ) { + if (it->packetNumber == packetNumber) { + sendRecordAcknowledged(*it); + it = _sendRecords.erase(_sendRecords.begin(), ++it); + } else { + it++; + } + } + } + + emit readyToRead(_inputStream); + _incomingPacketStream.device()->seek(0); + _inputStream.reset(); +} + +void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { + // stop acknowledging the recorded packets + } void DatagramSequencer::sendPacket(const QByteArray& packet) { @@ -106,9 +127,12 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { // increment the packet number _outgoingPacketNumber++; - // write the sequence number, acknowledgement, and size, which are the same between all fragments + // record the send + SendRecord record = { _outgoingPacketNumber, _receivedPacketNumbers }; + _sendRecords.append(record); + + // write the sequence number and size, which are the same between all fragments _datagramStream << (quint32)_outgoingPacketNumber; - _datagramStream << (quint32)_lastReceivedPacketNumber; _datagramStream << (quint32)packet.size(); int initialPosition = _datagramBuffer.pos(); diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index cf702f60eb..3301ccc459 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "Bitstream.h" @@ -45,6 +46,15 @@ signals: private: + class SendRecord { + public: + int packetNumber; + QList acknowledgedPacketNumbers; + }; + + /// Notes that the described send was acknowledged by the other party. + void sendRecordAcknowledged(const SendRecord& record); + /// Sends a packet to the other party, fragmenting it into multiple datagrams (and emitting /// readyToWrite) as necessary. void sendPacket(const QByteArray& packet); @@ -52,6 +62,8 @@ private: /// Handles the acknowledgement of a sent packet. void packetAcknowledged(int packetNumber); + QList _sendRecords; + QByteArray _outgoingPacketData; QDataStream _outgoingPacketStream; Bitstream _outputStream; @@ -62,8 +74,6 @@ private: int _outgoingPacketNumber; QByteArray _outgoingDatagram; - int _lastAcknowledgedPacketNumber; - int _incomingPacketNumber; QByteArray _incomingPacketData; QDataStream _incomingPacketStream; @@ -71,7 +81,7 @@ private: QSet _offsetsReceived; int _remainingBytes; - int _lastReceivedPacketNumber; + QList _receivedPacketNumbers; }; #endif /* defined(__interface__DatagramSequencer__) */ From 51d5a3761314ace1e448e9c000350e7c1f61879c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 24 Dec 2013 18:19:35 -0800 Subject: [PATCH 09/66] More work on datagram streaming. --- libraries/metavoxels/src/Bitstream.cpp | 6 +++ libraries/metavoxels/src/Bitstream.h | 40 +++++++++++++++---- .../metavoxels/src/DatagramSequencer.cpp | 27 ++++++++----- libraries/metavoxels/src/DatagramSequencer.h | 3 +- 4 files changed, 59 insertions(+), 17 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 737a829596..a4494e458c 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -107,6 +107,12 @@ void Bitstream::reset() { _position = 0; } +Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { + WriteMappings mappings = { _classNameStreamer.getAndResetTransientOffsets(), + _attributeStreamer.getAndResetTransientOffsets() }; + return mappings; +} + Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index ab159dba8f..66a91b64b5 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -47,7 +47,10 @@ private: template class RepeatedValueStreamer { public: - RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream), _lastNewID(0) { } + RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream), + _lastPersistentID(0), _lastTransientOffset(0) { } + + QHash getAndResetTransientOffsets(); RepeatedValueStreamer& operator<<(T value); RepeatedValueStreamer& operator>>(T& value); @@ -56,17 +59,31 @@ private: Bitstream& _stream; IDStreamer _idStreamer; - int _lastNewID; - QHash _ids; + int _lastPersistentID; + int _lastTransientOffset; + QHash _persistentIDs; + QHash _transientOffsets; QHash _values; }; +template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { + QHash transientOffsets; + _transientOffsets.swap(transientOffsets); + _lastTransientOffset = 0; + return transientOffsets; +} + template inline RepeatedValueStreamer& RepeatedValueStreamer::operator<<(T value) { - int& id = _ids[value]; + int id = _persistentIDs.value(value); if (id == 0) { - _idStreamer << (id = ++_lastNewID); - _stream << value; - + int& offset = _transientOffsets[value]; + if (offset == 0) { + _idStreamer << (_lastPersistentID + (offset = ++_lastTransientOffset)); + _stream << value; + + } else { + _idStreamer << (_lastPersistentID + offset); + } } else { _idStreamer << id; } @@ -91,6 +108,12 @@ template inline RepeatedValueStreamer& RepeatedValueStreamer::ope class Bitstream { public: + class WriteMappings { + public: + QHash classNameOffsets; + QHash attributeOffsets; + }; + /// Registers a metaobject under its name so that instances of it can be streamed. /// \return zero; the function only returns a value so that it can be used in static initialization static int registerMetaObject(const char* className, const QMetaObject* metaObject); @@ -121,6 +144,9 @@ public: /// Returns a reference to the attribute streamer. RepeatedValueStreamer& getAttributeStreamer() { return _attributeStreamer; } + /// Returns the set of transient mappings gathered during writing and resets them. + WriteMappings getAndResetWriteMappings(); + Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 17511cf912..9f7e381ec6 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -100,14 +100,16 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { for (int i = 0; i < acknowledgementCount; i++) { quint32 packetNumber; _incomingPacketStream >> packetNumber; - for (QList::iterator it = _sendRecords.begin(); it != _sendRecords.end(); ) { - if (it->packetNumber == packetNumber) { - sendRecordAcknowledged(*it); - it = _sendRecords.erase(_sendRecords.begin(), ++it); - } else { - it++; - } + if (_sendRecords.isEmpty()) { + continue; } + int index = packetNumber - _sendRecords.first().packetNumber; + if (index >= _sendRecords.size()) { + continue; + } + QList::iterator it = _sendRecords.begin() + index; + sendRecordAcknowledged(*it); + _sendRecords.erase(_sendRecords.begin(), it + 1); } emit readyToRead(_inputStream); @@ -116,7 +118,13 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { } void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { - // stop acknowledging the recorded packets + // stop acknowledging the recorded packets (TODO: replace with interpolation search?) + QList::iterator it = qBinaryFind(_receivedPacketNumbers.begin(), _receivedPacketNumbers.end(), + record.lastReceivedPacketNumber); + if (it != _receivedPacketNumbers.end()) { + _receivedPacketNumbers.erase(it + 1); + } + } @@ -128,7 +136,8 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { _outgoingPacketNumber++; // record the send - SendRecord record = { _outgoingPacketNumber, _receivedPacketNumbers }; + SendRecord record = { _outgoingPacketNumber, _receivedPacketNumbers.isEmpty() ? 0 : _receivedPacketNumbers.last(), + _outputStream.getAndResetWriteMappings() }; _sendRecords.append(record); // write the sequence number and size, which are the same between all fragments diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 3301ccc459..99e5a085c4 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -49,7 +49,8 @@ private: class SendRecord { public: int packetNumber; - QList acknowledgedPacketNumbers; + int lastReceivedPacketNumber; + Bitstream::WriteMappings mappings; }; /// Notes that the described send was acknowledged by the other party. From 6357bd00197fb1d89335391acb428044def78850 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 26 Dec 2013 00:27:47 -0800 Subject: [PATCH 10/66] More work on datagram sequencing. --- libraries/metavoxels/src/Bitstream.cpp | 77 ++++++++++++++----- libraries/metavoxels/src/Bitstream.h | 77 +++++++++++++++++-- .../metavoxels/src/DatagramSequencer.cpp | 7 +- libraries/metavoxels/src/DatagramSequencer.h | 7 ++ 4 files changed, 138 insertions(+), 30 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index a4494e458c..70f03cb604 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -25,6 +25,13 @@ IDStreamer::IDStreamer(Bitstream& stream) : _bits(1) { } +void IDStreamer::setBitsFromValue(int value) { + _bits = 1; + while (value >= (1 << _bits) - 1) { + _bits++; + } +} + IDStreamer& IDStreamer::operator<<(int value) { _stream.write(&value, _bits); if (value == (1 << _bits) - 1) { @@ -53,7 +60,7 @@ Bitstream::Bitstream(QDataStream& underlying) : _underlying(underlying), _byte(0), _position(0), - _classNameStreamer(*this), + _metaObjectStreamer(*this), _attributeStreamer(*this) { } @@ -108,11 +115,27 @@ void Bitstream::reset() { } Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { - WriteMappings mappings = { _classNameStreamer.getAndResetTransientOffsets(), + WriteMappings mappings = { _metaObjectStreamer.getAndResetTransientOffsets(), _attributeStreamer.getAndResetTransientOffsets() }; return mappings; } +void Bitstream::persistWriteMappings(const WriteMappings& mappings) { + _metaObjectStreamer.persistTransientOffsets(mappings.metaObjectOffsets); + _attributeStreamer.persistTransientOffsets(mappings.attributeOffsets); +} + +Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { + ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(), + _attributeStreamer.getAndResetTransientValues() }; + return mappings; +} + +void Bitstream::persistReadMappings(const ReadMappings& mappings) { + _metaObjectStreamer.persistTransientValues(mappings.metaObjectValues); + _attributeStreamer.persistTransientValues(mappings.attributeValues); +} + Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); @@ -186,11 +209,11 @@ Bitstream& Bitstream::operator>>(QVariant& value) { Bitstream& Bitstream::operator<<(QObject* object) { if (!object) { - _classNameStreamer << QByteArray(); + _metaObjectStreamer << NULL; return *this; } const QMetaObject* metaObject = object->metaObject(); - _classNameStreamer << QByteArray::fromRawData(metaObject->className(), strlen(metaObject->className())); + _metaObjectStreamer << metaObject; for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (!property.isStored(object)) { @@ -205,26 +228,40 @@ Bitstream& Bitstream::operator<<(QObject* object) { } Bitstream& Bitstream::operator>>(QObject*& object) { - QByteArray className; - _classNameStreamer >> className; - if (className.isEmpty()) { + const QMetaObject* metaObject; + _metaObjectStreamer >> metaObject; + if (!metaObject) { object = NULL; return *this; } - const QMetaObject* metaObject = getMetaObjects().value(className); - if (metaObject) { - object = metaObject->newInstance(); - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored(object)) { - continue; - } - TypeStreamer* streamer = getTypeStreamers().value(property.userType()); - if (streamer) { - property.write(object, streamer->read(*this)); - } + object = metaObject->newInstance(); + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored(object)) { + continue; } - } else { + TypeStreamer* streamer = getTypeStreamers().value(property.userType()); + if (streamer) { + property.write(object, streamer->read(*this)); + } + } + return *this; +} + +Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) { + return *this << (metaObject ? QByteArray::fromRawData( + metaObject->className(), strlen(metaObject->className())) : QByteArray()); +} + +Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) { + QByteArray className; + *this >> className; + if (className.isEmpty()) { + metaObject = NULL; + return *this; + } + metaObject = getMetaObjects().value(className); + if (!metaObject) { qDebug() << "Unknown class name: " << className << "\n"; } return *this; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 66a91b64b5..48c5e297ce 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -33,6 +33,8 @@ public: IDStreamer(Bitstream& stream); + void setBitsFromValue(int value); + IDStreamer& operator<<(int value); IDStreamer& operator>>(int& value); @@ -52,6 +54,12 @@ public: QHash getAndResetTransientOffsets(); + void persistTransientOffsets(const QHash& transientOffsets); + + QHash getAndResetTransientValues(); + + void persistTransientValues(const QHash& transientValues); + RepeatedValueStreamer& operator<<(T value); RepeatedValueStreamer& operator>>(T& value); @@ -63,16 +71,45 @@ private: int _lastTransientOffset; QHash _persistentIDs; QHash _transientOffsets; - QHash _values; + QHash _persistentValues; + QHash _transientValues; }; template inline QHash RepeatedValueStreamer::getAndResetTransientOffsets() { QHash transientOffsets; _transientOffsets.swap(transientOffsets); _lastTransientOffset = 0; + _idStreamer.setBitsFromValue(_lastPersistentID); return transientOffsets; } +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); + } + _idStreamer.setBitsFromValue(_lastPersistentID); +} + +template inline QHash RepeatedValueStreamer::getAndResetTransientValues() { + QHash transientValues; + _transientValues.swap(transientValues); + _idStreamer.setBitsFromValue(_lastPersistentID); + return transientValues; +} + +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()); + } + _idStreamer.setBitsFromValue(_lastPersistentID); +} + template inline RepeatedValueStreamer& RepeatedValueStreamer::operator<<(T value) { int id = _persistentIDs.value(value); if (id == 0) { @@ -93,13 +130,19 @@ template inline RepeatedValueStreamer& RepeatedValueStreamer::ope template inline RepeatedValueStreamer& RepeatedValueStreamer::operator>>(T& value) { int id; _idStreamer >> id; - typename QHash::iterator it = _values.find(id); - if (it == _values.end()) { - _stream >> value; - _values.insert(id, value); + if (id <= _lastPersistentID) { + value = _persistentValues.value(id); } else { - value = *it; + int offset = id - _lastPersistentID; + typename QHash::iterator it = _transientValues.find(offset); + if (it == _transientValues.end()) { + _stream >> value; + _transientValues.insert(offset, value); + + } else { + value = *it; + } } return *this; } @@ -110,10 +153,16 @@ public: class WriteMappings { public: - QHash classNameOffsets; + QHash metaObjectOffsets; QHash attributeOffsets; }; + class ReadMappings { + public: + QHash metaObjectValues; + QHash attributeValues; + }; + /// Registers a metaobject under its name so that instances of it can be streamed. /// \return zero; the function only returns a value so that it can be used in static initialization static int registerMetaObject(const char* className, const QMetaObject* metaObject); @@ -147,6 +196,15 @@ public: /// Returns the set of transient mappings gathered during writing and resets them. WriteMappings getAndResetWriteMappings(); + /// Persists a set of write mappings recorded earlier. + void persistWriteMappings(const WriteMappings& mappings); + + /// Returns the set of transient mappings gathered during reading and resets them. + ReadMappings getAndResetReadMappings(); + + /// Persists a set of read mappings recorded earlier. + void persistReadMappings(const ReadMappings& mappings); + Bitstream& operator<<(bool value); Bitstream& operator>>(bool& value); @@ -165,6 +223,9 @@ public: Bitstream& operator<<(QObject* object); Bitstream& operator>>(QObject*& object); + Bitstream& operator<<(const QMetaObject* metaObject); + Bitstream& operator>>(const QMetaObject*& metaObject); + Bitstream& operator<<(const AttributePointer& attribute); Bitstream& operator>>(AttributePointer& attribute); @@ -174,7 +235,7 @@ private: quint8 _byte; int _position; - RepeatedValueStreamer _classNameStreamer; + RepeatedValueStreamer _metaObjectStreamer; RepeatedValueStreamer _attributeStreamer; static QHash& getMetaObjects(); diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 9f7e381ec6..8e622aaf4f 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -115,6 +115,10 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { emit readyToRead(_inputStream); _incomingPacketStream.device()->seek(0); _inputStream.reset(); + + // record the receipt + ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings() }; + _receiveRecords.append(record); } void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { @@ -124,8 +128,7 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { if (it != _receivedPacketNumbers.end()) { _receivedPacketNumbers.erase(it + 1); } - - + _outputStream.persistWriteMappings(record.mappings); } void DatagramSequencer::sendPacket(const QByteArray& packet) { diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 99e5a085c4..26dcd5e62d 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -53,6 +53,12 @@ private: Bitstream::WriteMappings mappings; }; + class ReceiveRecord { + public: + int packetNumber; + Bitstream::ReadMappings mappings; + }; + /// Notes that the described send was acknowledged by the other party. void sendRecordAcknowledged(const SendRecord& record); @@ -64,6 +70,7 @@ private: void packetAcknowledged(int packetNumber); QList _sendRecords; + QList _receiveRecords; QByteArray _outgoingPacketData; QDataStream _outgoingPacketStream; From b986d9121767490f03db64b84b580a92255346cf Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 26 Dec 2013 18:26:29 -0800 Subject: [PATCH 11/66] One more piece of the datagram sequencing puzzle. --- .../metavoxels/src/DatagramSequencer.cpp | 23 ++++++++----------- libraries/metavoxels/src/DatagramSequencer.h | 7 ++---- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 8e622aaf4f..ee6e92d697 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -39,9 +39,9 @@ private: Bitstream& DatagramSequencer::startPacket() { // start with the list of acknowledgements - _outgoingPacketStream << (quint32)_receivedPacketNumbers.size(); - foreach (int packetNumber, _receivedPacketNumbers) { - _outgoingPacketStream << (quint32)packetNumber; + _outgoingPacketStream << (quint32)_receiveRecords.size(); + foreach (const ReceiveRecord& record, _receiveRecords) { + _outgoingPacketStream << (quint32)record.packetNumber; } return _outputStream; } @@ -92,8 +92,7 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { if ((_remainingBytes -= _datagramBuffer.bytesAvailable()) > 0) { return; } - _receivedPacketNumbers.append(_incomingPacketNumber); - + // read the list of acknowledged packets quint32 acknowledgementCount; _incomingPacketStream >> acknowledgementCount; @@ -123,10 +122,11 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { // stop acknowledging the recorded packets (TODO: replace with interpolation search?) - QList::iterator it = qBinaryFind(_receivedPacketNumbers.begin(), _receivedPacketNumbers.end(), - record.lastReceivedPacketNumber); - if (it != _receivedPacketNumbers.end()) { - _receivedPacketNumbers.erase(it + 1); + ReceiveRecord compare = { record.lastReceivedPacketNumber }; + QList::iterator it = qBinaryFind(_receiveRecords.begin(), _receiveRecords.end(), compare); + if (it != _receiveRecords.end()) { + _inputStream.persistReadMappings(it->mappings); + _receiveRecords.erase(it + 1); } _outputStream.persistWriteMappings(record.mappings); } @@ -139,7 +139,7 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { _outgoingPacketNumber++; // record the send - SendRecord record = { _outgoingPacketNumber, _receivedPacketNumbers.isEmpty() ? 0 : _receivedPacketNumbers.last(), + SendRecord record = { _outgoingPacketNumber, _receiveRecords.isEmpty() ? 0 : _receiveRecords.last().packetNumber, _outputStream.getAndResetWriteMappings() }; _sendRecords.append(record); @@ -164,6 +164,3 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { } while(offset < packet.size()); } -void DatagramSequencer::packetAcknowledged(int packetNumber) { - -} diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 26dcd5e62d..869c0deebb 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -57,6 +57,8 @@ private: public: int packetNumber; Bitstream::ReadMappings mappings; + + bool operator<(const ReceiveRecord& other) const { return packetNumber < other.packetNumber; } }; /// Notes that the described send was acknowledged by the other party. @@ -66,9 +68,6 @@ private: /// readyToWrite) as necessary. void sendPacket(const QByteArray& packet); - /// Handles the acknowledgement of a sent packet. - void packetAcknowledged(int packetNumber); - QList _sendRecords; QList _receiveRecords; @@ -88,8 +87,6 @@ private: Bitstream _inputStream; QSet _offsetsReceived; int _remainingBytes; - - QList _receivedPacketNumbers; }; #endif /* defined(__interface__DatagramSequencer__) */ From 97e7340ba6ee33e23db315e8a491602889c1a1da Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sat, 28 Dec 2013 18:27:37 -0800 Subject: [PATCH 12/66] Working on server sessions. --- .../src/metavoxels/MetavoxelServer.cpp | 33 +++++++++++++++- .../src/metavoxels/MetavoxelServer.h | 11 +++++- assignment-client/src/metavoxels/Session.cpp | 29 ++++++++++++++ assignment-client/src/metavoxels/Session.h | 39 +++++++++++++++++++ libraries/shared/src/NodeList.cpp | 3 +- 5 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 assignment-client/src/metavoxels/Session.cpp create mode 100644 assignment-client/src/metavoxels/Session.h diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index e18903197f..6945fa8024 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -9,8 +9,10 @@ #include #include +#include #include "MetavoxelServer.h" +#include "Session.h" MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) : ThreadedAssignment(dataBuffer, numBytes) { @@ -37,5 +39,34 @@ void MetavoxelServer::run() { } void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { - NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*)dataByteArray.data(), dataByteArray.size()); + switch (dataByteArray.at(0)) { + case PACKET_TYPE_METAVOXEL_DATA: + processData(dataByteArray, senderSockAddr); + break; + + default: + NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*)dataByteArray.data(), dataByteArray.size()); + break; + } +} + +void MetavoxelServer::processData(const QByteArray& data, const HifiSockAddr& sender) { + // get the header size + int headerSize = numBytesForPacketHeader((unsigned char*)data.constData()); + + // read the session id + const int UUID_BYTES = 16; + if (data.size() < headerSize + UUID_BYTES) { + qWarning() << "Metavoxel data too short [size=" << data.size() << ", sender=" << sender << "]\n"; + return; + } + QUuid sessionId = QUuid::fromRfc4122(QByteArray::fromRawData(data.constData() + headerSize, UUID_BYTES)); + + // forward to session, creating if necessary + Session*& session = _sessions[sessionId]; + if (!session) { + session = new Session(this); + } + session->receivedData(QByteArray::fromRawData(data.constData() + headerSize + UUID_BYTES, + data.size() - headerSize - UUID_BYTES), sender); } diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 85ca05d0c9..f249357c8e 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -2,17 +2,22 @@ // MetavoxelServer.h // hifi // -// Created by Andrzej Kapolka on 12/28/2013. +// Created by Andrzej Kapolka on 12/18/13. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // #ifndef __hifi__MetavoxelServer__ #define __hifi__MetavoxelServer__ +#include +#include + #include #include +class Session; + /// Maintains a shared metavoxel system, accepting change requests and broadcasting updates. class MetavoxelServer : public ThreadedAssignment { Q_OBJECT @@ -27,7 +32,11 @@ public: private: + void processData(const QByteArray& data, const HifiSockAddr& sender); + MetavoxelData _data; + + QHash _sessions; }; #endif /* defined(__hifi__MetavoxelServer__) */ diff --git a/assignment-client/src/metavoxels/Session.cpp b/assignment-client/src/metavoxels/Session.cpp new file mode 100644 index 0000000000..b6a0a7a6eb --- /dev/null +++ b/assignment-client/src/metavoxels/Session.cpp @@ -0,0 +1,29 @@ +// +// Session.cpp +// hifi +// +// Created by Andrzej Kapolka on 12/28/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include "MetavoxelServer.h" +#include "Session.h" + +Session::Session(MetavoxelServer* server) : + QObject(server), + _server(server) { + + connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); + connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); +} + +void Session::receivedData(const QByteArray& data, const HifiSockAddr& sender) { + // process through sequencer + _sequencer.receivedDatagram(data); +} + +void Session::sendData(const QByteArray& data) { +} + +void Session::readPacket(Bitstream& in) { +} diff --git a/assignment-client/src/metavoxels/Session.h b/assignment-client/src/metavoxels/Session.h new file mode 100644 index 0000000000..8585ae518d --- /dev/null +++ b/assignment-client/src/metavoxels/Session.h @@ -0,0 +1,39 @@ +// +// Session.h +// hifi +// +// Created by Andrzej Kapolka on 12/28/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__Session__ +#define __hifi__Session__ + +#include + +class HifiSockAddr; +class MetavoxelServer; + +/// Contains the state of a single client session. +class Session : public QObject { + Q_OBJECT + +public: + + Session(MetavoxelServer* server); + + void receivedData(const QByteArray& data, const HifiSockAddr& sender); + +private slots: + + void sendData(const QByteArray& data); + + void readPacket(Bitstream& in); + +private: + + MetavoxelServer* _server; + DatagramSequencer _sequencer; +}; + +#endif /* defined(__hifi__Session__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index cded2f7d4e..79c2cdce26 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -703,7 +703,8 @@ Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, node->lock(); if (node->getType() == NODE_TYPE_AUDIO_MIXER || - node->getType() == NODE_TYPE_VOXEL_SERVER) { + node->getType() == NODE_TYPE_VOXEL_SERVER || + node->getType() == NODE_TYPE_METAVOXEL_SERVER) { // 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 node->setLastHeardMicrostamp(usecTimestampNow()); From f70670d0a98434f61a1652c391a5b2ecb65c3cd7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 30 Dec 2013 13:39:08 -0800 Subject: [PATCH 13/66] Working on sessions. --- .../src/metavoxels/MetavoxelServer.cpp | 23 ++-- .../src/metavoxels/MetavoxelServer.h | 4 +- assignment-client/src/metavoxels/Session.cpp | 23 +++- assignment-client/src/metavoxels/Session.h | 15 ++- interface/src/Application.cpp | 2 + interface/src/Application.h | 2 +- .../src/{renderer => }/MetavoxelSystem.cpp | 80 +++++++++++++ interface/src/MetavoxelSystem.h | 107 ++++++++++++++++++ interface/src/renderer/MetavoxelSystem.h | 61 ---------- .../metavoxels/src/DatagramSequencer.cpp | 7 +- libraries/metavoxels/src/DatagramSequencer.h | 3 +- libraries/metavoxels/src/MetavoxelUtil.cpp | 29 +++++ libraries/metavoxels/src/MetavoxelUtil.h | 23 ++++ 13 files changed, 298 insertions(+), 81 deletions(-) rename interface/src/{renderer => }/MetavoxelSystem.cpp (59%) create mode 100644 interface/src/MetavoxelSystem.h delete mode 100644 interface/src/renderer/MetavoxelSystem.h create mode 100644 libraries/metavoxels/src/MetavoxelUtil.cpp create mode 100644 libraries/metavoxels/src/MetavoxelUtil.h diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 6945fa8024..bc7c6af709 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include "MetavoxelServer.h" #include "Session.h" @@ -18,6 +20,10 @@ MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) ThreadedAssignment(dataBuffer, numBytes) { } +void MetavoxelServer::removeSession(const QUuid& sessionId) { + delete _sessions.take(sessionId); +} + void MetavoxelServer::run() { // change the logging target name while the metavoxel server is running Logging::setTargetName("metavoxel-server"); @@ -51,22 +57,17 @@ void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const Hif } void MetavoxelServer::processData(const QByteArray& data, const HifiSockAddr& sender) { - // get the header size - int headerSize = numBytesForPacketHeader((unsigned char*)data.constData()); - // read the session id - const int UUID_BYTES = 16; - if (data.size() < headerSize + UUID_BYTES) { - qWarning() << "Metavoxel data too short [size=" << data.size() << ", sender=" << sender << "]\n"; + int headerPlusIDSize; + QUuid sessionID = readSessionID(data, sender, headerPlusIDSize); + if (sessionID.isNull()) { return; } - QUuid sessionId = QUuid::fromRfc4122(QByteArray::fromRawData(data.constData() + headerSize, UUID_BYTES)); // forward to session, creating if necessary - Session*& session = _sessions[sessionId]; + Session*& session = _sessions[sessionID]; if (!session) { - session = new Session(this); + session = new Session(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize)); } - session->receivedData(QByteArray::fromRawData(data.constData() + headerSize + UUID_BYTES, - data.size() - headerSize - UUID_BYTES), sender); + session->receivedData(data, sender); } diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index f249357c8e..854b46dcba 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -25,7 +25,9 @@ class MetavoxelServer : public ThreadedAssignment { public: MetavoxelServer(const unsigned char* dataBuffer, int numBytes); - + + void removeSession(const QUuid& sessionId); + virtual void run(); virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); diff --git a/assignment-client/src/metavoxels/Session.cpp b/assignment-client/src/metavoxels/Session.cpp index b6a0a7a6eb..c367341ed9 100644 --- a/assignment-client/src/metavoxels/Session.cpp +++ b/assignment-client/src/metavoxels/Session.cpp @@ -9,20 +9,39 @@ #include "MetavoxelServer.h" #include "Session.h" -Session::Session(MetavoxelServer* server) : +Session::Session(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader) : QObject(server), - _server(server) { + _server(server), + _sessionId(sessionId), + _sequencer(datagramHeader) { + + const int TIMEOUT_INTERVAL = 30 * 1000; + _timeoutTimer.setInterval(TIMEOUT_INTERVAL); + _timeoutTimer.setSingleShot(true); + connect(&_timeoutTimer, SIGNAL(timeout()), SLOT(timedOut())); connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); } void Session::receivedData(const QByteArray& data, const HifiSockAddr& sender) { + // reset the timeout timer + _timeoutTimer.start(); + + // save the most recent sender + _sender = sender; + // process through sequencer _sequencer.receivedDatagram(data); } +void Session::timedOut() { + qDebug() << "Session timed out [sessionId=" << _sessionId << ", sender=" << _sender << "]\n"; + _server->removeSession(_sessionId); +} + void Session::sendData(const QByteArray& data) { + NodeList::getInstance()->getNodeSocket().writeDatagram(data, _sender.getAddress(), _sender.getPort()); } void Session::readPacket(Bitstream& in) { diff --git a/assignment-client/src/metavoxels/Session.h b/assignment-client/src/metavoxels/Session.h index 8585ae518d..bcacf1eed5 100644 --- a/assignment-client/src/metavoxels/Session.h +++ b/assignment-client/src/metavoxels/Session.h @@ -9,9 +9,13 @@ #ifndef __hifi__Session__ #define __hifi__Session__ +#include +#include + +#include + #include -class HifiSockAddr; class MetavoxelServer; /// Contains the state of a single client session. @@ -20,12 +24,14 @@ class Session : public QObject { public: - Session(MetavoxelServer* server); + Session(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader); void receivedData(const QByteArray& data, const HifiSockAddr& sender); private slots: + void timedOut(); + void sendData(const QByteArray& data); void readPacket(Bitstream& in); @@ -33,7 +39,12 @@ private slots: private: MetavoxelServer* _server; + QUuid _sessionId; + + QTimer _timeoutTimer; DatagramSequencer _sequencer; + + HifiSockAddr _sender; }; #endif /* defined(__hifi__Session__) */ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b1eeaec636..ca0300c7da 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4376,6 +4376,8 @@ void* Application::networkReceive(void* args) { break; } case PACKET_TYPE_METAVOXEL_DATA: + app->_metavoxels.processData(QByteArray((const char*)app->_incomingPacket, bytesReceived), + senderSockAddr); break; case PACKET_TYPE_BULK_AVATAR_DATA: NodeList::getInstance()->processBulkNodeData(senderSockAddr, diff --git a/interface/src/Application.h b/interface/src/Application.h index e917f6d63d..c616bca7c4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -37,6 +37,7 @@ #include "Cloud.h" #include "Environment.h" #include "GLCanvas.h" +#include "MetavoxelSystem.h" #include "PacketHeaders.h" #include "PieMenu.h" #include "Stars.h" @@ -59,7 +60,6 @@ #include "renderer/AmbientOcclusionEffect.h" #include "renderer/GeometryCache.h" #include "renderer/GlowEffect.h" -#include "renderer/MetavoxelSystem.h" #include "renderer/PointShader.h" #include "renderer/TextureCache.h" #include "renderer/VoxelShader.h" diff --git a/interface/src/renderer/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp similarity index 59% rename from interface/src/renderer/MetavoxelSystem.cpp rename to interface/src/MetavoxelSystem.cpp index 543a8b6301..c63416dc47 100644 --- a/interface/src/renderer/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -12,6 +12,8 @@ #include +#include + #include "Application.h" #include "MetavoxelSystem.h" @@ -23,6 +25,10 @@ MetavoxelSystem::MetavoxelSystem() : _buffer(QOpenGLBuffer::VertexBuffer) { } +MetavoxelSystem::~MetavoxelSystem() { + NodeList::getInstance()->removeHook(this); +} + void MetavoxelSystem::init() { if (!_program.isLinked()) { switchToResourcesParentIfRequired(); @@ -32,6 +38,8 @@ void MetavoxelSystem::init() { _pointScaleLocation = _program.uniformLocation("pointScale"); } + NodeList::getInstance()->addHook(this); + AttributeRegistry::getInstance()->configureScriptEngine(&_scriptEngine); QFile scriptFile("resources/scripts/sphere.js"); @@ -44,6 +52,10 @@ void MetavoxelSystem::init() { _buffer.create(); } +void MetavoxelSystem::processData(const QByteArray& data, const HifiSockAddr& sender) { + QMetaObject::invokeMethod(this, "receivedData", Q_ARG(const QByteArray&, data), Q_ARG(const HifiSockAddr&, sender)); +} + void MetavoxelSystem::simulate(float deltaTime) { _points.clear(); _data.guide(_pointVisitor); @@ -99,6 +111,43 @@ void MetavoxelSystem::render() { _program.release(); } +void MetavoxelSystem::nodeAdded(Node* node) { + if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) { + QMetaObject::invokeMethod(this, "addClient", Q_ARG(const QUuid&, node->getUUID()), + Q_ARG(const HifiSockAddr&, node->getPublicSocket())); + } +} + +void MetavoxelSystem::nodeKilled(Node* node) { + if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) { + QMetaObject::invokeMethod(this, "removeClient", Q_ARG(const QUuid&, node->getUUID())); + } +} + +void MetavoxelSystem::addClient(const QUuid& uuid, const HifiSockAddr& address) { + MetavoxelClient* client = new MetavoxelClient(address); + _clients.insert(uuid, client); + _clientsBySessionID.insert(client->getSessionID(), client); +} + +void MetavoxelSystem::removeClient(const QUuid& uuid) { + MetavoxelClient* client = _clients.take(uuid); + _clientsBySessionID.remove(client->getSessionID()); + delete client; +} + +void MetavoxelSystem::receivedData(const QByteArray& data, const HifiSockAddr& sender) { + int headerPlusIDSize; + QUuid sessionID = readSessionID(data, sender, headerPlusIDSize); + if (sessionID.isNull()) { + return; + } + MetavoxelClient* client = _clientsBySessionID.value(sessionID); + if (client) { + client->receivedData(data, sender); + } +} + MetavoxelSystem::PointVisitor::PointVisitor(QVector& points) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getColorAttribute() << @@ -120,3 +169,34 @@ bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) { } return false; } + +static QByteArray createDatagramHeader(const QUuid& sessionID) { + QByteArray header(MAX_PACKET_HEADER_BYTES, 0); + populateTypeAndVersion(reinterpret_cast(header.data()), PACKET_TYPE_METAVOXEL_DATA); + header += sessionID.toRfc4122(); + return header; +} + +MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : + _address(address), + _sessionID(QUuid::createUuid()), + _sequencer(createDatagramHeader(_sessionID)) { + + connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); + connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); +} + +void MetavoxelClient::receivedData(const QByteArray& data, const HifiSockAddr& sender) { + // save the most recent sender + _address = sender; + + // process through sequencer + _sequencer.receivedDatagram(data); +} + +void MetavoxelClient::sendData(const QByteArray& data) { + 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 new file mode 100644 index 0000000000..13b07991ca --- /dev/null +++ b/interface/src/MetavoxelSystem.h @@ -0,0 +1,107 @@ +// +// MetavoxelSystem.h +// interface +// +// Created by Andrzej Kapolka on 12/10/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelSystem__ +#define __interface__MetavoxelSystem__ + +#include +#include +#include + +#include + +#include + +#include +#include + +#include "renderer/ProgramObject.h" + +class MetavoxelClient; + +/// Renders a metavoxel tree. +class MetavoxelSystem : public QObject, public NodeListHook { + Q_OBJECT + +public: + + MetavoxelSystem(); + ~MetavoxelSystem(); + + void init(); + + void processData(const QByteArray& data, const HifiSockAddr& sender); + + void simulate(float deltaTime); + void render(); + + virtual void nodeAdded(Node* node); + virtual void nodeKilled(Node* node); + +private: + + Q_INVOKABLE void addClient(const QUuid& uuid, const HifiSockAddr& address); + Q_INVOKABLE void removeClient(const QUuid& uuid); + Q_INVOKABLE void receivedData(const QByteArray& data, const HifiSockAddr& sender); + + class Point { + public: + glm::vec4 vertex; + quint8 color[4]; + quint8 normal[3]; + }; + + class PointVisitor : public MetavoxelVisitor { + public: + PointVisitor(QVector& points); + virtual bool visit(const MetavoxelInfo& info); + + private: + QVector& _points; + }; + + static ProgramObject _program; + static int _pointScaleLocation; + + QScriptEngine _scriptEngine; + MetavoxelData _data; + QVector _points; + PointVisitor _pointVisitor; + QOpenGLBuffer _buffer; + + QHash _clients; + QHash _clientsBySessionID; +}; + +/// A client session associated with a single server. +class MetavoxelClient : public QObject { + Q_OBJECT + +public: + + MetavoxelClient(const HifiSockAddr& address); + + const QUuid& getSessionID() const { return _sessionID; } + + void receivedData(const QByteArray& data, const HifiSockAddr& sender); + +private slots: + + void sendData(const QByteArray& data); + + void readPacket(Bitstream& in); + +private: + + HifiSockAddr _address; + QUuid _sessionID; + + DatagramSequencer _sequencer; +}; + +#endif /* defined(__interface__MetavoxelSystem__) */ diff --git a/interface/src/renderer/MetavoxelSystem.h b/interface/src/renderer/MetavoxelSystem.h deleted file mode 100644 index b8617d99a4..0000000000 --- a/interface/src/renderer/MetavoxelSystem.h +++ /dev/null @@ -1,61 +0,0 @@ -// -// MetavoxelSystem.h -// interface -// -// Created by Andrzej Kapolka on 12/10/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__MetavoxelSystem__ -#define __interface__MetavoxelSystem__ - -#include -#include -#include - -#include - -#include - -#include "ProgramObject.h" - -/// Renders a metavoxel tree. -class MetavoxelSystem { -public: - - MetavoxelSystem(); - - void init(); - - void simulate(float deltaTime); - void render(); - -private: - - class Point { - public: - glm::vec4 vertex; - quint8 color[4]; - quint8 normal[3]; - }; - - class PointVisitor : public MetavoxelVisitor { - public: - PointVisitor(QVector& points); - virtual bool visit(const MetavoxelInfo& info); - - private: - QVector& _points; - }; - - static ProgramObject _program; - static int _pointScaleLocation; - - QScriptEngine _scriptEngine; - MetavoxelData _data; - QVector _points; - PointVisitor _pointVisitor; - QOpenGLBuffer _buffer; -}; - -#endif /* defined(__interface__MetavoxelSystem__) */ diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index ee6e92d697..06d716f718 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -12,10 +12,11 @@ const int MAX_DATAGRAM_SIZE = 1500; -DatagramSequencer::DatagramSequencer() : +DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : _outgoingPacketStream(&_outgoingPacketData, QIODevice::WriteOnly), _outputStream(_outgoingPacketStream), _datagramStream(&_datagramBuffer), + _datagramHeaderSize(datagramHeader.size()), _outgoingPacketNumber(0), _outgoingDatagram(MAX_DATAGRAM_SIZE, 0), _incomingPacketNumber(0), @@ -23,6 +24,7 @@ DatagramSequencer::DatagramSequencer() : _inputStream(_incomingPacketStream) { _datagramStream.setByteOrder(QDataStream::LittleEndian); + memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize); } /// Simple RAII-style object to keep a device open when in scope. @@ -53,7 +55,7 @@ void DatagramSequencer::endPacket() { } void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { - _datagramBuffer.setBuffer(const_cast(&datagram)); + _datagramBuffer.setData(datagram.constData() + _datagramHeaderSize, datagram.size() - _datagramHeaderSize); QIODeviceOpener opener(&_datagramBuffer, QIODevice::ReadOnly); // read the sequence number @@ -144,6 +146,7 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { _sendRecords.append(record); // write the sequence number and size, which are the same between all fragments + _datagramBuffer.seek(_datagramHeaderSize); _datagramStream << (quint32)_outgoingPacketNumber; _datagramStream << (quint32)packet.size(); int initialPosition = _datagramBuffer.pos(); diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 869c0deebb..0a1f6c06ea 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -23,7 +23,7 @@ class DatagramSequencer : public QObject { public: - DatagramSequencer(); + DatagramSequencer(const QByteArray& datagramHeader = QByteArray()); /// Starts a new packet for transmission. /// \return a reference to the Bitstream to use for writing to the packet @@ -77,6 +77,7 @@ private: QBuffer _datagramBuffer; QDataStream _datagramStream; + int _datagramHeaderSize; int _outgoingPacketNumber; QByteArray _outgoingDatagram; diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp new file mode 100644 index 0000000000..29b8c57168 --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -0,0 +1,29 @@ +// +// MetavoxelUtil.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 12/30/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include + +#include +#include + +#include "MetavoxelUtil.h" + +QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& headerPlusIDSize) { + // get the header size + int headerSize = numBytesForPacketHeader(reinterpret_cast(data.constData())); + + // read the session id + const int UUID_BYTES = 16; + headerPlusIDSize = headerSize + UUID_BYTES; + if (data.size() < headerPlusIDSize) { + qWarning() << "Metavoxel data too short [size=" << data.size() << ", sender=" << sender << "]\n"; + return QUuid(); + } + return QUuid::fromRfc4122(QByteArray::fromRawData(data.constData() + headerSize, UUID_BYTES)); +} diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h new file mode 100644 index 0000000000..f7e0edbadf --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -0,0 +1,23 @@ +// +// MetavoxelUtil.h +// metavoxels +// +// Created by Andrzej Kapolka on 12/30/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelUtil__ +#define __interface__MetavoxelUtil__ + +#include + +class QByteArray; + +class HifiSockAddr; + +/// Reads and returns the session ID from a datagram. +/// \param[out] headerPlusIDSize the size of the header (including the session ID) within the data +/// \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); + +#endif /* defined(__interface__MetavoxelUtil__) */ From ecd9c9f41d472feca946746d7b15d447b307c370 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 30 Dec 2013 18:28:30 -0800 Subject: [PATCH 14/66] Communication between client and server. --- assignment-client/src/audio/AudioMixer.cpp | 21 +----- assignment-client/src/avatars/AvatarMixer.cpp | 17 +---- .../src/metavoxels/MetavoxelServer.cpp | 68 ++++++++++++------- .../src/metavoxels/MetavoxelServer.h | 36 +++++++++- assignment-client/src/metavoxels/Session.cpp | 48 ------------- assignment-client/src/metavoxels/Session.h | 50 -------------- interface/src/MetavoxelSystem.cpp | 13 +++- interface/src/MetavoxelSystem.h | 2 + .../audio/src/PositionalAudioRingBuffer.cpp | 2 +- .../metavoxels/src/DatagramSequencer.cpp | 54 ++++++++------- libraries/metavoxels/src/DatagramSequencer.h | 6 +- libraries/shared/src/HifiSockAddr.cpp | 2 + libraries/shared/src/ThreadedAssignment.cpp | 23 +++++++ libraries/shared/src/ThreadedAssignment.h | 1 + 14 files changed, 159 insertions(+), 184 deletions(-) delete mode 100644 assignment-client/src/metavoxels/Session.cpp delete mode 100644 assignment-client/src/metavoxels/Session.h diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index d8e0580cc9..de1b0d43fb 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -239,30 +239,15 @@ void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSock void AudioMixer::run() { + init(AUDIO_MIXER_LOGGING_TARGET_NAME, NODE_TYPE_AUDIO_MIXER); + NodeList* nodeList = NodeList::getInstance(); - // change the logging target name while this is running - Logging::setTargetName(AUDIO_MIXER_LOGGING_TARGET_NAME); - - nodeList->setOwnerType(NODE_TYPE_AUDIO_MIXER); - const char AUDIO_MIXER_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_AGENT, NODE_TYPE_AUDIO_INJECTOR }; nodeList->setNodeTypesOfInterest(AUDIO_MIXER_NODE_TYPES_OF_INTEREST, sizeof(AUDIO_MIXER_NODE_TYPES_OF_INTEREST)); nodeList->linkedDataCreateCallback = attachNewBufferToNode; - QTimer* domainServerTimer = new QTimer(this); - connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); - domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - - QTimer* pingNodesTimer = new QTimer(this); - connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); - pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - int nextFrame = 0; timeval startTime; @@ -314,4 +299,4 @@ void AudioMixer::run() { } } -} \ No newline at end of file +} diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 0f72ae9680..b46c781c27 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -162,28 +162,13 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc } void AvatarMixer::run() { - // change the logging target name while AvatarMixer is running - Logging::setTargetName(AVATAR_MIXER_LOGGING_NAME); + init(AVATAR_MIXER_LOGGING_NAME, NODE_TYPE_AVATAR_MIXER); NodeList* nodeList = NodeList::getInstance(); - nodeList->setOwnerType(NODE_TYPE_AVATAR_MIXER); - nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1); nodeList->linkedDataCreateCallback = attachAvatarDataToNode; - QTimer* domainServerTimer = new QTimer(this); - connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); - domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - - QTimer* pingNodesTimer = new QTimer(this); - connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); - pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - int nextFrame = 0; timeval startTime; diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index bc7c6af709..d2916b0a71 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -6,15 +6,11 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -#include - -#include #include #include #include "MetavoxelServer.h" -#include "Session.h" MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) : ThreadedAssignment(dataBuffer, numBytes) { @@ -25,23 +21,7 @@ void MetavoxelServer::removeSession(const QUuid& sessionId) { } void MetavoxelServer::run() { - // change the logging target name while the metavoxel server is running - Logging::setTargetName("metavoxel-server"); - - NodeList* nodeList = NodeList::getInstance(); - nodeList->setOwnerType(NODE_TYPE_METAVOXEL_SERVER); - - QTimer* domainServerTimer = new QTimer(this); - connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); - domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - - QTimer* pingNodesTimer = new QTimer(this); - connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); - pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); + init("metavoxel-server", NODE_TYPE_METAVOXEL_SERVER); } void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { @@ -65,9 +45,51 @@ void MetavoxelServer::processData(const QByteArray& data, const HifiSockAddr& se } // forward to session, creating if necessary - Session*& session = _sessions[sessionID]; + MetavoxelSession*& session = _sessions[sessionID]; if (!session) { - session = new Session(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize)); + session = new MetavoxelSession(this, sessionID, QByteArray::fromRawData(data.constData(), headerPlusIDSize)); } session->receivedData(data, sender); } + +MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader) : + QObject(server), + _server(server), + _sessionId(sessionId), + _sequencer(datagramHeader) { + + const int TIMEOUT_INTERVAL = 30 * 1000; + _timeoutTimer.setInterval(TIMEOUT_INTERVAL); + _timeoutTimer.setSingleShot(true); + connect(&_timeoutTimer, SIGNAL(timeout()), SLOT(timedOut())); + + connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); + connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); +} + +void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr& sender) { + // reset the timeout timer + _timeoutTimer.start(); + + // save the most recent sender + _sender = sender; + + // process through sequencer + _sequencer.receivedDatagram(data); +} + +void MetavoxelSession::timedOut() { + qDebug() << "Session timed out [sessionId=" << _sessionId << ", sender=" << _sender << "]\n"; + _server->removeSession(_sessionId); +} + +void MetavoxelSession::sendData(const QByteArray& data) { + NodeList::getInstance()->getNodeSocket().writeDatagram(data, _sender.getAddress(), _sender.getPort()); +} + +void MetavoxelSession::readPacket(Bitstream& in) { + qDebug("got packet from client!\n"); + + Bitstream& out = _sequencer.startPacket(); + _sequencer.endPacket(); +} diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 854b46dcba..33735d4a45 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -10,13 +10,16 @@ #define __hifi__MetavoxelServer__ #include +#include #include +#include #include +#include #include -class Session; +class MetavoxelSession; /// Maintains a shared metavoxel system, accepting change requests and broadcasting updates. class MetavoxelServer : public ThreadedAssignment { @@ -38,7 +41,36 @@ private: MetavoxelData _data; - QHash _sessions; + QHash _sessions; +}; + +/// Contains the state of a single client session. +class MetavoxelSession : public QObject { + Q_OBJECT + +public: + + MetavoxelSession(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader); + + void receivedData(const QByteArray& data, const HifiSockAddr& sender); + +private slots: + + void timedOut(); + + void sendData(const QByteArray& data); + + void readPacket(Bitstream& in); + +private: + + MetavoxelServer* _server; + QUuid _sessionId; + + QTimer _timeoutTimer; + DatagramSequencer _sequencer; + + HifiSockAddr _sender; }; #endif /* defined(__hifi__MetavoxelServer__) */ diff --git a/assignment-client/src/metavoxels/Session.cpp b/assignment-client/src/metavoxels/Session.cpp deleted file mode 100644 index c367341ed9..0000000000 --- a/assignment-client/src/metavoxels/Session.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Session.cpp -// hifi -// -// Created by Andrzej Kapolka on 12/28/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#include "MetavoxelServer.h" -#include "Session.h" - -Session::Session(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader) : - QObject(server), - _server(server), - _sessionId(sessionId), - _sequencer(datagramHeader) { - - const int TIMEOUT_INTERVAL = 30 * 1000; - _timeoutTimer.setInterval(TIMEOUT_INTERVAL); - _timeoutTimer.setSingleShot(true); - connect(&_timeoutTimer, SIGNAL(timeout()), SLOT(timedOut())); - - connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); - connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); -} - -void Session::receivedData(const QByteArray& data, const HifiSockAddr& sender) { - // reset the timeout timer - _timeoutTimer.start(); - - // save the most recent sender - _sender = sender; - - // process through sequencer - _sequencer.receivedDatagram(data); -} - -void Session::timedOut() { - qDebug() << "Session timed out [sessionId=" << _sessionId << ", sender=" << _sender << "]\n"; - _server->removeSession(_sessionId); -} - -void Session::sendData(const QByteArray& data) { - NodeList::getInstance()->getNodeSocket().writeDatagram(data, _sender.getAddress(), _sender.getPort()); -} - -void Session::readPacket(Bitstream& in) { -} diff --git a/assignment-client/src/metavoxels/Session.h b/assignment-client/src/metavoxels/Session.h deleted file mode 100644 index bcacf1eed5..0000000000 --- a/assignment-client/src/metavoxels/Session.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// Session.h -// hifi -// -// Created by Andrzej Kapolka on 12/28/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __hifi__Session__ -#define __hifi__Session__ - -#include -#include - -#include - -#include - -class MetavoxelServer; - -/// Contains the state of a single client session. -class Session : public QObject { - Q_OBJECT - -public: - - Session(MetavoxelServer* server, const QUuid& sessionId, const QByteArray& datagramHeader); - - void receivedData(const QByteArray& data, const HifiSockAddr& sender); - -private slots: - - void timedOut(); - - void sendData(const QByteArray& data); - - void readPacket(Bitstream& in); - -private: - - MetavoxelServer* _server; - QUuid _sessionId; - - QTimer _timeoutTimer; - DatagramSequencer _sequencer; - - HifiSockAddr _sender; -}; - -#endif /* defined(__hifi__Session__) */ diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index c63416dc47..56d2b39056 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -57,6 +57,11 @@ 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); @@ -114,7 +119,7 @@ void MetavoxelSystem::render() { void MetavoxelSystem::nodeAdded(Node* node) { if (node->getType() == NODE_TYPE_METAVOXEL_SERVER) { QMetaObject::invokeMethod(this, "addClient", Q_ARG(const QUuid&, node->getUUID()), - Q_ARG(const HifiSockAddr&, node->getPublicSocket())); + Q_ARG(const HifiSockAddr&, node->getLocalSocket())); } } @@ -186,6 +191,11 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); } +void MetavoxelClient::simulate(float deltaTime) { + Bitstream& out = _sequencer.startPacket(); + _sequencer.endPacket(); +} + void MetavoxelClient::receivedData(const QByteArray& data, const HifiSockAddr& sender) { // save the most recent sender _address = sender; @@ -199,4 +209,5 @@ void MetavoxelClient::sendData(const QByteArray& data) { } void MetavoxelClient::readPacket(Bitstream& in) { + qDebug("got packet from server!\n"); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 13b07991ca..b84f30dc2c 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -88,6 +88,8 @@ public: const QUuid& getSessionID() const { return _sessionID; } + void simulate(float deltaTime); + void receivedData(const QByteArray& data, const HifiSockAddr& sender); private slots: diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 4be6b80265..dfda215262 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -57,7 +57,7 @@ int PositionalAudioRingBuffer::parsePositionalData(unsigned char* sourceBuffer, bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) { if (!isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL + numJitterBufferSamples)) { - qDebug() << "Starved and do not have minimum samples to start. Buffer held back.\n"; + //qDebug() << "Starved and do not have minimum samples to start. Buffer held back.\n"; return false; } else if (samplesAvailable() < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { qDebug() << "Do not have number of samples needed for interval. Buffer starved.\n"; diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 06d716f718..0694f2b370 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -8,6 +8,8 @@ #include +#include + #include "DatagramSequencer.h" const int MAX_DATAGRAM_SIZE = 1500; @@ -15,15 +17,21 @@ const int MAX_DATAGRAM_SIZE = 1500; DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) : _outgoingPacketStream(&_outgoingPacketData, QIODevice::WriteOnly), _outputStream(_outgoingPacketStream), - _datagramStream(&_datagramBuffer), + _incomingDatagramStream(&_incomingDatagramBuffer), _datagramHeaderSize(datagramHeader.size()), _outgoingPacketNumber(0), _outgoingDatagram(MAX_DATAGRAM_SIZE, 0), + _outgoingDatagramBuffer(&_outgoingDatagram), + _outgoingDatagramStream(&_outgoingDatagramBuffer), _incomingPacketNumber(0), _incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly), _inputStream(_incomingPacketStream) { - _datagramStream.setByteOrder(QDataStream::LittleEndian); + _outgoingPacketStream.setByteOrder(QDataStream::LittleEndian); + _incomingDatagramStream.setByteOrder(QDataStream::LittleEndian); + _incomingPacketStream.setByteOrder(QDataStream::LittleEndian); + _outgoingDatagramStream.setByteOrder(QDataStream::LittleEndian); + memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize); } @@ -55,12 +63,12 @@ void DatagramSequencer::endPacket() { } void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { - _datagramBuffer.setData(datagram.constData() + _datagramHeaderSize, datagram.size() - _datagramHeaderSize); - QIODeviceOpener opener(&_datagramBuffer, QIODevice::ReadOnly); - + _incomingDatagramBuffer.setData(datagram.constData() + _datagramHeaderSize, datagram.size() - _datagramHeaderSize); + QIODeviceOpener opener(&_incomingDatagramBuffer, QIODevice::ReadOnly); + // read the sequence number quint32 sequenceNumber; - _datagramStream >> sequenceNumber; + _incomingDatagramStream >> sequenceNumber; // if it's less than the last, ignore if (sequenceNumber < _incomingPacketNumber) { @@ -69,7 +77,7 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { // read the size and offset quint32 packetSize, offset; - _datagramStream >> packetSize >> offset; + _incomingDatagramStream >> packetSize >> offset; // if it's greater, reset if (sequenceNumber > _incomingPacketNumber) { @@ -88,13 +96,14 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { } // copy in the data - memcpy(_incomingPacketData.data() + offset, datagram.constData() + _datagramBuffer.pos(), _datagramBuffer.bytesAvailable()); + memcpy(_incomingPacketData.data() + offset, _incomingDatagramBuffer.data().constData() + _incomingDatagramBuffer.pos(), + _incomingDatagramBuffer.bytesAvailable()); // see if we're done - if ((_remainingBytes -= _datagramBuffer.bytesAvailable()) > 0) { + if ((_remainingBytes -= _incomingDatagramBuffer.bytesAvailable()) > 0) { return; } - + // read the list of acknowledged packets quint32 acknowledgementCount; _incomingPacketStream >> acknowledgementCount; @@ -105,7 +114,7 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { continue; } int index = packetNumber - _sendRecords.first().packetNumber; - if (index >= _sendRecords.size()) { + if (index < 0 || index >= _sendRecords.size()) { continue; } QList::iterator it = _sendRecords.begin() + index; @@ -128,14 +137,13 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { QList::iterator it = qBinaryFind(_receiveRecords.begin(), _receiveRecords.end(), compare); if (it != _receiveRecords.end()) { _inputStream.persistReadMappings(it->mappings); - _receiveRecords.erase(it + 1); + _receiveRecords.erase(_receiveRecords.begin(), it + 1); } _outputStream.persistWriteMappings(record.mappings); } void DatagramSequencer::sendPacket(const QByteArray& packet) { - _datagramBuffer.setBuffer(&_outgoingDatagram); - QIODeviceOpener opener(&_datagramBuffer, QIODevice::WriteOnly); + QIODeviceOpener opener(&_outgoingDatagramBuffer, QIODevice::WriteOnly); // increment the packet number _outgoingPacketNumber++; @@ -146,21 +154,21 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) { _sendRecords.append(record); // write the sequence number and size, which are the same between all fragments - _datagramBuffer.seek(_datagramHeaderSize); - _datagramStream << (quint32)_outgoingPacketNumber; - _datagramStream << (quint32)packet.size(); - int initialPosition = _datagramBuffer.pos(); + _outgoingDatagramBuffer.seek(_datagramHeaderSize); + _outgoingDatagramStream << (quint32)_outgoingPacketNumber; + _outgoingDatagramStream << (quint32)packet.size(); + int initialPosition = _outgoingDatagramBuffer.pos(); // break the packet into MTU-sized datagrams int offset = 0; do { - _datagramBuffer.seek(initialPosition); - _datagramStream << (quint32)offset; + _outgoingDatagramBuffer.seek(initialPosition); + _outgoingDatagramStream << (quint32)offset; - int payloadSize = qMin((int)(_outgoingDatagram.size() - _datagramBuffer.pos()), packet.size() - offset); - memcpy(_outgoingDatagram.data() + _datagramBuffer.pos(), packet.constData() + offset, payloadSize); + int payloadSize = qMin((int)(_outgoingDatagram.size() - _outgoingDatagramBuffer.pos()), packet.size() - offset); + memcpy(_outgoingDatagram.data() + _outgoingDatagramBuffer.pos(), packet.constData() + offset, payloadSize); - emit readyToWrite(QByteArray::fromRawData(_outgoingDatagram.constData(), _datagramBuffer.pos() + payloadSize)); + emit readyToWrite(QByteArray::fromRawData(_outgoingDatagram.constData(), _outgoingDatagramBuffer.pos() + payloadSize)); offset += payloadSize; diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 0a1f6c06ea..629aeabf39 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -75,12 +75,14 @@ private: QDataStream _outgoingPacketStream; Bitstream _outputStream; - QBuffer _datagramBuffer; - QDataStream _datagramStream; + QBuffer _incomingDatagramBuffer; + QDataStream _incomingDatagramStream; int _datagramHeaderSize; int _outgoingPacketNumber; QByteArray _outgoingDatagram; + QBuffer _outgoingDatagramBuffer; + QDataStream _outgoingDatagramStream; int _incomingPacketNumber; QByteArray _incomingPacketData; diff --git a/libraries/shared/src/HifiSockAddr.cpp b/libraries/shared/src/HifiSockAddr.cpp index 0a6f5dce2d..55c950c580 100644 --- a/libraries/shared/src/HifiSockAddr.cpp +++ b/libraries/shared/src/HifiSockAddr.cpp @@ -11,6 +11,8 @@ #include #include +static int hifiSockAddrMetaTypeId = qMetaTypeId(); + HifiSockAddr::HifiSockAddr() : _address(), _port(0) diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index 095999adca..303779e168 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -6,6 +6,9 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // +#include + +#include "Logging.h" #include "ThreadedAssignment.h" ThreadedAssignment::ThreadedAssignment(const unsigned char* dataBuffer, int numBytes) : @@ -23,6 +26,26 @@ void ThreadedAssignment::setFinished(bool isFinished) { } } +void ThreadedAssignment::init(const char* targetName, NODE_TYPE nodeType) { + // change the logging target name while the assignment is running + Logging::setTargetName(targetName); + + NodeList* nodeList = NodeList::getInstance(); + nodeList->setOwnerType(nodeType); + + QTimer* domainServerTimer = new QTimer(this); + connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); + domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); + + QTimer* pingNodesTimer = new QTimer(this); + connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); + pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); + + QTimer* silentNodeTimer = new QTimer(this); + connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); +} + void ThreadedAssignment::checkInWithDomainServerOrExit() { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { setFinished(true); diff --git a/libraries/shared/src/ThreadedAssignment.h b/libraries/shared/src/ThreadedAssignment.h index 2d6e49b724..ad2e7c608c 100644 --- a/libraries/shared/src/ThreadedAssignment.h +++ b/libraries/shared/src/ThreadedAssignment.h @@ -23,6 +23,7 @@ public slots: virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) = 0; protected: + void init(const char* targetName, NODE_TYPE nodeType); bool _isFinished; private slots: void checkInWithDomainServerOrExit(); From f9043c3cb954dfbfce26a777a0264f4c54dcfbf0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 31 Dec 2013 11:34:10 -0800 Subject: [PATCH 15/66] Working on streaming types. --- libraries/metavoxels/src/Bitstream.cpp | 44 +++++++++++++++----- libraries/metavoxels/src/Bitstream.h | 29 ++++++++++++- libraries/metavoxels/src/MetavoxelMessages.h | 23 ++++++++++ 3 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 libraries/metavoxels/src/MetavoxelMessages.h diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 70f03cb604..8fdeb1c1fc 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -52,6 +52,7 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta } int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { + streamer->setType(type); getTypeStreamers().insert(type, streamer); return 0; } @@ -61,6 +62,7 @@ Bitstream::Bitstream(QDataStream& underlying) : _byte(0), _position(0), _metaObjectStreamer(*this), + _typeStreamerStreamer(*this), _attributeStreamer(*this) { } @@ -116,23 +118,27 @@ void Bitstream::reset() { Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { WriteMappings mappings = { _metaObjectStreamer.getAndResetTransientOffsets(), + _typeStreamerStreamer.getAndResetTransientOffsets(), _attributeStreamer.getAndResetTransientOffsets() }; return mappings; } void Bitstream::persistWriteMappings(const WriteMappings& mappings) { _metaObjectStreamer.persistTransientOffsets(mappings.metaObjectOffsets); + _typeStreamerStreamer.persistTransientOffsets(mappings.typeStreamerOffsets); _attributeStreamer.persistTransientOffsets(mappings.attributeOffsets); } Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(), + _typeStreamerStreamer.getAndResetTransientValues(), _attributeStreamer.getAndResetTransientValues() }; return mappings; } void Bitstream::persistReadMappings(const ReadMappings& mappings) { _metaObjectStreamer.persistTransientValues(mappings.metaObjectValues); + _typeStreamerStreamer.persistTransientValues(mappings.typeStreamerValues); _attributeStreamer.persistTransientValues(mappings.attributeValues); } @@ -189,25 +195,26 @@ Bitstream& Bitstream::operator>>(QString& string) { } Bitstream& Bitstream::operator<<(const QVariant& value) { - *this << value.userType(); - TypeStreamer* streamer = getTypeStreamers().value(value.userType()); + const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); if (streamer) { + _typeStreamerStreamer << streamer; streamer->write(*this, value); + } else { + qWarning() << "Non-streamable type: " << value.typeName() << "\n"; } return *this; } Bitstream& Bitstream::operator>>(QVariant& value) { - int type; - *this >> type; - TypeStreamer* streamer = getTypeStreamers().value(type); + const TypeStreamer* streamer; + _typeStreamerStreamer >> streamer; if (streamer) { value = streamer->read(*this); } return *this; } -Bitstream& Bitstream::operator<<(QObject* object) { +Bitstream& Bitstream::operator<<(const QObject* object) { if (!object) { _metaObjectStreamer << NULL; return *this; @@ -219,7 +226,7 @@ Bitstream& Bitstream::operator<<(QObject* object) { if (!property.isStored(object)) { continue; } - TypeStreamer* streamer = getTypeStreamers().value(property.userType()); + const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); if (streamer) { streamer->write(*this, property.read(object)); } @@ -240,7 +247,7 @@ Bitstream& Bitstream::operator>>(QObject*& object) { if (!property.isStored(object)) { continue; } - TypeStreamer* streamer = getTypeStreamers().value(property.userType()); + const TypeStreamer* streamer = getTypeStreamers().value(property.userType()); if (streamer) { property.write(object, streamer->read(*this)); } @@ -262,7 +269,22 @@ Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) { } metaObject = getMetaObjects().value(className); if (!metaObject) { - qDebug() << "Unknown class name: " << className << "\n"; + qWarning() << "Unknown class name: " << className << "\n"; + } + return *this; +} + +Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) { + const char* typeName = QMetaType::typeName(streamer->getType()); + return *this << QByteArray::fromRawData(typeName, strlen(typeName)); +} + +Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { + QByteArray typeName; + *this >> typeName; + streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + if (!streamer) { + qWarning() << "Unknown type name: " << typeName << "\n"; } return *this; } @@ -283,8 +305,8 @@ QHash& Bitstream::getMetaObjects() { return metaObjects; } -QHash& Bitstream::getTypeStreamers() { - static QHash typeStreamers; +QHash& Bitstream::getTypeStreamers() { + static QHash typeStreamers; return typeStreamers; } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 48c5e297ce..d86bad67cc 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -154,12 +154,14 @@ public: class WriteMappings { public: QHash metaObjectOffsets; + QHash typeStreamerOffsets; QHash attributeOffsets; }; class ReadMappings { public: QHash metaObjectValues; + QHash typeStreamerValues; QHash attributeValues; }; @@ -220,12 +222,15 @@ public: Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); - Bitstream& operator<<(QObject* object); + Bitstream& operator<<(const QObject* object); Bitstream& operator>>(QObject*& object); Bitstream& operator<<(const QMetaObject* metaObject); Bitstream& operator>>(const QMetaObject*& metaObject); + Bitstream& operator<<(const TypeStreamer* streamer); + Bitstream& operator>>(const TypeStreamer*& streamer); + Bitstream& operator<<(const AttributePointer& attribute); Bitstream& operator>>(AttributePointer& attribute); @@ -236,10 +241,11 @@ private: int _position; RepeatedValueStreamer _metaObjectStreamer; + RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; static QHash& getMetaObjects(); - static QHash& getTypeStreamers(); + static QHash& getTypeStreamers(); }; /// Macro for registering streamable meta-objects. @@ -249,8 +255,15 @@ private: class TypeStreamer { public: + void setType(int type) { _type = type; } + int getType() const { return _type; } + virtual void write(Bitstream& out, const QVariant& value) const = 0; virtual QVariant read(Bitstream& in) const = 0; + +private: + + int _type; }; /// A streamer that works with Bitstream's operators. @@ -265,4 +278,16 @@ public: #define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); +/// Declares the metatype and the streaming operators. +#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ + Bitstream& operator<<(Bitstream& out, const X& obj); \ + Bitstream& operator>>(Bitstream& in, X& obj); + +/// Registers a streamable type and its streamer. +template int registerStreamableMetaType() { + int type = qRegisterMetaType(); + Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer()); + return type; +} + #endif /* defined(__interface__Bitstream__) */ diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h new file mode 100644 index 0000000000..389d8b68bf --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -0,0 +1,23 @@ +// +// MetavoxelMessages.h +// metavoxels +// +// Created by Andrzej Kapolka on 12/31/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelMessages__ +#define __interface__MetavoxelMessages__ + +#include "Bitstream.h" + +/// A message containing the position of a client. +class ClientPositionMessage { +public: + + int test; +}; + +DECLARE_STREAMABLE_METATYPE(ClientPositionMessage) + +#endif /* defined(__interface__MetavoxelMessages__) */ From effd00a4056799db69d213bb2d515e14e90e7bc9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 31 Dec 2013 15:30:15 -0800 Subject: [PATCH 16/66] Basic "meta type compiler" for streaming, streaming fixes and test. --- .../src/metavoxels/MetavoxelServer.cpp | 5 +- cmake/macros/AutoMTC.cmake | 20 ++ interface/src/MetavoxelSystem.cpp | 3 + libraries/metavoxels/CMakeLists.txt | 3 + libraries/metavoxels/src/Bitstream.cpp | 8 +- libraries/metavoxels/src/Bitstream.h | 13 +- libraries/metavoxels/src/MetavoxelMessages.h | 4 +- tools/mtc/CMakeLists.txt | 11 ++ tools/mtc/src/main.cpp | 180 ++++++++++++++++++ 9 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 cmake/macros/AutoMTC.cmake create mode 100644 tools/mtc/CMakeLists.txt create mode 100644 tools/mtc/src/main.cpp diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index d2916b0a71..a39fefada2 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -8,6 +8,7 @@ #include +#include #include #include "MetavoxelServer.h" @@ -88,7 +89,9 @@ void MetavoxelSession::sendData(const QByteArray& data) { } void MetavoxelSession::readPacket(Bitstream& in) { - qDebug("got packet from client!\n"); + QVariant msg; + in >> msg; + qDebug() << msg.value().test << "\n"; Bitstream& out = _sequencer.startPacket(); _sequencer.endPacket(); diff --git a/cmake/macros/AutoMTC.cmake b/cmake/macros/AutoMTC.cmake new file mode 100644 index 0000000000..f22430a8d0 --- /dev/null +++ b/cmake/macros/AutoMTC.cmake @@ -0,0 +1,20 @@ +macro(AUTO_MTC TARGET ROOT_DIR) + if (NOT TARGET mtc) + add_subdirectory(${ROOT_DIR}/tools/mtc ${ROOT_DIR}/tools/mtc) + endif (NOT TARGET mtc) + + file(GLOB INCLUDE_FILES src/*.h) + + add_custom_command(OUTPUT ${TARGET}_automtc.cpp COMMAND mtc -o ${TARGET}_automtc.cpp ${INCLUDE_FILES} DEPENDS mtc) + + find_package(Qt5Core REQUIRED) + + add_library(${TARGET}_automtc STATIC ${TARGET}_automtc.cpp) + + qt5_use_modules(${TARGET}_automtc Core) + + target_link_libraries(${TARGET} ${TARGET}_automtc) + +endmacro() + + diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 56d2b39056..018c7005cf 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -12,6 +12,7 @@ #include +#include #include #include "Application.h" @@ -193,6 +194,8 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : void MetavoxelClient::simulate(float deltaTime) { Bitstream& out = _sequencer.startPacket(); + ClientPositionMessage msg = { 55 }; + out << QVariant::fromValue(msg); _sequencer.endPacket(); } diff --git a/libraries/metavoxels/CMakeLists.txt b/libraries/metavoxels/CMakeLists.txt index 0f9c1c695c..98b2baf7ac 100644 --- a/libraries/metavoxels/CMakeLists.txt +++ b/libraries/metavoxels/CMakeLists.txt @@ -13,6 +13,9 @@ find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiLibrary.cmake) setup_hifi_library(${TARGET_NAME}) +include(${MACRO_DIR}/AutoMTC.cmake) +auto_mtc(${TARGET_NAME} ${ROOT_DIR}) + qt5_use_modules(${TARGET_NAME} Widgets Script) include(${MACRO_DIR}/IncludeGLM.cmake) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 8fdeb1c1fc..bd991829a9 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -40,6 +40,7 @@ IDStreamer& IDStreamer::operator<<(int value) { } IDStreamer& IDStreamer::operator>>(int& value) { + value = 0; _stream.read(&value, _bits); if (value == (1 << _bits) - 1) { _bits++; @@ -93,7 +94,8 @@ Bitstream& Bitstream::read(void* data, int bits, int offset) { _underlying >> _byte; } int bitsToRead = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits)); - *dest |= ((_byte >> _position) & ((1 << bitsToRead) - 1)) << offset; + int mask = ((1 << bitsToRead) - 1) << offset; + *dest = (*dest & ~mask) | (((_byte >> _position) << offset) & mask); _position = (_position + bitsToRead) & LAST_BIT_POSITION; if ((offset += bitsToRead) == BITS_IN_BYTE) { dest++; @@ -166,7 +168,7 @@ Bitstream& Bitstream::operator<<(int value) { } Bitstream& Bitstream::operator>>(int& value) { - qint32 sizedValue; + qint32 sizedValue = 0; read(&sizedValue, 32); value = sizedValue; return *this; @@ -180,6 +182,7 @@ Bitstream& Bitstream::operator<<(const QByteArray& string) { Bitstream& Bitstream::operator>>(QByteArray& string) { int size; *this >> size; + string.resize(size); return read(string.data(), size * BITS_IN_BYTE); } @@ -191,6 +194,7 @@ Bitstream& Bitstream::operator<<(const QString& string) { Bitstream& Bitstream::operator>>(QString& string) { int size; *this >> size; + string.resize(size); return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE); } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index d86bad67cc..2e75c85a0a 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -13,6 +13,7 @@ #include #include #include +#include class QByteArray; class QDataStream; @@ -278,10 +279,12 @@ public: #define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); -/// Declares the metatype and the streaming operators. +/// Declares the metatype and the streaming operators. The last line +/// ensures that the generated file will be included in the link phase. #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ - Bitstream& operator>>(Bitstream& in, X& obj); + Bitstream& operator>>(Bitstream& in, X& obj); \ + static const int* _TypePtr##X = &X::Type; /// Registers a streamable type and its streamer. template int registerStreamableMetaType() { @@ -290,4 +293,10 @@ template int registerStreamableMetaType() { return type; } +/// Flags a class as streamable (use as you would Q_OBJECT). +#define STREAMABLE public: static const int Type; private: + +/// Flags a field or base class as streaming. +#define STREAM + #endif /* defined(__interface__Bitstream__) */ diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 389d8b68bf..6a199fe0f7 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -13,9 +13,11 @@ /// A message containing the position of a client. class ClientPositionMessage { + STREAMABLE + public: - int test; + STREAM int test; }; DECLARE_STREAMABLE_METATYPE(ClientPositionMessage) diff --git a/tools/mtc/CMakeLists.txt b/tools/mtc/CMakeLists.txt new file mode 100644 index 0000000000..95cb95d573 --- /dev/null +++ b/tools/mtc/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +set(TARGET_NAME mtc) + +set(ROOT_DIR ../..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +include(${MACRO_DIR}/SetupHifiProject.cmake) +setup_hifi_project(${TARGET_NAME} TRUE) + + diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp new file mode 100644 index 0000000000..77f0a069b5 --- /dev/null +++ b/tools/mtc/src/main.cpp @@ -0,0 +1,180 @@ +// +// main.cpp +// mtc +// +// Created by Andrzej Kapolka on 12/31/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class Class { +public: + QString name; + QStringList bases; +}; + +class Streamable { +public: + Class clazz; + QStringList fields; +}; + +void processInput(QTextStream& in, QList* streamables) { + Class clazz; + Streamable currentStreamable; + + QRegExp exp( + "(/\\*.*\\*/)|" // multi-line comments + "(//.*\n)|" // single-line comments + "(\\s*#.*\n)|" // preprocessor definitions + "(\\s*STREAMABLE\\s+)|" // STREAMABLE tag for classes + "(\\s*STREAM\\s+.*;)|" // STREAM tag for fields + "(\\s*class\\s+[^;]+\\{)" // class definition + ); + exp.setMinimal(true); + + QRegExp classExp("class (\\w+) ?:?([^:]*)\\{"); + + // read in the entire input and look for matches with our expression + QString all = in.readAll(); + for (int off = 0; (off = exp.indexIn(all, off)) != -1; off += exp.matchedLength()) { + QString match = exp.cap().simplified(); + if (match.startsWith("/*") || match.startsWith("//") || match.startsWith('#')) { + continue; // comment, preprocessor definition + } + if (match.startsWith("STREAMABLE")) { + if (clazz.name.isEmpty()) { + cerr << "Found STREAMABLE marker before class definition." << endl; + continue; + } + if (!currentStreamable.clazz.name.isEmpty()) { + streamables->append(currentStreamable); + } + currentStreamable.clazz = clazz; + currentStreamable.fields.clear(); + + } else if (match.startsWith("STREAM")) { + match.chop(1); // get rid of the semicolon + match = match.trimmed(); // and any space before it + currentStreamable.fields.append(match.mid(match.lastIndexOf(' ') + 1)); + + } else { // match.startsWith("class") + classExp.exactMatch(match); + clazz.name = classExp.cap(1); + clazz.bases.clear(); + foreach (const QString& bstr, classExp.cap(2).split(',')) { + QString base = bstr.trimmed(); + if (!base.isEmpty() && base.startsWith("STREAM")) { + clazz.bases.append(base.mid(base.lastIndexOf(' ') + 1)); + } + } + } + } + if (!currentStreamable.clazz.name.isEmpty()) { + streamables->append(currentStreamable); + } +} + +void generateOutput (QTextStream& out, const QList& streamables) { + foreach (const Streamable& str, streamables) { + const QString& name = str.clazz.name; + + out << "Bitstream& operator<< (Bitstream& out, const " << name << "& obj) {\n"; + foreach (const QString& base, str.clazz.bases) { + out << " out << static_cast(obj);\n"; + } + foreach (const QString& field, str.fields) { + out << " out << obj." << field << ";\n"; + } + out << " return out;\n"; + out << "}\n"; + + out << "Bitstream& operator>> (Bitstream& in, " << name << "& obj) {\n"; + foreach (const QString& base, str.clazz.bases) { + out << " in >> static_cast<" << base << "&>(obj);\n"; + } + foreach (const QString& field, str.fields) { + out << " in >> obj." << field << ";\n"; + } + out << " return in;\n"; + out << "}\n"; + + out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n\n"; + } +} + +int main (int argc, char** argv) { + // process the command line arguments + QStringList inputs; + QString output; + for (int ii = 1; ii < argc; ii++) { + QString arg(argv[ii]); + if (!arg.startsWith('-')) { + inputs.append(arg); + continue; + } + QStringRef name = arg.midRef(1); + if (name == "o") { + if (++ii == argc) { + cerr << "Missing file name argument for -o" << endl; + return 1; + } + output = argv[ii]; + + } else { + cerr << "Unknown option " << arg.toStdString() << endl; + return 1; + } + } + if (inputs.isEmpty()) { + cerr << "Usage: mtc [OPTION]... input files" << endl; + cerr << "Where options include:" << endl; + cerr << " -o filename: Send output to filename rather than standard output." << endl; + return 0; + } + + QList streamables; + foreach (const QString& input, inputs) { + QFile ifile(input); + if (!ifile.open(QIODevice::ReadOnly | QIODevice::Text)) { + cerr << ("Couldn't open " + input + ": " + ifile.errorString()).toStdString() << endl; + continue; + } + QTextStream istream(&ifile); + int oldSize = streamables.size(); + processInput(istream, &streamables); + if (streamables.size() == oldSize) { + // no streamables; remove from list + inputs.removeOne(input); + } + } + + QFile ofile(output); + if (output.isNull()) { + ofile.open(stdout, QIODevice::WriteOnly | QIODevice::Text); + + } else if (!ofile.open(QIODevice::WriteOnly | QIODevice::Text)) { + cerr << ("Couldn't open " + output + ": " + ofile.errorString()).toStdString() << endl; + return 1; + } + + QTextStream ostream(&ofile); + ostream << "// generated by mtc\n"; + foreach (const QString& input, inputs) { + ostream << "#include \"" << input << "\"\n"; + } + generateOutput(ostream, streamables); + + return 0; +} From a210d57c03b34b594e612d17abca9da8c0093340 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 31 Dec 2013 15:40:01 -0800 Subject: [PATCH 17/66] Add back this spammy log message that I disabled. --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index dfda215262..4be6b80265 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -57,7 +57,7 @@ int PositionalAudioRingBuffer::parsePositionalData(unsigned char* sourceBuffer, bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) { if (!isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL + numJitterBufferSamples)) { - //qDebug() << "Starved and do not have minimum samples to start. Buffer held back.\n"; + qDebug() << "Starved and do not have minimum samples to start. Buffer held back.\n"; return false; } else if (samplesAvailable() < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { qDebug() << "Do not have number of samples needed for interval. Buffer starved.\n"; From d7d8cdce445254ac6fdb597182dd7e601cf95915 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 31 Dec 2013 16:19:03 -0800 Subject: [PATCH 18/66] init -> commonInit, streaming tweaks. --- assignment-client/src/audio/AudioMixer.cpp | 2 +- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- .../src/metavoxels/MetavoxelServer.cpp | 5 +++-- cmake/macros/AutoMTC.cmake | 3 ++- interface/src/MetavoxelSystem.cpp | 4 ++-- libraries/metavoxels/src/Bitstream.cpp | 18 +++++++++++++++++- libraries/metavoxels/src/Bitstream.h | 8 ++++++++ libraries/metavoxels/src/MetavoxelMessages.h | 8 ++++---- libraries/shared/src/ThreadedAssignment.cpp | 2 +- libraries/shared/src/ThreadedAssignment.h | 2 +- 10 files changed, 40 insertions(+), 14 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index de1b0d43fb..e315d366f8 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -239,7 +239,7 @@ void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSock void AudioMixer::run() { - init(AUDIO_MIXER_LOGGING_TARGET_NAME, NODE_TYPE_AUDIO_MIXER); + commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NODE_TYPE_AUDIO_MIXER); NodeList* nodeList = NodeList::getInstance(); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b46c781c27..f6096fd18a 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -162,7 +162,7 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc } void AvatarMixer::run() { - init(AVATAR_MIXER_LOGGING_NAME, NODE_TYPE_AVATAR_MIXER); + commonInit(AVATAR_MIXER_LOGGING_NAME, NODE_TYPE_AVATAR_MIXER); NodeList* nodeList = NodeList::getInstance(); nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1); diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index a39fefada2..36b06d38cf 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -22,7 +22,7 @@ void MetavoxelServer::removeSession(const QUuid& sessionId) { } void MetavoxelServer::run() { - init("metavoxel-server", NODE_TYPE_METAVOXEL_SERVER); + commonInit("metavoxel-server", NODE_TYPE_METAVOXEL_SERVER); } void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { @@ -91,7 +91,8 @@ void MetavoxelSession::sendData(const QByteArray& data) { void MetavoxelSession::readPacket(Bitstream& in) { QVariant msg; in >> msg; - qDebug() << msg.value().test << "\n"; + glm::vec3 position = msg.value().position; + qDebug() << position.x << " " << position.y << " " << position.z << "\n"; Bitstream& out = _sequencer.startPacket(); _sequencer.endPacket(); diff --git a/cmake/macros/AutoMTC.cmake b/cmake/macros/AutoMTC.cmake index f22430a8d0..fca8170e64 100644 --- a/cmake/macros/AutoMTC.cmake +++ b/cmake/macros/AutoMTC.cmake @@ -5,7 +5,8 @@ macro(AUTO_MTC TARGET ROOT_DIR) file(GLOB INCLUDE_FILES src/*.h) - add_custom_command(OUTPUT ${TARGET}_automtc.cpp COMMAND mtc -o ${TARGET}_automtc.cpp ${INCLUDE_FILES} DEPENDS mtc) + add_custom_command(OUTPUT ${TARGET}_automtc.cpp COMMAND mtc -o ${TARGET}_automtc.cpp + ${INCLUDE_FILES} DEPENDS mtc ${INCLUDE_FILES}) find_package(Qt5Core REQUIRED) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 018c7005cf..61f4db6d95 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -194,8 +194,8 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : void MetavoxelClient::simulate(float deltaTime) { Bitstream& out = _sequencer.startPacket(); - ClientPositionMessage msg = { 55 }; - out << QVariant::fromValue(msg); + ClientStateMessage state = { glm::vec3(0.5f, 0.5f, 1.0f) }; + out << QVariant::fromValue(state); _sequencer.endPacket(); } diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index bd991829a9..56fdcde378 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -168,12 +168,28 @@ Bitstream& Bitstream::operator<<(int value) { } Bitstream& Bitstream::operator>>(int& value) { - qint32 sizedValue = 0; + qint32 sizedValue; read(&sizedValue, 32); value = sizedValue; return *this; } +Bitstream& Bitstream::operator<<(float value) { + return write(&value, 32); +} + +Bitstream& Bitstream::operator>>(float& value) { + return read(&value, 32); +} + +Bitstream& Bitstream::operator<<(const glm::vec3& value) { + return *this << value.x << value.y << value.z; +} + +Bitstream& Bitstream::operator>>(glm::vec3& value) { + return *this >> value.x >> value.y >> value.z; +} + Bitstream& Bitstream::operator<<(const QByteArray& string) { *this << string.size(); return write(string.constData(), string.size() * BITS_IN_BYTE); diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 2e75c85a0a..5fd31f5c9d 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -15,6 +15,8 @@ #include #include +#include + class QByteArray; class QDataStream; class QMetaObject; @@ -214,6 +216,12 @@ public: Bitstream& operator<<(int value); Bitstream& operator>>(int& value); + Bitstream& operator<<(float value); + Bitstream& operator>>(float& value); + + Bitstream& operator<<(const glm::vec3& value); + Bitstream& operator>>(glm::vec3& value); + Bitstream& operator<<(const QByteArray& string); Bitstream& operator>>(QByteArray& string); diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 6a199fe0f7..42b088729b 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -11,15 +11,15 @@ #include "Bitstream.h" -/// A message containing the position of a client. -class ClientPositionMessage { +/// A message containing the state of a client. +class ClientStateMessage { STREAMABLE public: - STREAM int test; + STREAM glm::vec3 position; }; -DECLARE_STREAMABLE_METATYPE(ClientPositionMessage) +DECLARE_STREAMABLE_METATYPE(ClientStateMessage) #endif /* defined(__interface__MetavoxelMessages__) */ diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index 303779e168..b16c180c9d 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -26,7 +26,7 @@ void ThreadedAssignment::setFinished(bool isFinished) { } } -void ThreadedAssignment::init(const char* targetName, NODE_TYPE nodeType) { +void ThreadedAssignment::commonInit(const char* targetName, NODE_TYPE 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 ad2e7c608c..0dfd8a320f 100644 --- a/libraries/shared/src/ThreadedAssignment.h +++ b/libraries/shared/src/ThreadedAssignment.h @@ -23,7 +23,7 @@ public slots: virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) = 0; protected: - void init(const char* targetName, NODE_TYPE nodeType); + void commonInit(const char* targetName, NODE_TYPE nodeType); bool _isFinished; private slots: void checkInWithDomainServerOrExit(); From a8bb890187da048754122b7d883ac1de8749e050 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 31 Dec 2013 16:27:54 -0800 Subject: [PATCH 19/66] Fixed a couple of Xcode warnings. --- libraries/metavoxels/src/Bitstream.cpp | 2 ++ libraries/metavoxels/src/Bitstream.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 56fdcde378..819373b733 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -37,6 +37,7 @@ IDStreamer& IDStreamer::operator<<(int value) { if (value == (1 << _bits) - 1) { _bits++; } + return *this; } IDStreamer& IDStreamer::operator>>(int& value) { @@ -45,6 +46,7 @@ IDStreamer& IDStreamer::operator>>(int& value) { if (value == (1 << _bits) - 1) { _bits++; } + return *this; } int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 5fd31f5c9d..b2958143b3 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -19,7 +19,7 @@ class QByteArray; class QDataStream; -class QMetaObject; +struct QMetaObject; class QObject; class Attribute; From 839e5e085c1f9c9062d6abb78e3a84d97e06ad07 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 31 Dec 2013 16:56:18 -0800 Subject: [PATCH 20/66] Attempting to silence unused variable warning on Xcode. --- libraries/metavoxels/src/Bitstream.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index b2958143b3..36bcafd80d 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -289,10 +289,12 @@ public: /// Declares the metatype and the streaming operators. The last line /// ensures that the generated file will be included in the link phase. +#define STRINGIFY(x) #x #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ Bitstream& operator>>(Bitstream& in, X& obj); \ - static const int* _TypePtr##X = &X::Type; + static const int* _TypePtr##X = &X::Type; \ + _Pragma(STRINGIFY(unused(_TypePtr##X))) /// Registers a streamable type and its streamer. template int registerStreamableMetaType() { From 72b002ddc462761ade30efa963ac98859743aad8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 31 Dec 2013 17:30:41 -0800 Subject: [PATCH 21/66] Allow streaming QVariantLists. --- libraries/metavoxels/src/Bitstream.cpp | 21 +++++++++++++++++++++ libraries/metavoxels/src/Bitstream.h | 7 +++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 819373b733..258ef1ca29 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -236,6 +236,27 @@ Bitstream& Bitstream::operator>>(QVariant& value) { return *this; } +Bitstream& Bitstream::operator<<(const QVariantList& value) { + *this << value.size(); + foreach (const QVariant& entry, value) { + *this << entry; + } + return *this; +} + +Bitstream& Bitstream::operator>>(QVariantList& value) { + int size; + *this >> size; + value.clear(); + value.reserve(size); + for (int i = 0; i < size; i++) { + QVariant entry; + *this >> entry; + value.append(entry); + } + 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 36bcafd80d..066da71b34 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -231,6 +231,9 @@ public: Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); + Bitstream& operator<<(const QVariantList& value); + Bitstream& operator>>(QVariantList& value); + Bitstream& operator<<(const QObject* object); Bitstream& operator>>(QObject*& object); @@ -287,8 +290,8 @@ public: #define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); -/// Declares the metatype and the streaming operators. The last line -/// ensures that the generated file will be included in the link phase. +/// Declares the metatype and the streaming operators. The last lines +/// ensure that the generated file will be included in the link phase. #define STRINGIFY(x) #x #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ From a08afae3192004aea0558cdc32470548c1d1ebbc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 2 Jan 2014 17:40:43 -0800 Subject: [PATCH 22/66] More work on client/server communication. --- .../src/metavoxels/MetavoxelServer.cpp | 74 +++++++++++++++++-- .../src/metavoxels/MetavoxelServer.h | 23 ++++++ interface/src/MetavoxelSystem.cpp | 2 +- .../metavoxels/src/DatagramSequencer.cpp | 3 + libraries/metavoxels/src/DatagramSequencer.h | 6 ++ 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 36b06d38cf..6039a15919 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -6,6 +6,8 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include + #include #include @@ -13,8 +15,13 @@ #include "MetavoxelServer.h" +const int SEND_INTERVAL = 50; + MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) : ThreadedAssignment(dataBuffer, numBytes) { + + _sendTimer.setSingleShot(true); + connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas())); } void MetavoxelServer::removeSession(const QUuid& sessionId) { @@ -23,8 +30,11 @@ void MetavoxelServer::removeSession(const QUuid& sessionId) { void MetavoxelServer::run() { commonInit("metavoxel-server", NODE_TYPE_METAVOXEL_SERVER); -} + _lastSend = QDateTime::currentMSecsSinceEpoch(); + _sendTimer.start(SEND_INTERVAL); +} + void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { switch (dataByteArray.at(0)) { case PACKET_TYPE_METAVOXEL_DATA: @@ -37,6 +47,20 @@ void MetavoxelServer::processDatagram(const QByteArray& dataByteArray, const Hif } } +void MetavoxelServer::sendDeltas() { + // send deltas for all sessions + foreach (MetavoxelSession* session, _sessions) { + session->sendDelta(); + } + + // restart the send timer + qint64 now = QDateTime::currentMSecsSinceEpoch(); + int elapsed = now - _lastSend; + _lastSend = now; + + _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - elapsed)); +} + void MetavoxelServer::processData(const QByteArray& data, const HifiSockAddr& sender) { // read the session id int headerPlusIDSize; @@ -66,6 +90,11 @@ 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))); + + // insert the baseline send record + SendRecord record = { 0 }; + _sendRecords.append(record); } void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr& sender) { @@ -79,6 +108,15 @@ void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr& _sequencer.receivedDatagram(data); } +void MetavoxelSession::sendDelta() { + Bitstream& out = _sequencer.startPacket(); + _sequencer.endPacket(); + + // record the send + SendRecord record = { _sequencer.getOutgoingPacketNumber() }; + _sendRecords.append(record); +} + void MetavoxelSession::timedOut() { qDebug() << "Session timed out [sessionId=" << _sessionId << ", sender=" << _sender << "]\n"; _server->removeSession(_sessionId); @@ -89,11 +127,31 @@ void MetavoxelSession::sendData(const QByteArray& data) { } void MetavoxelSession::readPacket(Bitstream& in) { - QVariant msg; - in >> msg; - glm::vec3 position = msg.value().position; - qDebug() << position.x << " " << position.y << " " << position.z << "\n"; - - Bitstream& out = _sequencer.startPacket(); - _sequencer.endPacket(); + QVariant message; + in >> message; + handleMessage(message); +} + +void MetavoxelSession::clearSendRecordsBefore(int packetNumber) { + if (_sendRecords.isEmpty()) { + return; + } + int index = packetNumber - _sendRecords.first().packetNumber; + if (index <= 0 || index >= _sendRecords.size()) { + return; + } + _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index); +} + +void MetavoxelSession::handleMessage(const QVariant& message) { + int userType = message.userType(); + if (userType == ClientStateMessage::Type) { + ClientStateMessage state = message.value(); + _position = state.position; + + } else if (userType == QMetaType::QVariantList) { + foreach (const QVariant& element, message.toList()) { + handleMessage(element); + } + } } diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 33735d4a45..b5bfb63232 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -10,6 +10,7 @@ #define __hifi__MetavoxelServer__ #include +#include #include #include @@ -35,12 +36,19 @@ public: virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); +private slots: + + void sendDeltas(); + private: void processData(const QByteArray& data, const HifiSockAddr& sender); MetavoxelData _data; + QTimer _sendTimer; + qint64 _lastSend; + QHash _sessions; }; @@ -54,6 +62,8 @@ public: void receivedData(const QByteArray& data, const HifiSockAddr& sender); + void sendDelta(); + private slots: void timedOut(); @@ -62,8 +72,17 @@ private slots: void readPacket(Bitstream& in); + void clearSendRecordsBefore(int packetNumber); + private: + void handleMessage(const QVariant& message); + + class SendRecord { + public: + int packetNumber; + }; + MetavoxelServer* _server; QUuid _sessionId; @@ -71,6 +90,10 @@ private: DatagramSequencer _sequencer; HifiSockAddr _sender; + + glm::vec3 _position; + + QList _sendRecords; }; #endif /* defined(__hifi__MetavoxelServer__) */ diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 61f4db6d95..01328df80e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -194,7 +194,7 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : void MetavoxelClient::simulate(float deltaTime) { Bitstream& out = _sequencer.startPacket(); - ClientStateMessage state = { glm::vec3(0.5f, 0.5f, 1.0f) }; + ClientStateMessage state = { Application::getInstance()->getCamera()->getPosition() }; out << QVariant::fromValue(state); _sequencer.endPacket(); } diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 0694f2b370..a97425036c 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -140,6 +140,9 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { _receiveRecords.erase(_receiveRecords.begin(), it + 1); } _outputStream.persistWriteMappings(record.mappings); + + // notify any listeners + emit sendAcknowledged(record.packetNumber); } void DatagramSequencer::sendPacket(const QByteArray& packet) { diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 629aeabf39..28318bd2e6 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -25,6 +25,9 @@ public: DatagramSequencer(const QByteArray& datagramHeader = QByteArray()); + /// Returns the packet number of the last packet sent. + int getOutgoingPacketNumber() const { return _outgoingPacketNumber; } + /// Starts a new packet for transmission. /// \return a reference to the Bitstream to use for writing to the packet Bitstream& startPacket(); @@ -44,6 +47,9 @@ signals: /// Emitted when a packet is available to read. void readyToRead(Bitstream& input); + /// Emitted when a sent packet has been acknowledged by the remote side. + void sendAcknowledged(int packetNumber); + private: class SendRecord { From 67ed04586966fc60242bc7eeb3caad3eda5294a0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 3 Jan 2014 11:10:21 -0800 Subject: [PATCH 23/66] More work on delta streaming. --- .../src/metavoxels/MetavoxelServer.cpp | 14 ++++----- .../src/metavoxels/MetavoxelServer.h | 3 +- interface/src/MetavoxelSystem.cpp | 29 ++++++++++++++++++- interface/src/MetavoxelSystem.h | 13 +++++++++ libraries/metavoxels/src/Bitstream.cpp | 22 +------------- libraries/metavoxels/src/Bitstream.h | 25 ++++++++++++++-- .../metavoxels/src/DatagramSequencer.cpp | 5 ++-- libraries/metavoxels/src/DatagramSequencer.h | 12 ++++++-- libraries/metavoxels/src/MetavoxelMessages.h | 7 +++++ 9 files changed, 91 insertions(+), 39 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 6039a15919..5c5c1a839b 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -132,15 +132,8 @@ void MetavoxelSession::readPacket(Bitstream& in) { handleMessage(message); } -void MetavoxelSession::clearSendRecordsBefore(int packetNumber) { - if (_sendRecords.isEmpty()) { - return; - } - int index = packetNumber - _sendRecords.first().packetNumber; - if (index <= 0 || index >= _sendRecords.size()) { - return; - } - _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index); +void MetavoxelSession::clearSendRecordsBefore(int index) { + _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1); } void MetavoxelSession::handleMessage(const QVariant& message) { @@ -148,7 +141,10 @@ void MetavoxelSession::handleMessage(const QVariant& message) { if (userType == ClientStateMessage::Type) { ClientStateMessage state = message.value(); _position = state.position; + + } else if (userType == MetavoxelDeltaMessage::Type) { + } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { handleMessage(element); diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index b5bfb63232..0d44e532c4 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -72,7 +72,7 @@ private slots: void readPacket(Bitstream& in); - void clearSendRecordsBefore(int packetNumber); + void clearSendRecordsBefore(int index); private: @@ -81,6 +81,7 @@ private: class SendRecord { public: int packetNumber; + MetavoxelData data; }; MetavoxelServer* _server; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 01328df80e..6c13714887 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -190,6 +190,11 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); + connect(&_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int))); + + // insert the baseline receive record + ReceiveRecord record = { 0 }; + _receiveRecords.append(record); } void MetavoxelClient::simulate(float deltaTime) { @@ -212,5 +217,27 @@ void MetavoxelClient::sendData(const QByteArray& data) { } void MetavoxelClient::readPacket(Bitstream& in) { - qDebug("got packet from server!\n"); + QVariant message; + in >> message; + handleMessage(message); + + // record the receipt + ReceiveRecord record = { _sequencer.getIncomingPacketNumber() }; + _receiveRecords.append(record); +} + +void MetavoxelClient::clearReceiveRecordsBefore(int index) { + _receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1); +} + +void MetavoxelClient::handleMessage(const QVariant& message) { + int userType = message.userType(); + if (userType == MetavoxelDeltaMessage::Type) { + + + } else if (userType == QMetaType::QVariantList) { + foreach (const QVariant& element, message.toList()) { + handleMessage(element); + } + } } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index b84f30dc2c..8f4a4f7368 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -9,6 +9,7 @@ #ifndef __interface__MetavoxelSystem__ #define __interface__MetavoxelSystem__ +#include #include #include #include @@ -98,12 +99,24 @@ private slots: void readPacket(Bitstream& in); + void clearReceiveRecordsBefore(int index); + private: + void handleMessage(const QVariant& message); + + class ReceiveRecord { + public: + int packetNumber; + MetavoxelData data; + }; + HifiSockAddr _address; QUuid _sessionID; DatagramSequencer _sequencer; + + QList _receiveRecords; }; #endif /* defined(__interface__MetavoxelSystem__) */ diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 258ef1ca29..e1ab79c974 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -17,6 +17,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) REGISTER_SIMPLE_TYPE_STREAMER(QString) +REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) REGISTER_SIMPLE_TYPE_STREAMER(bool) REGISTER_SIMPLE_TYPE_STREAMER(int) @@ -236,27 +237,6 @@ Bitstream& Bitstream::operator>>(QVariant& value) { return *this; } -Bitstream& Bitstream::operator<<(const QVariantList& value) { - *this << value.size(); - foreach (const QVariant& entry, value) { - *this << entry; - } - return *this; -} - -Bitstream& Bitstream::operator>>(QVariantList& value) { - int size; - *this >> size; - value.clear(); - value.reserve(size); - for (int i = 0; i < size; i++) { - QVariant entry; - *this >> entry; - value.append(entry); - } - 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 066da71b34..6709d4d03b 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -231,8 +231,8 @@ public: Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); - Bitstream& operator<<(const QVariantList& value); - Bitstream& operator>>(QVariantList& value); + template Bitstream& operator<<(const QList& list); + template Bitstream& operator>>(QList& list); Bitstream& operator<<(const QObject* object); Bitstream& operator>>(QObject*& object); @@ -260,6 +260,27 @@ private: static QHash& getTypeStreamers(); }; +template inline Bitstream& Bitstream::operator<<(const QList& list) { + *this << list.size(); + foreach (const T& entry, list) { + *this << entry; + } + return *this; +} + +template inline Bitstream& Bitstream::operator>>(QList& list) { + int size; + *this >> size; + list.clear(); + list.reserve(size); + for (int i = 0; i < size; i++) { + T entry; + *this >> entry; + list.append(entry); + } + return *this; +} + /// Macro for registering streamable meta-objects. #define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index a97425036c..82ea27dfab 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -119,6 +119,7 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { } QList::iterator it = _sendRecords.begin() + index; sendRecordAcknowledged(*it); + emit sendAcknowledged(index); _sendRecords.erase(_sendRecords.begin(), it + 1); } @@ -137,12 +138,10 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) { QList::iterator it = qBinaryFind(_receiveRecords.begin(), _receiveRecords.end(), compare); if (it != _receiveRecords.end()) { _inputStream.persistReadMappings(it->mappings); + emit receiveAcknowledged(it - _receiveRecords.begin()); _receiveRecords.erase(_receiveRecords.begin(), it + 1); } _outputStream.persistWriteMappings(record.mappings); - - // notify any listeners - emit sendAcknowledged(record.packetNumber); } void DatagramSequencer::sendPacket(const QByteArray& packet) { diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 28318bd2e6..e5e4b8c4be 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -28,6 +28,9 @@ public: /// Returns the packet number of the last packet sent. int getOutgoingPacketNumber() const { return _outgoingPacketNumber; } + /// Returns the packet number of the last packet received (or the packet currently being assembled). + int getIncomingPacketNumber() const { return _incomingPacketNumber; } + /// Starts a new packet for transmission. /// \return a reference to the Bitstream to use for writing to the packet Bitstream& startPacket(); @@ -47,8 +50,13 @@ signals: /// Emitted when a packet is available to read. void readyToRead(Bitstream& input); - /// Emitted when a sent packet has been acknowledged by the remote side. - void sendAcknowledged(int packetNumber); + /// 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); + + /// Emitted when our acknowledgement of a received packet has been acknowledged by the remote side. + /// \param index the index of the packet in our list of receive records + void receiveAcknowledged(int index); private: diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 42b088729b..3951e16d22 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -22,4 +22,11 @@ public: DECLARE_STREAMABLE_METATYPE(ClientStateMessage) +/// A message preceding metavoxel delta information. The actual delta will follow it in the stream. +class MetavoxelDeltaMessage { + STREAMABLE +}; + +DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaMessage) + #endif /* defined(__interface__MetavoxelMessages__) */ From 31443e4f4e8e191387bb360780bf3ebde432ec32 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 5 Jan 2014 22:07:38 -0800 Subject: [PATCH 24/66] Working on delta encoding. --- .../src/metavoxels/MetavoxelServer.cpp | 15 +++--- .../src/metavoxels/MetavoxelServer.h | 10 ++-- interface/src/MetavoxelSystem.cpp | 15 +++--- interface/src/MetavoxelSystem.h | 6 ++- libraries/metavoxels/src/MetavoxelData.cpp | 52 ++++++++++++++----- libraries/metavoxels/src/MetavoxelData.h | 22 +++++++- 6 files changed, 87 insertions(+), 33 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 5c5c1a839b..9c551ed99a 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -18,7 +18,8 @@ const int SEND_INTERVAL = 50; MetavoxelServer::MetavoxelServer(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes) { + ThreadedAssignment(dataBuffer, numBytes), + _data(new MetavoxelData()) { _sendTimer.setSingleShot(true); connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas())); @@ -93,7 +94,7 @@ MetavoxelSession::MetavoxelSession(MetavoxelServer* server, const QUuid& session connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int))); // insert the baseline send record - SendRecord record = { 0 }; + SendRecord record = { 0, MetavoxelDataPointer(new MetavoxelData()) }; _sendRecords.append(record); } @@ -110,10 +111,12 @@ void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr& void MetavoxelSession::sendDelta() { Bitstream& out = _sequencer.startPacket(); + out << QVariant::fromValue(MetavoxelDeltaMessage()); + _server->getData()->writeDelta(*_sendRecords.first().data, out); _sequencer.endPacket(); // record the send - SendRecord record = { _sequencer.getOutgoingPacketNumber() }; + SendRecord record = { _sequencer.getOutgoingPacketNumber(), _server->getData() }; _sendRecords.append(record); } @@ -129,14 +132,14 @@ void MetavoxelSession::sendData(const QByteArray& data) { void MetavoxelSession::readPacket(Bitstream& in) { QVariant message; in >> message; - handleMessage(message); + handleMessage(message, in); } void MetavoxelSession::clearSendRecordsBefore(int index) { _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1); } -void MetavoxelSession::handleMessage(const QVariant& message) { +void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { int userType = message.userType(); if (userType == ClientStateMessage::Type) { ClientStateMessage state = message.value(); @@ -147,7 +150,7 @@ void MetavoxelSession::handleMessage(const QVariant& message) { } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { - handleMessage(element); + handleMessage(element, in); } } } diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 0d44e532c4..407c520116 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -30,6 +30,8 @@ public: MetavoxelServer(const unsigned char* dataBuffer, int numBytes); + const MetavoxelDataPointer& getData() const { return _data; } + void removeSession(const QUuid& sessionId); virtual void run(); @@ -44,12 +46,12 @@ private: void processData(const QByteArray& data, const HifiSockAddr& sender); - MetavoxelData _data; - QTimer _sendTimer; qint64 _lastSend; QHash _sessions; + + MetavoxelDataPointer _data; }; /// Contains the state of a single client session. @@ -76,12 +78,12 @@ private slots: private: - void handleMessage(const QVariant& message); + void handleMessage(const QVariant& message, Bitstream& in); class SendRecord { public: int packetNumber; - MetavoxelData data; + MetavoxelDataPointer data; }; MetavoxelServer* _server; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 6c13714887..7c8b78c9f9 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -186,14 +186,15 @@ static QByteArray createDatagramHeader(const QUuid& sessionID) { MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : _address(address), _sessionID(QUuid::createUuid()), - _sequencer(createDatagramHeader(_sessionID)) { + _sequencer(createDatagramHeader(_sessionID)), + _data(new MetavoxelData()) { connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&))); connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&))); connect(&_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int))); // insert the baseline receive record - ReceiveRecord record = { 0 }; + ReceiveRecord record = { 0, _data }; _receiveRecords.append(record); } @@ -219,10 +220,10 @@ void MetavoxelClient::sendData(const QByteArray& data) { void MetavoxelClient::readPacket(Bitstream& in) { QVariant message; in >> message; - handleMessage(message); + handleMessage(message, in); // record the receipt - ReceiveRecord record = { _sequencer.getIncomingPacketNumber() }; + ReceiveRecord record = { _sequencer.getIncomingPacketNumber(), _data }; _receiveRecords.append(record); } @@ -230,14 +231,14 @@ void MetavoxelClient::clearReceiveRecordsBefore(int index) { _receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1); } -void MetavoxelClient::handleMessage(const QVariant& message) { +void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { int userType = message.userType(); if (userType == MetavoxelDeltaMessage::Type) { - + _data->readDelta(*_receiveRecords.first().data, in); } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { - handleMessage(element); + handleMessage(element, in); } } } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 8f4a4f7368..ddb69644d5 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -103,12 +103,12 @@ private slots: private: - void handleMessage(const QVariant& message); + void handleMessage(const QVariant& message, Bitstream& in); class ReceiveRecord { public: int packetNumber; - MetavoxelData data; + MetavoxelDataPointer data; }; HifiSockAddr _address; @@ -116,6 +116,8 @@ private: DatagramSequencer _sequencer; + MetavoxelDataPointer _data; + QList _receiveRecords; }; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index f7879e8b6d..a1e0a692fa 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -11,11 +11,22 @@ #include "MetavoxelData.h" +MetavoxelData::MetavoxelData() { +} + +MetavoxelData::MetavoxelData(const MetavoxelData& other) : _roots(other._roots) { + incrementRootReferenceCounts(); +} + MetavoxelData::~MetavoxelData() { - for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { - it.value()->destroy(it.key()); - delete it.value(); - } + decrementRootReferenceCounts(); +} + +MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) { + decrementRootReferenceCounts(); + _roots = other._roots; + incrementRootReferenceCounts(); + return *this; } void MetavoxelData::guide(MetavoxelVisitor& visitor) { @@ -43,8 +54,7 @@ void MetavoxelData::setAttributeValue(const MetavoxelPath& path, const Attribute node = new MetavoxelNode(attributeValue.getAttribute()); } if (node->setAttributeValue(path, 0, attributeValue) && attributeValue.isDefault()) { - node->destroy(attributeValue.getAttribute()); - delete node; + node->decrementReferenceCount(attributeValue.getAttribute()); _roots.remove(attributeValue.getAttribute()); } } @@ -85,8 +95,7 @@ void MetavoxelData::read(Bitstream& in) { // clear out the remaining old roots for (QHash::const_iterator it = oldRoots.constBegin(); it != oldRoots.constEnd(); it++) { - it.value()->destroy(it.key()); - delete it.value(); + it.value()->decrementReferenceCount(it.key()); } } @@ -104,7 +113,19 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const { } -MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) { +void MetavoxelData::incrementRootReferenceCounts() { + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + it.value()->incrementReferenceCount(); + } +} + +void MetavoxelData::decrementRootReferenceCounts() { + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + it.value()->decrementReferenceCount(it.key()); + } +} + +MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceCount(1) { _attributeValue = attributeValue.copy(); for (int i = 0; i < CHILD_COUNT; i++) { _children[i] = NULL; @@ -236,12 +257,18 @@ void MetavoxelNode::writeDelta(const AttributePointer& attribute, const Metavoxe } } +void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) { + if (--_referenceCount == 0) { + destroy(attribute); + delete this; + } +} + void MetavoxelNode::destroy(const AttributePointer& attribute) { attribute->destroy(_attributeValue); for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i]) { - _children[i]->destroy(attribute); - delete _children[i]; + _children[i]->decrementReferenceCount(attribute); } } } @@ -249,8 +276,7 @@ void MetavoxelNode::destroy(const AttributePointer& attribute) { void MetavoxelNode::clearChildren(const AttributePointer& attribute) { for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i]) { - _children[i]->destroy(attribute); - delete _children[i]; + _children[i]->decrementReferenceCount(attribute); _children[i] = NULL; } } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index be6b8c2288..f880b9f686 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -10,7 +10,9 @@ #define __interface__MetavoxelData__ #include +#include #include +#include #include #include #include @@ -21,17 +23,24 @@ class QScriptContext; +class MetavoxelData; class MetavoxelNode; class MetavoxelPath; class MetavoxelVisitation; class MetavoxelVisitor; +typedef QExplicitlySharedDataPointer MetavoxelDataPointer; + /// The base metavoxel representation shared between server and client. -class MetavoxelData { +class MetavoxelData : public QSharedData { public: + MetavoxelData(); + MetavoxelData(const MetavoxelData& other); ~MetavoxelData(); + MetavoxelData& operator=(const MetavoxelData& other); + /// Applies the specified visitor to the contained voxels. void guide(MetavoxelVisitor& visitor); @@ -49,6 +58,9 @@ public: private: + void incrementRootReferenceCounts(); + void decrementRootReferenceCounts(); + QHash _roots; }; @@ -81,6 +93,13 @@ public: void readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in); void writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const; + /// Increments the node's reference count. + void incrementReferenceCount() { _referenceCount++; } + + /// Decrements the node's reference count. If the resulting reference count is zero, destroys the node + /// and calls delete this. + void decrementReferenceCount(const AttributePointer& attribute); + void destroy(const AttributePointer& attribute); private: @@ -88,6 +107,7 @@ private: void clearChildren(const AttributePointer& attribute); + int _referenceCount; void* _attributeValue; MetavoxelNode* _children[CHILD_COUNT]; }; From 3504cc6d5ac3f43ae1a7d41b4a9d23c3b2c075e3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 6 Jan 2014 12:01:14 -0800 Subject: [PATCH 25/66] More work on deltas. --- .../src/metavoxels/MetavoxelServer.cpp | 2 +- interface/src/MetavoxelSystem.cpp | 2 +- libraries/metavoxels/src/MetavoxelData.cpp | 18 ++++++++++++++++++ libraries/metavoxels/src/MetavoxelData.h | 9 ++++++--- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 9c551ed99a..fc586599ce 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -112,7 +112,7 @@ void MetavoxelSession::receivedData(const QByteArray& data, const HifiSockAddr& void MetavoxelSession::sendDelta() { Bitstream& out = _sequencer.startPacket(); out << QVariant::fromValue(MetavoxelDeltaMessage()); - _server->getData()->writeDelta(*_sendRecords.first().data, out); + writeDelta(_server->getData(), _sendRecords.first().data, out); _sequencer.endPacket(); // record the send diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 7c8b78c9f9..355b027e93 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -234,7 +234,7 @@ void MetavoxelClient::clearReceiveRecordsBefore(int index) { void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { int userType = message.userType(); if (userType == MetavoxelDeltaMessage::Type) { - _data->readDelta(*_receiveRecords.first().data, in); + readDelta(_data, _receiveRecords.first().data, in); } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index a1e0a692fa..849d85960e 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -125,6 +125,24 @@ void MetavoxelData::decrementRootReferenceCounts() { } } +void writeDelta(const MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& out) { + if (data == reference) { + out << false; + return; + } + 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); + } +} + 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 f880b9f686..d588c8a687 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -23,14 +23,11 @@ class QScriptContext; -class MetavoxelData; class MetavoxelNode; class MetavoxelPath; class MetavoxelVisitation; class MetavoxelVisitor; -typedef QExplicitlySharedDataPointer MetavoxelDataPointer; - /// The base metavoxel representation shared between server and client. class MetavoxelData : public QSharedData { public: @@ -64,6 +61,12 @@ 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: From c163b513d2b10f6f38854d65ac38f455a4033d8f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 13 Jan 2014 18:09:28 -0800 Subject: [PATCH 26/66] Working on allowing visitors to modify, as well as read, data. --- interface/resources/scripts/sphere.js | 20 +++--- interface/src/MetavoxelSystem.cpp | 7 +- libraries/metavoxels/src/MetavoxelData.cpp | 81 +++++++++++++--------- libraries/metavoxels/src/MetavoxelData.h | 30 +++++--- 4 files changed, 82 insertions(+), 56 deletions(-) diff --git a/interface/resources/scripts/sphere.js b/interface/resources/scripts/sphere.js index 403374e812..b696021fe8 100644 --- a/interface/resources/scripts/sphere.js +++ b/interface/resources/scripts/sphere.js @@ -35,11 +35,11 @@ function setNormal(vector) { if (normalIndex != -1) { var length = Math.sqrt(lengthSquared(vector[0], vector[1], vector[2])); if (length == 0.0) { - info.attributeValues[normalIndex] = 0x007F00; + info.inputValues[normalIndex] = 0x007F00; } else { var scale = 127.0 / length; - info.attributeValues[normalIndex] = + info.inputValues[normalIndex] = (Math.floor(vector[0] * scale) & 0xFF) << 16 | (Math.floor(vector[1] * scale) & 0xFF) << 8 | Math.floor(vector[2] * scale) & 0xFF; @@ -61,7 +61,7 @@ function guide(minimum, size, depth) { maximum[2] <= sphereCenter[2] - sphereRadius) { info.isLeaf = true; if (colorIndex != -1) { - info.attributeValues[colorIndex] = 0x0; + info.inputValues[colorIndex] = 0x0; } visitor.visit(info); return; @@ -110,7 +110,7 @@ function guide(minimum, size, depth) { if (inside == 8) { info.isLeaf = true; if (colorIndex != -1) { - info.attributeValues[colorIndex] = sphereColor; + info.inputValues[colorIndex] = sphereColor; } setNormal(vector); visitor.visit(info); @@ -122,13 +122,13 @@ function guide(minimum, size, depth) { info.isLeaf = true; if (inside >= 3) { if (colorIndex != -1) { - info.attributeValues[colorIndex] = sphereColor; + info.inputValues[colorIndex] = sphereColor; } setNormal(vector); } else { if (colorIndex != -1) { - info.attributeValues[colorIndex] = 0x0; + info.inputValues[colorIndex] = 0x0; } } visitor.visit(info); @@ -152,11 +152,11 @@ function guide(minimum, size, depth) { } (function(visitation) { - var attributes = visitation.visitor.getAttributes(); - colorIndex = strictIndexOf(attributes, AttributeRegistry.colorAttribute); - normalIndex = strictIndexOf(attributes, AttributeRegistry.normalAttribute); + var inputs = visitation.visitor.getInputs(); + colorIndex = strictIndexOf(inputs, AttributeRegistry.colorAttribute); + normalIndex = strictIndexOf(inputs, AttributeRegistry.normalAttribute); visitor = visitation.visitor; - info = { attributeValues: new Array(attributes.length) }; + info = { inputValues: new Array(inputs.length) }; // have the sphere orbit the center and pulse in size var time = new Date().getTime(); diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 355b027e93..298367b253 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -157,7 +157,8 @@ void MetavoxelSystem::receivedData(const QByteArray& data, const HifiSockAddr& s MetavoxelSystem::PointVisitor::PointVisitor(QVector& points) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getColorAttribute() << - AttributeRegistry::getInstance()->getNormalAttribute()), + AttributeRegistry::getInstance()->getNormalAttribute(), + QVector()), _points(points) { } @@ -165,8 +166,8 @@ bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) { if (!info.isLeaf) { return true; } - QRgb color = info.attributeValues.at(0).getInlineValue(); - QRgb normal = info.attributeValues.at(1).getInlineValue(); + QRgb color = info.inputValues.at(0).getInlineValue(); + QRgb normal = info.inputValues.at(1).getInlineValue(); int alpha = qAlpha(color); if (alpha > 0) { Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 8f2bd7429c..f89b64727b 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -32,19 +32,20 @@ MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) { void MetavoxelData::guide(MetavoxelVisitor& visitor) { // start with the root values/defaults (plus the guide attribute) const float TOP_LEVEL_SIZE = 1.0f; - const QVector& attributes = visitor.getAttributes(); - MetavoxelVisitation firstVisitation = { visitor, QVector(attributes.size() + 1), - { glm::vec3(), TOP_LEVEL_SIZE, QVector(attributes.size() + 1) } }; - for (int i = 0; i < attributes.size(); i++) { - MetavoxelNode* node = _roots.value(attributes[i]); + const QVector& inputs = visitor.getInputs(); + const QVector& outputs = visitor.getOutputs(); + MetavoxelVisitation firstVisitation = { visitor, QVector(inputs.size() + 1), + { glm::vec3(), TOP_LEVEL_SIZE, QVector(inputs.size() + 1), QVector(outputs.size()) } }; + for (int i = 0; i < inputs.size(); i++) { + MetavoxelNode* node = _roots.value(inputs[i]); firstVisitation.nodes[i] = node; - firstVisitation.info.attributeValues[i] = node ? node->getAttributeValue(attributes[i]) : attributes[i]; + firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(inputs[i]) : inputs[i]; } AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute(); MetavoxelNode* node = _roots.value(guideAttribute); firstVisitation.nodes.last() = node; - firstVisitation.info.attributeValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; - static_cast(firstVisitation.info.attributeValues.last().getInlineValue< + firstVisitation.info.inputValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; + static_cast(firstVisitation.info.inputValues.last().getInlineValue< PolymorphicDataPointer>().data())->guide(firstVisitation); } @@ -369,6 +370,11 @@ MetavoxelPath& MetavoxelPath::operator+=(int element) { return *this; } +MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, const QVector& outputs) : + _inputs(inputs), + _outputs(outputs) { +} + PolymorphicData* DefaultMetavoxelGuide::clone() const { return new DefaultMetavoxelGuide(); } @@ -379,7 +385,7 @@ const int Z_MAXIMUM_FLAG = 4; void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { visitation.info.isLeaf = visitation.allNodesLeaves(); - if (!visitation.visitor.visit(visitation.info) || visitation.info.isLeaf) { + if (!visitation.visitor.visit(visitation.info)) { return; } MetavoxelVisitation nextVisitation = { visitation.visitor, QVector(visitation.nodes.size()), @@ -388,32 +394,40 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { for (int j = 0; j < visitation.nodes.size(); j++) { MetavoxelNode* node = visitation.nodes.at(j); MetavoxelNode* child = node ? node->getChild(i) : NULL; - nextVisitation.info.attributeValues[j] = ((nextVisitation.nodes[j] = child)) ? - child->getAttributeValue(visitation.info.attributeValues[j].getAttribute()) : - visitation.info.attributeValues[j]; + nextVisitation.info.inputValues[j] = ((nextVisitation.nodes[j] = child)) ? + child->getAttributeValue(visitation.info.inputValues[j].getAttribute()) : + visitation.info.inputValues[j]; } nextVisitation.info.minimum = visitation.info.minimum + glm::vec3( (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); - static_cast(nextVisitation.info.attributeValues.last().getInlineValue< + static_cast(nextVisitation.info.inputValues.last().getInlineValue< PolymorphicDataPointer>().data())->guide(nextVisitation); } } -QScriptValue ScriptedMetavoxelGuide::getAttributes(QScriptContext* context, QScriptEngine* engine) { - ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); +static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, + const QVector& attributes) { - const QVector& attributes = guide->_visitation->visitor.getAttributes(); QScriptValue attributesValue = engine->newArray(attributes.size()); for (int i = 0; i < attributes.size(); i++) { attributesValue.setProperty(i, engine->newQObject(attributes.at(i).data(), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject)); } - return attributesValue; } +QScriptValue ScriptedMetavoxelGuide::getInputs(QScriptContext* context, QScriptEngine* engine) { + ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); + return getAttributes(engine, guide, guide->_visitation->visitor.getInputs()); +} + +QScriptValue ScriptedMetavoxelGuide::getOutputs(QScriptContext* context, QScriptEngine* engine) { + ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); + return getAttributes(engine, guide, guide->_visitation->visitor.getOutputs()); +} + QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) { ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); @@ -422,26 +436,26 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin QScriptValue minimum = infoValue.property(guide->_minimumHandle); MetavoxelInfo info = { glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()), - infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.attributeValues, - infoValue.property(guide->_isLeafHandle).toBool() }; + infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.inputValues, + guide->_visitation->info.outputValues, infoValue.property(guide->_isLeafHandle).toBool() }; // extract and convert the values provided by the script - QScriptValue attributeValues = infoValue.property(guide->_attributeValuesHandle); - const QVector& attributes = guide->_visitation->visitor.getAttributes(); - for (int i = 0; i < attributes.size(); i++) { - QScriptValue attributeValue = attributeValues.property(i); + QScriptValue inputValues = infoValue.property(guide->_inputValuesHandle); + const QVector& inputs = guide->_visitation->visitor.getInputs(); + for (int i = 0; i < inputs.size(); i++) { + QScriptValue attributeValue = inputValues.property(i); if (attributeValue.isValid()) { - info.attributeValues[i] = AttributeValue(attributes.at(i), - attributes.at(i)->createFromScript(attributeValue, engine)); + info.inputValues[i] = AttributeValue(inputs.at(i), + inputs.at(i)->createFromScript(attributeValue, engine)); } } QScriptValue result = guide->_visitation->visitor.visit(info); // destroy any created values - for (int i = 0; i < attributes.size(); i++) { - if (attributeValues.property(i).isValid()) { - info.attributeValues[i].getAttribute()->destroy(info.attributeValues[i].getValue()); + for (int i = 0; i < inputs.size(); i++) { + if (inputValues.property(i).isValid()) { + info.inputValues[i].getAttribute()->destroy(info.inputValues[i].getValue()); } } @@ -452,16 +466,19 @@ ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction _guideFunction(guideFunction), _minimumHandle(guideFunction.engine()->toStringHandle("minimum")), _sizeHandle(guideFunction.engine()->toStringHandle("size")), - _attributeValuesHandle(guideFunction.engine()->toStringHandle("attributeValues")), + _inputValuesHandle(guideFunction.engine()->toStringHandle("inputValues")), + _outputValuesHandle(guideFunction.engine()->toStringHandle("outputValues")), _isLeafHandle(guideFunction.engine()->toStringHandle("isLeaf")), - _getAttributesFunction(guideFunction.engine()->newFunction(getAttributes, 0)), + _getInputsFunction(guideFunction.engine()->newFunction(getInputs, 0)), + _getOutputsFunction(guideFunction.engine()->newFunction(getOutputs, 0)), _visitFunction(guideFunction.engine()->newFunction(visit, 1)), _info(guideFunction.engine()->newObject()), _minimum(guideFunction.engine()->newArray(3)) { _arguments.append(guideFunction.engine()->newObject()); QScriptValue visitor = guideFunction.engine()->newObject(); - visitor.setProperty("getAttributes", _getAttributesFunction); + visitor.setProperty("getInputs", _getInputsFunction); + visitor.setProperty("getOutputs", _getOutputsFunction); visitor.setProperty("visit", _visitFunction); _arguments[0].setProperty("visitor", visitor); _arguments[0].setProperty("info", _info); @@ -474,7 +491,7 @@ PolymorphicData* ScriptedMetavoxelGuide::clone() const { void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue(this)); - _getAttributesFunction.setData(data); + _getInputsFunction.setData(data); _visitFunction.setData(data); _minimum.setProperty(0, visitation.info.minimum.x); _minimum.setProperty(1, visitation.info.minimum.y); diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index d588c8a687..a129930e93 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -139,7 +139,8 @@ public: glm::vec3 minimum; ///< the minimum extent of the area covered by the voxel float size; ///< the size of the voxel in all dimensions - QVector attributeValues; + QVector inputValues; + QVector outputValues; bool isLeaf; }; @@ -147,19 +148,23 @@ public: class MetavoxelVisitor { public: - MetavoxelVisitor(const QVector& attributes) : _attributes(attributes) { } - - /// Returns a reference to the list of attributes desired. - const QVector& getAttributes() const { return _attributes; } + MetavoxelVisitor(const QVector& inputs, const QVector& outputs); + + /// Returns a reference to the list of input attributes desired. + const QVector& getInputs() const { return _inputs; } + + /// Returns a reference to the list of output attributes provided. + const QVector& getOutputs() const { return _outputs; } /// Visits a metavoxel. - /// \param info the metavoxel ata - /// \param if true, continue descending; if false, stop + /// \param info the metavoxel data + /// \return if true, continue descending; if false, stop virtual bool visit(const MetavoxelInfo& info) = 0; protected: - QVector _attributes; + QVector _inputs; + QVector _outputs; }; /// Interface for objects that guide metavoxel visitors. @@ -191,16 +196,19 @@ public: private: - static QScriptValue getAttributes(QScriptContext* context, QScriptEngine* engine); + static QScriptValue getInputs(QScriptContext* context, QScriptEngine* engine); + static QScriptValue getOutputs(QScriptContext* context, QScriptEngine* engine); static QScriptValue visit(QScriptContext* context, QScriptEngine* engine); QScriptValue _guideFunction; QScriptString _minimumHandle; QScriptString _sizeHandle; - QScriptString _attributeValuesHandle; + QScriptString _inputValuesHandle; + QScriptString _outputValuesHandle; QScriptString _isLeafHandle; QScriptValueList _arguments; - QScriptValue _getAttributesFunction; + QScriptValue _getInputsFunction; + QScriptValue _getOutputsFunction; QScriptValue _visitFunction; QScriptValue _info; QScriptValue _minimum; From d9589cd89814cb877fb33e26f9174f1f6f563162 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Jan 2014 11:43:15 -0800 Subject: [PATCH 27/66] Starting on metavoxel editor dialog. --- interface/src/Menu.cpp | 10 ++++++++++ interface/src/Menu.h | 7 ++++++- interface/src/ui/MetavoxelEditorDialog.cpp | 18 +++++++++++++++++ interface/src/ui/MetavoxelEditorDialog.h | 23 ++++++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 interface/src/ui/MetavoxelEditorDialog.cpp create mode 100644 interface/src/ui/MetavoxelEditorDialog.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 87edfa82a2..0aa7b8047f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -32,6 +32,7 @@ #include "Menu.h" #include "Util.h" #include "InfoView.h" +#include "ui/MetavoxelEditorDialog.h" Menu* Menu::_instance = NULL; @@ -220,6 +221,8 @@ Menu::Menu() : SLOT(increaseVoxelSize())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors())); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor())); + QMenu* viewMenu = addMenu("View"); @@ -1007,6 +1010,13 @@ void Menu::bandwidthDetails() { _bandwidthDialog->raise(); } +void Menu::showMetavoxelEditor() { + if (_metavoxelEditorDialog.isNull()) { + _metavoxelEditorDialog = new MetavoxelEditorDialog(); + } + _metavoxelEditorDialog->raise(); +} + void Menu::audioMuteToggled() { QAction *muteAction = _actionHash.value(MenuOption::MuteAudio); muteAction->setChecked(Application::getInstance()->getAudio()->getMuted()); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b356f29a85..22e92a76be 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -36,8 +37,9 @@ struct ViewFrustumOffset { class QSettings; class BandwidthDialog; -class VoxelStatsDialog; class LodToolsDialog; +class MetavoxelEditorDialog; +class VoxelStatsDialog; class Menu : public QMenuBar, public AbstractMenuInterface { Q_OBJECT @@ -107,6 +109,7 @@ private slots: void chooseVoxelPaintColor(); void runTests(); void resetSwatchColors(); + void showMetavoxelEditor(); void audioMuteToggled(); private: @@ -140,6 +143,7 @@ private: FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; QActionGroup* _voxelModeActionsGroup; + QPointer _metavoxelEditorDialog; VoxelStatsDialog* _voxelStatsDialog; LodToolsDialog* _lodToolsDialog; int _maxVoxels; @@ -218,6 +222,7 @@ namespace MenuOption { const QString Login = "Login"; const QString LookAtIndicator = "Look-at Indicator"; const QString LookAtVectors = "Look-at Vectors"; + const QString MetavoxelEditor = "Metavoxel Editor..."; const QString Metavoxels = "Metavoxels"; const QString Mirror = "Mirror"; const QString MoveWithLean = "Move with Lean"; diff --git a/interface/src/ui/MetavoxelEditorDialog.cpp b/interface/src/ui/MetavoxelEditorDialog.cpp new file mode 100644 index 0000000000..d5903d6d43 --- /dev/null +++ b/interface/src/ui/MetavoxelEditorDialog.cpp @@ -0,0 +1,18 @@ +// +// MetavoxelEditorDialog.cpp +// interface +// +// Created by Andrzej Kapolka on 1/21/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. + +#include "Application.h" +#include "MetavoxelEditorDialog.h" + +MetavoxelEditorDialog::MetavoxelEditorDialog() : + QDialog(Application::getInstance()->getGLWidget()) { + + setWindowTitle("Metavoxel Editor"); + setAttribute(Qt::WA_DeleteOnClose); + + show(); +} diff --git a/interface/src/ui/MetavoxelEditorDialog.h b/interface/src/ui/MetavoxelEditorDialog.h new file mode 100644 index 0000000000..14ebf62109 --- /dev/null +++ b/interface/src/ui/MetavoxelEditorDialog.h @@ -0,0 +1,23 @@ +// +// MetavoxelEditorDialog.h +// interface +// +// Created by Andrzej Kapolka on 1/21/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelEditorDialog__ +#define __interface__MetavoxelEditorDialog__ + +#include + +/// Allows editing metavoxels. +class MetavoxelEditorDialog : public QDialog { + Q_OBJECT + +public: + + MetavoxelEditorDialog(); +}; + +#endif /* defined(__interface__MetavoxelEditorDialog__) */ From 5b4447d0b94dd1d17082a1333d9195b1f3aeae61 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Jan 2014 16:17:31 -0800 Subject: [PATCH 28/66] More work on metavoxel editing. --- interface/src/Menu.cpp | 2 +- interface/src/ui/MetavoxelEditorDialog.cpp | 107 ++++++++++++++++++ interface/src/ui/MetavoxelEditorDialog.h | 16 +++ .../metavoxels/src/AttributeRegistry.cpp | 28 +++++ libraries/metavoxels/src/AttributeRegistry.h | 34 ++++++ 5 files changed, 186 insertions(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0aa7b8047f..e57dd74fe2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1011,7 +1011,7 @@ void Menu::bandwidthDetails() { } void Menu::showMetavoxelEditor() { - if (_metavoxelEditorDialog.isNull()) { + if (!_metavoxelEditorDialog) { _metavoxelEditorDialog = new MetavoxelEditorDialog(); } _metavoxelEditorDialog->raise(); diff --git a/interface/src/ui/MetavoxelEditorDialog.cpp b/interface/src/ui/MetavoxelEditorDialog.cpp index d5903d6d43..1a1cac3adf 100644 --- a/interface/src/ui/MetavoxelEditorDialog.cpp +++ b/interface/src/ui/MetavoxelEditorDialog.cpp @@ -5,6 +5,16 @@ // Created by Andrzej Kapolka on 1/21/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +#include +#include +#include +#include +#include +#include +#include + +#include + #include "Application.h" #include "MetavoxelEditorDialog.h" @@ -13,6 +23,103 @@ MetavoxelEditorDialog::MetavoxelEditorDialog() : setWindowTitle("Metavoxel Editor"); setAttribute(Qt::WA_DeleteOnClose); + + QVBoxLayout* topLayout = new QVBoxLayout(); + setLayout(topLayout); + + QGroupBox* attributeGroup = new QGroupBox(); + attributeGroup->setTitle("Attributes"); + topLayout->addWidget(attributeGroup); + + QVBoxLayout* attributeLayout = new QVBoxLayout(); + attributeGroup->setLayout(attributeLayout); + + attributeLayout->addWidget(_attributes = new QListWidget()); + connect(_attributes, SIGNAL(itemSelectionChanged()), SLOT(updateValueEditor())); + + QPushButton* newAttribute = new QPushButton("New..."); + attributeLayout->addWidget(newAttribute); + connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute())); + + _value = new QGroupBox(); + _value->setTitle("Value"); + topLayout->addWidget(_value); + + QVBoxLayout* valueLayout = new QVBoxLayout(); + _value->setLayout(valueLayout); + + updateAttributes(); show(); } + +void MetavoxelEditorDialog::updateValueEditor() { + QString selected = getSelectedAttribute(); + if (selected.isNull()) { + _value->setVisible(false); + return; + } + _value->setVisible(true); + + if (!_value->layout()->isEmpty()) { + delete _value->layout()->takeAt(0); + } + + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); + QWidget* editor = attribute->createEditor(); + if (editor) { + _value->layout()->addWidget(editor); + } +} + +void MetavoxelEditorDialog::createNewAttribute() { + QDialog dialog(this); + dialog.setWindowTitle("New Attribute"); + + QVBoxLayout layout; + dialog.setLayout(&layout); + + QFormLayout form; + layout.addLayout(&form); + + QLineEdit name; + form.addRow("Name:", &name); + + QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + dialog.connect(&buttons, SIGNAL(accepted()), SLOT(accept())); + dialog.connect(&buttons, SIGNAL(rejected()), SLOT(reject())); + + layout.addWidget(&buttons); + + if (!dialog.exec()) { + return; + } + QString nameText = name.text().trimmed(); + AttributeRegistry::getInstance()->registerAttribute(new QRgbAttribute(nameText)); + + updateAttributes(nameText); +} + +void MetavoxelEditorDialog::updateAttributes(const QString& select) { + // remember the selection in order to preserve it + QString selected = select.isNull() ? getSelectedAttribute() : select; + _attributes->clear(); + + // sort the names for consistent ordering + QList names = AttributeRegistry::getInstance()->getAttributes().keys(); + qSort(names); + + foreach (const QString& name, names) { + QListWidgetItem* item = new QListWidgetItem(name); + _attributes->addItem(item); + if (name == selected || selected.isNull()) { + item->setSelected(true); + selected = name; + } + } +} + +QString MetavoxelEditorDialog::getSelectedAttribute() const { + QList selectedItems = _attributes->selectedItems(); + return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); +} diff --git a/interface/src/ui/MetavoxelEditorDialog.h b/interface/src/ui/MetavoxelEditorDialog.h index 14ebf62109..15eaac7801 100644 --- a/interface/src/ui/MetavoxelEditorDialog.h +++ b/interface/src/ui/MetavoxelEditorDialog.h @@ -11,6 +11,9 @@ #include +class QGroupBox; +class QListWidget; + /// Allows editing metavoxels. class MetavoxelEditorDialog : public QDialog { Q_OBJECT @@ -18,6 +21,19 @@ class MetavoxelEditorDialog : public QDialog { public: MetavoxelEditorDialog(); + +private slots: + + void updateValueEditor(); + void createNewAttribute(); + +private: + + void updateAttributes(const QString& select = QString()); + QString getSelectedAttribute() const; + + QListWidget* _attributes; + QGroupBox* _value; }; #endif /* defined(__interface__MetavoxelEditorDialog__) */ diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9a1f220034..accf9d81cf 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -6,7 +6,10 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include +#include #include +#include #include "AttributeRegistry.h" #include "MetavoxelData.h" @@ -132,6 +135,31 @@ void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* return encodeInline((QRgb)value.toUInt32()); } +QWidget* QRgbAttribute::createEditor(QWidget* parent) const { + QRgbEditor* editor = new QRgbEditor(parent); + editor->setColor(_defaultValue); + return editor; +} + +QRgbEditor::QRgbEditor(QWidget* parent) : QWidget(parent) { + setLayout(new QVBoxLayout()); + layout()->addWidget(_button = new QPushButton()); + connect(_button, SIGNAL(clicked()), SLOT(selectColor())); +} + +void QRgbEditor::setColor(int color) { + QString name = QColor::fromRgba(_color = color).name(); + _button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color).name())); + _button->setText(name); +} + +void QRgbEditor::selectColor() { + QColor color = QColorDialog::getColor(QColor::fromRgba(_color), this, QString(), QColorDialog::ShowAlphaChannel); + if (color.isValid()) { + setColor(color.rgba()); + } +} + PolymorphicData::~PolymorphicData() { } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 4f2f1d79b2..3501c289c6 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -16,9 +16,11 @@ #include #include #include +#include #include "Bitstream.h" +class QPushButton; class QScriptContext; class QScriptEngine; class QScriptValue; @@ -52,6 +54,9 @@ public: /// Retrieves an attribute by name. AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); } + /// Returns a reference to the attribute hash. + const QHash& getAttributes() const { return _attributes; } + /// Returns a reference to the standard PolymorphicDataPointer "guide" attribute. const AttributePointer& getGuideAttribute() const { return _guideAttribute; } @@ -153,6 +158,10 @@ public: virtual void* getDefaultValue() const = 0; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); } + + /// Creates a widget to use to edit values of this attribute, or returns NULL if the attribute isn't editable. + /// The widget should have a single "user" property that will be used to get/set the value. + virtual QWidget* createEditor(QWidget* parent = NULL) const { return NULL; } }; /// A simple attribute class that stores its values inline. @@ -222,6 +231,31 @@ public: virtual bool merge(void*& parent, void* children[]) const; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; + + virtual QWidget* createEditor(QWidget* parent = NULL) const; +}; + +/// Editor for RGBA values. +class QRgbEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(int color MEMBER _color WRITE setColor USER true) + +public: + + QRgbEditor(QWidget* parent); + +public slots: + + void setColor(int color); + +private slots: + + void selectColor(); + +private: + + QPushButton* _button; + QRgb _color; }; /// An attribute class that stores pointers to its values. From 230b66680f2131cb3b0757098e23131c76c5cb19 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Jan 2014 17:20:12 -0800 Subject: [PATCH 29/66] More work on the editor. --- interface/src/Application.cpp | 3 ++ interface/src/Application.h | 5 +++ interface/src/ui/MetavoxelEditorDialog.cpp | 41 ++++++++++++++++++++++ interface/src/ui/MetavoxelEditorDialog.h | 8 +++++ 4 files changed, 57 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2b75763875..8c36d4a867 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3138,6 +3138,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glPopMatrix(); } + + // give external parties a change to hook in + emit renderingInWorldInterface(); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index ae2638c230..7ecbb69084 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -210,6 +210,11 @@ public: void skipVersion(QString latestVersion); +signals: + + /// Fired when we're rendering in-world interface elements; allows external parties to hook in. + void renderingInWorldInterface(); + public slots: void domainChanged(const QString& domainHostname); void nodeKilled(SharedNodePointer node); diff --git a/interface/src/ui/MetavoxelEditorDialog.cpp b/interface/src/ui/MetavoxelEditorDialog.cpp index 1a1cac3adf..b21cfb0840 100644 --- a/interface/src/ui/MetavoxelEditorDialog.cpp +++ b/interface/src/ui/MetavoxelEditorDialog.cpp @@ -5,7 +5,9 @@ // Created by Andrzej Kapolka on 1/21/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +#include #include +#include #include #include #include @@ -18,6 +20,10 @@ #include "Application.h" #include "MetavoxelEditorDialog.h" +enum GridPlane { + GRID_PLANE_XY, GRID_PLANE_XZ, GRID_PLANE_YZ +}; + MetavoxelEditorDialog::MetavoxelEditorDialog() : QDialog(Application::getInstance()->getGLWidget()) { @@ -41,6 +47,26 @@ MetavoxelEditorDialog::MetavoxelEditorDialog() : attributeLayout->addWidget(newAttribute); connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute())); + QFormLayout* formLayout = new QFormLayout(); + topLayout->addLayout(formLayout); + + formLayout->addRow("Grid Plane:", _gridPlane = new QComboBox()); + _gridPlane->addItem("X/Y"); + _gridPlane->addItem("X/Z"); + _gridPlane->addItem("Y/Z"); + _gridPlane->setCurrentIndex(GRID_PLANE_XZ); + + formLayout->addRow("Grid Spacing:", _gridSpacing = new QDoubleSpinBox()); + _gridSpacing->setValue(0.1); + _gridSpacing->setMaximum(FLT_MAX); + _gridSpacing->setSingleStep(0.01); + 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); + _value = new QGroupBox(); _value->setTitle("Value"); topLayout->addWidget(_value); @@ -50,6 +76,8 @@ MetavoxelEditorDialog::MetavoxelEditorDialog() : updateAttributes(); + connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); + show(); } @@ -100,6 +128,19 @@ void MetavoxelEditorDialog::createNewAttribute() { updateAttributes(nameText); } +void MetavoxelEditorDialog::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)); + } +} + +void MetavoxelEditorDialog::render() { + +} + void MetavoxelEditorDialog::updateAttributes(const QString& select) { // remember the selection in order to preserve it QString selected = select.isNull() ? getSelectedAttribute() : select; diff --git a/interface/src/ui/MetavoxelEditorDialog.h b/interface/src/ui/MetavoxelEditorDialog.h index 15eaac7801..530a858a94 100644 --- a/interface/src/ui/MetavoxelEditorDialog.h +++ b/interface/src/ui/MetavoxelEditorDialog.h @@ -11,6 +11,8 @@ #include +class QComboBox; +class QDoubleSpinBox; class QGroupBox; class QListWidget; @@ -26,6 +28,9 @@ private slots: void updateValueEditor(); void createNewAttribute(); + void updateGridPosition(); + + void render(); private: @@ -33,6 +38,9 @@ private: QString getSelectedAttribute() const; QListWidget* _attributes; + QComboBox* _gridPlane; + QDoubleSpinBox* _gridSpacing; + QDoubleSpinBox* _gridPosition; QGroupBox* _value; }; From 0a3d6ae5e05d8ea768e7f1acdb5d44e6f089cbb7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Jan 2014 19:31:56 -0800 Subject: [PATCH 30/66] More work on metavoxel editing (grid, etc.) --- interface/resources/shaders/grid.frag | 13 ++++++ interface/src/renderer/GeometryCache.cpp | 48 ++++++++++++++++++++++ interface/src/renderer/GeometryCache.h | 3 ++ interface/src/ui/MetavoxelEditorDialog.cpp | 46 +++++++++++++++++++++ interface/src/ui/MetavoxelEditorDialog.h | 4 ++ 5 files changed, 114 insertions(+) create mode 100644 interface/resources/shaders/grid.frag diff --git a/interface/resources/shaders/grid.frag b/interface/resources/shaders/grid.frag new file mode 100644 index 0000000000..c62241cc3e --- /dev/null +++ b/interface/resources/shaders/grid.frag @@ -0,0 +1,13 @@ +#version 120 + +// +// grid.frag +// fragment shader +// +// Created by Andrzej Kapolka on 1/21/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +void main(void) { + gl_FragColor = vec4(gl_Color.rgb, exp(-0.25 / gl_FragCoord.w)); +} diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 9fff306aca..bdb3916694 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -7,7 +7,11 @@ #include +// include this before QOpenGLBuffer, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + #include +#include #include "Application.h" #include "GeometryCache.h" @@ -241,6 +245,50 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } +void GeometryCache::renderGrid(int xDivisions, int yDivisions) { + QOpenGLBuffer& buffer = _gridBuffers[IntPair(xDivisions, yDivisions)]; + int vertices = (xDivisions + 1 + yDivisions + 1) * 2; + if (!buffer.isCreated()) { + GLfloat* vertexData = new GLfloat[vertices * 2]; + GLfloat* vertex = vertexData; + for (int i = 0; i <= xDivisions; i++) { + float x = (float)i / xDivisions; + + *(vertex++) = x; + *(vertex++) = 0.0f; + + *(vertex++) = x; + *(vertex++) = 1.0f; + } + for (int i = 0; i <= yDivisions; i++) { + float y = (float)i / yDivisions; + + *(vertex++) = 0.0f; + *(vertex++) = y; + + *(vertex++) = 1.0f; + *(vertex++) = y; + } + buffer.create(); + buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); + buffer.bind(); + buffer.allocate(vertexData, vertices * 2 * sizeof(GLfloat)); + delete[] vertexData; + + } else { + buffer.bind(); + } + glEnableClientState(GL_VERTEX_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, 0); + + glDrawArrays(GL_LINES, 0, vertices); + + glDisableClientState(GL_VERTEX_ARRAY); + + buffer.release(); +} + QSharedPointer GeometryCache::getGeometry(const QUrl& url) { QSharedPointer geometry = _networkGeometry.value(url); if (geometry.isNull()) { diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 8a68917ba5..312d8bcd91 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -19,6 +19,7 @@ #include "InterfaceConfig.h" class QNetworkReply; +class QOpenGLBuffer; class NetworkGeometry; class NetworkMesh; @@ -33,6 +34,7 @@ public: void renderHemisphere(int slices, int stacks); void renderSquare(int xDivisions, int yDivisions); void renderHalfCylinder(int slices, int stacks); + void renderGrid(int xDivisions, int yDivisions); /// Loads geometry from the specified URL. QSharedPointer getGeometry(const QUrl& url); @@ -45,6 +47,7 @@ private: QHash _hemisphereVBOs; QHash _squareVBOs; QHash _halfCylinderVBOs; + QHash _gridBuffers; QHash > _networkGeometry; }; diff --git a/interface/src/ui/MetavoxelEditorDialog.cpp b/interface/src/ui/MetavoxelEditorDialog.cpp index b21cfb0840..73319cdc08 100644 --- a/interface/src/ui/MetavoxelEditorDialog.cpp +++ b/interface/src/ui/MetavoxelEditorDialog.cpp @@ -79,6 +79,13 @@ MetavoxelEditorDialog::MetavoxelEditorDialog() : connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); show(); + + if (_gridProgram.isLinked()) { + return; + } + switchToResourcesParentIfRequired(); + _gridProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/grid.frag"); + _gridProgram.link(); } void MetavoxelEditorDialog::updateValueEditor() { @@ -138,7 +145,44 @@ void MetavoxelEditorDialog::updateGridPosition() { } void MetavoxelEditorDialog::render() { + const float GRID_BRIGHTNESS = 0.5f; + glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); + glDisable(GL_LIGHTING); + glPushMatrix(); + + glm::quat rotation; + switch (_gridPlane->currentIndex()) { + case GRID_PLANE_XZ: + rotation = glm::angleAxis(90.0f, 1.0f, 0.0f, 0.0f); + glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); + break; + + case GRID_PLANE_YZ: + rotation = glm::angleAxis(-90.0f, 0.0f, 1.0f, 0.0f); + glRotatef(90.0f, 0.0f, 1.0f, 0.0f); + break; + } + + // center the grid around the camera position on the plane + glm::vec3 rotated = rotation * Application::getInstance()->getCamera()->getPosition(); + float spacing = _gridSpacing->value(); + const int GRID_DIVISIONS = 300; + glTranslatef(spacing * (floor(rotated.x / spacing) - GRID_DIVISIONS / 2), + spacing * (floor(rotated.y / spacing) - GRID_DIVISIONS / 2), _gridPosition->value()); + + float scale = GRID_DIVISIONS * spacing; + glScalef(scale, scale, scale); + + _gridProgram.bind(); + + Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); + + _gridProgram.release(); + + glPopMatrix(); + + glEnable(GL_LIGHTING); } void MetavoxelEditorDialog::updateAttributes(const QString& select) { @@ -164,3 +208,5 @@ QString MetavoxelEditorDialog::getSelectedAttribute() const { QList selectedItems = _attributes->selectedItems(); return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); } + +ProgramObject MetavoxelEditorDialog::_gridProgram; diff --git a/interface/src/ui/MetavoxelEditorDialog.h b/interface/src/ui/MetavoxelEditorDialog.h index 530a858a94..872cc50810 100644 --- a/interface/src/ui/MetavoxelEditorDialog.h +++ b/interface/src/ui/MetavoxelEditorDialog.h @@ -11,6 +11,8 @@ #include +#include "renderer/ProgramObject.h" + class QComboBox; class QDoubleSpinBox; class QGroupBox; @@ -42,6 +44,8 @@ private: QDoubleSpinBox* _gridSpacing; QDoubleSpinBox* _gridPosition; QGroupBox* _value; + + static ProgramObject _gridProgram; }; #endif /* defined(__interface__MetavoxelEditorDialog__) */ From 15021b6ebc9ee68d9ac41dd54fb564337ccfb863 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 22 Jan 2014 11:11:12 -0800 Subject: [PATCH 31/66] More work on metavoxel editing (track mouse ray intersection with grid). --- interface/src/Application.cpp | 60 ++++++++++------------ interface/src/Application.h | 23 +++++---- interface/src/ui/MetavoxelEditorDialog.cpp | 26 ++++++++-- 3 files changed, 62 insertions(+), 47 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2f7e5acc28..22c7ff5107 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1896,25 +1896,23 @@ const float HEAD_SPHERE_RADIUS = 0.07f; static QUuid DEFAULT_NODE_ID_REF; -void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition) { +void Application::updateLookatTargetAvatar(glm::vec3& eyePosition) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); if (!_mousePressed) { - _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); + _lookatTargetAvatar = findLookatTargetAvatar(eyePosition, DEFAULT_NODE_ID_REF); } } -Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) { +Avatar* Application::findLookatTargetAvatar(glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) { foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { Avatar* avatar = (Avatar*)node->getLinkedData(); float distance; - if (avatar->findRayIntersection(mouseRayOrigin, mouseRayDirection, distance)) { + if (avatar->findRayIntersection(_mouseRayOrigin, _mouseRayDirection, distance)) { // rescale to compensate for head embiggening eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); @@ -1965,7 +1963,7 @@ void Application::renderHighlightVoxel(VoxelDetail voxel) { glPopMatrix(); } -void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection) { +void Application::updateAvatars(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); @@ -1977,7 +1975,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: avatar->init(); } avatar->simulate(deltaTime, NULL); - avatar->setMouseRay(mouseRayOrigin, mouseRayDirection); + avatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection); } } @@ -2000,28 +1998,28 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: } } -void Application::updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection) { +void Application::updateMouseRay() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseRay()"); _viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(), - mouseRayOrigin, mouseRayDirection); + _mouseRayOrigin, _mouseRayDirection); // adjust for mirroring if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - glm::vec3 mouseRayOffset = mouseRayOrigin - _viewFrustum.getPosition(); - mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) + + glm::vec3 mouseRayOffset = _mouseRayOrigin - _viewFrustum.getPosition(); + _mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) + _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayOffset)); - mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayDirection) + - _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayDirection)); + _mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) + + _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), _mouseRayDirection)); } // tell my avatar if the mouse is being pressed... _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() { @@ -2038,8 +2036,7 @@ void Application::updateFaceshift() { } } -void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, - glm::vec3& lookAtRayDirection) { +void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); @@ -2061,7 +2058,7 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& } else { // Just look in direction of the mouse ray - lookAtSpot = lookAtRayOrigin + lookAtRayDirection * FAR_AWAY_STARE; + lookAtSpot = _mouseRayOrigin + _mouseRayDirection * FAR_AWAY_STARE; } } if (_faceshift.isActive()) { @@ -2076,8 +2073,7 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& _myAvatar.getHead().setLookAtPosition(lookAtSpot); } -void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face) { +void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels()"); @@ -2108,7 +2104,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, if (!(_voxels.treeIsBusy() || _mousePressed)) { { PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()"); - _isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face); + _isHoverVoxel = _voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _hoverVoxel, distance, face); } if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel && glm::vec4(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s) != oldVoxel) { @@ -2124,8 +2120,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, } } -void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face) { +void Application::updateMouseVoxels(float deltaTime, float& distance, BoxFace& face) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseVoxels()"); @@ -2137,7 +2132,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, fabs(_myAvatar.getVelocity().y) + fabs(_myAvatar.getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) { - if (_voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _mouseVoxel, distance, face)) { + if (_voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _mouseVoxel, distance, face)) { if (distance < MAX_VOXEL_EDIT_DISTANCE) { // set the voxel scale to that of the first moused-over voxel if (!wasInitialized) { @@ -2157,7 +2152,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3 faceVector = getFaceVector(face); if (_mouseVoxelScale < _mouseVoxel.s) { // find the closest contained voxel - glm::vec3 pt = (mouseRayOrigin + mouseRayDirection * distance) / (float)TREE_SCALE - + glm::vec3 pt = (_mouseRayOrigin + _mouseRayDirection * distance) / (float)TREE_SCALE - faceVector * (_mouseVoxelScale * 0.5f); _mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / _mouseVoxelScale); _mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / _mouseVoxelScale); @@ -2178,7 +2173,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, || Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { // place the voxel a fixed distance away float worldMouseVoxelScale = _mouseVoxelScale * TREE_SCALE; - glm::vec3 pt = mouseRayOrigin + mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f); + glm::vec3 pt = _mouseRayOrigin + _mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f); _mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / worldMouseVoxelScale); _mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / worldMouseVoxelScale); _mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / worldMouseVoxelScale); @@ -2412,29 +2407,28 @@ void Application::update(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::update()"); // check what's under the mouse and update the mouse voxel - glm::vec3 mouseRayOrigin, mouseRayDirection; - updateMouseRay(deltaTime, mouseRayOrigin, mouseRayDirection); + updateMouseRay(); // Set where I am looking based on my mouse ray (so that other people can see) glm::vec3 lookAtSpot; updateFaceshift(); - updateLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot); - updateMyAvatarLookAtPosition(lookAtSpot, mouseRayOrigin, mouseRayDirection); + updateLookatTargetAvatar(lookAtSpot); + updateMyAvatarLookAtPosition(lookAtSpot); // Find the voxel we are hovering over, and respond if clicked float distance; BoxFace face; - updateHoverVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // clicking on voxels and making sounds - updateMouseVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // UI/UX related to voxels + updateHoverVoxels(deltaTime, distance, face); // clicking on voxels and making sounds + updateMouseVoxels(deltaTime, distance, face); // UI/UX related to voxels updateHandAndTouch(deltaTime); // Update state for touch sensors 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 updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... - updateAvatars(deltaTime, mouseRayOrigin, mouseRayDirection); //loop through all the other avatars and simulate them... + updateAvatars(deltaTime); //loop through all the other avatars and simulate them... updateMyAvatarSimulation(deltaTime); // Simulate myself updateParticles(deltaTime); // Simulate particle cloud movements updateMetavoxels(deltaTime); // update metavoxels diff --git a/interface/src/Application.h b/interface/src/Application.h index 699fb3e0e3..9e7bbd9daf 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -155,6 +155,8 @@ public: VoxelTree* getClipboard() { return &_clipboard; } Environment* getEnvironment() { return &_environment; } bool isMouseHidden() const { return _mouseHidden; } + const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } + const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } Faceshift* getFaceshift() { return &_faceshift; } SixenseManager* getSixenseManager() { return &_sixenseManager; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } @@ -280,15 +282,12 @@ private: void update(float deltaTime); // Various helper functions called during update() - void updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection); + void updateMouseRay(); void updateFaceshift(); - void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, glm::vec3& lookAtRayDirection); - void updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face); - void updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face); - void updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition); + void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot); + void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face); + void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face); + void updateLookatTargetAvatar(glm::vec3& eyePosition); void updateHandAndTouch(float deltaTime); void updateLeap(float deltaTime); void updateSixense(float deltaTime); @@ -303,15 +302,14 @@ private: void updateAudio(float deltaTime); void updateCursor(float deltaTime); - Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition, QUuid &nodeUUID); + Avatar* findLookatTargetAvatar(glm::vec3& eyePosition, QUuid &nodeUUID); bool isLookingAtMyAvatar(Avatar* avatar); void renderLookatIndicator(glm::vec3 pointOfInterest); void renderHighlightVoxel(VoxelDetail voxel); void updateAvatar(float deltaTime); - void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection); + void updateAvatars(float deltaTime); void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); @@ -413,6 +411,9 @@ private: bool _mouseHidden; bool _seenMouseMove; + glm::vec3 _mouseRayOrigin; + glm::vec3 _mouseRayDirection; + float _touchAvgX; float _touchAvgY; float _lastTouchAvgX; diff --git a/interface/src/ui/MetavoxelEditorDialog.cpp b/interface/src/ui/MetavoxelEditorDialog.cpp index 73319cdc08..0862dfff87 100644 --- a/interface/src/ui/MetavoxelEditorDialog.cpp +++ b/interface/src/ui/MetavoxelEditorDialog.cpp @@ -164,12 +164,32 @@ void MetavoxelEditorDialog::render() { break; } + // find the intersection of the rotated mouse ray with the plane + glm::vec3 rayOrigin = rotation * Application::getInstance()->getMouseRayOrigin(); + glm::vec3 rayDirection = rotation * Application::getInstance()->getMouseRayDirection(); + float spacing = _gridSpacing->value(); + float position = _gridPosition->value(); + if (fabs(rayDirection.z) > EPSILON) { + float distance = (position - rayOrigin.z) / rayDirection.z; + glm::vec3 intersection = rayOrigin + rayDirection * distance; + + glLineWidth(4.0f); + glBegin(GL_LINE_LOOP); + float x = spacing * floorf(intersection.x / spacing); + float y = spacing * floorf(intersection.y / spacing); + glVertex3f(x, y, position); + glVertex3f(x + spacing, y, position); + glVertex3f(x + spacing, y + spacing, position); + glVertex3f(x, y + spacing, position); + glEnd(); + glLineWidth(1.0f); + } + // center the grid around the camera position on the plane glm::vec3 rotated = rotation * Application::getInstance()->getCamera()->getPosition(); - float spacing = _gridSpacing->value(); const int GRID_DIVISIONS = 300; - glTranslatef(spacing * (floor(rotated.x / spacing) - GRID_DIVISIONS / 2), - spacing * (floor(rotated.y / spacing) - GRID_DIVISIONS / 2), _gridPosition->value()); + glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position); float scale = GRID_DIVISIONS * spacing; glScalef(scale, scale, scale); From d068b668d8a4b08fab0d3c1c4d3d347ab5343d17 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 22 Jan 2014 12:48:10 -0800 Subject: [PATCH 32/66] add marker spheres at 1 meter away from origin for better avatar size testing --- interface/src/Application.cpp | 4 ++-- interface/src/Util.cpp | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b59893a10e..d4a3e7118d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2921,10 +2921,10 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (!selfAvatarOnly) { // draw a red sphere - float sphereRadius = 0.25f; + float originSphereRadius = 0.05f; glColor3f(1,0,0); glPushMatrix(); - glutSolidSphere(sphereRadius, 15, 15); + glutSolidSphere(originSphereRadius, 15, 15); glPopMatrix(); // disable specular lighting for ground and voxels diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index d557eb67b3..ffee4c7879 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -332,27 +332,29 @@ void renderWorldBox() { glVertex3f(TREE_SCALE, 0, TREE_SCALE); glVertex3f(TREE_SCALE, 0, 0); glEnd(); - // Draw marker dots at very end + // Draw meter markers along the 3 axis to help with measuring things + const float MARKER_DISTANCE = 1.f; + const float MARKER_RADIUS = 0.05f; glEnable(GL_LIGHTING); glPushMatrix(); - glTranslatef(TREE_SCALE, 0, 0); + glTranslatef(MARKER_DISTANCE, 0, 0); glColor3fv(red); - glutSolidSphere(0.125, 10, 10); + glutSolidSphere(MARKER_RADIUS, 10, 10); glPopMatrix(); glPushMatrix(); - glTranslatef(0, TREE_SCALE, 0); + glTranslatef(0, MARKER_DISTANCE, 0); glColor3fv(green); - glutSolidSphere(0.125, 10, 10); + glutSolidSphere(MARKER_RADIUS, 10, 10); glPopMatrix(); glPushMatrix(); - glTranslatef(0, 0, TREE_SCALE); + glTranslatef(0, 0, MARKER_DISTANCE); glColor3fv(blue); - glutSolidSphere(0.125, 10, 10); + glutSolidSphere(MARKER_RADIUS, 10, 10); glPopMatrix(); glPushMatrix(); glColor3fv(gray); - glTranslatef(TREE_SCALE, 0, TREE_SCALE); - glutSolidSphere(0.125, 10, 10); + glTranslatef(MARKER_DISTANCE, 0, MARKER_DISTANCE); + glutSolidSphere(MARKER_RADIUS, 10, 10); glPopMatrix(); } From 064f0cd3abd17ba5234b7228401d259d0a37212b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 22 Jan 2014 13:55:14 -0800 Subject: [PATCH 33/66] Basic drag-out behavior for metavoxel editing. --- interface/src/Application.cpp | 11 +- interface/src/ui/MetavoxelEditorDialog.cpp | 115 ++++++++++++++++++--- interface/src/ui/MetavoxelEditorDialog.h | 13 +++ 3 files changed, 118 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 22c7ff5107..2a6539bce3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1122,13 +1122,12 @@ void Application::mouseMoveEvent(QMouseEvent* event) { _seenMouseMove = true; } + int deltaX = event->x() - _mouseX; + int deltaY = event->y() - _mouseY; + _mouseX = event->x(); + _mouseY = event->y(); + if (activeWindow() == _window) { - int deltaX = event->x() - _mouseX; - int deltaY = event->y() - _mouseY; - - _mouseX = event->x(); - _mouseY = event->y(); - // orbit behavior if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) { if (_lookatTargetAvatar) { diff --git a/interface/src/ui/MetavoxelEditorDialog.cpp b/interface/src/ui/MetavoxelEditorDialog.cpp index 0862dfff87..7df1dfe680 100644 --- a/interface/src/ui/MetavoxelEditorDialog.cpp +++ b/interface/src/ui/MetavoxelEditorDialog.cpp @@ -24,6 +24,8 @@ enum GridPlane { GRID_PLANE_XY, GRID_PLANE_XZ, GRID_PLANE_YZ }; +const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX); + MetavoxelEditorDialog::MetavoxelEditorDialog() : QDialog(Application::getInstance()->getGLWidget()) { @@ -78,6 +80,10 @@ MetavoxelEditorDialog::MetavoxelEditorDialog() : connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); + Application::getInstance()->getGLWidget()->installEventFilter(this); + + resetState(); + show(); if (_gridProgram.isLinked()) { @@ -88,6 +94,32 @@ MetavoxelEditorDialog::MetavoxelEditorDialog() : _gridProgram.link(); } +bool MetavoxelEditorDialog::eventFilter(QObject* watched, QEvent* event) { + switch (_state) { + case HOVERING_STATE: + if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) { + _state = DRAGGING_STATE; + return true; + } + break; + + case DRAGGING_STATE: + if (event->type() == QEvent::MouseButtonRelease) { + _state = RAISING_STATE; + return true; + } + break; + + case RAISING_STATE: + if (event->type() == QEvent::MouseButtonPress) { + resetState(); + return true; + } + break; + } + return false; +} + void MetavoxelEditorDialog::updateValueEditor() { QString selected = getSelectedAttribute(); if (selected.isNull()) { @@ -145,9 +177,14 @@ void MetavoxelEditorDialog::updateGridPosition() { } void MetavoxelEditorDialog::render() { - const float GRID_BRIGHTNESS = 0.5f; - glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); + QString selected = getSelectedAttribute(); + if (selected.isNull()) { + resetState(); + return; + } + glDisable(GL_LIGHTING); + glDepthMask(GL_FALSE); glPushMatrix(); @@ -164,27 +201,67 @@ void MetavoxelEditorDialog::render() { break; } - // find the intersection of the rotated mouse ray with the plane + glm::vec3 rayOrigin = rotation * Application::getInstance()->getMouseRayOrigin(); glm::vec3 rayDirection = rotation * Application::getInstance()->getMouseRayDirection(); float spacing = _gridSpacing->value(); float position = _gridPosition->value(); - if (fabs(rayDirection.z) > EPSILON) { + if (_state == RAISING_STATE) { + // find the plane at the mouse position, orthogonal to the plane, facing the eye position + glLineWidth(4.0f); + glm::vec3 eyePosition = rotation * Application::getInstance()->getViewFrustum()->getOffsetPosition(); + glm::vec3 mousePoint = glm::vec3(_mousePosition, position); + glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), mousePoint - eyePosition); + glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f)); + float divisor = glm::dot(normal, rayDirection); + if (fabs(divisor) > EPSILON) { + float distance = (glm::dot(normal, mousePoint) - glm::dot(normal, rayOrigin)) / divisor; + float projection = rayOrigin.z + distance * rayDirection.z; + _height = spacing * roundf(projection / spacing); + } + } else if (fabs(rayDirection.z) > EPSILON) { + // find the intersection of the rotated mouse ray with the plane float distance = (position - rayOrigin.z) / rayDirection.z; - glm::vec3 intersection = rayOrigin + rayDirection * distance; + _mousePosition = glm::vec2(rayOrigin + rayDirection * distance); + glm::vec2 snappedPosition = spacing * glm::floor(_mousePosition / spacing); - glLineWidth(4.0f); - glBegin(GL_LINE_LOOP); - float x = spacing * floorf(intersection.x / spacing); - float y = spacing * floorf(intersection.y / spacing); - glVertex3f(x, y, position); - glVertex3f(x + spacing, y, position); - glVertex3f(x + spacing, y + spacing, position); - glVertex3f(x, y + spacing, position); - glEnd(); - glLineWidth(1.0f); + if (_state == HOVERING_STATE) { + _startPosition = _endPosition = snappedPosition; + glLineWidth(2.0f); + + } else if (_state == DRAGGING_STATE) { + _endPosition = snappedPosition; + glLineWidth(4.0f); + } + } else { + // cancel any operation in progress + resetState(); } + const float GRID_BRIGHTNESS = 0.5f; + if (_startPosition != INVALID_VECTOR) { + glm::vec2 minimum = glm::min(_startPosition, _endPosition); + glm::vec2 maximum = glm::max(_startPosition, _endPosition); + + glPushMatrix(); + glTranslatef(minimum.x, minimum.y, position); + glScalef(maximum.x + spacing - minimum.x, maximum.y + spacing - minimum.y, _height); + + glTranslatef(0.5f, 0.5f, 0.5f); + if (_state != HOVERING_STATE) { + const float BOX_ALPHA = 0.25f; + glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA); + glEnable(GL_CULL_FACE); + glutSolidCube(1.0); + glDisable(GL_CULL_FACE); + } + glutWireCube(1.0); + + glPopMatrix(); + } + + glLineWidth(1.0f); + // center the grid around the camera position on the plane glm::vec3 rotated = rotation * Application::getInstance()->getCamera()->getPosition(); const int GRID_DIVISIONS = 300; @@ -196,6 +273,7 @@ void MetavoxelEditorDialog::render() { _gridProgram.bind(); + glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); _gridProgram.release(); @@ -203,6 +281,7 @@ void MetavoxelEditorDialog::render() { glPopMatrix(); glEnable(GL_LIGHTING); + glDepthMask(GL_TRUE); } void MetavoxelEditorDialog::updateAttributes(const QString& select) { @@ -229,4 +308,10 @@ QString MetavoxelEditorDialog::getSelectedAttribute() const { return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); } +void MetavoxelEditorDialog::resetState() { + _state = HOVERING_STATE; + _startPosition = INVALID_VECTOR; + _height = 0.0f; +} + ProgramObject MetavoxelEditorDialog::_gridProgram; diff --git a/interface/src/ui/MetavoxelEditorDialog.h b/interface/src/ui/MetavoxelEditorDialog.h index 872cc50810..fcc413d6d6 100644 --- a/interface/src/ui/MetavoxelEditorDialog.h +++ b/interface/src/ui/MetavoxelEditorDialog.h @@ -26,6 +26,8 @@ public: MetavoxelEditorDialog(); + virtual bool eventFilter(QObject* watched, QEvent* event); + private slots: void updateValueEditor(); @@ -38,6 +40,7 @@ private: void updateAttributes(const QString& select = QString()); QString getSelectedAttribute() const; + void resetState(); QListWidget* _attributes; QComboBox* _gridPlane; @@ -45,6 +48,16 @@ private: QDoubleSpinBox* _gridPosition; QGroupBox* _value; + enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE }; + + State _state; + + glm::vec2 _mousePosition; ///< the position of the mouse in rotated space + + glm::vec2 _startPosition; ///< the first corner of the selection base + glm::vec2 _endPosition; ///< the second corner of the selection base + float _height; ///< the selection height + static ProgramObject _gridProgram; }; From f2cfd868a6d158ec7ff46d279cdeea23c9c006ae Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 22 Jan 2014 13:57:59 -0800 Subject: [PATCH 34/66] MetavoxelEditorDialog -> MetavoxelEditor. --- interface/src/Menu.cpp | 8 +++---- interface/src/Menu.h | 4 ++-- ...elEditorDialog.cpp => MetavoxelEditor.cpp} | 24 +++++++++---------- ...avoxelEditorDialog.h => MetavoxelEditor.h} | 12 +++++----- 4 files changed, 24 insertions(+), 24 deletions(-) rename interface/src/ui/{MetavoxelEditorDialog.cpp => MetavoxelEditor.cpp} (94%) rename interface/src/ui/{MetavoxelEditorDialog.h => MetavoxelEditor.h} (83%) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2e387dd7f4..cd94861333 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -32,7 +32,7 @@ #include "Menu.h" #include "Util.h" #include "InfoView.h" -#include "ui/MetavoxelEditorDialog.h" +#include "ui/MetavoxelEditor.h" Menu* Menu::_instance = NULL; @@ -1013,10 +1013,10 @@ void Menu::bandwidthDetails() { } void Menu::showMetavoxelEditor() { - if (!_metavoxelEditorDialog) { - _metavoxelEditorDialog = new MetavoxelEditorDialog(); + if (!_MetavoxelEditor) { + _MetavoxelEditor = new MetavoxelEditor(); } - _metavoxelEditorDialog->raise(); + _MetavoxelEditor->raise(); } void Menu::audioMuteToggled() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 22e92a76be..0b9d30ad96 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -38,7 +38,7 @@ class QSettings; class BandwidthDialog; class LodToolsDialog; -class MetavoxelEditorDialog; +class MetavoxelEditor; class VoxelStatsDialog; class Menu : public QMenuBar, public AbstractMenuInterface { @@ -143,7 +143,7 @@ private: FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; QActionGroup* _voxelModeActionsGroup; - QPointer _metavoxelEditorDialog; + QPointer _MetavoxelEditor; VoxelStatsDialog* _voxelStatsDialog; LodToolsDialog* _lodToolsDialog; int _maxVoxels; diff --git a/interface/src/ui/MetavoxelEditorDialog.cpp b/interface/src/ui/MetavoxelEditor.cpp similarity index 94% rename from interface/src/ui/MetavoxelEditorDialog.cpp rename to interface/src/ui/MetavoxelEditor.cpp index 7df1dfe680..d0d569cc70 100644 --- a/interface/src/ui/MetavoxelEditorDialog.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1,5 +1,5 @@ // -// MetavoxelEditorDialog.cpp +// MetavoxelEditor.cpp // interface // // Created by Andrzej Kapolka on 1/21/14. @@ -18,7 +18,7 @@ #include #include "Application.h" -#include "MetavoxelEditorDialog.h" +#include "MetavoxelEditor.h" enum GridPlane { GRID_PLANE_XY, GRID_PLANE_XZ, GRID_PLANE_YZ @@ -26,7 +26,7 @@ enum GridPlane { const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX); -MetavoxelEditorDialog::MetavoxelEditorDialog() : +MetavoxelEditor::MetavoxelEditor() : QDialog(Application::getInstance()->getGLWidget()) { setWindowTitle("Metavoxel Editor"); @@ -94,7 +94,7 @@ MetavoxelEditorDialog::MetavoxelEditorDialog() : _gridProgram.link(); } -bool MetavoxelEditorDialog::eventFilter(QObject* watched, QEvent* event) { +bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { switch (_state) { case HOVERING_STATE: if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) { @@ -120,7 +120,7 @@ bool MetavoxelEditorDialog::eventFilter(QObject* watched, QEvent* event) { return false; } -void MetavoxelEditorDialog::updateValueEditor() { +void MetavoxelEditor::updateValueEditor() { QString selected = getSelectedAttribute(); if (selected.isNull()) { _value->setVisible(false); @@ -139,7 +139,7 @@ void MetavoxelEditorDialog::updateValueEditor() { } } -void MetavoxelEditorDialog::createNewAttribute() { +void MetavoxelEditor::createNewAttribute() { QDialog dialog(this); dialog.setWindowTitle("New Attribute"); @@ -167,7 +167,7 @@ void MetavoxelEditorDialog::createNewAttribute() { updateAttributes(nameText); } -void MetavoxelEditorDialog::updateGridPosition() { +void MetavoxelEditor::updateGridPosition() { // make sure our grid position matches our grid spacing double step = _gridSpacing->value(); if (step > 0.0) { @@ -176,7 +176,7 @@ void MetavoxelEditorDialog::updateGridPosition() { } } -void MetavoxelEditorDialog::render() { +void MetavoxelEditor::render() { QString selected = getSelectedAttribute(); if (selected.isNull()) { resetState(); @@ -284,7 +284,7 @@ void MetavoxelEditorDialog::render() { glDepthMask(GL_TRUE); } -void MetavoxelEditorDialog::updateAttributes(const QString& select) { +void MetavoxelEditor::updateAttributes(const QString& select) { // remember the selection in order to preserve it QString selected = select.isNull() ? getSelectedAttribute() : select; _attributes->clear(); @@ -303,15 +303,15 @@ void MetavoxelEditorDialog::updateAttributes(const QString& select) { } } -QString MetavoxelEditorDialog::getSelectedAttribute() const { +QString MetavoxelEditor::getSelectedAttribute() const { QList selectedItems = _attributes->selectedItems(); return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); } -void MetavoxelEditorDialog::resetState() { +void MetavoxelEditor::resetState() { _state = HOVERING_STATE; _startPosition = INVALID_VECTOR; _height = 0.0f; } -ProgramObject MetavoxelEditorDialog::_gridProgram; +ProgramObject MetavoxelEditor::_gridProgram; diff --git a/interface/src/ui/MetavoxelEditorDialog.h b/interface/src/ui/MetavoxelEditor.h similarity index 83% rename from interface/src/ui/MetavoxelEditorDialog.h rename to interface/src/ui/MetavoxelEditor.h index fcc413d6d6..d183cce84b 100644 --- a/interface/src/ui/MetavoxelEditorDialog.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -1,13 +1,13 @@ // -// MetavoxelEditorDialog.h +// MetavoxelEditor.h // interface // // Created by Andrzej Kapolka on 1/21/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // -#ifndef __interface__MetavoxelEditorDialog__ -#define __interface__MetavoxelEditorDialog__ +#ifndef __interface__MetavoxelEditor__ +#define __interface__MetavoxelEditor__ #include @@ -19,12 +19,12 @@ class QGroupBox; class QListWidget; /// Allows editing metavoxels. -class MetavoxelEditorDialog : public QDialog { +class MetavoxelEditor : public QDialog { Q_OBJECT public: - MetavoxelEditorDialog(); + MetavoxelEditor(); virtual bool eventFilter(QObject* watched, QEvent* event); @@ -61,4 +61,4 @@ private: static ProgramObject _gridProgram; }; -#endif /* defined(__interface__MetavoxelEditorDialog__) */ +#endif /* defined(__interface__MetavoxelEditor__) */ From aabff3f141aa5e5c058f96b8aad38dfb45f00a5c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 22 Jan 2014 14:17:02 -0800 Subject: [PATCH 35/66] Adjust the fog density; getting different results on Linux vs. OS X. --- interface/resources/shaders/grid.frag | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/shaders/grid.frag b/interface/resources/shaders/grid.frag index c62241cc3e..b9e3baccd4 100644 --- a/interface/resources/shaders/grid.frag +++ b/interface/resources/shaders/grid.frag @@ -9,5 +9,7 @@ // void main(void) { - gl_FragColor = vec4(gl_Color.rgb, exp(-0.25 / gl_FragCoord.w)); + // use the standard exponential fog calculation + const float FOG_DENSITY = 0.5; + gl_FragColor = vec4(gl_Color.rgb, exp(-FOG_DENSITY / gl_FragCoord.w)); } From e545cfe4067cf8115ae7ec05a49f5178526d7659 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 22 Jan 2014 14:33:47 -0800 Subject: [PATCH 36/66] Fix for planes not at the origin. --- interface/src/ui/MetavoxelEditor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index d0d569cc70..42cde77f86 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -211,13 +211,13 @@ void MetavoxelEditor::render() { glLineWidth(4.0f); glm::vec3 eyePosition = rotation * Application::getInstance()->getViewFrustum()->getOffsetPosition(); glm::vec3 mousePoint = glm::vec3(_mousePosition, position); - glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), mousePoint - eyePosition); + glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), eyePosition - mousePoint); glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f)); float divisor = glm::dot(normal, rayDirection); if (fabs(divisor) > EPSILON) { float distance = (glm::dot(normal, mousePoint) - glm::dot(normal, rayOrigin)) / divisor; float projection = rayOrigin.z + distance * rayDirection.z; - _height = spacing * roundf(projection / spacing); + _height = spacing * roundf(projection / spacing) - position; } } else if (fabs(rayDirection.z) > EPSILON) { // find the intersection of the rotated mouse ray with the plane From e9e2557576714417f758f3b9c4e49dd59a3358f4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 22 Jan 2014 14:47:04 -0800 Subject: [PATCH 37/66] change node types of interest to a QSet --- animation-server/src/AnimationServer.cpp | 2 +- assignment-client/src/Agent.cpp | 6 ++--- assignment-client/src/audio/AudioMixer.cpp | 3 +-- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- interface/src/Application.cpp | 6 ++--- libraries/octree-server/src/OctreeServer.cpp | 3 +-- libraries/shared/src/Node.cpp | 2 -- libraries/shared/src/NodeList.cpp | 26 +++++++------------ libraries/shared/src/NodeList.h | 8 +++--- libraries/shared/src/NodeTypes.h | 1 - 10 files changed, 24 insertions(+), 35 deletions(-) diff --git a/animation-server/src/AnimationServer.cpp b/animation-server/src/AnimationServer.cpp index f684a6e672..ba07fac21a 100644 --- a/animation-server/src/AnimationServer.cpp +++ b/animation-server/src/AnimationServer.cpp @@ -807,7 +807,7 @@ AnimationServer::AnimationServer(int &argc, char **argv) : pthread_create(&::animateVoxelThread, NULL, animateVoxels, NULL); - NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1); + NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_VOXEL_SERVER); QTimer* domainServerTimer = new QTimer(this); connect(domainServerTimer, SIGNAL(timeout()), nodeList, SLOT(sendDomainServerCheckIn())); diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index d27845f337..bb7d7890cc 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -55,10 +55,8 @@ void Agent::run() { // XXXBHG - this seems less than ideal. There might be classes (like jurisdiction listeners, that need access to // other node types, but for them to get access to those node types, we have to add them here. It seems like // NodeList should support adding types of interest - const NODE_TYPE AGENT_NODE_TYPES_OF_INTEREST[] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, - NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER }; - - nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST)); + nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER + << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER); // figure out the URL for the script for this agent assignment QString scriptURLString("http://%1:8080/assignment/%2"); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index aa54c874ef..705a877a00 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -241,8 +241,7 @@ void AudioMixer::run() { NodeList* nodeList = NodeList::getInstance(); - const char AUDIO_MIXER_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_AGENT, NODE_TYPE_AUDIO_INJECTOR }; - nodeList->setNodeTypesOfInterest(AUDIO_MIXER_NODE_TYPES_OF_INTEREST, sizeof(AUDIO_MIXER_NODE_TYPES_OF_INTEREST)); + nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT); nodeList->linkedDataCreateCallback = attachNewBufferToNode; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 56464d2415..2f7c4a83d4 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -149,7 +149,7 @@ void AvatarMixer::run() { commonInit(AVATAR_MIXER_LOGGING_NAME, NODE_TYPE_AVATAR_MIXER); NodeList* nodeList = NodeList::getInstance(); - nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1); + nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT); nodeList->linkedDataCreateCallback = attachAvatarDataToNode; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8e1ba5afa1..3d8258e05c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -221,9 +221,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : #endif // tell the NodeList instance who to tell the domain server we care about - const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER, - NODE_TYPE_PARTICLE_SERVER, NODE_TYPE_METAVOXEL_SERVER}; - nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); + nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER + << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER + << NODE_TYPE_METAVOXEL_SERVER); QTimer* silentNodeTimer = new QTimer(this); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index 55bc77f1bf..9fe5cc8ca1 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -569,8 +569,7 @@ void OctreeServer::run() { nodeList->setOwnerType(getMyNodeType()); // we need to ask the DS about agents so we can ping/reply with them - const char nodeTypesOfInterest[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER}; - nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); + nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_AGENT << NODE_TYPE_ANIMATION_SERVER); setvbuf(stdout, NULL, _IOLBF, 0); diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index 271629ff10..fa8bf0ef8f 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -74,8 +74,6 @@ const char* Node::getTypeName() const { return NODE_TYPE_NAME_AUDIO_MIXER; case NODE_TYPE_AVATAR_MIXER: return NODE_TYPE_NAME_AVATAR_MIXER; - case NODE_TYPE_AUDIO_INJECTOR: - return NODE_TYPE_NAME_AUDIO_INJECTOR; case NODE_TYPE_ANIMATION_SERVER: return NODE_TYPE_NAME_ANIMATION_SERVER; case NODE_TYPE_UNASSIGNED: diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index f33137a092..99003820da 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -61,7 +61,7 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : _domainSockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), _nodeSocket(), _ownerType(newOwnerType), - _nodeTypesOfInterest(NULL), + _nodeTypesOfInterest(), _ownerUUID(QUuid::createUuid()), _numNoReplyDomainCheckIns(0), _assignmentServerSocket(), @@ -75,8 +75,6 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : NodeList::~NodeList() { - delete _nodeTypesOfInterest; - clear(); } @@ -293,19 +291,18 @@ void NodeList::reset() { clear(); _numNoReplyDomainCheckIns = 0; - delete _nodeTypesOfInterest; - _nodeTypesOfInterest = NULL; + _nodeTypesOfInterest.clear(); // refresh the owner UUID _ownerUUID = QUuid::createUuid(); } -void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest) { - delete _nodeTypesOfInterest; +void NodeList::addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd) { + _nodeTypesOfInterest << nodeTypeToAdd; +} - _nodeTypesOfInterest = new char[numNodeTypesOfInterest + sizeof(char)]; - memcpy(_nodeTypesOfInterest, nodeTypesOfInterest, numNodeTypesOfInterest); - _nodeTypesOfInterest[numNodeTypesOfInterest] = '\0'; +void NodeList::addSetOfNodeTypesToNodeInterestSet(const QSet& setOfNodeTypes) { + _nodeTypesOfInterest.unite(setOfNodeTypes); } const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442; @@ -522,7 +519,7 @@ void NodeList::sendDomainServerCheckIn() { sendSTUNRequest(); } else { // construct the DS check in packet if we need to - int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0; + int numBytesNodesOfInterest = _nodeTypesOfInterest.size(); const int IP_ADDRESS_BYTES = 4; @@ -563,11 +560,8 @@ void NodeList::sendDomainServerCheckIn() { *(packetPosition++) = numBytesNodesOfInterest; // copy over the bytes for node types of interest, if required - if (numBytesNodesOfInterest > 0) { - memcpy(packetPosition, - _nodeTypesOfInterest, - numBytesNodesOfInterest); - packetPosition += numBytesNodesOfInterest; + foreach (NODE_TYPE nodeTypeOfInterest, _nodeTypesOfInterest) { + *(packetPosition++) = nodeTypeOfInterest; } _nodeSocket.writeDatagram((char*) checkInPacket, packetPosition - checkInPacket, diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index b5e27564b1..eac125d889 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -22,6 +22,7 @@ #endif #include +#include #include #include #include @@ -89,8 +90,9 @@ public: void clear(); void reset(); - - void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest); + + void addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd); + void addSetOfNodeTypesToNodeInterestSet(const QSet& setOfNodeTypes); int processDomainServerList(unsigned char *packetData, size_t dataBytes); @@ -149,7 +151,7 @@ private: HifiSockAddr _domainSockAddr; QUdpSocket _nodeSocket; char _ownerType; - char* _nodeTypesOfInterest; + QSet _nodeTypesOfInterest; QUuid _ownerUUID; int _numNoReplyDomainCheckIns; HifiSockAddr _assignmentServerSocket; diff --git a/libraries/shared/src/NodeTypes.h b/libraries/shared/src/NodeTypes.h index 37e0503bab..6e8523c7d7 100644 --- a/libraries/shared/src/NodeTypes.h +++ b/libraries/shared/src/NodeTypes.h @@ -25,7 +25,6 @@ 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_AUDIO_INJECTOR = 'A'; const NODE_TYPE NODE_TYPE_ANIMATION_SERVER = 'a'; const NODE_TYPE NODE_TYPE_UNASSIGNED = 1; From ba8791274f759cd8eecdfb57846c949e32c4b35c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 22 Jan 2014 15:58:41 -0800 Subject: [PATCH 38/66] Remove test script for now, working on actual edits. --- interface/src/MetavoxelSystem.cpp | 10 ----- interface/src/MetavoxelSystem.h | 5 ++- interface/src/ui/MetavoxelEditor.cpp | 56 +++++++++++++++++++--------- interface/src/ui/MetavoxelEditor.h | 2 + 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b6938bfdba..0bcd6dc5c2 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -6,8 +6,6 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -#include -#include #include #include @@ -40,14 +38,6 @@ void MetavoxelSystem::init() { connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); - AttributeRegistry::getInstance()->configureScriptEngine(&_scriptEngine); - - QFile scriptFile("resources/scripts/sphere.js"); - scriptFile.open(QIODevice::ReadOnly); - QScriptValue guideFunction = _scriptEngine.evaluate(QTextStream(&scriptFile).readAll()); - _data.setAttributeValue(MetavoxelPath(), AttributeValue(AttributeRegistry::getInstance()->getGuideAttribute(), - encodeInline(PolymorphicDataPointer(new ScriptedMetavoxelGuide(guideFunction))))); - _buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); _buffer.create(); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index a58729285d..68813235a3 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -30,6 +29,7 @@ class MetavoxelSystem : public QObject { Q_OBJECT public: + MetavoxelSystem(); void init(); @@ -40,8 +40,10 @@ public: void render(); public slots: + void nodeAdded(SharedNodePointer node); void nodeKilled(SharedNodePointer node); + private: Q_INVOKABLE void addClient(const QUuid& uuid, const HifiSockAddr& address); @@ -67,7 +69,6 @@ private: static ProgramObject _program; static int _pointScaleLocation; - QScriptEngine _scriptEngine; MetavoxelData _data; QVector _points; PointVisitor _pointVisitor; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 42cde77f86..1659255e28 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -112,6 +112,19 @@ bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { case RAISING_STATE: if (event->type() == QEvent::MouseButtonPress) { + if (_height != 0) { + // find the start and end corners in X/Y + float base = _gridPosition->value(); + 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(); + glm::vec3 end = rotation * glm::vec3(glm::max(_startPosition, _endPosition) + + glm::vec2(spacing, spacing), glm::max(base, top)); + + // find the minimum and maximum extents after rotation + applyValue(glm::min(start, end), glm::max(start, end)); + } resetState(); return true; } @@ -188,28 +201,19 @@ void MetavoxelEditor::render() { glPushMatrix(); - glm::quat rotation; - switch (_gridPlane->currentIndex()) { - case GRID_PLANE_XZ: - rotation = glm::angleAxis(90.0f, 1.0f, 0.0f, 0.0f); - glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); - break; - - case GRID_PLANE_YZ: - rotation = glm::angleAxis(-90.0f, 0.0f, 1.0f, 0.0f); - glRotatef(90.0f, 0.0f, 1.0f, 0.0f); - break; - } + glm::quat rotation = getGridRotation(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); - - glm::vec3 rayOrigin = rotation * Application::getInstance()->getMouseRayOrigin(); - glm::vec3 rayDirection = rotation * Application::getInstance()->getMouseRayDirection(); + 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 position = _gridPosition->value(); if (_state == RAISING_STATE) { // find the plane at the mouse position, orthogonal to the plane, facing the eye position glLineWidth(4.0f); - glm::vec3 eyePosition = rotation * Application::getInstance()->getViewFrustum()->getOffsetPosition(); + glm::vec3 eyePosition = inverseRotation * Application::getInstance()->getViewFrustum()->getOffsetPosition(); glm::vec3 mousePoint = glm::vec3(_mousePosition, position); glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), eyePosition - mousePoint); glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f)); @@ -263,7 +267,7 @@ void MetavoxelEditor::render() { glLineWidth(1.0f); // center the grid around the camera position on the plane - glm::vec3 rotated = rotation * Application::getInstance()->getCamera()->getPosition(); + glm::vec3 rotated = inverseRotation * Application::getInstance()->getCamera()->getPosition(); const int GRID_DIVISIONS = 300; glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2), spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position); @@ -308,10 +312,28 @@ QString MetavoxelEditor::getSelectedAttribute() const { return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); } +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()) { + case GRID_PLANE_XY: + return glm::quat(); + + case GRID_PLANE_XZ: + return glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f); + + case GRID_PLANE_YZ: + return glm::angleAxis(90.0f, 0.0f, 1.0f, 0.0f); + } +} + void MetavoxelEditor::resetState() { _state = HOVERING_STATE; _startPosition = INVALID_VECTOR; _height = 0.0f; } +void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { + +} + ProgramObject MetavoxelEditor::_gridProgram; diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index d183cce84b..824887954f 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -40,7 +40,9 @@ private: void updateAttributes(const QString& select = QString()); QString getSelectedAttribute() const; + glm::quat getGridRotation() const; void resetState(); + void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); QListWidget* _attributes; QComboBox* _gridPlane; From 6979135fe3999f84700d469bb3ded291c1d1fc31 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 22 Jan 2014 16:23:24 -0800 Subject: [PATCH 39/66] move some of the node interest additions to correct spots --- assignment-client/src/Agent.cpp | 6 +----- libraries/octree-server/src/OctreeServer.cpp | 2 +- libraries/particles/src/ParticlesScriptingInterface.cpp | 1 + libraries/voxel-server/src/VoxelServer.cpp | 2 ++ libraries/voxels/src/VoxelsScriptingInterface.cpp | 4 ++++ libraries/voxels/src/VoxelsScriptingInterface.h | 2 ++ 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 3584dd9f5d..4f81c42046 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -52,11 +52,7 @@ void Agent::run() { NodeList* nodeList = NodeList::getInstance(); nodeList->setOwnerType(NODE_TYPE_AGENT); - // XXXBHG - this seems less than ideal. There might be classes (like jurisdiction listeners, that need access to - // other node types, but for them to get access to those node types, we have to add them here. It seems like - // NodeList should support adding types of interest - nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER - << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER); + nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER); // figure out the URL for the script for this agent assignment QString scriptURLString("http://%1:8080/assignment/%2"); diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index 64f19700a8..cc362f58a0 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -569,7 +569,7 @@ void OctreeServer::run() { nodeList->setOwnerType(getMyNodeType()); // we need to ask the DS about agents so we can ping/reply with them - nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_AGENT << NODE_TYPE_ANIMATION_SERVER); + nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT); setvbuf(stdout, NULL, _IOLBF, 0); diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index 14388b6ac4..d52ed5ba13 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -13,6 +13,7 @@ ParticlesScriptingInterface::ParticlesScriptingInterface() : _nextCreatorTokenID(0), _particleTree(NULL) { + NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_PARTICLE_SERVER); } diff --git a/libraries/voxel-server/src/VoxelServer.cpp b/libraries/voxel-server/src/VoxelServer.cpp index a1f5eaf55e..c8aee246e3 100644 --- a/libraries/voxel-server/src/VoxelServer.cpp +++ b/libraries/voxel-server/src/VoxelServer.cpp @@ -68,4 +68,6 @@ void VoxelServer::beforeRun() { qDebug("Using Minimal Environment=%s", debug::valueOf(_sendMinimalEnvironment)); } qDebug("Sending environments=%s", debug::valueOf(_sendEnvironments)); + + NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_ANIMATION_SERVER); } diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index 3f4e19f60a..40c6ff8d1d 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -8,6 +8,10 @@ #include "VoxelsScriptingInterface.h" +VoxelsScriptingInterface::VoxelsScriptingInterface() { + NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_VOXEL_SERVER); +} + void VoxelsScriptingInterface::queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails) { getVoxelPacketSender()->queueVoxelEditMessages(addPacketType, 1, &addVoxelDetails); } diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h index a1f52fc1c3..500a0cbc5d 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -20,6 +20,8 @@ class VoxelsScriptingInterface : public OctreeScriptingInterface { Q_OBJECT public: + VoxelsScriptingInterface(); + VoxelEditPacketSender* getVoxelPacketSender() { return (VoxelEditPacketSender*)getPacketSender(); } virtual NODE_TYPE getServerNodeType() const { return NODE_TYPE_VOXEL_SERVER; } From b965d850d5f47651a5890cbfc8b6dc7aef57422a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 22 Jan 2014 16:26:28 -0800 Subject: [PATCH 40/66] move node interest add for JL to JL --- libraries/octree/src/JurisdictionListener.cpp | 3 +++ libraries/particles/src/ParticlesScriptingInterface.cpp | 1 - libraries/voxels/src/VoxelsScriptingInterface.cpp | 4 ---- libraries/voxels/src/VoxelsScriptingInterface.h | 4 +--- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index 6ed039f0bd..d8686debad 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -23,6 +23,9 @@ JurisdictionListener::JurisdictionListener(NODE_TYPE type, PacketSenderNotify* n connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &JurisdictionListener::nodeKilled); //qDebug("JurisdictionListener::JurisdictionListener(NODE_TYPE type=%c)", type); + + // tell our NodeList we want to hear about nodes with our node type + NodeList::getInstance()->addNodeTypeToInterestSet(type); } void JurisdictionListener::nodeKilled(SharedNodePointer node) { diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index d52ed5ba13..14388b6ac4 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -13,7 +13,6 @@ ParticlesScriptingInterface::ParticlesScriptingInterface() : _nextCreatorTokenID(0), _particleTree(NULL) { - NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_PARTICLE_SERVER); } diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index 40c6ff8d1d..3f4e19f60a 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -8,10 +8,6 @@ #include "VoxelsScriptingInterface.h" -VoxelsScriptingInterface::VoxelsScriptingInterface() { - NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_VOXEL_SERVER); -} - void VoxelsScriptingInterface::queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails) { getVoxelPacketSender()->queueVoxelEditMessages(addPacketType, 1, &addVoxelDetails); } diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h index 500a0cbc5d..97bdfb2c59 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -19,9 +19,7 @@ /// handles scripting of voxel commands from JS passed to assigned clients class VoxelsScriptingInterface : public OctreeScriptingInterface { Q_OBJECT -public: - VoxelsScriptingInterface(); - +public: VoxelEditPacketSender* getVoxelPacketSender() { return (VoxelEditPacketSender*)getPacketSender(); } virtual NODE_TYPE getServerNodeType() const { return NODE_TYPE_VOXEL_SERVER; } From 4c07d61257f65ad1b14902e01e8dea25d8c8bc78 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 22 Jan 2014 16:34:48 -0800 Subject: [PATCH 41/66] Get value from editor. --- interface/src/ui/MetavoxelEditor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 1659255e28..18d438cf61 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -333,7 +334,8 @@ void MetavoxelEditor::resetState() { } void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { - + QWidget* editor = _value->layout()->itemAt(0)->widget(); + QVariant value = editor->metaObject()->userProperty().read(editor); } ProgramObject MetavoxelEditor::_gridProgram; From a3ffb1d8a32a835c8a061c6a3733e5c3782c2497 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 22 Jan 2014 19:17:48 -0800 Subject: [PATCH 42/66] Working on performing the actual edit. --- interface/src/Application.h | 1 + interface/src/MetavoxelSystem.cpp | 2 +- interface/src/MetavoxelSystem.h | 4 +- interface/src/ui/MetavoxelEditor.cpp | 54 ++++++++++++++++++- .../metavoxels/src/AttributeRegistry.cpp | 20 +++++-- libraries/metavoxels/src/AttributeRegistry.h | 14 ++++- libraries/metavoxels/src/MetavoxelData.cpp | 35 +++++++----- libraries/metavoxels/src/MetavoxelData.h | 7 +-- 8 files changed, 113 insertions(+), 24 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 59827b17b2..852f863754 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -145,6 +145,7 @@ public: ViewFrustum* getViewFrustum() { return &_viewFrustum; } VoxelSystem* getVoxels() { return &_voxels; } ParticleTreeRenderer* getParticles() { return &_particles; } + MetavoxelSystem* getMetavoxels() { return &_metavoxels; } VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; } VoxelTree* getClipboard() { return &_clipboard; } Environment* getEnvironment() { return &_environment; } diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 0bcd6dc5c2..258db6da00 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -151,7 +151,7 @@ MetavoxelSystem::PointVisitor::PointVisitor(QVector& points) : _points(points) { } -bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) { +bool MetavoxelSystem::PointVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return true; } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 68813235a3..708b4d0839 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -34,6 +34,8 @@ public: void init(); + MetavoxelData& getData() { return _data; } + void processData(const QByteArray& data, const HifiSockAddr& sender); void simulate(float deltaTime); @@ -60,7 +62,7 @@ private: class PointVisitor : public MetavoxelVisitor { public: PointVisitor(QVector& points); - virtual bool visit(const MetavoxelInfo& info); + virtual bool visit(MetavoxelInfo& info); private: QVector& _points; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 18d438cf61..bcc89fc6d9 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -333,9 +333,61 @@ 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; + } QWidget* editor = _value->layout()->itemAt(0)->widget(); - QVariant value = editor->metaObject()->userProperty().read(editor); + OwnedAttributeValue value(attribute, attribute->createFromVariant(editor->metaObject()->userProperty().read(editor))); + + Applier applier(minimum, maximum, _gridSpacing->value(), value); + Application::getInstance()->getMetavoxels()->getData().guide(applier); } ProgramObject MetavoxelEditor::_gridProgram; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index accf9d81cf..0814048719 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -72,12 +72,12 @@ bool AttributeValue::operator==(void* other) const { return _attribute && _attribute->equal(_value, other); } -OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) : - AttributeValue(attribute, attribute ? attribute->create() : NULL) { +OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) : + AttributeValue(attribute, value) { } -OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) : - AttributeValue(attribute, attribute ? attribute->create(value) : NULL) { +OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) : + AttributeValue(attribute, attribute ? attribute->create() : NULL) { } OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) : @@ -95,7 +95,7 @@ OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) _attribute->destroy(_value); } if ((_attribute = other.getAttribute())) { - _value = _attribute->create(other.getValue()); + _value = other.copy(); } return *this; } @@ -135,6 +135,16 @@ void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* return encodeInline((QRgb)value.toUInt32()); } +void* QRgbAttribute::createFromVariant(const QVariant& value) const { + switch (value.userType()) { + case QMetaType::QColor: + return encodeInline(value.value().rgba()); + + default: + return encodeInline((QRgb)value.toUInt()); + } +} + QWidget* QRgbAttribute::createEditor(QWidget* parent) const { QRgbEditor* editor = new QRgbEditor(parent); editor->setColor(_defaultValue); diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 3501c289c6..6e5e53d657 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -118,11 +118,19 @@ protected: class OwnedAttributeValue : public AttributeValue { public: - OwnedAttributeValue(const AttributePointer& attribute = AttributePointer()); + /// Assumes ownership of the specified value. It will be destroyed when this is destroyed or reassigned. OwnedAttributeValue(const AttributePointer& attribute, void* value); + + /// Creates an owned attribute with a copy of the specified attribute's default value. + OwnedAttributeValue(const AttributePointer& attribute = AttributePointer()); + + /// Creates an owned attribute with a copy of the specified other value. OwnedAttributeValue(const AttributeValue& other); + + /// Destroys the current value, if any. ~OwnedAttributeValue(); + /// Destroys the current value, if any, and copies the specified other value. OwnedAttributeValue& operator=(const AttributeValue& other); }; @@ -159,6 +167,8 @@ public: virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); } + virtual void* createFromVariant(const QVariant& value) const { return create(); } + /// Creates a widget to use to edit values of this attribute, or returns NULL if the attribute isn't editable. /// The widget should have a single "user" property that will be used to get/set the value. virtual QWidget* createEditor(QWidget* parent = NULL) const { return NULL; } @@ -232,6 +242,8 @@ public: virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; + virtual void* createFromVariant(const QVariant& value) const; + virtual QWidget* createEditor(QWidget* parent = NULL) const; }; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 6d10af2cf1..628dc21152 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -35,16 +35,21 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) { const QVector& inputs = visitor.getInputs(); const QVector& outputs = visitor.getOutputs(); MetavoxelVisitation firstVisitation = { visitor, QVector(inputs.size() + 1), - { glm::vec3(), TOP_LEVEL_SIZE, QVector(inputs.size() + 1), QVector(outputs.size()) } }; + QVector(outputs.size()), { glm::vec3(), TOP_LEVEL_SIZE, + QVector(inputs.size() + 1), QVector(outputs.size()) } }; for (int i = 0; i < inputs.size(); i++) { - MetavoxelNode* node = _roots.value(inputs[i]); - firstVisitation.nodes[i] = node; + MetavoxelNode* node = _roots.value(inputs.at(i)); + firstVisitation.inputNodes[i] = node; firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(inputs[i]) : inputs[i]; } AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute(); MetavoxelNode* node = _roots.value(guideAttribute); - firstVisitation.nodes.last() = node; + firstVisitation.inputNodes.last() = node; firstVisitation.info.inputValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; + for (int i = 0; i < outputs.size(); i++) { + MetavoxelNode* node = _roots.value(outputs.at(i)); + firstVisitation.outputNodes[i] = node; + } static_cast(firstVisitation.info.inputValues.last().getInlineValue< PolymorphicDataPointer>().data())->guide(firstVisitation); } @@ -384,20 +389,26 @@ const int Y_MAXIMUM_FLAG = 2; const int Z_MAXIMUM_FLAG = 4; void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - visitation.info.isLeaf = visitation.allNodesLeaves(); + visitation.info.isLeaf = visitation.allInputNodesLeaves(); if (!visitation.visitor.visit(visitation.info)) { return; } - MetavoxelVisitation nextVisitation = { visitation.visitor, QVector(visitation.nodes.size()), - { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.nodes.size()) } }; + MetavoxelVisitation nextVisitation = { 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()) } }; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - for (int j = 0; j < visitation.nodes.size(); j++) { - MetavoxelNode* node = visitation.nodes.at(j); + for (int j = 0; j < visitation.inputNodes.size(); j++) { + MetavoxelNode* node = visitation.inputNodes.at(j); MetavoxelNode* child = node ? node->getChild(i) : NULL; - nextVisitation.info.inputValues[j] = ((nextVisitation.nodes[j] = child)) ? + nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? child->getAttributeValue(visitation.info.inputValues[j].getAttribute()) : visitation.info.inputValues[j]; } + for (int j = 0; j < visitation.outputNodes.size(); j++) { + MetavoxelNode* node = visitation.outputNodes.at(j); + MetavoxelNode* child = node ? node->getChild(i) : NULL; + nextVisitation.outputNodes[j] = child; + } nextVisitation.info.minimum = visitation.info.minimum + glm::vec3( (i & X_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, (i & Y_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, @@ -505,8 +516,8 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } } -bool MetavoxelVisitation::allNodesLeaves() const { - foreach (MetavoxelNode* node, nodes) { +bool MetavoxelVisitation::allInputNodesLeaves() const { + foreach (MetavoxelNode* node, inputNodes) { if (node != NULL && !node->isLeaf()) { return false; } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index a129930e93..71bcb20551 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -159,7 +159,7 @@ public: /// Visits a metavoxel. /// \param info the metavoxel data /// \return if true, continue descending; if false, stop - virtual bool visit(const MetavoxelInfo& info) = 0; + virtual bool visit(MetavoxelInfo& info) = 0; protected: @@ -221,10 +221,11 @@ class MetavoxelVisitation { public: MetavoxelVisitor& visitor; - QVector nodes; + QVector inputNodes; + QVector outputNodes; MetavoxelInfo info; - bool allNodesLeaves() const; + bool allInputNodesLeaves() const; }; #endif /* defined(__interface__MetavoxelData__) */ From 3d0d5f130aac4abff587dad52a9cff7571cfde90 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 23 Jan 2014 10:02:04 -0800 Subject: [PATCH 43/66] fix particle-server spinning out at 100% cpu% --- libraries/octree/src/Octree.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index dad97b18cd..808b49d475 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1390,9 +1390,8 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* node) { bytesWritten = encodeTreeBitstream(subTree, &packetData, nodeBag, params); unlock(); - // if bytesWritten == 0, then it means that the subTree couldn't fit, and so we should reset the packet - // and reinsert the node in our bag and try again... - if (bytesWritten == 0) { + // if the subTree couldn't fit, and so we should reset the packet and reinsert the node in our bag and try again... + if (bytesWritten == 0 && (params.stopReason == EncodeBitstreamParams::DIDNT_FIT)) { if (packetData.hasContent()) { file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize()); lastPacketWritten = true; From 7efc9e2e78427732fc84966ad4079a32ccbdd5ac Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Jan 2014 10:32:23 -0800 Subject: [PATCH 44/66] move datagram processing back to separate thread --- interface/src/Application.cpp | 126 +++++++--------------------- interface/src/Application.h | 10 +-- interface/src/Audio.cpp | 2 +- interface/src/DatagramProcessor.cpp | 110 ++++++++++++++++++++++++ interface/src/DatagramProcessor.h | 31 +++++++ libraries/shared/src/NodeList.cpp | 2 +- 6 files changed, 176 insertions(+), 105 deletions(-) create mode 100644 interface/src/DatagramProcessor.cpp create mode 100644 interface/src/DatagramProcessor.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3d206e4e73..cf865ce299 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -108,6 +108,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : QApplication(argc, argv), _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), + _nodeThread(new QThread(this)), + _datagramProcessor(new DatagramProcessor()), _frameCount(0), _fps(120.0f), _justStarted(true), @@ -145,10 +147,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _voxelHideShowThread(&_voxels), _voxelEditSender(this), _particleEditSender(this), - _packetCount(0), _packetsPerSecond(0), _bytesPerSecond(0), - _bytesCount(0), _recentMaxPackets(0), _resetRecentMaxPacketsSoon(true), _swatch(NULL), @@ -173,11 +173,21 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : if (portStr) { listenPort = atoi(portStr); } - + + // put the NodeList and datagram processing on the node thread NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort); - - // connect our processDatagrams slot to the QUDPSocket readyRead() signal - connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(processDatagrams())); + + nodeList->moveToThread(_nodeThread); + _datagramProcessor->moveToThread(_nodeThread); + + // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal + connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), _datagramProcessor, SLOT(processDatagrams())); + + // make sure the node thread is given highest priority + _nodeThread->setPriority(QThread::TimeCriticalPriority); + + // start the nodeThread so its event loop is running + _nodeThread->start(); // put the audio processing on a separate thread QThread* audioThread = new QThread(this); @@ -192,7 +202,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); - + // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat); @@ -225,8 +235,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : NODE_TYPE_PARTICLE_SERVER, NODE_TYPE_METAVOXEL_SERVER}; nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); + // move the silentNodeTimer to the _nodeThread QTimer* silentNodeTimer = new QTimer(this); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); + silentNodeTimer->moveToThread(_nodeThread); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); @@ -273,6 +285,10 @@ Application::~Application() { // make sure we don't call the idle timer any more delete idleTimer; + + // ask the datagram processing thread to quit and wait until it is done + _nodeThread->thread()->quit(); + _nodeThread->thread()->wait(); // ask the audio thread to quit and wait until it is done _audio.thread()->quit(); @@ -1312,11 +1328,12 @@ void Application::timer() { } _fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); - _packetsPerSecond = (float)_packetCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); - _bytesPerSecond = (float)_bytesCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); + + _packetsPerSecond = (float) _datagramProcessor->getPacketCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); + _bytesPerSecond = (float) _datagramProcessor->getByteCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _frameCount = 0; - _packetCount = 0; - _bytesCount = 0; + + _datagramProcessor->resetCounters(); gettimeofday(&_timerStart, NULL); @@ -4052,93 +4069,6 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen return statsMessageLength; } -// Receive packets from other nodes/servers and decide what to do with them! -void Application::processDatagrams() { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::networkReceive()"); - - HifiSockAddr senderSockAddr; - ssize_t bytesReceived; - - while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() && - (bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) _incomingPacket, - MAX_PACKET_SIZE, - senderSockAddr.getAddressPointer(), - senderSockAddr.getPortPointer()))) { - - _packetCount++; - _bytesCount += bytesReceived; - - 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: - // V2 = IOS transmitter app - _myTransmitter.processIncomingData(_incomingPacket, bytesReceived); - - break; - case PACKET_TYPE_MIXED_AUDIO: - QMetaObject::invokeMethod(&_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection, - Q_ARG(QByteArray, QByteArray((char*) _incomingPacket, bytesReceived))); - break; - - case PACKET_TYPE_PARTICLE_ADD_RESPONSE: - // this will keep creatorTokenIDs to IDs mapped correctly - Particle::handleAddParticleResponse(_incomingPacket, bytesReceived); - 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: { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); - - bool wantExtraDebugging = getLogger()->extraDebugging(); - if (wantExtraDebugging && _incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) { - int numBytesPacketHeader = numBytesForPacketHeader(_incomingPacket); - unsigned char* dataAt = _incomingPacket + numBytesPacketHeader; - dataAt += sizeof(VOXEL_PACKET_FLAGS); - VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt); - dataAt += sizeof(VOXEL_PACKET_SEQUENCE); - VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt); - dataAt += sizeof(VOXEL_PACKET_SENT_TIME); - VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); - int flightTime = arrivedAt - sentAt; - - printf("got PACKET_TYPE_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 - _voxelProcessor.queueReceivedPacket(senderSockAddr, _incomingPacket, bytesReceived); - break; - } - case PACKET_TYPE_METAVOXEL_DATA: - _metavoxels.processData(QByteArray((const char*) _incomingPacket, bytesReceived), - senderSockAddr); - break; - case PACKET_TYPE_BULK_AVATAR_DATA: - NodeList::getInstance()->processBulkNodeData(senderSockAddr, - _incomingPacket, - bytesReceived); - getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived); - 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); - break; - default: - NodeList::getInstance()->processNodeData(senderSockAddr, _incomingPacket, bytesReceived); - break; - } - } - } -} - void Application::packetSentNotification(ssize_t length) { _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 6be01db39f..514591e1df 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -33,6 +33,7 @@ #include "BandwidthMeter.h" #include "Camera.h" #include "Cloud.h" +#include "DatagramProcessor.h" #include "Environment.h" #include "GLCanvas.h" #include "MetavoxelSystem.h" @@ -96,6 +97,7 @@ class Application : public QApplication, public PacketSenderNotify { friend class VoxelPacketProcessor; friend class VoxelEditPacketSender; + friend class DatagramProcessor; public: static Application* getInstance() { return static_cast(QCoreApplication::instance()); } @@ -208,8 +210,6 @@ public slots: void domainChanged(const QString& domainHostname); void nodeKilled(SharedNodePointer node); - void processDatagrams(); - void exportVoxels(); void importVoxels(); void cutVoxels(); @@ -331,6 +331,9 @@ private: QGLWidget* _glWidget; BandwidthMeter _bandwidthMeter; + + QThread* _nodeThread; + DatagramProcessor* _datagramProcessor; QNetworkAccessManager* _networkAccessManager; QSettings* _settings; @@ -461,11 +464,8 @@ private: VoxelEditPacketSender _voxelEditSender; ParticleEditPacketSender _particleEditSender; - unsigned char _incomingPacket[MAX_PACKET_SIZE]; - int _packetCount; int _packetsPerSecond; int _bytesPerSecond; - int _bytesCount; int _recentMaxPackets; // recent max incoming voxel packets to process bool _resetRecentMaxPacketsSoon; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 0709b40624..f31ab9056f 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -415,7 +415,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { _totalPacketsReceived++; double timeDiff = diffclock(&_lastReceiveTime, ¤tReceiveTime); - + // Discard first few received packets for computing jitter (often they pile up on start) if (_totalPacketsReceived > NUM_INITIAL_PACKETS_DISCARD) { _stdev.addValue(timeDiff); diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp new file mode 100644 index 0000000000..9a6c1bea05 --- /dev/null +++ b/interface/src/DatagramProcessor.cpp @@ -0,0 +1,110 @@ +// +// DatagramProcessor.cpp +// hifi +// +// Created by Stephen Birarda on 1/23/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include + +#include "Application.h" +#include "Menu.h" + +#include "DatagramProcessor.h" + +DatagramProcessor::DatagramProcessor(QObject* parent) : + QObject(parent) +{ + +} + +void DatagramProcessor::processDatagrams() { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "DatagramProcessor::processDatagrams()"); + + HifiSockAddr senderSockAddr; + ssize_t bytesReceived; + + static unsigned char incomingPacket[MAX_PACKET_SIZE]; + + Application* application = Application::getInstance(); + + while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() && + (bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) incomingPacket, + MAX_PACKET_SIZE, + senderSockAddr.getAddressPointer(), + senderSockAddr.getPortPointer()))) { + + _packetCount++; + _byteCount += bytesReceived; + + 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: + // V2 = IOS transmitter app + application->_myTransmitter.processIncomingData(incomingPacket, bytesReceived); + + break; + case PACKET_TYPE_MIXED_AUDIO: + QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection, + Q_ARG(QByteArray, QByteArray((char*) incomingPacket, bytesReceived))); + break; + + case PACKET_TYPE_PARTICLE_ADD_RESPONSE: + // this will keep creatorTokenIDs to IDs mapped correctly + Particle::handleAddParticleResponse(incomingPacket, bytesReceived); + 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: { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); + + bool wantExtraDebugging = application->getLogger()->extraDebugging(); + if (wantExtraDebugging && incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) { + int numBytesPacketHeader = numBytesForPacketHeader(incomingPacket); + unsigned char* dataAt = incomingPacket + numBytesPacketHeader; + dataAt += sizeof(VOXEL_PACKET_FLAGS); + VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt); + dataAt += sizeof(VOXEL_PACKET_SEQUENCE); + VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt); + dataAt += sizeof(VOXEL_PACKET_SENT_TIME); + VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); + int flightTime = arrivedAt - sentAt; + + printf("got PACKET_TYPE_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); + break; + } + case PACKET_TYPE_METAVOXEL_DATA: + application->_metavoxels.processData(QByteArray((const char*) incomingPacket, bytesReceived), + senderSockAddr); + break; + case PACKET_TYPE_BULK_AVATAR_DATA: + NodeList::getInstance()->processBulkNodeData(senderSockAddr, + incomingPacket, + bytesReceived); + application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived); + 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); + break; + default: + NodeList::getInstance()->processNodeData(senderSockAddr, incomingPacket, bytesReceived); + break; + } + } + } +} \ No newline at end of file diff --git a/interface/src/DatagramProcessor.h b/interface/src/DatagramProcessor.h new file mode 100644 index 0000000000..722e5a9d41 --- /dev/null +++ b/interface/src/DatagramProcessor.h @@ -0,0 +1,31 @@ +// +// DatagramProcessor.h +// hifi +// +// Created by Stephen Birarda on 1/23/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__DatagramProcessor__ +#define __hifi__DatagramProcessor__ + +#include + +class DatagramProcessor : public QObject { + Q_OBJECT +public: + DatagramProcessor(QObject* parent = 0); + + int getPacketCount() const { return _packetCount; } + int getByteCount() const { return _byteCount; } + + void resetCounters() { _packetCount = 0; _byteCount = 0; } +public slots: + void processDatagrams(); + +private: + int _packetCount; + int _byteCount; +}; + +#endif /* defined(__hifi__DatagramProcessor__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index f37f2b48dc..034c272536 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -59,7 +59,7 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : _nodeHashMutex(), _domainHostname(DEFAULT_DOMAIN_HOSTNAME), _domainSockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), - _nodeSocket(), + _nodeSocket(this), _ownerType(newOwnerType), _nodeTypesOfInterest(NULL), _ownerUUID(QUuid::createUuid()), From ccc30d95fb1f5b0f4b0cf52ec15a4f78983a026a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 23 Jan 2014 10:32:42 -0800 Subject: [PATCH 45/66] fix crashing particle server bug --- libraries/particles/src/ParticleTree.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index a3c8072b3a..31f5216462 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -390,17 +390,20 @@ 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()"; _recentlyDeletedParticlesLock.lockForWrite(); - QMultiMap::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin(); - while (iterator != _recentlyDeletedParticleIDs.constEnd()) { + QMultiMap::iterator iterator = _recentlyDeletedParticleIDs.begin(); + while (iterator != _recentlyDeletedParticleIDs.end()) { //qDebug() << "considering... time/key:" << iterator.key(); if (iterator.key() <= sinceTime) { //qDebug() << "YES older... time/key:" << iterator.key(); _recentlyDeletedParticleIDs.remove(iterator.key()); } + //qDebug() << "about to ++iterator"; ++iterator; } _recentlyDeletedParticlesLock.unlock(); + //qDebug() << "DONE forgetParticlesDeletedBefore()"; } From 04286e39e68975819b1b4a68cda8e0d57c1c411f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Jan 2014 10:49:51 -0800 Subject: [PATCH 46/66] use NodeList getNodeHash getter for methods that can be called from other threads --- libraries/shared/src/NodeList.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 034c272536..65993cb275 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -257,7 +257,7 @@ int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, SharedNodePointer NodeList::nodeWithAddress(const HifiSockAddr &senderSockAddr) { // naively returns the first node that has a matching active HifiSockAddr // note that there can be multiple nodes that have a matching active socket, so this isn't a good way to uniquely identify - foreach (const SharedNodePointer& node, _nodeHash) { + foreach (const SharedNodePointer& node, getNodeHash()) { if (node->getActiveSocket() && *node->getActiveSocket() == senderSockAddr) { return node; } @@ -738,7 +738,7 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) { unsigned n = 0; - foreach (const SharedNodePointer& node, _nodeHash) { + foreach (const SharedNodePointer& node, getNodeHash()) { // only send to the NodeTypes we are asked to send to. if (memchr(nodeTypes, node->getType(), numNodeTypes)) { if (getNodeActiveSocketOrPing(node.data())) { From fa3c3448f4689d6b6bf61e205e559c8e0c95bb69 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 23 Jan 2014 10:55:51 -0800 Subject: [PATCH 47/66] Adding option to disable hand collisions against their own avatar. --- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/avatar/Hand.cpp | 18 ++++++++++-------- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 1723e10f61..740effcf1c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -352,6 +352,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::VoxelDrumming, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false); addDisabledActionAndSeparator(developerMenu, "Testing"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b356f29a85..dd3d5b7588 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -188,6 +188,7 @@ namespace MenuOption { const QString ExportVoxels = "Export Voxels"; const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes"; const QString HeadMouse = "Head Mouse"; + const QString HandsCollideWithSelf = "Collide With Self"; const QString FaceshiftTCP = "Faceshift (TCP)"; const QString FalseColorByDistance = "FALSE Color By Distance"; const QString FalseColorBySource = "FALSE Color By Source"; diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 51f1bc10de..e5aa33ad40 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -231,14 +231,16 @@ void Hand::updateCollisions() { } } - // and the current avatar (ignoring everything below the parent of the parent of the last free joint) - glm::vec3 owningPenetration; - const Model& skeletonModel = _owningAvatar->getSkeletonModel(); - int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex( - skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() : - (i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1))); - if (_owningAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, owningPenetration, skipIndex)) { - totalPenetration = addPenetrations(totalPenetration, owningPenetration); + if (Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) { + // and the current avatar (ignoring everything below the parent of the parent of the last free joint) + glm::vec3 owningPenetration; + const Model& skeletonModel = _owningAvatar->getSkeletonModel(); + int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex( + skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() : + (i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1))); + if (_owningAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, owningPenetration, skipIndex)) { + totalPenetration = addPenetrations(totalPenetration, owningPenetration); + } } // un-penetrate From b5a72ef669af70f14b99cf410047329c12cb0c09 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Jan 2014 11:11:22 -0800 Subject: [PATCH 48/66] more nodelist repairs for concurrency issues --- interface/src/Application.cpp | 65 ++++++++++++++----------------- interface/src/Application.h | 1 - libraries/shared/src/NodeList.cpp | 10 +++-- libraries/shared/src/NodeList.h | 2 +- 4 files changed, 37 insertions(+), 41 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cf865ce299..ff320340bf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -174,6 +174,12 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : listenPort = atoi(portStr); } + // start the nodeThread so its event loop is running + _nodeThread->start(); + + // make sure the node thread is given highest priority + _nodeThread->setPriority(QThread::TimeCriticalPriority); + // put the NodeList and datagram processing on the node thread NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort); @@ -182,12 +188,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), _datagramProcessor, SLOT(processDatagrams())); - - // make sure the node thread is given highest priority - _nodeThread->setPriority(QThread::TimeCriticalPriority); - - // start the nodeThread so its event loop is running - _nodeThread->start(); // put the audio processing on a separate thread QThread* audioThread = new QThread(this); @@ -236,7 +236,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); // move the silentNodeTimer to the _nodeThread - QTimer* silentNodeTimer = new QTimer(this); + QTimer* silentNodeTimer = new QTimer(); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->moveToThread(_nodeThread); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); @@ -282,17 +282,35 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : Application::~Application() { qInstallMessageHandler(NULL); - + // make sure we don't call the idle timer any more delete idleTimer; + Menu::getInstance()->saveSettings(); + + _rearMirrorTools->saveSettings(_settings); + _settings->sync(); + + // let the avatar mixer know we're out + NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + // ask the datagram processing thread to quit and wait until it is done - _nodeThread->thread()->quit(); - _nodeThread->thread()->wait(); - + _nodeThread->quit(); + _nodeThread->wait(); + // ask the audio thread to quit and wait until it is done _audio.thread()->quit(); _audio.thread()->wait(); + + _voxelProcessor.terminate(); + _voxelHideShowThread.terminate(); + _voxelEditSender.terminate(); + _particleEditSender.terminate(); + if (_persistThread) { + _persistThread->terminate(); + _persistThread->deleteLater(); + _persistThread = NULL; + } storeSizeAndPosition(); saveScripts(); @@ -377,9 +395,6 @@ void Application::initializeGL() { qDebug("Voxel parsing thread created."); } - // call terminate before exiting - connect(this, SIGNAL(aboutToQuit()), SLOT(terminate())); - // call our timer function every second QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(timer())); @@ -1412,28 +1427,6 @@ void Application::idle() { } } -void Application::terminate() { - // Close serial port - // close(serial_fd); - - Menu::getInstance()->saveSettings(); - _rearMirrorTools->saveSettings(_settings); - _settings->sync(); - - // let the avatar mixer know we're out - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); - - _voxelProcessor.terminate(); - _voxelHideShowThread.terminate(); - _voxelEditSender.terminate(); - _particleEditSender.terminate(); - if (_persistThread) { - _persistThread->terminate(); - _persistThread->deleteLater(); - _persistThread = NULL; - } -} - void Application::checkBandwidthMeterClick() { // ... to be called upon button release diff --git a/interface/src/Application.h b/interface/src/Application.h index 514591e1df..ba25db928f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -230,7 +230,6 @@ private slots: void timer(); void idle(); - void terminate(); void setFullscreen(bool fullscreen); void setEnable3DTVMode(bool enable3DTVMode); diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 65993cb275..6bb582fcd5 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -236,7 +236,11 @@ int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, node->setLastHeardMicrostamp(usecTimestampNow()); if (!senderSockAddr.isNull()) { - activateSocketFromNodeCommunication(senderSockAddr); + if (senderSockAddr == node->getPublicSocket()) { + node->activatePublicSocket(); + } else if (senderSockAddr == node->getLocalSocket()) { + node->activateLocalSocket(); + } } if (node->getActiveSocket() || senderSockAddr.isNull()) { @@ -754,7 +758,7 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt } void NodeList::pingInactiveNodes() { - foreach (const SharedNodePointer& node, _nodeHash) { + foreach (const SharedNodePointer& node, getNodeHash()) { if (!node->getActiveSocket()) { // we don't have an active link to this node, ping it to set that up pingPublicAndLocalSocketsForInactiveNode(node.data()); @@ -791,7 +795,7 @@ void NodeList::activateSocketFromNodeCommunication(const HifiSockAddr& nodeAddre SharedNodePointer NodeList::soloNodeOfType(char nodeType) { if (memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES)) != NULL) { - foreach (const SharedNodePointer& node, _nodeHash) { + foreach (const SharedNodePointer& node, getNodeHash()) { if (node->getType() == nodeType) { return node; } diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 9ac6c5970c..851110f0b4 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -87,7 +87,6 @@ public: int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; } - void clear(); void reset(); void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest); @@ -163,6 +162,7 @@ private: void timePingReply(const HifiSockAddr& nodeAddress, unsigned char *packetData); void resetDomainData(char domainField[], const char* domainData); void domainLookup(); + void clear(); }; #endif /* defined(__hifi__NodeList__) */ From 24bed0947382f57dc49434ac1040d1e3521b3753 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Jan 2014 11:13:03 -0800 Subject: [PATCH 49/66] DatagramProcessor need not be a pointer in Application --- interface/src/Application.cpp | 12 ++++++------ interface/src/Application.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ff320340bf..432a8ae2d2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -109,7 +109,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), _nodeThread(new QThread(this)), - _datagramProcessor(new DatagramProcessor()), + _datagramProcessor(), _frameCount(0), _fps(120.0f), _justStarted(true), @@ -184,10 +184,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort); nodeList->moveToThread(_nodeThread); - _datagramProcessor->moveToThread(_nodeThread); + _datagramProcessor.moveToThread(_nodeThread); // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal - connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), _datagramProcessor, SLOT(processDatagrams())); + connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); // put the audio processing on a separate thread QThread* audioThread = new QThread(this); @@ -1344,11 +1344,11 @@ void Application::timer() { _fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); - _packetsPerSecond = (float) _datagramProcessor->getPacketCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); - _bytesPerSecond = (float) _datagramProcessor->getByteCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); + _packetsPerSecond = (float) _datagramProcessor.getPacketCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); + _bytesPerSecond = (float) _datagramProcessor.getByteCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _frameCount = 0; - _datagramProcessor->resetCounters(); + _datagramProcessor.resetCounters(); gettimeofday(&_timerStart, NULL); diff --git a/interface/src/Application.h b/interface/src/Application.h index ba25db928f..803cb296a2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -332,7 +332,7 @@ private: BandwidthMeter _bandwidthMeter; QThread* _nodeThread; - DatagramProcessor* _datagramProcessor; + DatagramProcessor _datagramProcessor; QNetworkAccessManager* _networkAccessManager; QSettings* _settings; From c54eacbf36a8a9eebc71f7d750b6338c44e95007 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Jan 2014 11:15:21 -0800 Subject: [PATCH 50/66] make sure starve is displayed even if ring buffer just received data --- interface/src/Audio.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index f31ab9056f..47ab8b0aba 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -443,6 +443,15 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { QByteArray outputBuffer; outputBuffer.resize(numRequiredOutputSamples * sizeof(int16_t)); + + if (!_ringBuffer.isStarved() && _audioOutput->bytesFree() == _audioOutput->bufferSize()) { + // we don't have any audio data left in the output buffer + // we just starved + qDebug() << "Audio output just starved."; + _ringBuffer.setIsStarved(true); + _numFramesDisplayStarve = 10; + } + // if there is anything in the ring buffer, decide what to do if (_ringBuffer.samplesAvailable() > 0) { if (!_ringBuffer.isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO @@ -515,12 +524,6 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { } } - } else if (_audioOutput->bytesFree() == _audioOutput->bufferSize()) { - // we don't have any audio data left in the output buffer, and the ring buffer from - // the network has nothing in it either - we just starved - qDebug() << "Audio output just starved."; - _ringBuffer.setIsStarved(true); - _numFramesDisplayStarve = 10; } Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO).updateValue(audioByteArray.size()); From 4e3988f8432bffc3e5acde48ad83f9d3e3291189 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 23 Jan 2014 11:20:07 -0800 Subject: [PATCH 51/66] Have the drag-out box show up in the selected color. --- interface/src/ui/MetavoxelEditor.cpp | 18 +++++++++++++++--- interface/src/ui/MetavoxelEditor.h | 1 + libraries/metavoxels/src/AttributeRegistry.cpp | 12 ++++++------ libraries/metavoxels/src/AttributeRegistry.h | 6 +++--- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index bcc89fc6d9..d3e4494ac9 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -255,7 +255,12 @@ void MetavoxelEditor::render() { glTranslatef(0.5f, 0.5f, 0.5f); if (_state != HOVERING_STATE) { const float BOX_ALPHA = 0.25f; - glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA); + QColor color = getValue().value(); + if (color.isValid()) { + glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA); + } else { + glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA); + } glEnable(GL_CULL_FACE); glutSolidCube(1.0); glDisable(GL_CULL_FACE); @@ -383,11 +388,18 @@ void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maxi if (!attribute) { return; } - QWidget* editor = _value->layout()->itemAt(0)->widget(); - OwnedAttributeValue value(attribute, attribute->createFromVariant(editor->metaObject()->userProperty().read(editor))); + OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue())); Applier applier(minimum, maximum, _gridSpacing->value(), value); Application::getInstance()->getMetavoxels()->getData().guide(applier); } +QVariant MetavoxelEditor::getValue() const { + if (_value->layout()->isEmpty()) { + return QVariant(); + } + QWidget* editor = _value->layout()->itemAt(0)->widget(); + return editor->metaObject()->userProperty().read(editor); +} + ProgramObject MetavoxelEditor::_gridProgram; diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 824887954f..21c8478d95 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -43,6 +43,7 @@ private: glm::quat getGridRotation() const; void resetState(); void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); + QVariant getValue() const; QListWidget* _attributes; QComboBox* _gridPlane; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 0814048719..5b7a8859ca 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -147,7 +147,7 @@ void* QRgbAttribute::createFromVariant(const QVariant& value) const { QWidget* QRgbAttribute::createEditor(QWidget* parent) const { QRgbEditor* editor = new QRgbEditor(parent); - editor->setColor(_defaultValue); + editor->setColor(QColor::fromRgba(_defaultValue)); return editor; } @@ -157,16 +157,16 @@ QRgbEditor::QRgbEditor(QWidget* parent) : QWidget(parent) { connect(_button, SIGNAL(clicked()), SLOT(selectColor())); } -void QRgbEditor::setColor(int color) { - QString name = QColor::fromRgba(_color = color).name(); - _button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color).name())); +void QRgbEditor::setColor(const QColor& color) { + QString name = (_color = color).name(); + _button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color.rgb()).name())); _button->setText(name); } void QRgbEditor::selectColor() { - QColor color = QColorDialog::getColor(QColor::fromRgba(_color), this, QString(), QColorDialog::ShowAlphaChannel); + QColor color = QColorDialog::getColor(_color, this, QString(), QColorDialog::ShowAlphaChannel); if (color.isValid()) { - setColor(color.rgba()); + setColor(color); } } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 6e5e53d657..058b02d78f 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -250,7 +250,7 @@ public: /// Editor for RGBA values. class QRgbEditor : public QWidget { Q_OBJECT - Q_PROPERTY(int color MEMBER _color WRITE setColor USER true) + Q_PROPERTY(QColor color MEMBER _color WRITE setColor USER true) public: @@ -258,7 +258,7 @@ public: public slots: - void setColor(int color); + void setColor(const QColor& color); private slots: @@ -267,7 +267,7 @@ private slots: private: QPushButton* _button; - QRgb _color; + QColor _color; }; /// An attribute class that stores pointers to its values. From b32b0a495a175392266c551838a5712a5068c407 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 23 Jan 2014 11:49:34 -0800 Subject: [PATCH 52/66] Remove eyes' hovering on mouse avatar, voxels. Closes #1643. --- interface/src/Application.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f85f375118..178bbd0615 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2008,15 +2008,9 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& _viewFrustum.computePickRay(0.5f, 0.5f, rayOrigin, rayDirection); lookAtSpot = rayOrigin + rayDirection * FAR_AWAY_STARE; - } else if (!_lookatTargetAvatar) { - if (_isHoverVoxel) { - // Look at the hovered voxel - lookAtSpot = getMouseVoxelWorldCoordinates(_hoverVoxel); - - } else { - // Just look in direction of the mouse ray - lookAtSpot = lookAtRayOrigin + lookAtRayDirection * FAR_AWAY_STARE; - } + } else { + // just look in direction of the mouse ray + lookAtSpot = lookAtRayOrigin + lookAtRayDirection * FAR_AWAY_STARE; } if (_faceshift.isActive()) { // deflect using Faceshift gaze data From 86e37f8fc2a0f86c4ba15eb4974eb8aa56474ad8 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 23 Jan 2014 11:53:46 -0800 Subject: [PATCH 53/66] new sounds for drumsticks --- examples/drumStick.js | 4 ++-- examples/fountain.js | 6 +++--- examples/toyball.js | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/drumStick.js b/examples/drumStick.js index 5224900414..955fddbdee 100644 --- a/examples/drumStick.js +++ b/examples/drumStick.js @@ -18,8 +18,8 @@ function vMinus(a, b) { // First, load two percussion sounds to be used on the sticks -var drum1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/MusicalInstruments/drums/snare.raw"); -var drum2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/MusicalInstruments/drums/snare.raw"); +var drum1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Drums/RackTomHi.raw"); +var drum2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Drums/RackTomLo.raw"); // State Machine: // 0 = not triggered diff --git a/examples/fountain.js b/examples/fountain.js index 86c125a834..a095f91ed3 100644 --- a/examples/fountain.js +++ b/examples/fountain.js @@ -42,7 +42,7 @@ Voxels.setVoxel(position.x, 0, position.z, 0.5, 0, 0, 255); var totalParticles = 0; function makeFountain() { - if (Math.random() < 0.06) { + if (Math.random() < 0.10) { //print("Made particle!\n"); var properties = { position: position, @@ -51,9 +51,9 @@ function makeFountain() { velocity: { x: (Math.random() * 1.0 - 0.5), y: (1.0 + (Math.random() * 2.0)), z: (Math.random() * 1.0 - 0.5) }, - gravity: { x: 0, y: -0.5, z: 0 }, + gravity: { x: 0, y: -0.1, z: 0 }, damping: 0.25, - lifetime: 2 + lifetime: 1 } Particles.addParticle(properties); diff --git a/examples/toyball.js b/examples/toyball.js index 1682cbe3d4..ddb3ac9135 100644 --- a/examples/toyball.js +++ b/examples/toyball.js @@ -134,8 +134,9 @@ function checkControllerSide(whichSide) { gravity: { x: 0, y: 0, z: 0}, inHand: true, radius: 0.05, + damping: 0.999, color: { red: 255, green: 0, blue: 0 }, - lifetime: 10 // 10 seconds + lifetime: 10 // 10 seconds }; newParticle = Particles.addParticle(properties); From 8aa7a915e7077f7b0a94d9a60507d3c7218d4602 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 23 Jan 2014 13:01:07 -0800 Subject: [PATCH 54/66] ok, one more shot at fixing particle-server crash --- libraries/particles/src/ParticleTree.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index 31f5216462..73e8dd6711 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -391,17 +391,26 @@ 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(); - _recentlyDeletedParticleIDs.remove(iterator.key()); + keysToRemove << iterator.key(); } - //qDebug() << "about to ++iterator"; ++iterator; } + + // 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()"; } From d2bf3a2a5de032b6d1f7169ad99472d8c7230e10 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 23 Jan 2014 13:46:12 -0800 Subject: [PATCH 55/66] set default particle lifetime to 10 seconds, make sure lifetime is always honored --- examples/toyball.js | 2 +- libraries/particles/src/Particle.cpp | 2 +- libraries/particles/src/Particle.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/toyball.js b/examples/toyball.js index 1682cbe3d4..be26c3d3fb 100644 --- a/examples/toyball.js +++ b/examples/toyball.js @@ -135,7 +135,7 @@ function checkControllerSide(whichSide) { inHand: true, radius: 0.05, color: { red: 255, green: 0, blue: 0 }, - lifetime: 10 // 10 seconds + lifetime: 10 // 10 seconds - same as default, not needed but here as an example }; newParticle = Particles.addParticle(properties); diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 5570797ab4..5cf5e9248d 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -682,7 +682,7 @@ void Particle::update(const uint64_t& now) { const uint64_t REALLY_OLD = 30 * USECS_PER_SECOND; // 30 seconds bool isReallyOld = ((now - _created) > REALLY_OLD); bool isInHand = getInHand(); - bool shouldDie = getShouldDie() || (!isInHand && isStopped && isReallyOld); + bool shouldDie = (getAge() > getLifetime()) || getShouldDie() || (!isInHand && isStopped && isReallyOld); setShouldDie(shouldDie); runUpdateScript(); // allow the javascript to alter our state diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index 9c93423c08..e534c7b418 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -42,7 +42,7 @@ const uint16_t PACKET_CONTAINS_INHAND = 128; const uint16_t PACKET_CONTAINS_SCRIPT = 256; const uint16_t PACKET_CONTAINS_SHOULDDIE = 512; -const float DEFAULT_LIFETIME = 60.0f * 60.0f * 24.0f; // particles live for 1 day by default +const float DEFAULT_LIFETIME = 10.0f; // particles live for 10 seconds by default const float DEFAULT_DAMPING = 0.99f; const float DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_PARTICLE_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container From 29f33a4e795c98b1068797ba9f10d3b9a3d37276 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 23 Jan 2014 13:54:45 -0800 Subject: [PATCH 56/66] =?UTF-8?q?Balls=20rolling=20on=20voxels=20don?= =?UTF-8?q?=E2=80=99t=20make=20sound?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libraries/particles/src/ParticleCollisionSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index ff65bc4298..def6768e46 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -276,7 +276,7 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, float elast void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency) { // consider whether to have the collision make a sound - const float AUDIBLE_COLLISION_THRESHOLD = 0.1f; + const float AUDIBLE_COLLISION_THRESHOLD = 0.3f; const float COLLISION_LOUDNESS = 1.f; const float DURATION_SCALING = 0.004f; const float NOISE_SCALING = 0.1f; From bdcca9cbf4319f76251bdce58de13727c31c3cbf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 23 Jan 2014 14:57:32 -0800 Subject: [PATCH 57/66] fix random crash on interface shutdown --- interface/src/VoxelHideShowThread.cpp | 1 + libraries/octree/src/JurisdictionListener.cpp | 3 +++ libraries/octree/src/OctreePersistThread.cpp | 1 + libraries/shared/src/GenericThread.cpp | 11 ++++++++++- libraries/shared/src/PacketSender.cpp | 1 + libraries/shared/src/ReceivedPacketProcessor.cpp | 1 + 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/interface/src/VoxelHideShowThread.cpp b/interface/src/VoxelHideShowThread.cpp index 2a42db7f70..a568edfc8e 100644 --- a/interface/src/VoxelHideShowThread.cpp +++ b/interface/src/VoxelHideShowThread.cpp @@ -18,6 +18,7 @@ VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) : _theSystem(theSystem) { + //qDebug() << "VoxelHideShowThread::VoxelHideShowThread() this=" << this; } bool VoxelHideShowThread::process() { diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index d8686debad..056f4508da 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -18,6 +18,9 @@ JurisdictionListener::JurisdictionListener(NODE_TYPE type, PacketSenderNotify* notify) : _packetSender(notify, JurisdictionListener::DEFAULT_PACKETS_PER_SECOND) { + //qDebug() << "JurisdictionListener::JurisdictionListener() this=" << this; + //qDebug() << "JurisdictionListener::JurisdictionListener() this=" << this << " _packetSender=" << &_packetSender; + _nodeType = type; ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 052773f475..ea85c6e178 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -20,6 +20,7 @@ OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, _persistInterval(persistInterval), _initialLoadComplete(false), _loadTimeUSecs(0) { + //qDebug() << "OctreePersistThread::OctreePersistThread()... this=" << this; } bool OctreePersistThread::process() { diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index a2dcf1004d..23d3649544 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -17,10 +17,16 @@ GenericThread::GenericThread() : _stopThread(false), _isThreaded(false) // assume non-threaded, must call initialize() { + //qDebug() << "GenericThread::GenericThread() this=" << this; } GenericThread::~GenericThread() { - terminate(); + //qDebug() << "GenericThread::~GenericThread() this=" << this; + + // we only need to call terminate() if we're actually threaded and still running + if (isStillRunning() && isThreaded()) { + terminate(); + } } void GenericThread::initialize(bool isThreaded) { @@ -39,12 +45,14 @@ void GenericThread::initialize(bool isThreaded) { } void GenericThread::terminate() { + //qDebug() << "GenericThread::terminate()... this=" << this; if (_isThreaded) { _stopThread = true; if (_thread) { _thread->wait(); _thread->deleteLater(); + _thread = NULL; } } } @@ -66,6 +74,7 @@ void GenericThread::threadRoutine() { // If we were on a thread, then quit our thread if (_isThreaded && _thread) { + //qDebug() << "GenericThread::threadRoutine()... about to call _thread->quit() this=" << this << " _thread=" << _thread; _thread->quit(); } diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index ce720bf447..7d4680985c 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -42,6 +42,7 @@ PacketSender::PacketSender(PacketSenderNotify* notify, int packetsPerSecond) : _totalPacketsQueued(0), _totalBytesQueued(0) { + //qDebug() << "PacketSender::PacketSender()... this=" << this; } PacketSender::~PacketSender() { diff --git a/libraries/shared/src/ReceivedPacketProcessor.cpp b/libraries/shared/src/ReceivedPacketProcessor.cpp index d61db2b184..8d0a4f8280 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.cpp +++ b/libraries/shared/src/ReceivedPacketProcessor.cpp @@ -14,6 +14,7 @@ ReceivedPacketProcessor::ReceivedPacketProcessor() { _dontSleep = false; + //qDebug() << "ReceivedPacketProcessor::ReceivedPacketProcessor()... this=" << this; } void ReceivedPacketProcessor::queueReceivedPacket(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength) { From 9de99ce6a8df00c81a861cef930a9ccfdfe9d610 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 23 Jan 2014 15:07:17 -0800 Subject: [PATCH 58/66] removed commented out debug --- interface/src/VoxelHideShowThread.cpp | 1 - libraries/octree/src/JurisdictionListener.cpp | 3 --- libraries/octree/src/OctreePersistThread.cpp | 1 - libraries/shared/src/GenericThread.cpp | 5 ----- libraries/shared/src/PacketSender.cpp | 1 - libraries/shared/src/ReceivedPacketProcessor.cpp | 1 - 6 files changed, 12 deletions(-) diff --git a/interface/src/VoxelHideShowThread.cpp b/interface/src/VoxelHideShowThread.cpp index a568edfc8e..2a42db7f70 100644 --- a/interface/src/VoxelHideShowThread.cpp +++ b/interface/src/VoxelHideShowThread.cpp @@ -18,7 +18,6 @@ VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) : _theSystem(theSystem) { - //qDebug() << "VoxelHideShowThread::VoxelHideShowThread() this=" << this; } bool VoxelHideShowThread::process() { diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index 056f4508da..d8686debad 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -18,9 +18,6 @@ JurisdictionListener::JurisdictionListener(NODE_TYPE type, PacketSenderNotify* notify) : _packetSender(notify, JurisdictionListener::DEFAULT_PACKETS_PER_SECOND) { - //qDebug() << "JurisdictionListener::JurisdictionListener() this=" << this; - //qDebug() << "JurisdictionListener::JurisdictionListener() this=" << this << " _packetSender=" << &_packetSender; - _nodeType = type; ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index ea85c6e178..052773f475 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -20,7 +20,6 @@ OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, _persistInterval(persistInterval), _initialLoadComplete(false), _loadTimeUSecs(0) { - //qDebug() << "OctreePersistThread::OctreePersistThread()... this=" << this; } bool OctreePersistThread::process() { diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index 23d3649544..186366c606 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -17,12 +17,9 @@ GenericThread::GenericThread() : _stopThread(false), _isThreaded(false) // assume non-threaded, must call initialize() { - //qDebug() << "GenericThread::GenericThread() this=" << this; } GenericThread::~GenericThread() { - //qDebug() << "GenericThread::~GenericThread() this=" << this; - // we only need to call terminate() if we're actually threaded and still running if (isStillRunning() && isThreaded()) { terminate(); @@ -45,7 +42,6 @@ void GenericThread::initialize(bool isThreaded) { } void GenericThread::terminate() { - //qDebug() << "GenericThread::terminate()... this=" << this; if (_isThreaded) { _stopThread = true; @@ -74,7 +70,6 @@ void GenericThread::threadRoutine() { // If we were on a thread, then quit our thread if (_isThreaded && _thread) { - //qDebug() << "GenericThread::threadRoutine()... about to call _thread->quit() this=" << this << " _thread=" << _thread; _thread->quit(); } diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index 7d4680985c..ce720bf447 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -42,7 +42,6 @@ PacketSender::PacketSender(PacketSenderNotify* notify, int packetsPerSecond) : _totalPacketsQueued(0), _totalBytesQueued(0) { - //qDebug() << "PacketSender::PacketSender()... this=" << this; } PacketSender::~PacketSender() { diff --git a/libraries/shared/src/ReceivedPacketProcessor.cpp b/libraries/shared/src/ReceivedPacketProcessor.cpp index 8d0a4f8280..d61db2b184 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.cpp +++ b/libraries/shared/src/ReceivedPacketProcessor.cpp @@ -14,7 +14,6 @@ ReceivedPacketProcessor::ReceivedPacketProcessor() { _dontSleep = false; - //qDebug() << "ReceivedPacketProcessor::ReceivedPacketProcessor()... this=" << this; } void ReceivedPacketProcessor::queueReceivedPacket(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength) { From 761bcb98252436adf5a90a9fe2919264763aaebe Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Jan 2014 15:02:23 -0800 Subject: [PATCH 59/66] allow recursive locking on the NodeHashMutex --- 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 edffc127c0..7272a41cb1 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -56,7 +56,7 @@ NodeList* NodeList::getInstance() { NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : _nodeHash(), - _nodeHashMutex(), + _nodeHashMutex(QMutex::Recursive), _domainHostname(DEFAULT_DOMAIN_HOSTNAME), _domainSockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), _nodeSocket(this), From ba7a73e347606dca0fac2fcbbbf99922a420105c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Jan 2014 15:05:09 -0800 Subject: [PATCH 60/66] add a missing break, don't double activate sockets --- assignment-client/src/avatars/AvatarMixer.cpp | 4 ++-- libraries/shared/src/NodeList.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 2f7c4a83d4..28b8358742 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -132,9 +132,9 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc // parse positional data from an node nodeList->updateNodeWithData(avatarNode.data(), senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); - } else { - break; + } + break; } case PACKET_TYPE_KILL_NODE: default: diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 7272a41cb1..da35fa92ae 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -233,7 +233,7 @@ int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, node->setLastHeardMicrostamp(usecTimestampNow()); - if (!senderSockAddr.isNull()) { + if (!senderSockAddr.isNull() && !node->getActiveSocket()) { if (senderSockAddr == node->getPublicSocket()) { node->activatePublicSocket(); } else if (senderSockAddr == node->getLocalSocket()) { From ed9118fd671f909bcc7265588d5b905f7f04a9d1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Jan 2014 15:28:46 -0800 Subject: [PATCH 61/66] more NodeList QSet changes, closes #1660 --- interface/src/Application.cpp | 34 +++++++++----------- interface/src/Application.h | 4 +-- interface/src/Menu.cpp | 4 +-- interface/src/avatar/Profile.cpp | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 2 +- libraries/shared/src/NodeList.cpp | 9 +++--- libraries/shared/src/NodeList.h | 5 +-- 7 files changed, 29 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dadc4f1d3d..2b29f22aaa 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -292,7 +292,7 @@ Application::~Application() { _settings->sync(); // let the avatar mixer know we're out - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + NodeList::getInstance()->sendKillNode(QSet() << NODE_TYPE_AVATAR_MIXER); // ask the datagram processing thread to quit and wait until it is done _nodeThread->quit(); @@ -643,21 +643,20 @@ void Application::resetProfile(const QString& username) { } void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, - const char* nodeTypes, int numNodeTypes) { - Application* self = getInstance(); - for (int i = 0; i < numNodeTypes; ++i) { - + const QSet& destinationNodeTypes) { + foreach(NODE_TYPE type, destinationNodeTypes) { // Intercept data to voxel server when voxels are disabled - if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { + if (type == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { continue; } - + // Perform the broadcast for one type - int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, & nodeTypes[i], 1); - + int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, + QSet() << type); + // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise) BandwidthMeter::ChannelIndex channel; - switch (nodeTypes[i]) { + switch (type) { case NODE_TYPE_AGENT: case NODE_TYPE_AVATAR_MIXER: channel = BandwidthMeter::AVATARS; @@ -668,7 +667,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ default: continue; } - self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); + _bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); } } @@ -1323,15 +1322,13 @@ void Application::wheelEvent(QWheelEvent* event) { } void Application::sendPingPackets() { - - const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, - NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_METAVOXEL_SERVER}; - unsigned char pingPacket[MAX_PACKET_SIZE]; int length = NodeList::getInstance()->fillPingPacket(pingPacket); - getInstance()->controlledBroadcastToNodes(pingPacket, length, - nodesToPing, sizeof(nodesToPing)); + getInstance()->controlledBroadcastToNodes(pingPacket, length, QSet() + << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER + << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER + << NODE_TYPE_METAVOXEL_SERVER); } // Every second, check the frame rates and other stuff @@ -2453,9 +2450,8 @@ void Application::updateAvatar(float deltaTime) { endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); - const char nodeTypesOfInterest[] = { NODE_TYPE_AVATAR_MIXER }; controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, - nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); + 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 diff --git a/interface/src/Application.h b/interface/src/Application.h index 803cb296a2..60ada75731 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -171,8 +171,8 @@ public: Profile* getProfile() { return &_profile; } void resetProfile(const QString& username); - static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, - const char* nodeTypes, int numNodeTypes); + void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, + const QSet& destinationNodeTypes); void setupWorldLight(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 740effcf1c..55f721afea 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -899,7 +899,7 @@ void Menu::goToDomain() { } // send a node kill request, indicating to other clients that they should play the "disappeared" effect - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + NodeList::getInstance()->sendKillNode(QSet() << NODE_TYPE_AVATAR_MIXER); // give our nodeList the new domain-server hostname NodeList::getInstance()->setDomainHostname(domainDialog.textValue()); @@ -941,7 +941,7 @@ void Menu::goToLocation() { if (newAvatarPos != avatarPos) { // send a node kill request, indicating to other clients that they should play the "disappeared" effect - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + NodeList::getInstance()->sendKillNode(QSet() << NODE_TYPE_AVATAR_MIXER); qDebug("Going To Location: %f, %f, %f...", x, y, z); myAvatar->setPosition(newAvatarPos); diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index fee5fbf6fc..dd2b547c47 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -198,7 +198,7 @@ void Profile::processDataServerResponse(const QString& userString, const QString if (coordinateItems.size() == 3 && orientationItems.size() == 3) { // send a node kill request, indicating to other clients that they should play the "disappeared" effect - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + NodeList::getInstance()->sendKillNode(QSet() << NODE_TYPE_AVATAR_MIXER); qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() << ", position to" << valueList[i + 1].toLocal8Bit().constData() << diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a1c1f602fb..f77b428764 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -250,7 +250,7 @@ void ScriptEngine::run() { int numAvatarPacketBytes = _avatarData->getBroadcastData(avatarPacket + numAvatarHeaderBytes) + numAvatarHeaderBytes; - nodeList->broadcastToNodes(avatarPacket, numAvatarPacketBytes, &NODE_TYPE_AVATAR_MIXER, 1); + nodeList->broadcastToNodes(avatarPacket, numAvatarPacketBytes, QSet() << NODE_TYPE_AVATAR_MIXER); } if (willSendVisualDataCallBack) { diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index da35fa92ae..3107d4e620 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -460,7 +460,7 @@ NodeHash::iterator NodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItem return _nodeHash.erase(nodeItemToKill); } -void NodeList::sendKillNode(const char* nodeTypes, int numNodeTypes) { +void NodeList::sendKillNode(const QSet& destinationNodeTypes) { unsigned char packet[MAX_PACKET_SIZE]; unsigned char* packetPosition = packet; @@ -470,7 +470,7 @@ void NodeList::sendKillNode(const char* nodeTypes, int numNodeTypes) { memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size()); packetPosition += rfcUUID.size(); - broadcastToNodes(packet, packetPosition - packet, nodeTypes, numNodeTypes); + broadcastToNodes(packet, packetPosition - packet, destinationNodeTypes); } void NodeList::processKillNode(unsigned char* packetData, size_t dataBytes) { @@ -733,12 +733,13 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, } } -unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) { +unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes, + const QSet& destinationNodeTypes) { unsigned n = 0; foreach (const SharedNodePointer& node, getNodeHash()) { // only send to the NodeTypes we are asked to send to. - if (memchr(nodeTypes, node->getType(), numNodeTypes)) { + 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, diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 2143ae87eb..13f9485069 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -90,6 +90,7 @@ public: void reset(); + const QSet& getNodeInterestSet() const { return _nodeTypesOfInterest; } void addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd); void addSetOfNodeTypesToNodeInterestSet(const QSet& setOfNodeTypes); @@ -104,7 +105,7 @@ public: int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer); void pingPublicAndLocalSocketsForInactiveNode(Node* node); - void sendKillNode(const char* nodeTypes, int numNodeTypes); + void sendKillNode(const QSet& destinationNodeTypes); SharedNodePointer nodeWithAddress(const HifiSockAddr& senderSockAddr); SharedNodePointer nodeWithUUID(const QUuid& nodeUUID); @@ -116,7 +117,7 @@ public: int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes); - unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes); + unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const QSet& destinationNodeTypes); SharedNodePointer soloNodeOfType(char nodeType); void loadData(QSettings* settings); From 80106979a5d7e840c5281ea40141e308e75e4b89 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Jan 2014 15:55:36 -0800 Subject: [PATCH 62/66] replace packetSenderNotify with signal/slot combo --- interface/src/Application.cpp | 8 +++++--- interface/src/Application.h | 8 +++----- libraries/octree/src/JurisdictionListener.cpp | 4 ++-- libraries/octree/src/JurisdictionListener.h | 2 +- libraries/octree/src/JurisdictionSender.cpp | 4 ++-- libraries/octree/src/JurisdictionSender.h | 2 +- libraries/octree/src/OctreeEditPacketSender.cpp | 4 ++-- libraries/octree/src/OctreeEditPacketSender.h | 3 ++- .../particles/src/ParticleEditPacketSender.h | 4 +--- libraries/shared/src/PacketSender.cpp | 10 ++++------ libraries/shared/src/PacketSender.h | 17 ++++------------- libraries/voxels/src/VoxelEditPacketSender.h | 4 +--- 12 files changed, 28 insertions(+), 42 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2b29f22aaa..b1592115fe 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -145,8 +145,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _enableProcessVoxelsThread(true), _voxelProcessor(), _voxelHideShowThread(&_voxels), - _voxelEditSender(this), - _particleEditSender(this), _packetsPerSecond(0), _bytesPerSecond(0), _recentMaxPackets(0), @@ -234,6 +232,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER << NODE_TYPE_METAVOXEL_SERVER); + + // connect to the packet sent signal of the _voxelEditSender and the _particleEditSender + connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent); + connect(&_particleEditSender, &ParticleEditPacketSender::packetSent, this, &Application::packetSent); // move the silentNodeTimer to the _nodeThread QTimer* silentNodeTimer = new QTimer(); @@ -4052,7 +4054,7 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen return statsMessageLength; } -void Application::packetSentNotification(ssize_t length) { +void Application::packetSent(quint64 length) { _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 60ada75731..db3ded55be 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -92,7 +92,7 @@ static const float NODE_KILLED_RED = 1.0f; static const float NODE_KILLED_GREEN = 0.0f; static const float NODE_KILLED_BLUE = 0.0f; -class Application : public QApplication, public PacketSenderNotify { +class Application : public QApplication { Q_OBJECT friend class VoxelPacketProcessor; @@ -188,9 +188,6 @@ public: void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const; - - virtual void packetSentNotification(ssize_t length); - VoxelShader& getVoxelShader() { return _voxelShader; } PointShader& getPointShader() { return _pointShader; } FileLogger* getLogger() { return _logger; } @@ -209,7 +206,8 @@ public: public slots: void domainChanged(const QString& domainHostname); void nodeKilled(SharedNodePointer node); - + void packetSent(quint64 length); + void exportVoxels(); void importVoxels(); void cutVoxels(); diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index d8686debad..e761a15e76 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -15,8 +15,8 @@ #include #include "JurisdictionListener.h" -JurisdictionListener::JurisdictionListener(NODE_TYPE type, PacketSenderNotify* notify) : - _packetSender(notify, JurisdictionListener::DEFAULT_PACKETS_PER_SECOND) +JurisdictionListener::JurisdictionListener(NODE_TYPE type) : + _packetSender(JurisdictionListener::DEFAULT_PACKETS_PER_SECOND) { _nodeType = type; ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to diff --git a/libraries/octree/src/JurisdictionListener.h b/libraries/octree/src/JurisdictionListener.h index c112f43b52..110b2f0d98 100644 --- a/libraries/octree/src/JurisdictionListener.h +++ b/libraries/octree/src/JurisdictionListener.h @@ -27,7 +27,7 @@ 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, PacketSenderNotify* notify = NULL); + JurisdictionListener(NODE_TYPE type = NODE_TYPE_VOXEL_SERVER); virtual bool process(); diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp index 521dbdb7e4..e1bbf78506 100644 --- a/libraries/octree/src/JurisdictionSender.cpp +++ b/libraries/octree/src/JurisdictionSender.cpp @@ -16,10 +16,10 @@ #include "JurisdictionSender.h" -JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NODE_TYPE type, PacketSenderNotify* notify) : +JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NODE_TYPE type) : ReceivedPacketProcessor(), _jurisdictionMap(map), - _packetSender(notify, JurisdictionSender::DEFAULT_PACKETS_PER_SECOND) + _packetSender(JurisdictionSender::DEFAULT_PACKETS_PER_SECOND) { _nodeType = type; } diff --git a/libraries/octree/src/JurisdictionSender.h b/libraries/octree/src/JurisdictionSender.h index 287a5dbf61..e70070b21d 100644 --- a/libraries/octree/src/JurisdictionSender.h +++ b/libraries/octree/src/JurisdictionSender.h @@ -26,7 +26,7 @@ class JurisdictionSender : public ReceivedPacketProcessor { public: static const int DEFAULT_PACKETS_PER_SECOND = 1; - JurisdictionSender(JurisdictionMap* map, NODE_TYPE type = NODE_TYPE_VOXEL_SERVER, PacketSenderNotify* notify = NULL); + JurisdictionSender(JurisdictionMap* map, NODE_TYPE type = NODE_TYPE_VOXEL_SERVER); ~JurisdictionSender(); void setJurisdiction(JurisdictionMap* map) { _jurisdictionMap = map; } diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index a3d4e7862b..d4c1051747 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -27,8 +27,8 @@ EditPacketBuffer::EditPacketBuffer(PACKET_TYPE type, unsigned char* buffer, ssiz const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::DEFAULT_PACKETS_PER_SECOND; -OctreeEditPacketSender::OctreeEditPacketSender(PacketSenderNotify* notify) : - PacketSender(notify), +OctreeEditPacketSender::OctreeEditPacketSender() : + PacketSender(), _shouldSend(true), _maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES), _releaseQueuedMessagesPending(false), diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index 9539f309fd..f214796f9d 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -28,8 +28,9 @@ public: /// Utility for processing, packing, queueing and sending of outbound edit messages. class OctreeEditPacketSender : public PacketSender { + Q_OBJECT public: - OctreeEditPacketSender(PacketSenderNotify* notify = NULL); + OctreeEditPacketSender(); ~OctreeEditPacketSender(); /// Queues a single edit message. Will potentially send a pending multi-command packet. Determines which server diff --git a/libraries/particles/src/ParticleEditPacketSender.h b/libraries/particles/src/ParticleEditPacketSender.h index f142931222..51f0fd9b96 100644 --- a/libraries/particles/src/ParticleEditPacketSender.h +++ b/libraries/particles/src/ParticleEditPacketSender.h @@ -16,10 +16,8 @@ /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. class ParticleEditPacketSender : public OctreeEditPacketSender { + Q_OBJECT public: - ParticleEditPacketSender(PacketSenderNotify* notify = NULL) : OctreeEditPacketSender(notify) { } - ~ParticleEditPacketSender() { } - /// 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); diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index ce720bf447..52274d9e9c 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -27,13 +27,12 @@ const int PacketSender::MINIMAL_SLEEP_INTERVAL = (USECS_PER_SECOND / TARGET_FPS) const int AVERAGE_CALL_TIME_SAMPLES = 10; -PacketSender::PacketSender(PacketSenderNotify* notify, int packetsPerSecond) : +PacketSender::PacketSender(int packetsPerSecond) : _packetsPerSecond(packetsPerSecond), _usecsPerProcessCallHint(0), _lastProcessCallTime(0), _averageProcessCallTime(AVERAGE_CALL_TIME_SAMPLES), _lastSendTime(0), // Note: we set this to 0 to indicate we haven't yet sent something - _notify(notify), _lastPPSCheck(0), _packetsOverCheckInterval(0), _started(usecTimestampNow()), @@ -271,10 +270,9 @@ bool PacketSender::nonThreadedProcess() { _packetsOverCheckInterval++; _totalPacketsSent++; _totalBytesSent += temporary.getLength(); - - if (_notify) { - _notify->packetSentNotification(temporary.getLength()); - } + + emit packetSent(temporary.getLength()); + _lastSendTime = now; } return isStillRunning(); diff --git a/libraries/shared/src/PacketSender.h b/libraries/shared/src/PacketSender.h index 7a2c6e6d2a..4d87a62e5a 100644 --- a/libraries/shared/src/PacketSender.h +++ b/libraries/shared/src/PacketSender.h @@ -15,15 +15,9 @@ #include "NetworkPacket.h" #include "SharedUtil.h" -/// Notification Hook for packets being sent by a PacketSender -class PacketSenderNotify { -public: - virtual void packetSentNotification(ssize_t length) = 0; -}; - - /// Generalized threaded processor for queueing and sending of outbound packets. class PacketSender : public GenericThread { + Q_OBJECT public: static const uint64_t USECS_PER_SECOND; @@ -35,7 +29,7 @@ public: static const int MINIMUM_PACKETS_PER_SECOND; static const int MINIMAL_SLEEP_INTERVAL; - PacketSender(PacketSenderNotify* notify = NULL, int packetsPerSecond = DEFAULT_PACKETS_PER_SECOND); + PacketSender(int packetsPerSecond = DEFAULT_PACKETS_PER_SECOND); ~PacketSender(); /// Add packet to outbound queue. @@ -48,9 +42,6 @@ public: void setPacketsPerSecond(int packetsPerSecond); int getPacketsPerSecond() const { return _packetsPerSecond; } - void setPacketSenderNotify(PacketSenderNotify* notify) { _notify = notify; } - PacketSenderNotify* getPacketSenderNotify() const { return _notify; } - virtual bool process(); /// are there packets waiting in the send queue to be sent @@ -97,7 +88,8 @@ public: /// returns the total bytes queued by this object over its lifetime uint64_t getLifetimeBytesQueued() const { return _totalBytesQueued; } - +signals: + void packetSent(quint64); protected: int _packetsPerSecond; int _usecsPerProcessCallHint; @@ -107,7 +99,6 @@ protected: private: std::vector _packets; uint64_t _lastSendTime; - PacketSenderNotify* _notify; bool threadedProcess(); bool nonThreadedProcess(); diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h index d0ce018d95..ec9b74dff8 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -15,10 +15,8 @@ /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. class VoxelEditPacketSender : public OctreeEditPacketSender { + Q_OBJECT public: - VoxelEditPacketSender(PacketSenderNotify* notify = NULL) : OctreeEditPacketSender(notify) { } - ~VoxelEditPacketSender() { } - /// Send voxel edit message immediately void sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail); From 70c675e9bec19f09674656f43c5980665426200c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 23 Jan 2014 16:03:52 -0800 Subject: [PATCH 63/66] Adding comments and minor cleanup after tracking down some NaN's that were showing up in collision sound triggers. --- libraries/octree/src/Octree.cpp | 1 + .../particles/src/ParticleCollisionSystem.cpp | 24 ++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 808b49d475..5d25f0c164 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -592,6 +592,7 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { if (element->hasContent()) { glm::vec3 elementPenetration; if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) { + // NOTE: it is possible for this penetration accumulation algorithm to produce a final penetration vector with zero length. args->penetration = addPenetrations(args->penetration, elementPenetration * (float)(TREE_SCALE)); args->found = true; } diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index ff65bc4298..f386b3ef4a 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -79,8 +79,8 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { // let the particles run their collision scripts if they have them particle->collisionWithVoxel(voxelDetails); - collisionInfo._penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); + collisionInfo._penetration /= (float)(TREE_SCALE); applyHardCollision(particle, ELASTICITY, DAMPING, collisionInfo); delete voxelDetails; // cleanup returned details @@ -134,7 +134,6 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) _packetSender->releaseQueuedMessages(); - penetration /= (float)(TREE_SCALE); updateCollisionSound(particleA, penetration, COLLISION_FREQUENCY); } } @@ -184,8 +183,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { } // HACK END - collisionInfo._penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); + collisionInfo._penetration /= (float)(TREE_SCALE); applyHardCollision(particle, elasticity, damping, collisionInfo); } } @@ -216,8 +215,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { } // HACK END - collisionInfo._penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); + collisionInfo._penetration /= (float)(TREE_SCALE); applyHardCollision(particle, ELASTICITY, damping, collisionInfo); } } @@ -293,18 +292,21 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity); } */ - float velocityTowardCollision = glm::dot(velocity, glm::normalize(penetration)); - float velocityTangentToCollision = glm::length(velocity) - velocityTowardCollision; + float normalSpeed = glm::dot(velocity, glm::normalize(penetration)); + // NOTE: it is possible for normalSpeed to be NaN at this point + // (sometimes the average penetration of a bunch of voxels is a zero length vector which cannot be normalized) + // however the check below will fail (NaN comparisons always fail) and everything will be fine. - if (velocityTowardCollision > AUDIBLE_COLLISION_THRESHOLD) { + if (normalSpeed > AUDIBLE_COLLISION_THRESHOLD) { // Volume is proportional to collision velocity // Base frequency is modified upward by the angle of the collision // Noise is a function of the angle of collision // Duration of the sound is a function of both base frequency and velocity of impact + float tangentialSpeed = glm::length(velocity) - normalSpeed; _audio->startCollisionSound( - std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.f), - frequency * (1.f + velocityTangentToCollision / velocityTowardCollision), - std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f), - 1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, false); + std::min(COLLISION_LOUDNESS * normalSpeed, 1.f), + frequency * (1.f + tangentialSpeed / normalSpeed), + std::min(tangentialSpeed / normalSpeed * NOISE_SCALING, 1.f), + 1.f - DURATION_SCALING * powf(frequency, 0.5f) / normalSpeed, false); } } From 93482d9d1888dfbda410f813dcb7410c543a4b45 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 23 Jan 2014 16:41:03 -0800 Subject: [PATCH 64/66] Fixing indentation. --- libraries/particles/src/ParticleCollisionSystem.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 65ed8f1e2b..8eb525a388 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -303,10 +303,9 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm // Noise is a function of the angle of collision // Duration of the sound is a function of both base frequency and velocity of impact float tangentialSpeed = glm::length(velocity) - normalSpeed; - _audio->startCollisionSound( - std::min(COLLISION_LOUDNESS * normalSpeed, 1.f), - frequency * (1.f + tangentialSpeed / normalSpeed), - std::min(tangentialSpeed / normalSpeed * NOISE_SCALING, 1.f), - 1.f - DURATION_SCALING * powf(frequency, 0.5f) / normalSpeed, false); + _audio->startCollisionSound( std::min(COLLISION_LOUDNESS * normalSpeed, 1.f), + frequency * (1.f + tangentialSpeed / normalSpeed), + std::min(tangentialSpeed / normalSpeed * NOISE_SCALING, 1.f), + 1.f - DURATION_SCALING * powf(frequency, 0.5f) / normalSpeed, false); } } From 5560a0fb4d967d33b68e618ccf5d48bde728b4ce Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 23 Jan 2014 17:03:11 -0800 Subject: [PATCH 65/66] fix crash on shutdown of interface --- .../octree/src/OctreeScriptingInterface.cpp | 27 ++++++++++++------- .../octree/src/OctreeScriptingInterface.h | 3 +++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/libraries/octree/src/OctreeScriptingInterface.cpp b/libraries/octree/src/OctreeScriptingInterface.cpp index 7355b8e2de..553ab961df 100644 --- a/libraries/octree/src/OctreeScriptingInterface.cpp +++ b/libraries/octree/src/OctreeScriptingInterface.cpp @@ -6,6 +6,8 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // +#include + #include "OctreeScriptingInterface.h" OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender, @@ -16,18 +18,21 @@ OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packe } OctreeScriptingInterface::~OctreeScriptingInterface() { - //printf("OctreeScriptingInterface::~OctreeScriptingInterface()\n"); + cleanupManagedObjects(); +} + +void OctreeScriptingInterface::cleanupManagedObjects() { if (_managedJurisdictionListener) { - //printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedJurisdictionListener... _jurisdictionListener->terminate()\n"); _jurisdictionListener->terminate(); - //printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedJurisdictionListener... deleting _jurisdictionListener\n"); - delete _jurisdictionListener; + _jurisdictionListener->deleteLater(); + _managedJurisdictionListener = false; + _jurisdictionListener = NULL; } if (_managedPacketSender) { - //printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedJurisdictionListener... _packetSender->terminate()\n"); _packetSender->terminate(); - //printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedPacketSender... deleting _packetSender\n"); - delete _packetSender; + _packetSender->deleteLater(); + _managedPacketSender = false; + _packetSender = NULL; } } @@ -40,13 +45,11 @@ void OctreeScriptingInterface::setJurisdictionListener(JurisdictionListener* jur } void OctreeScriptingInterface::init() { - //printf("OctreeScriptingInterface::init()\n"); if (_jurisdictionListener) { _managedJurisdictionListener = false; } else { _managedJurisdictionListener = true; _jurisdictionListener = new JurisdictionListener(getServerNodeType()); - //qDebug("OctreeScriptingInterface::init() _managedJurisdictionListener=true, creating _jurisdictionListener=%p", _jurisdictionListener); _jurisdictionListener->initialize(true); } @@ -55,7 +58,11 @@ void OctreeScriptingInterface::init() { } else { _managedPacketSender = true; _packetSender = createPacketSender(); - //qDebug("OctreeScriptingInterface::init() _managedPacketSender=true, creating _packetSender=%p", _packetSender); _packetSender->setServerJurisdictions(_jurisdictionListener->getJurisdictions()); } + + if (QCoreApplication::instance()) { + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(cleanupManagedObjects())); + } + } diff --git a/libraries/octree/src/OctreeScriptingInterface.h b/libraries/octree/src/OctreeScriptingInterface.h index b9466fb9e9..1158f21438 100644 --- a/libraries/octree/src/OctreeScriptingInterface.h +++ b/libraries/octree/src/OctreeScriptingInterface.h @@ -84,6 +84,9 @@ public slots: /// returns the total bytes queued by this object over its lifetime long long unsigned int getLifetimeBytesQueued() const { return _packetSender->getLifetimeBytesQueued(); } + // TODO: hmmm... we don't want this called from JS, how to handle that? + void cleanupManagedObjects(); + protected: /// attached OctreeEditPacketSender that handles queuing and sending of packets to VS OctreeEditPacketSender* _packetSender; From c4254cd423819fae512877e2ea5c5bc5382a1961 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 23 Jan 2014 18:08:45 -0800 Subject: [PATCH 66/66] Fix OS X warning. --- interface/src/ui/MetavoxelEditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index d3e4494ac9..df932a9933 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -328,6 +328,7 @@ glm::quat MetavoxelEditor::getGridRotation() const { return glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f); case GRID_PLANE_YZ: + default: return glm::angleAxis(90.0f, 0.0f, 1.0f, 0.0f); } }