diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js new file mode 100644 index 0000000000..3dc6d072f6 --- /dev/null +++ b/examples/libraries/entityCameraTool.js @@ -0,0 +1,172 @@ +var MOUSE_SENSITIVITY = 0.5; +var SCROLL_SENSITIVITY = 0.05; +var PAN_ZOOM_SCALE_RATIO = 0.1; +var ZOOM_SCALING = 0.02; + +var MIN_ZOOM_DISTANCE = 0.01; +var MAX_ZOOM_DISTANCE = 100; + +var MODE_INACTIVE = null; +var MODE_ORBIT = 'orbit'; +var MODE_PAN = 'pan'; + +var INITIAL_ZOOM_DISTANCE = 4; + +EntityCameraTool = function() { + var that = {}; + + that.enabled = false; + that.mode = MODE_INACTIVE; + + that.zoomDistance = INITIAL_ZOOM_DISTANCE; + that.yaw = 0; + that.pitch = 0; + that.focalPoint = { x: 0, y: 0, z: 0 }; + + that.previousCameraMode = null; + + that.lastMousePosition = { x: 0, y: 0 }; + + that.enable = function() { + if (that.enabled) return; + that.enabled = true; + that.mode = MODE_INACTIVE; + + // Pick a point INITIAL_ZOOM_DISTANCE in front of the camera to use + // as a focal point + that.zoomDistance = INITIAL_ZOOM_DISTANCE; + var focalPoint = Vec3.sum(Camera.getPosition(), + Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); + + // 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); + + that.pitch = -Math.atan2(dPos.y, xzDist) * 180 / Math.PI; + that.yaw = Math.atan2(dPos.x, dPos.z) * 180 / Math.PI; + + that.setFocalPoint(focalPoint); + that.previousCameraMode = Camera.getMode(); + Camera.setMode("independent"); + + that.updateCamera(); + } + + that.disable = function() { + if (!that.enabled) return; + that.enabled = false; + that.mode = MODE_INACTIVE; + + Camera.setMode(that.previousCameraMode); + } + + that.focus = function(entityProperties) { + that.setFocalPoint(entityProperties.position); + + that.updateCamera(); + } + + that.moveFocalPoint = function(dPos) { + that.setFocalPoint(Vec3.sum(that.focalPoint, dPos)); + } + + that.setFocalPoint = function(pos) { + that.focalPoint = pos + that.updateCamera(); + } + + that.mouseMoveEvent = function(event) { + if (that.enabled && that.mode != MODE_INACTIVE) { + if (that.mode == MODE_ORBIT) { + var diffX = event.x - that.lastMousePosition.x; + var diffY = event.y - that.lastMousePosition.y; + that.yaw -= MOUSE_SENSITIVITY * (diffX / 5.0) + that.pitch += MOUSE_SENSITIVITY * (diffY / 10.0) + + while (that.yaw > 180.0) that.yaw -= 360; + while (that.yaw < -180.0) that.yaw += 360; + + if (that.pitch > 90) that.pitch = 90; + if (that.pitch < -90) that.pitch = -90; + + that.updateCamera(); + } else if (that.mode == MODE_PAN) { + var diffX = event.x - that.lastMousePosition.x; + var diffY = event.y - that.lastMousePosition.y; + + var up = Quat.getUp(Camera.getOrientation()); + var right = Quat.getRight(Camera.getOrientation()); + + up = Vec3.multiply(up, diffY * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance); + right = Vec3.multiply(right, -diffX * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance); + + var dPosition = Vec3.sum(up, right); + + that.moveFocalPoint(dPosition); + } + that.lastMousePosition.x = event.x; + that.lastMousePosition.y = event.y; + + return true; + } + return false; + } + + that.mousePressEvent = function(event) { + if (!that.enabled) return; + + if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) { + that.mode = MODE_ORBIT; + that.lastMousePosition.x = event.x; + that.lastMousePosition.y = event.y; + return true; + } else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) { + that.mode = MODE_PAN; + that.lastMousePosition.x = event.x; + that.lastMousePosition.y = event.y; + return true; + } + + return false; + } + + that.mouseReleaseEvent = function(event) { + if (!that.enabled) return; + + that.mode = MODE_INACTIVE; + } + + that.wheelEvent = function(event) { + if (!that.enabled) return; + + var dZoom = -event.delta * SCROLL_SENSITIVITY; + + // Scale based on current zoom level + dZoom *= that.zoomDistance * ZOOM_SCALING; + + that.zoomDistance = Math.max(Math.min(that.zoomDistance + dZoom, MAX_ZOOM_DISTANCE), MIN_ZOOM_DISTANCE); + + that.updateCamera(); + } + + that.updateCamera = function() { + if (!that.enabled) 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 }); + var q = Quat.multiply(yRot, xRot); + + var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance); + Camera.setPosition(Vec3.sum(that.focalPoint, pos)); + + yRot = Quat.angleAxis(that.yaw - 180, { x: 0, y: 1, z: 0 }); + xRot = Quat.angleAxis(-that.pitch, { x: 1, y: 0, z: 0 }); + q = Quat.multiply(yRot, xRot); + + Camera.setOrientation(q); + } + + Controller.wheelEvent.connect(that.wheelEvent); + + return that; +} diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 503d4415de..aa7e67a32e 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -17,6 +17,8 @@ SelectionDisplay = (function () { var that = {}; var MINIMUM_DIMENSION = 0.001; + + var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.015; var mode = "UNKNOWN"; var overlayNames = new Array(); @@ -174,6 +176,34 @@ SelectionDisplay = (function () { var grabberEdgeFR = Overlays.addOverlay("cube", grabberPropertiesEdge); var grabberEdgeFL = Overlays.addOverlay("cube", grabberPropertiesEdge); + var cornerEdgeFaceGrabbers = [ + grabberLBN, + grabberRBN, + grabberLBF, + grabberRBF, + grabberLTN, + grabberRTN, + grabberLTF, + grabberRTF, + grabberTOP, + grabberBOTTOM, + grabberLEFT, + grabberRIGHT, + grabberNEAR, + grabberFAR, + grabberEdgeTR, + grabberEdgeTL, + grabberEdgeTF, + grabberEdgeTN, + grabberEdgeBR, + grabberEdgeBL, + grabberEdgeBF, + grabberEdgeBN, + grabberEdgeNR, + grabberEdgeNL, + grabberEdgeFR, + grabberEdgeFL, + ]; var baseOverlayAngles = { x: 0, y: 0, z: 0 }; var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles); @@ -2423,6 +2453,19 @@ SelectionDisplay = (function () { return true; }; + that.updateHandleSizes = function() { + if (selectedEntityProperties) { + var diff = Vec3.subtract(selectedEntityProperties.position, Camera.getPosition()); + var grabberSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO; + for (var i = 0; i < cornerEdgeFaceGrabbers.length; i++) { + Overlays.editOverlay(cornerEdgeFaceGrabbers[i], { + size: grabberSize, + }); + } + } + } + Script.update.connect(that.updateHandleSizes); + that.mouseReleaseEvent = function(event) { var showHandles = false; // hide our rotation overlays..., and show our handles diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index e05416d5a7..756fbdb645 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -31,6 +31,9 @@ Script.include("libraries/ToolTip.js"); Script.include("libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; +Script.include("libraries/entityCameraTool.js"); +var entityCameraTool = new EntityCameraTool(); + var windowDimensions = Controller.getViewportDimensions(); var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; var toolHeight = 50; @@ -163,6 +166,9 @@ var toolBar = (function () { Overlays.editOverlay(loadFileMenuItem, { visible: active }); } + var RESIZE_INTERVAL = 50; + var RESIZE_TIMEOUT = 20000; + var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL; function addModel(url) { var position; @@ -176,6 +182,27 @@ var toolBar = (function () { modelURL: url }); print("Model added: " + url); + + var checkCount = 0; + function resize() { + var entityProperties = Entities.getEntityProperties(entityId); + var naturalDimensions = entityProperties.naturalDimensions; + + checkCount++; + + if (naturalDimensions.x == 0 && naturalDimensions.y == 0 && naturalDimensions.z == 0) { + if (checkCount < RESIZE_MAX_CHECKS) { + Script.setTimeout(resize, RESIZE_INTERVAL); + } else { + print("Resize failed: timed out waiting for model (" + url + ") to load"); + } + } else { + entityProperties.dimensions = naturalDimensions; + Entities.editEntity(entityId, entityProperties); + } + } + + Script.setTimeout(resize, RESIZE_INTERVAL); } else { print("Can't add model: Model would be out of bounds."); } @@ -217,6 +244,9 @@ var toolBar = (function () { isActive = !isActive; if (!isActive) { selectionDisplay.unselectAll(); + entityCameraTool.disable(); + } else { + entityCameraTool.enable(); } return true; } @@ -292,6 +322,7 @@ var toolBar = (function () { } + return false; }; @@ -339,7 +370,8 @@ function mousePressEvent(event) { mouseLastPosition = { x: event.x, y: event.y }; var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { + if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) + || entityCameraTool.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { // Event handled; do nothing. return; } else { @@ -442,8 +474,8 @@ function mouseMoveEvent(event) { return; } - // allow the selectionDisplay to handle the event first, if it doesn't handle it, then do our own thing - if (selectionDisplay.mouseMoveEvent(event)) { + // 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)) { return; } @@ -482,6 +514,7 @@ function mouseReleaseEvent(event) { if (entitySelected) { tooltip.show(false); } + entityCameraTool.mouseReleaseEvent(event); } Controller.mousePressEvent.connect(mousePressEvent); @@ -613,6 +646,10 @@ Controller.keyReleaseEvent.connect(function (event) { } if (event.text == "BACKSPACE") { handeMenuEvent("Delete"); + } else if (event.text == "f") { + if (entitySelected) { + entityCameraTool.focus(selectedEntityProperties); + } } });