From 874b542c0cd5797301a6b6668b35696f534fb662 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 9 Jul 2014 04:13:53 -0700 Subject: [PATCH 01/45] Add import/export/paste model functionality to editModels.js --- examples/editModels.js | 427 +++++++++++++++++- interface/src/Application.cpp | 38 ++ interface/src/Application.h | 5 + .../scripting/ClipboardScriptingInterface.cpp | 13 + .../scripting/ClipboardScriptingInterface.h | 4 + .../scripting/WindowScriptingInterface.cpp | 24 +- .../src/scripting/WindowScriptingInterface.h | 4 +- 7 files changed, 510 insertions(+), 5 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 64c203534c..6cde543e55 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -61,6 +61,398 @@ var jointList = MyAvatar.getJointNames(); var mode = 0; +var exportMenu = null; + +var ExportMenu = function(opts) { + var self = this; + + var windowDimensions = Controller.getViewportDimensions(); + var pos = { x: windowDimensions.x / 2, y: windowDimensions.y - 100 }; + + this._onClose = opts.onClose || function() {}; + this._position = { x: 0.0, y: 0.0, z: 0.0 }; + this._scale = 1.0; + + var minScale = 1; + var maxScale = 32768; + var titleWidth = 120; + var locationWidth = 100; + var scaleWidth = 144; + var exportWidth = 100; + var cancelWidth = 100; + var margin = 4; + var height = 30; + var outerHeight = height + (2 * margin); + var buttonColor = { red: 128, green: 128, blue: 128}; + + var SCALE_MINUS = scaleWidth * 40.0 / 100.0; + var SCALE_PLUS = scaleWidth * 63.0 / 100.0; + + var fullWidth = locationWidth + scaleWidth + exportWidth + cancelWidth + (2 * margin); + var offset = fullWidth / 2; + pos.x -= offset; + + var background= Overlays.addOverlay("text", { + x: pos.x, + y: pos.y, + opacity: 1, + width: fullWidth, + height: outerHeight, + backgroundColor: { red: 200, green: 200, blue: 200 }, + text: "", + }); + + var titleText = Overlays.addOverlay("text", { + x: pos.x, + y: pos.y - height, + font: { size: 14 }, + width: titleWidth, + height: height, + backgroundColor: { red: 255, green: 255, blue: 255 }, + color: { red: 255, green: 255, blue: 255 }, + text: "Export Models" + }); + + var locationButton = Overlays.addOverlay("text", { + x: pos.x + margin, + y: pos.y + margin, + width: locationWidth, + height: height, + color: { red: 255, green: 255, blue: 255 }, + text: "0, 0, 0", + }); + var scaleOverlay = Overlays.addOverlay("image", { + x: pos.x + margin + locationWidth, + y: pos.y + margin, + width: scaleWidth, + height: height, + subImage: { x: 0, y: 3, width: 144, height: height}, + imageURL: toolIconUrl + "voxel-size-selector.svg", + alpha: 0.9, + }); + var scaleViewWidth = 40; + var scaleView = Overlays.addOverlay("text", { + x: pos.x + margin + locationWidth + SCALE_MINUS, + y: pos.y + margin, + width: scaleViewWidth, + height: height, + alpha: 0.0, + color: { red: 255, green: 255, blue: 255 }, + text: "1" + }); + var exportButton = Overlays.addOverlay("text", { + x: pos.x + margin + locationWidth + scaleWidth, + y: pos.y + margin, + width: exportWidth, + height: height, + color: { red: 0, green: 255, blue: 255 }, + text: "Export" + }); + var cancelButton = Overlays.addOverlay("text", { + x: pos.x + margin + locationWidth + scaleWidth + exportWidth, + y: pos.y + margin, + width: cancelWidth, + height: height, + color: { red: 255, green: 255, blue: 255 }, + text: "Cancel" + }); + + var voxelPreview = Overlays.addOverlay("cube", { + position: { x: 0, y: 0, z: 0}, + size: this._scale, + color: { red: 255, green: 255, blue: 0}, + alpha: 1, + solid: false, + visible: true, + lineWidth: 4 + }); + + this.parsePosition = function(str) { + var parts = str.split(','); + if (parts.length == 3) { + var x = parseFloat(parts[0]); + var y = parseFloat(parts[1]); + var z = parseFloat(parts[2]); + if (isFinite(x) && isFinite(y) && isFinite(z)) { + return { x: x, y: y, z: z }; + } + } + return null; + }; + + this.showPositionPrompt = function() { + var positionStr = self._position.x + ", " + self._position.y + ", " + self._position.z; + while (1) { + positionStr = Window.prompt("Position to export form:", positionStr); + if (positionStr == null) { + break; + } + print("position: " + position); + var position = self.parsePosition(positionStr); + if (position != null) { + self.setPosition(position.x, position.y, position.z); + break; + } + Window.alert("The position you entered was invalid."); + } + }; + + this.setScale = function(scale) { + self._scale = Math.min(maxScale, Math.max(minScale, scale)); + Overlays.editOverlay(scaleView, { text: self._scale }); + Overlays.editOverlay(voxelPreview, { size: self._scale }); + } + + this.decreaseScale = function() { + self.setScale(self._scale /= 2); + } + + this.increaseScale = function() { + self.setScale(self._scale *= 2); + } + + this.exportModels = function() { + var x = self._position.x; + var y = self._position.y; + var z = self._position.z; + var s = self._scale; + var filename = "models__" + Window.location.hostname + "__" + x + "_" + y + "_" + z + "_" + s + "__.svo"; + filename = Window.save("Select where to save", filename, "*.svo") + Clipboard.exportModels(filename, x, y, z, s); + self.close(); + }; + + this.getPosition = function() { + return self._position; + }; + + this.setPosition = function(x, y, z) { + self._position = { x: x, y: y, z: z }; + var positionStr = x + ", " + y + ", " + z; + Overlays.editOverlay(locationButton, { text: positionStr }); + Overlays.editOverlay(voxelPreview, { position: self._position }); + + }; + + this.mousePressEvent = function(event) { + print("Mouse press"); + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + }; + + this.mouseReleaseEvent = function(event) { + print("Mouse release"); + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + + if (clickedOverlay == locationButton) { + self.showPositionPrompt(); + } else if (clickedOverlay == exportButton) { + self.exportModels(); + } else if (clickedOverlay == cancelButton) { + self.close(); + } else if (clickedOverlay == scaleOverlay) { + var x = event.x - pos.x - margin - locationWidth; + print(x); + if (x < SCALE_MINUS) { + self.decreaseScale(); + } else if (x > SCALE_PLUS) { + self.increaseScale(); + } + } + }; + + this.close = function() { + this.cleanup(); + this._onClose(); + }; + + this.cleanup = function() { + Overlays.deleteOverlay(background); + Overlays.deleteOverlay(titleText); + Overlays.deleteOverlay(locationButton); + Overlays.deleteOverlay(exportButton); + Overlays.deleteOverlay(cancelButton); + Overlays.deleteOverlay(voxelPreview); + Overlays.deleteOverlay(scaleOverlay); + Overlays.deleteOverlay(scaleView); + }; + + print("CONNECTING!"); + Controller.mousePressEvent.connect(this.mousePressEvent); + Controller.mouseReleaseEvent.connect(this.mouseReleaseEvent); +}; + +var ModelImporter = function(opts) { + var self = this; + + var height = 30; + var margin = 4; + var outerHeight = height + (2 * margin); + var titleWidth = 120; + var cancelWidth = 100; + var fullWidth = titleWidth + cancelWidth + (2 * margin); + + // TODO: Show import preview + var importBoundaries = Overlays.addOverlay("cube", { + position: { x: 0, y: 0, z: 0 }, + size: 1, + color: { red: 128, blue: 128, green: 128 }, + lineWidth: 4, + solid: false, + visible: false + }); + + var pos = { x: windowDimensions.x / 2 - (fullWidth / 2), y: windowDimensions.y - 100 }; + + var background= Overlays.addOverlay("text", { + x: pos.x, + y: pos.y, + opacity: 1, + width: fullWidth, + height: outerHeight, + backgroundColor: { red: 200, green: 200, blue: 200 }, + visible: false, + text: "", + }); + + var titleText = Overlays.addOverlay("text", { + x: pos.x + margin, + y: pos.y + margin, + font: { size: 14 }, + width: titleWidth, + height: height, + backgroundColor: { red: 255, green: 255, blue: 255 }, + color: { red: 255, green: 255, blue: 255 }, + visible: false, + text: "Import Models" + }); + var cancelButton = Overlays.addOverlay("text", { + x: pos.x + margin + titleWidth, + y: pos.y + margin, + width: cancelWidth, + height: height, + color: { red: 255, green: 255, blue: 255 }, + visible: false, + text: "Cancel" + }); + this._importing = false; + + this.setImportVisible = function(visible) { + Overlays.editOverlay(importBoundaries, { visible: visible }); + Overlays.editOverlay(cancelButton, { visible: visible }); + Overlays.editOverlay(titleText, { visible: visible }); + Overlays.editOverlay(background, { visible: visible }); + }; + + var importPosition = { x: 0, y: 0, z: 0 }; + this.moveImport = function(position) { + importPosition = position; + // TODO: Show import preview + Overlays.editOverlay(importBoundaries, { + position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } + }); + } + + this.mouseMoveEvent = function(event) { + if (self._importing) { + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = Voxels.findRayIntersection(pickRay); + + var distance = 2;// * self._scale; + + if (false) {//intersection.intersects) { + var intersectionDistance = Vec3.length(Vec3.subtract(pickRay.origin, intersection.intersection)); + if (intersectionDistance < distance) { + distance = intersectionDistance * 0.99; + } + + } + + var targetPosition = { + x: pickRay.origin.x + (pickRay.direction.x * distance), + y: pickRay.origin.y + (pickRay.direction.y * distance), + z: pickRay.origin.z + (pickRay.direction.z * distance) + }; + + if (targetPosition.x < 0) targetPosition.x = 0; + if (targetPosition.y < 0) targetPosition.y = 0; + if (targetPosition.z < 0) targetPosition.z = 0; + + var nudgeFactor = 1; + var newPosition = { + x: Math.floor(targetPosition.x / nudgeFactor) * nudgeFactor, + y: Math.floor(targetPosition.y / nudgeFactor) * nudgeFactor, + z: Math.floor(targetPosition.z / nudgeFactor) * nudgeFactor + } + + self.moveImport(newPosition); + } + } + + this.mouseReleaseEvent = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + + if (clickedOverlay == cancelButton) { + self._importing = false; + self.setImportVisible(false); + } + }; + + // Would prefer to use {4} for the coords, but it would only capture the last digit. + var fileRegex = /__(.+)__(\d+(?:\.\d+)?)_(\d+(?:\.\d+)?)_(\d+(?:\.\d+)?)_(\d+(?:\.\d+)?)__/; + this.doImport = function() { + print("IMPORTING "); + if (!self._importing) { + var filename = Window.browse("Select models to import", "", "*.svo") + if (filename) { + parts = fileRegex.exec(filename); + if (parts == null) { + Window.alert("The file you selected does not contain source domain or location information"); + } else { + var hostname = parts[1]; + var x = parts[2]; + var y = parts[3]; + var z = parts[4]; + var s = parts[5]; + if (hostname != location.hostname) { + if (!Window.confirm(("These models were not originally exported from this domain. Continue?"))) { + return; + } + } else { + if (Window.confirm(("Would you like to import back to the source location?"))) { + Window.alert("(TODO) Importing backing to source location"); + Clipboard.importModels(filename); + return; + } + } + } + Clipboard.importModels(filename); + self._importing = true; + self.setImportVisible(true); + } + } + } + + this.paste = function() { + if (self._importing) { + self._importing = false; + self.setImportVisible(false); + Clipboard.pasteModels(); + } + } + + this.cleanup = function() { + Overlays.deleteOverlay(importBoundaries); + Overlays.deleteOverlay(cancelButton); + Overlays.deleteOverlay(titleText); + Overlays.deleteOverlay(background); + } + + Controller.mouseReleaseEvent.connect(this.mouseReleaseEvent); + Controller.mouseMoveEvent.connect(this.mouseMoveEvent); +}; + +var modelImporter = new ModelImporter(); + function isLocked(properties) { // special case to lock the ground plane model in hq. if (location.hostname == "hq.highfidelity.io" && @@ -1045,7 +1437,7 @@ function mouseReleaseEvent(event) { var modelMenuAddedDelete = false; function setupModelMenus() { print("setupModelMenus()"); - // add our menuitems + // adj our menuitems Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Edit Properties...", shortcutKeyEvent: { text: "`" }, afterItem: "Models" }); @@ -1057,6 +1449,12 @@ function setupModelMenus() { } else { print("delete exists... don't add ours"); } + + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." }); + + Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); + Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" }); + Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" }); } function cleanupModelMenus() { @@ -1066,6 +1464,12 @@ function cleanupModelMenus() { // delete our menuitems Menu.removeMenuItem("Edit", "Delete"); } + + Menu.removeMenuItem("Edit", "Paste Models"); + + Menu.removeSeparator("File", "Models"); + Menu.removeMenuItem("File", "Export Models"); + Menu.removeMenuItem("File", "Import Models"); } function scriptEnding() { @@ -1074,6 +1478,10 @@ function scriptEnding() { toolBar.cleanup(); cleanupModelMenus(); tooltip.cleanup(); + modelImporter.cleanup(); + if (exportMenu) { + exportMenu.close(); + } } Script.scriptEnding.connect(scriptEnding); @@ -1128,6 +1536,21 @@ function handeMenuEvent(menuItem){ Models.editModel(editModelID, properties); } } + } else if (menuItem == "Paste Models") { + modelImporter.paste(); + } else if (menuItem == "Export Models") { + if (!exportMenu) { + // if (modelImporter) { + // modelImporter.close(); + // } + exportMenu = new ExportMenu({ + onClose: function() { + exportMenu = null; + } + }); + } + } else if (menuItem == "Import Models") { + modelImporter.doImport(); } tooltip.show(false); } @@ -1173,4 +1596,4 @@ Controller.keyReleaseEvent.connect(function(event) { if (event.text == "BACKSPACE") { handeMenuEvent("Delete"); } -}); \ No newline at end of file +}); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3e121bc2ee..2c097318e4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -141,6 +141,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _voxelImporter(NULL), _importSucceded(false), _sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard), + _modelClipboardRenderer(), + _modelClipboard(), _wantToKillLocalVoxels(false), _viewFrustum(), _lastQueriedViewFrustum(), @@ -1483,6 +1485,21 @@ struct SendVoxelsOperationArgs { const unsigned char* newBaseOctCode; }; +void Application::exportModels(const QString& filename, float x, float y, float z, float scale) { + ModelTreeElement* selectedNode = _models.getTree()->getModelAt(x, y, z, scale); + if (selectedNode) { + qDebug() << "Exporting models doing it!" << filename; + ModelTree exportTree; + _models.getTree()->copySubTreeIntoNewTree(selectedNode, &exportTree, true); + exportTree.writeToSVOFile(filename.toLocal8Bit().constData()); + } else { + qDebug() << "No models were selected"; + } + + // restore the main window's active state + _window->activateWindow(); +} + bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; SendVoxelsOperationArgs* args = (SendVoxelsOperationArgs*)extraData; @@ -1564,6 +1581,20 @@ void Application::importVoxels() { emit importDone(); } +void Application::importModels(const QString& filename) { + _importSucceded = false; + + + _models.getTree()->readFromSVOFile(filename.toLocal8Bit().constData()); + _models.getTree()->reaverageOctreeElements(); + + + // restore the main window's active state + _window->activateWindow(); + + emit importDone(); +} + void Application::cutVoxels(const VoxelDetail& sourceVoxel) { copyVoxels(sourceVoxel); deleteVoxelAt(sourceVoxel); @@ -1719,6 +1750,10 @@ void Application::init() { _models.init(); _models.setViewFrustum(getViewFrustum()); + _modelClipboardRenderer.init(); + _modelClipboardRenderer.setViewFrustum(getViewFrustum()); + _modelClipboardRenderer.setTree(&_modelClipboard); + _metavoxels.init(); _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager); @@ -2083,6 +2118,7 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("idle/update/_models"); _models.update(); // update the models... + _modelClipboardRenderer.update(); } { @@ -2566,6 +2602,7 @@ void Application::updateShadowMap() { _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE); _particles.render(OctreeRenderer::SHADOW_RENDER_MODE); _models.render(OctreeRenderer::SHADOW_RENDER_MODE); + _modelClipboardRenderer.render(OctreeRenderer::SHADOW_RENDER_MODE); glDisable(GL_POLYGON_OFFSET_FILL); @@ -2767,6 +2804,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... models..."); _models.render(); + _modelClipboardRenderer.render(); } // render the ambient occlusion effect if enabled diff --git a/interface/src/Application.h b/interface/src/Application.h index 11f406abf0..c2f21f3738 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -308,6 +308,9 @@ public slots: void nodeKilled(SharedNodePointer node); void packetSent(quint64 length); + void exportModels(const QString& filename, float x, float y, float z, float scale); + void importModels(const QString& filename); + void importVoxels(); // doesn't include source voxel because it goes to clipboard void cutVoxels(const VoxelDetail& sourceVoxel); void copyVoxels(const VoxelDetail& sourceVoxel); @@ -452,6 +455,8 @@ private: ParticleCollisionSystem _particleCollisionSystem; ModelTreeRenderer _models; + ModelTreeRenderer _modelClipboardRenderer; + ModelTree _modelClipboard; QByteArray _voxelsFilename; bool _wantToKillLocalVoxels; diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index e8fb545343..39b5db9418 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -100,3 +100,16 @@ void ClipboardScriptingInterface::nudgeVoxel(float x, float y, float z, float s, Application::getInstance()->nudgeVoxelsByVector(sourceVoxel, nudgeVecInTreeSpace); } + + +void ClipboardScriptingInterface::exportModels(const QString& filename, float x, float y, float z, float s) { + Application::getInstance()->exportModels(filename, x / TREE_SCALE, y / TREE_SCALE, z / TREE_SCALE, s / TREE_SCALE); +} + +void ClipboardScriptingInterface::importModels(const QString& filename) { + Application::getInstance()->importModels(filename); +} + +void ClipboardScriptingInterface::pasteModels() { + // TODO +} diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index f0258b0cc7..2ff287b29f 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -42,6 +42,10 @@ public slots: void nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec); void nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec); + + void importModels(const QString& filename); + void exportModels(const QString& filename, float x, float y, float z, float s); + void pasteModels(); }; #endif // hifi_ClipboardScriptingInterface_h diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index e10197d488..84ccdc9d7e 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -53,6 +53,15 @@ QScriptValue WindowScriptingInterface::browse(const QString& title, const QStrin return retVal; } +QScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) { + QScriptValue retVal; + QMetaObject::invokeMethod(this, "showBrowse", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QScriptValue, retVal), + Q_ARG(const QString&, title), Q_ARG(const QString&, directory), Q_ARG(const QString&, nameFilter), + Q_ARG(QFileDialog::AcceptMode, QFileDialog::AcceptSave)); + return retVal; +} + /// Display an alert box /// \param const QString& message message to display /// \return QScriptValue::UndefinedValue @@ -93,18 +102,29 @@ QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const /// \param const QString& directory directory to start the file browser at /// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` /// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QString& directory, const QString& nameFilter) { +QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QString& directory, const QString& nameFilter, + QFileDialog::AcceptMode acceptMode) { // On OS X `directory` does not work as expected unless a file is included in the path, so we append a bogus // filename if the directory is valid. QString path = ""; QFileInfo fileInfo = QFileInfo(directory); + qDebug() << "File: " << directory << fileInfo.isFile(); if (fileInfo.isDir()) { fileInfo.setFile(directory, "__HIFI_INVALID_FILE__"); path = fileInfo.filePath(); } QFileDialog fileDialog(Application::getInstance()->getWindow(), title, path, nameFilter); - fileDialog.setFileMode(QFileDialog::ExistingFile); + // fileDialog.setFileMode(QFileDialog::ExistingFile); + fileDialog.setAcceptMode(acceptMode); + qDebug() << "Opening!"; + QUrl fileUrl(directory); + if (acceptMode == QFileDialog::AcceptSave) { + fileDialog.setFileMode(QFileDialog::Directory); + fileDialog.selectFile(fileUrl.fileName()); + // qDebug() << "Setting filename!"; + // fileDialog.setLabelText(QFileDialog::FileName, fileUrl.fileName()); + } if (fileDialog.exec()) { return QScriptValue(fileDialog.selectedFiles().first()); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index b97113af9c..3fb0974b4f 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -31,12 +31,14 @@ public slots: QScriptValue confirm(const QString& message = ""); QScriptValue prompt(const QString& message = "", const QString& defaultText = ""); QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); + QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); private slots: QScriptValue showAlert(const QString& message); QScriptValue showConfirm(const QString& message); QScriptValue showPrompt(const QString& message, const QString& defaultText); - QScriptValue showBrowse(const QString& title, const QString& directory, const QString& nameFilter); + QScriptValue showBrowse(const QString& title, const QString& directory, const QString& nameFilter, + QFileDialog::AcceptMode acceptMode = QFileDialog::AcceptOpen); }; #endif // hifi_WindowScriptingInterface_h From ade1903595c1be26ea15d5b99a9bb475c2dfca66 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 9 Jul 2014 04:14:47 -0700 Subject: [PATCH 02/45] Add ModelTree::getModelAt --- libraries/models/src/ModelTree.cpp | 5 +++++ libraries/models/src/ModelTree.h | 1 + 2 files changed, 6 insertions(+) diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index 763f0a969e..1489f4db19 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -108,6 +108,11 @@ bool FindAndUpdateModelOperator::PostRecursion(OctreeElement* element) { return !_found; // if we haven't yet found it, keep looking } + +ModelTreeElement* ModelTree::getModelAt(float x, float y, float z, float s) const { + return static_cast(getOctreeElementAt(x, y, z, s)); +} + // TODO: improve this to not use multiple recursions void ModelTree::storeModel(const ModelItem& model, const SharedNodePointer& senderNode) { // First, look for the existing model in the tree.. diff --git a/libraries/models/src/ModelTree.h b/libraries/models/src/ModelTree.h index a2a3c9cd28..9ab081f46c 100644 --- a/libraries/models/src/ModelTree.h +++ b/libraries/models/src/ModelTree.h @@ -36,6 +36,7 @@ public: /// Type safe version of getRoot() ModelTreeElement* getRoot() { return static_cast(_rootElement); } + ModelTreeElement* getModelAt(float x, float y, float z, float s) const; // These methods will allow the OctreeServer to send your tree inbound edit packets of your // own definition. Implement these to allow your octree based server to support editing From 2fea3c58cdc99249dee7cfbd0eacbc6a9a64555e Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 17 Jul 2014 15:25:54 -0700 Subject: [PATCH 03/45] Added working rockPaperScissors.js that works for multiple ACs --- examples/rockPaperScissorsCells.js | 225 +++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 examples/rockPaperScissorsCells.js diff --git a/examples/rockPaperScissorsCells.js b/examples/rockPaperScissorsCells.js new file mode 100644 index 0000000000..9227278455 --- /dev/null +++ b/examples/rockPaperScissorsCells.js @@ -0,0 +1,225 @@ +// rockPaperScissorsCells.js +// examples +// +// Created by Ben Arnold on 7/16/14. +// Copyright 2014 High Fidelity, Inc. +// +// This sample script creates a voxel wall that simulates the Rock Paper Scissors cellular +// automata. http://www.gamedev.net/blog/844/entry-2249737-another-cellular-automaton-video/ +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var NUMBER_OF_CELLS_EACH_DIMENSION = 64; +var NUMBER_OF_CELLS_REGION_EACH_DIMESION = 16; +var REGIONS_EACH_DIMENSION = NUMBER_OF_CELLS_EACH_DIMENSION / NUMBER_OF_CELLS_REGION_EACH_DIMESION; + +var currentCells = []; +var nextCells = []; + +var cornerPosition = {x: 100, y: 0, z: 0 } +var position = {x: 0, y: 0, z: 0 }; + +var METER_LENGTH = 1; +var cellScale = (NUMBER_OF_CELLS_EACH_DIMENSION * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION; + +//Feel free to add new cell types here. It can be more than three. +var cellTypes = []; +cellTypes[0] = { r: 255, g: 0, b: 0 }; +cellTypes[1] = { r: 0, g: 255, b: 0 }; +cellTypes[2] = { r: 0, g:0, b: 255 }; +cellTypes[3] = { r: 255, g: 255, b: 255 }; +cellTypes[4] = { r: 255, g: 0, b: 255 }; +cellTypes[5] = { r: 0, g: 255, b: 255 }; + +//Check for free region for AC +var regionMarkerX = -1; +var regionMarkerY = -1; +var regionMarkerI = -1; +var regionMarkerJ = -1; + +var regionMarkerColor = {r: 255, g: 0, b: 255}; + +for (var i = 0; i < REGIONS_EACH_DIMENSION; i++) { + for (var j = 0; j < REGIONS_EACH_DIMENSION; j++) { + var x = cornerPosition.x + (j) * cellScale; + var y = cornerPosition.y + (i + NUMBER_OF_CELLS_EACH_DIMENSION) * cellScale; + var z = cornerPosition.z; + var voxel = Voxels.getVoxelAt(x, y, z, cellScale); + if (voxel.x != x || voxel.y != y || voxel.z != z || voxel.s != cellScale || + voxel.red != regionMarkerColor.r || voxel.green != regionMarkerColor.g || voxel.blue != regionMarkerColor.b) { + regionMarkerX = x; + regionMarkerY = y; + regionMarkerI = i; + regionMarkerJ = j; + i = REGIONS_EACH_DIMENSION; //force quit loop + break; + } + } +} + +if (regionMarkerX == -1) { + print("No available Cellular Automata regions found!") + Script.stop(); +} + +position.x = cornerPosition.x + regionMarkerJ * NUMBER_OF_CELLS_REGION_EACH_DIMESION * cellScale; +position.y = cornerPosition.y + regionMarkerI * NUMBER_OF_CELLS_REGION_EACH_DIMESION * cellScale; +position.z = cornerPosition.z; + +Voxels.setVoxel(regionMarkerX, regionMarkerY, position.z, cellScale, regionMarkerColor.r, regionMarkerColor.g, regionMarkerColor.b); + +// randomly populate the cell start values +for (var i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { + // create the array to hold this row + currentCells[i] = []; + + // create the array to hold this row in the nextCells array + nextCells[i] = []; + + for (var j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { + currentCells[i][j] = { changed: true, type: Math.floor(Math.random() * cellTypes.length) }; + + // put the same value in the nextCells array for first board draw + nextCells[i][j] = currentCells[i][j]; + } +} + +function updateCells() { + var i = 0; + var j = 0; + var cell; + var y = 0; + var x = 0; + + for (i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { + + cell = currentCells[i][j]; + + var r = Math.floor(Math.random() * 8); + + switch (r){ + case 0: + y = i - 1; + x = j - 1; + break; + case 1: + y = i; + x = j-1; + break; + case 2: + y = i + 1; + x = j - 1; + break; + case 3: + y = i + 1; + x = j; + break; + case 4: + y = i + 1; + x = j + 1; + break; + case 5: + y = i; + x = j + 1; + break; + case 6: + y = i - 1; + x = j + 1; + break; + case 7: + y = i - 1; + x = j; + break; + default: + continue; + + } + + //check the voxel grid instead of local array when on the edge + if (x == -1 || x == NUMBER_OF_CELLS_REGION_EACH_DIMESION || + y == -1 || y == NUMBER_OF_CELLS_REGION_EACH_DIMESION) { + + var voxel = Voxels.getVoxelAt(position.x + x * cellScale, position.y + y * cellScale, position.z, cellScale); + var predatorCellType = ((cell.type + 1) % cellTypes.length); + var predatorCellColor = cellTypes[predatorCellType]; + if (voxel.red == predatorCellColor.r && voxel.green == predatorCellColor.g && voxel.blue == predatorCellColor.b) { + nextCells[i][j].type = predatorCellType; + nextCells[i][j].changed = true; + } + } else { + + if (currentCells[y][x].type == ((cell.type + 1) % cellTypes.length)) { + nextCells[i][j] = currentCells[y][x]; + nextCells[i][j].changed = true; + } else { + //indicate no update + nextCells[i][j].changed = true; + } + } + } + } + + for (i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { + if (nextCells[i][j].changed == true) { + // there has been a change to this cell, change the value in the currentCells array + currentCells[i][j] = nextCells[i][j]; + } + } + } +} + +function sendNextCells() { + for (var i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { + for (var j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { + if (nextCells[i][j].changed == true) { + // there has been a change to the state of this cell, send it + + // find the x and y position for this voxel, z = 0 + var x = j * cellScale; + var y = i * cellScale; + var type = nextCells[i][j].type; + + // queue a packet to add a voxel for the new cell + Voxels.setVoxel(position.x + x, position.y + y, position.z, cellScale, cellTypes[type].r, cellTypes[type].g, cellTypes[type].b); + } + } + } +} + +var sentFirstBoard = false; + +var UPDATES_PER_SECOND = 3.0; +var frameIndex = 1.0; +var oldFrameIndex = 0; + +function step(deltaTime) { + frameIndex += deltaTime * UPDATES_PER_SECOND; + if (Math.floor(frameIndex) == oldFrameIndex) { + return; + } + oldFrameIndex++; + + if (sentFirstBoard) { + // we've already sent the first full board, perform a step in time + updateCells(); + } else { + // this will be our first board send + sentFirstBoard = true; + } + + sendNextCells(); +} + +function scriptEnding() { + Voxels.eraseVoxel(regionMarkerX, regionMarkerY, position.z, cellScale); +} + +Script.scriptEnding.connect(scriptEnding); + +Script.update.connect(step); +//Voxels.setMaxPacketSize(1); //this is needed or a bug occurs :( +Voxels.setPacketsPerSecond(2000); From 592c571fcb6d3c9d71916dd4fd3d82c3c2fdc8a2 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 17 Jul 2014 18:15:34 -0700 Subject: [PATCH 04/45] Fixed crash bug in voxelScriptingInterface --- examples/rockPaperScissorsCells.js | 10 ++++------ libraries/voxels/src/VoxelsScriptingInterface.cpp | 6 ++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/examples/rockPaperScissorsCells.js b/examples/rockPaperScissorsCells.js index 9227278455..4f6de888a1 100644 --- a/examples/rockPaperScissorsCells.js +++ b/examples/rockPaperScissorsCells.js @@ -29,9 +29,7 @@ var cellTypes = []; cellTypes[0] = { r: 255, g: 0, b: 0 }; cellTypes[1] = { r: 0, g: 255, b: 0 }; cellTypes[2] = { r: 0, g:0, b: 255 }; -cellTypes[3] = { r: 255, g: 255, b: 255 }; -cellTypes[4] = { r: 255, g: 0, b: 255 }; -cellTypes[5] = { r: 0, g: 255, b: 255 }; + //Check for free region for AC var regionMarkerX = -1; @@ -152,11 +150,11 @@ function updateCells() { } else { if (currentCells[y][x].type == ((cell.type + 1) % cellTypes.length)) { - nextCells[i][j] = currentCells[y][x]; + nextCells[i][j].type = currentCells[y][x].type; nextCells[i][j].changed = true; } else { //indicate no update - nextCells[i][j].changed = true; + nextCells[i][j].changed = false; } } } @@ -192,7 +190,7 @@ function sendNextCells() { var sentFirstBoard = false; -var UPDATES_PER_SECOND = 3.0; +var UPDATES_PER_SECOND = 6.0; var frameIndex = 1.0; var oldFrameIndex = 0; diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index e877f99760..ba1c3d72de 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -81,11 +81,17 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale, DeleteVoxelCommand* deleteCommand = new DeleteVoxelCommand(_tree, addVoxelDetail, getVoxelPacketSender()); + static QMutex mutex; + mutex.lock(); + _undoStack->beginMacro(addCommand->text()); // As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves. _undoStack->push(deleteCommand); _undoStack->push(addCommand); _undoStack->endMacro(); + + //Unlock the mutex + mutex.unlock(); } else { // queue the destructive add queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail); From 814dd0fcb6408ed9b07fdf4be9ea5d22c5a1fcbb Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 18 Jul 2014 11:37:13 -0700 Subject: [PATCH 05/45] Fixed another crash relating to undo stack. Made script support the VoxelViewer --- examples/rockPaperScissorsCells.js | 31 +++++++++++++++++++--- libraries/networking/src/ResourceCache.cpp | 9 +++++++ libraries/networking/src/ResourceCache.h | 2 +- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/examples/rockPaperScissorsCells.js b/examples/rockPaperScissorsCells.js index 4f6de888a1..aa7d4ef040 100644 --- a/examples/rockPaperScissorsCells.js +++ b/examples/rockPaperScissorsCells.js @@ -11,10 +11,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var NUMBER_OF_CELLS_EACH_DIMENSION = 64; +var NUMBER_OF_CELLS_EACH_DIMENSION = 48; var NUMBER_OF_CELLS_REGION_EACH_DIMESION = 16; var REGIONS_EACH_DIMENSION = NUMBER_OF_CELLS_EACH_DIMENSION / NUMBER_OF_CELLS_REGION_EACH_DIMESION; +var isLocal = false; + var currentCells = []; var nextCells = []; @@ -24,11 +26,20 @@ var position = {x: 0, y: 0, z: 0 }; var METER_LENGTH = 1; var cellScale = (NUMBER_OF_CELLS_EACH_DIMENSION * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION; +var viewerPosition = {x: cornerPosition.x + (NUMBER_OF_CELLS_EACH_DIMENSION / 2) * cellScale, y: cornerPosition.y + (NUMBER_OF_CELLS_EACH_DIMENSION / 2) * cellScale, z: cornerPosition.z }; + +viewerPosition.z += 50; +var yaw = 0; +var orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0); + //Feel free to add new cell types here. It can be more than three. var cellTypes = []; cellTypes[0] = { r: 255, g: 0, b: 0 }; cellTypes[1] = { r: 0, g: 255, b: 0 }; cellTypes[2] = { r: 0, g:0, b: 255 }; +cellTypes[3] = { r: 0, g:255, b: 255 }; +cellTypes[4] = { r: 255, g:0, b: 255 }; +//cellTypes[5] = { r: 255, g:255, b: 255 }; //Check for free region for AC @@ -76,8 +87,10 @@ for (var i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { // create the array to hold this row in the nextCells array nextCells[i] = []; + var randomColor = Math.floor(Math.random() * cellTypes.length); + for (var j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { - currentCells[i][j] = { changed: true, type: Math.floor(Math.random() * cellTypes.length) }; + currentCells[i][j] = { changed: true, type: randomColor }; // put the same value in the nextCells array for first board draw nextCells[i][j] = currentCells[i][j]; @@ -207,6 +220,15 @@ function step(deltaTime) { } else { // this will be our first board send sentFirstBoard = true; + + print("AHHHH"); + print(viewerPosition.x + " " + viewerPosition.y + " " + viewerPosition.z); + + if (isLocal == false) { + VoxelViewer.setPosition(viewerPosition); + VoxelViewer.setOrientation(orientation); + VoxelViewer.queryOctree(); + } } sendNextCells(); @@ -219,5 +241,8 @@ function scriptEnding() { Script.scriptEnding.connect(scriptEnding); Script.update.connect(step); -//Voxels.setMaxPacketSize(1); //this is needed or a bug occurs :( Voxels.setPacketsPerSecond(2000); + +// test for local... +Menu.isOptionChecked("Voxels"); +isLocal = true; // will only get here on local client \ No newline at end of file diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index a183e2f9a1..73c01ef582 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -40,6 +40,15 @@ void ResourceCache::refresh(const QUrl& url) { } QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& fallback, bool delayLoad, void* extra) { + + if (QThread::currentThread() != thread()) { + QSharedPointer result; + QMetaObject::invokeMethod(this, "getResource", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QSharedPointer, result), Q_ARG(const QUrl&, url), Q_ARG(const QUrl&, fallback), + Q_ARG(bool, delayLoad), Q_ARG(void*, extra)); + return result; + } + if (!url.isValid() && !url.isEmpty() && fallback.isValid()) { return getResource(fallback, QUrl(), delayLoad); } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 1593ad45fc..c3a5974da7 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -52,7 +52,7 @@ protected: /// \param fallback a fallback URL to load if the desired one is unavailable /// \param delayLoad if true, don't load the resource immediately; wait until load is first requested /// \param extra extra data to pass to the creator, if appropriate - QSharedPointer getResource(const QUrl& url, const QUrl& fallback = QUrl(), + Q_INVOKABLE QSharedPointer getResource(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false, void* extra = NULL); /// Creates a new resource. From 4410411d9d4c2f32eeaafca369e572a1c37e8d86 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 18 Jul 2014 14:51:53 -0700 Subject: [PATCH 06/45] Script improvements for AC --- examples/rockPaperScissorsCells.js | 118 ++++++++++-------- .../voxels/src/VoxelsScriptingInterface.cpp | 5 + 2 files changed, 68 insertions(+), 55 deletions(-) diff --git a/examples/rockPaperScissorsCells.js b/examples/rockPaperScissorsCells.js index aa7d4ef040..d7101ce4c7 100644 --- a/examples/rockPaperScissorsCells.js +++ b/examples/rockPaperScissorsCells.js @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var NUMBER_OF_CELLS_EACH_DIMENSION = 48; +var NUMBER_OF_CELLS_EACH_DIMENSION = 64; var NUMBER_OF_CELLS_REGION_EACH_DIMESION = 16; var REGIONS_EACH_DIMENSION = NUMBER_OF_CELLS_EACH_DIMENSION / NUMBER_OF_CELLS_REGION_EACH_DIMESION; @@ -37,8 +37,8 @@ var cellTypes = []; cellTypes[0] = { r: 255, g: 0, b: 0 }; cellTypes[1] = { r: 0, g: 255, b: 0 }; cellTypes[2] = { r: 0, g:0, b: 255 }; -cellTypes[3] = { r: 0, g:255, b: 255 }; -cellTypes[4] = { r: 255, g:0, b: 255 }; +cellTypes[3] = { r: 0, g: 255, b: 255 }; +//cellTypes[4] = { r: 255, g:0, b: 255 }; //cellTypes[5] = { r: 255, g:255, b: 255 }; @@ -50,51 +50,45 @@ var regionMarkerJ = -1; var regionMarkerColor = {r: 255, g: 0, b: 255}; -for (var i = 0; i < REGIONS_EACH_DIMENSION; i++) { - for (var j = 0; j < REGIONS_EACH_DIMENSION; j++) { - var x = cornerPosition.x + (j) * cellScale; - var y = cornerPosition.y + (i + NUMBER_OF_CELLS_EACH_DIMENSION) * cellScale; - var z = cornerPosition.z; - var voxel = Voxels.getVoxelAt(x, y, z, cellScale); - if (voxel.x != x || voxel.y != y || voxel.z != z || voxel.s != cellScale || - voxel.red != regionMarkerColor.r || voxel.green != regionMarkerColor.g || voxel.blue != regionMarkerColor.b) { - regionMarkerX = x; - regionMarkerY = y; - regionMarkerI = i; - regionMarkerJ = j; - i = REGIONS_EACH_DIMENSION; //force quit loop - break; + +function init() { + + for (var i = 0; i < REGIONS_EACH_DIMENSION; i++) { + for (var j = 0; j < REGIONS_EACH_DIMENSION; j++) { + var x = cornerPosition.x + (j) * cellScale; + var y = cornerPosition.y + (i + NUMBER_OF_CELLS_EACH_DIMENSION) * cellScale; + var z = cornerPosition.z; + var voxel = Voxels.getVoxelAt(x, y, z, cellScale); + if (voxel.x != x || voxel.y != y || voxel.z != z || voxel.s != cellScale || + voxel.red != regionMarkerColor.r || voxel.green != regionMarkerColor.g || voxel.blue != regionMarkerColor.b) { + regionMarkerX = x; + regionMarkerY = y; + regionMarkerI = i; + regionMarkerJ = j; + i = REGIONS_EACH_DIMENSION; //force quit loop + break; + } } } -} + + Voxels.setVoxel(regionMarkerX, regionMarkerY, position.z, cellScale, regionMarkerColor.r, regionMarkerColor.g, regionMarkerColor.b); -if (regionMarkerX == -1) { - print("No available Cellular Automata regions found!") - Script.stop(); -} - -position.x = cornerPosition.x + regionMarkerJ * NUMBER_OF_CELLS_REGION_EACH_DIMESION * cellScale; -position.y = cornerPosition.y + regionMarkerI * NUMBER_OF_CELLS_REGION_EACH_DIMESION * cellScale; -position.z = cornerPosition.z; - -Voxels.setVoxel(regionMarkerX, regionMarkerY, position.z, cellScale, regionMarkerColor.r, regionMarkerColor.g, regionMarkerColor.b); - -// randomly populate the cell start values -for (var i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { - // create the array to hold this row - currentCells[i] = []; - - // create the array to hold this row in the nextCells array - nextCells[i] = []; - - var randomColor = Math.floor(Math.random() * cellTypes.length); - - for (var j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { - currentCells[i][j] = { changed: true, type: randomColor }; - - // put the same value in the nextCells array for first board draw - nextCells[i][j] = currentCells[i][j]; - } + // randomly populate the cell start values + var randomColor = Math.floor(Math.random() * cellTypes.length); + for (var i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { + // create the array to hold this row + currentCells[i] = []; + + // create the array to hold this row in the nextCells array + nextCells[i] = []; + + for (var j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { + currentCells[i][j] = { changed: true, type: randomColor }; + + // put the same value in the nextCells array for first board draw + nextCells[i][j] = currentCells[i][j]; + } + } } function updateCells() { @@ -202,35 +196,49 @@ function sendNextCells() { } var sentFirstBoard = false; +var voxelViewerInit = false; var UPDATES_PER_SECOND = 6.0; var frameIndex = 1.0; var oldFrameIndex = 0; +var framesToWait = UPDATES_PER_SECOND; + function step(deltaTime) { + + if (isLocal == false) { + if (voxelViewerInit == false) { + VoxelViewer.setPosition(viewerPosition); + VoxelViewer.setOrientation(orientation); + voxelViewerInit = true; + } + VoxelViewer.queryOctree(); + } + frameIndex += deltaTime * UPDATES_PER_SECOND; if (Math.floor(frameIndex) == oldFrameIndex) { return; } oldFrameIndex++; + + if (frameIndex <= framesToWait) { + return; + } + print("UPDATE"); + if (sentFirstBoard) { // we've already sent the first full board, perform a step in time updateCells(); } else { // this will be our first board send sentFirstBoard = true; - - print("AHHHH"); - print(viewerPosition.x + " " + viewerPosition.y + " " + viewerPosition.z); - - if (isLocal == false) { - VoxelViewer.setPosition(viewerPosition); - VoxelViewer.setOrientation(orientation); - VoxelViewer.queryOctree(); - } + init(); + } + + if (isLocal == false) { + VoxelViewer.queryOctree(); } - sendNextCells(); } diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index ba1c3d72de..093d736720 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -116,11 +116,16 @@ void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale } if (_undoStack) { + DeleteVoxelCommand* command = new DeleteVoxelCommand(_tree, deleteVoxelDetail, getVoxelPacketSender()); + + static QMutex mutex; + mutex.lock(); // As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves. _undoStack->push(command); + mutex.unlock(); } else { getVoxelPacketSender()->queueVoxelEditMessages(PacketTypeVoxelErase, 1, &deleteVoxelDetail); _tree->deleteVoxelAt(deleteVoxelDetail.x, deleteVoxelDetail.y, deleteVoxelDetail.z, deleteVoxelDetail.s); From 06294b403b0a8efbe04ed9f22081b078b1152757 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Jul 2014 18:31:58 -0700 Subject: [PATCH 07/45] Add pasteModels --- interface/src/Application.cpp | 19 ++++++------------- interface/src/Application.h | 3 +++ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 476cc45242..7a2a359dbb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1602,17 +1602,13 @@ void Application::importVoxels() { } void Application::importModels(const QString& filename) { - _importSucceded = false; + _modelClipboard.eraseAllOctreeElements(); + _modelClipboard.readFromSVOFile(filename.toLocal8Bit().constData()); + _modelClipboard.reaverageOctreeElements(); +} - - _models.getTree()->readFromSVOFile(filename.toLocal8Bit().constData()); - _models.getTree()->reaverageOctreeElements(); - - - // restore the main window's active state - _window->activateWindow(); - - emit importDone(); +void Application::pasteModels(float x, float y, float z) { + _modelClipboard.sendModels(&_modelEditSender, x, y, z); } void Application::cutVoxels(const VoxelDetail& sourceVoxel) { @@ -2124,7 +2120,6 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("models"); _models.update(); // update the models... - _modelClipboardRenderer.update(); } { @@ -2613,7 +2608,6 @@ void Application::updateShadowMap() { _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE); _particles.render(OctreeRenderer::SHADOW_RENDER_MODE); _models.render(OctreeRenderer::SHADOW_RENDER_MODE); - _modelClipboardRenderer.render(OctreeRenderer::SHADOW_RENDER_MODE); glDisable(GL_POLYGON_OFFSET_FILL); @@ -2814,7 +2808,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... models..."); _models.render(); - _modelClipboardRenderer.render(); } // render the ambient occlusion effect if enabled diff --git a/interface/src/Application.h b/interface/src/Application.h index 1f20d21de6..82427f697a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -200,6 +200,8 @@ public: bool getImportSucceded() { return _importSucceded; } VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; } VoxelTree* getClipboard() { return &_clipboard; } + ModelTree* getModelClipboard() { return &_modelClipboard; } + ModelTreeRenderer* getModelClipboardRenderer() { return &_modelClipboardRenderer; } Environment* getEnvironment() { return &_environment; } bool isMousePressed() const { return _mousePressed; } bool isMouseHidden() const { return _mouseHidden; } @@ -312,6 +314,7 @@ public slots: void nodeKilled(SharedNodePointer node); void packetSent(quint64 length); + void pasteModels(float x, float y, float z); void exportModels(const QString& filename, float x, float y, float z, float scale); void importModels(const QString& filename); From 051bbd5d3403ec8b4a66b51c2336c9c15c8194a5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Jul 2014 18:34:22 -0700 Subject: [PATCH 08/45] Add LocalModelsOverlay --- .../src/ui/overlays/LocalModelsOverlay.cpp | 45 +++++++++++++++++++ .../src/ui/overlays/LocalModelsOverlay.h | 35 +++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 interface/src/ui/overlays/LocalModelsOverlay.cpp create mode 100644 interface/src/ui/overlays/LocalModelsOverlay.h diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp new file mode 100644 index 0000000000..ed38d41603 --- /dev/null +++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp @@ -0,0 +1,45 @@ +// +// LocalModelsOverlay.cpp +// interface/src/ui/overlays +// +// Created by Ryan Huffman on 07/08/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Application.h" + +#include "LocalModelsOverlay.h" + +LocalModelsOverlay::LocalModelsOverlay(ModelTreeRenderer* modelTreeRenderer) : + Volume3DOverlay(), + _modelTreeRenderer(modelTree) { +} + +LocalModelsOverlay::~LocalModelsOverlay() { +} + +void LocalModelsOverlay::update(float deltatime) { + _modelTreeRenderer->update(); +} + +void LocalModelsOverlay::render() { + if (_visible) { + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + glScalef(_size, _size, _size); + _modelTreeRenderer->render(); + } glPopMatrix(); + } +} + +void LocalModelsOverlay::setProperties(const QScriptValue &properties) { + Volume3DOverlay::setProperties(properties); + + if (properties.property("scale").isValid()) { + setSize(properties.property("scale").toVariant().toFloat()); + } +} + diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h new file mode 100644 index 0000000000..02a990119a --- /dev/null +++ b/interface/src/ui/overlays/LocalModelsOverlay.h @@ -0,0 +1,35 @@ +// +// LocalModelsOverlay.h +// interface/src/ui/overlays +// +// Created by Ryan Huffman on 07/08/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_LocalModelsOverlay_h +#define hifi_LocalModelsOverlay_h + +// #include "models/ModelTree.h" +#include "models/ModelTreeRenderer.h" + +#include "Volume3DOverlay.h" + +class LocalModelsOverlay : public Volume3DOverlay { + Q_OBJECT +public: + LocalModelsOverlay(ModelTreeRenderer* modelTreeRenderer); + ~LocalModelsOverlay(); + + virtual void update(float deltatime); + virtual void render(); + + virtual void setProperties(const QScriptValue& properties); + +private: + ModelTreeRenderer *_modelTreeRenderer; +}; + +#endif // hifi_LocalModelsOverlay_h From 7bd1628465d3343c3bf1326025b8f7cdee6b8ebb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Jul 2014 18:36:43 -0700 Subject: [PATCH 09/45] Add SendModelsOperation --- libraries/models/src/ModelTree.cpp | 29 +++++++++++++++++++++++++ libraries/models/src/ModelTree.h | 3 +++ libraries/models/src/ModelTreeElement.h | 6 +++++ 3 files changed, 38 insertions(+) diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index 1489f4db19..98902ac6c0 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "ModelEditPacketSender.h" +#include "ModelItem.h" + #include "ModelTree.h" ModelTree::ModelTree(bool shouldReaverage) : Octree(shouldReaverage) { @@ -204,6 +207,32 @@ void ModelTree::deleteModel(const ModelItemID& modelID) { } } +void ModelTree::sendModels(ModelEditPacketSender* packetSender, float x, float y, float z) { + SendModelsOperationArgs args; + args.packetSender = packetSender; + args.root = glm::vec3(x, y, z); + recurseTreeWithOperation(sendModelsOperation, &args); + packetSender->releaseQueuedMessages(); +} + +bool ModelTree::sendModelsOperation(OctreeElement* element, void* extraData) { + SendModelsOperationArgs* args = static_cast(extraData); + ModelTreeElement* modelTreeElement = static_cast(element); + + const QList& modelList = modelTreeElement->getModels(); + + for (int i = 0; i < modelList.size(); i++) { + uint32_t creatorTokenID = ModelItem::getNextCreatorTokenID(); + ModelItemID id(NEW_MODEL, creatorTokenID, false); + ModelItemProperties properties; + properties.copyFromModelItem(modelList.at(i)); + properties.setPosition(properties.getPosition() + args->root); + args->packetSender->queueModelEditMessage(PacketTypeModelAddOrEdit, id, properties); + } + + return true; +} + // scans the tree and handles mapping locally created models to know IDs. // in the event that this tree is also viewing the scene, then we need to also // search the tree to make sure we don't have a duplicate model from the viewing diff --git a/libraries/models/src/ModelTree.h b/libraries/models/src/ModelTree.h index 9ab081f46c..7827d9f9f3 100644 --- a/libraries/models/src/ModelTree.h +++ b/libraries/models/src/ModelTree.h @@ -84,10 +84,13 @@ public: void setFBXService(ModelItemFBXService* service) { _fbxService = service; } const FBXGeometry* getGeometryForModel(const ModelItem& modelItem) { return _fbxService ? _fbxService->getGeometryForModel(modelItem) : NULL; + } + void sendModels(ModelEditPacketSender* packetSender, float x, float y, float z); private: + static bool sendModelsOperation(OctreeElement* element, void* extraData); static bool updateOperation(OctreeElement* element, void* extraData); static bool findAndUpdateOperation(OctreeElement* element, void* extraData); static bool findAndUpdateWithIDandPropertiesOperation(OctreeElement* element, void* extraData); diff --git a/libraries/models/src/ModelTreeElement.h b/libraries/models/src/ModelTreeElement.h index 8d2f5064bd..5f28b2f981 100644 --- a/libraries/models/src/ModelTreeElement.h +++ b/libraries/models/src/ModelTreeElement.h @@ -44,6 +44,12 @@ public: bool isViewing; }; +class SendModelsOperationArgs { +public: + glm::vec3 root; + ModelEditPacketSender* packetSender; +}; + class ModelTreeElement : public OctreeElement { From 521bf6023e40b137c10fb997a2da41155f4bd657 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Jul 2014 18:38:09 -0700 Subject: [PATCH 10/45] Implement pasteModels in Clipboard --- interface/src/scripting/ClipboardScriptingInterface.cpp | 6 +++--- interface/src/scripting/ClipboardScriptingInterface.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index 39b5db9418..d75e29e423 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -103,13 +103,13 @@ void ClipboardScriptingInterface::nudgeVoxel(float x, float y, float z, float s, void ClipboardScriptingInterface::exportModels(const QString& filename, float x, float y, float z, float s) { - Application::getInstance()->exportModels(filename, x / TREE_SCALE, y / TREE_SCALE, z / TREE_SCALE, s / TREE_SCALE); + Application::getInstance()->exportModels(filename, x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE, s / (float)TREE_SCALE); } void ClipboardScriptingInterface::importModels(const QString& filename) { Application::getInstance()->importModels(filename); } -void ClipboardScriptingInterface::pasteModels() { - // TODO +void ClipboardScriptingInterface::pasteModels(float x, float y, float z, float s) { + Application::getInstance()->pasteModels(x, y, z); } diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 2ff287b29f..373a7308ae 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -45,7 +45,7 @@ public slots: void importModels(const QString& filename); void exportModels(const QString& filename, float x, float y, float z, float s); - void pasteModels(); + void pasteModels(float x, float y, float z, float s); }; #endif // hifi_ClipboardScriptingInterface_h From 43f61b4718c9ef47ed94bf1e3613e37b7f31181e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Jul 2014 18:38:42 -0700 Subject: [PATCH 11/45] Add localmodels overlay type --- interface/src/ui/overlays/Overlays.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 581947c074..d73a01103d 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -14,6 +14,7 @@ #include "Cube3DOverlay.h" #include "ImageOverlay.h" #include "Line3DOverlay.h" +#include "LocalModelsOverlay.h" #include "LocalVoxelsOverlay.h" #include "ModelOverlay.h" #include "Overlays.h" @@ -158,6 +159,13 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay->setProperties(properties); created = true; is3D = true; + } else if (type == "localmodels") { + qDebug() << "Making localmodels"; + thisOverlay = new LocalModelsOverlay(Application::getInstance()->getModelClipboardRenderer()); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + created = true; + is3D = true; } else if (type == "model") { thisOverlay = new ModelOverlay(); thisOverlay->init(_parent); From 06e6505584429e69b31aed8a972935bd0d7b2803 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Jul 2014 18:39:44 -0700 Subject: [PATCH 12/45] Update and add localModels overlay to editModels.js import/export --- examples/editModels.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index aa0505304b..47161d57a8 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -153,7 +153,7 @@ var ExportMenu = function(opts) { width: cancelWidth, height: height, color: { red: 255, green: 255, blue: 255 }, - text: "Cancel" + text: "Close" }); var voxelPreview = Overlays.addOverlay("cube", { @@ -291,6 +291,12 @@ var ModelImporter = function(opts) { var fullWidth = titleWidth + cancelWidth + (2 * margin); // TODO: Show import preview + var localModels = Overlays.addOverlay("localmodels", { + position: { x: 1, y: 1, z: 1 }, + scale: 1, + visible: false + }); + var importScale = 1; var importBoundaries = Overlays.addOverlay("cube", { position: { x: 0, y: 0, z: 0 }, size: 1, @@ -337,6 +343,7 @@ var ModelImporter = function(opts) { this.setImportVisible = function(visible) { Overlays.editOverlay(importBoundaries, { visible: visible }); + Overlays.editOverlay(localModels, { visible: visible }); Overlays.editOverlay(cancelButton, { visible: visible }); Overlays.editOverlay(titleText, { visible: visible }); Overlays.editOverlay(background, { visible: visible }); @@ -346,6 +353,9 @@ var ModelImporter = function(opts) { this.moveImport = function(position) { importPosition = position; // TODO: Show import preview + Overlays.editOverlay(localModels, { + position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } + }); Overlays.editOverlay(importBoundaries, { position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } }); @@ -412,14 +422,15 @@ var ModelImporter = function(opts) { var y = parts[3]; var z = parts[4]; var s = parts[5]; + importScale = s; if (hostname != location.hostname) { if (!Window.confirm(("These models were not originally exported from this domain. Continue?"))) { return; } } else { if (Window.confirm(("Would you like to import back to the source location?"))) { - Window.alert("(TODO) Importing backing to source location"); Clipboard.importModels(filename); + Clipboard.pasteModels(x, y, z, 1); return; } } @@ -427,19 +438,21 @@ var ModelImporter = function(opts) { Clipboard.importModels(filename); self._importing = true; self.setImportVisible(true); + Overlays.editOverlay(importBoundaries, { size: s }); } } } this.paste = function() { if (self._importing) { - self._importing = false; - self.setImportVisible(false); - Clipboard.pasteModels(); + // self._importing = false; + // self.setImportVisible(false); + Clipboard.pasteModels(importPosition.x, importPosition.y, importPosition.z, 1); } } this.cleanup = function() { + Overlays.deleteOverlay(localModels); Overlays.deleteOverlay(importBoundaries); Overlays.deleteOverlay(cancelButton); Overlays.deleteOverlay(titleText); From 01737e18b2e9e06c78dc650d662238923a9208de Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Jul 2014 19:06:10 -0700 Subject: [PATCH 13/45] Update LocalModelOverlay to work prorperly with translations --- interface/src/Application.cpp | 1 + interface/src/Application.h | 1 + .../src/ui/overlays/LocalModelsOverlay.cpp | 19 +++++++------------ .../src/ui/overlays/LocalModelsOverlay.h | 2 -- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7a2a359dbb..e85d44a901 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -179,6 +179,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()) { + // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); diff --git a/interface/src/Application.h b/interface/src/Application.h index 82427f697a..756cb7dff7 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -227,6 +227,7 @@ public: float getPacketsPerSecond() const { return _packetsPerSecond; } float getBytesPerSecond() const { return _bytesPerSecond; } const glm::vec3& getViewMatrixTranslation() const { return _viewMatrixTranslation; } + void setViewMatrixTranslation(const glm::vec3& translation) { _viewMatrixTranslation = translation; } /// if you need to access the application settings, use lockSettings()/unlockSettings() QSettings* lockSettings() { _settingsMutex.lock(); return _settings; } diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp index ed38d41603..6bb1d9ce88 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.cpp +++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp @@ -15,7 +15,7 @@ LocalModelsOverlay::LocalModelsOverlay(ModelTreeRenderer* modelTreeRenderer) : Volume3DOverlay(), - _modelTreeRenderer(modelTree) { + _modelTreeRenderer(modelTreeRenderer) { } LocalModelsOverlay::~LocalModelsOverlay() { @@ -28,18 +28,13 @@ void LocalModelsOverlay::update(float deltatime) { void LocalModelsOverlay::render() { if (_visible) { glPushMatrix(); { - glTranslatef(_position.x, _position.y, _position.z); - glScalef(_size, _size, _size); + Application* app = Application::getInstance(); + glm::vec3 oldTranslation = app->getViewMatrixTranslation(); + app->setViewMatrixTranslation(oldTranslation + _position); + _modelTreeRenderer->render(); + + Application::getInstance()->setViewMatrixTranslation(oldTranslation); } glPopMatrix(); } } - -void LocalModelsOverlay::setProperties(const QScriptValue &properties) { - Volume3DOverlay::setProperties(properties); - - if (properties.property("scale").isValid()) { - setSize(properties.property("scale").toVariant().toFloat()); - } -} - diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h index 02a990119a..88bf55aaf0 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.h +++ b/interface/src/ui/overlays/LocalModelsOverlay.h @@ -25,8 +25,6 @@ public: virtual void update(float deltatime); virtual void render(); - - virtual void setProperties(const QScriptValue& properties); private: ModelTreeRenderer *_modelTreeRenderer; From 6a53765a47241d491b1e3924cd53faf0c4f95eef Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Jul 2014 19:06:23 -0700 Subject: [PATCH 14/45] Rename buttons in editModels.js --- examples/editModels.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 47161d57a8..671c79ad39 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -153,7 +153,7 @@ var ExportMenu = function(opts) { width: cancelWidth, height: height, color: { red: 255, green: 255, blue: 255 }, - text: "Close" + text: "Cancel" }); var voxelPreview = Overlays.addOverlay("cube", { @@ -337,7 +337,7 @@ var ModelImporter = function(opts) { height: height, color: { red: 255, green: 255, blue: 255 }, visible: false, - text: "Cancel" + text: "Close" }); this._importing = false; From d54e3741edc4fec430c8ba588de9158d2ae48f71 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 01:09:38 -0700 Subject: [PATCH 15/45] Add ModelTree::findModelsInCube --- libraries/models/src/ModelTree.cpp | 22 +++++++++++++++++++++- libraries/models/src/ModelTree.h | 4 +++- libraries/models/src/ModelTreeElement.cpp | 13 +++++++++++++ libraries/models/src/ModelTreeElement.h | 2 ++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index 98902ac6c0..df94213213 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -387,6 +387,26 @@ public: QVector _foundModels; }; +void ModelTree::findModelsInCube(const AACube& cube, QVector& foundModels) { + FindModelsInCubeArgs args(cube); + lockForRead(); + recurseTreeWithOperation(findInCubeOperation, &args); + unlock(); + // swap the two lists of model pointers instead of copy + foundModels.swap(args._foundModels); +} + +bool ModelTree::findInCubeOperation(OctreeElement* element, void* extraData) { + FindModelsInCubeArgs* args = static_cast< FindModelsInCubeArgs*>(extraData); + const AACube& elementCube = element->getAACube(); + if (elementCube.touches(args->_cube)) { + ModelTreeElement* modelTreeElement = static_cast(element); + modelTreeElement->getModelsInside(args->_cube, args->_foundModels); + return true; + } + return false; +} + bool ModelTree::findInCubeForUpdateOperation(OctreeElement* element, void* extraData) { FindModelsInCubeArgs* args = static_cast< FindModelsInCubeArgs*>(extraData); const AACube& elementCube = element->getAACube(); @@ -398,7 +418,7 @@ bool ModelTree::findInCubeForUpdateOperation(OctreeElement* element, void* extra return false; } -void ModelTree::findModelsForUpdate(const AACube& cube, QVector foundModels) { +void ModelTree::findModelsForUpdate(const AACube& cube, QVector& foundModels) { FindModelsInCubeArgs args(cube); lockForRead(); recurseTreeWithOperation(findInCubeForUpdateOperation, &args); diff --git a/libraries/models/src/ModelTree.h b/libraries/models/src/ModelTree.h index 7827d9f9f3..3a1159c9cf 100644 --- a/libraries/models/src/ModelTree.h +++ b/libraries/models/src/ModelTree.h @@ -63,12 +63,13 @@ public: /// \param foundModels[out] vector of const ModelItem* /// \remark Side effect: any initial contents in foundModels will be lost void findModels(const glm::vec3& center, float radius, QVector& foundModels); + void findModelsInCube(const AACube& cube, QVector& foundModels); /// finds all models that touch a cube /// \param cube the query cube /// \param foundModels[out] vector of non-const ModelItem* /// \remark Side effect: any initial contents in models will be lost - void findModelsForUpdate(const AACube& cube, QVector foundModels); + void findModelsForUpdate(const AACube& cube, QVector& foundModels); void addNewlyCreatedHook(NewlyCreatedModelHook* hook); void removeNewlyCreatedHook(NewlyCreatedModelHook* hook); @@ -92,6 +93,7 @@ private: static bool sendModelsOperation(OctreeElement* element, void* extraData); static bool updateOperation(OctreeElement* element, void* extraData); + static bool findInCubeOperation(OctreeElement* element, void* extraData); static bool findAndUpdateOperation(OctreeElement* element, void* extraData); static bool findAndUpdateWithIDandPropertiesOperation(OctreeElement* element, void* extraData); static bool findNearPointOperation(OctreeElement* element, void* extraData); diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index 960d1dd4cb..47ea8babac 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -399,6 +399,19 @@ void ModelTreeElement::getModels(const glm::vec3& searchPosition, float searchRa } } +void ModelTreeElement::getModelsInside(const AACube& box, QVector& foundModels) { + QList::iterator modelItr = _modelItems->begin(); + QList::iterator modelEnd = _modelItems->end(); + AACube modelCube; + while(modelItr != modelEnd) { + ModelItem* model = &(*modelItr); + if (box.contains(model->getPosition())) { + foundModels.push_back(model); + } + ++modelItr; + } +} + void ModelTreeElement::getModelsForUpdate(const AACube& box, QVector& foundModels) { QList::iterator modelItr = _modelItems->begin(); QList::iterator modelEnd = _modelItems->end(); diff --git a/libraries/models/src/ModelTreeElement.h b/libraries/models/src/ModelTreeElement.h index 5f28b2f981..c0e2e36095 100644 --- a/libraries/models/src/ModelTreeElement.h +++ b/libraries/models/src/ModelTreeElement.h @@ -138,6 +138,8 @@ public: /// \param models[out] vector of non-const ModelItem* void getModelsForUpdate(const AACube& box, QVector& foundModels); + void getModelsInside(const AACube& box, QVector& foundModels); + const ModelItem* getModelWithID(uint32_t id) const; bool removeModelWithID(uint32_t id); From 6ecc12cdeacb04eadb52758343f9b69871027529 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 01:11:05 -0700 Subject: [PATCH 16/45] Fix export models not cancelling correctly --- examples/editModels.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/editModels.js b/examples/editModels.js index 671c79ad39..1a5753838d 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -217,7 +217,9 @@ var ExportMenu = function(opts) { var s = self._scale; var filename = "models__" + Window.location.hostname + "__" + x + "_" + y + "_" + z + "_" + s + "__.svo"; filename = Window.save("Select where to save", filename, "*.svo") - Clipboard.exportModels(filename, x, y, z, s); + if (filename) { + Clipboard.exportModels(filename, x, y, z, s); + } self.close(); }; From a39a7e23564fe3b380b9ae493aa1d4b757d09e1c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 01:13:17 -0700 Subject: [PATCH 17/45] Update exportModels implementation --- interface/src/Application.cpp | 22 +++++++++++++++++----- interface/src/Application.h | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e85d44a901..e469cc7535 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1506,19 +1506,31 @@ struct SendVoxelsOperationArgs { const unsigned char* newBaseOctCode; }; -void Application::exportModels(const QString& filename, float x, float y, float z, float scale) { - ModelTreeElement* selectedNode = _models.getTree()->getModelAt(x, y, z, scale); - if (selectedNode) { - qDebug() << "Exporting models doing it!" << filename; +bool Application::exportModels(const QString& filename, float x, float y, float z, float scale) { + QVector models; + _models.getTree()->findModelsInCube(AACube(glm::vec3(x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE), scale / (float)TREE_SCALE), models); + if (models.size() > 0) { + glm::vec3 root(x, y, z); ModelTree exportTree; - _models.getTree()->copySubTreeIntoNewTree(selectedNode, &exportTree, true); + + for (int i = 0; i < models.size(); i++) { + ModelItemProperties properties; + ModelItemID id = models.at(i)->getModelItemID(); + id.isKnownID = false; + properties.copyFromModelItem(*models.at(i)); + properties.setPosition(properties.getPosition() - root); + exportTree.addModel(id, properties); + } + exportTree.writeToSVOFile(filename.toLocal8Bit().constData()); } else { qDebug() << "No models were selected"; + return false; } // restore the main window's active state _window->activateWindow(); + return true; } bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 756cb7dff7..44a93f5d7b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -316,7 +316,7 @@ public slots: void packetSent(quint64 length); void pasteModels(float x, float y, float z); - void exportModels(const QString& filename, float x, float y, float z, float scale); + bool exportModels(const QString& filename, float x, float y, float z, float scale); void importModels(const QString& filename); void importVoxels(); // doesn't include source voxel because it goes to clipboard From 68f1ad79d6dd0c9c5a71a6dd775ac195d3ed3de7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 01:14:07 -0700 Subject: [PATCH 18/45] Update clipboard call to exportModels to not scale to tree size --- interface/src/scripting/ClipboardScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index d75e29e423..10799c2abb 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -103,7 +103,7 @@ void ClipboardScriptingInterface::nudgeVoxel(float x, float y, float z, float s, void ClipboardScriptingInterface::exportModels(const QString& filename, float x, float y, float z, float s) { - Application::getInstance()->exportModels(filename, x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE, s / (float)TREE_SCALE); + Application::getInstance()->exportModels(filename, x, y, z, s); } void ClipboardScriptingInterface::importModels(const QString& filename) { From 49e35bbc84ea2f66e641712eb7402b3c7c3691c3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 01:14:18 -0700 Subject: [PATCH 19/45] Fix bug with ModelItemProperties When copying from a ModelItem the properties should be considered changed --- libraries/models/src/ModelItem.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 750af8f1b6..afa64e3bc6 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -1142,19 +1142,19 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _id = modelItem.getID(); _idSet = true; - _positionChanged = false; - _colorChanged = false; - _radiusChanged = false; + _positionChanged = true; + _colorChanged = true; + _radiusChanged = true; - _shouldDieChanged = false; - _modelURLChanged = false; - _modelRotationChanged = false; - _animationURLChanged = false; - _animationIsPlayingChanged = false; - _animationFrameIndexChanged = false; - _animationFPSChanged = false; - _glowLevelChanged = false; - _defaultSettings = false; + _shouldDieChanged = true; + _modelURLChanged = true; + _modelRotationChanged = true; + _animationURLChanged = true; + _animationIsPlayingChanged = true; + _animationFrameIndexChanged = true; + _animationFPSChanged = true; + _glowLevelChanged = true; + _defaultSettings = true; } QScriptValue ModelItemPropertiesToScriptValue(QScriptEngine* engine, const ModelItemProperties& properties) { From 3e2bb0f1684891f28f4c6d6dff5dc70ec2855a6c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 10:40:45 -0700 Subject: [PATCH 20/45] Add more complete error handling for import/export models --- examples/editModels.js | 25 +++++++++++++------ interface/src/Application.cpp | 9 ++++--- interface/src/Application.h | 2 +- .../scripting/ClipboardScriptingInterface.cpp | 8 +++--- .../scripting/ClipboardScriptingInterface.h | 4 +-- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 1a5753838d..f3650bada2 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -218,7 +218,10 @@ var ExportMenu = function(opts) { var filename = "models__" + Window.location.hostname + "__" + x + "_" + y + "_" + z + "_" + s + "__.svo"; filename = Window.save("Select where to save", filename, "*.svo") if (filename) { - Clipboard.exportModels(filename, x, y, z, s); + var success = Clipboard.exportModels(filename, x, y, z, s); + if (!succcess) { + WIndow.alert("Export failed: no models found in selected area."); + } } self.close(); }; @@ -431,16 +434,24 @@ var ModelImporter = function(opts) { } } else { if (Window.confirm(("Would you like to import back to the source location?"))) { - Clipboard.importModels(filename); - Clipboard.pasteModels(x, y, z, 1); + var success = Clipboard.importModels(filename); + if (success) { + Clipboard.pasteModels(x, y, z, 1); + } else { + Window.alert("There was an error importing the model file."); + } return; } } } - Clipboard.importModels(filename); - self._importing = true; - self.setImportVisible(true); - Overlays.editOverlay(importBoundaries, { size: s }); + var success = Clipboard.importModels(filename); + if (success) { + self._importing = true; + self.setImportVisible(true); + Overlays.editOverlay(importBoundaries, { size: s }); + } else { + Window.alert("There was an error importing the model file."); + } } } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e469cc7535..540cb47a79 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1614,10 +1614,13 @@ void Application::importVoxels() { emit importDone(); } -void Application::importModels(const QString& filename) { +bool Application::importModels(const QString& filename) { _modelClipboard.eraseAllOctreeElements(); - _modelClipboard.readFromSVOFile(filename.toLocal8Bit().constData()); - _modelClipboard.reaverageOctreeElements(); + bool success = _modelClipboard.readFromSVOFile(filename.toLocal8Bit().constData()); + if (success) { + _modelClipboard.reaverageOctreeElements(); + } + return success; } void Application::pasteModels(float x, float y, float z) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 44a93f5d7b..d15d8ae63f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -317,7 +317,7 @@ public slots: void pasteModels(float x, float y, float z); bool exportModels(const QString& filename, float x, float y, float z, float scale); - void importModels(const QString& filename); + bool importModels(const QString& filename); void importVoxels(); // doesn't include source voxel because it goes to clipboard void cutVoxels(const VoxelDetail& sourceVoxel); diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index 10799c2abb..aa4a06fa50 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -102,12 +102,12 @@ void ClipboardScriptingInterface::nudgeVoxel(float x, float y, float z, float s, } -void ClipboardScriptingInterface::exportModels(const QString& filename, float x, float y, float z, float s) { - Application::getInstance()->exportModels(filename, x, y, z, s); +bool ClipboardScriptingInterface::exportModels(const QString& filename, float x, float y, float z, float s) { + return Application::getInstance()->exportModels(filename, x, y, z, s); } -void ClipboardScriptingInterface::importModels(const QString& filename) { - Application::getInstance()->importModels(filename); +bool ClipboardScriptingInterface::importModels(const QString& filename) { + return Application::getInstance()->importModels(filename); } void ClipboardScriptingInterface::pasteModels(float x, float y, float z, float s) { diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 373a7308ae..aa5234a115 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -43,8 +43,8 @@ public slots: void nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec); void nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec); - void importModels(const QString& filename); - void exportModels(const QString& filename, float x, float y, float z, float s); + bool importModels(const QString& filename); + bool exportModels(const QString& filename, float x, float y, float z, float s); void pasteModels(float x, float y, float z, float s); }; From 85ed18fd415655cfb8817fa45e1ce71073d47061 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 10:43:44 -0700 Subject: [PATCH 21/45] Cleanup editModels.js --- examples/editModels.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index f3650bada2..fc1c43e934 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -186,7 +186,6 @@ var ExportMenu = function(opts) { if (positionStr == null) { break; } - print("position: " + position); var position = self.parsePosition(positionStr); if (position != null) { self.setPosition(position.x, position.y, position.z); @@ -238,13 +237,7 @@ var ExportMenu = function(opts) { }; - this.mousePressEvent = function(event) { - print("Mouse press"); - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - }; - this.mouseReleaseEvent = function(event) { - print("Mouse release"); var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); if (clickedOverlay == locationButton) { @@ -281,7 +274,6 @@ var ExportMenu = function(opts) { }; print("CONNECTING!"); - Controller.mousePressEvent.connect(this.mousePressEvent); Controller.mouseReleaseEvent.connect(this.mouseReleaseEvent); }; @@ -313,7 +305,7 @@ var ModelImporter = function(opts) { var pos = { x: windowDimensions.x / 2 - (fullWidth / 2), y: windowDimensions.y - 100 }; - var background= Overlays.addOverlay("text", { + var background = Overlays.addOverlay("text", { x: pos.x, y: pos.y, opacity: 1, @@ -414,7 +406,6 @@ var ModelImporter = function(opts) { // Would prefer to use {4} for the coords, but it would only capture the last digit. var fileRegex = /__(.+)__(\d+(?:\.\d+)?)_(\d+(?:\.\d+)?)_(\d+(?:\.\d+)?)_(\d+(?:\.\d+)?)__/; this.doImport = function() { - print("IMPORTING "); if (!self._importing) { var filename = Window.browse("Select models to import", "", "*.svo") if (filename) { @@ -1609,9 +1600,6 @@ function handeMenuEvent(menuItem){ modelImporter.paste(); } else if (menuItem == "Export Models") { if (!exportMenu) { - // if (modelImporter) { - // modelImporter.close(); - // } exportMenu = new ExportMenu({ onClose: function() { exportMenu = null; From a670889ffb15c82a36a3e299d2f4481768f48d47 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 10:44:36 -0700 Subject: [PATCH 22/45] Remove TODO comment --- examples/editModels.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index fc1c43e934..c0dfad871d 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -287,7 +287,6 @@ var ModelImporter = function(opts) { var cancelWidth = 100; var fullWidth = titleWidth + cancelWidth + (2 * margin); - // TODO: Show import preview var localModels = Overlays.addOverlay("localmodels", { position: { x: 1, y: 1, z: 1 }, scale: 1, @@ -349,7 +348,6 @@ var ModelImporter = function(opts) { var importPosition = { x: 0, y: 0, z: 0 }; this.moveImport = function(position) { importPosition = position; - // TODO: Show import preview Overlays.editOverlay(localModels, { position: { x: importPosition.x, y: importPosition.y, z: importPosition.z } }); From aa1c9d88dd88da6aeeb52878af086ba8d77e3dfa Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 10:48:26 -0700 Subject: [PATCH 23/45] Cleaup several qDebug calls and commented lines --- interface/src/scripting/WindowScriptingInterface.cpp | 3 --- interface/src/ui/overlays/LocalModelsOverlay.h | 1 - interface/src/ui/overlays/Overlays.cpp | 1 - 3 files changed, 5 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index f9b1b47549..ea0eeb0dd9 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -204,15 +204,12 @@ QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QS } QFileDialog fileDialog(Application::getInstance()->getWindow(), title, path, nameFilter); - // fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setAcceptMode(acceptMode); qDebug() << "Opening!"; QUrl fileUrl(directory); if (acceptMode == QFileDialog::AcceptSave) { fileDialog.setFileMode(QFileDialog::Directory); fileDialog.selectFile(fileUrl.fileName()); - // qDebug() << "Setting filename!"; - // fileDialog.setLabelText(QFileDialog::FileName, fileUrl.fileName()); } if (fileDialog.exec()) { return QScriptValue(fileDialog.selectedFiles().first()); diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h index 88bf55aaf0..7c4bffa342 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.h +++ b/interface/src/ui/overlays/LocalModelsOverlay.h @@ -12,7 +12,6 @@ #ifndef hifi_LocalModelsOverlay_h #define hifi_LocalModelsOverlay_h -// #include "models/ModelTree.h" #include "models/ModelTreeRenderer.h" #include "Volume3DOverlay.h" diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index d73a01103d..8ef800ef7b 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -160,7 +160,6 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope created = true; is3D = true; } else if (type == "localmodels") { - qDebug() << "Making localmodels"; thisOverlay = new LocalModelsOverlay(Application::getInstance()->getModelClipboardRenderer()); thisOverlay->init(_parent); thisOverlay->setProperties(properties); From ab52f384b7fddc64da65f762ba2f4cf01f3a3aff Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 31 Jul 2014 11:15:20 -0700 Subject: [PATCH 24/45] Improvements on RPS script --- examples/rockPaperScissorsCells.js | 50 ++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/examples/rockPaperScissorsCells.js b/examples/rockPaperScissorsCells.js index d7101ce4c7..2a9cb00a0b 100644 --- a/examples/rockPaperScissorsCells.js +++ b/examples/rockPaperScissorsCells.js @@ -6,12 +6,14 @@ // // This sample script creates a voxel wall that simulates the Rock Paper Scissors cellular // automata. http://www.gamedev.net/blog/844/entry-2249737-another-cellular-automaton-video/ +// If multiple instances of this script are run, they will combine into a larger wall. +// NOTE: You must run each instance one at a time. If they all start at once there are race conditions. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var NUMBER_OF_CELLS_EACH_DIMENSION = 64; +var NUMBER_OF_CELLS_EACH_DIMENSION = 48; var NUMBER_OF_CELLS_REGION_EACH_DIMESION = 16; var REGIONS_EACH_DIMENSION = NUMBER_OF_CELLS_EACH_DIMENSION / NUMBER_OF_CELLS_REGION_EACH_DIMESION; @@ -38,8 +40,6 @@ cellTypes[0] = { r: 255, g: 0, b: 0 }; cellTypes[1] = { r: 0, g: 255, b: 0 }; cellTypes[2] = { r: 0, g:0, b: 255 }; cellTypes[3] = { r: 0, g: 255, b: 255 }; -//cellTypes[4] = { r: 255, g:0, b: 255 }; -//cellTypes[5] = { r: 255, g:255, b: 255 }; //Check for free region for AC @@ -48,8 +48,19 @@ var regionMarkerY = -1; var regionMarkerI = -1; var regionMarkerJ = -1; -var regionMarkerColor = {r: 255, g: 0, b: 255}; +var regionMarkerColor = {r: 254, g: 0, b: 253}; +function setRegionToColor(startX, startY, width, height, color) { + for (var i = startY; i < startY + height; i++) { + for (var j = startX; j < startX + width; j++) { + + currentCells[i][j] = { changed: true, type: color }; + + // put the same value in the nextCells array for first board draw + nextCells[i][j] = { changed: true, type: color }; + } + } +} function init() { @@ -71,24 +82,30 @@ function init() { } } - Voxels.setVoxel(regionMarkerX, regionMarkerY, position.z, cellScale, regionMarkerColor.r, regionMarkerColor.g, regionMarkerColor.b); + //Didnt find an open spot, end script + if (regionMarkerX == -1) { + Script.stop(); + } + + position.x = cornerPosition.x + regionMarkerJ * NUMBER_OF_CELLS_REGION_EACH_DIMESION; + position.y = cornerPosition.y + regionMarkerI * NUMBER_OF_CELLS_REGION_EACH_DIMESION; + position.z = cornerPosition.z; + + Voxels.setVoxel(regionMarkerX, regionMarkerY, cornerPosition.z, cellScale, regionMarkerColor.r, regionMarkerColor.g, regionMarkerColor.b); - // randomly populate the cell start values - var randomColor = Math.floor(Math.random() * cellTypes.length); for (var i = 0; i < NUMBER_OF_CELLS_REGION_EACH_DIMESION; i++) { // create the array to hold this row currentCells[i] = []; // create the array to hold this row in the nextCells array nextCells[i] = []; - - for (var j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { - currentCells[i][j] = { changed: true, type: randomColor }; - - // put the same value in the nextCells array for first board draw - nextCells[i][j] = currentCells[i][j]; - } } + + var width = NUMBER_OF_CELLS_REGION_EACH_DIMESION / 2; + setRegionToColor(0, 0, width, width, 0); + setRegionToColor(0, width, width, width, 1); + setRegionToColor(width, width, width, width, 2); + setRegionToColor(width, 0, width, width, 3); } function updateCells() { @@ -171,7 +188,8 @@ function updateCells() { for (j = 0; j < NUMBER_OF_CELLS_REGION_EACH_DIMESION; j++) { if (nextCells[i][j].changed == true) { // there has been a change to this cell, change the value in the currentCells array - currentCells[i][j] = nextCells[i][j]; + currentCells[i][j].type = nextCells[i][j].type; + currentCells[i][j].changed = true; } } } @@ -224,8 +242,6 @@ function step(deltaTime) { if (frameIndex <= framesToWait) { return; } - - print("UPDATE"); if (sentFirstBoard) { // we've already sent the first full board, perform a step in time From 054e832cf874b97ceddc3667b6d97d4ab640621b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Jul 2014 11:32:17 -0700 Subject: [PATCH 25/45] Fix misspelled variable names --- examples/editModels.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index c0dfad871d..93954d84f3 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -218,8 +218,8 @@ var ExportMenu = function(opts) { filename = Window.save("Select where to save", filename, "*.svo") if (filename) { var success = Clipboard.exportModels(filename, x, y, z, s); - if (!succcess) { - WIndow.alert("Export failed: no models found in selected area."); + if (!success) { + Window.alert("Export failed: no models found in selected area."); } } self.close(); From f3b4f708a56bd95501adc947df3f0c6b9db5a965 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 31 Jul 2014 21:50:07 -0700 Subject: [PATCH 26/45] Add Script.load() JavaScript method --- interface/src/Application.cpp | 2 ++ libraries/script-engine/src/ScriptEngine.cpp | 13 +++++++++---- libraries/script-engine/src/ScriptEngine.h | 2 ++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 51f6076e7a..c3f191d9b4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3674,6 +3674,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(scriptFinished(const QString&))); + connect(scriptEngine, SIGNAL(loadScript(const QString&)), this, SLOT(loadScript(const QString&))); + scriptEngine->registerGlobalObject("Overlays", &_overlays); QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", WindowScriptingInterface::getInstance()); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index df66fa44d5..49cab1a1fb 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -156,7 +156,7 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, } else { NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); - qDebug() << "Downloading included script at" << url; + qDebug() << "Downloading script at" << url; QEventLoop loop; QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); @@ -681,12 +681,12 @@ void ScriptEngine::include(const QString& includeFile) { #endif QFile scriptFile(fileName); if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { - qDebug() << "Loading file:" << fileName; + qDebug() << "Including file:" << fileName; QTextStream in(&scriptFile); includeContents = in.readAll(); } else { - qDebug() << "ERROR Loading file:" << fileName; - emit errorMessage("ERROR Loading file:" + fileName); + qDebug() << "ERROR Including file:" << fileName; + emit errorMessage("ERROR Including file:" + fileName); } } @@ -699,6 +699,11 @@ void ScriptEngine::include(const QString& includeFile) { } } +void ScriptEngine::load(const QString& loadFile) { + QUrl url = resolveInclude(loadFile); + emit loadScript(url.toString()); +} + void ScriptEngine::nodeKilled(SharedNodePointer node) { _outgoingScriptAudioSequenceNumbers.remove(node->getUUID()); } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index fe39f286be..17cda5e183 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -102,6 +102,7 @@ public slots: void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void include(const QString& includeFile); + void load(const QString& loadfile); void print(const QString& message); void nodeKilled(SharedNodePointer node); @@ -115,6 +116,7 @@ signals: void errorMessage(const QString& message); void runningStateChanged(); void evaluationFinished(QScriptValue result, bool isException); + void loadScript(const QString& scriptName); protected: QString _scriptContents; From 779298b7dc63830d9d7f87fa77c1299823dbd31d Mon Sep 17 00:00:00 2001 From: Zu Date: Fri, 1 Aug 2014 14:11:50 +0800 Subject: [PATCH 27/45] improve: reduce animation jerks in avatar head rotation --- interface/src/devices/CaraFaceTracker.cpp | 53 +++++++++++------------ interface/src/devices/CaraFaceTracker.h | 9 ++-- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/interface/src/devices/CaraFaceTracker.cpp b/interface/src/devices/CaraFaceTracker.cpp index 9c67163dca..8160200641 100644 --- a/interface/src/devices/CaraFaceTracker.cpp +++ b/interface/src/devices/CaraFaceTracker.cpp @@ -37,6 +37,7 @@ static QString sampleJson = "[{\"id\":1, \ static const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.0f); static const float TRANSLATION_SCALE = 1.0f; static const int NUM_BLENDSHAPE_COEFF = 30; +static const int NUM_SMOOTHING_SAMPLES = 3; struct CaraPerson { struct CaraPose { @@ -217,9 +218,9 @@ private: CaraFaceTracker::CaraFaceTracker() : _lastReceiveTimestamp(0), - _previousPitch(0.0f), - _previousYaw(0.0f), - _previousRoll(0.0f), + _pitchAverage(NUM_SMOOTHING_SAMPLES), + _yawAverage(NUM_SMOOTHING_SAMPLES), + _rollAverage(NUM_SMOOTHING_SAMPLES), _eyeGazeLeftPitch(0.0f), _eyeGazeLeftYaw(0.0f), _eyeGazeRightPitch(0.0f), @@ -252,9 +253,9 @@ CaraFaceTracker::CaraFaceTracker() : CaraFaceTracker::CaraFaceTracker(const QHostAddress& host, quint16 port) : _lastReceiveTimestamp(0), - _previousPitch(0.0f), - _previousYaw(0.0f), - _previousRoll(0.0f), + _pitchAverage(NUM_SMOOTHING_SAMPLES), + _yawAverage(NUM_SMOOTHING_SAMPLES), + _rollAverage(NUM_SMOOTHING_SAMPLES), _eyeGazeLeftPitch(0.0f), _eyeGazeLeftYaw(0.0f), _eyeGazeRightPitch(0.0f), @@ -371,8 +372,9 @@ void CaraFaceTracker::decodePacket(const QByteArray& buffer) { CaraPerson person = CaraPacketDecoder::extractOne(buffer, &jsonError); if(jsonError.error == QJsonParseError::NoError) { + //do some noise filtering to the head poses - //reduce the noise first by truncating to 1 dp + //reduce the noise first by truncating to 1 dp person.pose.roll = glm::floor(person.pose.roll * 10) / 10; person.pose.pitch = glm::floor(person.pose.pitch * 10) / 10; person.pose.yaw = glm::floor(person.pose.yaw * 10) / 10; @@ -386,43 +388,40 @@ void CaraFaceTracker::decodePacket(const QByteArray& buffer) { float theta = 2 * acos(r.w); if (theta > EPSILON) { float rMag = glm::length(glm::vec3(r.x, r.y, r.z)); - const float AVERAGE_CARA_FRAME_TIME = 0.033f; + const float AVERAGE_CARA_FRAME_TIME = 0.04f; const float ANGULAR_VELOCITY_MIN = 1.2f; const float YAW_STANDARD_DEV_DEG = 2.5f; _headAngularVelocity = theta / AVERAGE_CARA_FRAME_TIME * glm::vec3(r.x, r.y, r.z) / rMag; - //use the angular velocity for roll and pitch, if it's below the threshold don't move - if(glm::abs(_headAngularVelocity.x) < ANGULAR_VELOCITY_MIN) { - person.pose.pitch = _previousPitch; - } + _pitchAverage.updateAverage(person.pose.pitch); + _rollAverage.updateAverage(person.pose.roll); - if(glm::abs(_headAngularVelocity.z) < ANGULAR_VELOCITY_MIN) { - person.pose.roll = _previousRoll; - } + //could use the angular velocity to detemine whether to update pitch and roll to further remove the noise. + //use the angular velocity for roll and pitch, update if > THRESHOLD + //if(glm::abs(_headAngularVelocity.x) > ANGULAR_VELOCITY_MIN) { + // _pitchAverage.updateAverage(person.pose.pitch); + //} + + //if(glm::abs(_headAngularVelocity.z) > ANGULAR_VELOCITY_MIN) { + // _rollAverage.updateAverage(person.pose.roll);; + //} //for yaw, the jitter is great, you can't use angular velocity because it swings too much //use the previous and current yaw, calculate the //abs difference and move it the difference is above the standard deviation which is around 2.5 - // (this will introduce some jerks but will not encounter lag) - - // < the standard deviation 2.5 deg, no move - if(glm::abs(person.pose.yaw - _previousYaw) < YAW_STANDARD_DEV_DEG) { + // > the standard deviation 2.5 deg, update the yaw smoothing average + if(glm::abs(person.pose.yaw - _yawAverage.getAverage()) > YAW_STANDARD_DEV_DEG) { //qDebug() << "Yaw Diff: " << glm::abs(person.pose.yaw - _previousYaw); - person.pose.yaw = _previousYaw; + _yawAverage.updateAverage(person.pose.yaw); } - //update the previous angles - _previousPitch = person.pose.pitch; - _previousYaw = person.pose.yaw; - _previousRoll = person.pose.roll; - //set the new rotation - newRotation = glm::quat(glm::vec3(DEGTORAD(person.pose.pitch), DEGTORAD(person.pose.yaw), DEGTORAD(-person.pose.roll))); + newRotation = glm::quat(glm::vec3(DEGTORAD(_pitchAverage.getAverage()), DEGTORAD(_yawAverage.getAverage()), DEGTORAD(-_rollAverage.getAverage()))); } else { //no change in position - newRotation = glm::quat(glm::vec3(DEGTORAD(_previousPitch), DEGTORAD(_previousYaw), DEGTORAD(-_previousRoll))); + newRotation = glm::quat(glm::vec3(DEGTORAD(_pitchAverage.getAverage()), DEGTORAD(_yawAverage.getAverage()), DEGTORAD(-_rollAverage.getAverage()))); _headAngularVelocity = glm::vec3(0,0,0); } diff --git a/interface/src/devices/CaraFaceTracker.h b/interface/src/devices/CaraFaceTracker.h index f51fed0f1b..41dd205d22 100644 --- a/interface/src/devices/CaraFaceTracker.h +++ b/interface/src/devices/CaraFaceTracker.h @@ -14,6 +14,7 @@ #include +#include #include "FaceTracker.h" /*! @@ -90,10 +91,10 @@ private: //head tracking glm::vec3 _headAngularVelocity; - //pose history - float _previousPitch; - float _previousYaw; - float _previousRoll; + //pose average + SimpleMovingAverage _pitchAverage; + SimpleMovingAverage _yawAverage; + SimpleMovingAverage _rollAverage; // eye gaze degrees float _eyeGazeLeftPitch; From 4aa5fb27954e0101fc1800fd9d200ec6b9915e9f Mon Sep 17 00:00:00 2001 From: Zu Date: Fri, 1 Aug 2014 16:12:17 +0800 Subject: [PATCH 28/45] fix: comments, extra white spaces --- interface/src/devices/CaraFaceTracker.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/CaraFaceTracker.cpp b/interface/src/devices/CaraFaceTracker.cpp index 8160200641..c7ae322ae8 100644 --- a/interface/src/devices/CaraFaceTracker.cpp +++ b/interface/src/devices/CaraFaceTracker.cpp @@ -374,7 +374,7 @@ void CaraFaceTracker::decodePacket(const QByteArray& buffer) { if(jsonError.error == QJsonParseError::NoError) { //do some noise filtering to the head poses - //reduce the noise first by truncating to 1 dp + //reduce the noise first by truncating to 1 dp person.pose.roll = glm::floor(person.pose.roll * 10) / 10; person.pose.pitch = glm::floor(person.pose.pitch * 10) / 10; person.pose.yaw = glm::floor(person.pose.yaw * 10) / 10; @@ -393,7 +393,6 @@ void CaraFaceTracker::decodePacket(const QByteArray& buffer) { const float YAW_STANDARD_DEV_DEG = 2.5f; _headAngularVelocity = theta / AVERAGE_CARA_FRAME_TIME * glm::vec3(r.x, r.y, r.z) / rMag; - _pitchAverage.updateAverage(person.pose.pitch); _rollAverage.updateAverage(person.pose.roll); @@ -413,14 +412,14 @@ void CaraFaceTracker::decodePacket(const QByteArray& buffer) { // > the standard deviation 2.5 deg, update the yaw smoothing average if(glm::abs(person.pose.yaw - _yawAverage.getAverage()) > YAW_STANDARD_DEV_DEG) { //qDebug() << "Yaw Diff: " << glm::abs(person.pose.yaw - _previousYaw); - _yawAverage.updateAverage(person.pose.yaw); + _yawAverage.updateAverage(person.pose.yaw); } //set the new rotation newRotation = glm::quat(glm::vec3(DEGTORAD(_pitchAverage.getAverage()), DEGTORAD(_yawAverage.getAverage()), DEGTORAD(-_rollAverage.getAverage()))); } else { - //no change in position + //no change in position, use previous averages newRotation = glm::quat(glm::vec3(DEGTORAD(_pitchAverage.getAverage()), DEGTORAD(_yawAverage.getAverage()), DEGTORAD(-_rollAverage.getAverage()))); _headAngularVelocity = glm::vec3(0,0,0); } @@ -455,4 +454,3 @@ void CaraFaceTracker::decodePacket(const QByteArray& buffer) { float CaraFaceTracker::getBlendshapeCoefficient(int index) const { return (index >= 0 && index < (int)_blendshapeCoefficients.size()) ? _blendshapeCoefficients[index] : 0.0f; } - From e1f905cb3667fafaa9e9935a7a6db333fb38639b Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 1 Aug 2014 10:23:03 -0700 Subject: [PATCH 29/45] fixed >100% loss rate bug in SequenceNumberStats --- interface/src/Audio.cpp | 4 +- interface/src/ui/OctreeStatsDialog.cpp | 14 ++--- libraries/audio/src/InboundAudioStream.cpp | 2 +- libraries/audio/src/InboundAudioStream.h | 2 +- .../networking/src/SequenceNumberStats.cpp | 55 +++++++++-------- .../networking/src/SequenceNumberStats.h | 61 ++++++++++--------- 6 files changed, 73 insertions(+), 65 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 4ed1f7aeb3..4fcab5b949 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -1424,9 +1424,9 @@ void Audio::renderAudioStreamStats(const AudioStreamStats& streamStats, int hori sprintf(stringBuffer, " Packet loss | overall: %5.2f%% (%d lost), last_30s: %5.2f%% (%d lost)", streamStats._packetStreamStats.getLostRate() * 100.0f, - streamStats._packetStreamStats._numLost, + streamStats._packetStreamStats._lost, streamStats._packetStreamWindowStats.getLostRate() * 100.0f, - streamStats._packetStreamWindowStats._numLost); + streamStats._packetStreamWindowStats._lost); verticalOffset += STATS_HEIGHT_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index afa799815f..7abc42d0e3 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -366,13 +366,13 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser QString incomingBytesString = locale.toString((uint)stats.getIncomingBytes()); QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes()); const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats(); - QString incomingOutOfOrderString = locale.toString((uint)seqStats.getNumOutOfOrder()); - QString incomingLateString = locale.toString((uint)seqStats.getNumLate()); - QString incomingUnreasonableString = locale.toString((uint)seqStats.getNumUnreasonable()); - QString incomingEarlyString = locale.toString((uint)seqStats.getNumEarly()); - QString incomingLikelyLostString = locale.toString((uint)seqStats.getNumLost()); - QString incomingRecovered = locale.toString((uint)seqStats.getNumRecovered()); - QString incomingDuplicateString = locale.toString((uint)seqStats.getNumDuplicate()); + QString incomingOutOfOrderString = locale.toString((uint)seqStats.getOutOfOrder()); + QString incomingLateString = locale.toString((uint)seqStats.getLate()); + QString incomingUnreasonableString = locale.toString((uint)seqStats.getUnreasonable()); + QString incomingEarlyString = locale.toString((uint)seqStats.getEarly()); + QString incomingLikelyLostString = locale.toString((uint)seqStats.getLost()); + QString incomingRecovered = locale.toString((uint)seqStats.getRecovered()); + QString incomingDuplicateString = locale.toString((uint)seqStats.getDuplicate()); int clockSkewInMS = node->getClockSkewUsec() / (int)USECS_PER_MSEC; QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage()); diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 6ade4b17e9..36145a35f6 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -212,7 +212,7 @@ SequenceNumberStats::ArrivalInfo InboundAudioStream::frameReceivedUpdateNetworkS // discard the first few packets we receive since they usually have gaps that aren't represensative of normal jitter const int NUM_INITIAL_PACKETS_DISCARD = 3; quint64 now = usecTimestampNow(); - if (_incomingSequenceNumberStats.getNumReceived() > NUM_INITIAL_PACKETS_DISCARD) { + if (_incomingSequenceNumberStats.getReceived() > NUM_INITIAL_PACKETS_DISCARD) { quint64 gap = now - _lastFrameReceivedTime; _interframeTimeGapStatsForStatsPacket.update(gap); diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index 06bd329fee..1a196a1c61 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -108,7 +108,7 @@ public: int getSilentFramesDropped() const { return _silentFramesDropped; } int getOverflowCount() const { return _ringBuffer.getOverflowCount(); } - int getPacketReceived() const { return _incomingSequenceNumberStats.getNumReceived(); } + int getPacketsReceived() const { return _incomingSequenceNumberStats.getReceived(); } private: void starved(); diff --git a/libraries/networking/src/SequenceNumberStats.cpp b/libraries/networking/src/SequenceNumberStats.cpp index 66d57500a5..00a3e66877 100644 --- a/libraries/networking/src/SequenceNumberStats.cpp +++ b/libraries/networking/src/SequenceNumberStats.cpp @@ -14,7 +14,8 @@ #include SequenceNumberStats::SequenceNumberStats(int statsHistoryLength) - : _lastReceived(std::numeric_limits::max()), + : _received(0), + _lastReceivedSequence(0), _missingSet(), _stats(), _lastSenderUUID(), @@ -23,8 +24,10 @@ SequenceNumberStats::SequenceNumberStats(int statsHistoryLength) } void SequenceNumberStats::reset() { + _received = 0; _missingSet.clear(); _stats = PacketStreamStats(); + _lastSenderUUID = QUuid(); _statsHistory.clear(); } @@ -36,7 +39,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui // if the sender node has changed, reset all stats if (senderUUID != _lastSenderUUID) { - if (_stats._numReceived > 0) { + if (_received > 0) { qDebug() << "sequence number stats was reset due to new sender node"; qDebug() << "previous:" << _lastSenderUUID << "current:" << senderUUID; reset(); @@ -45,13 +48,14 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui } // determine our expected sequence number... handle rollover appropriately - quint16 expected = _stats._numReceived > 0 ? _lastReceived + (quint16)1 : incoming; + quint16 expected = _received > 0 ? _lastReceivedSequence + (quint16)1 : incoming; - _stats._numReceived++; + _received++; if (incoming == expected) { // on time arrivalInfo._status = OnTime; - _lastReceived = incoming; + _lastReceivedSequence = incoming; + _stats._expectedReceived++; } else { // out of order if (wantExtraDebugging) { @@ -76,8 +80,8 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui // ignore packet if gap is unreasonable qDebug() << "ignoring unreasonable sequence number:" << incoming - << "previous:" << _lastReceived; - _stats._numUnreasonable++; + << "previous:" << _lastReceivedSequence; + _stats._unreasonable++; return arrivalInfo; } @@ -94,10 +98,11 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui qDebug() << "this packet is earlier than expected..."; qDebug() << ">>>>>>>> missing gap=" << (incomingInt - expectedInt); } - - _stats._numEarly++; - _stats._numLost += (incomingInt - expectedInt); - _lastReceived = incoming; + int skipped = incomingInt - expectedInt; + _stats._early++; + _stats._lost += skipped; + _stats._expectedReceived += (skipped + 1); + _lastReceivedSequence = incoming; // add all sequence numbers that were skipped to the missing sequence numbers list for (int missingInt = expectedInt; missingInt < incomingInt; missingInt++) { @@ -114,7 +119,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui qDebug() << "this packet is later than expected..."; } - _stats._numLate++; + _stats._late++; // do not update _lastReceived; it shouldn't become smaller @@ -125,15 +130,15 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui if (wantExtraDebugging) { qDebug() << "found it in _missingSet"; } - _stats._numLost--; - _stats._numRecovered++; + _stats._lost--; + _stats._recovered++; } else { arrivalInfo._status = Duplicate; if (wantExtraDebugging) { qDebug() << "sequence:" << incoming << "was NOT found in _missingSet and is probably a duplicate"; } - _stats._numDuplicate++; + _stats._duplicate++; } } } @@ -148,7 +153,7 @@ void SequenceNumberStats::pruneMissingSet(const bool wantExtraDebugging) { // some older sequence numbers may be from before a rollover point; this must be handled. // some sequence numbers in this list may be larger than _incomingLastSequence, indicating that they were received // before the most recent rollover. - int cutoff = (int)_lastReceived - MAX_REASONABLE_SEQUENCE_GAP; + int cutoff = (int)_lastReceivedSequence - MAX_REASONABLE_SEQUENCE_GAP; if (cutoff >= 0) { quint16 nonRolloverCutoff = (quint16)cutoff; QSet::iterator i = _missingSet.begin(); @@ -159,7 +164,7 @@ void SequenceNumberStats::pruneMissingSet(const bool wantExtraDebugging) { qDebug() << "old age cutoff:" << nonRolloverCutoff; } - if (missing > _lastReceived || missing < nonRolloverCutoff) { + if (missing > _lastReceivedSequence || missing < nonRolloverCutoff) { i = _missingSet.erase(i); if (wantExtraDebugging) { qDebug() << "pruning really old missing sequence:" << missing; @@ -178,7 +183,7 @@ void SequenceNumberStats::pruneMissingSet(const bool wantExtraDebugging) { qDebug() << "old age cutoff:" << rolloverCutoff; } - if (missing > _lastReceived && missing < rolloverCutoff) { + if (missing > _lastReceivedSequence && missing < rolloverCutoff) { i = _missingSet.erase(i); if (wantExtraDebugging) { qDebug() << "pruning really old missing sequence:" << missing; @@ -202,13 +207,13 @@ PacketStreamStats SequenceNumberStats::getStatsForHistoryWindow() const { // calculate difference between newest stats and oldest stats to get window stats PacketStreamStats windowStats; - windowStats._numReceived = newestStats->_numReceived - oldestStats->_numReceived; - windowStats._numUnreasonable = newestStats->_numUnreasonable - oldestStats->_numUnreasonable; - windowStats._numEarly = newestStats->_numEarly - oldestStats->_numEarly; - windowStats._numLate = newestStats->_numLate - oldestStats->_numLate; - windowStats._numLost = newestStats->_numLost - oldestStats->_numLost; - windowStats._numRecovered = newestStats->_numRecovered - oldestStats->_numRecovered; - windowStats._numDuplicate = newestStats->_numDuplicate - oldestStats->_numDuplicate; + windowStats._expectedReceived = newestStats->_expectedReceived - oldestStats->_expectedReceived; + windowStats._unreasonable = newestStats->_unreasonable - oldestStats->_unreasonable; + windowStats._early = newestStats->_early - oldestStats->_early; + windowStats._late = newestStats->_late - oldestStats->_late; + windowStats._lost = newestStats->_lost - oldestStats->_lost; + windowStats._recovered = newestStats->_recovered - oldestStats->_recovered; + windowStats._duplicate = newestStats->_duplicate - oldestStats->_duplicate; return windowStats; } diff --git a/libraries/networking/src/SequenceNumberStats.h b/libraries/networking/src/SequenceNumberStats.h index f4e85b6fb3..d611a494ad 100644 --- a/libraries/networking/src/SequenceNumberStats.h +++ b/libraries/networking/src/SequenceNumberStats.h @@ -21,29 +21,27 @@ const int MAX_REASONABLE_SEQUENCE_GAP = 1000; class PacketStreamStats { public: PacketStreamStats() - : _numReceived(0), - _numUnreasonable(0), - _numEarly(0), - _numLate(0), - _numLost(0), - _numRecovered(0), - _numDuplicate(0) + : _expectedReceived(0), + _unreasonable(0), + _early(0), + _late(0), + _lost(0), + _recovered(0), + _duplicate(0) {} - float getUnreasonableRate() const { return (float)_numUnreasonable / _numReceived; } - float getNumEaryRate() const { return (float)_numEarly / _numReceived; } - float getLateRate() const { return (float)_numLate / _numReceived; } - float getLostRate() const { return (float)_numLost / _numReceived; } - float getRecoveredRate() const { return (float)_numRecovered / _numReceived; } - float getDuplicateRate() const { return (float)_numDuplicate / _numReceived; } + float getUnreasonableRate() const { return (float)_unreasonable / _expectedReceived; } + float getEaryRate() const { return (float)_early / _expectedReceived; } + float getLateRate() const { return (float)_late / _expectedReceived; } + float getLostRate() const { return (float)_lost / _expectedReceived; } - quint32 _numReceived; - quint32 _numUnreasonable; - quint32 _numEarly; - quint32 _numLate; - quint32 _numLost; - quint32 _numRecovered; - quint32 _numDuplicate; + quint32 _expectedReceived; + quint32 _unreasonable; + quint32 _early; + quint32 _late; + quint32 _lost; + quint32 _recovered; + quint32 _duplicate; }; class SequenceNumberStats { @@ -70,20 +68,25 @@ public: void pruneMissingSet(const bool wantExtraDebugging = false); void pushStatsToHistory() { _statsHistory.insert(_stats); } - quint32 getNumReceived() const { return _stats._numReceived; } - quint32 getNumUnreasonable() const { return _stats._numUnreasonable; } - quint32 getNumOutOfOrder() const { return _stats._numEarly + _stats._numLate; } - quint32 getNumEarly() const { return _stats._numEarly; } - quint32 getNumLate() const { return _stats._numLate; } - quint32 getNumLost() const { return _stats._numLost; } - quint32 getNumRecovered() const { return _stats._numRecovered; } - quint32 getNumDuplicate() const { return _stats._numDuplicate; } + quint32 getReceived() const { return _received; } + + quint32 getExpectedReceived() const { return _stats._expectedReceived; } + quint32 getUnreasonable() const { return _stats._unreasonable; } + quint32 getOutOfOrder() const { return _stats._early + _stats._late; } + quint32 getEarly() const { return _stats._early; } + quint32 getLate() const { return _stats._late; } + quint32 getLost() const { return _stats._lost; } + quint32 getRecovered() const { return _stats._recovered; } + quint32 getDuplicate() const { return _stats._duplicate; } + const PacketStreamStats& getStats() const { return _stats; } PacketStreamStats getStatsForHistoryWindow() const; const QSet& getMissingSet() const { return _missingSet; } private: - quint16 _lastReceived; + int _received; + + quint16 _lastReceivedSequence; QSet _missingSet; PacketStreamStats _stats; From cdcc6ece04ef0fe9e5e3576abc27729280ebe2ef Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 1 Aug 2014 10:34:19 -0700 Subject: [PATCH 30/45] fixed compile errors caused by SequenceNumberStats changes --- interface/src/Audio.cpp | 2 +- .../src/SequenceNumberStatsTests.cpp | 96 +++++++++---------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 4fcab5b949..1391045568 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -709,7 +709,7 @@ void Audio::handleAudioInput() { delete[] inputAudioSamples; } - if (_receivedAudioStream.getPacketReceived() > 0) { + if (_receivedAudioStream.getPacketsReceived() > 0) { pushAudioToOutput(); } } diff --git a/tests/networking/src/SequenceNumberStatsTests.cpp b/tests/networking/src/SequenceNumberStatsTests.cpp index de487267e0..c4205f5fb5 100644 --- a/tests/networking/src/SequenceNumberStatsTests.cpp +++ b/tests/networking/src/SequenceNumberStatsTests.cpp @@ -38,12 +38,12 @@ void SequenceNumberStatsTests::rolloverTest() { stats.sequenceNumberReceived(seq); seq = seq + (quint16)1; - assert(stats.getNumDuplicate() == 0); - assert(stats.getNumEarly() == 0); - assert(stats.getNumLate() == 0); - assert(stats.getNumLost() == 0); - assert(stats.getNumReceived() == i + 1); - assert(stats.getNumRecovered() == 0); + assert(stats.getDuplicate() == 0); + assert(stats.getEarly() == 0); + assert(stats.getLate() == 0); + assert(stats.getLost() == 0); + assert(stats.getReceived() == i + 1); + assert(stats.getRecovered() == 0); } stats.reset(); } @@ -69,12 +69,12 @@ void SequenceNumberStatsTests::earlyLateTest() { seq = seq + (quint16)1; numSent++; - assert(stats.getNumDuplicate() == 0); - assert(stats.getNumEarly() == numEarly); - assert(stats.getNumLate() == numLate); - assert(stats.getNumLost() == numLost); - assert(stats.getNumReceived() == numSent); - assert(stats.getNumRecovered() == numRecovered); + assert(stats.getDuplicate() == 0); + assert(stats.getEarly() == numEarly); + assert(stats.getLate() == numLate); + assert(stats.getLost() == numLost); + assert(stats.getReceived() == numSent); + assert(stats.getRecovered() == numRecovered); } // skip 10 @@ -89,12 +89,12 @@ void SequenceNumberStatsTests::earlyLateTest() { seq = seq + (quint16)1; numSent++; - assert(stats.getNumDuplicate() == 0); - assert(stats.getNumEarly() == numEarly); - assert(stats.getNumLate() == numLate); - assert(stats.getNumLost() == numLost); - assert(stats.getNumReceived() == numSent); - assert(stats.getNumRecovered() == numRecovered); + assert(stats.getDuplicate() == 0); + assert(stats.getEarly() == numEarly); + assert(stats.getLate() == numLate); + assert(stats.getLost() == numLost); + assert(stats.getReceived() == numSent); + assert(stats.getRecovered() == numRecovered); } // send ones we skipped @@ -106,12 +106,12 @@ void SequenceNumberStatsTests::earlyLateTest() { numLost--; numRecovered++; - assert(stats.getNumDuplicate() == 0); - assert(stats.getNumEarly() == numEarly); - assert(stats.getNumLate() == numLate); - assert(stats.getNumLost() == numLost); - assert(stats.getNumReceived() == numSent); - assert(stats.getNumRecovered() == numRecovered); + assert(stats.getDuplicate() == 0); + assert(stats.getEarly() == numEarly); + assert(stats.getLate() == numLate); + assert(stats.getLost() == numLost); + assert(stats.getReceived() == numSent); + assert(stats.getRecovered() == numRecovered); } } stats.reset(); @@ -145,12 +145,12 @@ void SequenceNumberStatsTests::duplicateTest() { seq = seq + (quint16)1; numSent++; - assert(stats.getNumDuplicate() == numDuplicate); - assert(stats.getNumEarly() == numEarly); - assert(stats.getNumLate() == numLate); - assert(stats.getNumLost() == numLost); - assert(stats.getNumReceived() == numSent); - assert(stats.getNumRecovered() == 0); + assert(stats.getDuplicate() == numDuplicate); + assert(stats.getEarly() == numEarly); + assert(stats.getLate() == numLate); + assert(stats.getLost() == numLost); + assert(stats.getReceived() == numSent); + assert(stats.getRecovered() == 0); } // skip 10 @@ -167,12 +167,12 @@ void SequenceNumberStatsTests::duplicateTest() { seq = seq + (quint16)1; numSent++; - assert(stats.getNumDuplicate() == numDuplicate); - assert(stats.getNumEarly() == numEarly); - assert(stats.getNumLate() == numLate); - assert(stats.getNumLost() == numLost); - assert(stats.getNumReceived() == numSent); - assert(stats.getNumRecovered() == 0); + assert(stats.getDuplicate() == numDuplicate); + assert(stats.getEarly() == numEarly); + assert(stats.getLate() == numLate); + assert(stats.getLost() == numLost); + assert(stats.getReceived() == numSent); + assert(stats.getRecovered() == 0); } // send 5 duplicates from before skip @@ -183,12 +183,12 @@ void SequenceNumberStatsTests::duplicateTest() { numDuplicate++; numLate++; - assert(stats.getNumDuplicate() == numDuplicate); - assert(stats.getNumEarly() == numEarly); - assert(stats.getNumLate() == numLate); - assert(stats.getNumLost() == numLost); - assert(stats.getNumReceived() == numSent); - assert(stats.getNumRecovered() == 0); + assert(stats.getDuplicate() == numDuplicate); + assert(stats.getEarly() == numEarly); + assert(stats.getLate() == numLate); + assert(stats.getLost() == numLost); + assert(stats.getReceived() == numSent); + assert(stats.getRecovered() == 0); } // send 5 duplicates from after skip @@ -199,12 +199,12 @@ void SequenceNumberStatsTests::duplicateTest() { numDuplicate++; numLate++; - assert(stats.getNumDuplicate() == numDuplicate); - assert(stats.getNumEarly() == numEarly); - assert(stats.getNumLate() == numLate); - assert(stats.getNumLost() == numLost); - assert(stats.getNumReceived() == numSent); - assert(stats.getNumRecovered() == 0); + assert(stats.getDuplicate() == numDuplicate); + assert(stats.getEarly() == numEarly); + assert(stats.getLate() == numLate); + assert(stats.getLost() == numLost); + assert(stats.getReceived() == numSent); + assert(stats.getRecovered() == 0); } } stats.reset(); From eba07b03e29a4d2901ccdecb0044a2334359412d Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 1 Aug 2014 12:00:41 -0700 Subject: [PATCH 31/45] recursive SequenceNumberStats; untested --- .../networking/src/SequenceNumberStats.cpp | 103 +++++++++++++++++- .../networking/src/SequenceNumberStats.h | 21 +++- 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/SequenceNumberStats.cpp b/libraries/networking/src/SequenceNumberStats.cpp index 00a3e66877..a700e01f07 100644 --- a/libraries/networking/src/SequenceNumberStats.cpp +++ b/libraries/networking/src/SequenceNumberStats.cpp @@ -13,22 +13,67 @@ #include -SequenceNumberStats::SequenceNumberStats(int statsHistoryLength) +SequenceNumberStats::SequenceNumberStats(int statsHistoryLength, int maxRecursion) : _received(0), _lastReceivedSequence(0), _missingSet(), _stats(), _lastSenderUUID(), - _statsHistory(statsHistoryLength) + _statsHistory(statsHistoryLength), + + _unreasonableTracker(NULL), + _maxRecursion(maxRecursion) { } +SequenceNumberStats::SequenceNumberStats(const SequenceNumberStats& other) + : _received(other._received), + _lastReceivedSequence(other._lastReceivedSequence), + _missingSet(other._missingSet), + _stats(other._stats), + _lastSenderUUID(other._lastSenderUUID), + _statsHistory(other._statsHistory), + _unreasonableTracker(NULL), + _maxRecursion(other._maxRecursion) +{ + if (other._unreasonableTracker) { + _unreasonableTracker = new SequenceNumberStats(*other._unreasonableTracker); + } +} + +SequenceNumberStats& SequenceNumberStats::operator=(const SequenceNumberStats& rhs) { + _received = rhs._received; + _lastReceivedSequence = rhs._lastReceivedSequence; + _missingSet = rhs._missingSet; + _stats = rhs._stats; + _lastSenderUUID = rhs._lastSenderUUID; + _statsHistory = rhs._statsHistory; + _maxRecursion = rhs._maxRecursion; + + if (rhs._unreasonableTracker) { + _unreasonableTracker = new SequenceNumberStats(*rhs._unreasonableTracker); + } else { + _unreasonableTracker = NULL; + } + return *this; +} + +SequenceNumberStats::~SequenceNumberStats() { + if (_unreasonableTracker) { + delete _unreasonableTracker; + } +} + void SequenceNumberStats::reset() { _received = 0; _missingSet.clear(); _stats = PacketStreamStats(); _lastSenderUUID = QUuid(); _statsHistory.clear(); + + if (_unreasonableTracker) { + delete _unreasonableTracker; + } } static const int UINT16_RANGE = std::numeric_limits::max() + 1; @@ -78,10 +123,64 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui } else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) { arrivalInfo._status = Unreasonable; + /* // ignore packet if gap is unreasonable qDebug() << "ignoring unreasonable sequence number:" << incoming << "previous:" << _lastReceivedSequence; _stats._unreasonable++; + */ + + // do not create a child tracker for unreasonable seq nums if this instance is the last one in the chain. + // otherwise, create one if we don't have one. + + if (!_unreasonableTracker && _maxRecursion > 0) { + _unreasonableTracker = new SequenceNumberStats(0, _maxRecursion - 1); + } + + + if (_unreasonableTracker) { + + // track this unreasonable seq number with our _unreasonableTracker. + ArrivalInfo unreasonableTrackerArrivalInfo = _unreasonableTracker->sequenceNumberReceived(incoming); + + + const int UNREASONABLE_TRACKER_RECEIVED_THRESHOLD = 10; + const float UNRUNREASONABLE_TRACKER_UNREASONABLE_RATE_THRESHOLD = 0.1f; + const float UNREASONABLE_TRACKER_OUT_OF_ORDER_RATE_THRESHOLD = 0.1f; + + // when our _unreasonableTracker has received enough seq nums and doesn't have an _unreasonableTracker of its own, + // we'll either inherit its state only if we think its stream is plausible. it will then be deleted. + // (if it has an _unreasonableTracker of its own, its _unreasonableTracker may be detecting a plausible stream + // while its parent does not, so we should let it accrue seq nums and decide plausibility first) + + if (!_unreasonableTracker->hasUnreasonableTracker() && + _unreasonableTracker->_received >= UNREASONABLE_TRACKER_RECEIVED_THRESHOLD) { + + if (_unreasonableTracker->getUnreasonableRate() < UNRUNREASONABLE_TRACKER_UNREASONABLE_RATE_THRESHOLD && + _unreasonableTracker->getStats().getOutOfOrderRate() < UNREASONABLE_TRACKER_OUT_OF_ORDER_RATE_THRESHOLD) { + + // the _unreasonableTracker has detected a plausible stream of seq numbers; + // copy its state to this tracker. + + _received = _unreasonableTracker->_received; + _lastReceivedSequence = _unreasonableTracker->_lastReceivedSequence; + _missingSet = _unreasonableTracker->_missingSet; + _stats = _unreasonableTracker->_stats; + + // don't copy _lastSenderUUID; _unreasonableTracker always has null UUID for that member. + // ours should be up-to-date. + + // don't copy _statsHistory; _unreasonableTracker keeps a history of length 0. + // simply clear ours. + _statsHistory.clear(); + + arrivalInfo = unreasonableTrackerArrivalInfo; + + } + // remove our _unreasonableTracker + delete _unreasonableTracker; + } + } return arrivalInfo; } diff --git a/libraries/networking/src/SequenceNumberStats.h b/libraries/networking/src/SequenceNumberStats.h index d611a494ad..1f740d7af8 100644 --- a/libraries/networking/src/SequenceNumberStats.h +++ b/libraries/networking/src/SequenceNumberStats.h @@ -18,6 +18,10 @@ const int MAX_REASONABLE_SEQUENCE_GAP = 1000; + +const int DEFAULT_MAX_RECURSION = 5; + + class PacketStreamStats { public: PacketStreamStats() @@ -30,7 +34,7 @@ public: _duplicate(0) {} - float getUnreasonableRate() const { return (float)_unreasonable / _expectedReceived; } + float getOutOfOrderRate() const { return (float)(_early + _late) / _expectedReceived; } float getEaryRate() const { return (float)_early / _expectedReceived; } float getLateRate() const { return (float)_late / _expectedReceived; } float getLostRate() const { return (float)_lost / _expectedReceived; } @@ -61,14 +65,19 @@ public: }; - SequenceNumberStats(int statsHistoryLength = 0); + SequenceNumberStats(int statsHistoryLength = 0, int maxRecursion = DEFAULT_MAX_RECURSION); + SequenceNumberStats(const SequenceNumberStats& other); + SequenceNumberStats& operator=(const SequenceNumberStats& rhs); + ~SequenceNumberStats(); +public: void reset(); ArrivalInfo sequenceNumberReceived(quint16 incoming, QUuid senderUUID = QUuid(), const bool wantExtraDebugging = false); void pruneMissingSet(const bool wantExtraDebugging = false); void pushStatsToHistory() { _statsHistory.insert(_stats); } quint32 getReceived() const { return _received; } + float getUnreasonableRate() const { return _stats._unreasonable / _received; } quint32 getExpectedReceived() const { return _stats._expectedReceived; } quint32 getUnreasonable() const { return _stats._unreasonable; } @@ -94,6 +103,14 @@ private: QUuid _lastSenderUUID; RingBufferHistory _statsHistory; + + + // to deal with the incoming seq nums going out of sync with this tracker, we'll create another instance + // of this class when we encounter an unreasonable + SequenceNumberStats* _unreasonableTracker; + int _maxRecursion; + + bool hasUnreasonableTracker() const { return _unreasonableTracker != NULL; } }; #endif // hifi_SequenceNumberStats_h From 355cf074433c7fbd406091780f8e149b57e4af68 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 1 Aug 2014 13:03:10 -0700 Subject: [PATCH 32/45] Fix style issue --- libraries/models/src/ModelTree.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index df94213213..53ee60d696 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -397,7 +397,7 @@ void ModelTree::findModelsInCube(const AACube& cube, QVector& foundM } bool ModelTree::findInCubeOperation(OctreeElement* element, void* extraData) { - FindModelsInCubeArgs* args = static_cast< FindModelsInCubeArgs*>(extraData); + FindModelsInCubeArgs* args = static_cast(extraData); const AACube& elementCube = element->getAACube(); if (elementCube.touches(args->_cube)) { ModelTreeElement* modelTreeElement = static_cast(element); @@ -408,7 +408,7 @@ bool ModelTree::findInCubeOperation(OctreeElement* element, void* extraData) { } bool ModelTree::findInCubeForUpdateOperation(OctreeElement* element, void* extraData) { - FindModelsInCubeArgs* args = static_cast< FindModelsInCubeArgs*>(extraData); + FindModelsInCubeArgs* args = static_cast(extraData); const AACube& elementCube = element->getAACube(); if (elementCube.touches(args->_cube)) { ModelTreeElement* modelTreeElement = static_cast(element); From b98ee351a5817a0ac064be36b2a0031fc5c960bc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 1 Aug 2014 13:04:03 -0700 Subject: [PATCH 33/45] Remove unused ModelTree::getModelAt --- libraries/models/src/ModelTree.cpp | 4 ---- libraries/models/src/ModelTree.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index 53ee60d696..b9b4faed07 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -112,10 +112,6 @@ bool FindAndUpdateModelOperator::PostRecursion(OctreeElement* element) { } -ModelTreeElement* ModelTree::getModelAt(float x, float y, float z, float s) const { - return static_cast(getOctreeElementAt(x, y, z, s)); -} - // TODO: improve this to not use multiple recursions void ModelTree::storeModel(const ModelItem& model, const SharedNodePointer& senderNode) { // First, look for the existing model in the tree.. diff --git a/libraries/models/src/ModelTree.h b/libraries/models/src/ModelTree.h index 3a1159c9cf..e61cc6057e 100644 --- a/libraries/models/src/ModelTree.h +++ b/libraries/models/src/ModelTree.h @@ -36,8 +36,6 @@ public: /// Type safe version of getRoot() ModelTreeElement* getRoot() { return static_cast(_rootElement); } - ModelTreeElement* getModelAt(float x, float y, float z, float s) const; - // These methods will allow the OctreeServer to send your tree inbound edit packets of your // own definition. Implement these to allow your octree based server to support editing virtual bool getWantSVOfileVersions() const { return true; } From d01d605b4029fdf70529d07d952386e84b3a032b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 1 Aug 2014 13:52:00 -0700 Subject: [PATCH 34/45] Add ModelItemProperties::copyFromNewModelItem --- interface/src/Application.cpp | 2 +- libraries/models/src/ModelItem.cpp | 32 ++++++++++++++++++++++++++++++ libraries/models/src/ModelItem.h | 1 + libraries/models/src/ModelTree.cpp | 2 +- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 540cb47a79..490e5441d2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1517,7 +1517,7 @@ bool Application::exportModels(const QString& filename, float x, float y, float ModelItemProperties properties; ModelItemID id = models.at(i)->getModelItemID(); id.isKnownID = false; - properties.copyFromModelItem(*models.at(i)); + properties.copyFromNewModelItem(*models.at(i)); properties.setPosition(properties.getPosition() - root); exportTree.addModel(id, properties); } diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index afa64e3bc6..2a3a88fb65 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -1142,6 +1142,38 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _id = modelItem.getID(); _idSet = true; + _positionChanged = false; + _colorChanged = false; + _radiusChanged = false; + + _shouldDieChanged = false; + _modelURLChanged = false; + _modelRotationChanged = false; + _animationURLChanged = false; + _animationIsPlayingChanged = false; + _animationFrameIndexChanged = false; + _animationFPSChanged = false; + _glowLevelChanged = false; + _defaultSettings = false; +} + +void ModelItemProperties::copyFromNewModelItem(const ModelItem& modelItem) { + _position = modelItem.getPosition() * (float) TREE_SCALE; + _color = modelItem.getXColor(); + _radius = modelItem.getRadius() * (float) TREE_SCALE; + _shouldDie = modelItem.getShouldDie(); + _modelURL = modelItem.getModelURL(); + _modelRotation = modelItem.getModelRotation(); + _animationURL = modelItem.getAnimationURL(); + _animationIsPlaying = modelItem.getAnimationIsPlaying(); + _animationFrameIndex = modelItem.getAnimationFrameIndex(); + _animationFPS = modelItem.getAnimationFPS(); + _glowLevel = modelItem.getGlowLevel(); + _sittingPoints = modelItem.getSittingPoints(); + + _id = modelItem.getID(); + _idSet = true; + _positionChanged = true; _colorChanged = true; _radiusChanged = true; diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 43aaca48a0..3e3c46a12d 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -74,6 +74,7 @@ public: void copyToModelItem(ModelItem& modelItem) const; void copyFromModelItem(const ModelItem& modelItem); + void copyFromNewModelItem(const ModelItem& modelItem); const glm::vec3& getPosition() const { return _position; } xColor getColor() const { return _color; } diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index b9b4faed07..206e67078c 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -221,7 +221,7 @@ bool ModelTree::sendModelsOperation(OctreeElement* element, void* extraData) { uint32_t creatorTokenID = ModelItem::getNextCreatorTokenID(); ModelItemID id(NEW_MODEL, creatorTokenID, false); ModelItemProperties properties; - properties.copyFromModelItem(modelList.at(i)); + properties.copyFromNewModelItem(modelList.at(i)); properties.setPosition(properties.getPosition() + args->root); args->packetSender->queueModelEditMessage(PacketTypeModelAddOrEdit, id, properties); } From 34ca5ded72c98f4c572f40ed99178300e2bf5c74 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 1 Aug 2014 15:09:11 -0700 Subject: [PATCH 35/45] add reporting interval --- tests/jitter/src/main.cpp | 66 ++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/tests/jitter/src/main.cpp b/tests/jitter/src/main.cpp index f93e91e5cf..ef63e3b2e1 100644 --- a/tests/jitter/src/main.cpp +++ b/tests/jitter/src/main.cpp @@ -18,12 +18,14 @@ #include // for MovingMinMaxAvg #include // for usecTimestampNow -void runSend(const char* addressOption, int port, int gap, int size); -void runReceive(const char* addressOption, int port, int gap, int size); +const quint64 MSEC_TO_USEC = 1000; + +void runSend(const char* addressOption, int port, int gap, int size, int report); +void runReceive(const char* addressOption, int port, int gap, int size, int report); int main(int argc, const char * argv[]) { - if (argc != 6) { - printf("usage: jitter-tests <--send|--receive>
\n"); + if (argc != 7) { + printf("usage: jitter-tests <--send|--receive>
\n"); exit(1); } const char* typeOption = argv[1]; @@ -31,9 +33,11 @@ int main(int argc, const char * argv[]) { const char* portOption = argv[3]; const char* gapOption = argv[4]; const char* sizeOption = argv[5]; + const char* reportOption = argv[6]; int port = atoi(portOption); int gap = atoi(gapOption); int size = atoi(sizeOption); + int report = atoi(reportOption); std::cout << "type:" << typeOption << "\n"; std::cout << "address:" << addressOption << "\n"; @@ -42,14 +46,14 @@ int main(int argc, const char * argv[]) { std::cout << "size:" << size << "\n"; if (strcmp(typeOption, "--send") == 0) { - runSend(addressOption, port, gap, size); + runSend(addressOption, port, gap, size, report); } else if (strcmp(typeOption, "--receive") == 0) { - runReceive(addressOption, port, gap, size); + runReceive(addressOption, port, gap, size, report); } exit(1); } -void runSend(const char* addressOption, int port, int gap, int size) { +void runSend(const char* addressOption, int port, int gap, int size, int report) { std::cout << "runSend...\n"; int sockfd; @@ -69,6 +73,7 @@ void runSend(const char* addressOption, int port, int gap, int size) { MovingMinMaxAvg timeGaps(1, SAMPLES_FOR_30_SECONDS); // stats quint64 last = usecTimestampNow(); + quint64 lastReport = 0; while (true) { @@ -81,21 +86,25 @@ void runSend(const char* addressOption, int port, int gap, int size) { int gapDifferece = actualGap - gap; timeGaps.update(gapDifferece); - std::cout << "packet sent gap: " << actualGap << " " - << "gapDifference: " << gapDifferece << " " - << "min: " << timeGaps.getMin() << " " - << "max: " << timeGaps.getMax() << " " - << "avg: " << timeGaps.getAverage() << " " - << "min last 30: " << timeGaps.getWindowMin() << " " - << "max last 30: " << timeGaps.getWindowMax() << " " - << "avg last 30: " << timeGaps.getWindowAverage() << " " - << "\n"; last = now; + + if (now - lastReport >= (report * MSEC_TO_USEC)) { + std::cout << "packet sent gap: " << actualGap << " " + << "gapDifference: " << gapDifferece << " " + << "min: " << timeGaps.getMin() << " " + << "max: " << timeGaps.getMax() << " " + << "avg: " << timeGaps.getAverage() << " " + << "min last 30: " << timeGaps.getWindowMin() << " " + << "max last 30: " << timeGaps.getWindowMax() << " " + << "avg last 30: " << timeGaps.getWindowAverage() << " " + << "\n"; + lastReport = now; + } } } } -void runReceive(const char* addressOption, int port, int gap, int size) { +void runReceive(const char* addressOption, int port, int gap, int size, int report) { std::cout << "runReceive...\n"; @@ -121,6 +130,7 @@ void runReceive(const char* addressOption, int port, int gap, int size) { } quint64 last = 0; // first case + quint64 lastReport = 0; while (true) { n = recvfrom(sockfd, inputBuffer, size, 0, NULL, NULL); // we don't care about where it came from @@ -133,16 +143,20 @@ void runReceive(const char* addressOption, int port, int gap, int size) { int actualGap = now - last; int gapDifferece = actualGap - gap; timeGaps.update(gapDifferece); - std::cout << "packet received gap:" << actualGap << " " - << "gapDifference: " << gapDifferece << " " - << "min: " << timeGaps.getMin() << " " - << "max: " << timeGaps.getMax() << " " - << "avg: " << timeGaps.getAverage() << " " - << "min last 30: " << timeGaps.getWindowMin() << " " - << "max last 30: " << timeGaps.getWindowMax() << " " - << "avg last 30: " << timeGaps.getWindowAverage() << " " - << "\n"; last = now; + + if (now - lastReport >= (report * MSEC_TO_USEC)) { + std::cout << "packet received gap:" << actualGap << " " + << "gapDifference: " << gapDifferece << " " + << "min: " << timeGaps.getMin() << " " + << "max: " << timeGaps.getMax() << " " + << "avg: " << timeGaps.getAverage() << " " + << "min last 30: " << timeGaps.getWindowMin() << " " + << "max last 30: " << timeGaps.getWindowMax() << " " + << "avg last 30: " << timeGaps.getWindowAverage() << " " + << "\n"; + lastReport = now; + } } } } From ce46e8b812e750baf090464f1aa576f80fe5af34 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 1 Aug 2014 15:14:43 -0700 Subject: [PATCH 36/45] tweak format of report --- tests/jitter/src/main.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/jitter/src/main.cpp b/tests/jitter/src/main.cpp index ef63e3b2e1..bd6923330c 100644 --- a/tests/jitter/src/main.cpp +++ b/tests/jitter/src/main.cpp @@ -89,8 +89,7 @@ void runSend(const char* addressOption, int port, int gap, int size, int report) last = now; if (now - lastReport >= (report * MSEC_TO_USEC)) { - std::cout << "packet sent gap: " << actualGap << " " - << "gapDifference: " << gapDifferece << " " + std::cout << "SEND gap Difference From Expected " << "min: " << timeGaps.getMin() << " " << "max: " << timeGaps.getMax() << " " << "avg: " << timeGaps.getAverage() << " " @@ -146,8 +145,7 @@ void runReceive(const char* addressOption, int port, int gap, int size, int repo last = now; if (now - lastReport >= (report * MSEC_TO_USEC)) { - std::cout << "packet received gap:" << actualGap << " " - << "gapDifference: " << gapDifferece << " " + std::cout << "RECEIVE gap Difference From Expected " << "min: " << timeGaps.getMin() << " " << "max: " << timeGaps.getMax() << " " << "avg: " << timeGaps.getAverage() << " " From cb02ac16b6cf278929bf0aa4407e5f5f67cf1331 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 1 Aug 2014 15:23:52 -0700 Subject: [PATCH 37/45] tweak format of report --- tests/jitter/src/main.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/tests/jitter/src/main.cpp b/tests/jitter/src/main.cpp index bd6923330c..292b01e4a5 100644 --- a/tests/jitter/src/main.cpp +++ b/tests/jitter/src/main.cpp @@ -70,6 +70,9 @@ void runSend(const char* addressOption, int port, int gap, int size, int report) servaddr.sin_port=htons(port); const int SAMPLES_FOR_30_SECONDS = 30 * 1000000 / gap; + + std::cout << "SAMPLES_FOR_30_SECONDS:" << SAMPLES_FOR_30_SECONDS << "\n"; + MovingMinMaxAvg timeGaps(1, SAMPLES_FOR_30_SECONDS); // stats quint64 last = usecTimestampNow(); @@ -90,12 +93,12 @@ void runSend(const char* addressOption, int port, int gap, int size, int report) if (now - lastReport >= (report * MSEC_TO_USEC)) { std::cout << "SEND gap Difference From Expected " - << "min: " << timeGaps.getMin() << " " - << "max: " << timeGaps.getMax() << " " - << "avg: " << timeGaps.getAverage() << " " - << "min last 30: " << timeGaps.getWindowMin() << " " - << "max last 30: " << timeGaps.getWindowMax() << " " - << "avg last 30: " << timeGaps.getWindowAverage() << " " + << "min: " << timeGaps.getMin() << " usecs " + << "max: " << timeGaps.getMax() << " usecs " + << "avg: " << timeGaps.getAverage() << " usecs " + << "min last 30: " << timeGaps.getWindowMin() << " usecs " + << "max last 30: " << timeGaps.getWindowMax() << " usecs " + << "avg last 30: " << timeGaps.getWindowAverage() << " usecs " << "\n"; lastReport = now; } @@ -121,6 +124,9 @@ void runReceive(const char* addressOption, int port, int gap, int size, int repo myaddr.sin_port=htons(port); const int SAMPLES_FOR_30_SECONDS = 30 * 1000000 / gap; + + std::cout << "SAMPLES_FOR_30_SECONDS:" << SAMPLES_FOR_30_SECONDS << "\n"; + MovingMinMaxAvg timeGaps(1, SAMPLES_FOR_30_SECONDS); // stats if (bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { @@ -146,12 +152,12 @@ void runReceive(const char* addressOption, int port, int gap, int size, int repo if (now - lastReport >= (report * MSEC_TO_USEC)) { std::cout << "RECEIVE gap Difference From Expected " - << "min: " << timeGaps.getMin() << " " - << "max: " << timeGaps.getMax() << " " - << "avg: " << timeGaps.getAverage() << " " - << "min last 30: " << timeGaps.getWindowMin() << " " - << "max last 30: " << timeGaps.getWindowMax() << " " - << "avg last 30: " << timeGaps.getWindowAverage() << " " + << "min: " << timeGaps.getMin() << " usecs " + << "max: " << timeGaps.getMax() << " usecs " + << "avg: " << timeGaps.getAverage() << " usecs " + << "min last 30: " << timeGaps.getWindowMin() << " usecs " + << "max last 30: " << timeGaps.getWindowMax() << " usecs " + << "avg last 30: " << timeGaps.getWindowAverage() << " usecs " << "\n"; lastReport = now; } From 66d45d354419acd16f1dd45e5a358552191a01e1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 1 Aug 2014 15:25:03 -0700 Subject: [PATCH 38/45] tweak format of report --- tests/jitter/src/main.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/jitter/src/main.cpp b/tests/jitter/src/main.cpp index 292b01e4a5..aefed73ccf 100644 --- a/tests/jitter/src/main.cpp +++ b/tests/jitter/src/main.cpp @@ -93,11 +93,11 @@ void runSend(const char* addressOption, int port, int gap, int size, int report) if (now - lastReport >= (report * MSEC_TO_USEC)) { std::cout << "SEND gap Difference From Expected " - << "min: " << timeGaps.getMin() << " usecs " - << "max: " << timeGaps.getMax() << " usecs " - << "avg: " << timeGaps.getAverage() << " usecs " - << "min last 30: " << timeGaps.getWindowMin() << " usecs " - << "max last 30: " << timeGaps.getWindowMax() << " usecs " + << "min: " << timeGaps.getMin() << " usecs, " + << "max: " << timeGaps.getMax() << " usecs, " + << "avg: " << timeGaps.getAverage() << " usecs, " + << "min last 30: " << timeGaps.getWindowMin() << " usecs, " + << "max last 30: " << timeGaps.getWindowMax() << " usecs, " << "avg last 30: " << timeGaps.getWindowAverage() << " usecs " << "\n"; lastReport = now; @@ -152,11 +152,11 @@ void runReceive(const char* addressOption, int port, int gap, int size, int repo if (now - lastReport >= (report * MSEC_TO_USEC)) { std::cout << "RECEIVE gap Difference From Expected " - << "min: " << timeGaps.getMin() << " usecs " - << "max: " << timeGaps.getMax() << " usecs " - << "avg: " << timeGaps.getAverage() << " usecs " - << "min last 30: " << timeGaps.getWindowMin() << " usecs " - << "max last 30: " << timeGaps.getWindowMax() << " usecs " + << "min: " << timeGaps.getMin() << " usecs, " + << "max: " << timeGaps.getMax() << " usecs, " + << "avg: " << timeGaps.getAverage() << " usecs, " + << "min last 30: " << timeGaps.getWindowMin() << " usecs, " + << "max last 30: " << timeGaps.getWindowMax() << " usecs, " << "avg last 30: " << timeGaps.getWindowAverage() << " usecs " << "\n"; lastReport = now; From f9ec7b6c18e9444c5d304d91d7e4282ca62dd0a5 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 1 Aug 2014 16:13:52 -0700 Subject: [PATCH 39/45] redesiged SequenceNumberStats child instance handling --- .../networking/src/SequenceNumberStats.cpp | 128 ++++++++++-------- .../networking/src/SequenceNumberStats.h | 11 +- .../src/SequenceNumberStatsTests.cpp | 38 +++++- .../networking/src/SequenceNumberStatsTests.h | 1 + 4 files changed, 112 insertions(+), 66 deletions(-) diff --git a/libraries/networking/src/SequenceNumberStats.cpp b/libraries/networking/src/SequenceNumberStats.cpp index a700e01f07..7e9022934b 100644 --- a/libraries/networking/src/SequenceNumberStats.cpp +++ b/libraries/networking/src/SequenceNumberStats.cpp @@ -13,16 +13,16 @@ #include -SequenceNumberStats::SequenceNumberStats(int statsHistoryLength, int maxRecursion) +SequenceNumberStats::SequenceNumberStats(int statsHistoryLength, bool canDetectOutOfSync) : _received(0), _lastReceivedSequence(0), _missingSet(), _stats(), _lastSenderUUID(), _statsHistory(statsHistoryLength), - - _unreasonableTracker(NULL), - _maxRecursion(maxRecursion) + _canHaveChild(canDetectOutOfSync), + _childInstance(NULL), + _consecutiveReasonable(0) { } @@ -33,11 +33,12 @@ SequenceNumberStats::SequenceNumberStats(const SequenceNumberStats& other) _stats(other._stats), _lastSenderUUID(other._lastSenderUUID), _statsHistory(other._statsHistory), - _unreasonableTracker(NULL), - _maxRecursion(other._maxRecursion) + _childInstance(NULL), + _canHaveChild(other._canHaveChild), + _consecutiveReasonable(other._consecutiveReasonable) { - if (other._unreasonableTracker) { - _unreasonableTracker = new SequenceNumberStats(*other._unreasonableTracker); + if (other._childInstance) { + _childInstance = new SequenceNumberStats(*other._childInstance); } } @@ -48,19 +49,20 @@ SequenceNumberStats& SequenceNumberStats::operator=(const SequenceNumberStats& r _stats = rhs._stats; _lastSenderUUID = rhs._lastSenderUUID; _statsHistory = rhs._statsHistory; - _maxRecursion = rhs._maxRecursion; + _canHaveChild = rhs._canHaveChild; + _consecutiveReasonable = rhs._consecutiveReasonable; - if (rhs._unreasonableTracker) { - _unreasonableTracker = new SequenceNumberStats(*rhs._unreasonableTracker); + if (rhs._childInstance) { + _childInstance = new SequenceNumberStats(*rhs._childInstance); } else { - _unreasonableTracker = NULL; + _childInstance = NULL; } return *this; } SequenceNumberStats::~SequenceNumberStats() { - if (_unreasonableTracker) { - delete _unreasonableTracker; + if (_childInstance) { + delete _childInstance; } } @@ -71,8 +73,9 @@ void SequenceNumberStats::reset() { _lastSenderUUID = QUuid(); _statsHistory.clear(); - if (_unreasonableTracker) { - delete _unreasonableTracker; + if (_childInstance) { + delete _childInstance; + _childInstance = NULL; } } @@ -123,68 +126,78 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui } else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) { arrivalInfo._status = Unreasonable; - /* - // ignore packet if gap is unreasonable - qDebug() << "ignoring unreasonable sequence number:" << incoming - << "previous:" << _lastReceivedSequence; + qDebug() << "unreasonable sequence number:" << incoming << "previous:" << _lastReceivedSequence; + _stats._unreasonable++; - */ + _consecutiveReasonable = 0; - // do not create a child tracker for unreasonable seq nums if this instance is the last one in the chain. - // otherwise, create one if we don't have one. + + // if _canHaveChild, create a child instance of SequenceNumberStats to track this unreasonable seq num and ones in the future. + // if the child instance detects a valid stream of seq nums up to some length, the seq nums sender probably + // fell out of sync with us. - if (!_unreasonableTracker && _maxRecursion > 0) { - _unreasonableTracker = new SequenceNumberStats(0, _maxRecursion - 1); - } + if (_canHaveChild) { + + if (!_childInstance) { + _childInstance = new SequenceNumberStats(0, false); + } - if (_unreasonableTracker) { + ArrivalInfo unreasonableTrackerArrivalInfo = _childInstance->sequenceNumberReceived(incoming); - // track this unreasonable seq number with our _unreasonableTracker. - ArrivalInfo unreasonableTrackerArrivalInfo = _unreasonableTracker->sequenceNumberReceived(incoming); - - const int UNREASONABLE_TRACKER_RECEIVED_THRESHOLD = 10; - const float UNRUNREASONABLE_TRACKER_UNREASONABLE_RATE_THRESHOLD = 0.1f; - const float UNREASONABLE_TRACKER_OUT_OF_ORDER_RATE_THRESHOLD = 0.1f; - - // when our _unreasonableTracker has received enough seq nums and doesn't have an _unreasonableTracker of its own, - // we'll either inherit its state only if we think its stream is plausible. it will then be deleted. - // (if it has an _unreasonableTracker of its own, its _unreasonableTracker may be detecting a plausible stream - // while its parent does not, so we should let it accrue seq nums and decide plausibility first) + // the child instance will be used to detect some threshold number seq nums in a row that are perfectly + // in order. - if (!_unreasonableTracker->hasUnreasonableTracker() && - _unreasonableTracker->_received >= UNREASONABLE_TRACKER_RECEIVED_THRESHOLD) { + const int UNREASONABLE_TRACKER_RECEIVED_THRESHOLD = 8; - if (_unreasonableTracker->getUnreasonableRate() < UNRUNREASONABLE_TRACKER_UNREASONABLE_RATE_THRESHOLD && - _unreasonableTracker->getStats().getOutOfOrderRate() < UNREASONABLE_TRACKER_OUT_OF_ORDER_RATE_THRESHOLD) { + if (unreasonableTrackerArrivalInfo._status != OnTime) { + _childInstance->reset(); - // the _unreasonableTracker has detected a plausible stream of seq numbers; - // copy its state to this tracker. + } else if (_childInstance->getReceived() >= UNREASONABLE_TRACKER_RECEIVED_THRESHOLD) { - _received = _unreasonableTracker->_received; - _lastReceivedSequence = _unreasonableTracker->_lastReceivedSequence; - _missingSet = _unreasonableTracker->_missingSet; - _stats = _unreasonableTracker->_stats; + // the child instance has detected a threshold number of consecutive seq nums. + // copy its state to this instance. - // don't copy _lastSenderUUID; _unreasonableTracker always has null UUID for that member. - // ours should be up-to-date. + _received = _childInstance->_received; + _lastReceivedSequence = _childInstance->_lastReceivedSequence; + _missingSet = _childInstance->_missingSet; + _stats = _childInstance->_stats; - // don't copy _statsHistory; _unreasonableTracker keeps a history of length 0. - // simply clear ours. - _statsHistory.clear(); + // don't copy _lastSenderUUID; _unreasonableTracker always has null UUID for that member. + // ours should be up-to-date. - arrivalInfo = unreasonableTrackerArrivalInfo; + // don't copy _statsHistory; _unreasonableTracker keeps a history of length 0. + // simply clear ours. + _statsHistory.clear(); - } - // remove our _unreasonableTracker - delete _unreasonableTracker; + arrivalInfo = unreasonableTrackerArrivalInfo; + + // delete child instance; + delete _childInstance; + _childInstance = NULL; } } return arrivalInfo; } + _consecutiveReasonable++; + + // if we got a reasonable seq num but have a child instance tracking unreasonable seq nums, + // reset it. if many consecutive reasonable seq nums have occurred (implying the unreasonable seq num + // that caused the creation of the child instance was just a fluke), delete our child instance. + if (_childInstance) { + const int CONSECUTIVE_REASONABLE_CHILD_DELETE_THRESHOLD = 4; + if (_consecutiveReasonable >= CONSECUTIVE_REASONABLE_CHILD_DELETE_THRESHOLD) { + _childInstance->reset(); + } else { + delete _childInstance; + _childInstance = NULL; + } + } + + // now that rollover has been corrected for (if it occurred), incomingInt and expectedInt can be // compared to each other directly, though one of them might be negative @@ -241,6 +254,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui } } } + return arrivalInfo; } diff --git a/libraries/networking/src/SequenceNumberStats.h b/libraries/networking/src/SequenceNumberStats.h index 1f740d7af8..71092d7409 100644 --- a/libraries/networking/src/SequenceNumberStats.h +++ b/libraries/networking/src/SequenceNumberStats.h @@ -19,9 +19,6 @@ const int MAX_REASONABLE_SEQUENCE_GAP = 1000; -const int DEFAULT_MAX_RECURSION = 5; - - class PacketStreamStats { public: PacketStreamStats() @@ -65,7 +62,7 @@ public: }; - SequenceNumberStats(int statsHistoryLength = 0, int maxRecursion = DEFAULT_MAX_RECURSION); + SequenceNumberStats(int statsHistoryLength = 0, bool canDetectOutOfSync = true); SequenceNumberStats(const SequenceNumberStats& other); SequenceNumberStats& operator=(const SequenceNumberStats& rhs); ~SequenceNumberStats(); @@ -107,10 +104,10 @@ private: // to deal with the incoming seq nums going out of sync with this tracker, we'll create another instance // of this class when we encounter an unreasonable - SequenceNumberStats* _unreasonableTracker; - int _maxRecursion; + bool _canHaveChild; + SequenceNumberStats* _childInstance; - bool hasUnreasonableTracker() const { return _unreasonableTracker != NULL; } + int _consecutiveReasonable; }; #endif // hifi_SequenceNumberStats_h diff --git a/tests/networking/src/SequenceNumberStatsTests.cpp b/tests/networking/src/SequenceNumberStatsTests.cpp index c4205f5fb5..38aa901a6b 100644 --- a/tests/networking/src/SequenceNumberStatsTests.cpp +++ b/tests/networking/src/SequenceNumberStatsTests.cpp @@ -16,11 +16,12 @@ void SequenceNumberStatsTests::runAllTests() { - + /* rolloverTest(); earlyLateTest(); duplicateTest(); - pruneTest(); + pruneTest();*/ + recursiveTest(); } const quint32 UINT16_RANGE = std::numeric_limits::max() + 1; @@ -278,3 +279,36 @@ void SequenceNumberStatsTests::pruneTest() { numLost = 0; } } + +void SequenceNumberStatsTests::recursiveTest() { + + SequenceNumberStats stats(0); + + quint16 sequence; + + sequence = 89; + stats.sequenceNumberReceived(sequence); + + assert(stats.getUnreasonable() == 0); + + sequence = 2990; + for (int i = 0; i < 10; i++) { + stats.sequenceNumberReceived(sequence); + sequence += (quint16)1; + } + + assert(stats.getUnreasonable() == 0); + + + sequence = 0; + for (int R = 0; R < 7; R++) { + stats.sequenceNumberReceived(sequence); + sequence += (quint16)2000; + } + + for (int i = 0; i < 10; i++) { + stats.sequenceNumberReceived(sequence); + sequence += (quint16)1; + } + assert(stats.getUnreasonable() == 0); +} diff --git a/tests/networking/src/SequenceNumberStatsTests.h b/tests/networking/src/SequenceNumberStatsTests.h index 53a0b66480..80053fd822 100644 --- a/tests/networking/src/SequenceNumberStatsTests.h +++ b/tests/networking/src/SequenceNumberStatsTests.h @@ -23,6 +23,7 @@ namespace SequenceNumberStatsTests { void earlyLateTest(); void duplicateTest(); void pruneTest(); + void recursiveTest(); }; #endif // hifi_SequenceNumberStatsTests_h From fc670d9edb19662a91f4fd9e9cd49fb4dbcc5556 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 1 Aug 2014 16:44:01 -0700 Subject: [PATCH 40/45] minor threshold change --- libraries/networking/src/SequenceNumberStats.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/SequenceNumberStats.cpp b/libraries/networking/src/SequenceNumberStats.cpp index 7e9022934b..4d6e0bb688 100644 --- a/libraries/networking/src/SequenceNumberStats.cpp +++ b/libraries/networking/src/SequenceNumberStats.cpp @@ -186,9 +186,9 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui // if we got a reasonable seq num but have a child instance tracking unreasonable seq nums, // reset it. if many consecutive reasonable seq nums have occurred (implying the unreasonable seq num - // that caused the creation of the child instance was just a fluke), delete our child instance. + // that caused the creation of the child instance was probably a fluke), delete our child instance. if (_childInstance) { - const int CONSECUTIVE_REASONABLE_CHILD_DELETE_THRESHOLD = 4; + const int CONSECUTIVE_REASONABLE_CHILD_DELETE_THRESHOLD = 8; if (_consecutiveReasonable >= CONSECUTIVE_REASONABLE_CHILD_DELETE_THRESHOLD) { _childInstance->reset(); } else { From 1d74ae8197e4ba5d2451a4640b5d22f9a8eeabb9 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 1 Aug 2014 16:49:21 -0700 Subject: [PATCH 41/45] code cleanup --- libraries/networking/src/SequenceNumberStats.cpp | 2 -- libraries/networking/src/SequenceNumberStats.h | 1 - tests/networking/src/SequenceNumberStatsTests.cpp | 3 +-- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/libraries/networking/src/SequenceNumberStats.cpp b/libraries/networking/src/SequenceNumberStats.cpp index 4d6e0bb688..12901e11cd 100644 --- a/libraries/networking/src/SequenceNumberStats.cpp +++ b/libraries/networking/src/SequenceNumberStats.cpp @@ -142,10 +142,8 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui _childInstance = new SequenceNumberStats(0, false); } - ArrivalInfo unreasonableTrackerArrivalInfo = _childInstance->sequenceNumberReceived(incoming); - // the child instance will be used to detect some threshold number seq nums in a row that are perfectly // in order. diff --git a/libraries/networking/src/SequenceNumberStats.h b/libraries/networking/src/SequenceNumberStats.h index 71092d7409..fb3ff68f70 100644 --- a/libraries/networking/src/SequenceNumberStats.h +++ b/libraries/networking/src/SequenceNumberStats.h @@ -67,7 +67,6 @@ public: SequenceNumberStats& operator=(const SequenceNumberStats& rhs); ~SequenceNumberStats(); -public: void reset(); ArrivalInfo sequenceNumberReceived(quint16 incoming, QUuid senderUUID = QUuid(), const bool wantExtraDebugging = false); void pruneMissingSet(const bool wantExtraDebugging = false); diff --git a/tests/networking/src/SequenceNumberStatsTests.cpp b/tests/networking/src/SequenceNumberStatsTests.cpp index 38aa901a6b..1df48b781b 100644 --- a/tests/networking/src/SequenceNumberStatsTests.cpp +++ b/tests/networking/src/SequenceNumberStatsTests.cpp @@ -16,11 +16,10 @@ void SequenceNumberStatsTests::runAllTests() { - /* rolloverTest(); earlyLateTest(); duplicateTest(); - pruneTest();*/ + pruneTest(); recursiveTest(); } From 99df05f770f73b7b5fc9ed6ed52b800a1fdedd81 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 1 Aug 2014 16:54:29 -0700 Subject: [PATCH 42/45] forgot member in reset() --- libraries/networking/src/SequenceNumberStats.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/SequenceNumberStats.cpp b/libraries/networking/src/SequenceNumberStats.cpp index 12901e11cd..28ea06787a 100644 --- a/libraries/networking/src/SequenceNumberStats.cpp +++ b/libraries/networking/src/SequenceNumberStats.cpp @@ -77,6 +77,7 @@ void SequenceNumberStats::reset() { delete _childInstance; _childInstance = NULL; } + _consecutiveReasonable = 0; } static const int UINT16_RANGE = std::numeric_limits::max() + 1; From bd1fbaaf762a25635674d23ff73f24a965efc220 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 2 Aug 2014 16:02:08 -0700 Subject: [PATCH 43/45] Made static mutex into member variable --- libraries/voxels/src/VoxelsScriptingInterface.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index 093d736720..1b2df471f4 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -81,8 +81,7 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale, DeleteVoxelCommand* deleteCommand = new DeleteVoxelCommand(_tree, addVoxelDetail, getVoxelPacketSender()); - static QMutex mutex; - mutex.lock(); + _undoStackMutex.lock(); _undoStack->beginMacro(addCommand->text()); // As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves. @@ -91,7 +90,7 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale, _undoStack->endMacro(); //Unlock the mutex - mutex.unlock(); + _undoStackMutex.unlock(); } else { // queue the destructive add queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail); @@ -121,11 +120,10 @@ void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale deleteVoxelDetail, getVoxelPacketSender()); - static QMutex mutex; - mutex.lock(); + _undoStackMutex.lock(); // As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves. _undoStack->push(command); - mutex.unlock(); + _undoStackMutex.unlock(); } else { getVoxelPacketSender()->queueVoxelEditMessages(PacketTypeVoxelErase, 1, &deleteVoxelDetail); _tree->deleteVoxelAt(deleteVoxelDetail.x, deleteVoxelDetail.y, deleteVoxelDetail.z, deleteVoxelDetail.s); From 5829e3ad1ede5aa80abee796509928d4991c6bb1 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 2 Aug 2014 16:03:13 -0700 Subject: [PATCH 44/45] Forgot to save header --- libraries/voxels/src/VoxelsScriptingInterface.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h index 787c37fb20..2e1fc2a8d5 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -102,6 +102,7 @@ private: void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails); VoxelTree* _tree; QUndoStack* _undoStack; + QMutex _undoStackMutex; }; #endif // hifi_VoxelsScriptingInterface_h From 74edf6557f5915412939a86dbe7b86fe1c13d72b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 4 Aug 2014 12:05:31 -0700 Subject: [PATCH 45/45] turn off editModels by default --- examples/editModels.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index f55ea4bbbe..2b2e11756f 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -1051,6 +1051,11 @@ function checkController(deltaTime) { var numberOfTriggers = Controller.getNumberOfTriggers(); var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; + + if (!isActive) { + // So that we hide the lasers bellow and keep updating the overlays position + numberOfButtons = 0; + } // this is expected for hydras if (numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2) { @@ -1072,11 +1077,21 @@ function checkController(deltaTime) { moveOverlays(); } + +var isActive = false; +var active; var newModel; var browser; function initToolBar() { toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); // New Model + active = toolBar.addTool({ + imageURL: toolIconUrl + "models-tool.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true, false); newModel = toolBar.addTool({ imageURL: toolIconUrl + "add-model-tool.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, @@ -1194,6 +1209,11 @@ function mousePressEvent(event) { modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + if (active == toolBar.clicked(clickedOverlay)) { + isActive = !isActive; + return; + } + if (newModel == toolBar.clicked(clickedOverlay)) { var url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]); if (url == null || url == "") { @@ -1229,6 +1249,11 @@ function mousePressEvent(event) { } } else { + // If we aren't active and didn't click on an overlay: quit + if (!isActive) { + return; + } + var pickRay = Camera.computePickRay(event.x, event.y); Vec3.print("[Mouse] Looking at: ", pickRay.origin); var foundIntersection = Models.findRayIntersection(pickRay); @@ -1313,7 +1338,7 @@ var oldModifier = 0; var modifier = 0; var wasShifted = false; function mouseMoveEvent(event) { - if (event.isAlt) { + if (event.isAlt || !isActive) { return; } @@ -1456,7 +1481,7 @@ function mouseMoveEvent(event) { function mouseReleaseEvent(event) { - if (event.isAlt) { + if (event.isAlt || !isActive) { return; }