diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 104f65d7d9..b61410f22b 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,44 @@ 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 needs to be fixed, 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 { + 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 +148,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 +161,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/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index 58aea5c792..633ee3ede1 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -161,7 +161,7 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, int bytes) { OctreeQueryNode::~OctreeQueryNode() { if (_octreeSendThread) { _octreeSendThread->terminate(); - _octreeSendThread->deleteLater(); + delete _octreeSendThread; } delete[] _octreePacket; diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 121e80b808..8e7d9c37c6 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -22,8 +22,16 @@ OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer _myServer(myServer), _packetData() { + qDebug() << "client connected"; + _myServer->clientConnected(); } +OctreeSendThread::~OctreeSendThread() { + qDebug() << "client disconnected"; + _myServer->clientDisconnected(); +} + + bool OctreeSendThread::process() { quint64 start = usecTimestampNow(); bool gotLock = false; diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index ab7a7231ab..3554dcdfef 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -20,6 +20,7 @@ class OctreeSendThread : public GenericThread { public: OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer); + virtual ~OctreeSendThread(); static quint64 _totalBytes; static quint64 _totalWastedBytes; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 1fb858c884..e823722857 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -19,6 +19,8 @@ #include "OctreeServerConsts.h" OctreeServer* OctreeServer::_instance = NULL; +int OctreeServer::_clientCount = 0; + void OctreeServer::attachQueryNodeToNode(Node* newNode) { if (newNode->getLinkedData() == NULL) { @@ -35,6 +37,7 @@ OctreeServer::OctreeServer(const QByteArray& packet) : _parsedArgV(NULL), _httpManager(NULL), _packetsPerClientPerInterval(10), + _packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL), _tree(NULL), _wantPersist(true), _debugSending(false), @@ -75,8 +78,7 @@ OctreeServer::~OctreeServer() { delete _jurisdiction; _jurisdiction = NULL; - - qDebug() << "OctreeServer::run()... DONE"; + qDebug() << "OctreeServer::~OctreeServer()... DONE"; } void OctreeServer::initHTTPManager(int port) { @@ -155,12 +157,24 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& statsString += "Uptime: "; if (hours > 0) { - statsString += QString("%1 hour%2").arg(hours).arg((hours > 1) ? "s" : ""); + statsString += QString("%1 hour").arg(hours); + if (hours > 1) { + statsString += QString("s"); + } } if (minutes > 0) { - statsString += QString("%1 minute%s").arg(minutes).arg((minutes > 1) ? "s" : ""); + if (hours > 0) { + statsString += QString(" "); + } + statsString += QString("%1 minute").arg(minutes); + if (minutes > 1) { + statsString += QString("s"); + } } if (seconds > 0) { + if (hours > 0 || minutes > 0) { + statsString += QString(" "); + } statsString += QString().sprintf("%.3f seconds", seconds); } statsString += "\r\n\r\n"; @@ -180,14 +194,26 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR; int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR)); - statsString += QString("%1 File Load Took").arg(getMyServerName()); + statsString += QString("%1 File Load Took ").arg(getMyServerName()); if (hours > 0) { - statsString += QString("%1 hour%2").arg(hours).arg((hours > 1) ? "s" : ""); + statsString += QString("%1 hour").arg(hours); + if (hours > 1) { + statsString += QString("s"); + } } if (minutes > 0) { - statsString += QString("%1 minute%2").arg(minutes).arg((minutes > 1) ? "s" : ""); + if (hours > 0) { + statsString += QString(" "); + } + statsString += QString("%1 minute").arg(minutes); + if (minutes > 1) { + statsString += QString("s"); + } } if (seconds >= 0) { + if (hours > 0 || minutes > 0) { + statsString += QString(" "); + } statsString += QString().sprintf("%.3f seconds", seconds); } statsString += "\r\n"; @@ -234,6 +260,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor(); const int COLUMN_WIDTH = 10; + statsString += QString(" Configured Max PPS/Client: %1 pps/client\r\n") + .arg(locale.toString((uint)getPacketsPerClientPerSecond()).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Configured Max PPS/Server: %1 pps/server\r\n\r\n") + .arg(locale.toString((uint)getPacketsTotalPerSecond()).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Total Clients Connected: %1 clients\r\n\r\n") + .arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Total Outbound Packets: %1 packets\r\n") .arg(locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString(" Total Outbound Bytes: %1 bytes\r\n") @@ -612,15 +645,28 @@ void OctreeServer::run() { } // Check to see if the user passed in a command line option for setting packet send rate - const char* PACKETS_PER_SECOND = "--packetsPerSecond"; - const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND); - if (packetsPerSecond) { - _packetsPerClientPerInterval = atoi(packetsPerSecond) / INTERVALS_PER_SECOND; + const char* PACKETS_PER_SECOND_PER_CLIENT_MAX = "--packetsPerSecondPerClientMax"; + const char* packetsPerSecondPerClientMax = getCmdOption(_argc, _argv, PACKETS_PER_SECOND_PER_CLIENT_MAX); + if (packetsPerSecondPerClientMax) { + _packetsPerClientPerInterval = atoi(packetsPerSecondPerClientMax) / INTERVALS_PER_SECOND; if (_packetsPerClientPerInterval < 1) { _packetsPerClientPerInterval = 1; } - qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d", packetsPerSecond, _packetsPerClientPerInterval); } + qDebug("packetsPerSecondPerClientMax=%s _packetsPerClientPerInterval=%d", + packetsPerSecondPerClientMax, _packetsPerClientPerInterval); + + // Check to see if the user passed in a command line option for setting packet send rate + const char* PACKETS_PER_SECOND_TOTAL_MAX = "--packetsPerSecondTotalMax"; + const char* packetsPerSecondTotalMax = getCmdOption(_argc, _argv, PACKETS_PER_SECOND_TOTAL_MAX); + if (packetsPerSecondTotalMax) { + _packetsTotalPerInterval = atoi(packetsPerSecondTotalMax) / INTERVALS_PER_SECOND; + if (_packetsTotalPerInterval < 1) { + _packetsTotalPerInterval = 1; + } + } + qDebug("packetsPerSecondTotalMax=%s _packetsTotalPerInterval=%d", + packetsPerSecondTotalMax, _packetsTotalPerInterval); HifiSockAddr senderSockAddr; @@ -656,3 +702,4 @@ void OctreeServer::run() { connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); } + diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index e221011b76..b5d8f6fb67 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -24,6 +24,8 @@ #include "OctreeServerConsts.h" #include "OctreeInboundPacketProcessor.h" +const int DEFAULT_PACKETS_PER_INTERVAL = 2000; // some 120,000 packets per second total + /// Handles assignments of type OctreeServer - sending octrees to various clients. class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler { Q_OBJECT @@ -41,7 +43,16 @@ public: Octree* getOctree() { return _tree; } JurisdictionMap* getJurisdiction() { return _jurisdiction; } - int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; } + int getPacketsPerClientPerInterval() const { return std::min(_packetsPerClientPerInterval, + std::max(1, getPacketsTotalPerInterval() / std::max(1, getCurrentClientCount()))); } + + int getPacketsPerClientPerSecond() const { return getPacketsPerClientPerInterval() * INTERVALS_PER_SECOND; } + int getPacketsTotalPerInterval() const { return _packetsTotalPerInterval; } + int getPacketsTotalPerSecond() const { return getPacketsTotalPerInterval() * INTERVALS_PER_SECOND; } + + static int getCurrentClientCount() { return _clientCount; } + static void clientConnected() { _clientCount++; } + static void clientDisconnected() { _clientCount--; } bool isInitialLoadComplete() const { return (_persistThread) ? _persistThread->isInitialLoadComplete() : true; } bool isPersistEnabled() const { return (_persistThread) ? true : false; } @@ -81,6 +92,7 @@ protected: char _persistFilename[MAX_FILENAME_LENGTH]; int _packetsPerClientPerInterval; + int _packetsTotalPerInterval; Octree* _tree; // this IS a reaveraging tree bool _wantPersist; bool _debugSending; @@ -95,6 +107,8 @@ protected: time_t _started; quint64 _startedUSecs; + + static int _clientCount; }; #endif // __octree_server__OctreeServer__ diff --git a/examples/editVoxels.js b/examples/editVoxels.js index cd9566717a..ed63c12eb9 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -68,7 +68,8 @@ colors[4] = { red: 236, green: 174, blue: 0 }; colors[5] = { red: 234, green: 133, blue: 0 }; colors[6] = { red: 211, green: 115, blue: 0 }; colors[7] = { red: 48, green: 116, blue: 119 }; -var numColors = 8; +colors[8] = { red: 31, green: 64, blue: 64 }; +var numColors = 9; var whichColor = -1; // Starting color is 'Copy' mode // Create sounds for adding, deleting, recoloring voxels @@ -138,34 +139,46 @@ var linePreviewRight = Overlays.addOverlay("line3d", { // these will be used below -var sliderWidth = 158; -var sliderHeight = 35; +var sliderWidth = 154; +var sliderHeight = 37; // These will be our "overlay IDs" var swatches = new Array(); -var swatchHeight = 54; -var swatchWidth = 31; -var swatchesWidth = swatchWidth * numColors; +var swatchExtraPadding = 5; +var swatchHeight = 37; +var swatchWidth = 27; +var swatchesWidth = swatchWidth * numColors + numColors + swatchExtraPadding * 2; var swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2; -var swatchesY = windowDimensions.y - swatchHeight; +var swatchesY = windowDimensions.y - swatchHeight + 1; + +var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; // create the overlays, position them in a row, set their colors, and for the selected one, use a different source image // location so that it displays the "selected" marker for (s = 0; s < numColors; s++) { - var imageFromX = 12 + (s * 27); - var imageFromY = 0; - if (s == whichColor) { - imageFromY = 55; + + var extraWidth = 0; + + if (s == 0) { + extraWidth = swatchExtraPadding; + } + + var imageFromX = swatchExtraPadding - extraWidth + s * swatchWidth; + var imageFromY = swatchHeight + 1; + + var swatchX = swatchExtraPadding - extraWidth + swatchesX + ((swatchWidth - 1) * s); + + if (s == (numColors - 1)) { + extraWidth = swatchExtraPadding; } - var swatchX = swatchesX + (30 * s); swatches[s] = Overlays.addOverlay("image", { x: swatchX, y: swatchesY, - width: swatchWidth, + width: swatchWidth + extraWidth, height: swatchHeight, - subImage: { x: imageFromX, y: imageFromY, width: (swatchWidth - 1), height: swatchHeight }, - imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/swatches.svg", + subImage: { x: imageFromX, y: imageFromY, width: swatchWidth + extraWidth, height: swatchHeight }, + imageURL: toolIconUrl + "swatches.svg", color: colors[s], alpha: 1, visible: editToolsOn @@ -174,66 +187,41 @@ for (s = 0; s < numColors; s++) { // These will be our tool palette overlays -var numberOfTools = 5; -var toolHeight = 40; -var toolWidth = 62; -var toolsHeight = toolHeight * numberOfTools; -var toolsX = 0; +var numberOfTools = 3; +var toolHeight = 50; +var toolWidth = 50; +var toolVerticalSpacing = 4; +var toolsHeight = toolHeight * numberOfTools + toolVerticalSpacing * (numberOfTools - 1); +var toolsX = 8; var toolsY = (windowDimensions.y - toolsHeight) / 2; -var addToolAt = 0; -var deleteToolAt = 1; -var recolorToolAt = 2; -var eyedropperToolAt = 3; -var selectToolAt = 4; -var toolSelectedColor = { red: 255, green: 255, blue: 255 }; -var notSelectedColor = { red: 128, green: 128, blue: 128 }; +var voxelToolAt = 0; +var recolorToolAt = 1; +var eyedropperToolAt = 2; -var addTool = Overlays.addOverlay("image", { +var voxelTool = Overlays.addOverlay("image", { x: 0, y: 0, width: toolWidth, height: toolHeight, - subImage: { x: 0, y: toolHeight * addToolAt, width: toolWidth, height: toolHeight }, - imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg", - color: toolSelectedColor, - visible: false, - alpha: 0.9 - }); - -var deleteTool = Overlays.addOverlay("image", { - x: 0, y: 0, width: toolWidth, height: toolHeight, - subImage: { x: 0, y: toolHeight * deleteToolAt, width: toolWidth, height: toolHeight }, - imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg", - color: toolSelectedColor, + subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: toolIconUrl + "voxel-tool.svg", visible: false, alpha: 0.9 }); var recolorTool = Overlays.addOverlay("image", { x: 0, y: 0, width: toolWidth, height: toolHeight, - subImage: { x: 0, y: toolHeight * recolorToolAt, width: toolWidth, height: toolHeight }, - imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg", - color: toolSelectedColor, + subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: toolIconUrl + "paint-tool.svg", visible: false, alpha: 0.9 }); var eyedropperTool = Overlays.addOverlay("image", { x: 0, y: 0, width: toolWidth, height: toolHeight, - subImage: { x: 0, y: toolHeight * eyedropperToolAt, width: toolWidth, height: toolHeight }, - imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg", - color: toolSelectedColor, + subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: toolIconUrl + "eyedropper-tool.svg", visible: false, alpha: 0.9 }); - -var selectTool = Overlays.addOverlay("image", { - x: 0, y: 0, width: toolWidth, height: toolHeight, - subImage: { x: 0, y: toolHeight * selectToolAt, width: toolWidth, height: toolHeight }, - imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg", - color: toolSelectedColor, - visible: false, - alpha: 0.9 - }); - // This will create a couple of image overlays that make a "slider", we will demonstrate how to trap mouse messages to // move the slider @@ -242,35 +230,34 @@ var selectTool = Overlays.addOverlay("image", { //var sliderWidth = 158; //var sliderHeight = 35; -var sliderX = swatchesX + swatchesWidth; -var sliderY = windowDimensions.y - sliderHeight; +var sliderOffsetX = 17; +var sliderX = swatchesX - swatchWidth - sliderOffsetX; +var sliderY = windowDimensions.y - sliderHeight + 1; var slider = Overlays.addOverlay("image", { // alternate form of expressing bounds bounds: { x: sliderX, y: sliderY, width: sliderWidth, height: sliderHeight}, - imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/slider.png", - color: { red: 255, green: 255, blue: 255}, + imageURL: toolIconUrl + "voxel-size-slider-bg.svg", alpha: 1, visible: false }); - // The slider is handled in the mouse event callbacks. var isMovingSlider = false; var thumbClickOffsetX = 0; // This is the thumb of our slider -var minThumbX = 30; // relative to the x of the slider -var maxThumbX = minThumbX + 65; +var minThumbX = 20; // relative to the x of the slider +var maxThumbX = minThumbX + 90; var thumbExtents = maxThumbX - minThumbX; var thumbX = (minThumbX + maxThumbX) / 2; -var thumbY = sliderY + 9; +var thumbOffsetY = 11; +var thumbY = sliderY + thumbOffsetY; var thumb = Overlays.addOverlay("image", { x: sliderX + thumbX, y: thumbY, - width: 18, + width: 17, height: 17, - imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/thumb.png", - color: { red: 255, green: 255, blue: 255}, + imageURL: toolIconUrl + "voxel-size-slider-handle.svg", alpha: 1, visible: false }); @@ -347,11 +334,9 @@ var trackAsRecolor = false; var trackAsEyedropper = false; var trackAsOrbitOrPan = false; -var addToolSelected = true; -var deleteToolSelected = false; +var voxelToolSelected = true; var recolorToolSelected = false; var eyedropperToolSelected = false; -var selectToolSelected = false; function playRandomAddSound(audioOptions) { if (Math.random() < 0.33) { @@ -524,51 +509,12 @@ function showPreviewVoxel() { } var guidePosition; - - if (trackAsDelete || deleteToolSelected) { - guidePosition = calculateVoxelFromIntersection(intersection,"delete"); - Overlays.editOverlay(voxelPreview, { - position: guidePosition, - size: guidePosition.s + zFightingSizeAdjust, - visible: true, - color: { red: 255, green: 0, blue: 0 }, - solid: false, - alpha: 1 - }); - } else if (selectToolSelected) { - guidePosition = calculateVoxelFromIntersection(intersection,"select"); - Overlays.editOverlay(voxelPreview, { - position: guidePosition, - size: guidePosition.s + zFightingSizeAdjust, - visible: true, - color: { red: 255, green: 255, blue: 0 }, - solid: false, - alpha: 1 - }); - } else if (trackAsRecolor || recolorToolSelected || trackAsEyedropper|| eyedropperToolSelected) { - guidePosition = calculateVoxelFromIntersection(intersection,"recolor"); - - Overlays.editOverlay(voxelPreview, { - position: guidePosition, - size: guidePosition.s + zFightingSizeAdjust, - visible: true, - color: voxelColor, - solid: true, - alpha: 0.8 - }); + if (trackAsRecolor || recolorToolSelected || trackAsEyedropper || eyedropperToolSelected) { + Overlays.editOverlay(voxelPreview, { visible: true }); } else if (trackAsOrbitOrPan) { Overlays.editOverlay(voxelPreview, { visible: false }); - } else if (addToolSelected && !isExtruding) { - guidePosition = calculateVoxelFromIntersection(intersection,"add"); - - Overlays.editOverlay(voxelPreview, { - position: guidePosition, - size: (guidePosition.s - zFightingSizeAdjust), - visible: true, - color: voxelColor, - solid: true, - alpha: 0.7 - }); + } else if (voxelToolSelected && !isExtruding) { + Overlays.editOverlay(voxelPreview, { visible: true }); } else if (isExtruding) { Overlays.editOverlay(voxelPreview, { visible: false }); } @@ -587,23 +533,11 @@ function showPreviewLines() { } resultVoxel = calculateVoxelFromIntersection(intersection,""); - if (selectToolSelected) { - Overlays.editOverlay(voxelPreview, { - position: resultVoxel, - size: resultVoxel.s + zFightingSizeAdjust, - visible: true, - color: { red: 255, green: 255, blue: 0 }, - lineWidth: previewLineWidth, - solid: false, - alpha: 1 - }); - } else { - Overlays.editOverlay(voxelPreview, { visible: false }); - Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true }); - Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true }); - Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true }); - Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true }); - } + Overlays.editOverlay(voxelPreview, { visible: false }); + Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true }); + Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true }); + Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true }); + Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true }); } else { Overlays.editOverlay(voxelPreview, { visible: false }); Overlays.editOverlay(linePreviewTop, { visible: false }); @@ -829,44 +763,39 @@ function mousePressEvent(event) { isMovingSlider = true; thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb clickedOnSomething = true; - } else if (clickedOverlay == addTool) { - addToolSelected = true; - deleteToolSelected = false; + + Overlays.editOverlay(thumb, { imageURL: toolIconUrl + "voxel-size-slider-handle.svg", }); + + } else if (clickedOverlay == voxelTool) { + voxelToolSelected = true; recolorToolSelected = false; eyedropperToolSelected = false; - selectToolSelected = false; - moveTools(); - clickedOnSomething = true; - } else if (clickedOverlay == deleteTool) { - addToolSelected = false; - deleteToolSelected = true; - recolorToolSelected = false; - eyedropperToolSelected = false; - selectToolSelected = false; moveTools(); clickedOnSomething = true; } else if (clickedOverlay == recolorTool) { - addToolSelected = false; - deleteToolSelected = false; + voxelToolSelected = false; recolorToolSelected = true; eyedropperToolSelected = false; - selectToolSelected = false; moveTools(); clickedOnSomething = true; } else if (clickedOverlay == eyedropperTool) { - addToolSelected = false; - deleteToolSelected = false; + voxelToolSelected = false; recolorToolSelected = false; eyedropperToolSelected = true; - selectToolSelected = false; moveTools(); clickedOnSomething = true; - } else if (clickedOverlay == selectTool) { - addToolSelected = false; - deleteToolSelected = false; - recolorToolSelected = false; - eyedropperToolSelected = false; - selectToolSelected = true; + } else if (clickedOverlay == slider) { + + if (event.x < sliderX + minThumbX) { + thumbX -= thumbDeltaPerStep; + calcScaleFromThumb(thumbX); + } + + if (event.x > sliderX + maxThumbX) { + thumbX += thumbDeltaPerStep; + calcScaleFromThumb(thumbX); + } + moveTools(); clickedOnSomething = true; } else { @@ -908,7 +837,7 @@ function mousePressEvent(event) { startPanMode(event); isPanning = true; } - } else if (deleteToolSelected || trackAsDelete || (event.isRightButton && !trackAsEyedropper)) { + } else if (trackAsDelete || event.isRightButton && !trackAsEyedropper) { // Delete voxel voxelDetails = calculateVoxelFromIntersection(intersection,"delete"); Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); @@ -932,7 +861,7 @@ function mousePressEvent(event) { colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue); Audio.playSound(changeColorSound, audioOptions); Overlays.editOverlay(voxelPreview, { visible: false }); - } else if (addToolSelected) { + } else if (voxelToolSelected) { // Add voxel on face if (whichColor == -1) { // Copy mode - use clicked voxel color @@ -968,7 +897,7 @@ function keyPressEvent(event) { // if our tools are off, then don't do anything if (editToolsOn) { var nVal = parseInt(event.text); - if (event.text == "0") { + if (event.text == "`") { print("Color = Copy"); whichColor = -1; Audio.playSound(clickSound, audioOptions); @@ -978,7 +907,7 @@ function keyPressEvent(event) { print("Color = " + (whichColor + 1)); Audio.playSound(clickSound, audioOptions); moveTools(); - } else if (event.text == "9") { + } else if (event.text == "0") { // Create a brand new 1 meter voxel in front of your avatar var color = whichColor; if (color == -1) color = 0; @@ -1010,18 +939,6 @@ function keyPressEvent(event) { function keyReleaseEvent(event) { trackKeyReleaseEvent(event); // used by preview support - - // handle clipboard items - if (selectToolSelected) { - // menu tied to BACKSPACE, so we handle DELETE key here... - if (event.text == "DELETE") { - var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); - var intersection = Voxels.findRayIntersection(pickRay); - selectedVoxel = calculateVoxelFromIntersection(intersection,"select"); - print("the DELETE key was pressed... delete"); - Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); - } - } } function setupMenus() { @@ -1186,22 +1103,33 @@ function mouseReleaseEvent(event) { function moveTools() { // move the swatches swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2; - swatchesY = windowDimensions.y - swatchHeight; + swatchesY = windowDimensions.y - swatchHeight + 1; // create the overlays, position them in a row, set their colors, and for the selected one, use a different source image // location so that it displays the "selected" marker for (s = 0; s < numColors; s++) { - var imageFromX = 12 + (s * 27); - var imageFromY = 0; - if (s == whichColor) { - imageFromY = 55; - } - var swatchX = swatchesX + ((swatchWidth - 1) * s); + var extraWidth = 0; + if (s == 0) { + extraWidth = swatchExtraPadding; + } + + var imageFromX = swatchExtraPadding - extraWidth + s * swatchWidth; + var imageFromY = swatchHeight + 1; + if (s == whichColor) { + imageFromY = 0; + } + + var swatchX = swatchExtraPadding - extraWidth + swatchesX + ((swatchWidth - 1) * s); + + if (s == (numColors - 1)) { + extraWidth = swatchExtraPadding; + } + Overlays.editOverlay(swatches[s], { x: swatchX, y: swatchesY, - subImage: { x: imageFromX, y: imageFromY, width: (swatchWidth - 1), height: swatchHeight }, + subImage: { x: imageFromX, y: imageFromY, width: swatchWidth + extraWidth, height: swatchHeight }, color: colors[s], alpha: 1, visible: editToolsOn @@ -1210,62 +1138,45 @@ function moveTools() { // move the tools toolsY = (windowDimensions.y - toolsHeight) / 2; - addToolColor = notSelectedColor; - deleteToolColor = notSelectedColor; - recolorToolColor = notSelectedColor; - eyedropperToolColor = notSelectedColor; - selectToolColor = notSelectedColor; - if (trackAsDelete || deleteToolSelected) { - deleteToolColor = toolSelectedColor; - } else if (trackAsRecolor || recolorToolSelected) { - recolorToolColor = toolSelectedColor; + var voxelToolOffset = 1, + recolorToolOffset = 1, + eyedropperToolOffset = 1; + + if (trackAsRecolor || recolorToolSelected) { + recolorToolOffset = 2; } else if (trackAsEyedropper || eyedropperToolSelected) { - eyedropperToolColor = toolSelectedColor; - } else if (selectToolSelected) { - selectToolColor = toolSelectedColor; + eyedropperToolOffset = 2; } else if (trackAsOrbitOrPan) { // nothing gets selected in this case... } else { - addToolColor = toolSelectedColor; + voxelToolOffset = 2; } - Overlays.editOverlay(addTool, { - x: 0, y: toolsY + (toolHeight * addToolAt), width: toolWidth, height: toolHeight, - color: addToolColor, - visible: editToolsOn - }); - - Overlays.editOverlay(deleteTool, { - x: 0, y: toolsY + (toolHeight * deleteToolAt), width: toolWidth, height: toolHeight, - color: deleteToolColor, + Overlays.editOverlay(voxelTool, { + subImage: { x: 0, y: toolHeight * voxelToolOffset, width: toolWidth, height: toolHeight }, + x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * voxelToolAt), width: toolWidth, height: toolHeight, visible: editToolsOn }); Overlays.editOverlay(recolorTool, { - x: 0, y: toolsY + (toolHeight * recolorToolAt), width: toolWidth, height: toolHeight, - color: recolorToolColor, + subImage: { x: 0, y: toolHeight * recolorToolOffset, width: toolWidth, height: toolHeight }, + x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * recolorToolAt), width: toolWidth, height: toolHeight, visible: editToolsOn }); Overlays.editOverlay(eyedropperTool, { - x: 0, y: toolsY + (toolHeight * eyedropperToolAt), width: toolWidth, height: toolHeight, - color: eyedropperToolColor, + subImage: { x: 0, y: toolHeight * eyedropperToolOffset, width: toolWidth, height: toolHeight }, + x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * eyedropperToolAt), width: toolWidth, height: toolHeight, visible: editToolsOn }); - Overlays.editOverlay(selectTool, { - x: 0, y: toolsY + (toolHeight * selectToolAt), width: toolWidth, height: toolHeight, - color: selectToolColor, - visible: editToolsOn - }); - - sliderX = swatchesX + swatchesWidth; - sliderY = windowDimensions.y - sliderHeight; + sliderX = swatchesX + swatchesWidth - sliderOffsetX; + sliderY = windowDimensions.y - sliderHeight + 1; + thumbY = sliderY + thumbOffsetY; Overlays.editOverlay(slider, { x: sliderX, y: sliderY, visible: editToolsOn }); // This is the thumb of our slider - thumbY = sliderY + 9; Overlays.editOverlay(thumb, { x: sliderX + thumbX, y: thumbY, visible: editToolsOn }); } @@ -1421,11 +1332,9 @@ function scriptEnding() { for (s = 0; s < numColors; s++) { Overlays.deleteOverlay(swatches[s]); } - Overlays.deleteOverlay(addTool); - Overlays.deleteOverlay(deleteTool); + Overlays.deleteOverlay(voxelTool); Overlays.deleteOverlay(recolorTool); Overlays.deleteOverlay(eyedropperTool); - Overlays.deleteOverlay(selectTool); Overlays.deleteOverlay(slider); Overlays.deleteOverlay(thumb); Controller.releaseKeyEvents({ text: "+" }); diff --git a/examples/seeingVoxelsExample.js b/examples/seeingVoxelsExample.js new file mode 100644 index 0000000000..93f605755f --- /dev/null +++ b/examples/seeingVoxelsExample.js @@ -0,0 +1,74 @@ +// +// seeingVoxelsExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 2/26/14 +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script +// + +var count = 0; +var yawDirection = -1; +var yaw = 45; +var yawMax = 70; +var yawMin = 20; + +var isLocal = false; + +// set up our VoxelViewer with a position and orientation +var orientation = Quat.fromPitchYawRoll(0, yaw, 0); + +function init() { + if (isLocal) { + MyAvatar.position = {x: 5000, y: 500, z: 5000}; + MyAvatar.orientation = orientation; + } else { + VoxelViewer.setPosition({x: 5000, y: 500, z: 5000}); + VoxelViewer.setOrientation(orientation); + VoxelViewer.queryOctree(); + Agent.isAvatar = true; + } +} + +function keepLooking() { + //print("count =" + count); + + if (count == 0) { + init(); + } + count++; + if (count % 10 == 0) { + yaw += yawDirection; + orientation = Quat.fromPitchYawRoll(0, yaw, 0); + if (yaw > yawMax || yaw < yawMin) { + yawDirection = yawDirection * -1; + } + + print("calling VoxelViewer.queryOctree()... count=" + count + " yaw=" + yaw); + + if (isLocal) { + MyAvatar.orientation = orientation; + } else { + VoxelViewer.setOrientation(orientation); + VoxelViewer.queryOctree(); + print("VoxelViewer.getOctreeElementsCount()=" + VoxelViewer.getOctreeElementsCount()); + } + + } +} + +function scriptEnding() { + print("SCRIPT ENDNG!!!\n"); +} + +// register the call back so it fires before each data send +Script.willSendVisualDataCallback.connect(keepLooking); + +// register our scriptEnding callback +Script.scriptEnding.connect(scriptEnding); + + +// test for local... +Menu.isOptionChecked("Voxels"); +isLocal = true; // will only get here on local client diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ba623a4d04..9a3e2bde87 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1382,6 +1382,26 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { _window->activateWindow(); } +void Application::importVoxels() { + if (!_voxelImporter) { + _voxelImporter = new VoxelImporter(_window); + _voxelImporter->loadSettings(_settings); + } + + if (!_voxelImporter->exec()) { + qDebug() << "[DEBUG] Import succeeded." << endl; + } else { + qDebug() << "[DEBUG] Import failed." << endl; + if (_sharedVoxelSystem.getTree() == _voxelImporter->getVoxelTree()) { + _sharedVoxelSystem.killLocalVoxels(); + _sharedVoxelSystem.changeTree(&_clipboard); + } + } + + // restore the main window's active state + _window->activateWindow(); +} + void Application::cutVoxels(const VoxelDetail& sourceVoxel) { copyVoxels(sourceVoxel); deleteVoxelAt(sourceVoxel); @@ -1520,8 +1540,8 @@ void Application::init() { // Set up VoxelSystem after loading preferences so we can get the desired max voxel count _voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels()); - _voxels.setUseVoxelShader(Menu::getInstance()->isOptionChecked(MenuOption::UseVoxelShader)); - _voxels.setVoxelsAsPoints(Menu::getInstance()->isOptionChecked(MenuOption::VoxelsAsPoints)); + _voxels.setUseVoxelShader(false); + _voxels.setVoxelsAsPoints(false); _voxels.setDisableFastVoxelPipeline(false); _voxels.init(); @@ -1917,7 +1937,7 @@ void Application::updateMyAvatar(float deltaTime) { // actually need to calculate the view frustum planes to send these details // to the server. loadViewFrustum(_myCamera, _viewFrustum); - + // Update my voxel servers with my current voxel query... queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions); queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions); @@ -1930,26 +1950,28 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node return; } + //qDebug() << ">>> inside... queryOctree()... _viewFrustum.getFieldOfView()=" << _viewFrustum.getFieldOfView(); + bool wantExtraDebugging = getLogger()->extraDebugging(); // These will be the same for all servers, so we can set them up once and then reuse for each server we send to. - _voxelQuery.setWantLowResMoving(!Menu::getInstance()->isOptionChecked(MenuOption::DisableLowRes)); - _voxelQuery.setWantColor(!Menu::getInstance()->isOptionChecked(MenuOption::DisableColorVoxels)); - _voxelQuery.setWantDelta(!Menu::getInstance()->isOptionChecked(MenuOption::DisableDeltaSending)); - _voxelQuery.setWantOcclusionCulling(Menu::getInstance()->isOptionChecked(MenuOption::EnableOcclusionCulling)); - _voxelQuery.setWantCompression(Menu::getInstance()->isOptionChecked(MenuOption::EnableVoxelPacketCompression)); + _octreeQuery.setWantLowResMoving(true); + _octreeQuery.setWantColor(true); + _octreeQuery.setWantDelta(true); + _octreeQuery.setWantOcclusionCulling(false); + _octreeQuery.setWantCompression(true); - _voxelQuery.setCameraPosition(_viewFrustum.getPosition()); - _voxelQuery.setCameraOrientation(_viewFrustum.getOrientation()); - _voxelQuery.setCameraFov(_viewFrustum.getFieldOfView()); - _voxelQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio()); - _voxelQuery.setCameraNearClip(_viewFrustum.getNearClip()); - _voxelQuery.setCameraFarClip(_viewFrustum.getFarClip()); - _voxelQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition()); - _voxelQuery.setOctreeSizeScale(Menu::getInstance()->getVoxelSizeScale()); - _voxelQuery.setBoundaryLevelAdjust(Menu::getInstance()->getBoundaryLevelAdjust()); + _octreeQuery.setCameraPosition(_viewFrustum.getPosition()); + _octreeQuery.setCameraOrientation(_viewFrustum.getOrientation()); + _octreeQuery.setCameraFov(_viewFrustum.getFieldOfView()); + _octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio()); + _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip()); + _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip()); + _octreeQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition()); + _octreeQuery.setOctreeSizeScale(Menu::getInstance()->getVoxelSizeScale()); + _octreeQuery.setBoundaryLevelAdjust(Menu::getInstance()->getBoundaryLevelAdjust()); - unsigned char voxelQueryPacket[MAX_PACKET_SIZE]; + unsigned char queryPacket[MAX_PACKET_SIZE]; // Iterate all of the nodes, and get a count of how many voxel servers we have... int totalServers = 0; @@ -1989,7 +2011,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node } } - if (wantExtraDebugging && unknownJurisdictionServers > 0) { + if (wantExtraDebugging) { qDebug("Servers: total %d, in view %d, unknown jurisdiction %d", totalServers, inViewServers, unknownJurisdictionServers); } @@ -2010,7 +2032,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node } } - if (wantExtraDebugging && unknownJurisdictionServers > 0) { + if (wantExtraDebugging) { qDebug("perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer); } @@ -2059,7 +2081,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node } if (inView) { - _voxelQuery.setMaxOctreePacketsPerSecond(perServerPPS); + _octreeQuery.setMaxOctreePacketsPerSecond(perServerPPS); } else if (unknownView) { if (wantExtraDebugging) { qDebug() << "no known jurisdiction for node " << *node << ", give it budget of " @@ -2070,11 +2092,11 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node // If there's only one server, then don't do this, and just let the normal voxel query pass through // as expected... this way, we will actually get a valid scene if there is one to be seen if (totalServers > 1) { - _voxelQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1)); + _octreeQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1)); const glm::quat OFF_IN_NEGATIVE_SPACE = glm::quat(-0.5, 0, -0.5, 1.0); - _voxelQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE); - _voxelQuery.setCameraNearClip(0.1f); - _voxelQuery.setCameraFarClip(0.1f); + _octreeQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE); + _octreeQuery.setCameraNearClip(0.1f); + _octreeQuery.setCameraFarClip(0.1f); if (wantExtraDebugging) { qDebug() << "Using 'minimal' camera position for node" << *node; } @@ -2083,23 +2105,23 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node qDebug() << "Using regular camera position for node" << *node; } } - _voxelQuery.setMaxOctreePacketsPerSecond(perUnknownServer); + _octreeQuery.setMaxOctreePacketsPerSecond(perUnknownServer); } else { - _voxelQuery.setMaxOctreePacketsPerSecond(0); + _octreeQuery.setMaxOctreePacketsPerSecond(0); } // set up the packet for sending... - unsigned char* endOfVoxelQueryPacket = voxelQueryPacket; + unsigned char* endOfQueryPacket = queryPacket; // insert packet type/version and node UUID - endOfVoxelQueryPacket += populatePacketHeader(reinterpret_cast(endOfVoxelQueryPacket), packetType); + endOfQueryPacket += populatePacketHeader(reinterpret_cast(endOfQueryPacket), packetType); // encode the query data... - endOfVoxelQueryPacket += _voxelQuery.getBroadcastData(endOfVoxelQueryPacket); + endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket); - int packetLength = endOfVoxelQueryPacket - voxelQueryPacket; + int packetLength = endOfQueryPacket - queryPacket; // make sure we still have an active socket - nodeList->writeDatagram(reinterpret_cast(voxelQueryPacket), packetLength, node); + nodeList->writeDatagram(reinterpret_cast(queryPacket), packetLength, node); // Feed number of bytes to corresponding channel of the bandwidth meter _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(packetLength); @@ -2341,14 +2363,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxels..."); - if (!Menu::getInstance()->isOptionChecked(MenuOption::DontRenderVoxels)) { - _voxels.render(); - - // double check that our LOD doesn't need to be auto-adjusted - // only adjust if our option is set - if (Menu::getInstance()->isOptionChecked(MenuOption::AutoAdjustLOD)) { - Menu::getInstance()->autoAdjustLOD(_fps); - } + + _voxels.render(); + + // double check that our LOD doesn't need to be auto-adjusted + // adjust it unless we were asked to disable this feature + if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD)) { + Menu::getInstance()->autoAdjustLOD(_fps); } } @@ -3406,7 +3427,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 @@ -3437,7 +3458,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/Application.h b/interface/src/Application.h index 846d952e5f..4e91876dfb 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include "Audio.h" @@ -235,6 +235,7 @@ public slots: void nodeKilled(SharedNodePointer node); void packetSent(quint64 length); + void importVoxels(); // doesn't include source voxel because it goes to clipboard void cutVoxels(const VoxelDetail& sourceVoxel); void copyVoxels(const VoxelDetail& sourceVoxel); void pasteVoxels(const VoxelDetail& sourceVoxel); @@ -387,7 +388,7 @@ private: Oscilloscope _audioScope; - VoxelQuery _voxelQuery; // NodeData derived class for querying voxels from voxel server + OctreeQuery _octreeQuery; // NodeData derived class for querying voxels from voxel server AvatarManager _avatarManager; MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) 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/Menu.cpp b/interface/src/Menu.cpp index e333aee2e4..830ac17ebd 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -238,29 +238,12 @@ Menu::Menu() : true, appInstance, SLOT(setRenderVoxels(bool))); - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontRenderVoxels); - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontCallOpenGLForVoxels); - - _useVoxelShader = addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseVoxelShader, 0, - false, appInstance->getVoxels(), SLOT(setUseVoxelShader(bool))); - - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelsAsPoints, 0, - false, appInstance->getVoxels(), SLOT(setVoxelsAsPoints(bool))); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion); - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges); - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AutoAdjustLOD); addActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools())); - - QMenu* voxelProtoOptionsMenu = voxelOptionsMenu->addMenu("Voxel Server Protocol Options"); - - addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DisableColorVoxels); - addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DisableLowRes); - addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DisableDeltaSending); - addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::EnableVoxelPacketCompression); - addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::EnableOcclusionCulling); - addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DestructiveAddVoxel); + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges); + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DisableAutoAdjustLOD); QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 041f1fcd50..84a5eef481 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -201,7 +201,7 @@ namespace MenuOption { const QString AmbientOcclusion = "Ambient Occlusion"; const QString Avatars = "Avatars"; const QString Atmosphere = "Atmosphere"; - const QString AutoAdjustLOD = "Automatically Adjust LOD"; + const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD"; const QString AutomaticallyAuditTree = "Automatically Audit Tree Stats"; const QString Bandwidth = "Bandwidth Display"; const QString BandwidthDetails = "Bandwidth Details"; @@ -217,19 +217,11 @@ namespace MenuOption { const QString CullSharedFaces = "Cull Shared Voxel Faces"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DecreaseVoxelSize = "Decrease Voxel Size"; - const QString DestructiveAddVoxel = "Create Voxel is Destructive"; - const QString DisableColorVoxels = "Disable Colored Voxels"; - const QString DisableDeltaSending = "Disable Delta Sending"; - const QString DisableLowRes = "Disable Lower Resolution While Moving"; const QString DisplayFrustum = "Display Frustum"; const QString DisplayHands = "Display Hands"; const QString DisplayHandTargets = "Display Hand Targets"; const QString FilterSixense = "Smooth Sixense Movement"; - const QString DontRenderVoxels = "Don't call _voxels.render()"; - const QString DontCallOpenGLForVoxels = "Don't call glDrawRangeElementsEXT() for Voxels"; const QString Enable3DTVMode = "Enable 3DTV Mode"; - const QString EnableOcclusionCulling = "Enable Occlusion Culling"; - const QString EnableVoxelPacketCompression = "Enable Voxel Packet Compression"; const QString AudioNoiseReduction = "Audio Noise Reduction"; const QString EchoServerAudio = "Echo Server Audio"; const QString EchoLocalAudio = "Echo Local Audio"; @@ -301,8 +293,6 @@ namespace MenuOption { const QString TreeStats = "Calculate Tree Stats"; const QString TransmitterDrive = "Transmitter Drive"; const QString Quit = "Quit"; - const QString UseVoxelShader = "Use Voxel Shader"; - const QString VoxelsAsPoints = "Draw Voxels as Points"; const QString Voxels = "Voxels"; const QString VoxelDrumming = "Voxel Drumming"; const QString VoxelMode = "Cycle Voxel Mode"; 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/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 99f6171b7a..24d1e73ee2 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -867,7 +867,6 @@ void VoxelSystem::checkForCulling() { bool fullRedraw = (_lastKnownVoxelSizeScale != Menu::getInstance()->getVoxelSizeScale() || _lastKnownBoundaryLevelAdjust != Menu::getInstance()->getBoundaryLevelAdjust()); - // track that these values _lastKnownVoxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); _lastKnownBoundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); @@ -1386,7 +1385,6 @@ void VoxelSystem::render() { updateVBOs(); - bool dontCallOpenGLDraw = Menu::getInstance()->isOptionChecked(MenuOption::DontCallOpenGLForVoxels); // if not don't... then do... if (_useVoxelShader) { PerformanceWarning warn(showWarnings,"render().. _useVoxelShader openGL.."); @@ -1432,9 +1430,7 @@ void VoxelSystem::render() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboVoxelsIndicesID); - if (!dontCallOpenGLDraw) { - glDrawElements(GL_POINTS, _voxelsInReadArrays, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); //The starting point of the IBO - } + glDrawElements(GL_POINTS, _voxelsInReadArrays, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); //The starting point of the IBO // deactivate vertex and color arrays after drawing glDisableClientState(GL_VERTEX_ARRAY); @@ -1477,7 +1473,7 @@ void VoxelSystem::render() { // draw voxels in 6 passes - if (!dontCallOpenGLDraw) { + { PerformanceWarning warn(showWarnings, "render().. glDrawRangeElementsEXT()..."); glNormal3f(0,1.0f,0); @@ -1510,7 +1506,6 @@ void VoxelSystem::render() { glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); } - { PerformanceWarning warn(showWarnings, "render().. cleanup after glDrawRangeElementsEXT()..."); 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 new file mode 100644 index 0000000000..4ab85e7de8 --- /dev/null +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -0,0 +1,241 @@ +// +// OctreeHeadlessViewer.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 2/26/14 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// + +#include + +#include "OctreeHeadlessViewer.h" + +OctreeHeadlessViewer::OctreeHeadlessViewer() : + 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() { +} + +void OctreeHeadlessViewer::init() { + OctreeRenderer::init(); + setViewFrustum(&_viewFrustum); +} + +void OctreeHeadlessViewer::queryOctree() { + NodeType_t serverType = getMyNodeType(); + PacketType packetType = getMyQueryMessageType(); + NodeToJurisdictionMap& jurisdictions = *_jurisdictionListener->getJurisdictions(); + + 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); + _octreeQuery.setWantDelta(true); + _octreeQuery.setWantOcclusionCulling(false); + _octreeQuery.setWantCompression(true); // TODO: should be on by default + + _octreeQuery.setCameraPosition(_viewFrustum.getPosition()); + _octreeQuery.setCameraOrientation(_viewFrustum.getOrientation()); + _octreeQuery.setCameraFov(_viewFrustum.getFieldOfView()); + _octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio()); + _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip()); + _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip()); + _octreeQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition()); + _octreeQuery.setOctreeSizeScale(getVoxelSizeScale()); + _octreeQuery.setBoundaryLevelAdjust(getBoundaryLevelAdjust()); + + unsigned char queryPacket[MAX_PACKET_SIZE]; + + // Iterate all of the nodes, and get a count of how many voxel servers we have... + int totalServers = 0; + int inViewServers = 0; + int unknownJurisdictionServers = 0; + + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + // only send to the NodeTypes that are serverType + if (node->getActiveSocket() != NULL && node->getType() == serverType) { + totalServers++; + + // get the server bounds for this server + QUuid nodeUUID = node->getUUID(); + + // if we haven't heard from this voxel server, go ahead and send it a query, so we + // can get the jurisdiction... + if (jurisdictions.find(nodeUUID) == jurisdictions.end()) { + unknownJurisdictionServers++; + } else { + const JurisdictionMap& map = (jurisdictions)[nodeUUID]; + + unsigned char* rootCode = map.getRootOctalCode(); + + if (rootCode) { + VoxelPositionSize rootDetails; + voxelDetailsForCode(rootCode, rootDetails); + AABox serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s); + serverBounds.scale(TREE_SCALE); + + ViewFrustum::location serverFrustumLocation = _viewFrustum.boxInFrustum(serverBounds); + + if (serverFrustumLocation != ViewFrustum::OUTSIDE) { + inViewServers++; + } + } + } + } + } + + if (wantExtraDebugging) { + qDebug("Servers: total %d, in view %d, unknown jurisdiction %d", + totalServers, inViewServers, unknownJurisdictionServers); + } + + int perServerPPS = 0; + const int SMALL_BUDGET = 10; + int perUnknownServer = SMALL_BUDGET; + int totalPPS = getMaxPacketsPerSecond(); + + // determine PPS based on number of servers + if (inViewServers >= 1) { + // set our preferred PPS to be exactly evenly divided among all of the voxel servers... and allocate 1 PPS + // for each unknown jurisdiction server + perServerPPS = (totalPPS / inViewServers) - (unknownJurisdictionServers * perUnknownServer); + } else { + if (unknownJurisdictionServers > 0) { + perUnknownServer = (totalPPS / unknownJurisdictionServers); + } + } + + if (wantExtraDebugging) { + qDebug("perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer); + } + + NodeList* nodeList = NodeList::getInstance(); + + foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { + // only send to the NodeTypes that are serverType + if (node->getActiveSocket() != NULL && node->getType() == serverType) { + + + // get the server bounds for this server + QUuid nodeUUID = node->getUUID(); + + bool inView = false; + bool unknownView = false; + + // if we haven't heard from this voxel server, go ahead and send it a query, so we + // can get the jurisdiction... + if (jurisdictions.find(nodeUUID) == jurisdictions.end()) { + unknownView = true; // assume it's in view + if (wantExtraDebugging) { + qDebug() << "no known jurisdiction for node " << *node << ", assume it's visible."; + } + } else { + const JurisdictionMap& map = (jurisdictions)[nodeUUID]; + + unsigned char* rootCode = map.getRootOctalCode(); + + if (rootCode) { + VoxelPositionSize rootDetails; + voxelDetailsForCode(rootCode, rootDetails); + AABox serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s); + serverBounds.scale(TREE_SCALE); + + ViewFrustum::location serverFrustumLocation = _viewFrustum.boxInFrustum(serverBounds); + if (serverFrustumLocation != ViewFrustum::OUTSIDE) { + inView = true; + } else { + inView = false; + } + } else { + if (wantExtraDebugging) { + qDebug() << "Jurisdiction without RootCode for node " << *node << ". That's unusual!"; + } + } + } + + 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 " + << perUnknownServer << " to send us jurisdiction."; + } + + // set the query's position/orientation to be degenerate in a manner that will get the scene quickly + // If there's only one server, then don't do this, and just let the normal voxel query pass through + // as expected... this way, we will actually get a valid scene if there is one to be seen + if (totalServers > 1) { + _octreeQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1)); + const glm::quat OFF_IN_NEGATIVE_SPACE = glm::quat(-0.5, 0, -0.5, 1.0); + _octreeQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE); + _octreeQuery.setCameraNearClip(0.1f); + _octreeQuery.setCameraFarClip(0.1f); + if (wantExtraDebugging) { + qDebug() << "Using 'minimal' camera position for node" << *node; + } + } else { + if (wantExtraDebugging) { + qDebug() << "Using regular camera position for node" << *node; + } + } + _octreeQuery.setMaxOctreePacketsPerSecond(perUnknownServer); + } else { + _octreeQuery.setMaxOctreePacketsPerSecond(0); + } + // set up the packet for sending... + unsigned char* endOfQueryPacket = queryPacket; + + // insert packet type/version and node UUID + endOfQueryPacket += populatePacketHeader(reinterpret_cast(endOfQueryPacket), packetType); + + // encode the query data... + endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket); + + int packetLength = endOfQueryPacket - queryPacket; + + // make sure we still have an active socket + nodeList->writeDatagram(reinterpret_cast(queryPacket), packetLength, node); + } + } +} + + +int OctreeHeadlessViewer::parseOctreeStats(const QByteArray& packet, const SharedNodePointer& sourceNode) { + + OctreeSceneStats temp; + int statsMessageLength = temp.unpackFromMessage(reinterpret_cast(packet.data()), packet.size()); + + // TODO: actually do something with these stats, like expose them to JS... + + return statsMessageLength; +} + +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 new file mode 100644 index 0000000000..fc32dbc682 --- /dev/null +++ b/libraries/octree/src/OctreeHeadlessViewer.h @@ -0,0 +1,73 @@ +// +// OctreeHeadlessViewer.h +// hifi +// +// Created by Brad Hefta-Gaub on 2/26/14 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__OctreeHeadlessViewer__ +#define __hifi__OctreeHeadlessViewer__ + +#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" + +// Generic client side Octree renderer class. +class OctreeHeadlessViewer : public OctreeRenderer { + Q_OBJECT +public: + OctreeHeadlessViewer(); + virtual ~OctreeHeadlessViewer(); + virtual void renderElement(OctreeElement* element, RenderArgs* args) { /* swallow these */ }; + + virtual void init(); + 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(); + + // 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; } + int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } + int getMaxPacketsPerSecond() const { return _maxPacketsPerSecond; } + + unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); } + +private: + ViewFrustum _viewFrustum; + JurisdictionListener* _jurisdictionListener; + OctreeQuery _octreeQuery; + float _voxelSizeScale; + int _boundaryLevelAdjust; + int _maxPacketsPerSecond; +}; + +#endif /* defined(__hifi__OctreeHeadlessViewer__) */ \ No newline at end of file diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index 5c67715ab9..51e235c4e2 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -6,22 +6,11 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -#include -#include -#include - -#include -#include #include -#include + #include "OctreeConstants.h" - #include "OctreeQuery.h" -using namespace std; - -static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed - OctreeQuery::OctreeQuery() : NodeData(), _cameraPosition(0,0,0), diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index 800d30b7cd..3b53aaa501 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -9,8 +9,6 @@ #ifndef __hifi__OctreeQuery__ #define __hifi__OctreeQuery__ -#include - /* VS2010 defines stdint.h, but not inttypes.h */ #if defined(_MSC_VER) typedef signed char int8_t; @@ -27,17 +25,9 @@ typedef unsigned long long quint64; #endif -#include - #include #include -#include -#include -#include - -#include - #include // First bitset diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 9a00ce245b..06007cb0dc 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -27,42 +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); @@ -91,7 +97,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, - getDataSourceUUID(), sourceNode); + sourceUUID, sourceNode); _tree->lockForWrite(); OctreePacketData packetData(packetIsCompressed); packetData.loadFinalizedContent(dataAt, sectionLength); diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index e29cdc4020..52b0a3fc2e 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -13,6 +13,8 @@ #include #include +#include + #include #include @@ -31,7 +33,8 @@ public: // Generic client side Octree renderer class. -class OctreeRenderer { +class OctreeRenderer : public QObject { + Q_OBJECT public: OctreeRenderer(); virtual ~OctreeRenderer(); @@ -51,8 +54,6 @@ public: /// render the content of the octree virtual void render(); - void setDataSourceUUID(const QUuid& dataSourceUUID) { _dataSourceUUID = dataSourceUUID; } - const QUuid& getDataSourceUUID() const { return _dataSourceUUID; } ViewFrustum* getViewFrustum() const { return _viewFrustum; } void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; } @@ -62,7 +63,6 @@ public: void clear(); protected: Octree* _tree; - QUuid _dataSourceUUID; ViewFrustum* _viewFrustum; }; diff --git a/libraries/octree/src/OctreeScriptingInterface.h b/libraries/octree/src/OctreeScriptingInterface.h index 3c832cbae8..3003950bbf 100644 --- a/libraries/octree/src/OctreeScriptingInterface.h +++ b/libraries/octree/src/OctreeScriptingInterface.h @@ -32,6 +32,9 @@ public: virtual NodeType_t getServerNodeType() const = 0; virtual OctreeEditPacketSender* createPacketSender() = 0; +private slots: + void cleanupManagedObjects(); + public slots: /// Set the desired max packet size in bytes that should be created void setMaxPacketSize(int maxPacketSize) { return _packetSender->setMaxPacketSize(maxPacketSize); } @@ -84,9 +87,6 @@ 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; 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: diff --git a/libraries/particles/src/ParticleTreeHeadlessViewer.cpp b/libraries/particles/src/ParticleTreeHeadlessViewer.cpp new file mode 100644 index 0000000000..f5384c3fec --- /dev/null +++ b/libraries/particles/src/ParticleTreeHeadlessViewer.cpp @@ -0,0 +1,36 @@ +// +// ParticleTreeHeadlessViewer.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 2/26/14 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// + +#include "ParticleTreeHeadlessViewer.h" + +ParticleTreeHeadlessViewer::ParticleTreeHeadlessViewer() : + OctreeHeadlessViewer() { +} + +ParticleTreeHeadlessViewer::~ParticleTreeHeadlessViewer() { +} + +void ParticleTreeHeadlessViewer::init() { + OctreeHeadlessViewer::init(); +} + + +void ParticleTreeHeadlessViewer::update() { + if (_tree) { + ParticleTree* tree = static_cast(_tree); + if (tree->tryLockForWrite()) { + tree->update(); + tree->unlock(); + } + } +} + +void ParticleTreeHeadlessViewer::processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { + static_cast(_tree)->processEraseMessage(dataByteArray, sourceNode); +} diff --git a/libraries/particles/src/ParticleTreeHeadlessViewer.h b/libraries/particles/src/ParticleTreeHeadlessViewer.h new file mode 100644 index 0000000000..08aa12280c --- /dev/null +++ b/libraries/particles/src/ParticleTreeHeadlessViewer.h @@ -0,0 +1,42 @@ +// +// ParticleTreeHeadlessViewer.h +// hifi +// +// Created by Brad Hefta-Gaub on 2/26/14 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__ParticleTreeHeadlessViewer__ +#define __hifi__ParticleTreeHeadlessViewer__ + +#include +#include +#include +#include +#include +#include "ParticleTree.h" +#include + +// Generic client side Octree renderer class. +class ParticleTreeHeadlessViewer : public OctreeHeadlessViewer { + Q_OBJECT +public: + ParticleTreeHeadlessViewer(); + virtual ~ParticleTreeHeadlessViewer(); + + virtual Octree* createTree() { return new ParticleTree(true); } + virtual NodeType_t getMyNodeType() const { return NodeType::ParticleServer; } + virtual PacketType getMyQueryMessageType() const { return PacketTypeParticleQuery; } + virtual PacketType getExpectedPacketType() const { return PacketTypeParticleData; } + + void update(); + + ParticleTree* getTree() { return (ParticleTree*)_tree; } + + void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode); + + virtual void init(); +}; + +#endif /* defined(__hifi__ParticleTreeHeadlessViewer__) */ \ No newline at end of file diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h index 729561571e..3aa19629f0 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.h +++ b/libraries/particles/src/ParticlesScriptingInterface.h @@ -26,7 +26,19 @@ public: void setParticleTree(ParticleTree* particleTree) { _particleTree = particleTree; } ParticleTree* getParticleTree(ParticleTree*) { return _particleTree; } + +private slots: + /// inbound slots for external collision systems + void forwardParticleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel) { + qDebug() << "forwardParticleCollisionWithVoxel()"; + emit particleCollisionWithVoxel(particleID, voxel); + } + void forwardParticleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB) { + qDebug() << "forwardParticleCollisionWithParticle()"; + emit particleCollisionWithParticle(idA, idB); + } + public slots: /// adds a particle with the specific properties ParticleID addParticle(const ParticleProperties& properties); @@ -54,15 +66,6 @@ public slots: /// this function will not find any particles in script engine contexts which don't have access to particles QVector findParticles(const glm::vec3& center, float radius) const; - /// inbound slots for external collision systems - void forwardParticleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel) { - emit particleCollisionWithVoxel(particleID, voxel); - } - - void forwardParticleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB) { - emit particleCollisionWithParticle(idA, idB); - } - signals: void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel); void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB); diff --git a/libraries/voxels/src/VoxelQuery.cpp b/libraries/voxels/src/VoxelQuery.cpp deleted file mode 100644 index 239b352ca1..0000000000 --- a/libraries/voxels/src/VoxelQuery.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// VoxelQuery.cpp -// hifi -// -// Created by Brad Hefta-Gaub on 10/24/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#include "VoxelQuery.h" \ No newline at end of file diff --git a/libraries/voxels/src/VoxelQuery.h b/libraries/voxels/src/VoxelQuery.h deleted file mode 100644 index 6898430d82..0000000000 --- a/libraries/voxels/src/VoxelQuery.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// VoxelQuery.h -// hifi -// -// Created by Stephen Birarda on 4/9/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __hifi__VoxelQuery__ -#define __hifi__VoxelQuery__ - -#include - -class VoxelQuery : public OctreeQuery { - -public: - // currently just an alias - VoxelQuery() : OctreeQuery() {}; -private: - // privatize the copy constructor and assignment operator so they cannot be called - VoxelQuery(const VoxelQuery&); - VoxelQuery& operator= (const VoxelQuery&); -}; - -#endif /* defined(__hifi__VoxelQuery__) */ diff --git a/libraries/voxels/src/VoxelTreeHeadlessViewer.cpp b/libraries/voxels/src/VoxelTreeHeadlessViewer.cpp new file mode 100644 index 0000000000..999ef4fa36 --- /dev/null +++ b/libraries/voxels/src/VoxelTreeHeadlessViewer.cpp @@ -0,0 +1,22 @@ +// +// VoxelTreeHeadlessViewer.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 2/26/14 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// + +#include "VoxelTreeHeadlessViewer.h" + +VoxelTreeHeadlessViewer::VoxelTreeHeadlessViewer() : + OctreeHeadlessViewer() { +} + +VoxelTreeHeadlessViewer::~VoxelTreeHeadlessViewer() { +} + +void VoxelTreeHeadlessViewer::init() { + OctreeHeadlessViewer::init(); +} + diff --git a/libraries/voxels/src/VoxelTreeHeadlessViewer.h b/libraries/voxels/src/VoxelTreeHeadlessViewer.h new file mode 100644 index 0000000000..9c7481c929 --- /dev/null +++ b/libraries/voxels/src/VoxelTreeHeadlessViewer.h @@ -0,0 +1,38 @@ +// +// VoxelTreeHeadlessViewer.h +// hifi +// +// Created by Brad Hefta-Gaub on 2/26/14 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__VoxelTreeHeadlessViewer__ +#define __hifi__VoxelTreeHeadlessViewer__ + +#include +#include +#include +#include +#include +#include "VoxelTree.h" +#include + +// Generic client side Octree renderer class. +class VoxelTreeHeadlessViewer : public OctreeHeadlessViewer { + Q_OBJECT +public: + VoxelTreeHeadlessViewer(); + virtual ~VoxelTreeHeadlessViewer(); + + virtual Octree* createTree() { return new VoxelTree(true); } + virtual NodeType_t getMyNodeType() const { return NodeType::VoxelServer; } + virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; } + virtual PacketType getExpectedPacketType() const { return PacketTypeVoxelData; } + + VoxelTree* getTree() { return (VoxelTree*)_tree; } + + virtual void init(); +}; + +#endif /* defined(__hifi__VoxelTreeHeadlessViewer__) */ \ No newline at end of file