diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 13b1f2bdcf..46f4d233c2 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -25,7 +26,8 @@ Agent::Agent(const QByteArray& packet) : ThreadedAssignment(packet), _voxelEditSender(), - _particleEditSender() + _particleEditSender(), + _avatarAudioStream(NULL) { // be the parent of the script engine so it gets moved when we do _scriptEngine.setParent(this); @@ -34,6 +36,30 @@ Agent::Agent(const QByteArray& packet) : _scriptEngine.getParticlesScriptingInterface()->setPacketSender(&_particleEditSender); } +Agent::~Agent() { + delete _avatarAudioStream; +} + +const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000)) + 0.5); + +void Agent::setSendAvatarAudioStream(bool sendAvatarAudioStream) { + if (sendAvatarAudioStream) { + // the agentAudioStream number of samples is related to the ScriptEngine callback rate + _avatarAudioStream = new int16_t[SCRIPT_AUDIO_BUFFER_SAMPLES]; + + // fill the _audioStream with zeroes to start + memset(_avatarAudioStream, 0, SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t)); + + _scriptEngine.setNumAvatarAudioBufferSamples(SCRIPT_AUDIO_BUFFER_SAMPLES); + _scriptEngine.setAvatarAudioBuffer(_avatarAudioStream); + } else { + delete _avatarAudioStream; + _avatarAudioStream = NULL; + + _scriptEngine.setAvatarAudioBuffer(NULL); + } +} + void Agent::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 1b41636874..a051f42faf 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -28,12 +28,17 @@ class Agent : public ThreadedAssignment { Q_OBJECT Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) + Q_PROPERTY(bool sendAvatarAudioStream READ isSendingAvatarAudioStream WRITE setSendAvatarAudioStream) public: Agent(const QByteArray& packet); + ~Agent(); void setIsAvatar(bool isAvatar) { QMetaObject::invokeMethod(&_scriptEngine, "setIsAvatar", Q_ARG(bool, isAvatar)); } bool isAvatar() const { return _scriptEngine.isAvatar(); } + void setSendAvatarAudioStream(bool sendAvatarAudioStream); + bool isSendingAvatarAudioStream() const { return (bool) _scriptEngine.sendsAvatarAudioStream(); } + public slots: void run(); void readPendingDatagrams(); @@ -45,6 +50,8 @@ private: ParticleTreeHeadlessViewer _particleViewer; VoxelTreeHeadlessViewer _voxelViewer; + + int16_t* _avatarAudioStream; }; #endif /* defined(__hifi__Agent__) */ diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 751583960e..ef69771607 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -241,6 +241,9 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging()) + || nodeData->hasLodChanged(); + bool somethingToSend = true; // assume we have something // FOR NOW... node tells us if it wants to receive only view frustum deltas @@ -365,10 +368,6 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue << " Wasted:" << _totalWastedBytes; } - // start tracking our stats - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) - && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - // If we're starting a full scene, then definitely we want to empty the nodeBag if (isFullScene) { nodeData->nodeBag.deleteAll(); @@ -382,6 +381,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue } ::startSceneSleepTime = _usleepTime; + // start tracking our stats nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot(), _myServer->getJurisdiction()); // This is the start of "resending" the scene. @@ -432,10 +432,6 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && - nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index e5979c561f..a425052970 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -544,8 +544,16 @@ void DomainServer::readAvailableDatagrams() { // construct the requested assignment from the packet data Assignment requestAssignment(receivedPacket); - qDebug() << "Received a request for assignment type" << requestAssignment.getType() - << "from" << senderSockAddr; + // Suppress these for Assignment::AgentType to once per 5 seconds + static quint64 lastNoisyMessage = usecTimestampNow(); + quint64 timeNow = usecTimestampNow(); + const quint64 NOISY_TIME_ELAPSED = 5 * USECS_PER_SECOND; + bool noisyMessage = false; + if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) { + qDebug() << "Received a request for assignment type" << requestAssignment.getType() + << "from" << senderSockAddr; + noisyMessage = true; + } SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); @@ -562,8 +570,15 @@ void DomainServer::readAvailableDatagrams() { nodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } else { - qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() - << "from" << senderSockAddr; + if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) { + qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() + << "from" << senderSockAddr; + noisyMessage = true; + } + } + + if (noisyMessage) { + lastNoisyMessage = timeNow; } } } diff --git a/examples/editVoxels.js b/examples/editVoxels.js index b083128485..31d483d798 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -26,6 +26,10 @@ var PIXELS_PER_EXTRUDE_VOXEL = 16; var WHEEL_PIXELS_PER_SCALE_CHANGE = 100; var MAX_VOXEL_SCALE = 1.0; var MIN_VOXEL_SCALE = 1.0 / Math.pow(2.0, 8.0); +var WHITE_COLOR = { red: 255, green: 255, blue: 255 }; + +var MAX_PASTE_VOXEL_SCALE = 256; +var MIN_PASTE_VOXEL_SCALE = .256; var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting var previewLineWidth = 1.5; @@ -199,6 +203,8 @@ var voxelToolAt = 0; var recolorToolAt = 1; var eyedropperToolAt = 2; +var pasteModeColor = { red: 132, green: 61, blue: 255 }; + var voxelTool = Overlays.addOverlay("image", { x: 0, y: 0, width: toolWidth, height: toolHeight, subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, @@ -262,7 +268,7 @@ var thumb = Overlays.addOverlay("image", { visible: false }); -var pointerVoxelScale = 0; // this is the voxel scale used for click to add or delete +var pointerVoxelScale = Math.floor(MAX_VOXEL_SCALE + MIN_VOXEL_SCALE) / 2; // this is the voxel scale used for click to add or delete var pointerVoxelScaleSet = false; // if voxel scale has not yet been set, we use the intersection size var pointerVoxelScaleSteps = 8; // the number of slider position steps @@ -271,6 +277,106 @@ var pointerVoxelScaleMin = Math.pow(2, (1-pointerVoxelScaleOriginStep)); var pointerVoxelScaleMax = Math.pow(2, (pointerVoxelScaleSteps-pointerVoxelScaleOriginStep)); var thumbDeltaPerStep = thumbExtents / (pointerVoxelScaleSteps - 1); + + +///////////////////////////////////// IMPORT MODULE /////////////////////////////// +// Move the following code to a separate file when include will be available. +var importTree; +var importPreview; +var importBoundaries; +var isImporting; +var importPosition; +var importScale; + +function initImport() { + importPreview = Overlays.addOverlay("localvoxels", { + name: "import", + position: { x: 0, y: 0, z: 0}, + scale: 1, + visible: false + }); + importBoundaries = Overlays.addOverlay("cube", { + position: { x: 0, y: 0, z: 0 }, + scale: 1, + color: { red: 128, blue: 128, green: 128 }, + solid: false, + visible: false + }) + isImporting = false; + importPosition = { x: 0, y: 0, z: 0 }; + importScale = 0; +} + +function importVoxels() { + if (Clipboard.importVoxels()) { + isImporting = true; + if (importScale <= 0) { + importScale = 1; + } + } else { + isImporting = false; + } + + return isImporting; +} + +function moveImport(position) { + if (0 < position.x && 0 < position.y && 0 < position.z) { + importPosition = position; + Overlays.editOverlay(importPreview, { + position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } + }); + Overlays.editOverlay(importBoundaries, { + position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } + }); + } +} + +function rescaleImport(scale) { + if (0 < scale) { + importScale = scale; + Overlays.editOverlay(importPreview, { + scale: importScale + }); + Overlays.editOverlay(importBoundaries, { + scale: importScale + }); + } +} + +function showImport(doShow) { + Overlays.editOverlay(importPreview, { + visible: doShow + }); + Overlays.editOverlay(importBoundaries, { + visible: doShow + }); +} + +function placeImport() { + if (isImporting) { + Clipboard.pasteVoxel(importPosition.x, importPosition.y, importPosition.z, importScale); + isImporting = false; + } +} + +function cancelImport() { + if (isImporting) { + isImporting = false; + showImport(false); + } +} + +function cleanupImport() { + Overlays.deleteOverlay(importPreview); + Overlays.deleteOverlay(importBoundaries); + isImporting = false; + importPostion = { x: 0, y: 0, z: 0 }; + importScale = 0; +} +/////////////////////////////////// END IMPORT MODULE ///////////////////////////// +initImport(); + if (editToolsOn) { moveTools(); } @@ -295,6 +401,13 @@ function calcScaleFromThumb(newThumbX) { thumbAt = newThumbX - minThumbX; thumbStep = Math.floor((thumbAt/ thumbExtents) * (pointerVoxelScaleSteps-1)) + 1; pointerVoxelScale = Math.pow(2, (thumbStep-pointerVoxelScaleOriginStep)); + + // if importing, rescale import ... + if (isImporting) { + var importScale = (pointerVoxelScale / MAX_VOXEL_SCALE) * MAX_PASTE_VOXEL_SCALE; + rescaleImport(importScale); + } + // now reset the display accordingly... calcThumbFromScale(pointerVoxelScale); @@ -308,7 +421,23 @@ function setAudioPosition() { audioOptions.position = Vec3.sum(camera, forwardVector); } -function getNewVoxelPosition() { +function getNewPasteVoxel(pickRay) { + + var voxelSize = MIN_PASTE_VOXEL_SCALE + (MAX_PASTE_VOXEL_SCALE - MIN_PASTE_VOXEL_SCALE) * pointerVoxelScale - 1; + var origin = { x: pickRay.direction.x, y: pickRay.direction.y, z: pickRay.direction.z }; + + origin.x += pickRay.origin.x; + origin.y += pickRay.origin.y; + origin.z += pickRay.origin.z; + + origin.x -= voxelSize / 2; + origin.y -= voxelSize / 2; + origin.z += voxelSize / 2; + + return {origin: origin, voxelSize: voxelSize}; +} + +function getNewVoxelPosition() { var camera = Camera.getPosition(); var forwardVector = Quat.getFront(MyAvatar.orientation); var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, NEW_VOXEL_DISTANCE_FROM_CAMERA)); @@ -337,6 +466,7 @@ var trackAsOrbitOrPan = false; var voxelToolSelected = true; var recolorToolSelected = false; var eyedropperToolSelected = false; +var pasteMode = false; function playRandomAddSound(audioOptions) { if (Math.random() < 0.33) { @@ -523,6 +653,38 @@ function showPreviewVoxel() { function showPreviewLines() { var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); + + if (pasteMode) { // free voxel pasting + + Overlays.editOverlay(voxelPreview, { visible: false }); + Overlays.editOverlay(linePreviewLeft, { visible: false }); + + var pasteVoxel = getNewPasteVoxel(pickRay); + + // X axis + Overlays.editOverlay(linePreviewBottom, { + position: pasteVoxel.origin, + end: {x: pasteVoxel.origin.x + pasteVoxel.voxelSize, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z }, + visible: true + }); + + // Y axis + Overlays.editOverlay(linePreviewRight, { + position: pasteVoxel.origin, + end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y + pasteVoxel.voxelSize, z: pasteVoxel.origin.z }, + visible: true + }); + + // Z axis + Overlays.editOverlay(linePreviewTop, { + position: pasteVoxel.origin, + end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z - pasteVoxel.voxelSize }, + visible: true + }); + + return; + } + var intersection = Voxels.findRayIntersection(pickRay); if (intersection.intersects) { @@ -617,6 +779,8 @@ function trackKeyReleaseEvent(event) { if (editToolsOn) { if (event.text == "ESC") { pointerVoxelScaleSet = false; + pasteMode = false; + moveTools(); } if (event.text == "-") { thumbX -= thumbDeltaPerStep; @@ -821,6 +985,23 @@ function mousePressEvent(event) { var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Voxels.findRayIntersection(pickRay); audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction); + + if (isImporting) { + print("placing import..."); + placeImport(); + showImport(false); + moveTools(); + return; + } + + if (pasteMode) { + var pasteVoxel = getNewPasteVoxel(pickRay); + Clipboard.pasteVoxel(pasteVoxel.origin.x, pasteVoxel.origin.y, pasteVoxel.origin.z, pasteVoxel.voxelSize); + pasteMode = false; + moveTools(); + return; + } + if (intersection.intersects) { // if the user hasn't updated the if (!pointerVoxelScaleSet) { @@ -967,25 +1148,42 @@ function cleanupMenus() { function menuItemEvent(menuItem) { // handle clipboard items - if (selectToolSelected) { + if (editToolsOn) { + var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); var intersection = Voxels.findRayIntersection(pickRay); selectedVoxel = calculateVoxelFromIntersection(intersection,"select"); if (menuItem == "Copy") { print("copying..."); Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + pasteMode = true; + moveTools(); } if (menuItem == "Cut") { print("cutting..."); Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + pasteMode = true; + moveTools(); } if (menuItem == "Paste") { - print("pasting..."); - Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + if (isImporting) { + print("placing import..."); + placeImport(); + showImport(false); + } else { + print("pasting..."); + Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + pasteMode = false; + moveTools(); } if (menuItem == "Delete") { print("deleting..."); - Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + if (isImporting) { + cancelImport(); + } else { + Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } } if (menuItem == "Export Voxels") { @@ -993,8 +1191,11 @@ function menuItemEvent(menuItem) { Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } if (menuItem == "Import Voxels") { - print("import"); - Clipboard.importVoxels(); + print("importing..."); + if (importVoxels()) { + showImport(true); + } + moveTools(); } if (menuItem == "Nudge") { print("nudge"); @@ -1149,19 +1350,23 @@ function moveTools() { recolorToolOffset = 1, eyedropperToolOffset = 1; - if (trackAsRecolor || recolorToolSelected) { + var voxelToolColor = WHITE_COLOR; + + if (recolorToolSelected) { recolorToolOffset = 2; - } else if (trackAsEyedropper || eyedropperToolSelected) { + } else if (eyedropperToolSelected) { eyedropperToolOffset = 2; - } else if (trackAsOrbitOrPan) { - // nothing gets selected in this case... } else { + if (pasteMode) { + voxelToolColor = pasteModeColor; + } voxelToolOffset = 2; } Overlays.editOverlay(voxelTool, { subImage: { x: 0, y: toolHeight * voxelToolOffset, width: toolWidth, height: toolHeight }, x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * voxelToolAt), width: toolWidth, height: toolHeight, + color: voxelToolColor, visible: editToolsOn }); @@ -1323,14 +1528,27 @@ function checkControllers() { } function update(deltaTime) { - var newWindowDimensions = Controller.getViewportDimensions(); - if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) { - windowDimensions = newWindowDimensions; - moveTools(); - } - if (editToolsOn) { + var newWindowDimensions = Controller.getViewportDimensions(); + if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) { + windowDimensions = newWindowDimensions; + moveTools(); + } + checkControllers(); + + // Move Import Preview + if (isImporting) { + var position = MyAvatar.position; + var forwardVector = Quat.getFront(MyAvatar.orientation); + var targetPosition = Vec3.sum(position, Vec3.multiply(forwardVector, importScale)); + var newPosition = { + x: Math.floor(targetPosition.x / importScale) * importScale, + y: Math.floor(targetPosition.y / importScale) * importScale, + z: Math.floor(targetPosition.z / importScale) * importScale + } + moveImport(newPosition); + } } } @@ -1356,6 +1574,11 @@ function wheelEvent(event) { calcThumbFromScale(pointerVoxelScale); trackMouseEvent(event); wheelPixelsMoved = 0; + + if (isImporting) { + var importScale = (pointerVoxelScale / MAX_VOXEL_SCALE) * MAX_PASTE_VOXEL_SCALE; + rescaleImport(importScale); + } } } @@ -1388,6 +1611,7 @@ function scriptEnding() { Overlays.deleteOverlay(thumb); Controller.releaseKeyEvents({ text: "+" }); Controller.releaseKeyEvents({ text: "-" }); + cleanupImport(); cleanupMenus(); } Script.scriptEnding.connect(scriptEnding); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d049fc0154..1cb0344f86 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -73,8 +73,13 @@ file (GLOB_RECURSE QT_UI_FILES ui/*.ui) # have qt5 wrap them and generate the appropriate header files qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") +# grab the resource files in resources +file (GLOB_RECURSE QT_RESOURCE_FILES resources/*.qrc) +# have qt5 wrap them and generate the appropriate source files +qt5_add_resources(QT_RESOURCES "${QT_RESOURCE_FILES}") + # add them to the interface source files -set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}") +set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}") set(QM ${TARGET_NAME}_en.qm) set(TS ${TARGET_NAME}_en.ts) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 685c6e3f67..72e5ee3d3e 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -28,19 +28,19 @@ ChatWindow - + Chat - + Connecting to XMPP... - - + + online now: diff --git a/interface/resources/images/close.svg b/interface/resources/images/close.svg new file mode 100644 index 0000000000..8fe4bf4bdb --- /dev/null +++ b/interface/resources/images/close.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/interface/resources/resources.qrc b/interface/resources/resources.qrc new file mode 100644 index 0000000000..372fa8b1d4 --- /dev/null +++ b/interface/resources/resources.qrc @@ -0,0 +1,5 @@ + + + images/close.svg + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f709b24eb3..8091e58545 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -140,9 +140,13 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _fps(120.0f), _justStarted(true), _voxelImporter(NULL), + _importSucceded(false), + _sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard), _wantToKillLocalVoxels(false), + _viewFrustum(), + _lastQueriedViewFrustum(), + _lastQueriedTime(usecTimestampNow()), _audioScope(256, 200, true), - _myAvatar(), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _mouseX(0), _mouseY(0), @@ -1365,6 +1369,8 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { } void Application::importVoxels() { + _importSucceded = false; + if (!_voxelImporter) { _voxelImporter = new VoxelImporter(_window); _voxelImporter->loadSettings(_settings); @@ -1372,6 +1378,7 @@ void Application::importVoxels() { if (!_voxelImporter->exec()) { qDebug() << "[DEBUG] Import succeeded." << endl; + _importSucceded = true; } else { qDebug() << "[DEBUG] Import failed." << endl; if (_sharedVoxelSystem.getTree() == _voxelImporter->getVoxelTree()) { @@ -1382,6 +1389,8 @@ void Application::importVoxels() { // restore the main window's active state _window->activateWindow(); + + emit importDone(); } void Application::cutVoxels(const VoxelDetail& sourceVoxel) { @@ -1473,10 +1482,9 @@ void Application::init() { // Cleanup of the original shared tree _sharedVoxelSystem.init(); - VoxelTree* tmpTree = _sharedVoxelSystem.getTree(); - _sharedVoxelSystem.changeTree(&_clipboard); - delete tmpTree; - + + _voxelImporter = new VoxelImporter(_window); + _environment.init(); _glowEffect.init(); @@ -1895,8 +1903,19 @@ void Application::updateMyAvatar(float deltaTime) { loadViewFrustum(_myCamera, _viewFrustum); // Update my voxel servers with my current voxel query... - queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions); - queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions); + quint64 now = usecTimestampNow(); + quint64 sinceLastQuery = now - _lastQueriedTime; + const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND; + bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY; + bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum); + + // if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it + if (queryIsDue || viewIsDifferentEnough) { + _lastQueriedTime = now; + queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions); + queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions); + _lastQueriedViewFrustum = _viewFrustum; + } } void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) { @@ -2745,8 +2764,8 @@ void Application::displayStats() { voxelStats.str(""); QString packetsString = locale.toString((int)voxelPacketsToProcess); QString maxString = locale.toString((int)_recentMaxPackets); - voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData() - << " [Recent Max: " << maxString.toLocal8Bit().constData() << "]"; + voxelStats << "Voxel Packets to Process: " << qPrintable(packetsString) + << " [Recent Max: " << qPrintable(maxString) << "]"; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); } @@ -2769,7 +2788,7 @@ void Application::displayStats() { // Server Voxels voxelStats.str(""); - voxelStats << "Server voxels: " << serversTotalString.toLocal8Bit().constData(); + voxelStats << "Server voxels: " << qPrintable(serversTotalString); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); @@ -2779,8 +2798,8 @@ void Application::displayStats() { voxelStats.str(""); voxelStats << - "Internal: " << serversInternalString.toLocal8Bit().constData() << " " << - "Leaves: " << serversLeavesString.toLocal8Bit().constData() << ""; + "Internal: " << qPrintable(serversInternalString) << " " << + "Leaves: " << qPrintable(serversLeavesString) << ""; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); } @@ -2790,7 +2809,7 @@ void Application::displayStats() { // Local Voxels voxelStats.str(""); - voxelStats << "Local voxels: " << localTotalString.toLocal8Bit().constData(); + voxelStats << "Local voxels: " << qPrintable(localTotalString); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); @@ -2802,8 +2821,17 @@ void Application::displayStats() { voxelStats.str(""); voxelStats << - "Internal: " << localInternalString.toLocal8Bit().constData() << " " << - "Leaves: " << localLeavesString.toLocal8Bit().constData() << ""; + "Internal: " << qPrintable(localInternalString) << " " << + "Leaves: " << qPrintable(localLeavesString) << ""; + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); + } + + // LOD Details + if (_statsExpanded) { + voxelStats.str(""); + QString displayLODDetails = Menu::getInstance()->getLODFeedbackText(); + voxelStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); } diff --git a/interface/src/Application.h b/interface/src/Application.h index f1437c62ba..a84a455cb2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -156,6 +156,7 @@ public: VoxelTree* getVoxelTree() { return _voxels.getTree(); } ParticleTreeRenderer* getParticles() { return &_particles; } MetavoxelSystem* getMetavoxels() { return &_metavoxels; } + bool getImportSucceded() { return _importSucceded; } VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; } VoxelTree* getClipboard() { return &_clipboard; } Environment* getEnvironment() { return &_environment; } @@ -224,6 +225,9 @@ signals: /// Fired when we're rendering in-world interface elements; allows external parties to hook in. void renderingInWorldInterface(); + /// Fired when the import window is closed + void importDone(); + public slots: void domainChanged(const QString& domainHostname); void updateWindowTitle(); @@ -350,13 +354,13 @@ private: glm::vec3 _gravity; // Frame Rate Measurement + int _frameCount; float _fps; timeval _applicationStartupTime; timeval _timerStart, _timerEnd; timeval _lastTimeUpdated; bool _justStarted; - Stars _stars; BuckyBalls _buckyBalls; @@ -364,6 +368,7 @@ private: VoxelSystem _voxels; VoxelTree _clipboard; // if I copy/paste VoxelImporter* _voxelImporter; + bool _importSucceded; VoxelSystem _sharedVoxelSystem; ViewFrustum _sharedVoxelSystemViewFrustum; @@ -376,6 +381,8 @@ private: MetavoxelSystem _metavoxels; ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. + ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels, particles) + quint64 _lastQueriedTime; Oscilloscope _audioScope; diff --git a/interface/src/ClipboardScriptingInterface.cpp b/interface/src/ClipboardScriptingInterface.cpp index d9a8f04d90..2ddef524aa 100644 --- a/interface/src/ClipboardScriptingInterface.cpp +++ b/interface/src/ClipboardScriptingInterface.cpp @@ -9,6 +9,7 @@ #include "ClipboardScriptingInterface.h" ClipboardScriptingInterface::ClipboardScriptingInterface() { + connect(this, SIGNAL(readyToImport()), Application::getInstance(), SLOT(importVoxels())); } void ClipboardScriptingInterface::cutVoxel(const VoxelDetail& sourceVoxel) { @@ -70,12 +71,17 @@ void ClipboardScriptingInterface::exportVoxel(float x, float y, float z, float s z / (float)TREE_SCALE, s / (float)TREE_SCALE }; - QMetaObject::invokeMethod(Application::getInstance(), "exportVoxels", - Q_ARG(const VoxelDetail&, sourceVoxel)); + Application::getInstance()->exportVoxels(sourceVoxel); } -void ClipboardScriptingInterface::importVoxels() { - QMetaObject::invokeMethod(Application::getInstance(), "importVoxels"); +bool ClipboardScriptingInterface::importVoxels() { + qDebug() << "[DEBUG] Importing ... "; + QEventLoop loop; + connect(Application::getInstance(), SIGNAL(importDone()), &loop, SLOT(quit())); + emit readyToImport(); + loop.exec(); + + return Application::getInstance()->getImportSucceded(); } void ClipboardScriptingInterface::nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) { diff --git a/interface/src/ClipboardScriptingInterface.h b/interface/src/ClipboardScriptingInterface.h index 99747f56f6..78e72d6de0 100644 --- a/interface/src/ClipboardScriptingInterface.h +++ b/interface/src/ClipboardScriptingInterface.h @@ -18,6 +18,9 @@ class ClipboardScriptingInterface : public QObject { public: ClipboardScriptingInterface(); +signals: + void readyToImport(); + public slots: void cutVoxel(const VoxelDetail& sourceVoxel); void cutVoxel(float x, float y, float z, float s); @@ -34,7 +37,7 @@ public slots: void exportVoxel(const VoxelDetail& sourceVoxel); void exportVoxel(float x, float y, float z, float s); - void importVoxels(); + bool importVoxels(); void nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec); void nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 21c470c0cf..95b94bdcbf 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1039,15 +1039,10 @@ void Menu::showChat() { if (!_chatWindow) { _chatWindow = new ChatWindow(); QMainWindow* mainWindow = Application::getInstance()->getWindow(); - - // the height of the title bar is given by frameGeometry().height() - geometry().height() - // however, frameGeometry() is initialised after showing (Qt queries the OS windowing system) - // on the other hand, moving a window after showing it flickers; so just use some reasonable value - int titleBarHeight = 16; _chatWindow->setGeometry(mainWindow->width() - _chatWindow->width(), - mainWindow->geometry().y() + titleBarHeight, + mainWindow->geometry().y(), _chatWindow->width(), - mainWindow->height() - titleBarHeight); + mainWindow->height()); _chatWindow->show(); } _chatWindow->raise(); @@ -1091,6 +1086,40 @@ void Menu::octreeStatsDetailsClosed() { } } +QString Menu::getLODFeedbackText() { + // determine granularity feedback + int boundaryLevelAdjust = getBoundaryLevelAdjust(); + QString granularityFeedback; + + switch (boundaryLevelAdjust) { + case 0: { + granularityFeedback = QString("at standard granularity."); + } break; + case 1: { + granularityFeedback = QString("at half of standard granularity."); + } break; + case 2: { + granularityFeedback = QString("at a third of standard granularity."); + } break; + default: { + granularityFeedback = QString("at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1); + } break; + } + + // distance feedback + float voxelSizeScale = getVoxelSizeScale(); + float relativeToDefault = voxelSizeScale / DEFAULT_OCTREE_SIZE_SCALE; + QString result; + if (relativeToDefault > 1.01) { + result = QString("%1 further %2").arg(relativeToDefault,8,'f',2).arg(granularityFeedback); + } else if (relativeToDefault > 0.99) { + result = QString("the default distance %1").arg(granularityFeedback); + } else { + result = QString("%1 of default %2").arg(relativeToDefault,8,'f',3).arg(granularityFeedback); + } + return result; +} + void Menu::autoAdjustLOD(float currentFPS) { // NOTE: our first ~100 samples at app startup are completely all over the place, and we don't // really want to count them in our average, so we will ignore the real frame rates and stuff diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 380fe2d3b7..00ef51178a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -84,6 +84,7 @@ public: void handleViewFrustumOffsetKeyModifier(int key); // User Tweakable LOD Items + QString getLODFeedbackText(); void autoAdjustLOD(float currentFPS); void setVoxelSizeScale(float sizeScale); float getVoxelSizeScale() const { return _voxelSizeScale; } diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index aa628bac7b..a8bdeb1114 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1177,8 +1177,6 @@ void VoxelSystem::init() { } void VoxelSystem::changeTree(VoxelTree* newTree) { - disconnect(_tree, 0, this, 0); - _tree = newTree; _tree->setDirtyBit(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6cd7523a14..b7e3d675a5 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -109,9 +109,6 @@ void Avatar::simulate(float deltaTime) { _shouldRenderBillboard = true; } - // copy velocity so we can use it later for acceleration - glm::vec3 oldVelocity = getVelocity(); - getHand()->simulate(deltaTime, false); _skeletonModel.setLODDistance(getLODDistance()); @@ -192,7 +189,7 @@ void Avatar::render(bool forShadowMap) { { // glow when moving in the distance - const float GLOW_DISTANCE = 5.0f; + const float GLOW_DISTANCE = 20.0f; Glower glower(_moving && lengthToTarget > GLOW_DISTANCE && !forShadowMap ? 1.0f : 0.0f); // render body diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 4699b360f6..f405358710 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -146,7 +146,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin // rotate palm according to average finger direction float directionLength = glm::length(direction); - const int MIN_ROTATION_FINGERS = 3; + const unsigned int MIN_ROTATION_FINGERS = 3; if (directionLength > EPSILON && palm.getNumFingers() >= MIN_ROTATION_FINGERS) { applyRotationDelta(jointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction), false); getJointRotation(jointIndex, palmRotation, true); diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 287aa4e5f6..0482dbf84c 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -60,7 +60,7 @@ void SixenseManager::update(float deltaTime) { // Either find a palm matching the sixense controller, or make a new one PalmData* palm; bool foundHand = false; - for (int j = 0; j < hand->getNumPalms(); j++) { + for (size_t j = 0; j < hand->getNumPalms(); j++) { if (hand->getPalms()[j].getSixenseID() == data.controller_index) { palm = &(hand->getPalms()[j]); foundHand = true; @@ -128,7 +128,7 @@ void SixenseManager::update(float deltaTime) { } // if the controllers haven't been moved in a while, disable - const int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000; + const unsigned int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000; if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) { for (vector::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) { it->setActive(false); diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index cda14dc807..8bf5583a02 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -66,7 +66,7 @@ template QVariant readBinaryArray(QDataStream& in) { in >> compressedLength; QVector values; - const int DEFLATE_ENCODING = 1; + const unsigned int DEFLATE_ENCODING = 1; if (encoding == DEFLATE_ENCODING) { // preface encoded data with uncompressed length QByteArray compressed(sizeof(quint32) + compressedLength, 0); @@ -163,7 +163,7 @@ FBXNode parseBinaryFBXNode(QDataStream& in) { in >> nameLength; FBXNode node; - const int MIN_VALID_OFFSET = 40; + const unsigned int MIN_VALID_OFFSET = 40; if (endOffset < MIN_VALID_OFFSET || nameLength == 0) { // use a null name to indicate a null node return node; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f657ef01d7..21fdc80858 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -328,6 +328,8 @@ bool Model::render(float alpha) { glDisable(GL_COLOR_MATERIAL); + glEnable(GL_CULL_FACE); + // render opaque meshes with alpha testing glEnable(GL_ALPHA_TEST); @@ -339,8 +341,6 @@ bool Model::render(float alpha) { // render translucent meshes afterwards, with back face culling - glEnable(GL_CULL_FACE); - renderMeshes(alpha, true); glDisable(GL_CULL_FACE); diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 7a3f1ab906..3a1c26ee35 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -28,7 +28,7 @@ const int NUM_MESSAGES_TO_TIME_STAMP = 20; const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); ChatWindow::ChatWindow() : - QDialog(Application::getInstance()->getGLWidget(), Qt::Tool), + QDialog(Application::getInstance()->getGLWidget(), Qt::CustomizeWindowHint), ui(new Ui::ChatWindow), numMessagesAfterLastTimeStamp(0) { @@ -179,12 +179,13 @@ void ChatWindow::participantsChanged() { } void ChatWindow::messageReceived(const QXmppMessage& message) { + if (message.type() != QXmppMessage::GroupChat) { + return; + } + QLabel* userLabel = new QLabel(getParticipantName(message.from())); - QFont font = userLabel->font(); - font.setBold(true); - userLabel->setFont(font); userLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - userLabel->setStyleSheet("padding: 2px;"); + userLabel->setStyleSheet("padding: 2px; font-weight: bold"); userLabel->setAlignment(Qt::AlignTop); QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "\\1")); diff --git a/interface/src/ui/LocalVoxelsOverlay.cpp b/interface/src/ui/LocalVoxelsOverlay.cpp index 01a885dedc..7eaf9ed5c5 100644 --- a/interface/src/ui/LocalVoxelsOverlay.cpp +++ b/interface/src/ui/LocalVoxelsOverlay.cpp @@ -40,10 +40,12 @@ void LocalVoxelsOverlay::update(float deltatime) { _voxelSystem->init(); } - if (_voxelCount != _tree->getOctreeElementsCount()) { + _tree->lockForRead(); + if (_visible && _voxelCount != _tree->getOctreeElementsCount()) { _voxelCount = _tree->getOctreeElementsCount(); _voxelSystem->forceRedrawEntireTree(); } + _tree->unlock(); } void LocalVoxelsOverlay::render() { @@ -59,8 +61,11 @@ void LocalVoxelsOverlay::render() { void LocalVoxelsOverlay::setProperties(const QScriptValue &properties) { Volume3DOverlay::setProperties(properties); + if (properties.property("scale").isValid()) { + setSize(properties.property("scale").toVariant().toFloat()); + } + QScriptValue treeName = properties.property("name"); - // if "end" property was not there, check to see if they included aliases: endPoint, or p2 if (treeName.isValid()) { if ((_treeName = treeName.toString()) == DOMAIN_TREE_NAME) { qDebug() << "addOverlay(): Can't create overlay from domain tree"; diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index 4cf4a29bf1..564669757c 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -68,7 +68,7 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : const unsigned redish = 0xfff00000; palette.setColor(QPalette::WindowText, QColor::fromRgb(redish)); _feedback->setPalette(palette); - _feedback->setText(getFeedbackText()); + _feedback->setText(Menu::getInstance()->getLODFeedbackText()); const int FEEDBACK_WIDTH = 350; _feedback->setFixedWidth(FEEDBACK_WIDTH); form->addRow("You can see... ", _feedback); @@ -81,40 +81,6 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : this->QDialog::setLayout(form); } -QString LodToolsDialog::getFeedbackText() { - // determine granularity feedback - int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); - QString granularityFeedback; - - switch (boundaryLevelAdjust) { - case 0: { - granularityFeedback = QString("at standard granularity."); - } break; - case 1: { - granularityFeedback = QString("at half of standard granularity."); - } break; - case 2: { - granularityFeedback = QString("at a third of standard granularity."); - } break; - default: { - granularityFeedback = QString("at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1); - } break; - } - - // distance feedback - float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); - float relativeToDefault = voxelSizeScale / DEFAULT_OCTREE_SIZE_SCALE; - QString result; - if (relativeToDefault > 1.01) { - result = QString("%1 further %2").arg(relativeToDefault,8,'f',2).arg(granularityFeedback); - } else if (relativeToDefault > 0.99) { - result = QString("the default distance %1").arg(granularityFeedback); - } else { - result = QString("%1 of default %2").arg(relativeToDefault,8,'f',3).arg(granularityFeedback); - } - return result; -} - LodToolsDialog::~LodToolsDialog() { delete _feedback; delete _lodSize; @@ -124,19 +90,19 @@ LodToolsDialog::~LodToolsDialog() { void LodToolsDialog::reloadSliders() { _lodSize->setValue(Menu::getInstance()->getVoxelSizeScale() / TREE_SCALE); _boundaryLevelAdjust->setValue(Menu::getInstance()->getBoundaryLevelAdjust()); - _feedback->setText(getFeedbackText()); + _feedback->setText(Menu::getInstance()->getLODFeedbackText()); } void LodToolsDialog::sizeScaleValueChanged(int value) { float realValue = value * TREE_SCALE; Menu::getInstance()->setVoxelSizeScale(realValue); - _feedback->setText(getFeedbackText()); + _feedback->setText(Menu::getInstance()->getLODFeedbackText()); } void LodToolsDialog::boundaryLevelValueChanged(int value) { Menu::getInstance()->setBoundaryLevelAdjust(value); - _feedback->setText(getFeedbackText()); + _feedback->setText(Menu::getInstance()->getLODFeedbackText()); } void LodToolsDialog::resetClicked(bool checked) { diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index ee96cffd7e..98a5fce5f8 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -36,8 +36,6 @@ protected: void closeEvent(QCloseEvent*); private: - QString getFeedbackText(); - QSlider* _lodSize; QSlider* _boundaryLevelAdjust; QLabel* _feedback; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index b1842e0619..eed8a7b6f3 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -387,7 +387,7 @@ void BoxSetTool::render() { glm::quat rotation = _editor->getGridRotation(); glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glm::quat inverseRotation = glm::inverse(rotation); glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index 974964da04..daf5a68587 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -157,9 +157,9 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { statsValue.str(""); statsValue << - "Total: " << localTotalString.toLocal8Bit().constData() << " / " << - "Internal: " << localInternalString.toLocal8Bit().constData() << " / " << - "Leaves: " << localLeavesString.toLocal8Bit().constData() << ""; + "Total: " << qPrintable(localTotalString) << " / " << + "Internal: " << qPrintable(localInternalString) << " / " << + "Leaves: " << qPrintable(localLeavesString) << ""; label->setText(statsValue.str().c_str()); // iterate all the current voxel stats, and list their sending modes, total their voxels, etc... @@ -212,9 +212,9 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { label = _labels[_serverVoxels]; statsValue.str(""); statsValue << - "Total: " << serversTotalString.toLocal8Bit().constData() << " / " << - "Internal: " << serversInternalString.toLocal8Bit().constData() << " / " << - "Leaves: " << serversLeavesString.toLocal8Bit().constData() << ""; + "Total: " << qPrintable(serversTotalString) << " / " << + "Internal: " << qPrintable(serversInternalString) << " / " << + "Leaves: " << qPrintable(serversLeavesString) << ""; label->setText(statsValue.str().c_str()); showAllOctreeServers(); @@ -290,7 +290,7 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser AABox serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s); serverBounds.scale(TREE_SCALE); serverDetails << " jurisdiction: " - << rootCodeHex.toLocal8Bit().constData() + << qPrintable(rootCodeHex) << " [" << rootDetails.x << ", " << rootDetails.y << ", " @@ -315,13 +315,25 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser const unsigned long USECS_PER_MSEC = 1000; float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; + float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; + float lastFullPackets = stats.getLastFullTotalPackets(); + float lastFullPPS = lastFullPackets; + if (lastFullSendInSeconds > 0) { + lastFullPPS = lastFullPackets / lastFullSendInSeconds; + } QString lastFullEncodeString = locale.toString(lastFullEncode); QString lastFullSendString = locale.toString(lastFullSend); + QString lastFullPacketsString = locale.toString(lastFullPackets); + QString lastFullBytesString = locale.toString((uint)stats.getLastFullTotalBytes()); + QString lastFullPPSString = locale.toString(lastFullPPS); extraDetails << "
" << "Last Full Scene... " << - "Encode Time: " << lastFullEncodeString.toLocal8Bit().constData() << " ms " << - "Send Time: " << lastFullSendString.toLocal8Bit().constData() << " ms "; + "Encode: " << qPrintable(lastFullEncodeString) << " ms " << + "Send: " << qPrintable(lastFullSendString) << " ms " << + "Packets: " << qPrintable(lastFullPacketsString) << " " << + "Bytes: " << qPrintable(lastFullBytesString) << " " << + "Rate: " << qPrintable(lastFullPPSString) << " PPS"; for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); @@ -334,42 +346,51 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser QString internalString = locale.toString((uint)stats.getTotalInternal()); QString leavesString = locale.toString((uint)stats.getTotalLeaves()); - serverDetails << "
" << "Node UUID: " << - nodeUUID.toString().toLocal8Bit().constData() << " "; + serverDetails << "
" << "Node UUID: " << qPrintable(nodeUUID.toString()) << " "; serverDetails << "
" << "Voxels: " << - totalString.toLocal8Bit().constData() << " total " << - internalString.toLocal8Bit().constData() << " internal " << - leavesString.toLocal8Bit().constData() << " leaves "; + qPrintable(totalString) << " total " << + qPrintable(internalString) << " internal " << + qPrintable(leavesString) << " leaves "; QString incomingPacketsString = locale.toString((uint)stats.getIncomingPackets()); QString incomingBytesString = locale.toString((uint)stats.getIncomingBytes()); QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes()); QString incomingOutOfOrderString = locale.toString((uint)stats.getIncomingOutOfOrder()); + QString incomingLateString = locale.toString((uint)stats.getIncomingLate()); + QString incomingReallyLateString = locale.toString((uint)stats.getIncomingReallyLate()); + QString incomingEarlyString = locale.toString((uint)stats.getIncomingEarly()); QString incomingLikelyLostString = locale.toString((uint)stats.getIncomingLikelyLost()); + QString incomingRecovered = locale.toString((uint)stats.getIncomingRecovered()); + QString incomingDuplicateString = locale.toString((uint)stats.getIncomingPossibleDuplicate()); int clockSkewInMS = node->getClockSkewUsec() / (int)USECS_PER_MSEC; QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage()); QString incomingPingTimeString = locale.toString(node->getPingMs()); QString incomingClockSkewString = locale.toString(clockSkewInMS); - serverDetails << "
" << "Incoming Packets: " << - incomingPacketsString.toLocal8Bit().constData() << - " Out of Order: " << incomingOutOfOrderString.toLocal8Bit().constData() << - " Likely Lost: " << incomingLikelyLostString.toLocal8Bit().constData(); + serverDetails << "
" << "Incoming Packets: " << qPrintable(incomingPacketsString) << + "/ Lost: " << qPrintable(incomingLikelyLostString) << + "/ Recovered: " << qPrintable(incomingRecovered); + + serverDetails << "
" << " Out of Order: " << qPrintable(incomingOutOfOrderString) << + "/ Early: " << qPrintable(incomingEarlyString) << + "/ Late: " << qPrintable(incomingLateString) << + "/ Really Late: " << qPrintable(incomingReallyLateString) << + "/ Duplicate: " << qPrintable(incomingDuplicateString); serverDetails << "
" << - " Average Flight Time: " << incomingFlightTimeString.toLocal8Bit().constData() << " msecs"; + " Average Flight Time: " << qPrintable(incomingFlightTimeString) << " msecs"; serverDetails << "
" << - " Average Ping Time: " << incomingPingTimeString.toLocal8Bit().constData() << " msecs"; + " Average Ping Time: " << qPrintable(incomingPingTimeString) << " msecs"; serverDetails << "
" << - " Average Clock Skew: " << incomingClockSkewString.toLocal8Bit().constData() << " msecs"; - + " Average Clock Skew: " << qPrintable(incomingClockSkewString) << " msecs"; + serverDetails << "
" << "Incoming" << - " Bytes: " << incomingBytesString.toLocal8Bit().constData() << - " Wasted Bytes: " << incomingWastedBytesString.toLocal8Bit().constData(); + " Bytes: " << qPrintable(incomingBytesString) << + " Wasted Bytes: " << qPrintable(incomingWastedBytesString); serverDetails << extraDetails.str(); if (_extraServerDetails[serverCount-1] == MORE) { diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 199d9b42e3..9959c638b2 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -46,20 +46,53 @@ - - - - 0 - 0 - - - - font-weight: bold; color: palette(shadow); margin-bottom: 4px; - - - online now: - - + + + + + + 0 + 0 + + + + font-weight: bold; color: palette(shadow); margin-bottom: 4px; + + + online now: + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + Qt::NoFocus + + + + + + + :/images/close.svg:/images/close.svg + + + true + + + + @@ -67,7 +100,7 @@ - margin-top: 12px; + margin-top: 12px; font-family: Helvetica, Arial, sans-serif; Qt::ScrollBarAlwaysOff @@ -81,7 +114,7 @@ 0 0 358 - 448 + 452 @@ -121,12 +154,21 @@ 0 + + border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px; + + + QFrame::NoFrame + Qt::ScrollBarAlwaysOff QAbstractScrollArea::AdjustToContents + + true + @@ -135,6 +177,25 @@ messagePlainTextEdit messagesScrollArea - - + + + + + + closeButton + clicked() + ChatWindow + accept() + + + 342 + 42 + + + 550 + 42 + + + + diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9ec37acd80..ccb6d3970e 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -155,27 +155,27 @@ Attribute::Attribute(const QString& name) : Attribute::~Attribute() { } -void Attribute::read(MetavoxelData& data, MetavoxelStreamState& state) { +void Attribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) { data.createRoot(state.attribute)->read(state); } -void Attribute::write(const MetavoxelNode& root, MetavoxelStreamState& state) { +void Attribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) { root.write(state); } -void Attribute::readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { +void Attribute::readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { data.createRoot(state.attribute)->readDelta(reference, state); } -void Attribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { +void Attribute::writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { root.writeDelta(reference, state); } -void Attribute::readSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { +void Attribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { data.getRoot(state.attribute)->readSubdivision(state); } -void Attribute::writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { +void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { root.writeSubdivision(state); } @@ -327,7 +327,7 @@ SpannerSetAttribute::SpannerSetAttribute(const QString& name, const QMetaObject* SharedObjectSetAttribute(name, metaObject) { } -void SpannerSetAttribute::read(MetavoxelData& data, MetavoxelStreamState& state) { +void SpannerSetAttribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) { forever { SharedObjectPointer object; state.stream >> object; @@ -338,13 +338,14 @@ void SpannerSetAttribute::read(MetavoxelData& data, MetavoxelStreamState& state) } } -void SpannerSetAttribute::write(const MetavoxelNode& root, MetavoxelStreamState& state) { +void SpannerSetAttribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) { Spanner::incrementVisit(); root.writeSpanners(state); state.stream << SharedObjectPointer(); } -void SpannerSetAttribute::readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { +void SpannerSetAttribute::readMetavoxelDelta(MetavoxelData& data, + const MetavoxelNode& reference, MetavoxelStreamState& state) { forever { SharedObjectPointer object; state.stream >> object; @@ -355,13 +356,14 @@ void SpannerSetAttribute::readDelta(MetavoxelData& data, const MetavoxelNode& re } } -void SpannerSetAttribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { +void SpannerSetAttribute::writeMetavoxelDelta(const MetavoxelNode& root, + const MetavoxelNode& reference, MetavoxelStreamState& state) { Spanner::incrementVisit(); root.writeSpannerDelta(reference, state); state.stream << SharedObjectPointer(); } -void SpannerSetAttribute::readSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { +void SpannerSetAttribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { forever { SharedObjectPointer object; state.stream >> object; @@ -372,7 +374,7 @@ void SpannerSetAttribute::readSubdivision(MetavoxelData& data, MetavoxelStreamSt } } -void SpannerSetAttribute::writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { +void SpannerSetAttribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { Spanner::incrementVisit(); root.writeSpannerSubdivision(state); state.stream << SharedObjectPointer(); diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 3405f90315..b6a415bef7 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -177,14 +177,14 @@ public: virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); } virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); } - virtual void read(MetavoxelData& data, MetavoxelStreamState& state); - virtual void write(const MetavoxelNode& root, MetavoxelStreamState& state); + virtual void readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state); - virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void readSubdivision(MetavoxelData& data, MetavoxelStreamState& state); - virtual void writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); + virtual void readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); virtual bool equal(void* first, void* second) const = 0; @@ -265,7 +265,7 @@ template inline bool SimpleInlineAttribute::merge(vo /// Provides appropriate averaging for RGBA values. class QRgbAttribute : public InlineAttribute { Q_OBJECT - Q_PROPERTY(int defaultValue MEMBER _defaultValue) + Q_PROPERTY(uint defaultValue MEMBER _defaultValue) public: @@ -355,14 +355,14 @@ public: Q_INVOKABLE SpannerSetAttribute(const QString& name = QString(), const QMetaObject* metaObject = &SharedObject::staticMetaObject); - virtual void read(MetavoxelData& data, MetavoxelStreamState& state); - virtual void write(const MetavoxelNode& root, MetavoxelStreamState& state); + virtual void readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state); - virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state); + virtual void writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state); - virtual void readSubdivision(MetavoxelData& data, MetavoxelStreamState& state); - virtual void writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); + virtual void readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state); + virtual void writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); }; #endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 0af44eaae6..f80ef3aa28 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -23,6 +23,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(bool) REGISTER_SIMPLE_TYPE_STREAMER(int) +REGISTER_SIMPLE_TYPE_STREAMER(uint) REGISTER_SIMPLE_TYPE_STREAMER(float) REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) REGISTER_SIMPLE_TYPE_STREAMER(QColor) @@ -238,6 +239,17 @@ Bitstream& Bitstream::operator>>(int& value) { return *this; } +Bitstream& Bitstream::operator<<(uint value) { + return write(&value, 32); +} + +Bitstream& Bitstream::operator>>(uint& value) { + quint32 sizedValue; + read(&sizedValue, 32); + value = sizedValue; + return *this; +} + Bitstream& Bitstream::operator<<(float value) { return write(&value, 32); } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 297d265d97..ca26509d60 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -254,6 +254,9 @@ public: Bitstream& operator<<(int value); Bitstream& operator>>(int& value); + Bitstream& operator<<(uint value); + Bitstream& operator>>(uint& value); + Bitstream& operator<<(float value); Bitstream& operator>>(float& value); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 8a6b0bc16a..08cd62f30d 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -228,7 +228,7 @@ public: Spanner* getSpanner() const { return _spanner; } float getDistance() const { return _distance; } - virtual bool visit(Spanner* spanner, float distance); + virtual bool visitSpanner(Spanner* spanner, float distance); private: @@ -243,7 +243,7 @@ FirstRaySpannerIntersectionVisitor::FirstRaySpannerIntersectionVisitor( _spanner(NULL) { } -bool FirstRaySpannerIntersectionVisitor::visit(Spanner* spanner, float distance) { +bool FirstRaySpannerIntersectionVisitor::visitSpanner(Spanner* spanner, float distance) { _spanner = spanner; _distance = distance; return false; @@ -312,7 +312,7 @@ void MetavoxelData::read(Bitstream& in, const MetavoxelLOD& lod) { break; } MetavoxelStreamState state = { getMinimum(), _size, attribute, in, lod, lod }; - attribute->read(*this, state); + attribute->readMetavoxelRoot(*this, state); } } @@ -321,7 +321,7 @@ void MetavoxelData::write(Bitstream& out, const MetavoxelLOD& lod) const { for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { out << it.key(); MetavoxelStreamState state = { getMinimum(), _size, it.key(), out, lod, lod }; - it.key()->write(*it.value(), state); + it.key()->writeMetavoxelRoot(*it.value(), state); } out << AttributePointer(); } @@ -360,13 +360,13 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD in >> changed; if (changed) { oldRoot->incrementReferenceCount(); - attribute->readDelta(*this, *oldRoot, state); + attribute->readMetavoxelDelta(*this, *oldRoot, state); oldRoot->decrementReferenceCount(attribute); } else { - attribute->readSubdivision(*this, state); + attribute->readMetavoxelSubdivision(*this, state); } } else { - attribute->read(*this, state); + attribute->readMetavoxelRoot(*this, state); } } @@ -415,13 +415,13 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO if (referenceRoot) { if (it.value() == referenceRoot) { out << false; - it.key()->writeSubdivision(*it.value(), state); + it.key()->writeMetavoxelSubdivision(*it.value(), state); } else { out << true; - it.key()->writeDelta(*it.value(), *referenceRoot, state); + it.key()->writeMetavoxelDelta(*it.value(), *referenceRoot, state); } } else { - it.key()->write(*it.value(), state); + it.key()->writeMetavoxelRoot(*it.value(), state); } } } @@ -938,7 +938,7 @@ int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { } qStableSort(spannerDistances); foreach (const SpannerDistance& spannerDistance, spannerDistances) { - if (!visit(spannerDistance.spanner, spannerDistance.distance)) { + if (!visitSpanner(spannerDistance.spanner, spannerDistance.distance)) { return SHORT_CIRCUIT; } } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index ab50bacff9..9d642f8f34 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -302,7 +302,7 @@ public: /// Visits a spanner that the ray intersects. /// \return true to continue, false to short-circuit the tour - virtual bool visit(Spanner* spanner, float distance) = 0; + virtual bool visitSpanner(Spanner* spanner, float distance) = 0; virtual void prepare(); virtual int visit(MetavoxelInfo& info, float distance); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index f85ed7f487..4c8ed8c9f6 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -15,8 +15,6 @@ #include #include // to load voxels from file -#include - #include #include "CoverageMap.h" diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 794884334f..e42fe17df0 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -18,20 +18,32 @@ #include "OctreeSceneStats.h" +const uint16_t MAX_MISSING_SEQUENCE = 100; /// how many items in our _missingSequenceNumbers before we start to prune them +const uint16_t MAX_MISSING_SEQUENCE_OLD_AGE = 1000; /// age we allow items in _missingSequenceNumbers to be before pruning + + const int samples = 100; OctreeSceneStats::OctreeSceneStats() : _isReadyToSend(false), _isStarted(false), _lastFullElapsed(0), + _lastFullTotalEncodeTime(0), + _lastFullTotalPackets(0), + _lastFullTotalBytes(0), + _elapsedAverage(samples), _bitsPerOctreeAverage(samples), - _lastFullTotalEncodeTime(0), _incomingPacket(0), _incomingBytes(0), _incomingWastedBytes(0), _incomingLastSequence(0), - _incomingOutOfOrder(0), _incomingLikelyLost(0), + _incomingRecovered(0), + _incomingEarly(0), + _incomingLate(0), + _incomingReallyLate(0), + _incomingPossibleDuplicate(0), + _missingSequenceNumbers(), _incomingFlightTimeAverage(samples), _jurisdictionRoot(NULL) { @@ -53,8 +65,10 @@ OctreeSceneStats& OctreeSceneStats::operator=(const OctreeSceneStats& other) { void OctreeSceneStats::copyFromOther(const OctreeSceneStats& other) { _totalEncodeTime = other._totalEncodeTime; _elapsed = other._elapsed; - _lastFullTotalEncodeTime = other._lastFullTotalEncodeTime; _lastFullElapsed = other._lastFullElapsed; + _lastFullTotalEncodeTime = other._lastFullTotalEncodeTime; + _lastFullTotalPackets = other._lastFullTotalPackets; + _lastFullTotalBytes = other._lastFullTotalBytes; _encodeStart = other._encodeStart; _packets = other._packets; @@ -134,8 +148,14 @@ void OctreeSceneStats::copyFromOther(const OctreeSceneStats& other) { _incomingBytes = other._incomingBytes; _incomingWastedBytes = other._incomingWastedBytes; _incomingLastSequence = other._incomingLastSequence; - _incomingOutOfOrder = other._incomingOutOfOrder; _incomingLikelyLost = other._incomingLikelyLost; + _incomingRecovered = other._incomingRecovered; + _incomingEarly = other._incomingEarly; + _incomingLate = other._incomingLate; + _incomingReallyLate = other._incomingReallyLate; + _incomingPossibleDuplicate = other._incomingPossibleDuplicate; + + _missingSequenceNumbers = other._missingSequenceNumbers; } @@ -493,12 +513,6 @@ int OctreeSceneStats::unpackFromMessage(const unsigned char* sourceBuffer, int a memcpy(&_isFullScene, sourceBuffer, sizeof(_isFullScene)); sourceBuffer += sizeof(_isFullScene); - - if (_isFullScene) { - _lastFullElapsed = _elapsed; - _lastFullTotalEncodeTime = _totalEncodeTime; - } - memcpy(&_isMoving, sourceBuffer, sizeof(_isMoving)); sourceBuffer += sizeof(_isMoving); memcpy(&_packets, sourceBuffer, sizeof(_packets)); @@ -506,6 +520,13 @@ int OctreeSceneStats::unpackFromMessage(const unsigned char* sourceBuffer, int a memcpy(&_bytes, sourceBuffer, sizeof(_bytes)); sourceBuffer += sizeof(_bytes); + if (_isFullScene) { + _lastFullElapsed = _elapsed; + _lastFullTotalEncodeTime = _totalEncodeTime; + _lastFullTotalPackets = _packets; + _lastFullTotalBytes = _bytes; + } + memcpy(&_totalInternal, sourceBuffer, sizeof(_totalInternal)); sourceBuffer += sizeof(_totalInternal); memcpy(&_totalLeaves, sourceBuffer, sizeof(_totalLeaves)); @@ -823,18 +844,95 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet, float flightTimeMsecs = flightTime / USECS_PER_MSEC; _incomingFlightTimeAverage.updateAverage(flightTimeMsecs); + // track out of order and possibly lost packets... + const bool wantExtraDebugging = false; + if (sequence == _incomingLastSequence) { + if (wantExtraDebugging) { + qDebug() << "last packet duplicate got:" << sequence << "_incomingLastSequence:" << _incomingLastSequence; + } + } else { + OCTREE_PACKET_SEQUENCE expected = _incomingLastSequence+1; + if (sequence != expected) { + if (wantExtraDebugging) { + qDebug() << "out of order... got:" << sequence << "expected:" << expected; + } + + // if the sequence is less than our expected, then this might be a packet + // that was delayed and so we should find it in our lostSequence list + if (sequence < expected) { + if (wantExtraDebugging) { + qDebug() << "this packet is later than expected..."; + } + if (sequence < std::max(0, (expected - MAX_MISSING_SEQUENCE_OLD_AGE))) { + _incomingReallyLate++; + } else { + _incomingLate++; + } + + if (_missingSequenceNumbers.contains(sequence)) { + if (wantExtraDebugging) { + qDebug() << "found it in _missingSequenceNumbers"; + } + _missingSequenceNumbers.remove(sequence); + _incomingLikelyLost--; + _incomingRecovered++; + } else { + // if we're still in our pruning window, and we didn't find it in our missing list, + // than this is really unexpected and can probably only happen if the packet was a + // duplicate + if (sequence >= std::max(0, (expected - MAX_MISSING_SEQUENCE_OLD_AGE))) { + if (wantExtraDebugging) { + qDebug() << "sequence:" << sequence << "WAS NOT found in _missingSequenceNumbers, and not that old... (expected - MAX_MISSING_SEQUENCE_OLD_AGE):" << (expected - MAX_MISSING_SEQUENCE_OLD_AGE); + } + _incomingPossibleDuplicate++; + } + } + } + + if (sequence > expected) { + if (wantExtraDebugging) { + qDebug() << "this packet is earlier than expected..."; + } + _incomingEarly++; + + // hmm... so, we either didn't get some packets, or this guy came early... + unsigned int missing = sequence - expected; + if (wantExtraDebugging) { + qDebug() << ">>>>>>>> missing gap=" << missing; + } + _incomingLikelyLost += missing; + for(unsigned int missingSequence = expected; missingSequence < sequence; missingSequence++) { + _missingSequenceNumbers << missingSequence; + } + } + } + } + + // only bump the last sequence if it was greater than our previous last sequence, this will keep us from + // accidentally going backwards when an out of order (recovered) packet comes in + if (sequence > _incomingLastSequence) { + _incomingLastSequence = sequence; + } - // detect out of order packets - if (sequence < _incomingLastSequence) { - _incomingOutOfOrder++; + // do some garbage collecting on our _missingSequenceNumbers + if (_missingSequenceNumbers.size() > MAX_MISSING_SEQUENCE) { + if (wantExtraDebugging) { + qDebug() << "too many _missingSequenceNumbers:" << _missingSequenceNumbers.size(); + } + foreach(unsigned int missingItem, _missingSequenceNumbers) { + if (wantExtraDebugging) { + qDebug() << "checking item:" << missingItem << "is it in need of pruning?"; + qDebug() << "(_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE):" + << (_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE); + } + if (missingItem <= std::max(0, (_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE))) { + if (wantExtraDebugging) { + qDebug() << "pruning really old missing sequence:" << missingItem; + } + _missingSequenceNumbers.remove(missingItem); + } + } } - - // detect likely lost packets - OCTREE_PACKET_SEQUENCE expected = _incomingLastSequence+1; - if (sequence > expected) { - _incomingLikelyLost++; - } - - _incomingLastSequence = sequence; + } diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h index e106f53589..2b39b3434c 100644 --- a/libraries/octree/src/OctreeSceneStats.h +++ b/libraries/octree/src/OctreeSceneStats.h @@ -150,8 +150,10 @@ public: unsigned long getTotalEncodeTime() const { return _totalEncodeTime; } unsigned long getElapsedTime() const { return _elapsed; } - unsigned long getLastFullTotalEncodeTime() const { return _lastFullTotalEncodeTime; } unsigned long getLastFullElapsedTime() const { return _lastFullElapsed; } + unsigned long getLastFullTotalEncodeTime() const { return _lastFullTotalEncodeTime; } + unsigned int getLastFullTotalPackets() const { return _lastFullTotalPackets; } + unsigned long getLastFullTotalBytes() const { return _lastFullTotalBytes; } // Used in client implementations to track individual octree packets void trackIncomingOctreePacket(const QByteArray& packet, bool wasStatsPacket, int nodeClockSkewUsec); @@ -159,8 +161,13 @@ public: unsigned int getIncomingPackets() const { return _incomingPacket; } unsigned long getIncomingBytes() const { return _incomingBytes; } unsigned long getIncomingWastedBytes() const { return _incomingWastedBytes; } - unsigned int getIncomingOutOfOrder() const { return _incomingOutOfOrder; } + unsigned int getIncomingOutOfOrder() const { return _incomingLate + _incomingEarly; } unsigned int getIncomingLikelyLost() const { return _incomingLikelyLost; } + unsigned int getIncomingRecovered() const { return _incomingRecovered; } + unsigned int getIncomingEarly() const { return _incomingEarly; } + unsigned int getIncomingLate() const { return _incomingLate; } + unsigned int getIncomingReallyLate() const { return _incomingReallyLate; } + unsigned int getIncomingPossibleDuplicate() const { return _incomingPossibleDuplicate; } float getIncomingFlightTimeAverage() { return _incomingFlightTimeAverage.getAverage(); } private: @@ -176,13 +183,16 @@ private: quint64 _start; quint64 _end; quint64 _elapsed; + quint64 _lastFullElapsed; + quint64 _lastFullTotalEncodeTime; + unsigned int _lastFullTotalPackets; + unsigned long _lastFullTotalBytes; SimpleMovingAverage _elapsedAverage; SimpleMovingAverage _bitsPerOctreeAverage; quint64 _totalEncodeTime; - quint64 _lastFullTotalEncodeTime; quint64 _encodeStart; // scene octree related data @@ -251,9 +261,15 @@ private: unsigned int _incomingPacket; unsigned long _incomingBytes; unsigned long _incomingWastedBytes; - unsigned int _incomingLastSequence; - unsigned int _incomingOutOfOrder; - unsigned int _incomingLikelyLost; + + uint16_t _incomingLastSequence; /// last incoming sequence number + unsigned int _incomingLikelyLost; /// count of packets likely lost, may be off by _incomingReallyLate count + unsigned int _incomingRecovered; /// packets that were late, and we had in our missing list, we consider recovered + unsigned int _incomingEarly; /// out of order earlier than expected + unsigned int _incomingLate; /// out of order later than expected + unsigned int _incomingReallyLate; /// out of order and later than MAX_MISSING_SEQUENCE_OLD_AGE late + unsigned int _incomingPossibleDuplicate; /// out of order possibly a duplicate + QSet _missingSequenceNumbers; SimpleMovingAverage _incomingFlightTimeAverage; // features related items diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 04916687fb..9a672317ae 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -28,8 +28,6 @@ #include "LocalVoxels.h" #include "ScriptEngine.h" -const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; - int ScriptEngine::_scriptNumber = 1; VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; @@ -54,6 +52,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co _avatarIdentityTimer(NULL), _avatarBillboardTimer(NULL), _timerFunctionMap(), + _avatarAudioBuffer(NULL), _controllerScriptingInterface(controllerScriptingInterface), _avatarData(NULL), _wantMenuItems(wantMenuItems), @@ -77,9 +76,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co _scriptNumber++; } -ScriptEngine::~ScriptEngine() { -} - void ScriptEngine::setIsAvatar(bool isAvatar) { _isAvatar = isAvatar; @@ -169,8 +165,8 @@ void ScriptEngine::init() { _engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); // let the VoxelPacketSender know how frequently we plan to call it - _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); - _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); + _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); + _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); } @@ -228,7 +224,7 @@ void ScriptEngine::run() { qint64 lastUpdate = usecTimestampNow(); while (!_isFinished) { - int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow(); + int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); } @@ -268,6 +264,21 @@ void ScriptEngine::run() { avatarPacket.append(_avatarData->toByteArray()); nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer); + + if (_avatarAudioBuffer && _numAvatarAudioBufferSamples > 0) { + // if have an avatar audio stream then send it out to our audio-mixer + QByteArray audioPacket = byteArrayWithPopulatedHeader(PacketTypeMicrophoneAudioNoEcho); + QDataStream packetStream(&audioPacket, QIODevice::Append); + + // use the orientation and position of this avatar for the source of this audio + packetStream.writeRawData(reinterpret_cast(&_avatarData->getPosition()), sizeof(glm::vec3)); + glm::quat headOrientation = _avatarData->getHeadOrientation(); + packetStream.writeRawData(reinterpret_cast(&headOrientation), sizeof(glm::quat)); + packetStream.writeRawData(reinterpret_cast(_avatarAudioBuffer), + _numAvatarAudioBufferSamples * sizeof(int16_t)); + + nodeList->broadcastToNodes(audioPacket, NodeSet() << NodeType::AudioMixer); + } } qint64 now = usecTimestampNow(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 186524eb7f..606d0aabf4 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -20,14 +20,16 @@ #include -class ParticlesScriptingInterface; - #include "AbstractControllerScriptingInterface.h" #include "Quat.h" #include "Vec3.h" +class ParticlesScriptingInterface; + const QString NO_SCRIPT(""); +const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 1000) + 0.5); + class ScriptEngine : public QObject { Q_OBJECT public: @@ -35,8 +37,6 @@ public: const QString& scriptMenuName = QString(""), AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); - ~ScriptEngine(); - /// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; } @@ -56,6 +56,11 @@ public: void setAvatarData(AvatarData* avatarData, const QString& objectName); + void setAvatarAudioBuffer(int16_t* avatarAudioBuffer) { _avatarAudioBuffer = avatarAudioBuffer; } + bool sendsAvatarAudioStream() const { return (bool) _avatarAudioBuffer; } + void setNumAvatarAudioBufferSamples(int numAvatarAudioBufferSamples) + { _numAvatarAudioBufferSamples = numAvatarAudioBufferSamples; } + void init(); void run(); /// runs continuously until Agent.stop() is called void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller @@ -86,6 +91,8 @@ protected: QTimer* _avatarIdentityTimer; QTimer* _avatarBillboardTimer; QHash _timerFunctionMap; + int16_t* _avatarAudioBuffer; + int _numAvatarAudioBufferSamples; private: void sendAvatarIdentityPacket(); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 4c756c2257..5c48244a39 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -8,8 +8,6 @@ #include -#include - #include #include #include