From 5b5a1745fd15a55212f161c3b784073d15b2f90d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 23 Oct 2014 14:46:39 -0700 Subject: [PATCH 1/7] Add camera move tool --- examples/libraries/entityCameraTool.js | 389 +++++++++++++++++++++- examples/libraries/entitySelectionTool.js | 1 - 2 files changed, 380 insertions(+), 10 deletions(-) diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index 63121d88a9..b6bb6e149e 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -34,7 +34,7 @@ var EASING_MULTIPLIER = 8; var INITIAL_ZOOM_DISTANCE = 2; var INITIAL_ZOOM_DISTANCE_FIRST_PERSON = 3; -EntityCameraTool = function() { +CameraManager = function() { var that = {}; that.enabled = false; @@ -85,24 +85,28 @@ EntityCameraTool = function() { Camera.setMode("independent"); that.updateCamera(); + + cameraTool.setVisible(true); } - that.disable = function() { + that.disable = function(ignoreCamera) { if (!that.enabled) return; that.enabled = false; that.mode = MODE_INACTIVE; - Camera.setMode(that.previousCameraMode); + if (!ignoreCamera) { + Camera.setMode(that.previousCameraMode); + } + cameraTool.setVisible(false); } that.focus = function(entityProperties) { - var dim = entityProperties.dimensions; - dim = SelectionManager.worldDimensions; + var dim = SelectionManager.worldDimensions; var size = Math.max(dim.x, Math.max(dim.y, dim.z)); that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM); - that.setFocalPoint(SelectionManager.worldPosition);//entityProperties.position); + that.setFocalPoint(SelectionManager.worldPosition); that.updateCamera(); } @@ -116,6 +120,42 @@ EntityCameraTool = function() { that.updateCamera(); } + that.addYaw = function(yaw) { + that.targetYaw += yaw; + that.updateCamera(); + } + + that.addPitch = function(pitch) { + that.targetPitch += pitch; + that.updateCamera(); + } + + that.addZoom = function(zoom) { + zoom *= that.targetZoomDistance * ZOOM_SCALING; + that.targetZoomDistance = Math.min(Math.max(that.targetZoomDistance + zoom, MIN_ZOOM_DISTANCE), MAX_ZOOM_DISTANCE); + that.updateCamera(); + } + + that.getZoomPercentage = function() { + return (that.zoomDistance - MIN_ZOOM_DISTANCE) / MAX_ZOOM_DISTANCE; + } + + that.setZoomPercentage = function(pct) { + that.targetZoomDistance = pct * (MAX_ZOOM_DISTANCE - MIN_ZOOM_DISTANCE); + } + + that.pan = function(offset) { + var up = Quat.getUp(Camera.getOrientation()); + var right = Quat.getRight(Camera.getOrientation()); + + up = Vec3.multiply(up, offset.y * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance); + right = Vec3.multiply(right, offset.x * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance); + + var dPosition = Vec3.sum(up, right); + + that.moveFocalPoint(dPosition); + } + that.mouseMoveEvent = function(event) { if (that.enabled && that.mode != MODE_INACTIVE) { if (that.mode == MODE_ORBIT) { @@ -168,7 +208,7 @@ EntityCameraTool = function() { return true; } - return false; + return cameraTool.mousePressEvent(event); } that.mouseReleaseEvent = function(event) { @@ -185,13 +225,13 @@ EntityCameraTool = function() { // Scale based on current zoom level dZoom *= that.targetZoomDistance * ZOOM_SCALING; - that.targetZoomDistance = Math.max(that.targetZoomDistance + dZoom, MIN_ZOOM_DISTANCE); + that.targetZoomDistance = Math.min(Math.max(that.targetZoomDistance + dZoom, MIN_ZOOM_DISTANCE), MAX_ZOOM_DISTANCE); that.updateCamera(); } that.updateCamera = function() { - if (!that.enabled) return; + if (!that.enabled || Camera.getMode() != "independent") return; var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 }); var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 }); @@ -215,6 +255,10 @@ EntityCameraTool = function() { // Ease the position and orbit of the camera that.update = function(dt) { + if (Camera.getMode() != "independent") { + return; + } + var scale = Math.min(dt * EASING_MULTIPLIER, 1.0); var dYaw = that.targetYaw - that.yaw; @@ -239,9 +283,336 @@ EntityCameraTool = function() { that.updateCamera(); } + // Last mode that was first or third person + var lastAvatarCameraMode = "first person"; + Camera.modeUpdated.connect(function(newMode) { + print("Camera mode has been updated: " + newMode); + if (newMode == "first person" || newMode == "third person") { + lastAvatarCameraMode = newMode; + that.disable(true); + } else { + that.enable(); + } + }); + + Controller.keyReleaseEvent.connect(function (event) { + if (event.text == "ESC" && that.enabled) { + Camera.setMode(lastAvatarCameraMode); + cameraManager.disable(true); + } + }); + Script.update.connect(that.update); Controller.wheelEvent.connect(that.wheelEvent); + var cameraTool = new CameraTool(that); + return that; } + +var ZoomTool = function(opts) { + var that = {}; + + var position = opts.position || { x: 0, y: 0 }; + var height = opts.height || 200; + var color = opts.color || { red: 255, green: 0, blue: 0 }; + var arrowButtonSize = opts.buttonSize || 20; + var arrowButtonBackground = opts.arrowBackground || { red: 255, green: 255, blue: 255 }; + var zoomBackground = { red: 128, green: 0, blue: 0 }; + var zoomHeight = height - (arrowButtonSize * 2); + var zoomBarY = position.y + arrowButtonSize, + + var onIncreasePressed = opts.onIncreasePressed; + var onDecreasePressed = opts.onDecreasePressed; + var onPercentageSet = opts.onPercentageSet; + + var increaseButton = Overlays.addOverlay("text", { + x: position.x, + y: position.y, + width: arrowButtonSize, + height: arrowButtonSize, + color: color, + backgroundColor: arrowButtonBackground, + topMargin: 4, + leftMargin: 4, + text: "+", + alpha: 1.0, + visible: true, + }); + var decreaseButton = Overlays.addOverlay("text", { + x: position.x, + y: position.y + arrowButtonSize + zoomHeight, + width: arrowButtonSize, + height: arrowButtonSize, + color: color, + backgroundColor: arrowButtonBackground, + topMargin: 4, + leftMargin: 4, + text: "-", + alpha: 1.0, + visible: true, + }); + var zoomBar = Overlays.addOverlay("text", { + x: position.x + 5, + y: zoomBarY, + width: 10, + height: zoomHeight, + color: { red: 0, green: 255, blue: 0 }, + backgroundColor: zoomBackground, + topMargin: 4, + leftMargin: 4, + text: "", + alpha: 1.0, + visible: true, + }); + var zoomHandle = Overlays.addOverlay("text", { + x: position.x, + y: position.y + arrowButtonSize, + width: arrowButtonSize, + height: 10, + backgroundColor: { red: 0, green: 255, blue: 0 }, + topMargin: 4, + leftMargin: 4, + text: "", + alpha: 1.0, + visible: true, + }); + + var allOverlays = [ + increaseButton, + decreaseButton, + zoomBar, + zoomHandle, + ]; + + that.destroy = function() { + for (var i = 0; i < allOverlays.length; i++) { + Overlays.deleteOverlay(allOverlays[i]); + } + }; + + that.setVisible = function(visible) { + for (var i = 0; i < allOverlays.length; i++) { + Overlays.editOverlay(allOverlays[i], { visible: visible }); + } + } + + that.setZoomPercentage = function(pct) { + var yOffset = (zoomHeight - 10) * pct; + Overlays.editOverlay(zoomHandle, { + y: position.y + arrowButtonSize + yOffset, + }); + } + + that.mouseReleaseEvent = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + var clicked = false; + if (clickedOverlay == increaseButton) { + if (onIncreasePressed) onIncreasePressed(); + clicked = true; + } else if (clickedOverlay == decreaseButton) { + if (onDecreasePressed) onDecreasePressed(); + clicked = true; + } else if (clickedOverlay == zoomBar) { + if (onPercentageSet) onPercentageSet((event.y - zoomBarY) / zoomHeight); + clicked = true; + } + return clicked; + } + + return that; +}; + +var ArrowTool = function(opts) { + var that = {}; + + var position = opts.position || { x: 0, y: 0 }; + var arrowButtonSize = opts.buttonSize || 20; + var color = opts.color || { red: 255, green: 0, blue: 0 }; + var arrowButtonBackground = opts.arrowBackground || { red: 255, green: 255, blue: 255 }; + var centerButtonBackground = opts.centerBackground || { red: 255, green: 255, blue: 255 }; + var onUpPressed = opts.onUpPressed; + var onDownPressed = opts.onDownPressed; + var onLeftPressed = opts.onLeftPressed; + var onRightPressed = opts.onRightPressed; + var onCenterPressed = opts.onCenterPressed; + + var upButton = Overlays.addOverlay("text", { + x: position.x + arrowButtonSize, + y: position.y, + width: arrowButtonSize, + height: arrowButtonSize, + color: color, + backgroundColor: arrowButtonBackground, + topMargin: 4, + leftMargin: 4, + text: "^", + alpha: 1.0, + visible: true, + }); + var leftButton = Overlays.addOverlay("text", { + x: position.x, + y: position.y + arrowButtonSize, + width: arrowButtonSize, + height: arrowButtonSize, + color: color, + backgroundColor: arrowButtonBackground, + topMargin: 4, + leftMargin: 4, + text: "<", + alpha: 1.0, + visible: true, + }); + var rightButton = Overlays.addOverlay("text", { + x: position.x + (arrowButtonSize * 2), + y: position.y + arrowButtonSize, + width: arrowButtonSize, + height: arrowButtonSize, + color: color, + backgroundColor: arrowButtonBackground, + topMargin: 4, + leftMargin: 4, + text: ">", + alpha: 1.0, + visible: true, + }); + var downButton = Overlays.addOverlay("text", { + x: position.x + arrowButtonSize, + y: position.y + (arrowButtonSize * 2), + width: arrowButtonSize, + height: arrowButtonSize, + color: color, + backgroundColor: arrowButtonBackground, + topMargin: 4, + leftMargin: 4, + text: "v", + alpha: 1.0, + visible: true, + }); + var centerButton = Overlays.addOverlay("text", { + x: position.x + arrowButtonSize, + y: position.y + arrowButtonSize, + width: arrowButtonSize, + height: arrowButtonSize, + color: color, + backgroundColor: centerButtonBackground, + topMargin: 4, + leftMargin: 4, + text: "", + alpha: 1.0, + visible: true, + }); + + var allOverlays = [ + upButton, + downButton, + leftButton, + rightButton, + centerButton, + ]; + + that.destroy = function() { + for (var i = 0; i < allOverlays.length; i++) { + Overlays.deleteOverlay(allOverlays[i]); + } + }; + + that.setVisible = function(visible) { + for (var i = 0; i < allOverlays.length; i++) { + Overlays.editOverlay(allOverlays[i], { visible: visible }); + } + } + + that.mouseReleaseEvent = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + var clicked = false; + if (clickedOverlay == leftButton) { + if (onLeftPressed) onLeftPressed(); + clicked = true; + } else if (clickedOverlay == rightButton) { + if (onRightPressed) onRightPressed(); + clicked = true; + } else if (clickedOverlay == upButton) { + if (onUpPressed) onUpPressed(); + clicked = true; + } else if (clickedOverlay == downButton) { + if (onDownPressed) onDownPressed(); + clicked = true; + } else if (clickedOverlay == centerButton) { + if (onCenterPressed) onCenterPressed(); + clicked = true; + } + return clicked; + } + + return that; +} + + +CameraTool = function(cameraManager) { + var that = {}; + + var toolsPosition = { x: 20, y: 280 }; + var orbitToolPosition = toolsPosition; + var panToolPosition = { x: toolsPosition.x + 80, y: toolsPosition.y }; + var zoomToolPosition = { x: toolsPosition.x + 20, y: toolsPosition.y + 80 }; + + var orbitIncrement = 15; + orbitTool = ArrowTool({ + position: orbitToolPosition, + arrowBackground: { red: 192, green: 192, blue: 192 }, + centerBackground: { red: 128, green: 128, blue: 255 }, + color: { red: 0, green: 0, blue: 0 }, + onUpPressed: function() { cameraManager.addPitch(orbitIncrement); }, + onDownPressed: function() { cameraManager.addPitch(-orbitIncrement); }, + onLeftPressed: function() { cameraManager.addYaw(-orbitIncrement); }, + onRightPressed: function() { cameraManager.addYaw(orbitIncrement); }, + onCenterPressed: function() { cameraManager.focus(); }, + }); + panTool = ArrowTool({ + position: panToolPosition, + arrowBackground: { red: 192, green: 192, blue: 192 }, + centerBackground: { red: 128, green: 128, blue: 255 }, + color: { red: 0, green: 0, blue: 0 }, + onUpPressed: function() { cameraManager.pan({ x: 0, y: 15 }); }, + onDownPressed: function() { cameraManager.pan({ x: 0, y: -15 }); }, + onLeftPressed: function() { cameraManager.pan({ x: -15, y: 0 }); }, + onRightPressed: function() { cameraManager.pan({ x: 15, y: 0 }); }, + }); + zoomTool = ZoomTool({ + position: zoomToolPosition, + arrowBackground: { red: 192, green: 192, blue: 192 }, + color: { red: 0, green: 0, blue: 0 }, + onIncreasePressed: function() { cameraManager.addZoom(-10); }, + onDecreasePressed: function() { cameraManager.addZoom(10); }, + onPercentageSet: function(pct) { cameraManager.setZoomPercentage(pct); } + }); + + Script.scriptEnding.connect(function() { + orbitTool.destroy(); + panTool.destroy(); + zoomTool.destroy(); + }); + + that.mousePressEvent = function(event) { + return orbitTool.mouseReleaseEvent(event) + || panTool.mouseReleaseEvent(event) + || zoomTool.mouseReleaseEvent(event); + }; + + that.setVisible = function(visible) { + orbitTool.setVisible(visible); + panTool.setVisible(visible); + zoomTool.setVisible(visible); + }; + + Script.update.connect(function() { + cameraManager.getZoomPercentage(); + zoomTool.setZoomPercentage(cameraManager.getZoomPercentage()); + }); + + that.setVisible(false); + + return that; +}; diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index f1a94dbdd7..486ba41ee7 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -19,7 +19,6 @@ SPACE_WORLD = "world"; SelectionManager = (function() { var that = {}; - that.savedProperties = {}; that.eventListener = null; From 28095e9972ac0bf8b36d4f6f1c98035b206f053a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 23 Oct 2014 14:47:07 -0700 Subject: [PATCH 2/7] Remove print statements in edit entities --- examples/libraries/entitySelectionTool.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 486ba41ee7..70d1c7c918 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -926,7 +926,6 @@ SelectionDisplay = (function () { }; that.updateHandles = function() { - // print("Updating handles"); if (SelectionManager.selections.length == 0) { that.setOverlaysVisible(false); return; @@ -1287,7 +1286,6 @@ SelectionDisplay = (function () { var rotation = null; var onBegin = function(event) { - print("STARTING: " + stretchMode); var properties = Entities.getEntityProperties(currentSelection); initialProperties = properties; rotation = spaceMode == SPACE_LOCAL ? properties.rotation : Quat.fromPitchYawRollDegrees(0, 0, 0); @@ -1365,7 +1363,6 @@ SelectionDisplay = (function () { }; var onEnd = function(event, reason) { - print("ENDING: " + stretchMode); Overlays.editOverlay(xRailOverlay, { visible: false }); Overlays.editOverlay(yRailOverlay, { visible: false }); Overlays.editOverlay(zRailOverlay, { visible: false }); @@ -1414,7 +1411,6 @@ SelectionDisplay = (function () { var absX = Math.abs(changeInDimensions.x); var absY = Math.abs(changeInDimensions.y); var absZ = Math.abs(changeInDimensions.z); - print('abs: ' + absX + ', ' + absY + ', ' + absZ); var pctChange = 0; if (absX > absY && absX > absZ) { pctChange = changeInDimensions.x / initialProperties.dimensions.x; @@ -1426,7 +1422,6 @@ SelectionDisplay = (function () { pctChange = changeInDimensions.z / initialProperties.dimensions.z; pctChange = changeInDimensions.z / initialDimensions.z; } - print('change: ' + pctChange); pctChange += 1.0; newDimensions = Vec3.multiply(pctChange, initialDimensions); } else { @@ -1880,7 +1875,6 @@ SelectionDisplay = (function () { var tool = grabberTools[result.overlayID]; if (tool) { - print("FOUND TOOL! " + tool.mode); activeTool = tool; mode = tool.mode; somethingClicked = true; @@ -1980,7 +1974,6 @@ SelectionDisplay = (function () { if (result.intersects) { var tool = grabberTools[result.overlayID]; if (tool) { - print("FOUND TOOL! " + tool.mode); activeTool = tool; mode = tool.mode; somethingClicked = true; From fde7b30501dd8fd41259f37c75cebcf4ad13bbfa Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 23 Oct 2014 14:47:41 -0700 Subject: [PATCH 3/7] Add Camera::modeUpdated() --- examples/newEditEntities.js | 21 ++++++++------- interface/src/Camera.cpp | 51 ++++++++++++++++++++++++------------- interface/src/Camera.h | 14 +++++++++- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index 4d5abaf254..f80af7912e 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -33,7 +33,7 @@ Script.include("libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; Script.include("libraries/entityCameraTool.js"); -var entityCameraTool = new EntityCameraTool(); +var cameraManager = new CameraManager(); selectionManager.setEventListener(selectionDisplay.updateHandles); @@ -247,9 +247,9 @@ var toolBar = (function () { isActive = !isActive; if (!isActive) { selectionDisplay.unselectAll(); - entityCameraTool.disable(); + cameraManager.disable(); } else { - entityCameraTool.enable(); + cameraManager.enable(); } return true; } @@ -374,7 +374,7 @@ function mousePressEvent(event) { var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) - || entityCameraTool.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { + || cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { // Event handled; do nothing. return; } else { @@ -482,8 +482,8 @@ function mouseMoveEvent(event) { return; } - // allow the selectionDisplay and entityCameraTool to handle the event first, if it doesn't handle it, then do our own thing - if (selectionDisplay.mouseMoveEvent(event) || entityCameraTool.mouseMoveEvent(event)) { + // allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing + if (selectionDisplay.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) { return; } @@ -522,7 +522,7 @@ function mouseReleaseEvent(event) { if (entitySelected) { tooltip.show(false); } - entityCameraTool.mouseReleaseEvent(event); + cameraManager.mouseReleaseEvent(event); } Controller.mousePressEvent.connect(mousePressEvent); @@ -652,7 +652,6 @@ Menu.menuItemEvent.connect(handeMenuEvent); Controller.keyReleaseEvent.connect(function (event) { // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items - print(event.text); if (event.text == "`") { handeMenuEvent("Edit Properties..."); } @@ -666,7 +665,11 @@ Controller.keyReleaseEvent.connect(function (event) { if (entitySelected) { // Get latest properties var properties = Entities.getEntityProperties(selectedEntityID); - entityCameraTool.focus(properties); + cameraManager.focus(properties); + } + } else if (event.text == '[') { + if (isActive) { + cameraManager.enable(); } } }); diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index a8138363fa..ceb4cb09a0 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -21,6 +21,32 @@ #include "devices/OculusManager.h" +CameraMode stringToMode(const QString& mode) { + if (mode == "third person") { + return CAMERA_MODE_THIRD_PERSON; + } else if (mode == "first person") { + return CAMERA_MODE_FIRST_PERSON; + } else if (mode == "mirror") { + return CAMERA_MODE_MIRROR; + } else if (mode == "independent") { + return CAMERA_MODE_INDEPENDENT; + } + return CAMERA_MODE_NULL; +} + +QString modeToString(CameraMode mode) { + if (mode == CAMERA_MODE_THIRD_PERSON) { + return "third person"; + } else if (mode == CAMERA_MODE_FIRST_PERSON) { + return "first person"; + } else if (mode == CAMERA_MODE_MIRROR) { + return "mirror"; + } else if (mode == CAMERA_MODE_INDEPENDENT) { + return "independent"; + } + return "unknown"; +} + Camera::Camera() : _mode(CAMERA_MODE_THIRD_PERSON), _position(0.0f, 0.0f, 0.0f), @@ -48,6 +74,7 @@ float Camera::getFarClip() const { void Camera::setMode(CameraMode m) { _mode = m; + emit modeUpdated(m); } @@ -70,6 +97,7 @@ void Camera::setFarClip(float f) { CameraScriptableObject::CameraScriptableObject(Camera* camera, ViewFrustum* viewFrustum) : _camera(camera), _viewFrustum(viewFrustum) { + connect(_camera, &Camera::modeUpdated, this, &CameraScriptableObject::onModeUpdated); } PickRay CameraScriptableObject::computePickRay(float x, float y) { @@ -86,24 +114,7 @@ PickRay CameraScriptableObject::computePickRay(float x, float y) { } QString CameraScriptableObject::getMode() const { - QString mode("unknown"); - switch(_camera->getMode()) { - case CAMERA_MODE_THIRD_PERSON: - mode = "third person"; - break; - case CAMERA_MODE_FIRST_PERSON: - mode = "first person"; - break; - case CAMERA_MODE_MIRROR: - mode = "mirror"; - break; - case CAMERA_MODE_INDEPENDENT: - mode = "independent"; - break; - default: - break; - } - return mode; + return modeToString(_camera->getMode()); } void CameraScriptableObject::setMode(const QString& mode) { @@ -131,5 +142,9 @@ void CameraScriptableObject::setMode(const QString& mode) { } } +void CameraScriptableObject::onModeUpdated(CameraMode m) { + emit modeUpdated(modeToString(m)); +} + diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 80454a969e..e876f70e3a 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -27,8 +27,11 @@ enum CameraMode NUM_CAMERA_MODES }; -class Camera { +Q_DECLARE_METATYPE(CameraMode); +static int cameraModeId = qRegisterMetaType(); +class Camera : public QObject { + Q_OBJECT public: Camera(); @@ -63,6 +66,9 @@ public: const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; } const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; } float getScale() const { return _scale; } + +signals: + void modeUpdated(CameraMode newMode); private: @@ -100,6 +106,12 @@ public slots: PickRay computePickRay(float x, float y); +signals: + void modeUpdated(const QString& newMode); + +private slots: + void onModeUpdated(CameraMode m); + private: Camera* _camera; ViewFrustum* _viewFrustum; From e556886c72b9b0135c2c342f9660e093eded3c8f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 23 Oct 2014 15:28:16 -0700 Subject: [PATCH 4/7] Add entity copying and axis constraining to entity tools --- examples/libraries/entitySelectionTool.js | 62 ++++++++++++++++++----- examples/newEditEntities.js | 2 +- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 70d1c7c918..c503ab82a4 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1132,17 +1132,30 @@ SelectionDisplay = (function () { UndoStack.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData); } - var lastXZPick = null; + var initialXZPick = null; + var isConstrained = false; + var startPosition = null; var translateXZTool = { mode: 'TRANSLATE_XZ', onBegin: function(event) { SelectionManager.saveProperties(); - var position = SelectionManager.worldPosition; + startPosition = SelectionManager.worldPosition; var dimensions = SelectionManager.worldDimensions; - var bottom = position.y - (dimensions.y / 2) var pickRay = Camera.computePickRay(event.x, event.y); - lastXZPick = rayPlaneIntersection(pickRay, position, { x: 0, y: 1, z: 0 }); + initialXZPick = rayPlaneIntersection(pickRay, startPosition, { x: 0, y: 1, z: 0 }); + + // Duplicate entities if alt is pressed. This will make a + // copy of the selected entities and move the _original_ entities, not + // the new ones. + if (event.isAlt) { + for (var otherEntityID in SelectionManager.savedProperties) { + var properties = SelectionManager.savedProperties[otherEntityID]; + var entityID = Entities.addEntity(properties); + } + } + + isConstrained = false; }, onEnd: function(event, reason) { if (reason == 'cancel') { @@ -1154,6 +1167,8 @@ SelectionDisplay = (function () { } else { pushCommandForSelections(); } + Overlays.editOverlay(xRailOverlay, { visible: false }); + Overlays.editOverlay(zRailOverlay, { visible: false }); }, onMove: function(event) { if (!entitySelected || mode !== "TRANSLATE_XZ") { @@ -1168,26 +1183,47 @@ SelectionDisplay = (function () { Quat.getFront(lastCameraOrientation)); var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var pickRay = Camera.computePickRay(event.x, event.y); var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { x: 0, y: 1, z: 0 }); - vector = Vec3.subtract(pick, lastXZPick); - lastXZPick = pick; + var vector = Vec3.subtract(pick, initialXZPick); + // initialXZPick = pick; + + // If shifted, constrain to one axis + if (event.isShifted) { + if (Math.abs(vector.x) > Math.abs(vector.z)) { + vector.z = 0; + } else { + vector.x = 0; + } + if (!isConstrained) { + Overlays.editOverlay(xRailOverlay, { visible: true }); + var xStart = Vec3.sum(startPosition, { x: -10000, y: 0, z: 0 }); + var xEnd = Vec3.sum(startPosition, { x: 10000, y: 0, z: 0 }); + var zStart = Vec3.sum(startPosition, { x: 0, y: 0, z: -10000 }); + var zEnd = Vec3.sum(startPosition, { x: 0, y: 0, z: 10000 }); + Overlays.editOverlay(xRailOverlay, { start: xStart, end: xEnd, visible: true }); + Overlays.editOverlay(zRailOverlay, { start: zStart, end: zEnd, visible: true }); + isConstrained = true; + } + } else { + if (isConstrained) { + Overlays.editOverlay(xRailOverlay, { visible: false }); + Overlays.editOverlay(zRailOverlay, { visible: false }); + } + } var wantDebug = false; for (var i = 0; i < SelectionManager.selections.length; i++) { - var properties = Entities.getEntityProperties(SelectionManager.selections[i]); - var original = properties.position; - properties.position = Vec3.sum(properties.position, vector); - Entities.editEntity(SelectionManager.selections[i], properties); + var properties = SelectionManager.savedProperties[SelectionManager.selections[i].id]; + Entities.editEntity(SelectionManager.selections[i], { + position: Vec3.sum(properties.position, vector), + }); if (wantDebug) { print("translateXZ... "); Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); Vec3.print(" newIntersection:", newIntersection); Vec3.print(" vector:", vector); - Vec3.print(" originalPosition:", original); Vec3.print(" newPosition:", properties.position); Vec3.print(" newPosition:", newPosition); } diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index f80af7912e..aa38baf9e2 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -178,7 +178,7 @@ var toolBar = (function () { position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); if (position.x > 0 && position.y > 0 && position.z > 0) { - Entities.addEntity({ + var entityId = Entities.addEntity({ type: "Model", position: position, dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION }, From 8158733a27d5c99ac0666cc5e49596b7f7efe7e6 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 24 Oct 2014 20:21:57 -0700 Subject: [PATCH 5/7] double click to turn around --- examples/headMove.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/examples/headMove.js b/examples/headMove.js index b9330153fc..6a020a8c9a 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -43,8 +43,10 @@ var movingWithHead = false; var headStartPosition, headStartDeltaPitch, headStartFinalPitch, headStartRoll, headStartYaw; var deltaYaw = 0.0; var keyDownTime = 0.0; +var timeSinceLastUp = 0.0; var watchAvatar = false; var oldMode; +var lastYawTurned = 0.0; function saveCameraState() { oldMode = Camera.getMode(); @@ -134,7 +136,7 @@ function finishWarp() { } function update(deltaTime) { - + timeSinceLastUp += deltaTime; if (movingWithHead) { keyDownTime += deltaTime; updateWarp(); @@ -156,22 +158,26 @@ Controller.keyPressEvent.connect(function(event) { } }); -var TIME_FOR_TURN_AROUND = 0.50; var TIME_FOR_TURN = 0.25; +var DOUBLE_CLICK_TIME = 0.50; var TURN_AROUND = 180.0; Controller.keyReleaseEvent.connect(function(event) { if (event.text == "SPACE" && !event.isAutoRepeat) { movingWithHead = false; - if (keyDownTime < TIME_FOR_TURN_AROUND) { - if (keyDownTime < TIME_FOR_TURN) { - var currentYaw = MyAvatar.getHeadFinalYaw(); - MyAvatar.orientation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, currentYaw, 0), MyAvatar.orientation); - } else { - MyAvatar.orientation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, TURN_AROUND, 0), MyAvatar.orientation); - } + if (timeSinceLastUp < DOUBLE_CLICK_TIME) { + // Turn all the way around + var turnRemaining = TURN_AROUND - lastYawTurned; + lastYawTurned = 0.0; + MyAvatar.orientation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, TURN_AROUND, 0), MyAvatar.orientation); + playSound(); + } else if (keyDownTime < TIME_FOR_TURN) { + var currentYaw = MyAvatar.getHeadFinalYaw(); + lastYawTurned = currentYaw; + MyAvatar.orientation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, currentYaw, 0), MyAvatar.orientation); playSound(); } + timeSinceLastUp = 0.0; finishWarp(); if (watchAvatar) { restoreCameraState(); From fde85bb4d28994cc99fe8f3ee6b01281f3c65fca Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 26 Oct 2014 21:18:54 -0700 Subject: [PATCH 6/7] don't spam --- examples/guidedTour.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/guidedTour.js b/examples/guidedTour.js index 3416e55d1c..1882b527d7 100644 --- a/examples/guidedTour.js +++ b/examples/guidedTour.js @@ -17,9 +17,9 @@ function update(deltaTime) { if (Math.random() < deltaTime) { guide = AvatarList.avatarWithDisplayName(leaderName); if (guide && !isGuide) { - print("found a guide!"); + print("Found a tour guide!"); isGuide = true; - } else if (!isGuide) { + } else if (!guide && isGuide) { print("Lost My Guide"); isguide = false; } From 4b7eb682b3ea69eb55a9687371cb475a38146834 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 27 Oct 2014 08:44:45 -0700 Subject: [PATCH 7/7] Easier to get out of body and move from there with target --- examples/headMove.js | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/examples/headMove.js b/examples/headMove.js index 6a020a8c9a..f377409cb8 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -17,6 +17,8 @@ var willMove = false; var warpActive = false; var warpPosition = { x: 0, y: 0, z: 0 }; +var hipsToEyes; + // Overlays to show target location var WARP_SPHERE_SIZE = 0.15; @@ -47,6 +49,7 @@ var timeSinceLastUp = 0.0; var watchAvatar = false; var oldMode; var lastYawTurned = 0.0; +var startPullbackPosition; function saveCameraState() { oldMode = Camera.getMode(); @@ -67,6 +70,7 @@ function activateWarp() { var TRIGGER_PULLBACK_DISTANCE = 0.04; var WATCH_AVATAR_DISTANCE = 1.5; var MAX_PULLBACK_YAW = 5.0; +var MAX_PULLBACK_PITCH = 5.0; var sound = new Sound("http://public.highfidelity.io/sounds/Footsteps/FootstepW2Right-12db.wav"); function playSound() { @@ -76,35 +80,44 @@ function playSound() { options.volume = 1.0; Audio.playSound(sound, options); } - var WARP_SMOOTHING = 0.90; var WARP_START_TIME = 0.50; -var WARP_START_DISTANCE = 1.0; +var WARP_START_DISTANCE = 1.5; var WARP_SENSITIVITY = 0.15; +var fixedHeight = true; + function updateWarp() { if (!warpActive) return; - var look = Quat.getFront(Camera.getOrientation()); + var viewEulers = Quat.safeEulerAngles(Camera.getOrientation()); var deltaPosition = Vec3.subtract(MyAvatar.getTrackedHeadPosition(), headStartPosition); var deltaPitch = MyAvatar.getHeadFinalPitch() - headStartFinalPitch; deltaYaw = MyAvatar.getHeadFinalYaw() - headStartYaw; + viewEulers.x -= deltaPitch; + var look = Quat.getFront(Quat.fromVec3Degrees(viewEulers)); - willMove = (!watchAvatar && (keyDownTime > WARP_START_TIME)); + willMove = (keyDownTime > WARP_START_TIME); if (willMove) { - //var distance = Math.pow((deltaPitch - WARP_PITCH_DEAD_ZONE) * WARP_SENSITIVITY, 2.0); var distance = Math.exp(deltaPitch * WARP_SENSITIVITY) * WARP_START_DISTANCE; - var warpDirection = Vec3.normalize({ x: look.x, y: 0, z: look.z }); - warpPosition = Vec3.mix(Vec3.sum(MyAvatar.position, Vec3.multiply(warpDirection, distance)), warpPosition, WARP_SMOOTHING); + var warpDirection = Vec3.normalize({ x: look.x, y: (fixedHeight ? 0 : look.y), z: look.z }); + var startPosition = (watchAvatar ? Camera.getPosition(): MyAvatar.getEyePosition()); + warpPosition = Vec3.mix(Vec3.sum(startPosition, Vec3.multiply(warpDirection, distance)), warpPosition, WARP_SMOOTHING); } var height = MyAvatar.getEyePosition().y - MyAvatar.position.y; + var cameraPosition; - if (!watchAvatar && (Math.abs(deltaYaw) < MAX_PULLBACK_YAW) && (deltaPosition.z > TRIGGER_PULLBACK_DISTANCE)) { + if (!watchAvatar && + (Math.abs(deltaYaw) < MAX_PULLBACK_YAW) && + (Math.abs(deltaPitch) < MAX_PULLBACK_PITCH) && + (Vec3.length(deltaPosition) > TRIGGER_PULLBACK_DISTANCE)) { saveCameraState(); - var cameraPosition = Vec3.subtract(MyAvatar.position, Vec3.multiplyQbyV(Camera.getOrientation(), { x: 0, y: -height, z: -height * WATCH_AVATAR_DISTANCE })); + cameraPosition = Vec3.subtract(MyAvatar.position, Vec3.multiplyQbyV(Camera.getOrientation(), { x: 0, y: -height, z: -height * WATCH_AVATAR_DISTANCE })); Camera.setPosition(cameraPosition); + cameraPosition = Camera.getPosition(); + startPullbackPosition = cameraPosition; watchAvatar = true; } @@ -130,6 +143,7 @@ function finishWarp() { visible: false, }); if (willMove) { + warpPosition.y -= hipsToEyes; MyAvatar.position = warpPosition; playSound(); } @@ -147,6 +161,7 @@ Controller.keyPressEvent.connect(function(event) { if (event.text == "SPACE" && !event.isAutoRepeat && !movingWithHead) { keyDownTime = 0.0; movingWithHead = true; + hipsToEyes = MyAvatar.getEyePosition().y - MyAvatar.position.y; headStartPosition = MyAvatar.getTrackedHeadPosition(); headStartDeltaPitch = MyAvatar.getHeadDeltaPitch(); headStartFinalPitch = MyAvatar.getHeadFinalPitch(); @@ -154,6 +169,7 @@ Controller.keyPressEvent.connect(function(event) { headStartYaw = MyAvatar.getHeadFinalYaw(); deltaYaw = 0.0; warpPosition = MyAvatar.position; + warpPosition.y += hipsToEyes; activateWarp(); } });