From 640c2c5fbfaf045d605c5205bd6deb5ccb981058 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 4 Nov 2014 08:30:10 -0800 Subject: [PATCH 1/2] Add optional orientation easing to entity camera --- examples/libraries/entityCameraTool.js | 56 +++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index d591d33b6d..48abffb01e 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -34,6 +34,13 @@ var EASING_MULTIPLIER = 8; var INITIAL_ZOOM_DISTANCE = 2; var INITIAL_ZOOM_DISTANCE_FIRST_PERSON = 3; +var easeOutCubic = function(t) { + t--; + return t * t * t + 1; +}; + +EASE_TIME = 0.5; + CameraManager = function() { var that = {}; @@ -51,6 +58,10 @@ CameraManager = function() { that.focalPoint = { x: 0, y: 0, z: 0 }; that.targetFocalPoint = { x: 0, y: 0, z: 0 }; + easing = false; + easingTime = 0; + startOrientation = Quat.fromPitchYawRollDegrees(0, 0, 0); + that.previousCameraMode = null; that.lastMousePosition = { x: 0, y: 0 }; @@ -100,13 +111,35 @@ CameraManager = function() { cameraTool.setVisible(false); } - that.focus = function() { - var dim = SelectionManager.worldDimensions; - var size = Math.max(dim.x, Math.max(dim.y, dim.z)); + that.focus = function(position, dimensions, easeOrientation) { + if (dimensions) { + var size = Math.max(dimensions.x, Math.max(dimensions.y, dimensions.z)); + that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM); + } else { + that.targetZoomDistance = Vec3.length(Vec3.subtract(Camera.getPosition(), position)); + } - that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM); + if (easeOrientation) { + // Do eased turning towards target + that.focalPoint = that.targetFocalPoint = position; - that.setFocalPoint(SelectionManager.worldPosition); + that.zoomDistance = that.targetZoomDistance = Vec3.length(Vec3.subtract(Camera.getPosition(), position)); + + var dPos = Vec3.subtract(that.focalPoint, Camera.getPosition()); + var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z); + + that.targetPitch = -Math.atan2(dPos.y, xzDist) * 180 / Math.PI; + that.targetYaw = Math.atan2(dPos.x, dPos.z) * 180 / Math.PI; + that.pitch = that.targetPitch; + that.yaw = that.targetYaw; + + startOrientation = Camera.getOrientation(); + + easing = true; + easingTime = 0; + } else { + that.setFocalPoint(position); + } that.updateCamera(); } @@ -255,6 +288,11 @@ CameraManager = function() { xRot = Quat.angleAxis(-that.pitch, { x: 1, y: 0, z: 0 }); q = Quat.multiply(yRot, xRot); + if (easing) { + var t = easeOutCubic(easingTime / EASE_TIME); + q = Quat.slerp(startOrientation, q, t); + } + Camera.setOrientation(q); } @@ -270,6 +308,10 @@ CameraManager = function() { return; } + if (easing) { + easingTime = Math.min(EASE_TIME, easingTime + dt); + } + var scale = Math.min(dt * EASING_MULTIPLIER, 1.0); var dYaw = that.targetYaw - that.yaw; @@ -292,6 +334,10 @@ CameraManager = function() { that.zoomDistance += scale * dZoom; that.updateCamera(); + + if (easingTime >= 1) { + easing = false; + } } // Last mode that was first or third person From 185b9ac5453a1d12b7cc7c6c7f95d5defb45d69a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 4 Nov 2014 08:32:21 -0800 Subject: [PATCH 2/2] Add Inspect tool to newEditEntities --- examples/libraries/entityCameraTool.js | 4 - examples/newEditEntities.js | 230 ++++++++++++++----------- 2 files changed, 127 insertions(+), 107 deletions(-) diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index 48abffb01e..2f03cb28c8 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -77,10 +77,6 @@ CameraManager = function() { var focalPoint = Vec3.sum(Camera.getPosition(), Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); - if (Camera.getMode() == 'first person') { - that.targetZoomDistance = INITIAL_ZOOM_DISTANCE_FIRST_PERSON; - } - // Determine the correct yaw and pitch to keep the camera in the same location var dPos = Vec3.subtract(focalPoint, Camera.getPosition()); var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z); diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index d58f258e59..28d3eec660 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -51,6 +51,9 @@ var wantEntityGlow = false; var SPAWN_DISTANCE = 1; var DEFAULT_DIMENSION = 0.20; +var MENU_INSPECT_TOOL_ENABLED = 'Inspect Tool'; +var MENU_EASE_ON_FOCUS = 'Ease Orientation on Focus'; + var modelURLs = [ HIFI_PUBLIC_BUCKET + "meshes/Feisar_Ship.FBX", HIFI_PUBLIC_BUCKET + "meshes/birarda/birarda_head.fbx", @@ -387,7 +390,6 @@ function isLocked(properties) { var selectedEntityID; -var mouseLastPosition; var orientation; var intersection; @@ -401,142 +403,154 @@ function rayPlaneIntersection(pickRay, point, normal) { return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); } +function findClickedEntity(event) { + var pickRay = Camera.computePickRay(event.x, event.y); + + var foundIntersection = Entities.findRayIntersection(pickRay); + + if (!foundIntersection.accurate) { + return null; + } + var foundEntity = foundIntersection.entityID; + + if (!foundEntity.isKnownID) { + var identify = Entities.identifyEntity(foundEntity); + if (!identify.isKnownID) { + print("Unknown ID " + identify.id + " (update loop " + foundEntity.id + ")"); + selectionManager.clearSelections(); + return null; + } + foundEntity = identify; + } + + return { pickRay: pickRay, entityID: foundEntity }; +} + function mousePressEvent(event) { - mouseLastPosition = { x: event.x, y: event.y }; - var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - - var entitySelected = false; - if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) - || cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { - // Event handled; do nothing. + if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) { return; - } else { - - // If we aren't active and didn't click on an overlay: quit - if (!isActive) { + } + if (isActive) { + var entitySelected = false; + if (cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { + // Event handled; do nothing. return; - } - - var pickRay = Camera.computePickRay(event.x, event.y); - Vec3.print("[Mouse] Looking at: ", pickRay.origin); - var foundIntersection = Entities.findRayIntersection(pickRay); - - if(!foundIntersection.accurate) { - selectionManager.clearSelections(); - return; - } - var foundEntity = foundIntersection.entityID; - - if (!foundEntity.isKnownID) { - var identify = Entities.identifyEntity(foundEntity); - if (!identify.isKnownID) { - print("Unknown ID " + identify.id + " (update loop " + foundEntity.id + ")"); + } else { + var result = findClickedEntity(event); + if (result === null) { selectionManager.clearSelections(); return; } - foundEntity = identify; - } + var pickRay = result.pickRay; + var foundEntity = result.entityID; - var properties = Entities.getEntityProperties(foundEntity); - if (isLocked(properties)) { - print("Model locked " + properties.id); - } else { - var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; + var properties = Entities.getEntityProperties(foundEntity); + if (isLocked(properties)) { + print("Model locked " + properties.id); + } else { + var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; - print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal); - // P P - Model - // /| A - Palm - // / | d B - unit vector toward tip - // / | X - base of the perpendicular line - // A---X----->B d - distance fom axis - // x x - distance from A - // - // |X-A| = (P-A).B - // X == A + ((P-A).B)B - // d = |P-X| + print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal); + // P P - Model + // /| A - Palm + // / | d B - unit vector toward tip + // / | X - base of the perpendicular line + // A---X----->B d - distance fom axis + // x x - distance from A + // + // |X-A| = (P-A).B + // X == A + ((P-A).B)B + // d = |P-X| - var A = pickRay.origin; - var B = Vec3.normalize(pickRay.direction); - var P = properties.position; + var A = pickRay.origin; + var B = Vec3.normalize(pickRay.direction); + var P = properties.position; - var x = Vec3.dot(Vec3.subtract(P, A), B); - var X = Vec3.sum(A, Vec3.multiply(B, x)); - var d = Vec3.length(Vec3.subtract(P, X)); - var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; + var x = Vec3.dot(Vec3.subtract(P, A), B); + var X = Vec3.sum(A, Vec3.multiply(B, x)); + var d = Vec3.length(Vec3.subtract(P, X)); + var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; - var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; + var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) + && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); - if (0 < x && sizeOK) { - entitySelected = true; - selectedEntityID = foundEntity; - orientation = MyAvatar.orientation; - intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); + if (0 < x && sizeOK) { + entitySelected = true; + selectedEntityID = foundEntity; + orientation = MyAvatar.orientation; + intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); - if (!event.isShifted) { - selectionManager.clearSelections(); + if (!event.isShifted) { + selectionManager.clearSelections(); + } + selectionManager.addEntity(foundEntity); + + print("Model selected: " + foundEntity.id); } - selectionManager.addEntity(foundEntity); - - print("Model selected: " + foundEntity.id); } } - } - if (entitySelected) { - selectionDisplay.select(selectedEntityID, event); + if (entitySelected) { + selectionDisplay.select(selectedEntityID, event); + } + } else if (Menu.isOptionChecked(MENU_INSPECT_TOOL_ENABLED)) { + var result = findClickedEntity(event); + if (result !== null && event.isRightButton) { + var currentProperties = Entities.getEntityProperties(result.entityID); + cameraManager.enable(); + cameraManager.focus(currentProperties.position, null, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + cameraManager.mousePressEvent(event); + } } } var highlightedEntityID = { isKnownID: false }; function mouseMoveEvent(event) { - if (!isActive) { - return; - } - - // 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; - } - - var pickRay = Camera.computePickRay(event.x, event.y); - var entityIntersection = Entities.findRayIntersection(pickRay); - if (entityIntersection.accurate) { - if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) { - selectionDisplay.unhighlightSelectable(highlightedEntityID); - highlightedEntityID = { id: -1, isKnownID: false }; + if (isActive) { + // 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; } - var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0; - - var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), - entityIntersection.properties.position)) * 180 / 3.14; - - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); - - if (entityIntersection.entityID.isKnownID && sizeOK) { - if (wantEntityGlow) { - Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 }); + var pickRay = Camera.computePickRay(event.x, event.y); + var entityIntersection = Entities.findRayIntersection(pickRay); + if (entityIntersection.accurate) { + if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) { + selectionDisplay.unhighlightSelectable(highlightedEntityID); + highlightedEntityID = { id: -1, isKnownID: false }; } - highlightedEntityID = entityIntersection.entityID; - selectionDisplay.highlightSelectable(entityIntersection.entityID); - } + var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0; + + var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), + entityIntersection.properties.position)) * 180 / 3.14; + + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) + && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + + if (entityIntersection.entityID.isKnownID && sizeOK) { + if (wantEntityGlow) { + Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 }); + } + highlightedEntityID = entityIntersection.entityID; + selectionDisplay.highlightSelectable(entityIntersection.entityID); + } + + } + } else { + cameraManager.mouseMoveEvent(event); } } function mouseReleaseEvent(event) { - if (!isActive) { - return; - } - if (selectionManager.hasSelection()) { + if (isActive && selectionManager.hasSelection()) { tooltip.show(false); } + cameraManager.mouseReleaseEvent(event); } @@ -574,6 +588,9 @@ function setupModelMenus() { 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" }); Menu.addMenuItem({ menuName: "Developer", menuItemName: "Debug Ryans Rotation Problems", isCheckable: true }); + + Menu.addMenuItem({ menuName: "View", menuItemName: MENU_INSPECT_TOOL_ENABLED, afterItem: "Edit Entities Help...", isCheckable: true }); + Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED, isCheckable: true }); } setupModelMenus(); // do this when first running our script. @@ -594,6 +611,9 @@ function cleanupModelMenus() { Menu.removeMenuItem("File", "Export Models"); Menu.removeMenuItem("File", "Import Models"); Menu.removeMenuItem("Developer", "Debug Ryans Rotation Problems"); + + Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED); + Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS); } Script.scriptEnding.connect(function() { @@ -684,7 +704,11 @@ Controller.keyReleaseEvent.connect(function (event) { } else if (event.text == "TAB") { selectionDisplay.toggleSpaceMode(); } else if (event.text == "f") { - cameraManager.focus(); + if (isActive) { + cameraManager.focus(selectionManager.worldPosition, + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } } else if (event.text == '[') { if (isActive) { cameraManager.enable();