From 31b895058bc81ae817af1ac4f6f0af38f5f4de6f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 21 Feb 2016 09:30:26 -0800 Subject: [PATCH 1/9] depth reticle work --- interface/src/ui/ApplicationCompositor.cpp | 53 ++++++++++++++++++---- interface/src/ui/ApplicationCompositor.h | 9 ++++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 44f37f55c7..76bf48b45e 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -283,16 +283,51 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int //Mouse Pointer if (getReticleVisible()) { - glm::mat4 overlayXfm; - _modelTransform.getMatrix(overlayXfm); + if (_drawAt3D) { + //auto headPose = qApp->getHMDSensorPose(); + auto myCamera = qApp->getCamera(); + mat4 cameraMat = myCamera->getTransform(); + auto cameraOrientation = myCamera->getOrientation(); + auto UITransform = cameraMat * glm::inverse(headPose); + auto relativePosition4 = glm::inverse(UITransform) * vec4(_drawAt3DPosition, 1); + auto relativePosition = vec3(relativePosition4) / relativePosition4.w; + auto relativeDistance = glm::length(relativePosition); - auto reticlePosition = getReticlePosition(); - glm::vec2 projection = overlayToSpherical(reticlePosition); - float cursorDepth = getReticleDepth(); - mat4 pointerXfm = glm::scale(mat4(), vec3(cursorDepth)) * glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); - mat4 reticleXfm = overlayXfm * pointerXfm; - reticleXfm = glm::scale(reticleXfm, reticleScale); - batch.setModelTransform(reticleXfm); + // look at borrowed from overlays + float elevation = -asinf(relativePosition.y / glm::length(relativePosition)); + float azimuth = atan2f(relativePosition.x, relativePosition.z); + glm::quat faceCamera = glm::quat(glm::vec3(elevation, azimuth, 0)) * quat(vec3(0, 0, -1)); // this extra *quat(vec3(0,0,-1)) was required to get the quad to flip this seems like we could optimize + + qDebug() << "_drawAt3DPosition:" << _drawAt3DPosition; + qDebug() << "relativePosition:" << relativePosition; + qDebug() << "relativeDistance:" << relativeDistance; + + Transform transform; + transform.setTranslation(relativePosition); + transform.setScale(reticleScale); + transform.postScale(relativeDistance); // scale not quite working, distant things too large + transform.setRotation(faceCamera); + batch.setModelTransform(transform); + + /* + // this definitely doesn't work + mat4 pointerXfm = glm::scale(mat4(), vec3(relativeDistance)) * glm::mat4_cast(faceCamera) * glm::translate(mat4(), relativePosition); + batch.setModelTransform(pointerXfm); + */ + + } + else { + glm::mat4 overlayXfm; + _modelTransform.getMatrix(overlayXfm); + + auto reticlePosition = getReticlePosition(); + glm::vec2 projection = overlayToSpherical(reticlePosition); + float cursorDepth = getReticleDepth(); + mat4 pointerXfm = glm::scale(mat4(), vec3(cursorDepth)) * glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + } geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); } }); diff --git a/interface/src/ui/ApplicationCompositor.h b/interface/src/ui/ApplicationCompositor.h index 0566f63944..1fb37c3085 100644 --- a/interface/src/ui/ApplicationCompositor.h +++ b/interface/src/ui/ApplicationCompositor.h @@ -90,6 +90,9 @@ public: Q_INVOKABLE glm::vec2 getReticlePosition(); Q_INVOKABLE void setReticlePosition(glm::vec2 position, bool sendFakeEvent = true); + Q_INVOKABLE void setReticleApparentPosition(glm::vec3 position) { _drawAt3D = true; _drawAt3DPosition = position; } + Q_INVOKABLE void restoreReticleApparentPosition() { _drawAt3D = false; } + Q_INVOKABLE glm::vec2 getReticleMaximumPosition() const; ReticleInterface* getReticleInterface() { return _reticleInterface; } @@ -140,6 +143,8 @@ private: bool _reticleVisible { true }; float _reticleDepth { 1.0f }; + bool _drawAt3D { false }; + glm::vec3 _drawAt3DPosition; // NOTE: when the compositor is running in HMD mode, it will control the reticle position as a custom // application specific position, when it's in desktop mode, the reticle position will simply move @@ -174,6 +179,10 @@ public: Q_INVOKABLE void setPosition(glm::vec2 position) { _compositor->setReticlePosition(position); } Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } + + Q_INVOKABLE void setApparentPosition(glm::vec3 position) { _compositor->setReticleApparentPosition(position); } + Q_INVOKABLE void restoreApparentPosition() { _compositor->restoreReticleApparentPosition(); } + private: ApplicationCompositor* _compositor; }; From 3f03678d028fb0c660047f4f9e700ddc6514bfc9 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 22 Feb 2016 09:27:27 -0800 Subject: [PATCH 2/9] add version of edit that sets reticle depth --- examples/editWithHacks.js | 1910 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1910 insertions(+) create mode 100644 examples/editWithHacks.js diff --git a/examples/editWithHacks.js b/examples/editWithHacks.js new file mode 100644 index 0000000000..3504cddb96 --- /dev/null +++ b/examples/editWithHacks.js @@ -0,0 +1,1910 @@ +// newEditEntities.js +// examples +// +// Created by Brad Hefta-Gaub on 10/2/14. +// Persist toolbar by HRS 6/11/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +Script.include([ + "libraries/stringHelpers.js", + "libraries/dataViewHelpers.js", + "libraries/toolBars.js", + "libraries/progressDialog.js", + + "libraries/entitySelectionTool.js", + + "libraries/ToolTip.js", + + "libraries/entityCameraTool.js", + "libraries/gridTool.js", + "libraries/entityList.js", + "libraries/lightOverlayManager.js", +]); + +var selectionDisplay = SelectionDisplay; +var selectionManager = SelectionManager; + +var lightOverlayManager = new LightOverlayManager(); + +var cameraManager = new CameraManager(); + +var grid = Grid(); +// gridTool = GridTool({ +// horizontalGrid: grid +// }); +// gridTool.setVisible(false); + +var entityListTool = EntityListTool(); + +selectionManager.addEventListener(function() { + selectionDisplay.updateHandles(); + lightOverlayManager.updatePositions(); +}); + +var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; +var toolHeight = 50; +var toolWidth = 50; + +var DEGREES_TO_RADIANS = Math.PI / 180.0; +var RADIANS_TO_DEGREES = 180.0 / Math.PI; +var epsilon = 0.001; + +var MIN_ANGULAR_SIZE = 2; +var MAX_ANGULAR_SIZE = 45; +var allowLargeModels = true; +var allowSmallModels = true; +var wantEntityGlow = false; + +var SPAWN_DISTANCE = 1; +var DEFAULT_DIMENSION = 0.20; +var DEFAULT_TEXT_DIMENSION_X = 1.0; +var DEFAULT_TEXT_DIMENSION_Y = 1.0; +var DEFAULT_TEXT_DIMENSION_Z = 0.01; + +var DEFAULT_DIMENSIONS = { + x: DEFAULT_DIMENSION, + y: DEFAULT_DIMENSION, + z: DEFAULT_DIMENSION +}; + +var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS); + +var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select"; +var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; +var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode"; +var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Edit Mode"; + +var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled"; +var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect"; +var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus"; +var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode"; +var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode"; + +var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain." +var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain." + +var modelURLs = [ + "Insert the URL to your FBX" +]; + +var mode = 0; +var isActive = false; + +var placingEntityID = null; + +IMPORTING_SVO_OVERLAY_WIDTH = 144; +IMPORTING_SVO_OVERLAY_HEIGHT = 30; +IMPORTING_SVO_OVERLAY_MARGIN = 5; +IMPORTING_SVO_OVERLAY_LEFT_MARGIN = 34; +var importingSVOImageOverlay = Overlays.addOverlay("image", { + imageURL: HIFI_PUBLIC_BUCKET + "images/hourglass.svg", + width: 20, + height: 20, + alpha: 1.0, + color: { + red: 255, + green: 255, + blue: 255 + }, + x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH, + y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT, + visible: false, +}); +var importingSVOTextOverlay = Overlays.addOverlay("text", { + font: { + size: 14 + }, + text: "Importing SVO...", + leftMargin: IMPORTING_SVO_OVERLAY_LEFT_MARGIN, + x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH - IMPORTING_SVO_OVERLAY_MARGIN, + y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT - IMPORTING_SVO_OVERLAY_MARGIN, + width: IMPORTING_SVO_OVERLAY_WIDTH, + height: IMPORTING_SVO_OVERLAY_HEIGHT, + backgroundColor: { + red: 80, + green: 80, + blue: 80 + }, + backgroundAlpha: 0.7, + visible: false, +}); + +var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; +var marketplaceWindow = new OverlayWebWindow({ + title: 'Marketplace', + source: "about:blank", + width: 900, + height: 700, + visible: false +}); + +function showMarketplace(marketplaceID) { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + print("setting marketplace URL to " + url); + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); + marketplaceWindow.raise(); +} + +function hideMarketplace() { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); +} + +function toggleMarketplace() { + if (marketplaceWindow.visible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + +var toolBar = (function() { + var that = {}, + toolBar, + activeButton, + newModelButton, + newCubeButton, + newSphereButton, + newLightButton, + newTextButton, + newWebButton, + newZoneButton, + newPolyVoxButton; + + function initialize() { + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function(windowDimensions, toolbar) { + return { + x: windowDimensions.x - 8 - toolbar.width, + y: (windowDimensions.y - toolbar.height) / 2 + }; + }); + + + + activeButton = toolBar.addTool({ + imageURL: toolIconUrl + "edit-status.svg", + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: true + }, true, false); + + newModelButton = toolBar.addTool({ + imageURL: toolIconUrl + "upload.svg", + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + + newCubeButton = toolBar.addTool({ + imageURL: toolIconUrl + "add-cube.svg", + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + + newSphereButton = toolBar.addTool({ + imageURL: toolIconUrl + "add-sphere.svg", + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + + newLightButton = toolBar.addTool({ + imageURL: toolIconUrl + "light.svg", + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + + newTextButton = toolBar.addTool({ + imageURL: toolIconUrl + "add-text.svg", + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + + newWebButton = toolBar.addTool({ + imageURL: "https://hifi-public.s3.amazonaws.com/images/www.svg", + subImage: { + x: 0, + y: 0, + width: 128, + height: 128 + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + + newZoneButton = toolBar.addTool({ + imageURL: toolIconUrl + "zonecube_text.svg", + subImage: { + x: 0, + y: 128, + width: 128, + height: 128 + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + + newPolyVoxButton = toolBar.addTool({ + imageURL: toolIconUrl + "polyvox.svg", + subImage: { + x: 0, + y: 0, + width: 256, + height: 256 + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + + that.setActive(false); + } + + that.clearEntityList = function() { + entityListTool.clearEntityList(); + }; + + that.setActive = function(active) { + if (active != isActive) { + if (active && !Entities.canAdjustLocks()) { + Window.alert(INSUFFICIENT_PERMISSIONS_ERROR_MSG); + } else { + isActive = active; + if (!isActive) { + entityListTool.setVisible(false); + // gridTool.setVisible(false); + grid.setEnabled(false); + propertiesTool.setVisible(false); + selectionManager.clearSelections(); + cameraManager.disable(); + } else { + hasShownPropertiesTool = false; + entityListTool.setVisible(true); + // gridTool.setVisible(true); + grid.setEnabled(true); + propertiesTool.setVisible(true); + Window.setFocus(); + } + that.showTools(isActive); + } + } + toolBar.selectTool(activeButton, isActive); + lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); + Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); + }; + + // Sets visibility of tool buttons, excluding the power button + that.showTools = function(doShow) { + toolBar.showTool(newModelButton, doShow); + toolBar.showTool(newCubeButton, doShow); + toolBar.showTool(newSphereButton, doShow); + toolBar.showTool(newLightButton, doShow); + toolBar.showTool(newTextButton, doShow); + toolBar.showTool(newWebButton, doShow); + toolBar.showTool(newZoneButton, doShow); + toolBar.showTool(newPolyVoxButton, doShow); + }; + + var RESIZE_INTERVAL = 50; + var RESIZE_TIMEOUT = 120000; // 2 minutes + var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL; + + function addModel(url) { + var entityID = createNewEntity({ + type: "Model", + modelURL: url + }, false); + + if (entityID) { + print("Model added: " + url); + selectionManager.setSelections([entityID]); + } + } + + function createNewEntity(properties, dragOnCreate) { + // Default to true if not passed in + dragOnCreate = dragOnCreate == undefined ? true : dragOnCreate; + + var dimensions = properties.dimensions ? properties.dimensions : DEFAULT_DIMENSIONS; + var position = getPositionToCreateEntity(); + var entityID = null; + if (position != null) { + position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions), + properties.position = position; + + entityID = Entities.addEntity(properties); + if (dragOnCreate) { + placingEntityID = entityID; + } + } else { + Window.alert("Can't create " + properties.type + ": " + properties.type + " would be out of bounds."); + } + + return entityID; + } + + var newModelButtonDown = false; + that.mousePressEvent = function(event) { + var clickedOverlay, + url, + file; + + if (!event.isLeftButton) { + // if another mouse button than left is pressed ignore it + return false; + } + + clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + + if (activeButton === toolBar.clicked(clickedOverlay)) { + that.setActive(!isActive); + return true; + } + + // Handle these two buttons in the mouseRelease event handler so that we don't suppress a mouseRelease event from + // occurring when showing a modal dialog. + if (newModelButton === toolBar.clicked(clickedOverlay)) { + newModelButtonDown = true; + return true; + } + + + if (newCubeButton === toolBar.clicked(clickedOverlay)) { + createNewEntity({ + type: "Box", + dimensions: DEFAULT_DIMENSIONS, + color: { + red: 255, + green: 0, + blue: 0 + } + }); + + return true; + } + + if (newSphereButton === toolBar.clicked(clickedOverlay)) { + createNewEntity({ + type: "Sphere", + dimensions: DEFAULT_DIMENSIONS, + color: { + red: 255, + green: 0, + blue: 0 + } + }); + + return true; + } + + if (newLightButton === toolBar.clicked(clickedOverlay)) { + createNewEntity({ + type: "Light", + dimensions: DEFAULT_LIGHT_DIMENSIONS, + isSpotlight: false, + color: { + red: 150, + green: 150, + blue: 150 + }, + + constantAttenuation: 1, + linearAttenuation: 0, + quadraticAttenuation: 0, + exponent: 0, + cutoff: 180, // in degrees + }); + + return true; + } + + if (newTextButton === toolBar.clicked(clickedOverlay)) { + createNewEntity({ + type: "Text", + dimensions: { + x: 0.65, + y: 0.3, + z: 0.01 + }, + backgroundColor: { + red: 64, + green: 64, + blue: 64 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, + text: "some text", + lineHeight: 0.06 + }); + + return true; + } + + if (newWebButton === toolBar.clicked(clickedOverlay)) { + createNewEntity({ + type: "Web", + dimensions: { + x: 1.6, + y: 0.9, + z: 0.01 + }, + sourceUrl: "https://highfidelity.com/", + }); + + return true; + } + + if (newZoneButton === toolBar.clicked(clickedOverlay)) { + createNewEntity({ + type: "Zone", + dimensions: { + x: 10, + y: 10, + z: 10 + }, + }); + + return true; + } + + if (newPolyVoxButton === toolBar.clicked(clickedOverlay)) { + var polyVoxId = createNewEntity({ + type: "PolyVox", + dimensions: { + x: 10, + y: 10, + z: 10 + }, + voxelVolumeSize: { + x: 16, + y: 16, + z: 16 + }, + voxelSurfaceStyle: 2 + }); + for (var x = 1; x <= 14; x++) { + Entities.setVoxel(polyVoxId, { + x: x, + y: 1, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 14, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 1, + z: 14 + }, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 14, + z: 14 + }, 255); + } + for (var y = 2; y <= 13; y++) { + Entities.setVoxel(polyVoxId, { + x: 1, + y: y, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: y, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: y, + z: 14 + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: y, + z: 14 + }, 255); + } + for (var z = 2; z <= 13; z++) { + Entities.setVoxel(polyVoxId, { + x: 1, + y: 1, + z: z + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: 1, + z: z + }, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: 14, + z: z + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: 14, + z: z + }, 255); + } + + + return true; + } + + return false; + }; + + that.mouseReleaseEvent = function(event) { + var handled = false; + if (newModelButtonDown) { + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + if (newModelButton === toolBar.clicked(clickedOverlay)) { + url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]); + if (url !== null && url !== "") { + addModel(url); + } + handled = true; + } + } + + newModelButtonDown = false; + + + return handled; + } + + Window.domainChanged.connect(function() { + that.setActive(false); + that.clearEntityList(); + }); + + Entities.canAdjustLocksChanged.connect(function(canAdjustLocks) { + if (isActive && !canAdjustLocks) { + that.setActive(false); + } + }); + + that.cleanup = function() { + toolBar.cleanup(); + }; + + initialize(); + return that; +}()); + + +function isLocked(properties) { + // special case to lock the ground plane model in hq. + if (location.hostname == "hq.highfidelity.io" && + properties.modelURL == HIFI_PUBLIC_BUCKET + "ozan/Terrain_Reduce_forAlpha.fbx") { + return true; + } + return false; +} + + +var selectedEntityID; +var orientation; +var intersection; + + +var SCALE_FACTOR = 200.0; + +function rayPlaneIntersection(pickRay, point, normal) { + var d = -Vec3.dot(point, normal); + var t = -(Vec3.dot(pickRay.origin, normal) + d) / Vec3.dot(pickRay.direction, normal); + + return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); +} + +function findClickedEntity(event) { + var pickZones = event.isControl; + + if (pickZones) { + Entities.setZonesArePickable(true); + } + + var pickRay = Camera.computePickRay(event.x, event.y); + + var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking + var lightResult = lightOverlayManager.findRayIntersection(pickRay); + lightResult.accurate = true; + + if (pickZones) { + Entities.setZonesArePickable(false); + } + + var result; + + if (!entityResult.intersects && !lightResult.intersects) { + return null; + } else if (entityResult.intersects && !lightResult.intersects) { + result = entityResult; + } else if (!entityResult.intersects && lightResult.intersects) { + result = lightResult; + } else { + if (entityResult.distance < lightResult.distance) { + result = entityResult; + } else { + result = lightResult; + } + } + + if (!result.accurate) { + return null; + } + + var foundEntity = result.entityID; + return { + pickRay: pickRay, + entityID: foundEntity + }; +} + +var mouseHasMovedSincePress = false; +var mousePressStartTime = 0; +var mousePressStartPosition = { + x: 0, + y: 0 +}; +var mouseDown = false; + +function mousePressEvent(event) { + mouseDown = true; + mousePressStartPosition = { + x: event.x, + y: event.y + }; + mousePressStartTime = Date.now(); + mouseHasMovedSincePress = false; + mouseCapturedByTool = false; + + if (propertyMenu.mousePressEvent(event) || toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) { + mouseCapturedByTool = true; + return; + } + if (isActive) { + if (cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { + // Event handled; do nothing. + return; + } + } +} + +var highlightedEntityID = null; +var mouseCapturedByTool = false; +var lastMousePosition = null; +var idleMouseTimerId = null; +var CLICK_TIME_THRESHOLD = 500 * 1000; // 500 ms +var CLICK_MOVE_DISTANCE_THRESHOLD = 8; +var IDLE_MOUSE_TIMEOUT = 200; +var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0; + +var lastMouseMoveEvent = null; + +function mouseMoveEventBuffered(event) { + lastMouseMoveEvent = event; +} + + + +function mouseMove(event) { + if (mouseDown && !mouseHasMovedSincePress) { + var timeSincePressMicro = Date.now() - mousePressStartTime; + + var dX = mousePressStartPosition.x - event.x; + var dY = mousePressStartPosition.y - event.y; + var sqDist = (dX * dX) + (dY * dY); + + // If less than CLICK_TIME_THRESHOLD has passed since the mouse click AND the mouse has moved + // less than CLICK_MOVE_DISTANCE_THRESHOLD distance, then don't register this as a mouse move + // yet. The goal is to provide mouse clicks that are more lenient to small movements. + if (timeSincePressMicro < CLICK_TIME_THRESHOLD && sqDist < CLICK_MOVE_DISTANCE_THRESHOLD) { + return; + } + mouseHasMovedSincePress = true; + } + + if (placingEntityID) { + var pickRay = Camera.computePickRay(event.x, event.y); + var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE; + var offset = Vec3.multiply(distance, pickRay.direction); + var position = Vec3.sum(Camera.position, offset); + Entities.editEntity(placingEntityID, { + position: position, + }); + return; + } + if (!isActive) { + return; + } + if (idleMouseTimerId) { + Script.clearTimeout(idleMouseTimerId); + } + + // 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) || propertyMenu.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) { + return; + } + + lastMousePosition = { + x: event.x, + y: event.y + }; + + idleMouseTimerId = Script.setTimeout(handleIdleMouse, IDLE_MOUSE_TIMEOUT); + + + // brad's hack to move an overlay to the mouse + //Overlays.editOverlay(pointerOverlay, { position: lookAt3D }); + /* + var pickRay = Camera.computePickRay(event.x, event.y); + var result = Overlays.findRayIntersection(pickRay); + print(JSON.stringify(result)); + if (result.intersects) { + Overlays.editOverlay(pointerOverlay, { position: result.intersection }); + Reticle.setVisible(false); + print("intersects..."); + } else { + var lookAt3D = HMD.getHUDLookAtPosition3D(); + Overlays.editOverlay(pointerOverlay, { position: { x: 0, y: 0, z: 0 } }); + Reticle.setVisible(true); + print("NO INTERSECTION..."); + } + */ +} + +function handleIdleMouse() { + idleMouseTimerId = null; +} + +function highlightEntityUnderCursor(position, accurateRay) { + var pickRay = Camera.computePickRay(position.x, position.y); + var entityIntersection = Entities.findRayIntersection(pickRay, accurateRay); + if (entityIntersection.accurate) { + if (highlightedEntityID && highlightedEntityID != entityIntersection.entityID) { + selectionDisplay.unhighlightSelectable(highlightedEntityID); + highlightedEntityID = { + id: -1 + }; + } + + 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 && sizeOK) { + if (wantEntityGlow) { + Entities.editEntity(entityIntersection.entityID, { + glowLevel: 0.25 + }); + } + highlightedEntityID = entityIntersection.entityID; + selectionDisplay.highlightSelectable(entityIntersection.entityID); + } + + } +} + + +function mouseReleaseEvent(event) { + mouseDown = false; + + if (lastMouseMoveEvent) { + mouseMove(lastMouseMoveEvent); + lastMouseMoveEvent = null; + } + if (propertyMenu.mouseReleaseEvent(event) || toolBar.mouseReleaseEvent(event)) { + + return true; + } + if (placingEntityID) { + + if (isActive) { + + selectionManager.setSelections([placingEntityID]); + } + placingEntityID = null; + } + if (isActive && selectionManager.hasSelection()) { + + tooltip.show(false); + } + if (mouseCapturedByTool) { + + return; + } + + cameraManager.mouseReleaseEvent(event); + + if (!mouseHasMovedSincePress) { + mouseClickEvent(event); + } +} + +function mouseClickEvent(event) { + if (isActive && event.isLeftButton) { + var result = findClickedEntity(event); + if (result === null) { + if (!event.isShifted) { + selectionManager.clearSelections(); + } + return; + } + toolBar.setActive(true); + 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; + + print("Checking properties: " + properties.id + " " + " - 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 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 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 (!event.isShifted) { + selectionManager.setSelections([foundEntity]); + } else { + selectionManager.addEntity(foundEntity, true); + } + + print("Model selected: " + foundEntity); + selectionDisplay.select(selectedEntityID, event); + + if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) { + cameraManager.enable(); + cameraManager.focus(selectionManager.worldPosition, + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } + } + } + } else if (event.isRightButton) { + var result = findClickedEntity(event); + if (result) { + var properties = Entities.getEntityProperties(result.entityID); + if (properties.marketplaceID) { + propertyMenu.marketplaceID = properties.marketplaceID; + propertyMenu.updateMenuItemText(showMenuItem, "Show in Marketplace"); + } else { + propertyMenu.marketplaceID = null; + propertyMenu.updateMenuItemText(showMenuItem, "No marketplace info"); + } + propertyMenu.setPosition(event.x, event.y); + propertyMenu.show(); + } else { + propertyMenu.hide(); + } + } +} + +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseMoveEvent.connect(mouseMoveEventBuffered); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + + +// In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already +// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that +// added it. +var modelMenuAddedDelete = false; +var originalLightsArePickable = Entities.getLightsArePickable(); + +function setupModelMenus() { + print("setupModelMenus()"); + // adj our menuitems + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Entities", + isSeparator: true, + grouping: "Advanced" + }); + if (!Menu.menuItemExists("Edit", "Delete")) { + print("no delete... adding ours"); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Delete", + shortcutKeyEvent: { + text: "backspace" + }, + afterItem: "Entities", + grouping: "Advanced" + }); + modelMenuAddedDelete = true; + } else { + print("delete exists... don't add ours"); + } + + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Entity List...", + shortcutKey: "CTRL+META+L", + afterItem: "Entities", + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Large Models", + shortcutKey: "CTRL+META+L", + afterItem: "Entity List...", + isCheckable: true, + isChecked: true, + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Small Models", + shortcutKey: "CTRL+META+S", + afterItem: "Allow Selecting of Large Models", + isCheckable: true, + isChecked: true, + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Lights", + shortcutKey: "CTRL+SHIFT+META+L", + afterItem: "Allow Selecting of Small Models", + isCheckable: true, + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Select All Entities In Box", + shortcutKey: "CTRL+SHIFT+META+A", + afterItem: "Allow Selecting of Lights", + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Select All Entities Touching Box", + shortcutKey: "CTRL+SHIFT+META+T", + afterItem: "Select All Entities In Box", + grouping: "Advanced" + }); + + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Export Entities", + shortcutKey: "CTRL+META+E", + afterItem: "Entities", + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Import Entities", + shortcutKey: "CTRL+META+I", + afterItem: "Export Entities", + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Import Entities from URL", + shortcutKey: "CTRL+META+U", + afterItem: "Import Entities", + grouping: "Advanced" + }); + + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: MENU_AUTO_FOCUS_ON_SELECT, + isCheckable: true, + isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true", + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: MENU_EASE_ON_FOCUS, + afterItem: MENU_AUTO_FOCUS_ON_SELECT, + isCheckable: true, + isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true", + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + afterItem: MENU_EASE_ON_FOCUS, + isCheckable: true, + isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true", + grouping: "Advanced" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, + afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + isCheckable: true, + isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true", + grouping: "Advanced" + }); + + Entities.setLightsArePickable(false); +} + +setupModelMenus(); // do this when first running our script. + +function cleanupModelMenus() { + Menu.removeSeparator("Edit", "Entities"); + if (modelMenuAddedDelete) { + // delete our menuitems + Menu.removeMenuItem("Edit", "Delete"); + } + + Menu.removeMenuItem("Edit", "Entity List..."); + Menu.removeMenuItem("Edit", "Allow Selecting of Large Models"); + Menu.removeMenuItem("Edit", "Allow Selecting of Small Models"); + Menu.removeMenuItem("Edit", "Allow Selecting of Lights"); + Menu.removeMenuItem("Edit", "Select All Entities In Box"); + Menu.removeMenuItem("Edit", "Select All Entities Touching Box"); + + Menu.removeMenuItem("Edit", "Export Entities"); + Menu.removeMenuItem("Edit", "Import Entities"); + Menu.removeMenuItem("Edit", "Import Entities from URL"); + + Menu.removeMenuItem("Edit", MENU_AUTO_FOCUS_ON_SELECT); + Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS); + Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_IN_EDIT_MODE); + Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE); +} + +Script.scriptEnding.connect(function() { + Settings.setValue(SETTING_AUTO_FOCUS_ON_SELECT, Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)); + Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + Settings.setValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); + Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); + + progressDialog.cleanup(); + toolBar.cleanup(); + cleanupModelMenus(); + tooltip.cleanup(); + selectionDisplay.cleanup(); + Entities.setLightsArePickable(originalLightsArePickable); + + Overlays.deleteOverlay(importingSVOImageOverlay); + Overlays.deleteOverlay(importingSVOTextOverlay); +}); + +var intersectionBeaconPosition = { x: 0, y: 0, z: 0 }; +var intersectionBeaconSize = 0.03; +var intersectionBeacon = Overlays.addOverlay("sphere", { + position: intersectionBeaconPosition, + size: intersectionBeaconSize, + color: { red: 255, green: 0, blue: 0}, + alpha: 1, + solid: true, + ignoreRayIntersection: true, // this never ray intersects + }); + +Script.scriptEnding.connect(function(){ + Overlays.deleteOverlay(intersectionBeacon); +}); + + +var lastOrientation = null; +var lastPosition = null; +var lastDepthCheckTime = 0; + +// Do some stuff regularly, like check for placement of various overlays +Script.update.connect(function(deltaTime) { + progressDialog.move(); + selectionDisplay.checkMove(); + var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1); + var dPosition = Vec3.distance(Camera.position, lastPosition); + if (dOrientation > 0.001 || dPosition > 0.001) { + propertyMenu.hide(); + lastOrientation = Camera.orientation; + lastPosition = Camera.position; + } + if (lastMouseMoveEvent) { + mouseMove(lastMouseMoveEvent); + var TIME_BETWEEN_DEPTH_CHECKS = 100; + var timeSinceLastDepthCheck = Date.now() - lastDepthCheckTime; + if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) { + var pickRay = Camera.computePickRay(lastMouseMoveEvent.x, lastMouseMoveEvent.y); + var result = Overlays.findRayIntersection(pickRay); + + if (!result.intersects) { + result = Entities.findRayIntersection(pickRay, true); + } + if (result.intersects) { + // + JSON.stringify(result) + print("something hovered!! result.distance:" +result.distance); + Vec3.print("something hovered!! result.intersection:", result.intersection); + //Reticle.setDepth(result.distance); + Reticle.setApparentPosition(result.intersection); + Overlays.editOverlay(intersectionBeacon, { position: result.intersection, visible: false }); + + } else { + //Reticle.setDepth(1.0); + Reticle.restoreApparentPosition(); + print("NO INTERSECTION..."); + var pointAt2D = Reticle.position; + var pointAt3D = HMD.worldPointFromOverlay(pointAt2D); + Overlays.editOverlay(intersectionBeacon, { position: pointAt3D, visible: false }); + } + + } + lastMouseMoveEvent = null; + } +}); + +function insideBox(center, dimensions, point) { + return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0)) && (Math.abs(point.y - center.y) <= (dimensions.y / 2.0)) && (Math.abs(point.z - center.z) <= (dimensions.z / 2.0)); +} + +function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { + if (selectionManager.hasSelection()) { + // Get all entities touching the bounding box of the current selection + var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition, + Vec3.multiply(selectionManager.worldDimensions, 0.5)); + var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions); + + if (!keepIfTouching) { + var isValid; + if (selectionManager.localPosition === null) { + isValid = function(position) { + return insideBox(selectionManager.worldPosition, selectionManager.worldDimensions, position); + } + } else { + isValid = function(position) { + var localPosition = Vec3.multiplyQbyV(Quat.inverse(selectionManager.localRotation), + Vec3.subtract(position, + selectionManager.localPosition)); + return insideBox({ + x: 0, + y: 0, + z: 0 + }, selectionManager.localDimensions, localPosition); + } + } + for (var i = 0; i < entities.length; ++i) { + var properties = Entities.getEntityProperties(entities[i]); + if (!isValid(properties.position)) { + entities.splice(i, 1); + --i; + } + } + } + selectionManager.setSelections(entities); + } +} + +function deleteSelectedEntities() { + if (SelectionManager.hasSelection()) { + print(" Delete Entities"); + SelectionManager.saveProperties(); + var savedProperties = []; + for (var i = 0; i < selectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID]; + SelectionManager.savedProperties[entityID]; + savedProperties.push({ + entityID: entityID, + properties: initialProperties + }); + Entities.deleteEntity(entityID); + } + SelectionManager.clearSelections(); + pushCommandForSelections([], savedProperties); + } else { + print(" Delete Entity.... not holding..."); + } +} + +function handeMenuEvent(menuItem) { + if (menuItem == "Allow Selecting of Small Models") { + allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); + } else if (menuItem == "Allow Selecting of Large Models") { + allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models"); + } else if (menuItem == "Allow Selecting of Lights") { + Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights")); + } else if (menuItem == "Delete") { + deleteSelectedEntities(); + } else if (menuItem == "Export Entities") { + if (!selectionManager.hasSelection()) { + Window.alert("No entities have been selected."); + } else { + var filename = "entities__" + Window.location.hostname + ".svo.json"; + filename = Window.save("Select where to save", filename, "*.json") + if (filename) { + var success = Clipboard.exportEntities(filename, selectionManager.selections); + if (!success) { + Window.alert("Export failed."); + } + } + } + } else if (menuItem == "Import Entities" || menuItem == "Import Entities from URL") { + + var importURL = null; + if (menuItem == "Import Entities") { + var fullPath = Window.browse("Select models to import", "", "*.json"); + if (fullPath) { + importURL = "file:///" + fullPath; + } + } else { + importURL = Window.prompt("URL of SVO to import", ""); + } + + if (importURL) { + importSVO(importURL); + } + } else if (menuItem == "Entity List...") { + entityListTool.toggleVisible(); + } else if (menuItem == "Select All Entities In Box") { + selectAllEtitiesInCurrentSelectionBox(false); + } else if (menuItem == "Select All Entities Touching Box") { + selectAllEtitiesInCurrentSelectionBox(true); + } else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) { + lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); + } else if (menuItem == MENU_SHOW_ZONES_IN_EDIT_MODE) { + Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); + } + tooltip.show(false); +} + +// This function tries to find a reasonable position to place a new entity based on the camera +// position. If a reasonable position within the world bounds can't be found, `null` will +// be returned. The returned position will also take into account grid snapping settings. +function getPositionToCreateEntity() { + var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE; + var direction = Quat.getFront(Camera.orientation); + var offset = Vec3.multiply(distance, direction); + var placementPosition = Vec3.sum(Camera.position, offset); + + var cameraPosition = Camera.position; + + var HALF_TREE_SCALE = 16384; + + var cameraOutOfBounds = Math.abs(cameraPosition.x) > HALF_TREE_SCALE || Math.abs(cameraPosition.y) > HALF_TREE_SCALE || Math.abs(cameraPosition.z) > HALF_TREE_SCALE; + var placementOutOfBounds = Math.abs(placementPosition.x) > HALF_TREE_SCALE || Math.abs(placementPosition.y) > HALF_TREE_SCALE || Math.abs(placementPosition.z) > HALF_TREE_SCALE; + + if (cameraOutOfBounds && placementOutOfBounds) { + return null; + } + + placementPosition.x = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.x)); + placementPosition.y = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.y)); + placementPosition.z = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.z)); + + return placementPosition; +} + +function importSVO(importURL) { + print("Import URL requested: " + importURL) + if (!Entities.canAdjustLocks()) { + Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); + return; + } + + Overlays.editOverlay(importingSVOTextOverlay, { + visible: true + }); + Overlays.editOverlay(importingSVOImageOverlay, { + visible: true + }); + + var success = Clipboard.importEntities(importURL); + + if (success) { + var VERY_LARGE = 10000; + var position = { + x: 0, + y: 0, + z: 0 + }; + if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) { + position = getPositionToCreateEntity(); + } + if (position != null) { + var pastedEntityIDs = Clipboard.pasteEntities(position); + + if (isActive) { + selectionManager.setSelections(pastedEntityIDs); + } + + Window.raiseMainWindow(); + } else { + Window.alert("Can't import objects: objects would be out of bounds."); + } + } else { + Window.alert("There was an error importing the entity file."); + } + + Overlays.editOverlay(importingSVOTextOverlay, { + visible: false + }); + Overlays.editOverlay(importingSVOImageOverlay, { + visible: false + }); +} +Window.svoImportRequested.connect(importSVO); + +Menu.menuItemEvent.connect(handeMenuEvent); + +Controller.keyPressEvent.connect(function(event) { + if (isActive) { + cameraManager.keyPressEvent(event); + } +}); + +Controller.keyReleaseEvent.connect(function(event) { + if (isActive) { + cameraManager.keyReleaseEvent(event); + } + // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items + if (event.text == "BACKSPACE" || event.text == "DELETE") { + deleteSelectedEntities(); + } else if (event.text == "ESC") { + selectionManager.clearSelections(); + } else if (event.text == "TAB") { + selectionDisplay.toggleSpaceMode(); + } else if (event.text == "f") { + if (isActive) { + if (selectionManager.hasSelection()) { + cameraManager.enable(); + cameraManager.focus(selectionManager.worldPosition, + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } + } + } else if (event.text == '[') { + if (isActive) { + cameraManager.enable(); + } + } else if (event.text == 'g') { + if (isActive && selectionManager.hasSelection()) { + var newPosition = selectionManager.worldPosition; + newPosition = Vec3.subtract(newPosition, { + x: 0, + y: selectionManager.worldDimensions.y * 0.5, + z: 0 + }); + grid.setPosition(newPosition); + } + } +}); + +// When an entity has been deleted we need a way to "undo" this deletion. Because it's not currently +// possible to create an entity with a specific id, earlier undo commands to the deleted entity +// will fail if there isn't a way to find the new entity id. +DELETED_ENTITY_MAP = {} + +function applyEntityProperties(data) { + var properties = data.setProperties; + var selectedEntityIDs = []; + for (var i = 0; i < properties.length; i++) { + var entityID = properties[i].entityID; + if (DELETED_ENTITY_MAP[entityID] !== undefined) { + entityID = DELETED_ENTITY_MAP[entityID]; + } + Entities.editEntity(entityID, properties[i].properties); + selectedEntityIDs.push(entityID); + } + for (var i = 0; i < data.createEntities.length; i++) { + var entityID = data.createEntities[i].entityID; + var properties = data.createEntities[i].properties; + var newEntityID = Entities.addEntity(properties); + DELETED_ENTITY_MAP[entityID] = newEntityID; + if (data.selectCreated) { + selectedEntityIDs.push(newEntityID); + } + } + for (var i = 0; i < data.deleteEntities.length; i++) { + var entityID = data.deleteEntities[i].entityID; + if (DELETED_ENTITY_MAP[entityID] !== undefined) { + entityID = DELETED_ENTITY_MAP[entityID]; + } + Entities.deleteEntity(entityID); + } + + selectionManager.setSelections(selectedEntityIDs); +}; + +// For currently selected entities, push a command to the UndoStack that uses the current entity properties for the +// redo command, and the saved properties for the undo command. Also, include create and delete entity data. +function pushCommandForSelections(createdEntityData, deletedEntityData) { + var undoData = { + setProperties: [], + createEntities: deletedEntityData || [], + deleteEntities: createdEntityData || [], + selectCreated: true, + }; + var redoData = { + setProperties: [], + createEntities: createdEntityData || [], + deleteEntities: deletedEntityData || [], + selectCreated: false, + }; + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID]; + var currentProperties = Entities.getEntityProperties(entityID); + undoData.setProperties.push({ + entityID: entityID, + properties: { + position: initialProperties.position, + rotation: initialProperties.rotation, + dimensions: initialProperties.dimensions, + }, + }); + redoData.setProperties.push({ + entityID: entityID, + properties: { + position: currentProperties.position, + rotation: currentProperties.rotation, + dimensions: currentProperties.dimensions, + }, + }); + } + UndoStack.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData); +} + +PropertiesTool = function(opts) { + var that = {}; + + var url = Script.resolvePath('html/entityProperties.html'); + var webView = new OverlayWebWindow({ + title: 'Entity Properties', + source: url, + toolWindow: true + }); + + var visible = false; + + webView.setVisible(visible); + + that.setVisible = function(newVisible) { + visible = newVisible; + webView.setVisible(visible); + }; + + selectionManager.addEventListener(function() { + data = { + type: 'update', + }; + var selections = []; + for (var i = 0; i < selectionManager.selections.length; i++) { + var entity = {}; + entity.id = selectionManager.selections[i]; + entity.properties = Entities.getEntityProperties(selectionManager.selections[i]); + if (entity.properties.rotation !== undefined) { + entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); + } + if (entity.properties.keyLight !== undefined && entity.properties.keyLight.direction !== undefined) { + entity.properties.keyLight.direction = Vec3.multiply(RADIANS_TO_DEGREES, Vec3.toPolar(entity.properties.keyLight.direction)); + entity.properties.keyLight.direction.z = 0.0; + } + selections.push(entity); + } + data.selections = selections; + webView.eventBridge.emitScriptEvent(JSON.stringify(data)); + }); + + webView.eventBridge.webEventReceived.connect(function(data) { + data = JSON.parse(data); + if (data.type == "print") { + if (data.message) { + print(data.message); + } + } else if (data.type == "update") { + selectionManager.saveProperties(); + if (selectionManager.selections.length > 1) { + properties = { + locked: data.properties.locked, + visible: data.properties.visible, + }; + for (var i = 0; i < selectionManager.selections.length; i++) { + Entities.editEntity(selectionManager.selections[i], properties); + } + } else { + if (data.properties.dynamic === false) { + // this object is leaving dynamic, so we zero its velocities + data.properties["velocity"] = {x: 0, y: 0, z: 0}; + data.properties["angularVelocity"] = {x: 0, y: 0, z: 0}; + } + if (data.properties.rotation !== undefined) { + var rotation = data.properties.rotation; + data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); + } + if (data.properties.keyLight !== undefined && data.properties.keyLight.direction !== undefined) { + data.properties.keyLight.direction = Vec3.fromPolar( + data.properties.keyLight.direction.x * DEGREES_TO_RADIANS, data.properties.keyLight.direction.y * DEGREES_TO_RADIANS); + } + Entities.editEntity(selectionManager.selections[0], data.properties); + if (data.properties.name != undefined) { + entityListTool.sendUpdate(); + } + } + pushCommandForSelections(); + selectionManager._update(); + } else if (data.type == "showMarketplace") { + showMarketplace(); + } else if (data.type == "action") { + if (data.action == "moveSelectionToGrid") { + if (selectionManager.hasSelection()) { + selectionManager.saveProperties(); + var dY = grid.getOrigin().y - (selectionManager.worldPosition.y - selectionManager.worldDimensions.y / 2); + var diff = { + x: 0, + y: dY, + z: 0 + }; + for (var i = 0; i < selectionManager.selections.length; i++) { + var properties = selectionManager.savedProperties[selectionManager.selections[i]]; + var newPosition = Vec3.sum(properties.position, diff); + Entities.editEntity(selectionManager.selections[i], { + position: newPosition, + }); + } + pushCommandForSelections(); + selectionManager._update(); + } + } else if (data.action == "moveAllToGrid") { + if (selectionManager.hasSelection()) { + selectionManager.saveProperties(); + for (var i = 0; i < selectionManager.selections.length; i++) { + var properties = selectionManager.savedProperties[selectionManager.selections[i]]; + var bottomY = properties.boundingBox.center.y - properties.boundingBox.dimensions.y / 2; + var dY = grid.getOrigin().y - bottomY; + var diff = { + x: 0, + y: dY, + z: 0 + }; + var newPosition = Vec3.sum(properties.position, diff); + Entities.editEntity(selectionManager.selections[i], { + position: newPosition, + }); + } + pushCommandForSelections(); + selectionManager._update(); + } + } else if (data.action == "resetToNaturalDimensions") { + if (selectionManager.hasSelection()) { + selectionManager.saveProperties(); + for (var i = 0; i < selectionManager.selections.length; i++) { + var properties = selectionManager.savedProperties[selectionManager.selections[i]]; + var naturalDimensions = properties.naturalDimensions; + + // If any of the natural dimensions are not 0, resize + if (properties.type == "Model" && naturalDimensions.x == 0 && naturalDimensions.y == 0 && naturalDimensions.z == 0) { + Window.alert("Cannot reset entity to its natural dimensions: Model URL" + " is invalid or the model has not yet been loaded."); + } else { + Entities.editEntity(selectionManager.selections[i], { + dimensions: properties.naturalDimensions, + }); + } + } + pushCommandForSelections(); + selectionManager._update(); + } + } else if (data.action == "previewCamera") { + if (selectionManager.hasSelection()) { + Camera.mode = "entity"; + Camera.cameraEntity = selectionManager.selections[0]; + } + } else if (data.action == "rescaleDimensions") { + var multiplier = data.percentage / 100; + if (selectionManager.hasSelection()) { + selectionManager.saveProperties(); + for (var i = 0; i < selectionManager.selections.length; i++) { + var properties = selectionManager.savedProperties[selectionManager.selections[i]]; + Entities.editEntity(selectionManager.selections[i], { + dimensions: Vec3.multiply(multiplier, properties.dimensions), + }); + } + pushCommandForSelections(); + selectionManager._update(); + } + } else if (data.action == "reloadScript") { + if (selectionManager.hasSelection()) { + var timestamp = Date.now(); + for (var i = 0; i < selectionManager.selections.length; i++) { + Entities.editEntity(selectionManager.selections[i], { + scriptTimestamp: timestamp, + }); + } + } + } + } + }); + + return that; +}; + +PopupMenu = function() { + var self = this; + + var MENU_ITEM_HEIGHT = 21; + var MENU_ITEM_SPACING = 1; + var TEXT_MARGIN = 7; + + var overlays = []; + var overlayInfo = {}; + + var upColor = { + red: 0, + green: 0, + blue: 0 + }; + var downColor = { + red: 192, + green: 192, + blue: 192 + }; + var overColor = { + red: 128, + green: 128, + blue: 128 + }; + + self.onSelectMenuItem = function() {}; + + self.addMenuItem = function(name) { + var id = Overlays.addOverlay("text", { + text: name, + backgroundAlpha: 1.0, + backgroundColor: upColor, + topMargin: TEXT_MARGIN, + leftMargin: TEXT_MARGIN, + width: 210, + height: MENU_ITEM_HEIGHT, + font: { + size: 12 + }, + visible: false, + }); + overlays.push(id); + overlayInfo[id] = { + name: name + }; + return id; + }; + + self.updateMenuItemText = function(id, newText) { + Overlays.editOverlay(id, { + text: newText + }); + }; + + self.setPosition = function(x, y) { + for (var key in overlayInfo) { + Overlays.editOverlay(key, { + x: x, + y: y, + }); + y += MENU_ITEM_HEIGHT + MENU_ITEM_SPACING; + } + }; + + self.onSelected = function() {}; + + var pressingOverlay = null; + var hoveringOverlay = null; + + self.mousePressEvent = function(event) { + if (event.isLeftButton) { + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + if (overlay in overlayInfo) { + pressingOverlay = overlay; + Overlays.editOverlay(pressingOverlay, { + backgroundColor: downColor + }); + } else { + self.hide(); + } + return false; + } + }; + self.mouseMoveEvent = function(event) { + if (visible) { + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + if (!pressingOverlay) { + if (hoveringOverlay != null && overlay != hoveringOverlay) { + Overlays.editOverlay(hoveringOverlay, { + backgroundColor: upColor + }); + hoveringOverlay = null; + } + if (overlay != hoveringOverlay && overlay in overlayInfo) { + Overlays.editOverlay(overlay, { + backgroundColor: overColor + }); + hoveringOverlay = overlay; + } + } + } + return false; + }; + self.mouseReleaseEvent = function(event) { + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + if (pressingOverlay != null) { + if (overlay == pressingOverlay) { + self.onSelectMenuItem(overlayInfo[overlay].name); + } + Overlays.editOverlay(pressingOverlay, { + backgroundColor: upColor + }); + pressingOverlay = null; + self.hide(); + } + }; + + var visible = false; + + self.setVisible = function(newVisible) { + if (newVisible != visible) { + visible = newVisible; + for (var key in overlayInfo) { + Overlays.editOverlay(key, { + visible: newVisible + }); + } + } + } + self.show = function() { + self.setVisible(true); + } + self.hide = function() { + self.setVisible(false); + } + + function cleanup() { + for (var i = 0; i < overlays.length; i++) { + Overlays.deleteOverlay(overlays[i]); + } + } + + Controller.mousePressEvent.connect(self.mousePressEvent); + Controller.mouseMoveEvent.connect(self.mouseMoveEvent); + Controller.mouseReleaseEvent.connect(self.mouseReleaseEvent); + Script.scriptEnding.connect(cleanup); + + return this; +}; + +var propertyMenu = PopupMenu(); + +propertyMenu.onSelectMenuItem = function(name) { + if (propertyMenu.marketplaceID) { + showMarketplace(propertyMenu.marketplaceID); + } +}; + +var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); + +propertiesTool = PropertiesTool(); From e369c1f3279a72bb8499436262f9819cb21ab39b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 22 Feb 2016 10:28:00 -0800 Subject: [PATCH 3/9] fix unix warning --- interface/src/ui/ApplicationCompositor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 37eff80df9..38a0c6f9f9 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -287,7 +287,6 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int //auto headPose = qApp->getHMDSensorPose(); auto myCamera = qApp->getCamera(); mat4 cameraMat = myCamera->getTransform(); - auto cameraOrientation = myCamera->getOrientation(); auto UITransform = cameraMat * glm::inverse(headPose); auto relativePosition4 = glm::inverse(UITransform) * vec4(_drawAt3DPosition, 1); auto relativePosition = vec3(relativePosition4) / relativePosition4.w; From fc002df5e78eac9b18a36ed04a9c1511cd96af52 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 22 Feb 2016 13:24:14 -0800 Subject: [PATCH 4/9] implement Reticle.depth using the apparent 3D look at logic instead --- examples/editWithHacks.js | 6 ++-- interface/src/ui/ApplicationCompositor.cpp | 37 +++++++++------------- interface/src/ui/ApplicationCompositor.h | 13 ++------ 3 files changed, 20 insertions(+), 36 deletions(-) diff --git a/examples/editWithHacks.js b/examples/editWithHacks.js index 3504cddb96..11bf5785e2 100644 --- a/examples/editWithHacks.js +++ b/examples/editWithHacks.js @@ -1235,13 +1235,11 @@ Script.update.connect(function(deltaTime) { // + JSON.stringify(result) print("something hovered!! result.distance:" +result.distance); Vec3.print("something hovered!! result.intersection:", result.intersection); - //Reticle.setDepth(result.distance); - Reticle.setApparentPosition(result.intersection); + Reticle.setDepth(result.distance); Overlays.editOverlay(intersectionBeacon, { position: result.intersection, visible: false }); } else { - //Reticle.setDepth(1.0); - Reticle.restoreApparentPosition(); + Reticle.setDepth(100.0); print("NO INTERSECTION..."); var pointAt2D = Reticle.position; var pointAt3D = HMD.worldPointFromOverlay(pointAt2D); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 38a0c6f9f9..4fd810e22f 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -283,12 +283,18 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int //Mouse Pointer if (getReticleVisible()) { - if (_drawAt3D) { - //auto headPose = qApp->getHMDSensorPose(); + if (getReticleDepth() != 1.0f) { + // calculate the "apparent location" based on the depth and the current ray + glm::vec3 origin, direction; + auto reticlePosition = getReticlePosition(); + computeHmdPickRay(reticlePosition, origin, direction); + auto apparentPosition = origin + (direction * getReticleDepth()); + + // same code as used to render for apparent location auto myCamera = qApp->getCamera(); mat4 cameraMat = myCamera->getTransform(); auto UITransform = cameraMat * glm::inverse(headPose); - auto relativePosition4 = glm::inverse(UITransform) * vec4(_drawAt3DPosition, 1); + auto relativePosition4 = glm::inverse(UITransform) * vec4(apparentPosition, 1); auto relativePosition = vec3(relativePosition4) / relativePosition4.w; auto relativeDistance = glm::length(relativePosition); @@ -297,32 +303,19 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int float azimuth = atan2f(relativePosition.x, relativePosition.z); glm::quat faceCamera = glm::quat(glm::vec3(elevation, azimuth, 0)) * quat(vec3(0, 0, -1)); // this extra *quat(vec3(0,0,-1)) was required to get the quad to flip this seems like we could optimize - qDebug() << "_drawAt3DPosition:" << _drawAt3DPosition; - qDebug() << "relativePosition:" << relativePosition; - qDebug() << "relativeDistance:" << relativeDistance; - Transform transform; transform.setTranslation(relativePosition); transform.setScale(reticleScale); transform.postScale(relativeDistance); // scale not quite working, distant things too large transform.setRotation(faceCamera); batch.setModelTransform(transform); - - /* - // this definitely doesn't work - mat4 pointerXfm = glm::scale(mat4(), vec3(relativeDistance)) * glm::mat4_cast(faceCamera) * glm::translate(mat4(), relativePosition); - batch.setModelTransform(pointerXfm); - */ - - } - else { + } else { glm::mat4 overlayXfm; _modelTransform.getMatrix(overlayXfm); auto reticlePosition = getReticlePosition(); glm::vec2 projection = overlayToSpherical(reticlePosition); - float cursorDepth = getReticleDepth(); - mat4 pointerXfm = glm::scale(mat4(), vec3(cursorDepth)) * glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); mat4 reticleXfm = overlayXfm * pointerXfm; reticleXfm = glm::scale(reticleXfm, reticleScale); batch.setModelTransform(reticleXfm); @@ -334,7 +327,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int QPointF ApplicationCompositor::getMouseEventPosition(QMouseEvent* event) { if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticlePositionInHMDLock); + QMutexLocker locker(&_reticleLock); return QPointF(_reticlePositionInHMD.x, _reticlePositionInHMD.y); } return event->localPos(); @@ -388,7 +381,7 @@ bool ApplicationCompositor::handleRealMouseMoveEvent(bool sendFakeEvent) { // If we're in HMD mode if (shouldCaptureMouse()) { - QMutexLocker locker(&_reticlePositionInHMDLock); + QMutexLocker locker(&_reticleLock); auto newPosition = QCursor::pos(); auto changeInRealMouse = newPosition - _lastKnownRealMouse; auto newReticlePosition = _reticlePositionInHMD + toGlm(changeInRealMouse); @@ -404,7 +397,7 @@ bool ApplicationCompositor::handleRealMouseMoveEvent(bool sendFakeEvent) { glm::vec2 ApplicationCompositor::getReticlePosition() { if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticlePositionInHMDLock); + QMutexLocker locker(&_reticleLock); return _reticlePositionInHMD; } return toGlm(QCursor::pos()); @@ -412,7 +405,7 @@ glm::vec2 ApplicationCompositor::getReticlePosition() { void ApplicationCompositor::setReticlePosition(glm::vec2 position, bool sendFakeEvent) { if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticlePositionInHMDLock); + QMutexLocker locker(&_reticleLock); const float MOUSE_EXTENTS_VERT_ANGULAR_SIZE = 170.0f; // 5deg from poles const float MOUSE_EXTENTS_VERT_PIXELS = VIRTUAL_SCREEN_SIZE_Y * (MOUSE_EXTENTS_VERT_ANGULAR_SIZE / DEFAULT_HMD_UI_VERT_ANGULAR_SIZE); const float MOUSE_EXTENTS_HORZ_ANGULAR_SIZE = 360.0f; // full sphere diff --git a/interface/src/ui/ApplicationCompositor.h b/interface/src/ui/ApplicationCompositor.h index 57d8537b83..7337598d8f 100644 --- a/interface/src/ui/ApplicationCompositor.h +++ b/interface/src/ui/ApplicationCompositor.h @@ -29,6 +29,7 @@ class RenderArgs; class ReticleInterface; +const float DEFAULT_RETICLE_DEPTH = 1.0f; // FIXME - probably should be based on UI radius const float MAGNIFY_WIDTH = 220.0f; const float MAGNIFY_HEIGHT = 100.0f; @@ -88,13 +89,11 @@ public: float getReticleDepth() { return _reticleDepth; } void setReticleDepth(float depth) { _reticleDepth = depth; } + void resetReticleDepth() { _reticleDepth = DEFAULT_RETICLE_DEPTH; } glm::vec2 getReticlePosition(); void setReticlePosition(glm::vec2 position, bool sendFakeEvent = true); - void setReticleApparentPosition(glm::vec3 position) { _drawAt3D = true; _drawAt3DPosition = position; } - void restoreReticleApparentPosition() { _drawAt3D = false; } - glm::vec2 getReticleMaximumPosition() const; ReticleInterface* getReticleInterface() { return _reticleInterface; } @@ -150,10 +149,7 @@ private: // application specific position, when it's in desktop mode, the reticle position will simply move // the system mouse. glm::vec2 _reticlePositionInHMD { 0.0f, 0.0f }; - mutable QMutex _reticlePositionInHMDLock { QMutex::Recursive }; - - bool _drawAt3D { false }; - glm::vec3 _drawAt3DPosition; + mutable QMutex _reticleLock { QMutex::Recursive }; QPointF _lastKnownRealMouse; bool _ignoreMouseMove { false }; @@ -186,9 +182,6 @@ public: Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } - Q_INVOKABLE void setApparentPosition(glm::vec3 position) { _compositor->setReticleApparentPosition(position); } - Q_INVOKABLE void restoreApparentPosition() { _compositor->restoreReticleApparentPosition(); } - private: ApplicationCompositor* _compositor; }; From 54b21abf02781ea4ba9a4955c713620ade0004b4 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 22 Feb 2016 13:32:19 -0800 Subject: [PATCH 5/9] remove editWithHacks and move to more generic depthReticle --- examples/depthReticle.js | 29 + examples/editWithHacks.js | 1908 ------------------------------------- 2 files changed, 29 insertions(+), 1908 deletions(-) create mode 100644 examples/depthReticle.js delete mode 100644 examples/editWithHacks.js diff --git a/examples/depthReticle.js b/examples/depthReticle.js new file mode 100644 index 0000000000..4d72836577 --- /dev/null +++ b/examples/depthReticle.js @@ -0,0 +1,29 @@ +var lastDepthCheckTime = 0; + +// Do some stuff regularly, like check for placement of various overlays +Script.update.connect(function(deltaTime) { + var TIME_BETWEEN_DEPTH_CHECKS = 100; + var timeSinceLastDepthCheck = Date.now() - lastDepthCheckTime; + if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) { + var reticlePosition = Reticle.position; + var pickRay = Camera.computePickRay(reticlePosition.x, reticlePosition.y); + var result = Overlays.findRayIntersection(pickRay); + + if (!result.intersects) { + result = Entities.findRayIntersection(pickRay, true); + } + if (result.intersects) { + // + JSON.stringify(result) + print("something hovered!! result.distance:" +result.distance); + Vec3.print("something hovered!! result.intersection:", result.intersection); + Reticle.setDepth(result.distance); + + } else { + Reticle.setDepth(100.0); + print("NO INTERSECTION..."); + var pointAt2D = Reticle.position; + var pointAt3D = HMD.worldPointFromOverlay(pointAt2D); + } + + } +}); diff --git a/examples/editWithHacks.js b/examples/editWithHacks.js deleted file mode 100644 index 11bf5785e2..0000000000 --- a/examples/editWithHacks.js +++ /dev/null @@ -1,1908 +0,0 @@ -// newEditEntities.js -// examples -// -// Created by Brad Hefta-Gaub on 10/2/14. -// Persist toolbar by HRS 6/11/15. -// Copyright 2014 High Fidelity, Inc. -// -// This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; - -Script.include([ - "libraries/stringHelpers.js", - "libraries/dataViewHelpers.js", - "libraries/toolBars.js", - "libraries/progressDialog.js", - - "libraries/entitySelectionTool.js", - - "libraries/ToolTip.js", - - "libraries/entityCameraTool.js", - "libraries/gridTool.js", - "libraries/entityList.js", - "libraries/lightOverlayManager.js", -]); - -var selectionDisplay = SelectionDisplay; -var selectionManager = SelectionManager; - -var lightOverlayManager = new LightOverlayManager(); - -var cameraManager = new CameraManager(); - -var grid = Grid(); -// gridTool = GridTool({ -// horizontalGrid: grid -// }); -// gridTool.setVisible(false); - -var entityListTool = EntityListTool(); - -selectionManager.addEventListener(function() { - selectionDisplay.updateHandles(); - lightOverlayManager.updatePositions(); -}); - -var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; -var toolHeight = 50; -var toolWidth = 50; - -var DEGREES_TO_RADIANS = Math.PI / 180.0; -var RADIANS_TO_DEGREES = 180.0 / Math.PI; -var epsilon = 0.001; - -var MIN_ANGULAR_SIZE = 2; -var MAX_ANGULAR_SIZE = 45; -var allowLargeModels = true; -var allowSmallModels = true; -var wantEntityGlow = false; - -var SPAWN_DISTANCE = 1; -var DEFAULT_DIMENSION = 0.20; -var DEFAULT_TEXT_DIMENSION_X = 1.0; -var DEFAULT_TEXT_DIMENSION_Y = 1.0; -var DEFAULT_TEXT_DIMENSION_Z = 0.01; - -var DEFAULT_DIMENSIONS = { - x: DEFAULT_DIMENSION, - y: DEFAULT_DIMENSION, - z: DEFAULT_DIMENSION -}; - -var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS); - -var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select"; -var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; -var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode"; -var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Edit Mode"; - -var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled"; -var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect"; -var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus"; -var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode"; -var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode"; - -var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain." -var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain." - -var modelURLs = [ - "Insert the URL to your FBX" -]; - -var mode = 0; -var isActive = false; - -var placingEntityID = null; - -IMPORTING_SVO_OVERLAY_WIDTH = 144; -IMPORTING_SVO_OVERLAY_HEIGHT = 30; -IMPORTING_SVO_OVERLAY_MARGIN = 5; -IMPORTING_SVO_OVERLAY_LEFT_MARGIN = 34; -var importingSVOImageOverlay = Overlays.addOverlay("image", { - imageURL: HIFI_PUBLIC_BUCKET + "images/hourglass.svg", - width: 20, - height: 20, - alpha: 1.0, - color: { - red: 255, - green: 255, - blue: 255 - }, - x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH, - y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT, - visible: false, -}); -var importingSVOTextOverlay = Overlays.addOverlay("text", { - font: { - size: 14 - }, - text: "Importing SVO...", - leftMargin: IMPORTING_SVO_OVERLAY_LEFT_MARGIN, - x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH - IMPORTING_SVO_OVERLAY_MARGIN, - y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT - IMPORTING_SVO_OVERLAY_MARGIN, - width: IMPORTING_SVO_OVERLAY_WIDTH, - height: IMPORTING_SVO_OVERLAY_HEIGHT, - backgroundColor: { - red: 80, - green: 80, - blue: 80 - }, - backgroundAlpha: 0.7, - visible: false, -}); - -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var marketplaceWindow = new OverlayWebWindow({ - title: 'Marketplace', - source: "about:blank", - width: 900, - height: 700, - visible: false -}); - -function showMarketplace(marketplaceID) { - var url = MARKETPLACE_URL; - if (marketplaceID) { - url = url + "/items/" + marketplaceID; - } - print("setting marketplace URL to " + url); - marketplaceWindow.setURL(url); - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); -} - -function hideMarketplace() { - marketplaceWindow.setVisible(false); - marketplaceWindow.setURL("about:blank"); -} - -function toggleMarketplace() { - if (marketplaceWindow.visible) { - hideMarketplace(); - } else { - showMarketplace(); - } -} - -var toolBar = (function() { - var that = {}, - toolBar, - activeButton, - newModelButton, - newCubeButton, - newSphereButton, - newLightButton, - newTextButton, - newWebButton, - newZoneButton, - newPolyVoxButton; - - function initialize() { - toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function(windowDimensions, toolbar) { - return { - x: windowDimensions.x - 8 - toolbar.width, - y: (windowDimensions.y - toolbar.height) / 2 - }; - }); - - - - activeButton = toolBar.addTool({ - imageURL: toolIconUrl + "edit-status.svg", - subImage: { - x: 0, - y: Tool.IMAGE_WIDTH, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: true - }, true, false); - - newModelButton = toolBar.addTool({ - imageURL: toolIconUrl + "upload.svg", - subImage: { - x: 0, - y: Tool.IMAGE_WIDTH, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - - newCubeButton = toolBar.addTool({ - imageURL: toolIconUrl + "add-cube.svg", - subImage: { - x: 0, - y: Tool.IMAGE_WIDTH, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - - newSphereButton = toolBar.addTool({ - imageURL: toolIconUrl + "add-sphere.svg", - subImage: { - x: 0, - y: Tool.IMAGE_WIDTH, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - - newLightButton = toolBar.addTool({ - imageURL: toolIconUrl + "light.svg", - subImage: { - x: 0, - y: Tool.IMAGE_WIDTH, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - - newTextButton = toolBar.addTool({ - imageURL: toolIconUrl + "add-text.svg", - subImage: { - x: 0, - y: Tool.IMAGE_WIDTH, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - - newWebButton = toolBar.addTool({ - imageURL: "https://hifi-public.s3.amazonaws.com/images/www.svg", - subImage: { - x: 0, - y: 0, - width: 128, - height: 128 - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - - newZoneButton = toolBar.addTool({ - imageURL: toolIconUrl + "zonecube_text.svg", - subImage: { - x: 0, - y: 128, - width: 128, - height: 128 - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - - newPolyVoxButton = toolBar.addTool({ - imageURL: toolIconUrl + "polyvox.svg", - subImage: { - x: 0, - y: 0, - width: 256, - height: 256 - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - - that.setActive(false); - } - - that.clearEntityList = function() { - entityListTool.clearEntityList(); - }; - - that.setActive = function(active) { - if (active != isActive) { - if (active && !Entities.canAdjustLocks()) { - Window.alert(INSUFFICIENT_PERMISSIONS_ERROR_MSG); - } else { - isActive = active; - if (!isActive) { - entityListTool.setVisible(false); - // gridTool.setVisible(false); - grid.setEnabled(false); - propertiesTool.setVisible(false); - selectionManager.clearSelections(); - cameraManager.disable(); - } else { - hasShownPropertiesTool = false; - entityListTool.setVisible(true); - // gridTool.setVisible(true); - grid.setEnabled(true); - propertiesTool.setVisible(true); - Window.setFocus(); - } - that.showTools(isActive); - } - } - toolBar.selectTool(activeButton, isActive); - lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); - Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); - }; - - // Sets visibility of tool buttons, excluding the power button - that.showTools = function(doShow) { - toolBar.showTool(newModelButton, doShow); - toolBar.showTool(newCubeButton, doShow); - toolBar.showTool(newSphereButton, doShow); - toolBar.showTool(newLightButton, doShow); - toolBar.showTool(newTextButton, doShow); - toolBar.showTool(newWebButton, doShow); - toolBar.showTool(newZoneButton, doShow); - toolBar.showTool(newPolyVoxButton, doShow); - }; - - var RESIZE_INTERVAL = 50; - var RESIZE_TIMEOUT = 120000; // 2 minutes - var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL; - - function addModel(url) { - var entityID = createNewEntity({ - type: "Model", - modelURL: url - }, false); - - if (entityID) { - print("Model added: " + url); - selectionManager.setSelections([entityID]); - } - } - - function createNewEntity(properties, dragOnCreate) { - // Default to true if not passed in - dragOnCreate = dragOnCreate == undefined ? true : dragOnCreate; - - var dimensions = properties.dimensions ? properties.dimensions : DEFAULT_DIMENSIONS; - var position = getPositionToCreateEntity(); - var entityID = null; - if (position != null) { - position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions), - properties.position = position; - - entityID = Entities.addEntity(properties); - if (dragOnCreate) { - placingEntityID = entityID; - } - } else { - Window.alert("Can't create " + properties.type + ": " + properties.type + " would be out of bounds."); - } - - return entityID; - } - - var newModelButtonDown = false; - that.mousePressEvent = function(event) { - var clickedOverlay, - url, - file; - - if (!event.isLeftButton) { - // if another mouse button than left is pressed ignore it - return false; - } - - clickedOverlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - - if (activeButton === toolBar.clicked(clickedOverlay)) { - that.setActive(!isActive); - return true; - } - - // Handle these two buttons in the mouseRelease event handler so that we don't suppress a mouseRelease event from - // occurring when showing a modal dialog. - if (newModelButton === toolBar.clicked(clickedOverlay)) { - newModelButtonDown = true; - return true; - } - - - if (newCubeButton === toolBar.clicked(clickedOverlay)) { - createNewEntity({ - type: "Box", - dimensions: DEFAULT_DIMENSIONS, - color: { - red: 255, - green: 0, - blue: 0 - } - }); - - return true; - } - - if (newSphereButton === toolBar.clicked(clickedOverlay)) { - createNewEntity({ - type: "Sphere", - dimensions: DEFAULT_DIMENSIONS, - color: { - red: 255, - green: 0, - blue: 0 - } - }); - - return true; - } - - if (newLightButton === toolBar.clicked(clickedOverlay)) { - createNewEntity({ - type: "Light", - dimensions: DEFAULT_LIGHT_DIMENSIONS, - isSpotlight: false, - color: { - red: 150, - green: 150, - blue: 150 - }, - - constantAttenuation: 1, - linearAttenuation: 0, - quadraticAttenuation: 0, - exponent: 0, - cutoff: 180, // in degrees - }); - - return true; - } - - if (newTextButton === toolBar.clicked(clickedOverlay)) { - createNewEntity({ - type: "Text", - dimensions: { - x: 0.65, - y: 0.3, - z: 0.01 - }, - backgroundColor: { - red: 64, - green: 64, - blue: 64 - }, - textColor: { - red: 255, - green: 255, - blue: 255 - }, - text: "some text", - lineHeight: 0.06 - }); - - return true; - } - - if (newWebButton === toolBar.clicked(clickedOverlay)) { - createNewEntity({ - type: "Web", - dimensions: { - x: 1.6, - y: 0.9, - z: 0.01 - }, - sourceUrl: "https://highfidelity.com/", - }); - - return true; - } - - if (newZoneButton === toolBar.clicked(clickedOverlay)) { - createNewEntity({ - type: "Zone", - dimensions: { - x: 10, - y: 10, - z: 10 - }, - }); - - return true; - } - - if (newPolyVoxButton === toolBar.clicked(clickedOverlay)) { - var polyVoxId = createNewEntity({ - type: "PolyVox", - dimensions: { - x: 10, - y: 10, - z: 10 - }, - voxelVolumeSize: { - x: 16, - y: 16, - z: 16 - }, - voxelSurfaceStyle: 2 - }); - for (var x = 1; x <= 14; x++) { - Entities.setVoxel(polyVoxId, { - x: x, - y: 1, - z: 1 - }, 255); - Entities.setVoxel(polyVoxId, { - x: x, - y: 14, - z: 1 - }, 255); - Entities.setVoxel(polyVoxId, { - x: x, - y: 1, - z: 14 - }, 255); - Entities.setVoxel(polyVoxId, { - x: x, - y: 14, - z: 14 - }, 255); - } - for (var y = 2; y <= 13; y++) { - Entities.setVoxel(polyVoxId, { - x: 1, - y: y, - z: 1 - }, 255); - Entities.setVoxel(polyVoxId, { - x: 14, - y: y, - z: 1 - }, 255); - Entities.setVoxel(polyVoxId, { - x: 1, - y: y, - z: 14 - }, 255); - Entities.setVoxel(polyVoxId, { - x: 14, - y: y, - z: 14 - }, 255); - } - for (var z = 2; z <= 13; z++) { - Entities.setVoxel(polyVoxId, { - x: 1, - y: 1, - z: z - }, 255); - Entities.setVoxel(polyVoxId, { - x: 14, - y: 1, - z: z - }, 255); - Entities.setVoxel(polyVoxId, { - x: 1, - y: 14, - z: z - }, 255); - Entities.setVoxel(polyVoxId, { - x: 14, - y: 14, - z: z - }, 255); - } - - - return true; - } - - return false; - }; - - that.mouseReleaseEvent = function(event) { - var handled = false; - if (newModelButtonDown) { - var clickedOverlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - if (newModelButton === toolBar.clicked(clickedOverlay)) { - url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]); - if (url !== null && url !== "") { - addModel(url); - } - handled = true; - } - } - - newModelButtonDown = false; - - - return handled; - } - - Window.domainChanged.connect(function() { - that.setActive(false); - that.clearEntityList(); - }); - - Entities.canAdjustLocksChanged.connect(function(canAdjustLocks) { - if (isActive && !canAdjustLocks) { - that.setActive(false); - } - }); - - that.cleanup = function() { - toolBar.cleanup(); - }; - - initialize(); - return that; -}()); - - -function isLocked(properties) { - // special case to lock the ground plane model in hq. - if (location.hostname == "hq.highfidelity.io" && - properties.modelURL == HIFI_PUBLIC_BUCKET + "ozan/Terrain_Reduce_forAlpha.fbx") { - return true; - } - return false; -} - - -var selectedEntityID; -var orientation; -var intersection; - - -var SCALE_FACTOR = 200.0; - -function rayPlaneIntersection(pickRay, point, normal) { - var d = -Vec3.dot(point, normal); - var t = -(Vec3.dot(pickRay.origin, normal) + d) / Vec3.dot(pickRay.direction, normal); - - return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); -} - -function findClickedEntity(event) { - var pickZones = event.isControl; - - if (pickZones) { - Entities.setZonesArePickable(true); - } - - var pickRay = Camera.computePickRay(event.x, event.y); - - var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking - var lightResult = lightOverlayManager.findRayIntersection(pickRay); - lightResult.accurate = true; - - if (pickZones) { - Entities.setZonesArePickable(false); - } - - var result; - - if (!entityResult.intersects && !lightResult.intersects) { - return null; - } else if (entityResult.intersects && !lightResult.intersects) { - result = entityResult; - } else if (!entityResult.intersects && lightResult.intersects) { - result = lightResult; - } else { - if (entityResult.distance < lightResult.distance) { - result = entityResult; - } else { - result = lightResult; - } - } - - if (!result.accurate) { - return null; - } - - var foundEntity = result.entityID; - return { - pickRay: pickRay, - entityID: foundEntity - }; -} - -var mouseHasMovedSincePress = false; -var mousePressStartTime = 0; -var mousePressStartPosition = { - x: 0, - y: 0 -}; -var mouseDown = false; - -function mousePressEvent(event) { - mouseDown = true; - mousePressStartPosition = { - x: event.x, - y: event.y - }; - mousePressStartTime = Date.now(); - mouseHasMovedSincePress = false; - mouseCapturedByTool = false; - - if (propertyMenu.mousePressEvent(event) || toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) { - mouseCapturedByTool = true; - return; - } - if (isActive) { - if (cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { - // Event handled; do nothing. - return; - } - } -} - -var highlightedEntityID = null; -var mouseCapturedByTool = false; -var lastMousePosition = null; -var idleMouseTimerId = null; -var CLICK_TIME_THRESHOLD = 500 * 1000; // 500 ms -var CLICK_MOVE_DISTANCE_THRESHOLD = 8; -var IDLE_MOUSE_TIMEOUT = 200; -var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0; - -var lastMouseMoveEvent = null; - -function mouseMoveEventBuffered(event) { - lastMouseMoveEvent = event; -} - - - -function mouseMove(event) { - if (mouseDown && !mouseHasMovedSincePress) { - var timeSincePressMicro = Date.now() - mousePressStartTime; - - var dX = mousePressStartPosition.x - event.x; - var dY = mousePressStartPosition.y - event.y; - var sqDist = (dX * dX) + (dY * dY); - - // If less than CLICK_TIME_THRESHOLD has passed since the mouse click AND the mouse has moved - // less than CLICK_MOVE_DISTANCE_THRESHOLD distance, then don't register this as a mouse move - // yet. The goal is to provide mouse clicks that are more lenient to small movements. - if (timeSincePressMicro < CLICK_TIME_THRESHOLD && sqDist < CLICK_MOVE_DISTANCE_THRESHOLD) { - return; - } - mouseHasMovedSincePress = true; - } - - if (placingEntityID) { - var pickRay = Camera.computePickRay(event.x, event.y); - var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE; - var offset = Vec3.multiply(distance, pickRay.direction); - var position = Vec3.sum(Camera.position, offset); - Entities.editEntity(placingEntityID, { - position: position, - }); - return; - } - if (!isActive) { - return; - } - if (idleMouseTimerId) { - Script.clearTimeout(idleMouseTimerId); - } - - // 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) || propertyMenu.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) { - return; - } - - lastMousePosition = { - x: event.x, - y: event.y - }; - - idleMouseTimerId = Script.setTimeout(handleIdleMouse, IDLE_MOUSE_TIMEOUT); - - - // brad's hack to move an overlay to the mouse - //Overlays.editOverlay(pointerOverlay, { position: lookAt3D }); - /* - var pickRay = Camera.computePickRay(event.x, event.y); - var result = Overlays.findRayIntersection(pickRay); - print(JSON.stringify(result)); - if (result.intersects) { - Overlays.editOverlay(pointerOverlay, { position: result.intersection }); - Reticle.setVisible(false); - print("intersects..."); - } else { - var lookAt3D = HMD.getHUDLookAtPosition3D(); - Overlays.editOverlay(pointerOverlay, { position: { x: 0, y: 0, z: 0 } }); - Reticle.setVisible(true); - print("NO INTERSECTION..."); - } - */ -} - -function handleIdleMouse() { - idleMouseTimerId = null; -} - -function highlightEntityUnderCursor(position, accurateRay) { - var pickRay = Camera.computePickRay(position.x, position.y); - var entityIntersection = Entities.findRayIntersection(pickRay, accurateRay); - if (entityIntersection.accurate) { - if (highlightedEntityID && highlightedEntityID != entityIntersection.entityID) { - selectionDisplay.unhighlightSelectable(highlightedEntityID); - highlightedEntityID = { - id: -1 - }; - } - - 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 && sizeOK) { - if (wantEntityGlow) { - Entities.editEntity(entityIntersection.entityID, { - glowLevel: 0.25 - }); - } - highlightedEntityID = entityIntersection.entityID; - selectionDisplay.highlightSelectable(entityIntersection.entityID); - } - - } -} - - -function mouseReleaseEvent(event) { - mouseDown = false; - - if (lastMouseMoveEvent) { - mouseMove(lastMouseMoveEvent); - lastMouseMoveEvent = null; - } - if (propertyMenu.mouseReleaseEvent(event) || toolBar.mouseReleaseEvent(event)) { - - return true; - } - if (placingEntityID) { - - if (isActive) { - - selectionManager.setSelections([placingEntityID]); - } - placingEntityID = null; - } - if (isActive && selectionManager.hasSelection()) { - - tooltip.show(false); - } - if (mouseCapturedByTool) { - - return; - } - - cameraManager.mouseReleaseEvent(event); - - if (!mouseHasMovedSincePress) { - mouseClickEvent(event); - } -} - -function mouseClickEvent(event) { - if (isActive && event.isLeftButton) { - var result = findClickedEntity(event); - if (result === null) { - if (!event.isShifted) { - selectionManager.clearSelections(); - } - return; - } - toolBar.setActive(true); - 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; - - print("Checking properties: " + properties.id + " " + " - 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 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 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 (!event.isShifted) { - selectionManager.setSelections([foundEntity]); - } else { - selectionManager.addEntity(foundEntity, true); - } - - print("Model selected: " + foundEntity); - selectionDisplay.select(selectedEntityID, event); - - if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } - } - } else if (event.isRightButton) { - var result = findClickedEntity(event); - if (result) { - var properties = Entities.getEntityProperties(result.entityID); - if (properties.marketplaceID) { - propertyMenu.marketplaceID = properties.marketplaceID; - propertyMenu.updateMenuItemText(showMenuItem, "Show in Marketplace"); - } else { - propertyMenu.marketplaceID = null; - propertyMenu.updateMenuItemText(showMenuItem, "No marketplace info"); - } - propertyMenu.setPosition(event.x, event.y); - propertyMenu.show(); - } else { - propertyMenu.hide(); - } - } -} - -Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseMoveEvent.connect(mouseMoveEventBuffered); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); - - -// In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already -// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that -// added it. -var modelMenuAddedDelete = false; -var originalLightsArePickable = Entities.getLightsArePickable(); - -function setupModelMenus() { - print("setupModelMenus()"); - // adj our menuitems - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Entities", - isSeparator: true, - grouping: "Advanced" - }); - if (!Menu.menuItemExists("Edit", "Delete")) { - print("no delete... adding ours"); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Delete", - shortcutKeyEvent: { - text: "backspace" - }, - afterItem: "Entities", - grouping: "Advanced" - }); - modelMenuAddedDelete = true; - } else { - print("delete exists... don't add ours"); - } - - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Entity List...", - shortcutKey: "CTRL+META+L", - afterItem: "Entities", - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Allow Selecting of Large Models", - shortcutKey: "CTRL+META+L", - afterItem: "Entity List...", - isCheckable: true, - isChecked: true, - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Allow Selecting of Small Models", - shortcutKey: "CTRL+META+S", - afterItem: "Allow Selecting of Large Models", - isCheckable: true, - isChecked: true, - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Allow Selecting of Lights", - shortcutKey: "CTRL+SHIFT+META+L", - afterItem: "Allow Selecting of Small Models", - isCheckable: true, - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Select All Entities In Box", - shortcutKey: "CTRL+SHIFT+META+A", - afterItem: "Allow Selecting of Lights", - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Select All Entities Touching Box", - shortcutKey: "CTRL+SHIFT+META+T", - afterItem: "Select All Entities In Box", - grouping: "Advanced" - }); - - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Export Entities", - shortcutKey: "CTRL+META+E", - afterItem: "Entities", - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Import Entities", - shortcutKey: "CTRL+META+I", - afterItem: "Export Entities", - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: "Import Entities from URL", - shortcutKey: "CTRL+META+U", - afterItem: "Import Entities", - grouping: "Advanced" - }); - - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, - isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true", - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_EASE_ON_FOCUS, - afterItem: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, - isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true", - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, - afterItem: MENU_EASE_ON_FOCUS, - isCheckable: true, - isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true", - grouping: "Advanced" - }); - Menu.addMenuItem({ - menuName: "Edit", - menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, - afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE, - isCheckable: true, - isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true", - grouping: "Advanced" - }); - - Entities.setLightsArePickable(false); -} - -setupModelMenus(); // do this when first running our script. - -function cleanupModelMenus() { - Menu.removeSeparator("Edit", "Entities"); - if (modelMenuAddedDelete) { - // delete our menuitems - Menu.removeMenuItem("Edit", "Delete"); - } - - Menu.removeMenuItem("Edit", "Entity List..."); - Menu.removeMenuItem("Edit", "Allow Selecting of Large Models"); - Menu.removeMenuItem("Edit", "Allow Selecting of Small Models"); - Menu.removeMenuItem("Edit", "Allow Selecting of Lights"); - Menu.removeMenuItem("Edit", "Select All Entities In Box"); - Menu.removeMenuItem("Edit", "Select All Entities Touching Box"); - - Menu.removeMenuItem("Edit", "Export Entities"); - Menu.removeMenuItem("Edit", "Import Entities"); - Menu.removeMenuItem("Edit", "Import Entities from URL"); - - Menu.removeMenuItem("Edit", MENU_AUTO_FOCUS_ON_SELECT); - Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS); - Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_IN_EDIT_MODE); - Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE); -} - -Script.scriptEnding.connect(function() { - Settings.setValue(SETTING_AUTO_FOCUS_ON_SELECT, Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)); - Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - Settings.setValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); - Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); - - progressDialog.cleanup(); - toolBar.cleanup(); - cleanupModelMenus(); - tooltip.cleanup(); - selectionDisplay.cleanup(); - Entities.setLightsArePickable(originalLightsArePickable); - - Overlays.deleteOverlay(importingSVOImageOverlay); - Overlays.deleteOverlay(importingSVOTextOverlay); -}); - -var intersectionBeaconPosition = { x: 0, y: 0, z: 0 }; -var intersectionBeaconSize = 0.03; -var intersectionBeacon = Overlays.addOverlay("sphere", { - position: intersectionBeaconPosition, - size: intersectionBeaconSize, - color: { red: 255, green: 0, blue: 0}, - alpha: 1, - solid: true, - ignoreRayIntersection: true, // this never ray intersects - }); - -Script.scriptEnding.connect(function(){ - Overlays.deleteOverlay(intersectionBeacon); -}); - - -var lastOrientation = null; -var lastPosition = null; -var lastDepthCheckTime = 0; - -// Do some stuff regularly, like check for placement of various overlays -Script.update.connect(function(deltaTime) { - progressDialog.move(); - selectionDisplay.checkMove(); - var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1); - var dPosition = Vec3.distance(Camera.position, lastPosition); - if (dOrientation > 0.001 || dPosition > 0.001) { - propertyMenu.hide(); - lastOrientation = Camera.orientation; - lastPosition = Camera.position; - } - if (lastMouseMoveEvent) { - mouseMove(lastMouseMoveEvent); - var TIME_BETWEEN_DEPTH_CHECKS = 100; - var timeSinceLastDepthCheck = Date.now() - lastDepthCheckTime; - if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) { - var pickRay = Camera.computePickRay(lastMouseMoveEvent.x, lastMouseMoveEvent.y); - var result = Overlays.findRayIntersection(pickRay); - - if (!result.intersects) { - result = Entities.findRayIntersection(pickRay, true); - } - if (result.intersects) { - // + JSON.stringify(result) - print("something hovered!! result.distance:" +result.distance); - Vec3.print("something hovered!! result.intersection:", result.intersection); - Reticle.setDepth(result.distance); - Overlays.editOverlay(intersectionBeacon, { position: result.intersection, visible: false }); - - } else { - Reticle.setDepth(100.0); - print("NO INTERSECTION..."); - var pointAt2D = Reticle.position; - var pointAt3D = HMD.worldPointFromOverlay(pointAt2D); - Overlays.editOverlay(intersectionBeacon, { position: pointAt3D, visible: false }); - } - - } - lastMouseMoveEvent = null; - } -}); - -function insideBox(center, dimensions, point) { - return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0)) && (Math.abs(point.y - center.y) <= (dimensions.y / 2.0)) && (Math.abs(point.z - center.z) <= (dimensions.z / 2.0)); -} - -function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { - if (selectionManager.hasSelection()) { - // Get all entities touching the bounding box of the current selection - var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition, - Vec3.multiply(selectionManager.worldDimensions, 0.5)); - var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions); - - if (!keepIfTouching) { - var isValid; - if (selectionManager.localPosition === null) { - isValid = function(position) { - return insideBox(selectionManager.worldPosition, selectionManager.worldDimensions, position); - } - } else { - isValid = function(position) { - var localPosition = Vec3.multiplyQbyV(Quat.inverse(selectionManager.localRotation), - Vec3.subtract(position, - selectionManager.localPosition)); - return insideBox({ - x: 0, - y: 0, - z: 0 - }, selectionManager.localDimensions, localPosition); - } - } - for (var i = 0; i < entities.length; ++i) { - var properties = Entities.getEntityProperties(entities[i]); - if (!isValid(properties.position)) { - entities.splice(i, 1); - --i; - } - } - } - selectionManager.setSelections(entities); - } -} - -function deleteSelectedEntities() { - if (SelectionManager.hasSelection()) { - print(" Delete Entities"); - SelectionManager.saveProperties(); - var savedProperties = []; - for (var i = 0; i < selectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID]; - SelectionManager.savedProperties[entityID]; - savedProperties.push({ - entityID: entityID, - properties: initialProperties - }); - Entities.deleteEntity(entityID); - } - SelectionManager.clearSelections(); - pushCommandForSelections([], savedProperties); - } else { - print(" Delete Entity.... not holding..."); - } -} - -function handeMenuEvent(menuItem) { - if (menuItem == "Allow Selecting of Small Models") { - allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); - } else if (menuItem == "Allow Selecting of Large Models") { - allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models"); - } else if (menuItem == "Allow Selecting of Lights") { - Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights")); - } else if (menuItem == "Delete") { - deleteSelectedEntities(); - } else if (menuItem == "Export Entities") { - if (!selectionManager.hasSelection()) { - Window.alert("No entities have been selected."); - } else { - var filename = "entities__" + Window.location.hostname + ".svo.json"; - filename = Window.save("Select where to save", filename, "*.json") - if (filename) { - var success = Clipboard.exportEntities(filename, selectionManager.selections); - if (!success) { - Window.alert("Export failed."); - } - } - } - } else if (menuItem == "Import Entities" || menuItem == "Import Entities from URL") { - - var importURL = null; - if (menuItem == "Import Entities") { - var fullPath = Window.browse("Select models to import", "", "*.json"); - if (fullPath) { - importURL = "file:///" + fullPath; - } - } else { - importURL = Window.prompt("URL of SVO to import", ""); - } - - if (importURL) { - importSVO(importURL); - } - } else if (menuItem == "Entity List...") { - entityListTool.toggleVisible(); - } else if (menuItem == "Select All Entities In Box") { - selectAllEtitiesInCurrentSelectionBox(false); - } else if (menuItem == "Select All Entities Touching Box") { - selectAllEtitiesInCurrentSelectionBox(true); - } else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) { - lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); - } else if (menuItem == MENU_SHOW_ZONES_IN_EDIT_MODE) { - Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); - } - tooltip.show(false); -} - -// This function tries to find a reasonable position to place a new entity based on the camera -// position. If a reasonable position within the world bounds can't be found, `null` will -// be returned. The returned position will also take into account grid snapping settings. -function getPositionToCreateEntity() { - var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE; - var direction = Quat.getFront(Camera.orientation); - var offset = Vec3.multiply(distance, direction); - var placementPosition = Vec3.sum(Camera.position, offset); - - var cameraPosition = Camera.position; - - var HALF_TREE_SCALE = 16384; - - var cameraOutOfBounds = Math.abs(cameraPosition.x) > HALF_TREE_SCALE || Math.abs(cameraPosition.y) > HALF_TREE_SCALE || Math.abs(cameraPosition.z) > HALF_TREE_SCALE; - var placementOutOfBounds = Math.abs(placementPosition.x) > HALF_TREE_SCALE || Math.abs(placementPosition.y) > HALF_TREE_SCALE || Math.abs(placementPosition.z) > HALF_TREE_SCALE; - - if (cameraOutOfBounds && placementOutOfBounds) { - return null; - } - - placementPosition.x = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.x)); - placementPosition.y = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.y)); - placementPosition.z = Math.min(HALF_TREE_SCALE, Math.max(-HALF_TREE_SCALE, placementPosition.z)); - - return placementPosition; -} - -function importSVO(importURL) { - print("Import URL requested: " + importURL) - if (!Entities.canAdjustLocks()) { - Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); - return; - } - - Overlays.editOverlay(importingSVOTextOverlay, { - visible: true - }); - Overlays.editOverlay(importingSVOImageOverlay, { - visible: true - }); - - var success = Clipboard.importEntities(importURL); - - if (success) { - var VERY_LARGE = 10000; - var position = { - x: 0, - y: 0, - z: 0 - }; - if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) { - position = getPositionToCreateEntity(); - } - if (position != null) { - var pastedEntityIDs = Clipboard.pasteEntities(position); - - if (isActive) { - selectionManager.setSelections(pastedEntityIDs); - } - - Window.raiseMainWindow(); - } else { - Window.alert("Can't import objects: objects would be out of bounds."); - } - } else { - Window.alert("There was an error importing the entity file."); - } - - Overlays.editOverlay(importingSVOTextOverlay, { - visible: false - }); - Overlays.editOverlay(importingSVOImageOverlay, { - visible: false - }); -} -Window.svoImportRequested.connect(importSVO); - -Menu.menuItemEvent.connect(handeMenuEvent); - -Controller.keyPressEvent.connect(function(event) { - if (isActive) { - cameraManager.keyPressEvent(event); - } -}); - -Controller.keyReleaseEvent.connect(function(event) { - if (isActive) { - cameraManager.keyReleaseEvent(event); - } - // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items - if (event.text == "BACKSPACE" || event.text == "DELETE") { - deleteSelectedEntities(); - } else if (event.text == "ESC") { - selectionManager.clearSelections(); - } else if (event.text == "TAB") { - selectionDisplay.toggleSpaceMode(); - } else if (event.text == "f") { - if (isActive) { - if (selectionManager.hasSelection()) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } - } else if (event.text == '[') { - if (isActive) { - cameraManager.enable(); - } - } else if (event.text == 'g') { - if (isActive && selectionManager.hasSelection()) { - var newPosition = selectionManager.worldPosition; - newPosition = Vec3.subtract(newPosition, { - x: 0, - y: selectionManager.worldDimensions.y * 0.5, - z: 0 - }); - grid.setPosition(newPosition); - } - } -}); - -// When an entity has been deleted we need a way to "undo" this deletion. Because it's not currently -// possible to create an entity with a specific id, earlier undo commands to the deleted entity -// will fail if there isn't a way to find the new entity id. -DELETED_ENTITY_MAP = {} - -function applyEntityProperties(data) { - var properties = data.setProperties; - var selectedEntityIDs = []; - for (var i = 0; i < properties.length; i++) { - var entityID = properties[i].entityID; - if (DELETED_ENTITY_MAP[entityID] !== undefined) { - entityID = DELETED_ENTITY_MAP[entityID]; - } - Entities.editEntity(entityID, properties[i].properties); - selectedEntityIDs.push(entityID); - } - for (var i = 0; i < data.createEntities.length; i++) { - var entityID = data.createEntities[i].entityID; - var properties = data.createEntities[i].properties; - var newEntityID = Entities.addEntity(properties); - DELETED_ENTITY_MAP[entityID] = newEntityID; - if (data.selectCreated) { - selectedEntityIDs.push(newEntityID); - } - } - for (var i = 0; i < data.deleteEntities.length; i++) { - var entityID = data.deleteEntities[i].entityID; - if (DELETED_ENTITY_MAP[entityID] !== undefined) { - entityID = DELETED_ENTITY_MAP[entityID]; - } - Entities.deleteEntity(entityID); - } - - selectionManager.setSelections(selectedEntityIDs); -}; - -// For currently selected entities, push a command to the UndoStack that uses the current entity properties for the -// redo command, and the saved properties for the undo command. Also, include create and delete entity data. -function pushCommandForSelections(createdEntityData, deletedEntityData) { - var undoData = { - setProperties: [], - createEntities: deletedEntityData || [], - deleteEntities: createdEntityData || [], - selectCreated: true, - }; - var redoData = { - setProperties: [], - createEntities: createdEntityData || [], - deleteEntities: deletedEntityData || [], - selectCreated: false, - }; - for (var i = 0; i < SelectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID]; - var currentProperties = Entities.getEntityProperties(entityID); - undoData.setProperties.push({ - entityID: entityID, - properties: { - position: initialProperties.position, - rotation: initialProperties.rotation, - dimensions: initialProperties.dimensions, - }, - }); - redoData.setProperties.push({ - entityID: entityID, - properties: { - position: currentProperties.position, - rotation: currentProperties.rotation, - dimensions: currentProperties.dimensions, - }, - }); - } - UndoStack.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData); -} - -PropertiesTool = function(opts) { - var that = {}; - - var url = Script.resolvePath('html/entityProperties.html'); - var webView = new OverlayWebWindow({ - title: 'Entity Properties', - source: url, - toolWindow: true - }); - - var visible = false; - - webView.setVisible(visible); - - that.setVisible = function(newVisible) { - visible = newVisible; - webView.setVisible(visible); - }; - - selectionManager.addEventListener(function() { - data = { - type: 'update', - }; - var selections = []; - for (var i = 0; i < selectionManager.selections.length; i++) { - var entity = {}; - entity.id = selectionManager.selections[i]; - entity.properties = Entities.getEntityProperties(selectionManager.selections[i]); - if (entity.properties.rotation !== undefined) { - entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); - } - if (entity.properties.keyLight !== undefined && entity.properties.keyLight.direction !== undefined) { - entity.properties.keyLight.direction = Vec3.multiply(RADIANS_TO_DEGREES, Vec3.toPolar(entity.properties.keyLight.direction)); - entity.properties.keyLight.direction.z = 0.0; - } - selections.push(entity); - } - data.selections = selections; - webView.eventBridge.emitScriptEvent(JSON.stringify(data)); - }); - - webView.eventBridge.webEventReceived.connect(function(data) { - data = JSON.parse(data); - if (data.type == "print") { - if (data.message) { - print(data.message); - } - } else if (data.type == "update") { - selectionManager.saveProperties(); - if (selectionManager.selections.length > 1) { - properties = { - locked: data.properties.locked, - visible: data.properties.visible, - }; - for (var i = 0; i < selectionManager.selections.length; i++) { - Entities.editEntity(selectionManager.selections[i], properties); - } - } else { - if (data.properties.dynamic === false) { - // this object is leaving dynamic, so we zero its velocities - data.properties["velocity"] = {x: 0, y: 0, z: 0}; - data.properties["angularVelocity"] = {x: 0, y: 0, z: 0}; - } - if (data.properties.rotation !== undefined) { - var rotation = data.properties.rotation; - data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); - } - if (data.properties.keyLight !== undefined && data.properties.keyLight.direction !== undefined) { - data.properties.keyLight.direction = Vec3.fromPolar( - data.properties.keyLight.direction.x * DEGREES_TO_RADIANS, data.properties.keyLight.direction.y * DEGREES_TO_RADIANS); - } - Entities.editEntity(selectionManager.selections[0], data.properties); - if (data.properties.name != undefined) { - entityListTool.sendUpdate(); - } - } - pushCommandForSelections(); - selectionManager._update(); - } else if (data.type == "showMarketplace") { - showMarketplace(); - } else if (data.type == "action") { - if (data.action == "moveSelectionToGrid") { - if (selectionManager.hasSelection()) { - selectionManager.saveProperties(); - var dY = grid.getOrigin().y - (selectionManager.worldPosition.y - selectionManager.worldDimensions.y / 2); - var diff = { - x: 0, - y: dY, - z: 0 - }; - for (var i = 0; i < selectionManager.selections.length; i++) { - var properties = selectionManager.savedProperties[selectionManager.selections[i]]; - var newPosition = Vec3.sum(properties.position, diff); - Entities.editEntity(selectionManager.selections[i], { - position: newPosition, - }); - } - pushCommandForSelections(); - selectionManager._update(); - } - } else if (data.action == "moveAllToGrid") { - if (selectionManager.hasSelection()) { - selectionManager.saveProperties(); - for (var i = 0; i < selectionManager.selections.length; i++) { - var properties = selectionManager.savedProperties[selectionManager.selections[i]]; - var bottomY = properties.boundingBox.center.y - properties.boundingBox.dimensions.y / 2; - var dY = grid.getOrigin().y - bottomY; - var diff = { - x: 0, - y: dY, - z: 0 - }; - var newPosition = Vec3.sum(properties.position, diff); - Entities.editEntity(selectionManager.selections[i], { - position: newPosition, - }); - } - pushCommandForSelections(); - selectionManager._update(); - } - } else if (data.action == "resetToNaturalDimensions") { - if (selectionManager.hasSelection()) { - selectionManager.saveProperties(); - for (var i = 0; i < selectionManager.selections.length; i++) { - var properties = selectionManager.savedProperties[selectionManager.selections[i]]; - var naturalDimensions = properties.naturalDimensions; - - // If any of the natural dimensions are not 0, resize - if (properties.type == "Model" && naturalDimensions.x == 0 && naturalDimensions.y == 0 && naturalDimensions.z == 0) { - Window.alert("Cannot reset entity to its natural dimensions: Model URL" + " is invalid or the model has not yet been loaded."); - } else { - Entities.editEntity(selectionManager.selections[i], { - dimensions: properties.naturalDimensions, - }); - } - } - pushCommandForSelections(); - selectionManager._update(); - } - } else if (data.action == "previewCamera") { - if (selectionManager.hasSelection()) { - Camera.mode = "entity"; - Camera.cameraEntity = selectionManager.selections[0]; - } - } else if (data.action == "rescaleDimensions") { - var multiplier = data.percentage / 100; - if (selectionManager.hasSelection()) { - selectionManager.saveProperties(); - for (var i = 0; i < selectionManager.selections.length; i++) { - var properties = selectionManager.savedProperties[selectionManager.selections[i]]; - Entities.editEntity(selectionManager.selections[i], { - dimensions: Vec3.multiply(multiplier, properties.dimensions), - }); - } - pushCommandForSelections(); - selectionManager._update(); - } - } else if (data.action == "reloadScript") { - if (selectionManager.hasSelection()) { - var timestamp = Date.now(); - for (var i = 0; i < selectionManager.selections.length; i++) { - Entities.editEntity(selectionManager.selections[i], { - scriptTimestamp: timestamp, - }); - } - } - } - } - }); - - return that; -}; - -PopupMenu = function() { - var self = this; - - var MENU_ITEM_HEIGHT = 21; - var MENU_ITEM_SPACING = 1; - var TEXT_MARGIN = 7; - - var overlays = []; - var overlayInfo = {}; - - var upColor = { - red: 0, - green: 0, - blue: 0 - }; - var downColor = { - red: 192, - green: 192, - blue: 192 - }; - var overColor = { - red: 128, - green: 128, - blue: 128 - }; - - self.onSelectMenuItem = function() {}; - - self.addMenuItem = function(name) { - var id = Overlays.addOverlay("text", { - text: name, - backgroundAlpha: 1.0, - backgroundColor: upColor, - topMargin: TEXT_MARGIN, - leftMargin: TEXT_MARGIN, - width: 210, - height: MENU_ITEM_HEIGHT, - font: { - size: 12 - }, - visible: false, - }); - overlays.push(id); - overlayInfo[id] = { - name: name - }; - return id; - }; - - self.updateMenuItemText = function(id, newText) { - Overlays.editOverlay(id, { - text: newText - }); - }; - - self.setPosition = function(x, y) { - for (var key in overlayInfo) { - Overlays.editOverlay(key, { - x: x, - y: y, - }); - y += MENU_ITEM_HEIGHT + MENU_ITEM_SPACING; - } - }; - - self.onSelected = function() {}; - - var pressingOverlay = null; - var hoveringOverlay = null; - - self.mousePressEvent = function(event) { - if (event.isLeftButton) { - var overlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - if (overlay in overlayInfo) { - pressingOverlay = overlay; - Overlays.editOverlay(pressingOverlay, { - backgroundColor: downColor - }); - } else { - self.hide(); - } - return false; - } - }; - self.mouseMoveEvent = function(event) { - if (visible) { - var overlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - if (!pressingOverlay) { - if (hoveringOverlay != null && overlay != hoveringOverlay) { - Overlays.editOverlay(hoveringOverlay, { - backgroundColor: upColor - }); - hoveringOverlay = null; - } - if (overlay != hoveringOverlay && overlay in overlayInfo) { - Overlays.editOverlay(overlay, { - backgroundColor: overColor - }); - hoveringOverlay = overlay; - } - } - } - return false; - }; - self.mouseReleaseEvent = function(event) { - var overlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - if (pressingOverlay != null) { - if (overlay == pressingOverlay) { - self.onSelectMenuItem(overlayInfo[overlay].name); - } - Overlays.editOverlay(pressingOverlay, { - backgroundColor: upColor - }); - pressingOverlay = null; - self.hide(); - } - }; - - var visible = false; - - self.setVisible = function(newVisible) { - if (newVisible != visible) { - visible = newVisible; - for (var key in overlayInfo) { - Overlays.editOverlay(key, { - visible: newVisible - }); - } - } - } - self.show = function() { - self.setVisible(true); - } - self.hide = function() { - self.setVisible(false); - } - - function cleanup() { - for (var i = 0; i < overlays.length; i++) { - Overlays.deleteOverlay(overlays[i]); - } - } - - Controller.mousePressEvent.connect(self.mousePressEvent); - Controller.mouseMoveEvent.connect(self.mouseMoveEvent); - Controller.mouseReleaseEvent.connect(self.mouseReleaseEvent); - Script.scriptEnding.connect(cleanup); - - return this; -}; - -var propertyMenu = PopupMenu(); - -propertyMenu.onSelectMenuItem = function(name) { - if (propertyMenu.marketplaceID) { - showMarketplace(propertyMenu.marketplaceID); - } -}; - -var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); - -propertiesTool = PropertiesTool(); From 3f7a2eb10aa8cae9f3b8f9a815d15c3a640e1d76 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 22 Feb 2016 13:54:46 -0800 Subject: [PATCH 6/9] add support in depthReticle.js to set depth to overlay depth when you're over a 2D overlay --- examples/depthReticle.js | 35 +++++++++++++----------- interface/src/ui/ApplicationCompositor.h | 9 ++++++ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/examples/depthReticle.js b/examples/depthReticle.js index 4d72836577..4c93f714aa 100644 --- a/examples/depthReticle.js +++ b/examples/depthReticle.js @@ -6,24 +6,27 @@ Script.update.connect(function(deltaTime) { var timeSinceLastDepthCheck = Date.now() - lastDepthCheckTime; if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) { var reticlePosition = Reticle.position; - var pickRay = Camera.computePickRay(reticlePosition.x, reticlePosition.y); - var result = Overlays.findRayIntersection(pickRay); - - if (!result.intersects) { - result = Entities.findRayIntersection(pickRay, true); - } - if (result.intersects) { - // + JSON.stringify(result) - print("something hovered!! result.distance:" +result.distance); - Vec3.print("something hovered!! result.intersection:", result.intersection); - Reticle.setDepth(result.distance); + // first check the 2D Overlays + if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(reticlePosition)) { + print("intersecting with 2D overlay..."); + Reticle.setDepth(1.0); } else { - Reticle.setDepth(100.0); - print("NO INTERSECTION..."); - var pointAt2D = Reticle.position; - var pointAt3D = HMD.worldPointFromOverlay(pointAt2D); - } + var pickRay = Camera.computePickRay(reticlePosition.x, reticlePosition.y); + var result = Overlays.findRayIntersection(pickRay); + if (!result.intersects) { + result = Entities.findRayIntersection(pickRay, true); + } + if (result.intersects) { + // + JSON.stringify(result) + print("something hovered!! result.distance:" +result.distance); + Vec3.print("something hovered!! result.intersection:", result.intersection); + Reticle.setDepth(result.distance); + } else { + Reticle.setDepth(100.0); + print("NO INTERSECTION..."); + } + } } }); diff --git a/interface/src/ui/ApplicationCompositor.h b/interface/src/ui/ApplicationCompositor.h index 7337598d8f..3ee7c53f1b 100644 --- a/interface/src/ui/ApplicationCompositor.h +++ b/interface/src/ui/ApplicationCompositor.h @@ -105,6 +105,9 @@ public: bool shouldCaptureMouse() const; + /// if the reticle is pointing to a system overlay (a dialog box for example) then the function returns true otherwise false + bool isReticlePointingAtSystemOverlay() const { return _reticleOverQml; } + private: void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov); @@ -154,6 +157,8 @@ private: QPointF _lastKnownRealMouse; bool _ignoreMouseMove { false }; + bool _reticleOverQml { false }; + ReticleInterface* _reticleInterface; }; @@ -165,6 +170,7 @@ class ReticleInterface : public QObject { Q_PROPERTY(float depth READ getDepth WRITE setDepth) Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition) Q_PROPERTY(bool mouseCaptured READ isMouseCaptured) + Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay) public: ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {} @@ -182,6 +188,9 @@ public: Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } + Q_INVOKABLE bool isPointingAtSystemOverlay() { return _compositor->isReticlePointingAtSystemOverlay(); } + + private: ApplicationCompositor* _compositor; }; From 2db25690f1720b1e06286c04b2857aebd6544e53 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 23 Feb 2016 10:14:44 -0800 Subject: [PATCH 7/9] detect when mouse is over QML overlays or 2D JS Overlays --- interface/resources/qml/Stats.qml | 6 ++++++ interface/resources/qml/desktop/Desktop.qml | 1 + interface/resources/qml/desktop/FocusHack.qml | 1 + interface/resources/qml/hifi/Desktop.qml | 16 ++++++++++++++++ .../resources/qml/menus/MenuMouseHandler.qml | 2 ++ interface/src/Application.cpp | 2 ++ interface/src/ui/ApplicationCompositor.cpp | 2 +- interface/src/ui/ApplicationCompositor.h | 15 ++++++++------- 8 files changed, 37 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index d2f78191ea..660152ca0c 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -5,6 +5,8 @@ import QtQuick.Controls 1.2 Item { anchors.fill: parent anchors.leftMargin: 300 + objectName: "StatsItem" + Hifi.Stats { id: root objectName: "Stats" @@ -27,6 +29,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true } Column { @@ -83,6 +86,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true } Column { id: pingCol @@ -123,6 +127,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true } Column { id: geoCol @@ -172,6 +177,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true } Column { id: octreeCol diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 0286c45ac3..823b5cb923 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -304,6 +304,7 @@ FocusScope { Rectangle { id: focusDebugger; + objectName: "focusDebugger" z: 9999; visible: false; color: "red" ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 } } diff --git a/interface/resources/qml/desktop/FocusHack.qml b/interface/resources/qml/desktop/FocusHack.qml index 7514705983..14f2c62099 100644 --- a/interface/resources/qml/desktop/FocusHack.qml +++ b/interface/resources/qml/desktop/FocusHack.qml @@ -2,6 +2,7 @@ import QtQuick 2.5 FocusScope { id: root + objectName: "FocusHack" TextInput { id: textInput; diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 5227d3cb2e..dd9038e4b4 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -8,6 +8,22 @@ import ".." Desktop { id: desktop + MouseArea { + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + scrollGestureEnabled: false // we don't need/want these + onEntered: ApplicationCompositor.reticleOverDesktop = true + onExited: ApplicationCompositor.reticleOverDesktop = false + onClicked: mouse.accepted = false + onCanceled: mouse.accepted = false + onDoubleClicked: mouse.accepted = false + onPressed: mouse.accepted = false + onReleased: mouse.accepted = false + onWheel: mouse.accepted = false + onPositionChanged: mouse.accepted = false + } + Component.onCompleted: { WebEngine.settings.javascriptCanOpenWindows = true; WebEngine.settings.javascriptCanAccessClipboard = false; diff --git a/interface/resources/qml/menus/MenuMouseHandler.qml b/interface/resources/qml/menus/MenuMouseHandler.qml index bb2cc7d459..6f507dd445 100644 --- a/interface/resources/qml/menus/MenuMouseHandler.qml +++ b/interface/resources/qml/menus/MenuMouseHandler.qml @@ -6,9 +6,11 @@ import "." Item { id: root anchors.fill: parent + objectName: "MouseMenuHandlerItem" MouseArea { id: menuRoot; + objectName: "MouseMenuHandlerMouseArea" anchors.fill: parent enabled: d.topMenu !== null onClicked: { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64cd586e13..5c1f164fdb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1259,6 +1259,8 @@ void Application::initializeUi() { rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get()); rootContext->setContextProperty("Reticle", _compositor.getReticleInterface()); + rootContext->setContextProperty("ApplicationCompositor", &_compositor); + _glWidget->installEventFilter(offscreenUi.data()); offscreenUi->setMouseTranslator([=](const QPointF& pt) { QPointF result = pt; diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 4fd810e22f..5c6e4c9819 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -395,7 +395,7 @@ bool ApplicationCompositor::handleRealMouseMoveEvent(bool sendFakeEvent) { return false; // let the caller know to process the event } -glm::vec2 ApplicationCompositor::getReticlePosition() { +glm::vec2 ApplicationCompositor::getReticlePosition() const { if (qApp->isHMDMode()) { QMutexLocker locker(&_reticleLock); return _reticlePositionInHMD; diff --git a/interface/src/ui/ApplicationCompositor.h b/interface/src/ui/ApplicationCompositor.h index 3ee7c53f1b..7bdb97727f 100644 --- a/interface/src/ui/ApplicationCompositor.h +++ b/interface/src/ui/ApplicationCompositor.h @@ -47,6 +47,7 @@ class ApplicationCompositor : public QObject { Q_OBJECT Q_PROPERTY(float alpha READ getAlpha WRITE setAlpha) + Q_PROPERTY(bool reticleOverDesktop READ getReticleOverDesktop WRITE setReticleOverDesktop) public: ApplicationCompositor(); ~ApplicationCompositor(); @@ -84,14 +85,14 @@ public: float getAlpha() const { return _alpha; } void setAlpha(float alpha) { _alpha = alpha; } - bool getReticleVisible() { return _reticleVisible; } + bool getReticleVisible() const { return _reticleVisible; } void setReticleVisible(bool visible) { _reticleVisible = visible; } - float getReticleDepth() { return _reticleDepth; } + float getReticleDepth() const { return _reticleDepth; } void setReticleDepth(float depth) { _reticleDepth = depth; } void resetReticleDepth() { _reticleDepth = DEFAULT_RETICLE_DEPTH; } - glm::vec2 getReticlePosition(); + glm::vec2 getReticlePosition() const; void setReticlePosition(glm::vec2 position, bool sendFakeEvent = true); glm::vec2 getReticleMaximumPosition() const; @@ -106,9 +107,11 @@ public: bool shouldCaptureMouse() const; /// if the reticle is pointing to a system overlay (a dialog box for example) then the function returns true otherwise false - bool isReticlePointingAtSystemOverlay() const { return _reticleOverQml; } + bool getReticleOverDesktop() const { return _isOverDesktop; } + void setReticleOverDesktop(bool value) { _isOverDesktop = value; } private: + bool _isOverDesktop { true }; void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov); void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); @@ -176,6 +179,7 @@ public: ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {} Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); } + Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); } Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); } Q_INVOKABLE void setVisible(bool visible) { _compositor->setReticleVisible(visible); } @@ -188,9 +192,6 @@ public: Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } - Q_INVOKABLE bool isPointingAtSystemOverlay() { return _compositor->isReticlePointingAtSystemOverlay(); } - - private: ApplicationCompositor* _compositor; }; From 71262c3e646b462d11ea060fd886d04fdfaacbe5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 23 Feb 2016 11:14:16 -0800 Subject: [PATCH 8/9] cleaner QML rejection of mouse messages --- interface/resources/qml/hifi/Desktop.qml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index dd9038e4b4..409c468878 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -15,13 +15,7 @@ Desktop { scrollGestureEnabled: false // we don't need/want these onEntered: ApplicationCompositor.reticleOverDesktop = true onExited: ApplicationCompositor.reticleOverDesktop = false - onClicked: mouse.accepted = false - onCanceled: mouse.accepted = false - onDoubleClicked: mouse.accepted = false - onPressed: mouse.accepted = false - onReleased: mouse.accepted = false - onWheel: mouse.accepted = false - onPositionChanged: mouse.accepted = false + acceptedButtons: Qt.NoButton } Component.onCompleted: { From a71a78b2d344a97cf53b43c166c69b5cea39b0cd Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 23 Feb 2016 11:22:24 -0800 Subject: [PATCH 9/9] add comments and license header to reticleDepth.js --- examples/depthReticle.js | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/examples/depthReticle.js b/examples/depthReticle.js index 4c93f714aa..a60e61d07c 100644 --- a/examples/depthReticle.js +++ b/examples/depthReticle.js @@ -1,6 +1,19 @@ +// depthReticle.js +// examples +// +// Created by Brad Hefta-Gaub on 2/23/16. +// Copyright 2016 High Fidelity, Inc. +// +// When used in HMD, this script will make the reticle depth track to any clickable item in view. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var APPARENT_2D_OVERLAY_DEPTH = 1.0; +var APPARENT_MAXIMUM_DEPTH = 100.0; // this is a depth at which things all seem sufficiently distant var lastDepthCheckTime = 0; -// Do some stuff regularly, like check for placement of various overlays Script.update.connect(function(deltaTime) { var TIME_BETWEEN_DEPTH_CHECKS = 100; var timeSinceLastDepthCheck = Date.now() - lastDepthCheckTime; @@ -9,23 +22,25 @@ Script.update.connect(function(deltaTime) { // first check the 2D Overlays if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(reticlePosition)) { - print("intersecting with 2D overlay..."); - Reticle.setDepth(1.0); + Reticle.setDepth(APPARENT_2D_OVERLAY_DEPTH); } else { var pickRay = Camera.computePickRay(reticlePosition.x, reticlePosition.y); + + // Then check the 3D overlays var result = Overlays.findRayIntersection(pickRay); if (!result.intersects) { + // finally check the entities result = Entities.findRayIntersection(pickRay, true); } + + // If either the overlays or entities intersect, then set the reticle depth to + // the distance of intersection if (result.intersects) { - // + JSON.stringify(result) - print("something hovered!! result.distance:" +result.distance); - Vec3.print("something hovered!! result.intersection:", result.intersection); Reticle.setDepth(result.distance); } else { - Reticle.setDepth(100.0); - print("NO INTERSECTION..."); + // if nothing intersects... set the depth to some sufficiently large depth + Reticle.setDepth(APPARENT_MAXIMUM_DEPTH); } } }