mirror of
https://github.com/overte-org/overte.git
synced 2025-04-26 23:56:22 +02:00
552 lines
20 KiB
JavaScript
552 lines
20 KiB
JavaScript
//
|
|
// createPalette.js
|
|
//
|
|
// Created by David Rowe on 28 Jul 2017.
|
|
// Copyright 2017 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
|
|
//
|
|
|
|
/* global CreatePalette:true, App, Feedback, History, Preload, UIT */
|
|
|
|
CreatePalette = function (side, leftInputs, rightInputs, uiCommandCallback) {
|
|
// Tool menu displayed on top of forearm.
|
|
|
|
"use strict";
|
|
|
|
var paletteOriginOverlay,
|
|
paletteHeaderHeadingOverlay,
|
|
paletteHeaderBarOverlay,
|
|
paletteTitleOverlay,
|
|
palettePanelOverlay,
|
|
paletteItemOverlays = [],
|
|
paletteItemPositions = [],
|
|
paletteItemHoverOverlays = [],
|
|
iconOverlays = [],
|
|
staticOverlays = [],
|
|
|
|
LEFT_HAND = 0,
|
|
|
|
controlJointName,
|
|
|
|
PALETTE_ORIGIN_POSITION = {
|
|
x: 0,
|
|
y: UIT.dimensions.handOffset,
|
|
z: UIT.dimensions.canvasSeparation + UIT.dimensions.canvas.x / 2
|
|
},
|
|
PALETTE_ORIGIN_ROTATION = Quat.ZERO,
|
|
paletteLateralOffset,
|
|
|
|
PALETTE_ORIGIN_PROPERTIES = {
|
|
dimensions: { x: 0.005, y: 0.005, z: 0.005 },
|
|
localPosition: PALETTE_ORIGIN_POSITION,
|
|
localRotation: PALETTE_ORIGIN_ROTATION,
|
|
color: { red: 255, blue: 0, green: 0 },
|
|
alpha: 1.0,
|
|
parentID: Uuid.SELF,
|
|
ignoreRayIntersection: true,
|
|
visible: false
|
|
},
|
|
|
|
PALETTE_HEADER_HEADING_PROPERTIES = {
|
|
url: Script.resolvePath("../assets/gray-header.fbx"),
|
|
dimensions: UIT.dimensions.headerHeading, // Model is in rotated coordinate system but can override.
|
|
localPosition: {
|
|
x: 0,
|
|
y: UIT.dimensions.canvas.y / 2 - UIT.dimensions.headerHeading.y / 2,
|
|
z: UIT.dimensions.headerHeading.z / 2
|
|
},
|
|
localRotation: Quat.ZERO,
|
|
alpha: 1.0,
|
|
solid: true,
|
|
ignoreRayIntersection: false,
|
|
visible: true
|
|
},
|
|
|
|
PALETTE_HEADER_BAR_PROPERTIES = {
|
|
url: Script.resolvePath("../assets/blue-header-bar.fbx"),
|
|
dimensions: UIT.dimensions.headerBar, // Model is in rotated coordinate system but can override.
|
|
localPosition: {
|
|
x: 0,
|
|
y: UIT.dimensions.canvas.y / 2 - UIT.dimensions.headerHeading.y - UIT.dimensions.headerBar.y / 2,
|
|
z: UIT.dimensions.headerBar.z / 2
|
|
},
|
|
localRotation: Quat.ZERO,
|
|
alpha: 1.0,
|
|
solid: true,
|
|
ignoreRayIntersection: false,
|
|
visible: true
|
|
},
|
|
|
|
PALETTE_TITLE_PROPERTIES = {
|
|
url: Script.resolvePath("../assets/create/create-heading.svg"),
|
|
scale: 0.0363,
|
|
localPosition: {
|
|
x: 0,
|
|
y: 0,
|
|
z: PALETTE_HEADER_HEADING_PROPERTIES.dimensions.z / 2 + UIT.dimensions.imageOverlayOffset
|
|
},
|
|
localRotation: Quat.ZERO,
|
|
color: UIT.colors.white,
|
|
alpha: 1.0,
|
|
emissive: true,
|
|
ignoreRayIntersection: true,
|
|
isFacingAvatar: false,
|
|
visible: true
|
|
},
|
|
|
|
PALETTE_PANEL_PROPERTIES = {
|
|
dimensions: UIT.dimensions.panel,
|
|
localPosition: { x: 0, y: (UIT.dimensions.panel.y - UIT.dimensions.canvas.y) / 2, z: UIT.dimensions.panel.z / 2 },
|
|
localRotation: Quat.ZERO,
|
|
color: UIT.colors.baseGray,
|
|
alpha: 1.0,
|
|
solid: true,
|
|
ignoreRayIntersection: false,
|
|
visible: true
|
|
},
|
|
|
|
ENTITY_CREATION_DIMENSIONS = { x: 0.2, y: 0.2, z: 0.2 },
|
|
ENTITY_CREATION_COLOR = { red: 192, green: 192, blue: 192 },
|
|
|
|
PALETTE_ITEM = {
|
|
overlay: "cube", // Invisible cube for hit area.
|
|
properties: {
|
|
dimensions: UIT.dimensions.itemCollisionZone,
|
|
localRotation: Quat.ZERO,
|
|
alpha: 0.0, // Invisible.
|
|
solid: true,
|
|
ignoreRayIntersection: false,
|
|
visible: true // So that laser intersects.
|
|
},
|
|
hoverButton: {
|
|
// Relative to root overlay.
|
|
overlay: "cube",
|
|
properties: {
|
|
dimensions: UIT.dimensions.paletteItemButtonDimensions,
|
|
localPosition: UIT.dimensions.paletteItemButtonOffset,
|
|
localRotation: Quat.ZERO,
|
|
color: UIT.colors.blueHighlight,
|
|
alpha: 1.0,
|
|
emissive: true, // TODO: This has no effect.
|
|
solid: true,
|
|
ignoreRayIntersection: true,
|
|
visible: false
|
|
}
|
|
},
|
|
icon: {
|
|
// Relative to hoverButton.
|
|
overlay: "model",
|
|
properties: {
|
|
dimensions: UIT.dimensions.paletteItemIconDimensions,
|
|
localPosition: UIT.dimensions.paletteItemIconOffset,
|
|
localRotation: Quat.ZERO,
|
|
emissive: true, // TODO: This has no effect.
|
|
ignoreRayIntersection: true
|
|
}
|
|
},
|
|
entity: {
|
|
dimensions: ENTITY_CREATION_DIMENSIONS
|
|
}
|
|
},
|
|
|
|
PALETTE_ITEMS = [
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/cube.fbx")
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Box",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/sphere.fbx")
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Sphere",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/tetrahedron.fbx")
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Tetrahedron",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/octahedron.fbx")
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Octahedron",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/icosahedron.fbx")
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Icosahedron",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/dodecahedron.fbx")
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Dodecahedron",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/hexagon.fbx"),
|
|
dimensions: { x: 0.02078, y: 0.024, z: 0.024 },
|
|
localRotation: Quat.fromVec3Degrees({ x: 90, y: 0, z: 0 })
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Hexagon",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/prism.fbx"),
|
|
localRotation: Quat.fromVec3Degrees({ x: 90, y: 0, z: 0 })
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Triangle",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/octagon.fbx"),
|
|
dimensions: { x: 0.023805, y: 0.024, z: 0.024 },
|
|
localRotation: Quat.fromVec3Degrees({ x: 90, y: 0, z: 0 })
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Octagon",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/cylinder.fbx"),
|
|
localRotation: Quat.fromVec3Degrees({ x: 90, y: 0, z: 0 })
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Cylinder",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/cone.fbx"),
|
|
localRotation: Quat.fromVec3Degrees({ x: 90, y: 0, z: 0 })
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Cone",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
},
|
|
{
|
|
icon: {
|
|
properties: {
|
|
url: Script.resolvePath("../assets/create/circle.fbx"),
|
|
dimensions: { x: 0.024, y: 0.0005, z: 0.024 },
|
|
localRotation: Quat.fromVec3Degrees({ x: 90, y: 0, z: 0 })
|
|
}
|
|
},
|
|
entity: {
|
|
type: "Shape",
|
|
shape: "Circle",
|
|
dimensions: ENTITY_CREATION_DIMENSIONS,
|
|
color: ENTITY_CREATION_COLOR
|
|
}
|
|
}
|
|
],
|
|
|
|
isDisplaying = false,
|
|
|
|
NONE = -1,
|
|
highlightedItem = NONE,
|
|
wasTriggerClicked = false,
|
|
otherSide,
|
|
|
|
// References.
|
|
controlHand;
|
|
|
|
if (!(this instanceof CreatePalette)) {
|
|
return new CreatePalette();
|
|
}
|
|
|
|
|
|
function getAssetURLs() {
|
|
return Preload.findURLs([PALETTE_HEADER_HEADING_PROPERTIES, PALETTE_HEADER_BAR_PROPERTIES, PALETTE_TITLE_PROPERTIES,
|
|
PALETTE_ITEMS]);
|
|
}
|
|
|
|
function setHand(hand) {
|
|
// Assumes UI is not displaying.
|
|
var NUMBER_OF_HANDS = 2;
|
|
side = hand;
|
|
otherSide = (side + 1) % NUMBER_OF_HANDS;
|
|
controlHand = side === LEFT_HAND ? rightInputs.hand() : leftInputs.hand();
|
|
controlJointName = side === LEFT_HAND ? "LeftHand" : "RightHand";
|
|
paletteLateralOffset = side === LEFT_HAND ? -UIT.dimensions.handLateralOffset : UIT.dimensions.handLateralOffset;
|
|
}
|
|
|
|
setHand(side);
|
|
|
|
function getOverlayIDs() {
|
|
return [palettePanelOverlay, paletteHeaderHeadingOverlay, paletteHeaderBarOverlay].concat(paletteItemOverlays);
|
|
}
|
|
|
|
function setVisible(visible) {
|
|
var i,
|
|
length;
|
|
|
|
for (i = 0, length = staticOverlays.length; i < length; i++) {
|
|
Overlays.editOverlay(staticOverlays[i], { visible: visible });
|
|
}
|
|
|
|
if (!visible) {
|
|
for (i = 0, length = paletteItemHoverOverlays.length; i < length; i++) {
|
|
Overlays.editOverlay(paletteItemHoverOverlays[i], { visible: false });
|
|
}
|
|
}
|
|
}
|
|
|
|
function update(intersectionOverlayID) {
|
|
var itemIndex,
|
|
isTriggerClicked,
|
|
properties,
|
|
CREATE_OFFSET = { x: 0, y: 0.05, z: -0.02 },
|
|
INVERSE_HAND_BASIS_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 0, z: -90 }),
|
|
entityID;
|
|
|
|
itemIndex = paletteItemOverlays.indexOf(intersectionOverlayID);
|
|
|
|
// Unhighlight and lower old item.
|
|
if (highlightedItem !== NONE && (itemIndex === NONE || itemIndex !== highlightedItem)) {
|
|
Overlays.editOverlay(paletteItemHoverOverlays[highlightedItem], {
|
|
localPosition: UIT.dimensions.paletteItemButtonOffset,
|
|
visible: false
|
|
});
|
|
highlightedItem = NONE;
|
|
}
|
|
|
|
// Highlight and raise new item.
|
|
if (itemIndex !== NONE && highlightedItem !== itemIndex) {
|
|
Feedback.play(otherSide, Feedback.HOVER_BUTTON);
|
|
Overlays.editOverlay(paletteItemHoverOverlays[itemIndex], {
|
|
localPosition: UIT.dimensions.paletteItemButtonHoveredOffset,
|
|
visible: true
|
|
});
|
|
highlightedItem = itemIndex;
|
|
}
|
|
|
|
// Press item and create new entity.
|
|
isTriggerClicked = controlHand.triggerClicked();
|
|
if (highlightedItem !== NONE && isTriggerClicked && !wasTriggerClicked) {
|
|
// Create entity.
|
|
Feedback.play(otherSide, Feedback.CREATE_ENTITY);
|
|
properties = Object.clone(PALETTE_ITEMS[itemIndex].entity);
|
|
properties.position = Vec3.sum(controlHand.palmPosition(),
|
|
Vec3.multiplyQbyV(controlHand.orientation(),
|
|
Vec3.sum({ x: 0, y: properties.dimensions.z / 2, z: 0 }, CREATE_OFFSET)));
|
|
properties.rotation = Quat.multiply(controlHand.orientation(), INVERSE_HAND_BASIS_ROTATION);
|
|
entityID = Entities.addEntity(properties);
|
|
if (entityID !== Uuid.NULL) {
|
|
History.prePush(
|
|
otherSide,
|
|
{ deleteEntities: [{ entityID: entityID }] },
|
|
{ createEntities: [{ entityID: entityID, properties: properties }] }
|
|
);
|
|
} else {
|
|
Feedback.play(otherSide, Feedback.GENERAL_ERROR);
|
|
}
|
|
|
|
// Lower and unhighlight item.
|
|
Overlays.editOverlay(paletteItemHoverOverlays[itemIndex], {
|
|
localPosition: UIT.dimensions.paletteItemButtonOffset,
|
|
visible: false
|
|
});
|
|
|
|
uiCommandCallback("autoGrab");
|
|
}
|
|
|
|
wasTriggerClicked = isTriggerClicked;
|
|
}
|
|
|
|
function itemPosition(index) {
|
|
// Position relative to palette panel.
|
|
var ITEMS_PER_ROW = 4,
|
|
ROW_ZERO_Y_OFFSET = 0.0860,
|
|
ROW_SPACING = 0.0560,
|
|
COLUMN_ZERO_OFFSET = -0.08415,
|
|
COLUMN_SPACING = 0.0561,
|
|
row,
|
|
column;
|
|
|
|
row = Math.floor(index / ITEMS_PER_ROW);
|
|
column = index % ITEMS_PER_ROW;
|
|
|
|
return {
|
|
x: COLUMN_ZERO_OFFSET + column * COLUMN_SPACING,
|
|
y: ROW_ZERO_Y_OFFSET - row * ROW_SPACING,
|
|
z: UIT.dimensions.panel.z / 2 + UIT.dimensions.itemCollisionZone.z / 2
|
|
};
|
|
}
|
|
|
|
function display() {
|
|
// Creates and shows menu entities.
|
|
var handJointIndex,
|
|
properties,
|
|
i,
|
|
length;
|
|
|
|
if (isDisplaying) {
|
|
return;
|
|
}
|
|
|
|
// Joint index.
|
|
handJointIndex = MyAvatar.getJointIndex(controlJointName);
|
|
if (handJointIndex === NONE) {
|
|
// Don't display if joint isn't available (yet) to attach to.
|
|
// User can clear this condition by toggling the app off and back on once avatar finishes loading.
|
|
App.log(side, "ERROR: CreatePalette: Hand joint index isn't available!");
|
|
return;
|
|
}
|
|
|
|
// Calculate position to put palette.
|
|
properties = Object.clone(PALETTE_ORIGIN_PROPERTIES);
|
|
properties.parentJointIndex = handJointIndex;
|
|
properties.localPosition = Vec3.sum(PALETTE_ORIGIN_POSITION, { x: paletteLateralOffset, y: 0, z: 0 });
|
|
paletteOriginOverlay = Overlays.addOverlay("sphere", properties);
|
|
|
|
// Header.
|
|
properties = Object.clone(PALETTE_HEADER_HEADING_PROPERTIES);
|
|
properties.parentID = paletteOriginOverlay;
|
|
paletteHeaderHeadingOverlay = Overlays.addOverlay("model", properties);
|
|
properties = Object.clone(PALETTE_HEADER_BAR_PROPERTIES);
|
|
properties.parentID = paletteOriginOverlay;
|
|
paletteHeaderBarOverlay = Overlays.addOverlay("model", properties);
|
|
properties = Object.clone(PALETTE_TITLE_PROPERTIES);
|
|
properties.parentID = paletteHeaderHeadingOverlay;
|
|
paletteTitleOverlay = Overlays.addOverlay("image3d", properties);
|
|
|
|
// Palette background.
|
|
properties = Object.clone(PALETTE_PANEL_PROPERTIES);
|
|
properties.parentID = paletteOriginOverlay;
|
|
palettePanelOverlay = Overlays.addOverlay("cube", properties);
|
|
|
|
// Palette items.
|
|
for (i = 0, length = PALETTE_ITEMS.length; i < length; i++) {
|
|
// Collision overlay.
|
|
properties = Object.clone(PALETTE_ITEM.properties);
|
|
properties.parentID = palettePanelOverlay;
|
|
properties.localPosition = itemPosition(i);
|
|
paletteItemOverlays[i] = Overlays.addOverlay(PALETTE_ITEM.overlay, properties);
|
|
paletteItemPositions[i] = properties.localPosition;
|
|
|
|
// Highlight overlay.
|
|
properties = Object.clone(PALETTE_ITEM.hoverButton.properties);
|
|
properties.parentID = paletteItemOverlays[i];
|
|
paletteItemHoverOverlays[i] = Overlays.addOverlay(PALETTE_ITEM.hoverButton.overlay, properties);
|
|
|
|
// Icon overlay.
|
|
properties = Object.clone(PALETTE_ITEM.icon.properties);
|
|
properties = Object.merge(properties, PALETTE_ITEMS[i].icon.properties);
|
|
properties.parentID = paletteItemHoverOverlays[i];
|
|
iconOverlays[i] = Overlays.addOverlay(PALETTE_ITEM.icon.overlay, properties);
|
|
}
|
|
|
|
// Always-visible overlays.
|
|
staticOverlays = [].concat(paletteHeaderHeadingOverlay, paletteHeaderBarOverlay, paletteTitleOverlay,
|
|
palettePanelOverlay, paletteItemOverlays, iconOverlays);
|
|
|
|
isDisplaying = true;
|
|
}
|
|
|
|
function clear() {
|
|
// Deletes menu entities.
|
|
if (!isDisplaying) {
|
|
return;
|
|
}
|
|
Overlays.deleteOverlay(paletteOriginOverlay); // Automatically deletes all other overlays because they're children.
|
|
paletteItemOverlays = [];
|
|
paletteItemHoverOverlays = [];
|
|
iconOverlays = [];
|
|
staticOverlays = [];
|
|
isDisplaying = false;
|
|
}
|
|
|
|
function destroy() {
|
|
clear();
|
|
}
|
|
|
|
return {
|
|
assetURLs: getAssetURLs,
|
|
setHand: setHand,
|
|
overlayIDs: getOverlayIDs,
|
|
setVisible: setVisible,
|
|
update: update,
|
|
display: display,
|
|
clear: clear,
|
|
destroy: destroy
|
|
};
|
|
};
|
|
|
|
CreatePalette.prototype = {};
|