Merge pull request #932 from AleziaKurdis/CreateApp_DEC2020_RotateAsNextClickedSurface

Create Application: Snap To Next Clicked Surface
This commit is contained in:
Kalila 2021-02-04 17:15:32 -05:00 committed by GitHub
commit 62b70fda4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 314 additions and 61 deletions

View file

@ -2,6 +2,9 @@
// inEditMode.js
//
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -80,7 +83,9 @@ Script.include("/~/system/libraries/utils.js");
Messages.sendLocalMessage(this.ENTITY_TOOL_UPDATES_CHANNEL, JSON.stringify({
method: "selectEntity",
entityID: this.selectedTarget.objectID,
hand: hand
hand: hand,
surfaceNormal: this.selectedTarget.surfaceNormal,
intersection: this.selectedTarget.intersection
}));
} else if (this.selectedTarget.type === Picks.INTERSECTED_OVERLAY) {
Messages.sendLocalMessage(this.ENTITY_TOOL_UPDATES_CHANNEL, JSON.stringify({

View file

@ -12,9 +12,10 @@
audioFeedback = (function() {
var that = {};
var confirmationSound = SoundCache.getSound(Script.resolvePath("./sounds/confirmation.mp3"));
var rejectionSound = SoundCache.getSound(Script.resolvePath("./sounds/rejection.mp3"));
var actionSound = SoundCache.getSound(Script.resolvePath("./sounds/action.mp3"));
that.confirmation = function() { //Play a confirmation sound
var injector = Audio.playSound(confirmationSound, {
@ -25,8 +26,15 @@ audioFeedback = (function() {
that.rejection = function() { //Play a rejection sound
var injector = Audio.playSound(rejectionSound, {
"volume": 0.3,
"localOnly": true
"volume": 0.3,
"localOnly": true
});
}
that.action = function() { //Play an action sound
var injector = Audio.playSound(actionSound, {
"volume": 0.3,
"localOnly": true
});
}

Binary file not shown.

View file

@ -109,6 +109,8 @@ var entityIconOverlayManager = new EntityIconOverlayManager(["Light", "ParticleE
});
var hmdMultiSelectMode = false;
var expectingRotateAsClickedSurface = false;
var keepSelectedOnNextClick = false;
var cameraManager = new CameraManager();
@ -1106,25 +1108,50 @@ function findClickedEntity(event) {
}
var result;
if (iconResult.intersects) {
result = iconResult;
} else if (entityResult.intersects) {
result = entityResult;
if (expectingRotateAsClickedSurface) {
if (!SelectionManager.hasSelection() || !SelectionManager.hasUnlockedSelection()) {
audioFeedback.rejection();
Window.notifyEditError("You have nothing selected, or the selection is locked.");
expectingRotateAsClickedSurface = false;
} else {
//Rotate Selection according the Surface Normal
var normalRotation = Quat.lookAtSimple(Vec3.ZERO, Vec3.multiply(entityResult.surfaceNormal, -1));
selectionDisplay.rotateSelection(normalRotation);
//Translate Selection according the clicked Surface
var distanceFromSurface;
if (selectionDisplay.getSpaceMode() === "world"){
distanceFromSurface = SelectionManager.worldDimensions.z / 2;
} else {
distanceFromSurface = SelectionManager.localDimensions.z / 2;
}
selectionDisplay.moveSelection(Vec3.sum(entityResult.intersection, Vec3.multiplyQbyV( normalRotation, {"x": 0.0, "y":0.0, "z": distanceFromSurface})));
selectionManager._update(false, this);
pushCommandForSelections();
expectingRotateAsClickedSurface = false;
audioFeedback.action();
}
keepSelectedOnNextClick = true;
return null;
} else {
return null;
}
if (iconResult.intersects) {
result = iconResult;
} else if (entityResult.intersects) {
result = entityResult;
} else {
return null;
}
if (!result.accurate) {
return null;
}
if (!result.accurate) {
return null;
}
var foundEntity = result.entityID;
return {
pickRay: pickRay,
entityID: foundEntity,
intersection: result.intersection
};
var foundEntity = result.entityID;
return {
pickRay: pickRay,
entityID: foundEntity,
intersection: result.intersection
};
}
}
// Handles selections on overlays while in edit mode by querying entities from
@ -1295,7 +1322,10 @@ function mouseClickEvent(event) {
if (result === null || result === undefined) {
if (!event.isShifted) {
selectionManager.clearSelections(this);
if (!keepSelectedOnNextClick) {
selectionManager.clearSelections(this);
}
keepSelectedOnNextClick = false;
}
return;
}
@ -2052,6 +2082,26 @@ function gridToAvatarKey(value) {
alignGridToAvatar();
}
}
function rotateAsNextClickedSurfaceKey(value) {
if (value === 0) { // on release
rotateAsNextClickedSurface();
}
}
function quickRotate90xKey(value) {
if (value === 0) { // on release
selectionDisplay.rotate90degreeSelection("X");
}
}
function quickRotate90yKey(value) {
if (value === 0) { // on release
selectionDisplay.rotate90degreeSelection("Y");
}
}
function quickRotate90zKey(value) {
if (value === 0) { // on release
selectionDisplay.rotate90degreeSelection("Z");
}
}
function recursiveAdd(newParentID, parentData) {
if (parentData.children !== undefined) {
var children = parentData.children;
@ -2819,6 +2869,10 @@ mapping.from([Controller.Hardware.Keyboard.J]).to(gridKey);
mapping.from([Controller.Hardware.Keyboard.G]).to(viewGridKey);
mapping.from([Controller.Hardware.Keyboard.H]).to(snapKey);
mapping.from([Controller.Hardware.Keyboard.K]).to(gridToAvatarKey);
mapping.from([Controller.Hardware.Keyboard["0"]]).to(rotateAsNextClickedSurfaceKey);
mapping.from([Controller.Hardware.Keyboard["7"]]).to(quickRotate90xKey);
mapping.from([Controller.Hardware.Keyboard["8"]]).to(quickRotate90yKey);
mapping.from([Controller.Hardware.Keyboard["9"]]).to(quickRotate90zKey);
mapping.from([Controller.Hardware.Keyboard.X])
.when([Controller.Hardware.Keyboard.Control])
.to(whenReleased(function() { selectionManager.cutSelectedEntities() }));
@ -2867,6 +2921,14 @@ keyUpEventFromUIWindow = function(keyUpEvent) {
snapKey(pressedValue);
} else if (keyUpEvent.keyCodeString === "K") {
gridToAvatarKey(pressedValue);
} else if (keyUpEvent.keyCodeString === "0") {
rotateAsNextClickedSurfaceKey(pressedValue);
} else if (keyUpEvent.keyCodeString === "7") {
quickRotate90xKey(pressedValue);
} else if (keyUpEvent.keyCodeString === "8") {
quickRotate90yKey(pressedValue);
} else if (keyUpEvent.keyCodeString === "9") {
quickRotate90zKey(pressedValue);
} else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "X") {
selectionManager.cutSelectedEntities();
} else if (keyUpEvent.controlKey && keyUpEvent.keyCodeString === "C") {
@ -3015,4 +3077,14 @@ function toggleGridVisibility() {
}
}
function rotateAsNextClickedSurface() {
if (!SelectionManager.hasSelection() || !SelectionManager.hasUnlockedSelection()) {
audioFeedback.rejection();
Window.notifyEditError("You have nothing selected, or the selection is locked.");
expectingRotateAsClickedSurface = false;
} else {
expectingRotateAsClickedSurface = true;
}
}
}()); // END LOCAL_SCOPE

View file

@ -383,6 +383,14 @@ EntityListTool = function(shouldUseEditTabletApp) {
SelectionManager.selectTopFamily();
} else if (data.type === 'teleportToEntity') {
SelectionManager.teleportToEntity();
} else if (data.type === 'rotateAsTheNextClickedSurface') {
rotateAsNextClickedSurface();
} else if (data.type === 'quickRotate90x') {
selectionDisplay.rotate90degreeSelection("X");
} else if (data.type === 'quickRotate90y') {
selectionDisplay.rotate90degreeSelection("Y");
} else if (data.type === 'quickRotate90z') {
selectionDisplay.rotate90degreeSelection("Z");
} else if (data.type === 'moveEntitySelectionToAvatar') {
SelectionManager.moveEntitiesSelectionToAvatar();
} else if (data.type === 'loadConfigSetting') {

View file

@ -24,6 +24,11 @@
<script type="text/javascript" src="js/entityList.js"></script>
</head>
<body onload='loaded();' id="entity-list-body">
<div id="entity-list-menubar">
<input type="button" class="normal" id="selection" value="Select&#9662;" />
<input type="button" class="normal" id="actions" value="Edit&#9662;" />
<input type="button" class="normal" id="tools" value="Tools&#9662;" />
</div>
<div id="entity-list-header">
<input type="button" class="glyph" id="refresh" value="F" />
<div>
@ -32,9 +37,7 @@
</div>
<button id="toggle-space-mode" class="hifi-edit-button space-mode-local">Local</button>
<input type="button" class="vglyph" id="hmdmultiselect" value="I" style="display: none;" />
<input type="button" class="normal" id="selection" value="Select&#9662;" />
<input type="button" class="normal" id="actions" value="Edit&#9662;" />
<input type="button" class="normal" id="tools" value="Tools&#9662;" />
<input type="button" class="red glyph" id="delete" value="{" />
</div>
<div id="entity-list">
<div id="filter-area">
@ -58,8 +61,7 @@
</div>
<input type="button" id="filter-in-view" class="glyph" value="&#xe007;" />
<div id="filter-radius-and-unit" class="number">
<label for="radius">Search radius <span class="unit">m</span></label>
<input type="text" id="filter-radius" maxlength="9" value="100" />
<span class="icon-input-radius"><input type="text" id="filter-radius" maxlength="8" value="100" /><span>D</span><label>m</label></span>
</div>
</div>
<div id="entity-table-scroll">
@ -130,12 +132,6 @@
<div class = "menu-item-shortcut">Ctrl-D</div>
</div>
</button>
<button class="menu-button" id="delete" >
<div class = "menu-item">
<div class = "menu-item-caption">Delete</div>
<div class = "menu-item-shortcut">Del</div>
</div>
</button>
<div class="menu-separator"></div>
<button class="menu-button" id="parent" >
<div class = "menu-item">
@ -156,6 +152,31 @@
<div class = "menu-item-shortcut"></div>
</div>
</button>
<div class="menu-separator"></div>
<button class="menu-button" id="rotateAsTheNextClickedSurface" >
<div class = "menu-item">
<div class = "menu-item-caption">Snap To Next Clicked Surface</div>
<div class = "menu-item-shortcut">0</div>
</div>
</button>
<button class="menu-button" id="quickRotate90x" >
<div class = "menu-item">
<div class = "menu-item-caption">Rotate 90&deg; on X axis</div>
<div class = "menu-item-shortcut">7</div>
</div>
</button>
<button class="menu-button" id="quickRotate90y" >
<div class = "menu-item">
<div class = "menu-item-caption">Rotate 90&deg; on Y axis</div>
<div class = "menu-item-shortcut">8</div>
</div>
</button>
<button class="menu-button" id="quickRotate90z" >
<div class = "menu-item">
<div class = "menu-item-caption">Rotate 90&deg; on Z axis</div>
<div class = "menu-item-shortcut">9</div>
</div>
</button>
</div>
<div class="entity-list-menu" id="selection-menu" >
<button class="menu-button" id="selectall" >

View file

@ -15,7 +15,7 @@ const BYTES_PER_MEGABYTE = 1024 * 1024;
const COLLAPSE_EXTRA_INFO = "E";
const EXPAND_EXTRA_INFO = "D";
const FILTER_IN_VIEW_ATTRIBUTE = "pressed";
const WINDOW_NONVARIABLE_HEIGHT = 227;
const WINDOW_NONVARIABLE_HEIGHT = 180;
const EMPTY_ENTITY_ID = "0";
const MAX_LENGTH_RADIUS = 9;
const MINIMUM_COLUMN_WIDTH = 24;
@ -238,6 +238,10 @@ let elEntityTable,
elParent,
elUnparent,
elDelete,
elRotateAsTheNextClickedSurface,
elQuickRotate90x,
elQuickRotate90y,
elQuickRotate90z,
elMoveEntitySelectionToAvatar,
elSelectAll,
elSelectInverse,
@ -320,6 +324,10 @@ function loaded() {
elParent = document.getElementById("parent");
elUnparent = document.getElementById("unparent");
elDelete = document.getElementById("delete");
elRotateAsTheNextClickedSurface = document.getElementById("rotateAsTheNextClickedSurface");
elQuickRotate90x = document.getElementById("quickRotate90x");
elQuickRotate90y = document.getElementById("quickRotate90y");
elQuickRotate90z = document.getElementById("quickRotate90z");
elMoveEntitySelectionToAvatar = document.getElementById("moveEntitySelectionToAvatar");
elSelectAll = document.getElementById("selectall");
elSelectInverse = document.getElementById("selectinverse");
@ -430,6 +438,22 @@ function loaded() {
EventBridge.emitWebEvent(JSON.stringify({ type: "delete" }));
closeAllEntityListMenu();
};
elRotateAsTheNextClickedSurface.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: "rotateAsTheNextClickedSurface" }));
closeAllEntityListMenu();
};
elQuickRotate90x.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: "quickRotate90x" }));
closeAllEntityListMenu();
};
elQuickRotate90y.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: "quickRotate90y" }));
closeAllEntityListMenu();
};
elQuickRotate90z.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: "quickRotate90z" }));
closeAllEntityListMenu();
};
elMoveEntitySelectionToAvatar.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: "moveEntitySelectionToAvatar" }));
closeAllEntityListMenu();

View file

@ -103,10 +103,34 @@ SelectionManager = (function() {
if (wantDebug) {
print("setting selection to " + messageParsed.entityID);
}
if (hmdMultiSelectMode) {
that.addEntity(messageParsed.entityID, true, that);
if (expectingRotateAsClickedSurface) {
if (!SelectionManager.hasSelection() || !SelectionManager.hasUnlockedSelection()) {
audioFeedback.rejection();
Window.notifyEditError("You have nothing selected, or the selection is locked.");
expectingRotateAsClickedSurface = false;
} else {
//Rotate Selection according the Surface Normal
var normalRotation = Quat.lookAtSimple(Vec3.ZERO, Vec3.multiply(messageParsed.surfaceNormal, -1));
selectionDisplay.rotateSelection(normalRotation);
//Translate Selection according the clicked Surface
var distanceFromSurface;
if (selectionDisplay.getSpaceMode() === SPACE_WORLD){
distanceFromSurface = SelectionManager.worldDimensions.z / 2;
} else {
distanceFromSurface = SelectionManager.localDimensions.z / 2;
}
selectionDisplay.moveSelection(Vec3.sum(messageParsed.intersection, Vec3.multiplyQbyV( normalRotation, {"x": 0.0, "y":0.0, "z": distanceFromSurface})));
that._update(false, this);
pushCommandForSelections();
expectingRotateAsClickedSurface = false;
audioFeedback.action();
}
} else {
that.setSelections([messageParsed.entityID], that);
if (hmdMultiSelectMode) {
that.addEntity(messageParsed.entityID, true, that);
} else {
that.setSelections([messageParsed.entityID], that);
}
}
}
} else if (messageParsed.method === "clearSelection") {
@ -2377,7 +2401,68 @@ SelectionDisplay = (function() {
}
debugPickPlaneHits = [];
};
that.rotateSelection = function(rotation) {
SelectionManager.saveProperties();
if (SelectionManager.selections.length === 1) {
SelectionManager.savedProperties[SelectionManager.selections[0]].rotation = Quat.IDENTITY;
}
updateSelectionsRotation(rotation, SelectionManager.worldPosition);
};
that.moveSelection = function(targetPosition) {
SelectionManager.saveProperties();
// editing a parent will cause all the children to automatically follow along, so don't
// edit any entity who has an ancestor in SelectionManager.selections
var toMove = SelectionManager.selections.filter(function (selection) {
if (SelectionManager.selections.indexOf(SelectionManager.savedProperties[selection].parentID) >= 0) {
return false; // a parent is also being moved, so don't issue an edit for this entity
} else {
return true;
}
});
for (var i = 0; i < toMove.length; i++) {
var id = toMove[i];
var properties = SelectionManager.savedProperties[id];
var newPosition = Vec3.sum(targetPosition, Vec3.subtract(properties.position, SelectionManager.worldPosition));
Entities.editEntity(id, { position: newPosition });
}
};
that.rotate90degreeSelection = function(axis) {
//axis is a string and expect "X", "Y" or "Z"
if (!SelectionManager.hasSelection() || !SelectionManager.hasUnlockedSelection()) {
audioFeedback.rejection();
Window.notifyEditError("You have nothing selected, or the selection is locked.");
} else {
var currentRotation, axisRotation;
SelectionManager.saveProperties();
if (selectionManager.selections.length === 1 && spaceMode === SPACE_LOCAL) {
currentRotation = SelectionManager.localRotation;
} else {
currentRotation = SelectionManager.worldRotation;
}
switch(axis) {
case "X":
axisRotation = Quat.angleAxis(90.0, Quat.getRight(currentRotation));
break;
case "Y":
axisRotation = Quat.angleAxis(90.0, Quat.getUp(currentRotation));
break;
case "Z":
axisRotation = Quat.angleAxis(90.0, Quat.getForward(currentRotation));
break;
default:
return;
}
updateSelectionsRotation(axisRotation, SelectionManager.worldPosition);
SelectionManager._update(false, this);
pushCommandForSelections();
audioFeedback.action();
}
};
function addUnlitMaterialOnToolEntity(toolEntityParentID) {
var toolEntitiesMaterialData = "{\n \"materialVersion\": 1,\n \"materials\": [\n {\n \"name\": \"0\",\n \"defaultFallthrough\": true,\n \"unlit\": true,\n \"model\": \"hifi_pbr\"\n }\n ]\n}";
var materialEntityID = Entities.addEntity({

View file

@ -1208,7 +1208,7 @@ div.refresh input[type="button"] {
}
.fieldset .checkbox-sub-props .property:first-child {
margin-top: 0;
margin-top: 0px;
}
.column {
@ -1252,18 +1252,18 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
}
div#grid-section, body#entity-list-body {
padding-bottom: 0;
margin: 16px;
padding-bottom: 0px;
margin: 0px 8px 8px 8px;
}
#entity-list-header {
margin-bottom: 36px;
margin-bottom: 6px;
}
#entity-list-header div {
display: inline-block;
width: 65px;
margin-right: 6px;
margin-right: 4px;
}
#entity-list-header div input:first-child {
@ -1281,13 +1281,19 @@ div#grid-section, body#entity-list-body {
border-bottom-left-radius: 0;
}
#delete {
float: right;
margin-right: 0;
background-color: #ff0000;
}
#entity-list {
position: relative; /* New positioning context. */
}
#filter-area {
padding-right: 168px;
padding-bottom: 24px;
padding-bottom: 8px;
}
#filter-type-multiselect-box select {
@ -1340,35 +1346,59 @@ div#grid-section, body#entity-list-body {
#filter-type-options input[type=button]:enabled:hover {
background: linear-gradient(#afafaf 20%, #575757 100%);
}
#filter-search-and-icon {
position: relative;
left: 118px;
width: calc(100% - 126px);
}
#filter-in-view {
position: absolute;
top: 0;
top: 0px;
right: 126px;
}
#filter-radius-and-unit {
position: relative;
float: right;
margin-right: -168px;
top: -45px;
top: -28px;
}
#filter-radius-and-unit label {
margin-left: 2px;
#entity-list-menubar {
margin: 0px -8px 6px -8px;
padding: 0px 8px 0px 8px;
border: none;
background-color: #000;
background: linear-gradient(#343434 20%, #000 100%);
}
#filter-radius-and-unit span {
.icon-input-radius input {
position: relative;
top: 25px;
padding-left: 36px;
}
.icon-input-radius span {
position: absolute;
left: 4px;
top: -4px;
font-family: Vircadia-Glyphs;
font-size: 23px;
color: #afafaf;
}
.icon-input-radius input:focus + span + label {
color: #ffffff;
}
.icon-input-radius label {
position: absolute;
margin-left: 3px;
bottom: 6px;
right: 9px;
z-index: 2;
font-style: italic;
}
#filter-radius:focus {
outline: none;
box-sizing: border-box;
height: 26px;
margin-top: 1px;
margin-bottom: 1px;
box-shadow: 0 0 0 1px #00b4ef;
}
#filter-radius-and-unit input {
width: 120px;
border-radius: 14.5px;
@ -1384,12 +1414,12 @@ div#grid-section, body#entity-list-body {
#footer-text {
float: right;
padding-top: 12px;
padding-top: 6px;
padding-right: 22px;
}
input[type=button]#export {
height: 38px;
height: 26px;
width: 180px;
}
@ -1441,7 +1471,7 @@ input[type=button]#export {
overflow-x: hidden;
overflow-y: auto;
box-sizing: border-box;
padding-top: 28px; /* Space for header and footer outside of scroll region. */
padding-top: 37px; /* Space for header and footer outside of scroll region. */
margin-top: 28px;
border-left: 2px solid #575757;
border-right: 2px solid #575757;
@ -1462,7 +1492,7 @@ input[type=button]#export {
}
#entity-table {
margin-top: -28px;
margin-top: -20px;
margin-bottom: -18px;
table-layout: fixed;
border: none;
@ -1539,7 +1569,7 @@ input[type=button]#export {
}
#entity-table td .glyph .vglyph {
text-align: center;
padding: 0;
padding: 0px;
}
#properties-base {
@ -1888,10 +1918,10 @@ div.multiZoneSelToolbar {
div.entity-list-menu {
position: fixed;
display: none;
width: 70%;
width: 370px;
height: 30px;
top: 42px;
left: 150px;
top: 27px;
left: 8px;
right: 0;
bottom: 0;
border-style: solid;