diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 929d6c76c8..ae235eb1ff 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -21,7 +21,7 @@ #include #include #include - +#include #include "AssignmentFactory.h" #include "AssignmentThread.h" @@ -122,7 +122,9 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : connect(&AccountManager::getInstance(), &AccountManager::authRequired, this, &AssignmentClient::handleAuthenticationRequest); + // Create Singleton objects on main thread NetworkAccessManager::getInstance(); + SoundCache::getInstance(); } void AssignmentClient::sendAssignmentRequest() { diff --git a/examples/entityScripts/movable.js b/examples/entityScripts/movable.js index 94ed7137fe..21e6261179 100644 --- a/examples/entityScripts/movable.js +++ b/examples/entityScripts/movable.js @@ -15,17 +15,43 @@ this.graboffset = null; this.clickedAt = null; this.firstHolding = true; - this.clickedX = -1; - this.clickedY = -1; + this.clicked = { x: -1, y: -1}; + this.lastMovedPosition = { x: -1, y: -1}; + this.lastMovedTime = 0; this.rotateOverlayTarget = null; this.rotateOverlayInner = null; this.rotateOverlayOuter = null; this.rotateOverlayCurrent = null; this.rotateMode = false; this.originalRotation = null; - this.sound = null; + + this.moveSoundURLS = [ + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove1.wav", + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove2.wav", + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove3.wav" + ]; + + this.turnSoundURLS = [ + + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove1.wav", + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove2.wav", + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove3.wav" + + // TODO: determine if these or other turn sounds work better than move sounds. + //"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn1.wav", + //"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn2.wav", + //"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn3.wav" + ]; + + + this.moveSounds = new Array(); + this.turnSounds = new Array(); + this.moveSound = null; + this.turnSound = null; this.injector = null; + var debug = false; + var displayRotateTargets = true; // change to false if you don't want the rotate targets var rotateOverlayTargetSize = 10000; // really big target var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool var innerRadius; @@ -34,25 +60,62 @@ var yawZero; var rotationNormal; var yawNormal; + var stopSoundDelay = 100; // number of msecs of not moving to have sound stop - var debug = true; + this.getRandomInt = function(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } - // Download sound if needed - this.maybeDownloadSound = function() { - if (this.sound === null) { - this.sound = SoundCache.getSound("http://public.highfidelity.io/sounds/Collisions-otherorganic/whoosh2.raw"); + this.downloadSounds = function() { + for (var i = 0; i < this.moveSoundURLS.length; i++) { + this.moveSounds[i] = SoundCache.getSound(this.moveSoundURLS[i]); } - } - // Play drag sound - this.playSound = function() { - this.stopSound(); - if (this.sound && this.sound.downloaded) { - this.injector = Audio.playSound(this.sound, { position: this.properties.position, loop: true, volume: 0.1 }); + for (var i = 0; i < this.turnSoundURLS.length; i++) { + this.turnSounds[i] = SoundCache.getSound(this.turnSoundURLS[i]); } } - // stop drag sound + this.pickRandomSounds = function() { + var moveIndex = this.getRandomInt(0, this.moveSounds.length - 1); + var turnIndex = this.getRandomInt(0, this.turnSounds.length - 1); + if (debug) { + print("Random sounds -- turn:" + turnIndex + " move:" + moveIndex); + } + this.moveSound = this.moveSounds[moveIndex]; + this.turnSound = this.turnSounds[turnIndex]; + } + + // Play move sound + this.playMoveSound = function() { + if (debug) { + print("playMoveSound()"); + } + if (this.moveSound && this.moveSound.downloaded) { + if (debug) { + print("playMoveSound() --- calling this.injector = Audio.playSound(this.moveSound...)"); + } + this.injector = Audio.playSound(this.moveSound, { position: this.properties.position, loop: true, volume: 0.1 }); + } + } + + // Play turn sound + this.playTurnSound = function() { + if (debug) { + print("playTurnSound()"); + } + if (this.turnSound && this.turnSound.downloaded) { + if (debug) { + print("playTurnSound() --- calling this.injector = Audio.playSound(this.turnSound...)"); + } + this.injector = Audio.playSound(this.turnSound, { position: this.properties.position, loop: true, volume: 0.1 }); + } + } + + // stop sound this.stopSound = function() { + if (debug) { + print("stopSound()"); + } if (this.injector) { Audio.stopInjector(this.injector); this.injector = null; @@ -86,11 +149,33 @@ this.properties.position, upVector); this.graboffset = Vec3.subtract(this.properties.position, intersection); }; - + + this.stopSoundIfNotMoving = function(mouseEvent) { + var nowDate = new Date(); + var nowMSecs = nowDate.getTime(); + if(mouseEvent.x == this.lastMovedPosition.x && mouseEvent.y == this.lastMovedPosition.y) { + var elapsedSinceLastMove = nowMSecs - this.lastMovedMSecs; + if (debug) { + print("elapsedSinceLastMove:" + elapsedSinceLastMove); + } + if (elapsedSinceLastMove > stopSoundDelay) { + if (debug) { + print("calling stopSound()..."); + } + this.stopSound(); + } + } else { + // if we've moved, then track our last move position and time... + this.lastMovedMSecs = nowMSecs; + this.lastMovedPosition.x = mouseEvent.x; + this.lastMovedPosition.y = mouseEvent.y; + } + } + this.move = function(mouseEvent) { this.updatePosition(mouseEvent); if (this.injector === null) { - this.playSound(); + this.playMoveSound(); } }; @@ -146,7 +231,11 @@ majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); } - } + } + + if (this.injector === null) { + this.playTurnSound(); + } }; // All callbacks start by updating the properties this.updateProperties = function(entityID) { @@ -185,7 +274,7 @@ this.rotateOverlayTarget = Overlays.addOverlay("circle3d", { position: this.properties.position, - size: 10000, + size: rotateOverlayTargetSize, color: { red: 0, green: 0, blue: 0 }, alpha: 0.0, solid: true, @@ -201,7 +290,7 @@ alpha: innerAlpha, color: { red: 51, green: 152, blue: 203 }, solid: true, - visible: true, + visible: displayRotateTargets, rotation: yawOverlayRotation, hasTickMarks: true, majorTickMarksAngle: innerSnapAngle, @@ -222,7 +311,7 @@ alpha: outerAlpha, color: { red: 51, green: 152, blue: 203 }, solid: true, - visible: true, + visible: displayRotateTargets, rotation: yawOverlayRotation, hasTickMarks: true, @@ -244,7 +333,7 @@ color: { red: 224, green: 67, blue: 36}, alpha: 0.8, solid: true, - visible: true, + visible: displayRotateTargets, rotation: yawOverlayRotation, ignoreRayIntersection: true, // always ignore this hasTickMarks: true, @@ -260,19 +349,25 @@ this.preload = function(entityID) { this.updateProperties(entityID); // All callbacks start by updating the properties - this.maybeDownloadSound(); + this.downloadSounds(); }; this.clickDownOnEntity = function(entityID, mouseEvent) { this.updateProperties(entityID); // All callbacks start by updating the properties this.grab(mouseEvent); - var d = new Date(); - this.clickedAt = d.getTime(); + var nowDate = new Date(); + var nowMSecs = nowDate.getTime(); + this.clickedAt = nowMSecs; this.firstHolding = true; - this.clickedX = mouseEvent.x; - this.clickedY = mouseEvent.y; + this.clicked.x = mouseEvent.x; + this.clicked.y = mouseEvent.y; + this.lastMovedPosition.x = mouseEvent.x; + this.lastMovedPosition.y = mouseEvent.y; + this.lastMovedMSecs = nowMSecs; + + this.pickRandomSounds(); }; this.holdingClickOnEntity = function(entityID, mouseEvent) { @@ -281,7 +376,7 @@ if (this.firstHolding) { // if we haven't moved yet... - if (this.clickedX == mouseEvent.x && this.clickedY == mouseEvent.y) { + if (this.clicked.x == mouseEvent.x && this.clicked.y == mouseEvent.y) { var d = new Date(); var now = d.getTime(); @@ -295,12 +390,14 @@ this.firstHolding = false; } } - + if (this.rotateMode) { this.rotate(mouseEvent); } else { this.move(mouseEvent); } + + this.stopSoundIfNotMoving(mouseEvent); }; this.clickReleaseOnEntity = function(entityID, mouseEvent) { this.updateProperties(entityID); // All callbacks start by updating the properties diff --git a/examples/html/gridControls.html b/examples/html/gridControls.html index 4cb935e600..241fa2406c 100644 --- a/examples/html/gridControls.html +++ b/examples/html/gridControls.html @@ -12,14 +12,13 @@ { red: 0, green: 0, blue: 255 }, ]; - posY = document.getElementById("horiz-y"); - minorSpacing = document.getElementById("minor-spacing"); - majorSpacing = document.getElementById("major-spacing"); - gridOn = document.getElementById("grid-on"); - snapToGrid = document.getElementById("snap-to-grid"); - hGridVisible = document.getElementById("horiz-grid-visible"); - bMoveToSelection = document.getElementById("move-to-selection"); - bMoveToAvatar = document.getElementById("move-to-avatar"); + elPosY = document.getElementById("horiz-y"); + elMinorSpacing = document.getElementById("minor-spacing"); + elMajorSpacing = document.getElementById("major-spacing"); + elSnapToGrid = document.getElementById("snap-to-grid"); + elHorizontalGridVisible = document.getElementById("horiz-grid-visible"); + elMoveToSelection = document.getElementById("move-to-selection"); + elMoveToAvatar = document.getElementById("move-to-avatar"); if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { @@ -27,27 +26,27 @@ if (data.origin) { var origin = data.origin; - posY.value = origin.y; + elPosY.value = origin.y.toFixed(2); } if (data.minorGridSpacing) { - minorSpacing.value = data.minorGridSpacing; + elMinorSpacing.value = data.minorGridSpacing; } if (data.majorGridEvery) { - majorSpacing.value = data.majorGridEvery; + elMajorSpacing.value = data.majorGridEvery; } if (data.gridColor) { gridColor = data.gridColor; } - if (data.snapToGrid !== undefined) { - snapToGrid.checked = data.snapToGrid == true; + if (data.elSnapToGrid !== undefined) { + elSnapToGrid.checked = data.snapToGrid == true; } if (data.visible !== undefined) { - hGridVisible.checked = data.visible == true; + elHorizontalGridVisible.checked = data.visible == true; } }); @@ -55,29 +54,31 @@ EventBridge.emitWebEvent(JSON.stringify({ type: "update", origin: { - y: posY.value, + y: elPosY.value, }, - minorGridSpacing: minorSpacing.value, - majorGridEvery: majorSpacing.value, + minorGridSpacing: elMinorSpacing.value, + majorGridEvery: elMajorSpacing.value, gridColor: gridColor, - snapToGrid: snapToGrid.checked, - visible: hGridVisible.checked, + snapToGrid: elSnapToGrid.checked, + visible: elHorizontalGridVisible.checked, })); } } - document.addEventListener("input", emitUpdate); - hGridVisible.addEventListener("change", emitUpdate); - snapToGrid.addEventListener("change", emitUpdate); + elPosY.addEventListener("change", emitUpdate); + elMinorSpacing.addEventListener("change", emitUpdate); + elMajorSpacing.addEventListener("change", emitUpdate); + elSnapToGrid.addEventListener("change", emitUpdate); + elHorizontalGridVisible.addEventListener("change", emitUpdate); - bMoveToAvatar.addEventListener("click", function() { + elMoveToAvatar.addEventListener("click", function() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", action: "moveToAvatar", })); }); - bMoveToSelection.addEventListener("click", function() { + elMoveToSelection.addEventListener("click", function() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", action: "moveToSelection", diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 6733ccaf39..ada876e9b1 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1403,6 +1403,8 @@ SelectionDisplay = (function () { vector = vec3Mult(mask, vector); + vector = grid.snapToSpacing(vector); + var changeInDimensions = Vec3.multiply(-1, vec3Mult(signs, vector)); var newDimensions; if (proportional) { diff --git a/examples/libraries/gridTool.js b/examples/libraries/gridTool.js index d8b84babf9..eb2a227248 100644 --- a/examples/libraries/gridTool.js +++ b/examples/libraries/gridTool.js @@ -15,7 +15,7 @@ Grid = function(opts) { var minorGridWidth = 0.5; var majorGridWidth = 1.5; - var snapToGrid = true; + var snapToGrid = false; var gridOverlay = Overlays.addOverlay("grid", { position: { x: 0 , y: 0, z: 0 }, @@ -69,6 +69,24 @@ Grid = function(opts) { return Vec3.sum(position, origin); } + that.snapToSpacing = function(delta, majorOnly) { + print('snaptogrid? ' + snapToGrid); + if (!snapToGrid) { + return delta; + } + + var spacing = majorOnly ? (minorGridSpacing * majorGridEvery) : minorGridSpacing; + + var snappedDelta = { + x: Math.round(delta.x / spacing) * spacing, + y: Math.round(delta.y / spacing) * spacing, + z: Math.round(delta.z / spacing) * spacing, + }; + + return snappedDelta; + } + + that.setPosition = function(newPosition, noUpdate) { origin = Vec3.subtract(newPosition, { x: 0, y: yOffset, z: 0 }); origin.x = 0; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 31be016380..7cc7267f03 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1926,11 +1926,14 @@ void Application::init() { // check if we have a URL in settings to load to jump back to // we load this separate from the other settings so we don't double lookup a URL QSettings* interfaceSettings = lockSettings(); - QUrl addressURL = interfaceSettings->value(SETTINGS_ADDRESS_KEY).toUrl(); + QVariant addressVariant = interfaceSettings->value(SETTINGS_ADDRESS_KEY); - AddressManager::getInstance().handleLookupString(addressURL.toString()); + QString addressString = addressVariant.isNull() + ? DEFAULT_HIFI_ADDRESS : addressVariant.toUrl().toString(); unlockSettings(); + + AddressManager::getInstance().handleLookupString(addressString); } qDebug() << "Loaded settings"; diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 7e43de9dda..80ad9e5ef5 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -504,10 +504,12 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p (bodyOrientation * glm::quat(orientation.x, orientation.y, orientation.z, orientation.w) * glm::vec3(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z)); + RenderArgs::RenderSide renderSide = RenderArgs::STEREO_LEFT; if (eyeIndex == 0) { _leftEyePosition = thisEyePosition; } else { _rightEyePosition = thisEyePosition; + renderSide = RenderArgs::STEREO_RIGHT; } _camera->update(1.0f / Application::getInstance()->getFps()); @@ -522,11 +524,13 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p glViewport(_eyeRenderViewport[eye].Pos.x, _eyeRenderViewport[eye].Pos.y, _eyeRenderViewport[eye].Size.w, _eyeRenderViewport[eye].Size.h); + // Apply the offset for the left or right eye on the projection matrix + glTranslatef(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glTranslatef(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z); - Application::getInstance()->displaySide(*_camera); + Application::getInstance()->displaySide(*_camera, false, renderSide); applicationOverlay.displayOverlayTextureOculus(*_camera); _activeEyeIndex = -1; diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index 1375ff1ea5..44ea44c47d 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -19,6 +19,8 @@ ToolWindow::ToolWindow(QWidget* parent) : QMainWindow(parent), _hasShown(false), _lastGeometry() { + + Application::getInstance()->installEventFilter(this); } bool ToolWindow::event(QEvent* event) { @@ -47,8 +49,37 @@ bool ToolWindow::event(QEvent* event) { return QMainWindow::event(event); } +bool ToolWindow::eventFilter(QObject* sender, QEvent* event) { + switch (event->type()) { + case QEvent::WindowStateChange: + if (Application::getInstance()->getWindow()->isMinimized()) { + // If we are already visible, we are self-hiding + _selfHidden = isVisible(); + setVisible(false); + } else { + if (_selfHidden) { + setVisible(true); + } + } + break; + case QEvent::ApplicationDeactivate: + _selfHidden = isVisible(); + setVisible(false); + break; + case QEvent::ApplicationActivate: + if (_selfHidden) { + setVisible(true); + } + break; + default: + break; + } + + return false; +} + void ToolWindow::onChildVisibilityUpdated(bool visible) { - if (visible) { + if (!_selfHidden && visible) { setVisible(true); } else { bool hasVisible = false; @@ -59,7 +90,10 @@ void ToolWindow::onChildVisibilityUpdated(bool visible) { break; } } - setVisible(hasVisible); + // If a child was hidden and we don't have any children still visible, hide ourself. + if (!hasVisible) { + setVisible(false); + } } } diff --git a/interface/src/ui/ToolWindow.h b/interface/src/ui/ToolWindow.h index 87b94d46df..03ae85a418 100644 --- a/interface/src/ui/ToolWindow.h +++ b/interface/src/ui/ToolWindow.h @@ -28,11 +28,15 @@ public: virtual void addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation); virtual void removeDockWidget(QDockWidget* dockWidget); + virtual bool eventFilter(QObject* sender, QEvent* event); + public slots: void onChildVisibilityUpdated(bool visible); private: + // Indicates whether this window was hidden by itself (because the main window lost focus). + bool _selfHidden; bool _hasShown; QRect _lastGeometry; }; diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 84610d7981..4bf0d9ce93 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -69,9 +69,10 @@ void Grid3DOverlay::render(RenderArgs* args) { xColor color = getColor(); glm::vec3 position = getPosition(); - const int GRID_DIVISIONS = 300; + const int MINOR_GRID_DIVISIONS = 100; + const int MAJOR_GRID_DIVISIONS = 50; const float MAX_COLOR = 255.0f; - float scale = GRID_DIVISIONS * spacing; + glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); @@ -80,12 +81,13 @@ void Grid3DOverlay::render(RenderArgs* args) { // Minor grid glPushMatrix(); { - glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position.z); + glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z); + float scale = MINOR_GRID_DIVISIONS * spacing; glScalef(scale, scale, scale); - Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); + Application::getInstance()->getGeometryCache()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS); } glPopMatrix(); @@ -94,13 +96,13 @@ void Grid3DOverlay::render(RenderArgs* args) { { glLineWidth(4.0f); spacing *= _majorGridEvery; - glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position.z); + glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z); - scale *= _majorGridEvery; + float scale = MAJOR_GRID_DIVISIONS * spacing; glScalef(scale, scale, scale); - Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); + Application::getInstance()->getGeometryCache()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS); } glPopMatrix(); diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index d6ecdc9fc6..cfdaaa7d41 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -19,7 +19,8 @@ #include "AccountManager.h" -static const QString HIFI_URL_SCHEME = "hifi"; +const QString HIFI_URL_SCHEME = "hifi"; +const QString DEFAULT_HIFI_ADDRESS = "hifi://sandbox"; typedef const glm::vec3& (*PositionGetter)(); typedef glm::quat (*OrientationGetter)(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 86c6e6bc57..a27a5d1dd8 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -22,8 +22,6 @@ #include "HifiSockAddr.h" #include "NetworkPeer.h" -const QString DEFAULT_DOMAIN_HOSTNAME = "sandbox.highfidelity.io"; - const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102; const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103; const quint16 DOMAIN_SERVER_HTTP_PORT = 40100;