diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index e3ab2c51b8..eb1c478489 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -176,7 +176,7 @@ "type": "checkbox", "label": "Enabled", "help": "Assigns an asset-server in your domain to serve files to clients via the ATP protocol (over UDP)", - "default": false, + "default": true, "advanced": true } ] diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 05dfc8e5d5..f4bc7496da 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -480,7 +480,7 @@ function MyController(hand, triggerAction) { var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("kinematic-hold", this.grabbedEntity, { + this.actionID = Entities.addAction("hold", this.grabbedEntity, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: offsetPosition, diff --git a/examples/edit.js b/examples/edit.js index 0afc3ec1b6..09d30c712b 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -39,7 +39,9 @@ var lightOverlayManager = new LightOverlayManager(); var cameraManager = new CameraManager(); var grid = Grid(); -gridTool = GridTool({ horizontalGrid: grid }); +gridTool = GridTool({ + horizontalGrid: grid +}); gridTool.setVisible(false); var entityListTool = EntityListTool(); @@ -92,8 +94,8 @@ var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissi 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" - ]; + "Insert the URL to your FBX" +]; var mode = 0; var isActive = false; @@ -109,20 +111,30 @@ var importingSVOImageOverlay = Overlays.addOverlay("image", { width: 20, height: 20, alpha: 1.0, - color: { red: 255, green: 255, blue: 255 }, + 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 }, + 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 }, + backgroundColor: { + red: 80, + green: 80, + blue: 80 + }, backgroundAlpha: 0.7, visible: false, }); @@ -131,7 +143,7 @@ var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false); marketplaceWindow.setVisible(false); -var toolBar = (function () { +var toolBar = (function() { var that = {}, toolBar, activeButton, @@ -146,7 +158,7 @@ var toolBar = (function () { browseMarketplaceButton; function initialize() { - toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function (windowDimensions, toolbar) { + 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 @@ -163,7 +175,12 @@ var toolBar = (function () { activeButton = toolBar.addTool({ imageURL: toolIconUrl + "edit-status.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -172,7 +189,12 @@ var toolBar = (function () { newModelButton = toolBar.addTool({ imageURL: toolIconUrl + "upload.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -181,7 +203,12 @@ var toolBar = (function () { newCubeButton = toolBar.addTool({ imageURL: toolIconUrl + "add-cube.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -190,7 +217,12 @@ var toolBar = (function () { newSphereButton = toolBar.addTool({ imageURL: toolIconUrl + "add-sphere.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -199,7 +231,12 @@ var toolBar = (function () { newLightButton = toolBar.addTool({ imageURL: toolIconUrl + "light.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -208,7 +245,12 @@ var toolBar = (function () { newTextButton = toolBar.addTool({ imageURL: toolIconUrl + "add-text.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -217,7 +259,12 @@ var toolBar = (function () { newWebButton = toolBar.addTool({ imageURL: "https://hifi-public.s3.amazonaws.com/images/www.svg", - subImage: { x: 0, y: 0, width: 128, height: 128 }, + subImage: { + x: 0, + y: 0, + width: 128, + height: 128 + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -226,7 +273,12 @@ var toolBar = (function () { newZoneButton = toolBar.addTool({ imageURL: toolIconUrl + "zonecube_text.svg", - subImage: { x: 0, y: 128, width: 128, height: 128 }, + subImage: { + x: 0, + y: 128, + width: 128, + height: 128 + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -235,7 +287,12 @@ var toolBar = (function () { newPolyVoxButton = toolBar.addTool({ imageURL: toolIconUrl + "polyvox.svg", - subImage: { x: 0, y: 0, width: 256, height: 256 }, + subImage: { + x: 0, + y: 0, + width: 256, + height: 256 + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -293,6 +350,7 @@ var toolBar = (function () { 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", @@ -314,7 +372,7 @@ var toolBar = (function () { var entityID = null; if (position != null) { position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions), - properties.position = position; + properties.position = position; entityID = Entities.addEntity(properties); if (dragOnCreate) { @@ -329,12 +387,15 @@ var toolBar = (function () { var newModelButtonDown = false; var browseMarketplaceButtonDown = false; - that.mousePressEvent = function (event) { + that.mousePressEvent = function(event) { var clickedOverlay, url, file; - clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (activeButton === toolBar.clicked(clickedOverlay)) { that.setActive(!isActive); @@ -360,7 +421,11 @@ var toolBar = (function () { createNewEntity({ type: "Box", dimensions: DEFAULT_DIMENSIONS, - color: { red: 255, green: 0, blue: 0 } + color: { + red: 255, + green: 0, + blue: 0 + } }); return true; @@ -370,7 +435,11 @@ var toolBar = (function () { createNewEntity({ type: "Sphere", dimensions: DEFAULT_DIMENSIONS, - color: { red: 255, green: 0, blue: 0 } + color: { + red: 255, + green: 0, + blue: 0 + } }); return true; @@ -381,7 +450,11 @@ var toolBar = (function () { type: "Light", dimensions: DEFAULT_LIGHT_DIMENSIONS, isSpotlight: false, - color: { red: 150, green: 150, blue: 150 }, + color: { + red: 150, + green: 150, + blue: 150 + }, constantAttenuation: 1, linearAttenuation: 0, @@ -396,9 +469,21 @@ var toolBar = (function () { 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 }, + 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 }); @@ -409,7 +494,11 @@ var toolBar = (function () { if (newWebButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ type: "Web", - dimensions: { x: 1.6, y: 0.9, z: 0.01 }, + dimensions: { + x: 1.6, + y: 0.9, + z: 0.01 + }, sourceUrl: "https://highfidelity.com/", }); @@ -419,7 +508,11 @@ var toolBar = (function () { if (newZoneButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ type: "Zone", - dimensions: { x: 10, y: 10, z: 10 }, + dimensions: { + x: 10, + y: 10, + z: 10 + }, }); return true; @@ -428,27 +521,83 @@ var toolBar = (function () { if (newPolyVoxButton === toolBar.clicked(clickedOverlay)) { var polyVoxId = createNewEntity({ type: "PolyVox", - dimensions: { x: 10, y: 10, z: 10 }, - voxelVolumeSize: {x:16, y:16, z:16}, + 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); + 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); + 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); + 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); } @@ -461,7 +610,10 @@ var toolBar = (function () { that.mouseReleaseEvent = function(event) { var handled = false; if (newModelButtonDown) { - var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + 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 !== "") { @@ -470,7 +622,10 @@ var toolBar = (function () { handled = true; } } else if (browseMarketplaceButtonDown) { - var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { url = Window.s3Browse(".*(fbx|FBX|obj|OBJ)"); if (url !== null && url !== "") { @@ -497,7 +652,7 @@ var toolBar = (function () { } }); - that.cleanup = function () { + that.cleanup = function() { toolBar.cleanup(); }; @@ -568,17 +723,26 @@ function findClickedEntity(event) { } var foundEntity = result.entityID; - return { pickRay: pickRay, entityID: foundEntity }; + return { + pickRay: pickRay, + entityID: foundEntity + }; } var mouseHasMovedSincePress = false; var mousePressStartTime = 0; -var mousePressStartPosition = { x: 0, y: 0 }; +var mousePressStartPosition = { + x: 0, + y: 0 +}; var mouseDown = false; function mousePressEvent(event) { mouseDown = true; - mousePressStartPosition = { x: event.x, y: event.y }; + mousePressStartPosition = { + x: event.x, + y: event.y + }; mousePressStartTime = Date.now(); mouseHasMovedSincePress = false; mouseCapturedByTool = false; @@ -605,9 +769,11 @@ 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; @@ -647,7 +813,10 @@ function mouseMove(event) { return; } - lastMousePosition = { x: event.x, y: event.y }; + lastMousePosition = { + x: event.x, + y: event.y + }; idleMouseTimerId = Script.setTimeout(handleIdleMouse, IDLE_MOUSE_TIMEOUT); } @@ -660,22 +829,25 @@ 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) { + if (highlightedEntityID && highlightedEntityID != entityIntersection.entityID) { selectionDisplay.unhighlightSelectable(highlightedEntityID); - highlightedEntityID = { id: -1 }; + 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; + entityIntersection.properties.position)) * 180 / 3.14; - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); if (entityIntersection.entityID && sizeOK) { if (wantEntityGlow) { - Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 }); + Entities.editEntity(entityIntersection.entityID, { + glowLevel: 0.25 + }); } highlightedEntityID = entityIntersection.entityID; selectionDisplay.highlightSelectable(entityIntersection.entityID); @@ -693,18 +865,23 @@ function mouseReleaseEvent(event) { 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; } @@ -757,8 +934,7 @@ function mouseClickEvent(event) { var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); if (0 < x && sizeOK) { entitySelected = true; @@ -779,8 +955,8 @@ function mouseClickEvent(event) { if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } } @@ -813,44 +989,125 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent); // added it. var modelMenuAddedDelete = false; var originalLightsArePickable = Entities.getLightsArePickable(); + function setupModelMenus() { print("setupModelMenus()"); // adj our menuitems - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Models", + isSeparator: true, + beforeItem: "Physics" + }); if (!Menu.menuItemExists("Edit", "Delete")) { print("no delete... adding ours"); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", - shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Delete", + shortcutKeyEvent: { + text: "backspace" + }, + afterItem: "Models" + }); modelMenuAddedDelete = true; } else { print("delete exists... don't add ours"); } - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Entity List...", shortcutKey: "CTRL+META+L", afterItem: "Models" }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L", - afterItem: "Entity List...", isCheckable: true, isChecked: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S", - afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L", - afterItem: "Allow Selecting of Small Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities In Box", shortcutKey: "CTRL+SHIFT+META+A", - afterItem: "Allow Selecting of Lights" }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities Touching Box", shortcutKey: "CTRL+SHIFT+META+T", - afterItem: "Select All Entities In Box" }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Entity List...", + shortcutKey: "CTRL+META+L", + afterItem: "Models" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Large Models", + shortcutKey: "CTRL+META+L", + afterItem: "Entity List...", + isCheckable: true, + isChecked: true + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Small Models", + shortcutKey: "CTRL+META+S", + afterItem: "Allow Selecting of Large Models", + isCheckable: true, + isChecked: true + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Lights", + shortcutKey: "CTRL+SHIFT+META+L", + afterItem: "Allow Selecting of Small Models", + isCheckable: true + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Select All Entities In Box", + shortcutKey: "CTRL+SHIFT+META+A", + afterItem: "Allow Selecting of Lights" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Select All Entities Touching Box", + shortcutKey: "CTRL+SHIFT+META+T", + afterItem: "Select All Entities In Box" + }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities", shortcutKey: "CTRL+META+I", afterItem: "Export Entities" }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities from URL", shortcutKey: "CTRL+META+U", afterItem: "Import Entities" }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Models", + isSeparator: true, + beforeItem: "Settings" + }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Export Entities", + shortcutKey: "CTRL+META+E", + afterItem: "Models" + }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Import Entities", + shortcutKey: "CTRL+META+I", + afterItem: "Export Entities" + }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Import Entities from URL", + shortcutKey: "CTRL+META+U", + afterItem: "Import Entities" + }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, afterItem: MENU_EASE_ON_FOCUS, - isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" }); - Menu.addMenuItem({ menuName: "View", 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" }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_AUTO_FOCUS_ON_SELECT, + isCheckable: true, + isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" + }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_EASE_ON_FOCUS, + afterItem: MENU_AUTO_FOCUS_ON_SELECT, + isCheckable: true, + isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" + }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + afterItem: MENU_EASE_ON_FOCUS, + isCheckable: true, + isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" + }); + Menu.addMenuItem({ + menuName: "View", + 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" + }); Entities.setLightsArePickable(false); } @@ -903,7 +1160,7 @@ var lastOrientation = null; var lastPosition = null; // Do some stuff regularly, like check for placement of various overlays -Script.update.connect(function (deltaTime) { +Script.update.connect(function(deltaTime) { progressDialog.move(); selectionDisplay.checkMove(); var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1); @@ -920,16 +1177,14 @@ Script.update.connect(function (deltaTime) { }); 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)); + 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)); + Vec3.multiply(selectionManager.worldDimensions, 0.5)); var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions); if (!keepIfTouching) { @@ -941,9 +1196,13 @@ function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { } 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); + Vec3.subtract(position, + selectionManager.localPosition)); + return insideBox({ + x: 0, + y: 0, + z: 0 + }, selectionManager.localDimensions, localPosition); } } for (var i = 0; i < entities.length; ++i) { @@ -1038,15 +1297,11 @@ function getPositionToCreateEntity() { 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; + 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; @@ -1065,14 +1320,22 @@ function importSVO(importURL) { return; } - Overlays.editOverlay(importingSVOTextOverlay, { visible: true }); - Overlays.editOverlay(importingSVOImageOverlay, { visible: true }); + 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 }; + var position = { + x: 0, + y: 0, + z: 0 + }; if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) { position = getPositionToCreateEntity(); } @@ -1091,8 +1354,12 @@ function importSVO(importURL) { Window.alert("There was an error importing the entity file."); } - Overlays.editOverlay(importingSVOTextOverlay, { visible: false }); - Overlays.editOverlay(importingSVOImageOverlay, { visible: false }); + Overlays.editOverlay(importingSVOTextOverlay, { + visible: false + }); + Overlays.editOverlay(importingSVOImageOverlay, { + visible: false + }); } Window.svoImportRequested.connect(importSVO); @@ -1104,7 +1371,7 @@ Controller.keyPressEvent.connect(function(event) { } }); -Controller.keyReleaseEvent.connect(function (event) { +Controller.keyReleaseEvent.connect(function(event) { if (isActive) { cameraManager.keyReleaseEvent(event); } @@ -1120,8 +1387,8 @@ Controller.keyReleaseEvent.connect(function (event) { if (selectionManager.hasSelection()) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } } else if (event.text == '[') { @@ -1131,7 +1398,11 @@ Controller.keyReleaseEvent.connect(function (event) { } 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 }); + newPosition = Vec3.subtract(newPosition, { + x: 0, + y: selectionManager.worldDimensions.y * 0.5, + z: 0 + }); grid.setPosition(newPosition); } } @@ -1140,8 +1411,7 @@ Controller.keyReleaseEvent.connect(function (event) { // 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 = { -} +DELETED_ENTITY_MAP = {} function applyEntityProperties(data) { var properties = data.setProperties; @@ -1293,7 +1563,11 @@ PropertiesTool = function(opts) { 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 }; + 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); @@ -1311,7 +1585,11 @@ PropertiesTool = function(opts) { 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 diff = { + x: 0, + y: dY, + z: 0 + }; var newPosition = Vec3.sum(properties.position, diff); Entities.editEntity(selectionManager.selections[i], { position: newPosition, @@ -1328,10 +1606,8 @@ PropertiesTool = function(opts) { 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."); + 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, @@ -1370,12 +1646,16 @@ PropertiesTool = function(opts) { var properties = selectionManager.savedProperties[selectionManager.selections[i]]; if (properties.type == "Zone") { var centerOfZone = properties.boundingBox.center; - var atmosphereCenter = { x: centerOfZone.x, - y: centerOfZone.y - properties.atmosphere.innerRadius, - z: centerOfZone.z }; + var atmosphereCenter = { + x: centerOfZone.x, + y: centerOfZone.y - properties.atmosphere.innerRadius, + z: centerOfZone.z + }; Entities.editEntity(selectionManager.selections[i], { - atmosphere: { center: atmosphereCenter }, + atmosphere: { + center: atmosphereCenter + }, }); } } @@ -1399,11 +1679,23 @@ PopupMenu = function() { 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 }; + 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.onSelectMenuItem = function() {}; self.addMenuItem = function(name) { var id = Overlays.addOverlay("text", { @@ -1414,16 +1706,22 @@ PopupMenu = function() { leftMargin: TEXT_MARGIN, width: 210, height: MENU_ITEM_HEIGHT, - font: { size: 12 }, + font: { + size: 12 + }, visible: false, }); overlays.push(id); - overlayInfo[id] = { name: name }; + overlayInfo[id] = { + name: name + }; return id; }; self.updateMenuItemText = function(id, newText) { - Overlays.editOverlay(id, { text: newText }); + Overlays.editOverlay(id, { + text: newText + }); }; self.setPosition = function(x, y) { @@ -1436,17 +1734,22 @@ PopupMenu = function() { } }; - self.onSelected = function() { }; + 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 }); + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (overlay in overlayInfo) { pressingOverlay = overlay; - Overlays.editOverlay(pressingOverlay, { backgroundColor: downColor }); + Overlays.editOverlay(pressingOverlay, { + backgroundColor: downColor + }); } else { self.hide(); } @@ -1455,14 +1758,21 @@ PopupMenu = function() { }; self.mouseMoveEvent = function(event) { if (visible) { - var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (!pressingOverlay) { if (hoveringOverlay != null && overlay != hoveringOverlay) { - Overlays.editOverlay(hoveringOverlay, { backgroundColor: upColor}); + Overlays.editOverlay(hoveringOverlay, { + backgroundColor: upColor + }); hoveringOverlay = null; } if (overlay != hoveringOverlay && overlay in overlayInfo) { - Overlays.editOverlay(overlay, { backgroundColor: overColor }); + Overlays.editOverlay(overlay, { + backgroundColor: overColor + }); hoveringOverlay = overlay; } } @@ -1470,12 +1780,17 @@ PopupMenu = function() { return false; }; self.mouseReleaseEvent = function(event) { - var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + 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 }); + Overlays.editOverlay(pressingOverlay, { + backgroundColor: upColor + }); pressingOverlay = null; self.hide(); } @@ -1487,7 +1802,9 @@ PopupMenu = function() { if (newVisible != visible) { visible = newVisible; for (var key in overlayInfo) { - Overlays.editOverlay(key, { visible: newVisible }); + Overlays.editOverlay(key, { + visible: newVisible + }); } } } @@ -1527,4 +1844,4 @@ propertyMenu.onSelectMenuItem = function(name) { var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); -propertiesTool = PropertiesTool(); +propertiesTool = PropertiesTool(); \ No newline at end of file diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js index 6db7b38705..e678ff26eb 100644 --- a/examples/example/scripts/controllerScriptingExamples.js +++ b/examples/example/scripts/controllerScriptingExamples.js @@ -12,6 +12,20 @@ // Assumes you only have the default keyboard connected + +var hydra = Controller.Hardware.Hydra2; +if (hydra !== undefined) { + print("-----------------------------------"); + var mapping = NewControllers.newMapping("Default"); + var standard = Controller.Standard; + print("standard:" + standard); + mapping.from(hydra.LeftButton1).to(standard.A); + mapping.from(hydra.LeftButton2).to(standard.B); + mapping.from(hydra.LeftButton3).to(standard.X); + NewControllers.enableMapping("Default"); + print("-----------------------------------"); +} + Object.keys(Controller.Standard).forEach(function (input) { print("Controller.Standard." + input + ":" + Controller.Standard[input]); }); diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index a3b817e300..d304a6382e 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -118,8 +118,16 @@ CameraManager = function() { that.targetYaw = 0; that.targetPitch = 0; - that.focalPoint = { x: 0, y: 0, z: 0 }; - that.targetFocalPoint = { x: 0, y: 0, z: 0 }; + that.focalPoint = { + x: 0, + y: 0, + z: 0 + }; + that.targetFocalPoint = { + x: 0, + y: 0, + z: 0 + }; easing = false; easingTime = 0; @@ -127,13 +135,18 @@ CameraManager = function() { that.previousCameraMode = null; - that.lastMousePosition = { x: 0, y: 0 }; + that.lastMousePosition = { + x: 0, + y: 0 + }; that.enable = function() { if (Camera.mode == "independent" || that.enabled) return; for (var i = 0; i < CAPTURED_KEYS.length; i++) { - Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] }); + Controller.captureKeyEvents({ + text: CAPTURED_KEYS[i] + }); } that.enabled = true; @@ -143,7 +156,7 @@ CameraManager = function() { that.zoomDistance = INITIAL_ZOOM_DISTANCE; that.targetZoomDistance = that.zoomDistance + 3.0; var focalPoint = Vec3.sum(Camera.getPosition(), - Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); + Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); // Determine the correct yaw and pitch to keep the camera in the same location var dPos = Vec3.subtract(focalPoint, Camera.getPosition()); @@ -169,7 +182,9 @@ CameraManager = function() { if (!that.enabled) return; for (var i = 0; i < CAPTURED_KEYS.length; i++) { - Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] }); + Controller.releaseKeyEvents({ + text: CAPTURED_KEYS[i] + }); } that.enabled = false; @@ -335,19 +350,27 @@ CameraManager = function() { var hasDragged = false; that.mousePressEvent = function(event) { + if (cameraTool.mousePressEvent(event)) { + return true; } + if (!that.enabled) return; if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) { + that.mode = MODE_ORBIT; } else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) { + + that.mode = MODE_PAN; } - if (that.mode != MODE_INACTIVE) { + if (that.mode !== MODE_INACTIVE) { + + hasDragged = false; return true; @@ -357,10 +380,12 @@ CameraManager = function() { } that.mouseReleaseEvent = function(event) { + if (!that.enabled) return; - Window.setCursorVisible(true); that.mode = MODE_INACTIVE; + Window.setCursorVisible(true); + } that.keyPressEvent = function(event) { @@ -396,15 +421,31 @@ CameraManager = function() { return; } - var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 }); - var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 }); + var yRot = Quat.angleAxis(that.yaw, { + x: 0, + y: 1, + z: 0 + }); + var xRot = Quat.angleAxis(that.pitch, { + x: 1, + y: 0, + z: 0 + }); var q = Quat.multiply(yRot, xRot); var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance); Camera.setPosition(Vec3.sum(that.focalPoint, pos)); - yRot = Quat.angleAxis(that.yaw - 180, { x: 0, y: 1, z: 0 }); - xRot = Quat.angleAxis(-that.pitch, { x: 1, y: 0, z: 0 }); + yRot = Quat.angleAxis(that.yaw - 180, { + x: 0, + y: 1, + z: 0 + }); + xRot = Quat.angleAxis(-that.pitch, { + x: 1, + y: 0, + z: 0 + }); q = Quat.multiply(yRot, xRot); if (easing) { @@ -483,7 +524,7 @@ CameraManager = function() { } }); - Controller.keyReleaseEvent.connect(function (event) { + Controller.keyReleaseEvent.connect(function(event) { if (event.text == "ESC" && that.enabled) { Camera.mode = lastAvatarCameraMode; cameraManager.disable(true); @@ -503,9 +544,21 @@ CameraManager = function() { CameraTool = function(cameraManager) { var that = {}; - var RED = { red: 191, green: 78, blue: 38 }; - var GREEN = { red: 26, green: 193, blue: 105 }; - var BLUE = { red: 0, green: 131, blue: 204 }; + var RED = { + red: 191, + green: 78, + blue: 38 + }; + var GREEN = { + red: 26, + green: 193, + blue: 105 + }; + var BLUE = { + red: 0, + green: 131, + blue: 204 + }; var BORDER_WIDTH = 1; @@ -513,10 +566,10 @@ CameraTool = function(cameraManager) { var ORIENTATION_OVERLAY_HALF_SIZE = ORIENTATION_OVERLAY_SIZE / 2; var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5, - var ORIENTATION_OVERLAY_OFFSET = { - x: 30, - y: 30, - } + var ORIENTATION_OVERLAY_OFFSET = { + x: 30, + y: 30, + } var UI_WIDTH = 70; var UI_HEIGHT = 70; @@ -536,7 +589,11 @@ CameraTool = function(cameraManager) { height: UI_HEIGHT + BORDER_WIDTH * 2, alpha: 0, text: "", - backgroundColor: { red: 101, green: 101, blue: 101 }, + backgroundColor: { + red: 101, + green: 101, + blue: 101 + }, backgroundAlpha: 1.0, visible: false, }); @@ -548,7 +605,11 @@ CameraTool = function(cameraManager) { height: UI_HEIGHT, alpha: 0, text: "", - backgroundColor: { red: 51, green: 51, blue: 51 }, + backgroundColor: { + red: 51, + green: 51, + blue: 51 + }, backgroundAlpha: 1.0, visible: false, }); @@ -556,7 +617,11 @@ CameraTool = function(cameraManager) { var defaultCubeProps = { size: ORIENTATION_OVERLAY_CUBE_SIZE, alpha: 1, - color: { red: 255, green: 0, blue: 0 }, + color: { + red: 255, + green: 0, + blue: 0 + }, solid: true, visible: true, drawOnHUD: true, @@ -564,10 +629,26 @@ CameraTool = function(cameraManager) { var defaultLineProps = { lineWidth: 1.5, alpha: 1, - position: { x: 0, y: 0, z: 0 }, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, + position: { + x: 0, + y: 0, + z: 0 + }, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 255, + green: 0, + blue: 0 + }, visible: false, drawOnHUD: true, }; @@ -582,30 +663,66 @@ CameraTool = function(cameraManager) { var OOHS = ORIENTATION_OVERLAY_HALF_SIZE; var cubeX = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { - position: { x: -OOHS, y: OOHS, z: OOHS }, + position: { + x: -OOHS, + y: OOHS, + z: OOHS + }, color: RED, })); var cubeY = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { - position: { x: OOHS, y: -OOHS, z: OOHS }, + position: { + x: OOHS, + y: -OOHS, + z: OOHS + }, color: GREEN, })); var cubeZ = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { - position: { x: OOHS, y: OOHS, z: -OOHS }, + position: { + x: OOHS, + y: OOHS, + z: -OOHS + }, color: BLUE, })); orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { - start: { x: -OOHS, y: OOHS, z: OOHS }, - end: { x: OOHS, y: OOHS, z: OOHS }, + start: { + x: -OOHS, + y: OOHS, + z: OOHS + }, + end: { + x: OOHS, + y: OOHS, + z: OOHS + }, color: RED, })); orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { - start: { x: OOHS, y: -OOHS, z: OOHS }, - end: { x: OOHS, y: OOHS, z: OOHS }, + start: { + x: OOHS, + y: -OOHS, + z: OOHS + }, + end: { + x: OOHS, + y: OOHS, + z: OOHS + }, color: GREEN, })); orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { - start: { x: OOHS, y: OOHS, z: -OOHS }, - end: { x: OOHS, y: OOHS, z: OOHS }, + start: { + x: OOHS, + y: OOHS, + z: -OOHS + }, + end: { + x: OOHS, + y: OOHS, + z: OOHS + }, color: BLUE, })); @@ -645,7 +762,10 @@ CameraTool = function(cameraManager) { } that.mousePressEvent = function(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (clickedOverlay == cubeX) { targetPitch = 0; @@ -666,12 +786,18 @@ CameraTool = function(cameraManager) { }; that.setVisible = function(visible) { - orientationOverlay.setProperties({ visible: visible }); - Overlays.editOverlay(background, { visible: visible }); - Overlays.editOverlay(backgroundBorder, { visible: visible }); + orientationOverlay.setProperties({ + visible: visible + }); + Overlays.editOverlay(background, { + visible: visible + }); + Overlays.editOverlay(backgroundBorder, { + visible: visible + }); }; that.setVisible(false); return that; -}; +}; \ No newline at end of file diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index dceecddfe0..682e0be1bf 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -16,24 +16,38 @@ { "jointName": "RightHand", "positionVar": "rightHandPosition", - "rotationVar": "rightHandRotation" + "rotationVar": "rightHandRotation", + "typeVar": "rightHandType" }, { "jointName": "LeftHand", "positionVar": "leftHandPosition", - "rotationVar": "leftHandRotation" + "rotationVar": "leftHandRotation", + "typeVar": "leftHandType" + }, + { + "jointName": "RightFoot", + "positionVar": "rightFootPosition", + "rotationVar": "rightFootRotation", + "typeVar": "rightFootType" + }, + { + "jointName": "LeftFoot", + "positionVar": "leftFootPosition", + "rotationVar": "leftFootRotation", + "typeVar": "leftFootType" }, { "jointName": "Neck", "positionVar": "neckPosition", "rotationVar": "neckRotation", - "typeVar": "headAndNeckType" + "typeVar": "neckType" }, { "jointName": "Head", "positionVar": "headPosition", "rotationVar": "headRotation", - "typeVar": "headAndNeckType" + "typeVar": "headType" } ] }, @@ -51,7 +65,7 @@ "id": "spineLean", "type": "manipulator", "data": { - "alpha": 1.0, + "alpha": 0.0, "joints": [ { "var": "lean", "jointName": "Spine" } ] diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cfeb77f638..fd2f4e3ebc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -126,6 +126,7 @@ #include "Stars.h" #include "ui/AddressBarDialog.h" #include "ui/AvatarInputs.h" +#include "ui/AssetUploadDialogFactory.h" #include "ui/DataWebDialog.h" #include "ui/DialogsManager.h" #include "ui/LoginDialog.h" @@ -159,6 +160,7 @@ static const QString SVO_JSON_EXTENSION = ".svo.json"; static const QString JS_EXTENSION = ".js"; static const QString FST_EXTENSION = ".fst"; static const QString FBX_EXTENSION = ".fbx"; +static const QString OBJ_EXTENSION = ".obj"; static const int MIRROR_VIEW_TOP_PADDING = 5; static const int MIRROR_VIEW_LEFT_PADDING = 10; @@ -179,9 +181,6 @@ static const unsigned int THROTTLED_SIM_FRAMERATE = 15; static const int TARGET_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / TARGET_SIM_FRAMERATE; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; -const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml"; -const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; - #ifndef __APPLE__ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); #else @@ -197,8 +196,7 @@ const QHash Application::_acceptedExtensi { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, - { FST_EXTENSION, &Application::askToSetAvatarUrl }, - { FBX_EXTENSION, &Application::askToUploadAsset } + { FST_EXTENSION, &Application::askToSetAvatarUrl } }; #ifdef Q_OS_WIN @@ -1892,16 +1890,6 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { computePickRay(mappedEvent.x(), mappedEvent.y())); sendEvent(this, &actionEvent); - } else if (event->button() == Qt::RightButton) { - // "right click" on controllers to toggle the overlay - if (deviceID > 0) { - _overlayConductor.setEnabled(!_overlayConductor.getEnabled()); - } - } else if (event->button() == Qt::MiddleButton) { - // mouse middle click to toggle the overlay - if (deviceID == 0) { - _overlayConductor.setEnabled(!_overlayConductor.getEnabled()); - } } } } @@ -2023,21 +2011,14 @@ void Application::dropEvent(QDropEvent *event) { const QMimeData* mimeData = event->mimeData(); for (auto& url : mimeData->urls()) { QString urlString = url.toString(); - if (canAcceptURL(urlString) && acceptURL(urlString)) { + if (acceptURL(urlString, true)) { event->acceptProposedAction(); } } } void Application::dragEnterEvent(QDragEnterEvent* event) { - const QMimeData* mimeData = event->mimeData(); - for (auto& url : mimeData->urls()) { - auto urlString = url.toString(); - if (canAcceptURL(urlString)) { - event->acceptProposedAction(); - break; - } - } + event->acceptProposedAction(); } bool Application::acceptSnapshot(const QString& urlString) { @@ -2770,6 +2751,8 @@ void Application::update(float deltaTime) { updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... updateDialogs(deltaTime); // update various stats dialogs if present + _avatarUpdate->synchronousProcess(); + { PerformanceTimer perfTimer("physics"); myAvatar->relayDriveKeysToCharacterController(); @@ -2831,8 +2814,6 @@ void Application::update(float deltaTime) { _overlays.update(deltaTime); } - _avatarUpdate->synchronousProcess(); - // Update _viewFrustum with latest camera and view frustum data... // NOTE: we get this from the view frustum, to make it simpler, since the // loadViewFrumstum() method will get the correct details from the camera @@ -3973,26 +3954,26 @@ bool Application::canAcceptURL(const QString& urlString) { return false; } -bool Application::acceptURL(const QString& urlString) { +bool Application::acceptURL(const QString& urlString, bool defaultUpload) { if (urlString.startsWith(HIFI_URL_SCHEME)) { // this is a hifi URL - have the AddressManager handle it QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", Qt::AutoConnection, Q_ARG(const QString&, urlString)); return true; - } else { - QUrl url(urlString); - QHashIterator i(_acceptedExtensions); - QString lowerPath = url.path().toLower(); - while (i.hasNext()) { - i.next(); - if (lowerPath.endsWith(i.key())) { - AcceptURLMethod method = i.value(); - (this->*method)(urlString); - return true; - } + } + + QUrl url(urlString); + QHashIterator i(_acceptedExtensions); + QString lowerPath = url.path().toLower(); + while (i.hasNext()) { + i.next(); + if (lowerPath.endsWith(i.key())) { + AcceptURLMethod method = i.value(); + return (this->*method)(urlString); } } - return false; + + return defaultUpload && askToUploadAsset(urlString); } void Application::setSessionUUID(const QUuid& sessionUUID) { @@ -4076,8 +4057,36 @@ bool Application::askToUploadAsset(const QString& filename) { QUrl url { filename }; if (auto upload = DependencyManager::get()->createUpload(url.toLocalFile())) { + + QMessageBox messageBox; + messageBox.setWindowTitle("Asset upload"); + messageBox.setText("You are about to upload the following file to the asset server:\n" + + url.toDisplayString()); + messageBox.setInformativeText("Do you want to continue?"); + messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + messageBox.setDefaultButton(QMessageBox::Ok); + + // Option to drop model in world for models + if (filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION)) { + auto checkBox = new QCheckBox(&messageBox); + checkBox->setText("Add to scene"); + messageBox.setCheckBox(checkBox); + } + + if (messageBox.exec() != QMessageBox::Ok) { + upload->deleteLater(); + return false; + } + // connect to the finished signal so we know when the AssetUpload is done - QObject::connect(upload, &AssetUpload::finished, this, &Application::assetUploadFinished); + if (messageBox.checkBox() && (messageBox.checkBox()->checkState() == Qt::Checked)) { + // Custom behavior for models + QObject::connect(upload, &AssetUpload::finished, this, &Application::modelUploadFinished); + } else { + QObject::connect(upload, &AssetUpload::finished, + &AssetUploadDialogFactory::getInstance(), + &AssetUploadDialogFactory::handleUploadFinished); + } // start the upload now upload->start(); @@ -4089,47 +4098,26 @@ bool Application::askToUploadAsset(const QString& filename) { return false; } -void Application::assetUploadFinished(AssetUpload* upload, const QString& hash) { - if (upload->getError() != AssetUpload::NoError) { - // figure out the right error message for the message box - QString additionalError; +void Application::modelUploadFinished(AssetUpload* upload, const QString& hash) { + auto filename = QFileInfo(upload->getFilename()).fileName(); + + if ((upload->getError() == AssetUpload::NoError) && + (filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION))) { - switch (upload->getError()) { - case AssetUpload::PermissionDenied: - additionalError = "You do not have permission to upload content to this asset-server."; - break; - case AssetUpload::TooLarge: - additionalError = "The uploaded content was too large and could not be stored in the asset-server."; - break; - case AssetUpload::FileOpenError: - additionalError = "The file could not be opened. Please check your permissions and try again."; - break; - case AssetUpload::NetworkError: - additionalError = "The file could not be opened. Please check your network connectivity."; - break; - default: - // not handled, do not show a message box - return; - } + auto entities = DependencyManager::get(); - // display a message box with the error - auto filename = QFileInfo(upload->getFilename()).fileName(); - QString errorMessage = QString("Failed to upload %1.\n\n%2").arg(filename, additionalError); - QMessageBox::warning(_window, "Failed Upload", errorMessage); + EntityItemProperties properties; + properties.setType(EntityTypes::Model); + properties.setModelURL(QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension())); + properties.setPosition(_myCamera.getPosition() + _myCamera.getOrientation() * Vectors::FRONT * 2.0f); + properties.setName(QUrl(upload->getFilename()).fileName()); + + entities->addEntity(properties); + + upload->deleteLater(); + } else { + AssetUploadDialogFactory::getInstance().handleUploadFinished(upload, hash); } - - auto entities = DependencyManager::get(); - auto myAvatar = getMyAvatar(); - - EntityItemProperties properties; - properties.setType(EntityTypes::Model); - properties.setModelURL(QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension())); - properties.setPosition(myAvatar->getPosition() + myAvatar->getOrientation() * Vectors::FRONT * 2.0f); - properties.setName(QUrl(upload->getFilename()).fileName()); - - entities->addEntity(properties); - - upload->deleteLater(); } ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, diff --git a/interface/src/Application.h b/interface/src/Application.h index 8444eab2b6..a2125e7e09 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -211,7 +211,7 @@ public: void setScriptsLocation(const QString& scriptsLocation); bool canAcceptURL(const QString& url); - bool acceptURL(const QString& url); + bool acceptURL(const QString& url, bool defaultUpload = false); void setMaxOctreePacketsPerSecond(int maxOctreePPS); int getMaxOctreePacketsPerSecond(); @@ -329,7 +329,7 @@ private slots: bool askToSetAvatarUrl(const QString& url); bool askToLoadScript(const QString& scriptFilenameOrURL); bool askToUploadAsset(const QString& asset); - void assetUploadFinished(AssetUpload* upload, const QString& hash); + void modelUploadFinished(AssetUpload* upload, const QString& hash); void setSessionUUID(const QUuid& sessionUUID); void domainChanged(const QString& domainHostname); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 22816e9001..605a81e9d9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -152,7 +152,7 @@ void MyAvatar::reset() { setEnableRigAnimations(true); // Reset dynamic state. - _wasPushing = _isPushing = _isBraking = _billboardValid = _goToPending = _straighteningLean = false; + _wasPushing = _isPushing = _isBraking = _billboardValid = _straighteningLean = false; _skeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 9bdf8d1a4a..54aa72da6b 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include "Application.h" #include "devices/MotionTracker.h" #include "ControllerScriptingInterface.h" @@ -31,6 +33,11 @@ ControllerScriptingInterface::ControllerScriptingInterface() : } +ControllerScriptingInterface::~ControllerScriptingInterface() { + delete _newControllerScriptingInterface; +} + + static int actionMetaTypeId = qRegisterMetaType(); static int inputChannelMetaTypeId = qRegisterMetaType(); static int inputMetaTypeId = qRegisterMetaType(); @@ -121,6 +128,11 @@ void ControllerScriptingInterface::registerControllerTypes(ScriptEngine* engine) qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); wireUpControllers(engine); + + // hack in the new controller scripting interface... + _newControllerScriptingInterface = new controller::NewControllerScriptingInterface(); + engine->registerGlobalObject("NewControllers", _newControllerScriptingInterface); + } void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index aa0526accb..dfe87043cd 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -18,6 +18,9 @@ #include class PalmData; +namespace controller { + class NewControllerScriptingInterface; +} class InputController : public AbstractInputController { Q_OBJECT @@ -55,7 +58,8 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface public: ControllerScriptingInterface(); - + ~ControllerScriptingInterface(); + virtual void registerControllerTypes(ScriptEngine* engine); void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } @@ -169,6 +173,7 @@ private: void wireUpControllers(ScriptEngine* engine); + controller::NewControllerScriptingInterface* _newControllerScriptingInterface = nullptr; }; const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip diff --git a/interface/src/ui/AssetUploadDialogFactory.cpp b/interface/src/ui/AssetUploadDialogFactory.cpp index 4910e6d604..e169d88c8c 100644 --- a/interface/src/ui/AssetUploadDialogFactory.cpp +++ b/interface/src/ui/AssetUploadDialogFactory.cpp @@ -11,11 +11,6 @@ #include "AssetUploadDialogFactory.h" -#include -#include -#include -#include - #include #include #include @@ -24,6 +19,12 @@ #include #include +#include +#include +#include +#include +#include + AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() { static AssetUploadDialogFactory staticInstance; return staticInstance; @@ -85,7 +86,7 @@ void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const Q // setup the line edit to hold the copiable text QLineEdit* lineEdit = new QLineEdit; - QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()); + QString atpURL = QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()); // set the ATP URL as the text value so it's copiable lineEdit->insert(atpURL); diff --git a/interface/src/ui/AssetUploadDialogFactory.h b/interface/src/ui/AssetUploadDialogFactory.h index 36c5d10595..a8e8765f20 100644 --- a/interface/src/ui/AssetUploadDialogFactory.h +++ b/interface/src/ui/AssetUploadDialogFactory.h @@ -30,7 +30,6 @@ public: public slots: void showDialog(); -private slots: void handleUploadFinished(AssetUpload* upload, const QString& hash); private: diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 8a9971f362..17a1af1969 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -56,9 +56,9 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con } void AnimInverseKinematics::setTargetVars( - const QString& jointName, - const QString& positionVar, - const QString& rotationVar, + const QString& jointName, + const QString& positionVar, + const QString& rotationVar, const QString& typeVar) { // if there are dups, last one wins. bool found = false; @@ -95,14 +95,20 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } } else { IKTarget target; - AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); - target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); - target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); - target.setType(animVars.lookup(targetVar.typeVar, QString(""))); - target.index = targetVar.jointIndex; - targets.push_back(target); - if (target.index > _maxTargetIndex) { - _maxTargetIndex = target.index; + target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); + if (target.getType() != IKTarget::Type::Unknown) { + AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); + glm::quat rotation = animVars.lookup(targetVar.rotationVar, defaultPose.rot); + glm::vec3 translation = animVars.lookup(targetVar.positionVar, defaultPose.trans); + if (target.getType() == IKTarget::Type::HipsRelativeRotationAndPosition) { + translation += _hipsOffset; + } + target.setPose(rotation, translation); + target.setIndex(targetVar.jointIndex); + targets.push_back(target); + if (targetVar.jointIndex > _maxTargetIndex) { + _maxTargetIndex = targetVar.jointIndex; + } } } } @@ -141,107 +147,116 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); + if (pivotIndex == -1) { + continue; + } + int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); + if (pivotsParentIndex == -1) { + // TODO?: handle case where tip's parent is root? + continue; + } + glm::vec3 tipPosition = absolutePoses[tipIndex].trans; glm::quat tipRotation = absolutePoses[tipIndex].rot; - // cache tip's parent's absolute rotation so we can recompute the tip's parent-relative + // cache tip's parent's absolute rotation so we can recompute the tip's parent-relative // as we proceed walking down the joint chain - int pivotIndex = _skeleton->getParentIndex(tipIndex); - glm::quat tipParentRotation; - if (pivotIndex != -1) { - tipParentRotation = absolutePoses[pivotIndex].rot; - } + glm::quat tipParentRotation = absolutePoses[pivotIndex].rot; // descend toward root, pivoting each joint to get tip closer to target - int ancestorCount = 1; - while (pivotIndex != -1) { + while (pivotsParentIndex != -1) { // compute the two lines that should be aligned glm::vec3 jointPosition = absolutePoses[pivotIndex].trans; glm::vec3 leverArm = tipPosition - jointPosition; - glm::vec3 targetLine = targetPose.trans - jointPosition; - // compute the swing that would get get tip closer - glm::vec3 axis = glm::cross(leverArm, targetLine); - float axisLength = glm::length(axis); glm::quat deltaRotation; - const float MIN_AXIS_LENGTH = 1.0e-4f; - if (axisLength > MIN_AXIS_LENGTH) { - // compute deltaRotation for alignment (swings tip closer to target) - axis /= axisLength; - float angle = acosf(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine))); + if (targetType == IKTarget::Type::RotationAndPosition || + targetType == IKTarget::Type::HipsRelativeRotationAndPosition) { + // compute the swing that would get get tip closer + glm::vec3 targetLine = target.getTranslation() - jointPosition; + glm::vec3 axis = glm::cross(leverArm, targetLine); + float axisLength = glm::length(axis); + const float MIN_AXIS_LENGTH = 1.0e-4f; + if (axisLength > MIN_AXIS_LENGTH) { + // compute deltaRotation for alignment (swings tip closer to target) + axis /= axisLength; + float angle = acosf(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine))); - // NOTE: even when axisLength is not zero (e.g. lever-arm and pivot-arm are not quite aligned) it is - // still possible for the angle to be zero so we also check that to avoid unnecessary calculations. - const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f; - if (angle > MIN_ADJUSTMENT_ANGLE) { - // reduce angle by a fraction (reduces IK swing contribution of this joint) - angle /= (float)ancestorCount; - deltaRotation = glm::angleAxis(angle, axis); - } + // NOTE: even when axisLength is not zero (e.g. lever-arm and pivot-arm are not quite aligned) it is + // still possible for the angle to be zero so we also check that to avoid unnecessary calculations. + const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f; + if (angle > MIN_ADJUSTMENT_ANGLE) { + // reduce angle by a fraction (for stability) + const float fraction = 0.5f; + angle *= fraction; + deltaRotation = glm::angleAxis(angle, axis); - // The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's - // new rotation and its target. We compute that delta here and rotate the tipJoint accordingly. - glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * targetPose.rot; + // The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's + // new rotation and its target. This is the final parent-relative rotation that the tip joint have + // make to achieve its target rotation. + glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * target.getRotation(); - // enforce tip's constraint - RotationConstraint* constraint = getConstraint(tipIndex); - if (constraint) { - bool constrained = constraint->apply(tipRelativeRotation); - if (constrained) { - // The tip's final parent-relative rotation violates its constraint - // so we try to twist this pivot to compensate. - glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation; - glm::quat missingRotation = targetPose.rot * glm::inverse(constrainedTipRotation); - glm::quat swingPart; - glm::quat twistPart; - glm::vec3 axis = glm::normalize(deltaRotation * leverArm); - swingTwistDecomposition(missingRotation, axis, swingPart, twistPart); - deltaRotation = twistPart * deltaRotation; - } - // we update the tip rotation here to rotate it as close to its target orientation as possible - // before moving on to next pivot - tipRotation = tipParentRotation * tipRelativeRotation; - } - } - ++ancestorCount; - - int parentIndex = _skeleton->getParentIndex(pivotIndex); - if (parentIndex == -1) { - // TODO? apply constraints to root? - // TODO? harvest the root's transform as movement of entire skeleton? - } else { - // compute joint's new parent-relative rotation after swing - // Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q - glm::quat newRot = glm::normalize(glm::inverse( - absolutePoses[parentIndex].rot) * - deltaRotation * - absolutePoses[pivotIndex].rot); - - // enforce pivot's constraint - RotationConstraint* constraint = getConstraint(pivotIndex); - if (constraint) { - bool constrained = constraint->apply(newRot); - if (constrained) { - // the constraint will modify the movement of the tip so we have to compute the modified - // model-frame deltaRotation - // Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^ - deltaRotation = absolutePoses[parentIndex].rot * - newRot * - glm::inverse(absolutePoses[pivotIndex].rot); + // enforce tip's constraint + RotationConstraint* constraint = getConstraint(tipIndex); + if (constraint) { + bool constrained = constraint->apply(tipRelativeRotation); + if (constrained) { + // The tip's final parent-relative rotation would violate its constraint + // so we try to pre-twist this pivot to compensate. + glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation; + glm::quat missingRotation = target.getRotation() * glm::inverse(constrainedTipRotation); + glm::quat swingPart; + glm::quat twistPart; + glm::vec3 axis = glm::normalize(deltaRotation * leverArm); + swingTwistDecomposition(missingRotation, axis, swingPart, twistPart); + float dotSign = copysignf(1.0f, twistPart.w); + deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * twistPart, fraction)) * deltaRotation; + } + } } } - - // store the rotation change in the accumulator - _accumulators[pivotIndex].add(newRot); + } else if (targetType == IKTarget::Type::HmdHead) { + // An HmdHead target slaves the orientation of the end-effector by distributing rotation + // deltas up the hierarchy. Its target position is enforced later by shifting the hips. + deltaRotation = target.getRotation() * glm::inverse(tipRotation); + float dotSign = copysignf(1.0f, deltaRotation.w); + const float ANGLE_DISTRIBUTION_FACTOR = 0.15f; + deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * deltaRotation, ANGLE_DISTRIBUTION_FACTOR)); } + + // compute joint's new parent-relative rotation after swing + // Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q + glm::quat newRot = glm::normalize(glm::inverse( + absolutePoses[pivotsParentIndex].rot) * + deltaRotation * + absolutePoses[pivotIndex].rot); + + // enforce pivot's constraint + RotationConstraint* constraint = getConstraint(pivotIndex); + if (constraint) { + bool constrained = constraint->apply(newRot); + if (constrained) { + // the constraint will modify the movement of the tip so we have to compute the modified + // model-frame deltaRotation + // Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^ + deltaRotation = absolutePoses[pivotsParentIndex].rot * + newRot * + glm::inverse(absolutePoses[pivotIndex].rot); + } + } + + // store the rotation change in the accumulator + _accumulators[pivotIndex].add(newRot); + // this joint has been changed so we check to see if it has the lowest index if (pivotIndex < lowestMovedIndex) { lowestMovedIndex = pivotIndex; @@ -252,7 +267,8 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(pivotIndex); + pivotIndex = pivotsParentIndex; + pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); } } ++numLoops; @@ -275,26 +291,15 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector targets; computeTargets(animVars, targets, underPoses); - + if (targets.empty()) { // no IK targets but still need to enforce constraints std::map::iterator constraintItr = _constraints.begin(); @@ -355,7 +360,50 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars ++constraintItr; } } else { + // shift the hips according to the offset from the previous frame + float offsetLength = glm::length(_hipsOffset); + const float MIN_HIPS_OFFSET_LENGTH = 0.03f; + if (offsetLength > MIN_HIPS_OFFSET_LENGTH) { + // but only if offset is long enough + float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength); + _relativePoses[0].trans = underPoses[0].trans + scaleFactor * _hipsOffset; + } + solveWithCyclicCoordinateDescent(targets); + + // compute the new target hips offset (for next frame) + // by looking for discrepancies between where a targeted endEffector is + // and where it wants to be (after IK solutions are done) + glm::vec3 newHipsOffset = Vectors::ZERO; + for (auto& target: targets) { + int targetIndex = target.getIndex(); + if (targetIndex == _headIndex && _headIndex != -1) { + // special handling for headTarget + if (target.getType() == IKTarget::Type::RotationOnly) { + // we want to shift the hips to bring the underpose closer + // to where the head happens to be (overpose) + glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans; + glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; + const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f; + newHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * (actual - under); + } else if (target.getType() == IKTarget::Type::HmdHead) { + // we want to shift the hips to bring the head to its designated position + glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; + _hipsOffset += target.getTranslation() - actual; + // and ignore all other targets + newHipsOffset = _hipsOffset; + break; + } + } else if (target.getType() == IKTarget::Type::RotationAndPosition) { + glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans; + glm::vec3 targetPosition = target.getTranslation(); + newHipsOffset += targetPosition - actualPosition; + } + } + + // smooth transitions by relaxing _hipsOffset toward the new value + const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f; + _hipsOffset += (newHipsOffset - _hipsOffset) * (dt / HIPS_OFFSET_SLAVE_TIMESCALE); } } return _relativePoses; @@ -477,7 +525,7 @@ void AnimInverseKinematics::initConstraints() { stConstraint->setSwingLimits(minDots); constraint = static_cast(stConstraint); - } else if (0 == baseName.compare("UpLegXXX", Qt::CaseInsensitive)) { + } else if (0 == baseName.compare("UpLeg", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f); @@ -581,7 +629,7 @@ void AnimInverseKinematics::initConstraints() { } else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); - const float MAX_NECK_TWIST = PI / 2.0f; + const float MAX_NECK_TWIST = PI / 4.0f; stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST); std::vector minDots; @@ -589,6 +637,18 @@ void AnimInverseKinematics::initConstraints() { minDots.push_back(cosf(MAX_NECK_SWING)); stConstraint->setSwingLimits(minDots); + constraint = static_cast(stConstraint); + } else if (0 == baseName.compare("Head", Qt::CaseInsensitive)) { + SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); + stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); + const float MAX_HEAD_TWIST = PI / 4.0f; + stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST); + + std::vector minDots; + const float MAX_HEAD_SWING = PI / 4.0f; + minDots.push_back(cosf(MAX_HEAD_SWING)); + stConstraint->setSwingLimits(minDots); + constraint = static_cast(stConstraint); } else if (0 == baseName.compare("ForeArm", Qt::CaseInsensitive)) { // The elbow joint rotates about the parent-frame's zAxis (-zAxis) for the Right (Left) arm. @@ -621,7 +681,7 @@ void AnimInverseKinematics::initConstraints() { eConstraint->setAngleLimits(minAngle, maxAngle); constraint = static_cast(eConstraint); - } else if (0 == baseName.compare("LegXXX", Qt::CaseInsensitive)) { + } else if (0 == baseName.compare("Leg", Qt::CaseInsensitive)) { // The knee joint rotates about the parent-frame's -xAxis. ElbowConstraint* eConstraint = new ElbowConstraint(); glm::quat referenceRotation = _defaultRelativePoses[i].rot; @@ -652,7 +712,7 @@ void AnimInverseKinematics::initConstraints() { eConstraint->setAngleLimits(minAngle, maxAngle); constraint = static_cast(eConstraint); - } else if (0 == baseName.compare("FootXXX", Qt::CaseInsensitive)) { + } else if (0 == baseName.compare("Foot", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f); @@ -697,7 +757,9 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele if (skeleton) { initConstraints(); + _headIndex = _skeleton->nameToJointIndex("Head"); } else { clearConstraints(); + _headIndex = -1; } } diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 0ef81f8b3c..b96dd3711e 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -16,6 +16,7 @@ #include #include "AnimNode.h" +#include "IKTarget.h" #include "RotationAccumulator.h" @@ -37,18 +38,6 @@ public: virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; protected: - struct IKTarget { - enum class Type { - RotationAndPosition, - RotationOnly - }; - AnimPose pose; - int index; - Type type = Type::RotationAndPosition; - - void setType(const QString& typeVar) { type = ((typeVar == "RotationOnly") ? Type::RotationOnly : Type::RotationAndPosition); } - }; - void computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses); void solveWithCyclicCoordinateDescent(const std::vector& targets); virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; @@ -60,6 +49,10 @@ protected: void clearConstraints(); void initConstraints(); + // no copies + AnimInverseKinematics(const AnimInverseKinematics&) = delete; + AnimInverseKinematics& operator=(const AnimInverseKinematics&) = delete; + struct IKTargetVar { IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, @@ -85,9 +78,9 @@ protected: AnimPoseVec _defaultRelativePoses; // poses of the relaxed state AnimPoseVec _relativePoses; // current relative poses - // no copies - AnimInverseKinematics(const AnimInverseKinematics&) = delete; - AnimInverseKinematics& operator=(const AnimInverseKinematics&) = delete; + // experimental data for moving hips during IK + int _headIndex = -1; + glm::vec3 _hipsOffset = Vectors::ZERO; // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses // during the the cyclic coordinate descent algorithm diff --git a/libraries/animation/src/IKTarget.cpp b/libraries/animation/src/IKTarget.cpp new file mode 100644 index 0000000000..b17f186b47 --- /dev/null +++ b/libraries/animation/src/IKTarget.cpp @@ -0,0 +1,34 @@ +// +// IKTarget.cpp +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "IKTarget.h" + +void IKTarget::setPose(const glm::quat& rotation, const glm::vec3& translation) { + _pose.rot = rotation; + _pose.trans = translation; +} + +void IKTarget::setType(int type) { + switch (type) { + case (int)Type::RotationAndPosition: + _type = Type::RotationAndPosition; + break; + case (int)Type::RotationOnly: + _type = Type::RotationOnly; + break; + case (int)Type::HmdHead: + _type = Type::HmdHead; + break; + case (int)Type::HipsRelativeRotationAndPosition: + _type = Type::HipsRelativeRotationAndPosition; + break; + default: + _type = Type::Unknown; + } +} diff --git a/libraries/animation/src/IKTarget.h b/libraries/animation/src/IKTarget.h new file mode 100644 index 0000000000..983eafd9cb --- /dev/null +++ b/libraries/animation/src/IKTarget.h @@ -0,0 +1,43 @@ +// +// IKTarget.h +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_IKTarget_h +#define hifi_IKTarget_h + +#include "AnimSkeleton.h" + +class IKTarget { +public: + enum class Type { + RotationAndPosition, + RotationOnly, + HmdHead, + HipsRelativeRotationAndPosition, + Unknown, + }; + + IKTarget() {} + + const glm::vec3& getTranslation() const { return _pose.trans; } + const glm::quat& getRotation() const { return _pose.rot; } + int getIndex() const { return _index; } + Type getType() const { return _type; } + + void setPose(const glm::quat& rotation, const glm::vec3& translation); + void setIndex(int index) { _index = index; } + void setType(int); + +private: + AnimPose _pose; + int _index = -1; + Type _type = Type::RotationAndPosition; + +}; + +#endif // hifi_IKTarget_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7f300fc84a..340c09060a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -14,13 +14,14 @@ #include #include -#include "NumericalConstants.h" +#include +#include + #include "AnimationHandle.h" #include "AnimationLogging.h" #include "AnimSkeleton.h" -#include "DebugDraw.h" +#include "IKTarget.h" -#include "Rig.h" void Rig::HeadParameters::dump() const { qCDebug(animation, "HeadParameters ="); @@ -1057,9 +1058,11 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.set("headPosition", headPos); _animVars.set("headRotation", headRot); - _animVars.set("headAndNeckType", QString("RotationAndPosition")); + _animVars.set("headType", (int)IKTarget::Type::HmdHead); _animVars.set("neckPosition", neckPos); _animVars.set("neckRotation", neckRot); + //_animVars.set("neckType", (int)IKTarget::Type::RotationOnly); + _animVars.set("neckType", (int)IKTarget::Type::Unknown); // 'Unknown' disables the target } else { @@ -1070,9 +1073,11 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.unset("headPosition"); _animVars.set("headRotation", realLocalHeadOrientation); - _animVars.set("headAndNeckType", QString("RotationOnly")); + _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); + _animVars.set("headType", (int)IKTarget::Type::RotationOnly); _animVars.unset("neckPosition"); _animVars.unset("neckRotation"); + _animVars.set("neckType", (int)IKTarget::Type::RotationOnly); } } else if (!_enableAnimGraph) { @@ -1130,16 +1135,20 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (params.isLeftEnabled) { _animVars.set("leftHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.leftPosition); _animVars.set("leftHandRotation", rootBindPose.rot * yFlipHACK * params.leftOrientation); + _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("leftHandPosition"); _animVars.unset("leftHandRotation"); + _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); } if (params.isRightEnabled) { _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); _animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation); + _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("rightHandPosition"); _animVars.unset("rightHandRotation"); + _animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); } // set leftHand grab vars diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/Mapping.cpp index b6214a43fc..08e8aef9e8 100644 --- a/libraries/controllers/src/controllers/Mapping.cpp +++ b/libraries/controllers/src/controllers/Mapping.cpp @@ -7,5 +7,10 @@ // #include "Mapping.h" +namespace controller { + Mapping::Mapping(const QString& name ) : _name(name) { + } + +} diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index 39fe6ba788..e9c8e7a323 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -27,7 +27,10 @@ namespace controller { using Map = std::map; using Pointer = std::shared_ptr; + Mapping(const QString& name); + Map _channelMappings; + QString _name; protected: diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp index c4d1bb65c4..c80e5fc721 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp @@ -59,6 +59,25 @@ namespace controller { QJSValue _callable; }; + class ScriptEndpoint : public Endpoint { + public: + ScriptEndpoint(const QScriptValue& callable) + : Endpoint(UserInputMapper::Input(-1)), _callable(callable) { + } + + virtual float value() { + float result = (float)_callable.call().toNumber(); + return result; + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) { + _callable.call(QScriptValue(), QScriptValueList({ QScriptValue(newValue) })); + } + + private: + QScriptValue _callable; + }; + class CompositeEndpoint : public Endpoint, Endpoint::Pair { public: CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) @@ -158,7 +177,7 @@ namespace controller { qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; } qDebug() << "Creating new Mapping " << mappingName; - Mapping::Pointer mapping = std::make_shared(); + auto mapping = std::make_shared(mappingName); _mappingsByName[mappingName] = mapping; return new MappingBuilderProxy(*this, mapping); } @@ -297,6 +316,11 @@ namespace controller { return endpointFor(UserInputMapper::Input(endpoint.toInt32())); } + if (endpoint.isFunction()) { + auto result = std::make_shared(endpoint); + return result; + } + qWarning() << "Unsupported input type " << endpoint.toString(); return Endpoint::Pointer(); } diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h index 39791eacb2..bdc291bc03 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h @@ -39,7 +39,7 @@ namespace controller { Q_INVOKABLE float getValue(const int& source); Q_INVOKABLE void update(); - Q_INVOKABLE QObject* newMapping(const QString& mappingName); + Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString()); Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index 9080555cac..a191e19be2 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -44,7 +44,6 @@ QObject* MappingBuilderProxy::join(const QJSValue& source1, const QJSValue& sour return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); } - const QString JSON_NAME = QStringLiteral("name"); const QString JSON_CHANNELS = QStringLiteral("channels"); const QString JSON_CHANNEL_FROM = QStringLiteral("from"); @@ -84,4 +83,9 @@ QObject* MappingBuilderProxy::from(const QJsonValue& json) { } } +QObject* MappingBuilderProxy::enable(bool enable) { + _parent.enableMapping(_mapping->_name, enable); + return this; +} + diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 799fc99399..c71cc83569 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -31,15 +31,18 @@ public: Q_INVOKABLE QObject* from(const QJSValue& source); Q_INVOKABLE QObject* from(const QScriptValue& source); - Q_INVOKABLE QObject* join(const QJSValue& source1, const QJSValue& source2); + Q_INVOKABLE QObject* enable(bool enable = true); + Q_INVOKABLE QObject* disable() { return enable(false); } + + // JSON route creation point Q_INVOKABLE QObject* from(const QJsonValue& json); void parse(const QJsonObject& json); - // void serialize(QJsonObject& json); +// void serialize(QJsonObject& json); protected: QObject* from(const Endpoint::Pointer& source); diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 1f8242f081..914f80d983 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -27,7 +27,4 @@ void NullDisplayPlugin::preRender() {} void NullDisplayPlugin::preDisplay() {} void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {} void NullDisplayPlugin::finishFrame() {} - -void NullDisplayPlugin::activate() {} -void NullDisplayPlugin::deactivate() {} void NullDisplayPlugin::stop() {} diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index bb1ab2d97f..4f2cc77b8f 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -15,8 +15,6 @@ public: virtual ~NullDisplayPlugin() final {} virtual const QString & getName() const override; - void activate() override; - void deactivate() override; void stop() override; virtual glm::uvec2 getRecommendedRenderSize() const override; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 505dae824a..d93040690f 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -57,12 +57,12 @@ void OpenGLDisplayPlugin::customizeContext() { } void OpenGLDisplayPlugin::activate() { - _active = true; + DisplayPlugin::activate(); _timer.start(1); } void OpenGLDisplayPlugin::stop() { - _active = false; + DisplayPlugin::activate(); _timer.stop(); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 52715ebde7..4b0e03788a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -44,7 +44,6 @@ protected: mutable QTimer _timer; ProgramPtr _program; ShapeWrapperPtr _plane; - bool _active{ false }; bool _vsyncSupported{ false }; }; diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index b52dd3f658..2b16d905f5 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -22,7 +22,7 @@ InputPluginList getInputPlugins() { InputPlugin* PLUGIN_POOL[] = { new KeyboardMouseDevice(), new SDL2Manager(), - //new SixenseManager(), + new SixenseManager(), //new ViveControllerManager(), nullptr }; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 202a767244..4703d3ae6a 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -10,9 +10,9 @@ // #include "KeyboardMouseDevice.h" -#include -#include -#include +#include +#include +#include const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index d96566e9d1..e8a6131387 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -13,6 +13,9 @@ #define hifi_KeyboardMouseDevice_h #include + +#include + #include "InputDevice.h" #include "InputPlugin.h" @@ -65,9 +68,6 @@ public: virtual bool isJointController() const override { return false; } const QString& getName() const override { return NAME; } - virtual void activate() override {}; - virtual void deactivate() override {}; - virtual void pluginFocusOutEvent() override { focusOutEvent(); } virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.h b/libraries/input-plugins/src/input-plugins/SDL2Manager.h index 52d39597ef..23e3ee059f 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.h +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.h @@ -34,8 +34,6 @@ public: virtual void init() override; virtual void deinit() override; - virtual void activate() override {}; - virtual void deactivate() override {}; virtual void pluginFocusOutEvent() override; virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 3950fdea43..e7a4feedd8 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -85,6 +85,7 @@ bool SixenseManager::isSupported() const { } void SixenseManager::activate() { + InputPlugin::activate(); #ifdef HAVE_SIXENSE _calibrationState = CALIBRATION_STATE_IDLE; _avatarPosition = DEFAULT_AVATAR_POSITION; @@ -125,6 +126,7 @@ void SixenseManager::activate() { } void SixenseManager::deactivate() { + InputPlugin::deactivate(); #ifdef HAVE_SIXENSE CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); CONTAINER->removeMenu(MENU_PATH); diff --git a/libraries/input-plugins/src/input-plugins/StandardController.cpp b/libraries/input-plugins/src/input-plugins/StandardController.cpp index 040efca794..988714a962 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.cpp +++ b/libraries/input-plugins/src/input-plugins/StandardController.cpp @@ -17,8 +17,6 @@ const float CONTROLLER_THRESHOLD = 0.3f; -const float MAX_AXIS = 32768.0f; - StandardController::~StandardController() { } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index bb8267b616..a3374cc1c0 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -79,6 +79,7 @@ bool ViveControllerManager::isSupported() const { } void ViveControllerManager::activate() { + InputPlugin::activate(); #ifdef Q_OS_WIN CONTAINER->addMenu(MENU_PATH); CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS, @@ -143,6 +144,8 @@ void ViveControllerManager::activate() { } void ViveControllerManager::deactivate() { + InputPlugin::deactivate(); + #ifdef Q_OS_WIN CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); CONTAINER->removeMenu(MENU_PATH); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index b4e3882a5e..121b4cd4fd 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -14,10 +14,8 @@ #include #include -#include #include "AssetClient.h" -#include "NetworkAccessManager.h" #include "NetworkLogging.h" #include "NodeList.h" #include "ResourceCache.h" @@ -41,14 +39,14 @@ void AssetRequest::start() { } // Try to load from cache - if (loadFromCache()) { + _data = loadFromCache(getUrl()); + if (!_data.isNull()) { _info.hash = _hash; _info.size = _data.size(); _error = NoError; _state = Finished; emit finished(this); - qCDebug(asset_client) << getUrl().toDisplayString() << "loaded from disk cache."; return; } @@ -112,9 +110,7 @@ void AssetRequest::start() { _totalReceived += data.size(); emit progress(_totalReceived, _info.size); - if (saveToCache(data)) { - qCDebug(asset_client) << getUrl().toDisplayString() << "saved to disk cache"; - } + saveToCache(getUrl(), data); } else { // hash doesn't match - we have an error _error = HashVerificationFailed; @@ -133,49 +129,6 @@ void AssetRequest::start() { } QUrl AssetRequest::getUrl() const { - if (!_extension.isEmpty()) { - return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, _hash, _extension)); - } else { - return QUrl(QString("%1:%2").arg(URL_SCHEME_ATP, _hash)); - } -} - -bool AssetRequest::loadFromCache() { - if (auto cache = NetworkAccessManager::getInstance().cache()) { - auto url = getUrl(); - if (auto ioDevice = cache->data(url)) { - _data = ioDevice->readAll(); - return true; - } else { - qCDebug(asset_client) << url.toDisplayString() << "not in disk cache"; - } - } else { - qCWarning(asset_client) << "No disk cache to load assets from."; - } - return false; -} - -bool AssetRequest::saveToCache(const QByteArray& file) const { - if (auto cache = NetworkAccessManager::getInstance().cache()) { - auto url = getUrl(); - - if (!cache->metaData(url).isValid()) { - QNetworkCacheMetaData metaData; - metaData.setUrl(url); - metaData.setSaveToDisk(true); - metaData.setLastModified(QDateTime::currentDateTime()); - metaData.setExpirationDate(QDateTime()); // Never expires - - if (auto ioDevice = cache->prepare(metaData)) { - ioDevice->write(file); - cache->insert(ioDevice); - return true; - } - qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache."; - } - } else { - qCWarning(asset_client) << "No disk cache to save assets to."; - } - return false; + return ::getUrl(_hash, _extension); } diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index 75e2353425..a5275e718a 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -53,9 +53,6 @@ signals: void progress(qint64 totalReceived, qint64 total); private: - bool loadFromCache(); - bool saveToCache(const QByteArray& file) const; - State _state = NotStarted; Error _error = NoError; AssetInfo _info; diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 92632a43e5..6f4fcbf1f2 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -25,7 +25,7 @@ AssetUpload::AssetUpload(QObject* object, const QString& filename) : void AssetUpload::start() { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "start", Qt::AutoConnection); + QMetaObject::invokeMethod(this, "start"); return; } @@ -37,14 +37,15 @@ void AssetUpload::start() { // file opened, read the data and grab the extension _extension = QFileInfo(_filename).suffix(); - auto data = file.readAll(); + _data = file.readAll(); // ask the AssetClient to upload the asset and emit the proper signals from the passed callback auto assetClient = DependencyManager::get(); qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; - assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ + assetClient->uploadAsset(_data, _extension, [this](bool responseReceived, AssetServerError error, + const QString& hash){ if (!responseReceived) { _error = NetworkError; } else { @@ -63,6 +64,11 @@ void AssetUpload::start() { break; } } + + if (_error == NoError && hash == hashData(_data).toHex()) { + saveToCache(getUrl(hash, _extension), _data); + } + emit finished(this, hash); }); } else { diff --git a/libraries/networking/src/AssetUpload.h b/libraries/networking/src/AssetUpload.h index 80faa8a9c1..4047a44923 100644 --- a/libraries/networking/src/AssetUpload.h +++ b/libraries/networking/src/AssetUpload.h @@ -50,6 +50,7 @@ signals: private: QString _filename; QString _extension; + QByteArray _data; Error _error; }; diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp new file mode 100644 index 0000000000..7311d73525 --- /dev/null +++ b/libraries/networking/src/AssetUtils.cpp @@ -0,0 +1,69 @@ +// +// AssetUtils.h +// libraries/networking/src +// +// Created by Clément Brisset on 10/12/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AssetUtils.h" + +#include +#include + +#include "NetworkAccessManager.h" +#include "NetworkLogging.h" + +#include "ResourceManager.h" + +QUrl getUrl(const QString& hash, const QString& extension) { + if (!extension.isEmpty()) { + return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, hash, extension)); + } else { + return QUrl(QString("%1:%2").arg(URL_SCHEME_ATP, hash)); + } +} + +QByteArray hashData(const QByteArray& data) { + return QCryptographicHash::hash(data, QCryptographicHash::Sha256); +} + +QByteArray loadFromCache(const QUrl& url) { + if (auto cache = NetworkAccessManager::getInstance().cache()) { + if (auto ioDevice = cache->data(url)) { + qCDebug(asset_client) << url.toDisplayString() << "loaded from disk cache."; + return ioDevice->readAll(); + } else { + qCDebug(asset_client) << url.toDisplayString() << "not in disk cache"; + } + } else { + qCWarning(asset_client) << "No disk cache to load assets from."; + } + return QByteArray(); +} + +bool saveToCache(const QUrl& url, const QByteArray& file) { + if (auto cache = NetworkAccessManager::getInstance().cache()) { + if (!cache->metaData(url).isValid()) { + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + metaData.setSaveToDisk(true); + metaData.setLastModified(QDateTime::currentDateTime()); + metaData.setExpirationDate(QDateTime()); // Never expires + + if (auto ioDevice = cache->prepare(metaData)) { + ioDevice->write(file); + cache->insert(ioDevice); + qCDebug(asset_client) << url.toDisplayString() << "saved to disk cache"; + return true; + } + qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache."; + } + } else { + qCWarning(asset_client) << "No disk cache to save assets to."; + } + return false; +} \ No newline at end of file diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index b18ff329fc..5fd5c9144d 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -12,10 +12,11 @@ #ifndef hifi_AssetUtils_h #define hifi_AssetUtils_h -#include - #include +#include +#include + using MessageID = uint32_t; using DataOffset = int64_t; @@ -31,8 +32,11 @@ enum AssetServerError : uint8_t { PermissionDenied }; -const QString ATP_SCHEME = "atp"; +QUrl getUrl(const QString& hash, const QString& extension = QString()); -inline QByteArray hashData(const QByteArray& data) { return QCryptographicHash::hash(data, QCryptographicHash::Sha256); } +QByteArray hashData(const QByteArray& data); + +QByteArray loadFromCache(const QUrl& url); +bool saveToCache(const QUrl& url, const QByteArray& file); #endif diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index e2f57f97a3..84af87f217 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -319,10 +319,10 @@ void Resource::attemptRequest() { void Resource::finishedLoading(bool success) { if (success) { - qDebug() << "Finished loading:" << _url; + qDebug().noquote() << "Finished loading:" << _url.toDisplayString(); _loaded = true; } else { - qDebug() << "Failed to load:" << _url; + qDebug().noquote() << "Failed to load:" << _url.toDisplayString(); _failedToLoad = true; } _loadPriorities.clear(); @@ -339,13 +339,13 @@ void Resource::makeRequest() { _request = ResourceManager::createResourceRequest(this, _activeUrl); if (!_request) { - qDebug() << "Failed to get request for " << _url; + qDebug().noquote() << "Failed to get request for" << _url.toDisplayString(); ResourceCache::requestCompleted(this); finishedLoading(false); return; } - qDebug() << "Starting request for: " << _url; + qDebug().noquote() << "Starting request for:" << _url.toDisplayString(); connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress); connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished); @@ -368,7 +368,8 @@ void Resource::handleReplyFinished() { auto result = _request->getResult(); if (result == ResourceRequest::Success) { _data = _request->getData(); - qDebug() << "Request finished for " << _url << ", " << _activeUrl; + auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); + qDebug().noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); finishedLoading(true); emit loaded(_data); diff --git a/libraries/plugins/src/plugins/Plugin.h b/libraries/plugins/src/plugins/Plugin.h index 68e012b8e1..f53d309e97 100644 --- a/libraries/plugins/src/plugins/Plugin.h +++ b/libraries/plugins/src/plugins/Plugin.h @@ -33,9 +33,18 @@ public: virtual void deinit(); /// Called when a plugin is being activated for use. May be called multiple times. - virtual void activate() = 0; + virtual void activate() { + _active = true; + } + /// Called when a plugin is no longer being used. May be called multiple times. - virtual void deactivate() = 0; + virtual void deactivate() { + _active = false; + } + + virtual bool isActive() { + return _active; + } /** * Called by the application during it's idle phase. If the plugin needs to do @@ -48,6 +57,7 @@ public: virtual void loadSettings() {} protected: + bool _active{ false }; static PluginContainer* CONTAINER; static QString UNKNOWN_PLUGIN_ID; diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt index 34ab4c2eba..d9bef079ff 100644 --- a/tests/controllers/CMakeLists.txt +++ b/tests/controllers/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME controllers-test) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Script) +setup_hifi_project(Script Qml) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml index ce8a491419..41d623a389 100644 --- a/tests/controllers/qml/content.qml +++ b/tests/controllers/qml/content.qml @@ -8,10 +8,21 @@ import "./controls" Column { id: root - property var xbox: NewControllers.Hardware.X360Controller1 property var actions: NewControllers.Actions property var standard: NewControllers.Standard - property string mappingName: "TestMapping" + property var testMapping: null + property var xbox: null + + + Component.onCompleted: { + var patt = /^X360Controller/; + for (var prop in NewControllers.Hardware) { + if(patt.test(prop)) { + root.xbox = NewControllers.Hardware[prop] + break + } + } + } spacing: 12 @@ -49,13 +60,15 @@ Column { mapping.from(xbox.LT).to(standard.LT); mapping.from(xbox.RT).to(standard.RT); NewControllers.enableMapping("Default"); + enabled = false; + text = "Built" } } Button { text: "Build Mapping" onClicked: { - var mapping = NewControllers.newMapping(root.mappingName); + var mapping = NewControllers.newMapping(); // Inverting a value mapping.from(xbox.RY).invert().to(standard.RY); // Assigning a value from a function @@ -63,17 +76,22 @@ Column { // Constrainting a value to -1, 0, or 1, with a deadzone mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); mapping.join(standard.LB, standard.RB).pulse(0.5).to(actions.Yaw); + mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); + mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); + testMapping = mapping; + enabled = false + text = "Built" } } Button { text: "Enable Mapping" - onClicked: NewControllers.enableMapping(root.mappingName) + onClicked: root.testMapping.enable() } Button { text: "Disable Mapping" - onClicked: NewControllers.disableMapping(root.mappingName) + onClicked: root.testMapping.disable() } } @@ -85,6 +103,7 @@ Column { Row { + spacing: 8 ScrollingGraph { controlId: NewControllers.Actions.Yaw label: "Yaw" @@ -92,6 +111,22 @@ Column { max: 3.0 size: 128 } + + ScrollingGraph { + controlId: NewControllers.Actions.YAW_LEFT + label: "Yaw Left" + min: -3.0 + max: 3.0 + size: 128 + } + + ScrollingGraph { + controlId: NewControllers.Actions.YAW_RIGHT + label: "Yaw Right" + min: -3.0 + max: 3.0 + size: 128 + } } } diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index b42359e48a..9212cbb3e8 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -82,6 +82,9 @@ public: int main(int argc, char** argv) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; + for (auto path : qApp->libraryPaths()) { + qDebug() << path; + } for (auto path : qApp->libraryPaths()) { qDebug() << path; diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index d480c0ac74..0a678d668e 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -52,7 +52,6 @@ }; - MasterReset = function() { var resetKey = "resetMe"; var GRABBABLE_DATA_KEY = "grabbableKey"; @@ -407,11 +406,6 @@ type: 'Box', position: startPosition, dimensions: TARGET_DIMENSIONS, - color: { - red: 0, - green: 255, - blue: 0 - }, rotation: rotation, visible: false, collisionsWillMove: false, @@ -658,6 +652,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -685,6 +680,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -734,7 +730,6 @@ - var sconceLight3 = Entities.addEntity({ type: "Light", position: { @@ -755,6 +750,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -783,6 +779,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -810,6 +807,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -888,7 +886,7 @@ y: 1.13, z: 0.2 }, - rotation: rotation2, + rotation: rotation, collisionsWillMove: true, gravity: { x: 0, @@ -1256,7 +1254,7 @@ y: 0.05, z: 0.25 } - },]; + }, ]; var modelURL, entity; for (i = 0; i < blockTypes.length; i++) { @@ -1305,7 +1303,6 @@ Script.scriptEnding.connect(cleanup); } }; - // entity scripts always need to return a newly constructed object of our type return new ResetSwitch(); -}); +}); \ No newline at end of file diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 693e2eb28b..d9199a124d 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -625,6 +625,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -652,6 +653,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -701,7 +703,6 @@ MasterReset = function() { - var sconceLight3 = Entities.addEntity({ type: "Light", position: { @@ -722,6 +723,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -750,6 +752,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -777,6 +780,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -1223,7 +1227,7 @@ MasterReset = function() { y: 0.05, z: 0.25 } - },]; + }, ]; var modelURL, entity; for (i = 0; i < blockTypes.length; i++) { @@ -1271,4 +1275,4 @@ MasterReset = function() { Script.scriptEnding.connect(cleanup); } -}; +}; \ No newline at end of file