diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 104f65d7d9..db7e0dc2a3 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -39,6 +39,7 @@ void Agent::readPendingDatagrams() { while (readAvailableDatagram(receivedPacket, senderSockAddr)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { PacketType datagramPacketType = packetTypeForPacket(receivedPacket); + if (datagramPacketType == PacketTypeJurisdiction) { int headerBytes = numBytesForPacketHeader(receivedPacket); @@ -48,12 +49,12 @@ void Agent::readPendingDatagrams() { // PacketType_JURISDICTION, first byte is the node type... switch (receivedPacket[headerBytes]) { case NodeType::VoxelServer: - _scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(matchedNode, - receivedPacket); + _scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener()-> + queueReceivedPacket(matchedNode,receivedPacket); break; case NodeType::ParticleServer: - _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(matchedNode, - receivedPacket); + _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()-> + queueReceivedPacket(matchedNode, receivedPacket); break; } } @@ -63,7 +64,46 @@ void Agent::readPendingDatagrams() { Particle::handleAddParticleResponse(receivedPacket); // also give our local particle tree a chance to remap any internal locally created particles - _particleTree.handleAddParticleResponse(receivedPacket); + _particleViewer.getTree()->handleAddParticleResponse(receivedPacket); + + } else if (datagramPacketType == PacketTypeParticleData + || datagramPacketType == PacketTypeParticleErase + || datagramPacketType == PacketTypeOctreeStats + || datagramPacketType == PacketTypeVoxelData + ) { + SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket); + QByteArray mutablePacket = receivedPacket; + ssize_t messageLength = mutablePacket.size(); + + if (datagramPacketType == PacketTypeOctreeStats) { + + int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(mutablePacket, sourceNode); + if (messageLength > statsMessageLength) { + mutablePacket = mutablePacket.mid(statsMessageLength); + + // TODO: this does not look correct, the goal is to test the packet version for the piggyback, but + // this is testing the version and hash of the original packet + // need to use numBytesArithmeticCodingFromBuffer()... + if (!NodeList::getInstance()->packetVersionAndHashMatch(receivedPacket)) { + return; // bail since piggyback data doesn't match our versioning + } + } else { + // Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket() + return; // bail since no piggyback data + } + + datagramPacketType = packetTypeForPacket(mutablePacket); + + } // fall through to piggyback message + + if (datagramPacketType == PacketTypeParticleData || datagramPacketType == PacketTypeParticleErase) { + _particleViewer.processDatagram(mutablePacket, sourceNode); + } + + if (datagramPacketType == PacketTypeVoxelData) { + _voxelViewer.processDatagram(mutablePacket, sourceNode); + } + } else { NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket); } @@ -110,9 +150,6 @@ void Agent::run() { connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - // tell our script engine about our local particle tree - _scriptEngine.getParticlesScriptingInterface()->setParticleTree(&_particleTree); - // setup an Avatar for the script to use AvatarData scriptedAvatar; @@ -126,6 +163,21 @@ void Agent::run() { // register ourselves to the script engine _scriptEngine.registerGlobalObject("Agent", this); + _scriptEngine.init(); // must be done before we set up the viewers + + _scriptEngine.registerGlobalObject("VoxelViewer", &_voxelViewer); + // connect the VoxelViewer and the VoxelScriptingInterface to each other + JurisdictionListener* voxelJL = _scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener(); + _voxelViewer.setJurisdictionListener(voxelJL); + _voxelViewer.init(); + _scriptEngine.getVoxelsScriptingInterface()->setVoxelTree(_voxelViewer.getTree()); + + _scriptEngine.registerGlobalObject("ParticleViewer", &_particleViewer); + JurisdictionListener* particleJL = _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener(); + _particleViewer.setJurisdictionListener(particleJL); + _particleViewer.init(); + _scriptEngine.getParticlesScriptingInterface()->setParticleTree(_particleViewer.getTree()); + _scriptEngine.setScriptContents(scriptContents); - _scriptEngine.run(); + _scriptEngine.run(); } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 1502093d5b..6d37f66563 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -17,9 +17,11 @@ #include #include +#include #include #include #include +#include class Agent : public ThreadedAssignment { @@ -41,9 +43,11 @@ signals: void willSendVisualDataCallback(); private: ScriptEngine _scriptEngine; - ParticleTree _particleTree; VoxelEditPacketSender _voxelEditSender; ParticleEditPacketSender _particleEditSender; + + ParticleTreeHeadlessViewer _particleViewer; + VoxelTreeHeadlessViewer _voxelViewer; }; #endif /* defined(__hifi__Agent__) */ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2ebe556016..6d6920c588 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3404,7 +3404,7 @@ void Application::nodeKilled(SharedNodePointer node) { } // If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server - _voxelServerJurisdictions.erase(nodeUUID); + _voxelServerJurisdictions.erase(_voxelServerJurisdictions.find(nodeUUID)); } // also clean up scene stats for that server @@ -3435,7 +3435,7 @@ void Application::nodeKilled(SharedNodePointer node) { } // If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server - _particleServerJurisdictions.erase(nodeUUID); + _particleServerJurisdictions.erase(_particleServerJurisdictions.find(nodeUUID)); } // also clean up scene stats for that server diff --git a/interface/src/Camera.h b/interface/src/Camera.h index b5bf6ea0b7..81632f4424 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -14,8 +14,6 @@ #include #include -const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f; - enum CameraMode { CAMERA_MODE_NULL = -1, diff --git a/interface/src/VoxelPacketProcessor.cpp b/interface/src/VoxelPacketProcessor.cpp index dce391e587..142285664e 100644 --- a/interface/src/VoxelPacketProcessor.cpp +++ b/interface/src/VoxelPacketProcessor.cpp @@ -48,6 +48,9 @@ void VoxelPacketProcessor::processPacket(const SharedNodePointer& sendingNode, c wasStatsPacket = true; if (messageLength > statsMessageLength) { mutablePacket = mutablePacket.mid(statsMessageLength); + + // TODO: this does not look correct, the goal is to test the packet version for the piggyback, but + // this is testing the version and hash of the original packet if (!NodeList::getInstance()->packetVersionAndHashMatch(packet)) { return; // bail since piggyback data doesn't match our versioning } diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index 20e5af012f..1ad6c69912 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -62,8 +62,7 @@ bool JurisdictionListener::queueJurisdictionRequest() { } void JurisdictionListener::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { - //qDebug() << "JurisdictionListener::processPacket()"; - if (packetTypeForPacket(packet) == PacketTypeJurisdictionRequest && sendingNode) { + if (packetTypeForPacket(packet) == PacketTypeJurisdiction && sendingNode) { QUuid nodeUUID = sendingNode->getUUID(); JurisdictionMap map; map.unpackFromMessage(reinterpret_cast(packet.data()), packet.size()); diff --git a/libraries/octree/src/JurisdictionMap.h b/libraries/octree/src/JurisdictionMap.h index 219429712e..06305254d1 100644 --- a/libraries/octree/src/JurisdictionMap.h +++ b/libraries/octree/src/JurisdictionMap.h @@ -79,7 +79,8 @@ private: /// Map between node IDs and their reported JurisdictionMap. Typically used by classes that need to know which nodes are /// managing which jurisdictions. -typedef std::map NodeToJurisdictionMap; +typedef QMap NodeToJurisdictionMap; +typedef QMap::iterator NodeToJurisdictionMapIterator; #endif /* defined(__hifi__JurisdictionMap__) */ diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index 7cf586cbb2..55a0729742 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -12,7 +12,15 @@ #include "OctreeHeadlessViewer.h" OctreeHeadlessViewer::OctreeHeadlessViewer() : - OctreeRenderer() { + OctreeRenderer(), + _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), + _boundaryLevelAdjust(0), + _maxPacketsPerSecond(DEFAULT_MAX_OCTREE_PPS) +{ + _viewFrustum.setFieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES); + _viewFrustum.setAspectRatio(DEFAULT_ASPECT_RATIO); + _viewFrustum.setNearClip(DEFAULT_NEAR_CLIP); + _viewFrustum.setFarClip(TREE_SCALE); } OctreeHeadlessViewer::~OctreeHeadlessViewer() { @@ -30,6 +38,17 @@ void OctreeHeadlessViewer::queryOctree() { bool wantExtraDebugging = false; + if (wantExtraDebugging) { + qDebug() << "OctreeHeadlessViewer::queryOctree() _jurisdictionListener=" << _jurisdictionListener; + qDebug() << "---------------"; + qDebug() << "_jurisdictionListener=" << _jurisdictionListener; + qDebug() << "Jurisdictions..."; + for (NodeToJurisdictionMapIterator i = jurisdictions.begin(); i != jurisdictions.end(); ++i) { + qDebug() << i.key() << ": " << &i.value(); + } + qDebug() << "---------------"; + } + // These will be the same for all servers, so we can set them up once and then reuse for each server we send to. _octreeQuery.setWantLowResMoving(true); _octreeQuery.setWantColor(true); @@ -87,7 +106,7 @@ void OctreeHeadlessViewer::queryOctree() { } } - if (wantExtraDebugging && unknownJurisdictionServers > 0) { + if (wantExtraDebugging) { qDebug("Servers: total %d, in view %d, unknown jurisdiction %d", totalServers, inViewServers, unknownJurisdictionServers); } @@ -108,7 +127,7 @@ void OctreeHeadlessViewer::queryOctree() { } } - if (wantExtraDebugging && unknownJurisdictionServers > 0) { + if (wantExtraDebugging) { qDebug("perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer); } @@ -158,6 +177,9 @@ void OctreeHeadlessViewer::queryOctree() { if (inView) { _octreeQuery.setMaxOctreePacketsPerSecond(perServerPPS); + if (wantExtraDebugging) { + qDebug() << "inView for node " << *node << ", give it budget of " << perServerPPS; + } } else if (unknownView) { if (wantExtraDebugging) { qDebug() << "no known jurisdiction for node " << *node << ", give it budget of " @@ -201,3 +223,71 @@ void OctreeHeadlessViewer::queryOctree() { } } } + + +int OctreeHeadlessViewer::parseOctreeStats(const QByteArray& packet, const SharedNodePointer& sourceNode) { + // But, also identify the sender, and keep track of the contained jurisdiction root for this server + + // parse the incoming stats datas stick it in a temporary object for now, while we + // determine which server it belongs to + OctreeSceneStats temp; + int statsMessageLength = temp.unpackFromMessage(reinterpret_cast(packet.data()), packet.size()); + return statsMessageLength; + +#if 0 // CURRENTLY NOT YET IMPLEMENTED + + // quick fix for crash... why would voxelServer be NULL? + if (sourceNode) { + QUuid nodeUUID = sendingNode->getUUID(); + + // now that we know the node ID, let's add these stats to the stats for that node... + _voxelSceneStatsLock.lockForWrite(); + if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { + _octreeServerSceneStats[nodeUUID].unpackFromMessage(reinterpret_cast(packet.data()), + packet.size()); + } else { + _octreeServerSceneStats[nodeUUID] = temp; + } + _voxelSceneStatsLock.unlock(); + + VoxelPositionSize rootDetails; + voxelDetailsForCode(temp.getJurisdictionRoot(), rootDetails); + + // see if this is the first we've heard of this node... + NodeToJurisdictionMap* jurisdiction = NULL; + if (sendingNode->getType() == NodeType::VoxelServer) { + jurisdiction = &_voxelServerJurisdictions; + } else { + jurisdiction = &_particleServerJurisdictions; + } + + + if (jurisdiction->find(nodeUUID) == jurisdiction->end()) { + printf("stats from new server... v[%f, %f, %f, %f]\n", + rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); + + // Add the jurisditionDetails object to the list of "fade outs" + if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) { + VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE); + fade.voxelDetails = rootDetails; + const float slightly_smaller = 0.99f; + fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; + _voxelFades.push_back(fade); + } + } + // store jurisdiction details for later use + // This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it + // but VoxelSceneStats thinks it's just returning a reference to it's contents. So we need to make a copy of the + // details from the VoxelSceneStats to construct the JurisdictionMap + JurisdictionMap jurisdictionMap; + jurisdictionMap.copyContents(temp.getJurisdictionRoot(), temp.getJurisdictionEndNodes()); + (*jurisdiction)[nodeUUID] = jurisdictionMap; + } + return statsMessageLength; +#endif +} + +void OctreeHeadlessViewer::trackIncomingOctreePacket(const QByteArray& packet, + const SharedNodePointer& sendingNode, bool wasStatsPacket) { + +} diff --git a/libraries/octree/src/OctreeHeadlessViewer.h b/libraries/octree/src/OctreeHeadlessViewer.h index c178c2b7c8..c058809acd 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.h +++ b/libraries/octree/src/OctreeHeadlessViewer.h @@ -12,10 +12,13 @@ #include #include + #include "JurisdictionListener.h" #include "Octree.h" +#include "OctreeConstants.h" #include "OctreeQuery.h" #include "OctreeRenderer.h" +#include "OctreeSceneStats.h" #include "Octree.h" #include "ViewFrustum.h" @@ -31,13 +34,30 @@ public: virtual void render() { /* swallow these */ }; void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; } + + static int parseOctreeStats(const QByteArray& packet, const SharedNodePointer& sourceNode); + static void trackIncomingOctreePacket(const QByteArray& packet, const SharedNodePointer& sendingNode, bool wasStatsPacket); + +public slots: void queryOctree(); - void setVoxelSizeScale(float sizeScale); + // setters for camera attributes + void setPosition(const glm::vec3& position) { _viewFrustum.setPosition(position); } + void setOrientation(const glm::quat& orientation) { _viewFrustum.setOrientation(orientation); } + + // setters for LOD and PPS + void setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; } + void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; } + void setMaxPacketsPerSecond(int maxPacketsPerSecond) { _maxPacketsPerSecond = maxPacketsPerSecond; } + + // getters for camera attributes + const glm::vec3& getPosition() const { return _viewFrustum.getPosition(); } + const glm::quat& getOrientation() const { return _viewFrustum.getOrientation(); } + + // getters for LOD and PPS float getVoxelSizeScale() const { return _voxelSizeScale; } - void setBoundaryLevelAdjust(int boundaryLevelAdjust); int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } - int getMaxPacketsPerSecond() const { return _maxTotalPPS; } + int getMaxPacketsPerSecond() const { return _maxPacketsPerSecond; } private: ViewFrustum _viewFrustum; @@ -45,7 +65,7 @@ private: OctreeQuery _octreeQuery; float _voxelSizeScale; int _boundaryLevelAdjust; - int _maxTotalPPS; + int _maxPacketsPerSecond; }; #endif /* defined(__hifi__OctreeHeadlessViewer__) */ \ No newline at end of file diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index b3d2ea45e3..06007cb0dc 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -27,44 +27,48 @@ OctreeRenderer::~OctreeRenderer() { } void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { + bool extraDebugging = false; + + if (extraDebugging) { + qDebug() << "OctreeRenderer::processDatagram()"; + } + + if (!_tree) { + qDebug() << "OctreeRenderer::processDatagram() called before init, calling init()..."; + this->init(); + } + bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - bool extraDebugging = false; // Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging) PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram()",showTimingDetails); - const unsigned char* packetData = (const unsigned char*)dataByteArray.constData(); int packetLength = dataByteArray.size(); - - unsigned char command = *packetData; - + PacketType command = packetTypeForPacket(dataByteArray); int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray); QUuid sourceUUID = uuidFromPacketHeader(dataByteArray); - - PacketType expectedType = getExpectedPacketType(); if(command == expectedType) { PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails); - // if we are getting inbound packets, then our tree is also viewing, and we should remember that fact. _tree->setIsViewing(true); - + const unsigned char* dataAt = reinterpret_cast(dataByteArray.data()) + numBytesPacketHeader; OCTREE_PACKET_FLAGS flags = (*(OCTREE_PACKET_FLAGS*)(dataAt)); dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); - + OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt); dataAt += sizeof(OCTREE_PACKET_SENT_TIME); - + bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT); bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT); OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int clockSkew = sourceNode ? sourceNode->getClockSkewUsec() : 0; int flightTime = arrivedAt - sentAt + clockSkew; - + OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0; int dataBytes = packetLength - (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE); diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index b6fdcc3e99..e97e05dcb9 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -54,11 +54,9 @@ void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) { _direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FRONT, 0.0f)); } -///////////////////////////////////////////////////////////////////////////////////// // ViewFrustum::calculateViewFrustum() // -// Description: this will calculate the view frustum bounds for a given position -// and direction +// Description: this will calculate the view frustum bounds for a given position and direction // // Notes on how/why this works: // http://www.lighthouse3d.com/tutorials/view-frustum-culling/view-frustums-shape/ diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index bbce7124e0..36d3346cdb 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -17,9 +17,14 @@ #include "AABox.h" #include "Plane.h" +#include "OctreeConstants.h" #include "OctreeProjectedPolygon.h" const float DEFAULT_KEYHOLE_RADIUS = 3.0f; +const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f; +const float DEFAULT_ASPECT_RATIO = 16.f/9.f; +const float DEFAULT_NEAR_CLIP = 0.08f; +const float DEFAULT_FAR_CLIP = 50.0f * TREE_SCALE; class ViewFrustum { public: