diff --git a/scripts/system/assets/data/createAppTooltips.json b/scripts/system/assets/data/createAppTooltips.json index 5572779d46..720d4537ee 100644 --- a/scripts/system/assets/data/createAppTooltips.json +++ b/scripts/system/assets/data/createAppTooltips.json @@ -294,12 +294,21 @@ "position": { "tooltip": "The global position of this entity." }, + "localPosition": { + "tooltip": "The local position of this entity." + }, "rotation": { - "tooltip": "The rotation of the entity with respect to world coordinates." + "tooltip": "The global rotation of this entity." + }, + "localRotation": { + "tooltip": "The local rotation of this entity." }, "dimensions": { "tooltip": "The global dimensions of this entity." }, + "localDimensions": { + "tooltip": "The local dimensions of this entity." + }, "scale": { "tooltip": "The global scaling of this entity.", "skipJSProperty": true @@ -398,13 +407,13 @@ "userData": { "tooltip": "Used to store extra data about the entity in JSON format." }, - "velocity": { + "localVelocity": { "tooltip": "The linear velocity vector of the entity. The velocity at which this entity moves forward in space." }, "damping": { "tooltip": "The linear damping to slow down the linear velocity of an entity over time." }, - "angularVelocity": { + "localAngularVelocity": { "tooltip": "The angular velocity of the entity in rad/s with respect to its axes, about its pivot point." }, "angularDamping": { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index c8811bd603..38f9c5ea34 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2261,9 +2261,17 @@ var PropertiesTool = function (opts) { }); } + that.setSpaceMode = function(spaceMode) { + emitScriptEvent({ + type: 'setSpaceMode', + spaceMode: spaceMode + }) + }; + function updateSelections(selectionUpdated) { var data = { - type: 'update' + type: 'update', + spaceMode: selectionDisplay.getSpaceMode() }; if (selectionUpdated) { @@ -2293,6 +2301,9 @@ var PropertiesTool = function (opts) { if (entity.properties.rotation !== undefined) { entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); } + if (entity.properties.localRotation !== undefined) { + entity.properties.localRotation = Quat.safeEulerAngles(entity.properties.localRotation); + } if (entity.properties.emitOrientation !== undefined) { entity.properties.emitOrientation = Quat.safeEulerAngles(entity.properties.emitOrientation); } @@ -2329,12 +2340,15 @@ var PropertiesTool = function (opts) { } else if (data.properties) { if (data.properties.dynamic === false) { // this object is leaving dynamic, so we zero its velocities - data.properties.velocity = Vec3.ZERO; - data.properties.angularVelocity = Vec3.ZERO; + data.properties.localVelocity = Vec3.ZERO; + data.properties.localAngularVelocity = Vec3.ZERO; } if (data.properties.rotation !== undefined) { data.properties.rotation = Quat.fromVec3Degrees(data.properties.rotation); } + if (data.properties.localRotation !== undefined) { + data.properties.localRotation = Quat.fromVec3Degrees(data.properties.localRotation); + } if (data.properties.emitOrientation !== undefined) { data.properties.emitOrientation = Quat.fromVec3Degrees(data.properties.emitOrientation); } @@ -2725,4 +2739,10 @@ entityListTool.webView.webEventReceived.connect(function(data) { } }); + +selectionDisplay.onSpaceModeChange = function(spaceMode) { + entityListTool.setSpaceMode(spaceMode); + propertiesTool.setSpaceMode(spaceMode); +}; + }()); // END LOCAL_SCOPE diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 7d2350e1c8..941a9ec171 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -222,7 +222,7 @@ input::-webkit-input-placeholder { font-style: italic; } -input:focus, textarea:focus { +input:focus, textarea:focus, button:focus { color: #fff; background-color: #000; outline: 1px solid #00b4ef; @@ -340,7 +340,7 @@ input.no-spin::-webkit-inner-spin-button { padding-right: 12px; } -input[type=button] { +input[type=button], button.hifi-edit-button { font-family: Raleway-Bold; font-size: 13px; text-transform: uppercase; @@ -357,7 +357,7 @@ input[type=button] { cursor: pointer; } -input[type=button].glyph { +input[type=button].glyph, button.hifi-edit-button.glyph { font-family: HiFi-Glyphs; font-size: 20px; text-transform: none; @@ -365,58 +365,58 @@ input[type=button].glyph { padding: 0; } -input[type=button].red { +input[type=button].red, button.hifi-edit-button.red { color: #fff; background-color: #94132e; background: linear-gradient(#d42043 20%, #94132e 100%); } -input[type=button].blue { +input[type=button].blue, button.hifi-edit-button.blue { color: #fff; background-color: #1080b8; background: linear-gradient(#00b4ef 20%, #1080b8 100%); } -input[type=button].white { +input[type=button].white, button.hifi-edit-button.white { color: #121212; background-color: #afafaf; background: linear-gradient(#fff 20%, #afafaf 100%); } -input[type=button]:enabled:hover { +input[type=button]:enabled:hover, button.hifi-edit-button:enabled:hover { background: linear-gradient(#000, #000); border: none; } -input[type=button].red:enabled:hover { +input[type=button].red:enabled:hover, button.hifi-edit-button.red:enabled:hover { background: linear-gradient(#d42043, #d42043); border: none; } -input[type=button].blue:enabled:hover { +input[type=button].blue:enabled:hover, button.hifi-edit-button.blue:enabled:hover { background: linear-gradient(#00b4ef, #00b4ef); border: none; } -input[type=button].white:enabled:hover { +input[type=button].white:enabled:hover, button.hifi-edit-button.white:enabled:hover { background: linear-gradient(#fff, #fff); border: none; } -input[type=button]:active { +input[type=button]:active, button.hifi-edit-button:active { background: linear-gradient(#343434, #343434); } -input[type=button].red:active { +input[type=button].red:active, button.hifi-edit-button.red:active { background: linear-gradient(#94132e, #94132e); } -input[type=button].blue:active { +input[type=button].blue:active, button.hifi-edit-button.blue:active { background: linear-gradient(#1080b8, #1080b8); } -input[type=button].white:active { +input[type=button].white:active, button.hifi-edit-button.white:active { background: linear-gradient(#afafaf, #afafaf); } -input[type=button]:disabled { +input[type=button]:disabled, button.hifi-edit-button:disabled { color: #252525; background: linear-gradient(#575757 20%, #252525 100%); } -input[type=button][pressed=pressed] { +input[type=button][pressed=pressed], button.hifi-edit-button[pressed=pressed] { color: #00b4ef; } @@ -1620,3 +1620,21 @@ input.rename-entity { bottom: 0; margin-top: 5px; } + +#toggle-space-mode::before { + font-family: HiFi-Glyphs; + font-size: 20px; + text-transform: none; + min-width: 32px; + padding-right: 4px; + vertical-align: middle; +} + +#toggle-space-mode.space-mode-local::before { + content: "m"; +} + +#toggle-space-mode.space-mode-world::before { + content: "\e02c"; +} + diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 2e7ac58ac1..062847bcb6 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -9,6 +9,7 @@ --> <html> <head> + <title>Entity List</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <link rel="stylesheet" type="text/css" href="css/edit-style.css"> <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script> @@ -27,7 +28,8 @@ <input type="button" id="visible" class="glyph" value="" /> </div> <input type="button" id="pal" class="glyph" value="" /> - <input type="button" class="red" id="delete" value="Delete" /> + <button id="toggle-space-mode" class="hifi-edit-button space-mode-local">Local</button> + <input type="button" class="red glyph" id="delete" value="{" /> </div> <div id="entity-list"> <div id="filter-area"> diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 41c957c4fa..315e313ae6 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -149,6 +149,7 @@ function loaded() { elSelectedEntitiesCount = document.getElementById("selected-entities-count"); elVisibleEntitiesCount = document.getElementById("visible-entities-count"); elNoEntitiesMessage = document.getElementById("no-entities"); + elToggleSpaceMode = document.getElementById('toggle-space-mode'); document.body.onclick = onBodyClick; document.getElementById("entity-name").onclick = function() { @@ -205,6 +206,10 @@ function loaded() { elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); }; + elToggleSpaceMode.onclick = function() { + EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleSpaceMode' })); + }; + elFilterTypeSelectBox.onclick = onToggleTypeDropdown; elFilterSearch.onkeyup = refreshEntityList; elFilterSearch.onsearch = refreshEntityList; @@ -646,6 +651,8 @@ function loaded() { } } + elToggleSpaceMode.disabled = selectedIDs.length > 1; + refreshFooter(); return notFound; @@ -827,6 +834,16 @@ function loaded() { entityList.resize(); event.stopPropagation(); } + + function setSpaceMode(spaceMode) { + if (spaceMode === "local") { + elToggleSpaceMode.className = "space-mode-local hifi-edit-button"; + elToggleSpaceMode.innerText = "Local"; + } else { + elToggleSpaceMode.className = "space-mode-world hifi-edit-button"; + elToggleSpaceMode.innerText = "World"; + } + } document.addEventListener("keydown", function (keyDownEvent) { if (keyDownEvent.target.nodeName === "INPUT") { @@ -866,12 +883,15 @@ function loaded() { updateSelectedEntities(data.selectedIDs, true); } } + setSpaceMode(data.spaceMode); }); } else if (data.type === "removeEntities" && data.deletedIDs !== undefined && data.selectedIDs !== undefined) { removeEntities(data.deletedIDs); updateSelectedEntities(data.selectedIDs, true); } else if (data.type === "deleted" && data.ids) { removeEntities(data.ids); + } else if (data.type === "setSpaceMode") { + setSpaceMode(data.spaceMode); } }); } diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index b66d7e19c6..f2c84d2f36 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -31,6 +31,12 @@ const DEGREES_TO_RADIANS = Math.PI / 180.0; const NO_SELECTION = "<i>No selection</i>"; +const PROPERTY_SPACE_MODE = { + ALL: 0, + LOCAL: 1, + WORLD: 2 +}; + const GROUPS = [ { id: "base", @@ -662,7 +668,7 @@ const GROUPS = [ propertyID: "speedSpread", }, { - label: "Emit Dimension", + label: "Emit Dimensions", type: "vec3", vec3Type: "xyz", min: 0, @@ -957,6 +963,17 @@ const GROUPS = [ subLabels: [ "x", "y", "z" ], unit: "m", propertyID: "position", + spaceMode: PROPERTY_SPACE_MODE.WORLD, + }, + { + label: "Local Position", + type: "vec3", + vec3Type: "xyz", + decimals: 4, + subLabels: [ "x", "y", "z" ], + unit: "m", + propertyID: "localPosition", + spaceMode: PROPERTY_SPACE_MODE.LOCAL, }, { label: "Rotation", @@ -967,9 +984,21 @@ const GROUPS = [ subLabels: [ "pitch", "yaw", "roll" ], unit: "deg", propertyID: "rotation", + spaceMode: PROPERTY_SPACE_MODE.WORLD, }, { - label: "Dimension", + label: "Local Rotation", + type: "vec3", + vec3Type: "pyr", + step: 0.1, + decimals: 4, + subLabels: [ "pitch", "yaw", "roll" ], + unit: "deg", + propertyID: "localRotation", + spaceMode: PROPERTY_SPACE_MODE.LOCAL, + }, + { + label: "Dimensions", type: "vec3", vec3Type: "xyz", min: 0, @@ -978,6 +1007,19 @@ const GROUPS = [ subLabels: [ "x", "y", "z" ], unit: "m", propertyID: "dimensions", + spaceMode: PROPERTY_SPACE_MODE.WORLD, + }, + { + label: "Local Dimensions", + type: "vec3", + vec3Type: "xyz", + min: 0, + step: 0.1, + decimals: 4, + subLabels: [ "x", "y", "z" ], + unit: "m", + propertyID: "localDimensions", + spaceMode: PROPERTY_SPACE_MODE.LOCAL, }, { label: "Scale", @@ -1174,7 +1216,7 @@ const GROUPS = [ decimals: 4, subLabels: [ "x", "y", "z" ], unit: "m/s", - propertyID: "velocity", + propertyID: "localVelocity", }, { label: "Linear Damping", @@ -1190,7 +1232,7 @@ const GROUPS = [ decimals: 4, subLabels: [ "pitch", "yaw", "roll" ], unit: "deg/s", - propertyID: "angularVelocity", + propertyID: "localAngularVelocity", }, { label: "Angular Damping", @@ -1317,6 +1359,7 @@ var particlePropertyUpdates = {}; var selectedEntityProperties; var lastEntityID = null; var createAppTooltip = new CreateAppTooltip(); +let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; function debugPrint(message) { EventBridge.emitWebEvent( @@ -2666,7 +2709,17 @@ function showParentMaterialNameBox(number, elNumber, elString) { } } - +function updateVisibleSpaceModeProperties() { + for (let propertyID in properties) { + if (properties.hasOwnProperty(propertyID)) { + let property = properties[propertyID]; + let propertySpaceMode = property.spaceMode; + if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL) { + showPropertyElement(propertyID, propertySpaceMode === currentSpaceMode); + } + } + } +} function loaded() { openEventBridge(function() { @@ -2700,6 +2753,7 @@ function loaded() { let propertyType = propertyData.type; let propertyID = propertyData.propertyID; let propertyName = propertyData.propertyName !== undefined ? propertyData.propertyName : propertyID; + let propertySpaceMode = propertyData.spaceMode !== undefined ? propertyData.spaceMode : PROPERTY_SPACE_MODE.ALL; let propertyElementID = "property-" + propertyID; propertyElementID = propertyElementID.replace('.', '-'); @@ -2746,7 +2800,8 @@ function loaded() { elementID: propertyElementID, name: propertyName, isParticleProperty: group.id.includes("particles"), - elProperty: elProperty + elProperty, + spaceMode: propertySpaceMode, }; properties[propertyID] = property; @@ -2840,6 +2895,8 @@ function loaded() { elGroups[group.id] = elGroup; }); + + updateVisibleSpaceModeProperties(); if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { @@ -2860,6 +2917,9 @@ function loaded() { elServerScriptStatus.innerText = NOT_RUNNING_SCRIPT_STATUS; } } else if (data.type === "update" && data.selections) { + if (data.spaceMode !== undefined) { + currentSpaceMode = data.spaceMode === "local" ? PROPERTY_SPACE_MODE.LOCAL : PROPERTY_SPACE_MODE.WORLD; + } if (data.selections.length === 0) { if (lastEntityID !== null) { if (editor !== null) { @@ -3082,7 +3142,9 @@ function loaded() { } } } - + + updateVisibleSpaceModeProperties(); + if (selectedEntityProperties.type === "Image") { let imageLink = JSON.parse(selectedEntityProperties.textures)["tex.picture"]; getPropertyInputElement("image").value = imageLink; @@ -3167,6 +3229,9 @@ function loaded() { createAppTooltip.setTooltipData(data.tooltips); } else if (data.type === 'hmdActiveChanged') { createAppTooltip.setIsEnabled(!data.hmdActive); + } else if (data.type === 'setSpaceMode') { + currentSpaceMode = data.spaceMode === "local" ? PROPERTY_SPACE_MODE.LOCAL : PROPERTY_SPACE_MODE.WORLD; + updateVisibleSpaceModeProperties(); } }); diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 585820d32f..c47e8045d6 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -115,6 +115,13 @@ EntityListTool = function(shouldUseEditTabletApp) { }); }); + that.setSpaceMode = function(spaceMode) { + emitJSONScriptEvent({ + type: 'setSpaceMode', + spaceMode: spaceMode + }); + }; + that.clearEntityList = function() { emitJSONScriptEvent({ type: 'clearEntityList' @@ -200,6 +207,7 @@ EntityListTool = function(shouldUseEditTabletApp) { type: "update", entities: entities, selectedIDs: selectedIDs, + spaceMode: SelectionDisplay.getSpaceMode(), }); }); }; @@ -288,6 +296,8 @@ EntityListTool = function(shouldUseEditTabletApp) { Entities.editEntity(data.entityID, {name: data.name}); // make sure that the name also gets updated in the properties window SelectionManager._update(); + } else if (data.type === "toggleSpaceMode") { + SelectionDisplay.toggleSpaceMode(); } }; diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 3bb36d632e..b0b1740821 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -500,7 +500,7 @@ SelectionManager = (function() { that.entityType = properties.type; if (selectionUpdated) { - SelectionDisplay.setSpaceMode(SPACE_LOCAL); + SelectionDisplay.useDesiredSpaceMode(); } } else { properties = Entities.getEntityProperties(that.selections[0], ['type', 'boundingBox']); @@ -537,7 +537,7 @@ SelectionManager = (function() { }; // For 1+ selections we can only modify selections in world space - SelectionDisplay.setSpaceMode(SPACE_WORLD); + SelectionDisplay.setSpaceMode(SPACE_WORLD, false); } for (var j = 0; j < listeners.length; j++) { @@ -651,6 +651,8 @@ SelectionDisplay = (function() { }; var spaceMode = SPACE_LOCAL; + var desiredSpaceMode = SPACE_LOCAL; + var overlayNames = []; var lastControllerPoses = [ getControllerWorldLocation(Controller.Standard.LeftHand, true), @@ -1400,8 +1402,21 @@ SelectionDisplay = (function() { that.updateHandles(); }; + + /** + * This callback is used for spaceMode changes. + * @callback spaceModeChangedCallback + * @param {string} spaceMode + */ + + /** + * set this property with a callback to keep track of spaceMode changes. + * @type {spaceModeChangedCallback} + */ + that.onSpaceModeChange = null; + // FUNCTION: SET SPACE MODE - that.setSpaceMode = function(newSpaceMode) { + that.setSpaceMode = function(newSpaceMode, isDesiredChange) { var wantDebug = false; if (wantDebug) { print("======> SetSpaceMode called. ========"); @@ -1411,7 +1426,15 @@ SelectionDisplay = (function() { if (wantDebug) { print(" Updating SpaceMode From: " + spaceMode + " To: " + newSpaceMode); } + if (isDesiredChange) { + desiredSpaceMode = newSpaceMode; + } spaceMode = newSpaceMode; + + if (that.onSpaceModeChange !== null) { + that.onSpaceModeChange(newSpaceMode); + } + that.updateHandles(); } else if (wantDebug) { print("WARNING: entitySelectionTool.setSpaceMode - Can't update SpaceMode. CurrentMode: " + @@ -1437,14 +1460,36 @@ SelectionDisplay = (function() { if (wantDebug) { print("PreToggle: " + spaceMode); } - spaceMode = (spaceMode === SPACE_LOCAL) ? SPACE_WORLD : SPACE_LOCAL; - that.updateHandles(); + that.setSpaceMode((spaceMode === SPACE_LOCAL) ? SPACE_WORLD : SPACE_LOCAL, true); if (wantDebug) { print("PostToggle: " + spaceMode); print("======== ToggleSpaceMode called. <========="); } }; + /** + * Switches the display mode back to the set desired display mode + */ + that.useDesiredSpaceMode = function() { + var wantDebug = false; + if (wantDebug) { + print("========> UseDesiredSpaceMode called. ========="); + } + that.setSpaceMode(desiredSpaceMode, false); + if (wantDebug) { + print("PostToggle: " + spaceMode); + print("======== UseDesiredSpaceMode called. <========="); + } + }; + + /** + * Get the currently set SpaceMode + * @returns {string} spaceMode + */ + that.getSpaceMode = function() { + return spaceMode; + }; + function addHandleTool(overlay, tool) { handleTools[overlay] = tool; return tool;