mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 07:47:30 +02:00
BUGZ-657; BUGZ-713: Add a copy of the system library JS files because lots of system scripts depend on them being in the default scripts location
This commit is contained in:
parent
99feabd4c9
commit
7a4aa1e98c
25 changed files with 8352 additions and 0 deletions
12
scripts/simplifiedUI/system/libraries/EditEntityList.qml
Normal file
12
scripts/simplifiedUI/system/libraries/EditEntityList.qml
Normal file
|
@ -0,0 +1,12 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtWebChannel 1.0
|
||||
import QtGraphicalEffects 1.0
|
||||
import "qrc:///qml/controls" as HifiControls
|
||||
|
||||
HifiControls.WebView {
|
||||
id: entityListToolWebView
|
||||
url: Qt.resolvedUrl("../html/entityList.html")
|
||||
enabled: true
|
||||
blurOnCtrlShift: false
|
||||
}
|
85
scripts/simplifiedUI/system/libraries/ToolTip.js
Normal file
85
scripts/simplifiedUI/system/libraries/ToolTip.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// ToolTip.js
|
||||
// examples/libraries
|
||||
//
|
||||
// Copyright 2014 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
|
||||
//
|
||||
|
||||
|
||||
function Tooltip() {
|
||||
this.x = 285;
|
||||
this.y = 115;
|
||||
this.width = 500;
|
||||
this.height = 180; // 145;
|
||||
this.margin = 5;
|
||||
this.decimals = 3;
|
||||
|
||||
this.textOverlay = Overlays.addOverlay("text", {
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
margin: this.margin,
|
||||
text: "",
|
||||
color: { red: 128, green: 128, blue: 128 },
|
||||
alpha: 0.2,
|
||||
backgroundAlpha: 0.2,
|
||||
visible: false
|
||||
});
|
||||
this.show = function (doShow) {
|
||||
Overlays.editOverlay(this.textOverlay, { visible: doShow });
|
||||
}
|
||||
this.updateText = function(properties) {
|
||||
var angles = Quat.safeEulerAngles(properties.rotation);
|
||||
var text = "Entity Properties:\n"
|
||||
text += "type: " + properties.type + "\n"
|
||||
text += "X: " + properties.position.x.toFixed(this.decimals) + "\n"
|
||||
text += "Y: " + properties.position.y.toFixed(this.decimals) + "\n"
|
||||
text += "Z: " + properties.position.z.toFixed(this.decimals) + "\n"
|
||||
text += "Pitch: " + angles.x.toFixed(this.decimals) + "\n"
|
||||
text += "Yaw: " + angles.y.toFixed(this.decimals) + "\n"
|
||||
text += "Roll: " + angles.z.toFixed(this.decimals) + "\n"
|
||||
text += "Dimensions: " + properties.dimensions.x.toFixed(this.decimals) + ", "
|
||||
+ properties.dimensions.y.toFixed(this.decimals) + ", "
|
||||
+ properties.dimensions.z.toFixed(this.decimals) + "\n";
|
||||
|
||||
text += "Natural Dimensions: " + properties.naturalDimensions.x.toFixed(this.decimals) + ", "
|
||||
+ properties.naturalDimensions.y.toFixed(this.decimals) + ", "
|
||||
+ properties.naturalDimensions.z.toFixed(this.decimals) + "\n";
|
||||
|
||||
text += "ID: " + properties.id + "\n"
|
||||
if (properties.type == "Model") {
|
||||
text += "Model URL: " + properties.modelURL + "\n"
|
||||
text += "Shape Type: " + properties.shapeType + "\n"
|
||||
text += "Compound Shape URL: " + properties.compoundShapeURL + "\n"
|
||||
text += "Animation URL: " + properties.animationURL + "\n"
|
||||
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
|
||||
if (properties.sittingPoints && properties.sittingPoints.length > 0) {
|
||||
text += properties.sittingPoints.length + " Sitting points: "
|
||||
for (var i = 0; i < properties.sittingPoints.length; ++i) {
|
||||
text += properties.sittingPoints[i].name + " "
|
||||
}
|
||||
} else {
|
||||
text += "No sitting points" + "\n"
|
||||
}
|
||||
}
|
||||
if (properties.lifetime > -1) {
|
||||
text += "Lifetime: " + properties.lifetime + "\n"
|
||||
}
|
||||
text += "Age: " + properties.ageAsText + "\n"
|
||||
text += "Density: " + properties.density + "\n"
|
||||
text += "Script: " + properties.script + "\n"
|
||||
|
||||
|
||||
Overlays.editOverlay(this.textOverlay, { text: text });
|
||||
}
|
||||
|
||||
this.cleanup = function () {
|
||||
Overlays.deleteOverlay(this.textOverlay);
|
||||
}
|
||||
}
|
||||
|
||||
tooltip = new Tooltip();
|
87
scripts/simplifiedUI/system/libraries/Trigger.js
Normal file
87
scripts/simplifiedUI/system/libraries/Trigger.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
"use strict";
|
||||
|
||||
/*jslint vars: true, plusplus: true*/
|
||||
/*globals Script, Overlays, Controller, Reticle, HMD, Camera, Entities, MyAvatar, Settings, Menu, ScriptDiscoveryService, Window, Vec3, Quat, print*/
|
||||
|
||||
Trigger = function(properties) {
|
||||
properties = properties || {};
|
||||
var that = this;
|
||||
that.label = properties.label || Math.random();
|
||||
that.SMOOTH_RATIO = properties.smooth || 0.1; // Time averaging of trigger - 0.0 disables smoothing
|
||||
that.DEADZONE = properties.deadzone || 0.10; // Once pressed, a trigger must fall below the deadzone to be considered un-pressed once pressed.
|
||||
that.HYSTERESIS = properties.hystersis || 0.05; // If not pressed, a trigger must go above DEADZONE + HYSTERSIS to be considered pressed
|
||||
|
||||
that.value = 0;
|
||||
that.pressed = false;
|
||||
that.clicked = false;
|
||||
|
||||
// Handlers
|
||||
that.onPress = properties.onPress || function(){
|
||||
print("Pressed trigger " + that.label)
|
||||
};
|
||||
that.onRelease = properties.onRelease || function(){
|
||||
print("Released trigger " + that.label)
|
||||
};
|
||||
that.onClick = properties.onClick || function(){
|
||||
print("Clicked trigger " + that.label)
|
||||
};
|
||||
that.onUnclick = properties.onUnclick || function(){
|
||||
print("Unclicked trigger " + that.label)
|
||||
};
|
||||
|
||||
// Getters
|
||||
that.isPressed = function() {
|
||||
return that.pressed;
|
||||
}
|
||||
|
||||
that.isClicked = function() {
|
||||
return that.clicked;
|
||||
}
|
||||
|
||||
that.getValue = function() {
|
||||
return that.value;
|
||||
}
|
||||
|
||||
|
||||
// Private values
|
||||
var controller = properties.controller || Controller.Standard.LT;
|
||||
var controllerClick = properties.controllerClick || Controller.Standard.LTClick;
|
||||
that.mapping = Controller.newMapping('com.highfidelity.controller.trigger.' + controller + '-' + controllerClick + '.' + that.label + Math.random());
|
||||
Script.scriptEnding.connect(that.mapping.disable);
|
||||
|
||||
// Setup mapping,
|
||||
that.mapping.from(controller).peek().to(function(value) {
|
||||
that.value = (that.value * that.SMOOTH_RATIO) +
|
||||
(value * (1.0 - that.SMOOTH_RATIO));
|
||||
|
||||
var oldPressed = that.pressed;
|
||||
if (!that.pressed && that.value >= (that.DEADZONE + that.HYSTERESIS)) {
|
||||
that.pressed = true;
|
||||
that.onPress();
|
||||
}
|
||||
|
||||
if (that.pressed && that.value < that.HYSTERESIS) {
|
||||
that.pressed = false;
|
||||
that.onRelease();
|
||||
}
|
||||
});
|
||||
|
||||
that.mapping.from(controllerClick).peek().to(function(value){
|
||||
if (!that.clicked && value > 0.0) {
|
||||
that.clicked = true;
|
||||
that.onClick();
|
||||
}
|
||||
if (that.clicked && value == 0.0) {
|
||||
that.clicked = false;
|
||||
that.onUnclick();
|
||||
}
|
||||
});
|
||||
|
||||
that.enable = function() {
|
||||
that.mapping.enable();
|
||||
}
|
||||
|
||||
that.disable = function() {
|
||||
that.mapping.disable();
|
||||
}
|
||||
}
|
606
scripts/simplifiedUI/system/libraries/WebTablet.js
Normal file
606
scripts/simplifiedUI/system/libraries/WebTablet.js
Normal file
|
@ -0,0 +1,606 @@
|
|||
//
|
||||
// WebTablet.js
|
||||
//
|
||||
// Created by Anthony J. Thibault on 8/8/2016
|
||||
// Copyright 2016 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 getControllerWorldLocation, Tablet, WebTablet:true, HMD, Settings, Script,
|
||||
Vec3, Quat, MyAvatar, Entities, Overlays, Camera, Messages, Xform, clamp, Controller, Mat4, resizeTablet */
|
||||
|
||||
Script.include(Script.resolvePath("../libraries/utils.js"));
|
||||
Script.include(Script.resolvePath("../libraries/controllers.js"));
|
||||
Script.include(Script.resolvePath("../libraries/Xform.js"));
|
||||
|
||||
var Y_AXIS = {x: 0, y: 1, z: 0};
|
||||
var X_AXIS = {x: 1, y: 0, z: 0};
|
||||
var DEFAULT_DPI = 31;
|
||||
var DEFAULT_WIDTH = 0.4375;
|
||||
var DEFAULT_VERTICAL_FIELD_OF_VIEW = 45; // degrees
|
||||
var SENSOR_TO_ROOM_MATRIX = -2;
|
||||
var CAMERA_MATRIX = -7;
|
||||
var ROT_Y_180 = {x: 0.0, y: 1.0, z: 0, w: 0};
|
||||
var ROT_LANDSCAPE = {x: 1.0, y: 1.0, z: 0, w: 0};
|
||||
var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 };
|
||||
var INCHES_TO_METERS = 1 / 39.3701;
|
||||
|
||||
var NO_HANDS = -1;
|
||||
var DELAY_FOR_30HZ = 33; // milliseconds
|
||||
|
||||
var TABLET_MATERIAL_ENTITY_NAME = 'Tablet-Material-Entity';
|
||||
|
||||
|
||||
// will need to be recaclulated if dimensions of fbx model change.
|
||||
var TABLET_NATURAL_DIMENSIONS = {x: 32.083, y: 48.553, z: 2.269};
|
||||
|
||||
var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "images/button-close.png";
|
||||
// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png";
|
||||
// var TABLET_MODEL_PATH = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx";
|
||||
|
||||
var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-button-small-bezel.fbx";
|
||||
var HIGH_PRIORITY = 1;
|
||||
var LOW_PRIORITY = 0;
|
||||
var SUBMESH = 2;
|
||||
|
||||
// returns object with two fields:
|
||||
// * position - position in front of the user
|
||||
// * rotation - rotation of entity so it faces the user.
|
||||
function calcSpawnInfo(hand, landscape) {
|
||||
var finalPosition;
|
||||
|
||||
var LEFT_HAND = Controller.Standard.LeftHand;
|
||||
var sensorToWorldScale = MyAvatar.sensorToWorldScale;
|
||||
var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
|
||||
var headRot = Quat.cancelOutRollAndPitch((HMD.active && Camera.mode === "first person") ?
|
||||
HMD.orientation : Camera.orientation);
|
||||
|
||||
var right = Quat.getRight(headRot);
|
||||
var forward = Quat.getForward(headRot);
|
||||
var up = Quat.getUp(headRot);
|
||||
|
||||
var FORWARD_OFFSET = 0.5 * sensorToWorldScale;
|
||||
var UP_OFFSET = -0.16 * sensorToWorldScale;
|
||||
var RIGHT_OFFSET = ((hand === LEFT_HAND) ? -0.18 : 0.18) * sensorToWorldScale;
|
||||
|
||||
var forwardPosition = Vec3.sum(headPos, Vec3.multiply(FORWARD_OFFSET, forward));
|
||||
var lateralPosition = Vec3.sum(forwardPosition, Vec3.multiply(RIGHT_OFFSET, right));
|
||||
finalPosition = Vec3.sum(lateralPosition, Vec3.multiply(UP_OFFSET, up));
|
||||
|
||||
var MY_EYES = { x: 0.0, y: 0.15, z: 0.0 };
|
||||
var lookAtEndPosition = Vec3.sum(Vec3.multiply(RIGHT_OFFSET, right), Vec3.multiply(FORWARD_OFFSET, forward));
|
||||
var orientation = Quat.lookAt(MY_EYES, lookAtEndPosition, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y));
|
||||
|
||||
return {
|
||||
position: finalPosition,
|
||||
rotation: landscape ? Quat.multiply(orientation, ROT_LANDSCAPE) : Quat.multiply(orientation, ROT_Y_180)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
cleanUpOldMaterialEntities = function() {
|
||||
var avatarEntityData = MyAvatar.getAvatarEntityData();
|
||||
for (var entityID in avatarEntityData) {
|
||||
var entityName = Entities.getEntityProperties(entityID, ["name"]).name;
|
||||
|
||||
if (entityName === TABLET_MATERIAL_ENTITY_NAME) {
|
||||
Entities.deleteEntity(entityID);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* WebTablet
|
||||
* @param url [string] url of content to show on the tablet.
|
||||
* @param width [number] width in meters of the tablet model
|
||||
* @param dpi [number] dpi of web surface used to show the content.
|
||||
* @param hand [number] -1 indicates no hand, Controller.Standard.RightHand or Controller.Standard.LeftHand
|
||||
*/
|
||||
WebTablet = function (url, width, dpi, hand, location, visible) {
|
||||
|
||||
var _this = this;
|
||||
|
||||
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
|
||||
|
||||
// scale factor of natural tablet dimensions.
|
||||
var tabletWidth = (width || DEFAULT_WIDTH) * sensorScaleFactor;
|
||||
var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x;
|
||||
var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor;
|
||||
var tabletDepth = TABLET_NATURAL_DIMENSIONS.z * tabletScaleFactor;
|
||||
this.landscape = false;
|
||||
|
||||
visible = visible === true;
|
||||
|
||||
var tabletDpi;
|
||||
if (dpi) {
|
||||
tabletDpi = dpi;
|
||||
} else {
|
||||
tabletDpi = DEFAULT_DPI * (DEFAULT_WIDTH / tabletWidth);
|
||||
}
|
||||
|
||||
var modelURL = LOCAL_TABLET_MODEL_PATH;
|
||||
var tabletProperties = {
|
||||
name: "WebTablet Tablet",
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
url: modelURL, // for overlay
|
||||
grabbable: true, // for overlay
|
||||
loadPriority: 10.0, // for overlay
|
||||
grab: { grabbable: true },
|
||||
dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth },
|
||||
parentID: MyAvatar.SELF_ID,
|
||||
visible: visible,
|
||||
isGroupCulled: true
|
||||
};
|
||||
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
this.calculateTabletAttachmentProperties(hand, true, tabletProperties);
|
||||
if (location) {
|
||||
tabletProperties.localPosition = location.localPosition;
|
||||
tabletProperties.localRotation = location.localRotation;
|
||||
}
|
||||
|
||||
this.cleanUpOldTablets();
|
||||
cleanUpOldMaterialEntities();
|
||||
|
||||
this.tabletEntityID = Overlays.addOverlay("model", tabletProperties);
|
||||
|
||||
if (this.webOverlayID) {
|
||||
Overlays.deleteOverlay(this.webOverlayID);
|
||||
}
|
||||
|
||||
var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.5) * sensorScaleFactor;
|
||||
var WEB_ENTITY_Y_OFFSET = 1.25 * tabletScaleFactor;
|
||||
var screenWidth = 0.9367 * tabletWidth;
|
||||
var screenHeight = 0.9000 * tabletHeight;
|
||||
this.webOverlayID = Overlays.addOverlay("web3d", {
|
||||
name: "WebTablet Web",
|
||||
url: url,
|
||||
localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET },
|
||||
localRotation: Quat.angleAxis(180, Y_AXIS),
|
||||
dimensions: {x: screenWidth, y: screenHeight, z: 1.0},
|
||||
dpi: tabletDpi,
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
alpha: 1.0,
|
||||
parentID: this.tabletEntityID,
|
||||
parentJointIndex: -1,
|
||||
showKeyboardFocusHighlight: false,
|
||||
visible: visible
|
||||
});
|
||||
|
||||
var homeButtonDim = 4.0 * tabletScaleFactor / 1.5;
|
||||
var HOME_BUTTON_X_OFFSET = 0.00079 * sensorScaleFactor;
|
||||
var HOME_BUTTON_Y_OFFSET = -1 * ((tabletHeight / 2) - (4.0 * tabletScaleFactor / 2));
|
||||
var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleFactor;
|
||||
this.homeButtonID = Overlays.addOverlay("circle3d", {
|
||||
name: "homeButton",
|
||||
localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
|
||||
localRotation: Quat.fromVec3Degrees({ x: 180, y: 180, z: 0}),
|
||||
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim },
|
||||
solid: true,
|
||||
alpha: 0.0,
|
||||
visible: visible,
|
||||
drawInFront: false,
|
||||
parentID: this.tabletEntityID,
|
||||
parentJointIndex: -1
|
||||
});
|
||||
|
||||
this.homeButtonHighlightID = Overlays.addOverlay("circle3d", {
|
||||
name: "homeButtonHighlight",
|
||||
localPosition: { x: -HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
|
||||
localRotation: Quat.fromVec3Degrees({ x: 180, y: 180, z: 0}),
|
||||
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim },
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
solid: true,
|
||||
innerRadius: 0.9,
|
||||
ignorePickIntersection: true,
|
||||
alpha: 0.0,
|
||||
visible: visible,
|
||||
drawInFront: false,
|
||||
parentID: this.tabletEntityID,
|
||||
parentJointIndex: -1
|
||||
});
|
||||
|
||||
this.receive = function (channel, senderID, senderUUID, localOnly) {
|
||||
if (_this.homeButtonID === senderID) {
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var onHomeScreen = tablet.onHomeScreen();
|
||||
var isMessageOpen;
|
||||
if (onHomeScreen) {
|
||||
isMessageOpen = tablet.isMessageDialogOpen();
|
||||
if (isMessageOpen === false) {
|
||||
HMD.closeTablet();
|
||||
}
|
||||
} else {
|
||||
isMessageOpen = tablet.isMessageDialogOpen();
|
||||
if (isMessageOpen === false) {
|
||||
tablet.gotoHomeScreen();
|
||||
_this.setHomeButtonTexture();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.state = "idle";
|
||||
|
||||
this.getRoot = function() {
|
||||
return Entities.getWebViewRoot(_this.tabletEntityID);
|
||||
};
|
||||
|
||||
this.clicked = false;
|
||||
|
||||
this.myOnHmdChanged = function () {
|
||||
_this.onHmdChanged();
|
||||
};
|
||||
HMD.displayModeChanged.connect(this.myOnHmdChanged);
|
||||
|
||||
this.myMousePressEvent = function (event) {
|
||||
_this.mousePressEvent(event);
|
||||
};
|
||||
|
||||
this.myMouseMoveEvent = function (event) {
|
||||
_this.mouseMoveEvent(event);
|
||||
};
|
||||
|
||||
this.myMouseReleaseEvent = function (event) {
|
||||
_this.mouseReleaseEvent(event);
|
||||
};
|
||||
|
||||
Controller.mousePressEvent.connect(this.myMousePressEvent);
|
||||
Controller.mouseMoveEvent.connect(this.myMouseMoveEvent);
|
||||
Controller.mouseReleaseEvent.connect(this.myMouseReleaseEvent);
|
||||
|
||||
this.dragging = false;
|
||||
this.initialLocalIntersectionPoint = {x: 0, y: 0, z: 0};
|
||||
this.initialLocalPosition = {x: 0, y: 0, z: 0};
|
||||
|
||||
this.myGeometryChanged = function (geometry) {
|
||||
_this.geometryChanged(geometry);
|
||||
};
|
||||
Window.geometryChanged.connect(this.myGeometryChanged);
|
||||
|
||||
this.myCameraModeChanged = function(newMode) {
|
||||
_this.cameraModeChanged(newMode);
|
||||
};
|
||||
Camera.modeUpdated.connect(this.myCameraModeChanged);
|
||||
};
|
||||
|
||||
WebTablet.prototype.getTabletTextureResolution = function() {
|
||||
if (this.landscape) {
|
||||
return { x: TABLET_TEXTURE_RESOLUTION.y , y: TABLET_TEXTURE_RESOLUTION.x };
|
||||
} else {
|
||||
return TABLET_TEXTURE_RESOLUTION;
|
||||
}
|
||||
};
|
||||
|
||||
WebTablet.prototype.getLandscape = function() {
|
||||
return this.landscape;
|
||||
}
|
||||
|
||||
WebTablet.prototype.setLandscape = function(newLandscapeValue) {
|
||||
if (this.landscape === newLandscapeValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.landscape = newLandscapeValue;
|
||||
var cameraOrientation = Quat.cancelOutRollAndPitch(Camera.orientation);
|
||||
var tabletRotation = Quat.multiply(cameraOrientation, this.landscape ? ROT_LANDSCAPE : ROT_Y_180);
|
||||
Overlays.editOverlay(this.tabletEntityID, {
|
||||
rotation: tabletRotation
|
||||
});
|
||||
|
||||
var tabletWidth = getTabletWidthFromSettings() * MyAvatar.sensorToWorldScale;
|
||||
var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x;
|
||||
var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor;
|
||||
var screenWidth = 0.9275 * tabletWidth;
|
||||
var screenHeight = 0.8983 * tabletHeight;
|
||||
var screenRotation = Quat.angleAxis(180, Vec3.UP);
|
||||
Overlays.editOverlay(this.webOverlayID, {
|
||||
localRotation: this.landscape ? Quat.multiply(screenRotation, Quat.angleAxis(-90, Vec3.FRONT)) : screenRotation,
|
||||
dimensions: {x: this.landscape ? screenHeight : screenWidth, y: this.landscape ? screenWidth : screenHeight, z: 0.1}
|
||||
});
|
||||
};
|
||||
|
||||
WebTablet.prototype.getLocation = function() {
|
||||
var location = Overlays.getProperty(this.tabletEntityID, "localPosition");
|
||||
var orientation = Overlays.getProperty(this.tabletEntityID, "localOrientation");
|
||||
return {
|
||||
localPosition: location,
|
||||
localRotation: orientation
|
||||
};
|
||||
};
|
||||
|
||||
WebTablet.prototype.setHomeButtonTexture = function() {
|
||||
// TODO - is this still needed?
|
||||
// Entities.editEntity(this.tabletEntityID, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})});
|
||||
};
|
||||
|
||||
WebTablet.prototype.setURL = function (url) {
|
||||
Overlays.editOverlay(this.webOverlayID, { url: url });
|
||||
};
|
||||
|
||||
WebTablet.prototype.setScriptURL = function (scriptURL) {
|
||||
Overlays.editOverlay(this.webOverlayID, { scriptURL: scriptURL });
|
||||
};
|
||||
|
||||
WebTablet.prototype.getOverlayObject = function () {
|
||||
return Overlays.getOverlayObject(this.webOverlayID);
|
||||
};
|
||||
|
||||
WebTablet.prototype.setWidth = function (width) {
|
||||
// imported from libraries/utils.js
|
||||
resizeTablet(width);
|
||||
};
|
||||
|
||||
WebTablet.prototype.destroy = function () {
|
||||
Overlays.deleteOverlay(this.webOverlayID);
|
||||
Overlays.deleteOverlay(this.tabletEntityID);
|
||||
Overlays.deleteOverlay(this.homeButtonID);
|
||||
Overlays.deleteOverlay(this.homeButtonHighlightID);
|
||||
HMD.displayModeChanged.disconnect(this.myOnHmdChanged);
|
||||
|
||||
Controller.mousePressEvent.disconnect(this.myMousePressEvent);
|
||||
Controller.mouseMoveEvent.disconnect(this.myMouseMoveEvent);
|
||||
Controller.mouseReleaseEvent.disconnect(this.myMouseReleaseEvent);
|
||||
|
||||
Window.geometryChanged.disconnect(this.myGeometryChanged);
|
||||
Camera.modeUpdated.disconnect(this.myCameraModeChanged);
|
||||
};
|
||||
|
||||
WebTablet.prototype.geometryChanged = function (geometry) {
|
||||
if (!HMD.active && HMD.tabletID) {
|
||||
var tabletProperties = {};
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties);
|
||||
Overlays.editOverlay(HMD.tabletID, tabletProperties);
|
||||
}
|
||||
};
|
||||
|
||||
function gluPerspective(fovy, aspect, zNear, zFar) {
|
||||
var cotan = 1 / Math.tan(fovy / 2);
|
||||
var alpha = -(zFar + zNear) / (zFar - zNear);
|
||||
var beta = -(2 * zFar * zNear) / (zFar - zNear);
|
||||
var col0 = {x: cotan / aspect, y: 0, z: 0, w: 0};
|
||||
var col1 = {x: 0, y: cotan, z: 0, w: 0};
|
||||
var col2 = {x: 0, y: 0, z: alpha, w: -1};
|
||||
var col3 = {x: 0, y: 0, z: beta, w: 0};
|
||||
return Mat4.createFromColumns(col0, col1, col2, col3);
|
||||
}
|
||||
|
||||
// calclulate the appropriate position of the tablet in world space, such that it fits in the center of the screen.
|
||||
// with a bit of padding on the top and bottom.
|
||||
// windowPos is used to position the center of the tablet at the given position.
|
||||
WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function (windowPos) {
|
||||
|
||||
var DEFAULT_DESKTOP_TABLET_SCALE = 75;
|
||||
var DESKTOP_TABLET_SCALE = Settings.getValue("desktopTabletScale") || DEFAULT_DESKTOP_TABLET_SCALE;
|
||||
|
||||
// clamp window pos so 2d tablet is not off-screen.
|
||||
var TABLET_TEXEL_PADDING = {x: 60, y: 90};
|
||||
var X_CLAMP = (DESKTOP_TABLET_SCALE / 100) * ((this.getTabletTextureResolution().x / 2) + TABLET_TEXEL_PADDING.x);
|
||||
var Y_CLAMP = (DESKTOP_TABLET_SCALE / 100) * ((this.getTabletTextureResolution().y / 2) + TABLET_TEXEL_PADDING.y);
|
||||
windowPos.x = clamp(windowPos.x, X_CLAMP, Window.innerWidth - X_CLAMP);
|
||||
windowPos.y = clamp(windowPos.y, Y_CLAMP, Window.innerHeight - Y_CLAMP);
|
||||
|
||||
var fov = (Settings.getValue('fieldOfView') || DEFAULT_VERTICAL_FIELD_OF_VIEW) * (Math.PI / 180);
|
||||
|
||||
// scale factor of natural tablet dimensions.
|
||||
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
|
||||
var tabletWidth = getTabletWidthFromSettings() * sensorScaleFactor;
|
||||
var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x;
|
||||
var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor;
|
||||
var tabletDepth = TABLET_NATURAL_DIMENSIONS.z * tabletScaleFactor;
|
||||
var tabletDpi = DEFAULT_DPI * (DEFAULT_WIDTH / tabletWidth);
|
||||
|
||||
var MAX_PADDING_FACTOR = 2.2;
|
||||
var PADDING_FACTOR = Math.min(Window.innerHeight / this.getTabletTextureResolution().y, MAX_PADDING_FACTOR);
|
||||
var TABLET_HEIGHT = (this.getTabletTextureResolution().y / tabletDpi) * INCHES_TO_METERS;
|
||||
var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2);
|
||||
|
||||
// calcualte distance from camera
|
||||
var dist = (PADDING_FACTOR * TABLET_HEIGHT) / (2 * Math.tan(fov / 2) * (DESKTOP_TABLET_SCALE / 100)) - WEB_ENTITY_Z_OFFSET;
|
||||
|
||||
var Z_NEAR = 0.01;
|
||||
var Z_FAR = 100.0;
|
||||
|
||||
// calculate mouse position in clip space
|
||||
var alpha = -(Z_FAR + Z_NEAR) / (Z_FAR - Z_NEAR);
|
||||
var beta = -(2 * Z_FAR * Z_NEAR) / (Z_FAR - Z_NEAR);
|
||||
var clipZ = (beta / dist) - alpha;
|
||||
var clipMousePosition = {x: (2 * windowPos.x / Window.innerWidth) - 1,
|
||||
y: (2 * ((Window.innerHeight - windowPos.y) / Window.innerHeight)) - 1,
|
||||
z: clipZ};
|
||||
|
||||
// calculate projection matrix
|
||||
var aspect = Window.innerWidth / Window.innerHeight;
|
||||
var projMatrix = gluPerspective(fov, aspect, Z_NEAR, Z_FAR);
|
||||
|
||||
// transform mouse clip position into view coordinates.
|
||||
var viewMousePosition = Mat4.transformPoint(Mat4.inverse(projMatrix), clipMousePosition);
|
||||
|
||||
// transform view mouse position into world coordinates.
|
||||
var viewToWorldMatrix = Mat4.createFromRotAndTrans(Camera.orientation, Camera.position);
|
||||
var worldMousePosition = Mat4.transformPoint(viewToWorldMatrix, viewMousePosition);
|
||||
|
||||
return {
|
||||
position: worldMousePosition,
|
||||
rotation: this.landscape ? Quat.multiply(Camera.orientation, ROT_LANDSCAPE) : Quat.multiply(Camera.orientation, ROT_Y_180)
|
||||
};
|
||||
};
|
||||
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMouse, tabletProperties) {
|
||||
if (HMD.active) {
|
||||
// in HMD mode, the tablet should be relative to the sensor to world matrix.
|
||||
tabletProperties.parentJointIndex = SENSOR_TO_ROOM_MATRIX;
|
||||
|
||||
// compute the appropriate position of the tablet, near the hand controller that was used to spawn it.
|
||||
var spawnInfo = calcSpawnInfo(hand, this.landscape);
|
||||
tabletProperties.position = spawnInfo.position;
|
||||
tabletProperties.rotation = spawnInfo.rotation;
|
||||
} else {
|
||||
// in desktop mode, the tablet should be relative to the camera
|
||||
tabletProperties.parentJointIndex = CAMERA_MATRIX;
|
||||
|
||||
var windowPos;
|
||||
if (useMouse) {
|
||||
// compute the appropriate postion of the tablet such that it fits in the center of the screen nicely.
|
||||
windowPos = {x: Controller.getValue(Controller.Hardware.Keyboard.MouseX),
|
||||
y: Controller.getValue(Controller.Hardware.Keyboard.MouseY)};
|
||||
} else {
|
||||
windowPos = {x: Window.innerWidth / 2,
|
||||
y: Window.innerHeight / 2};
|
||||
}
|
||||
var attitude = this.calculateWorldAttitudeRelativeToCamera(windowPos);
|
||||
tabletProperties.position = attitude.position;
|
||||
tabletProperties.rotation = attitude.rotation;
|
||||
}
|
||||
};
|
||||
|
||||
WebTablet.prototype.onHmdChanged = function () {
|
||||
if (!HMD.tabletID) {
|
||||
return;
|
||||
}
|
||||
var tabletProperties = {};
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties);
|
||||
Overlays.editOverlay(HMD.tabletID, tabletProperties);
|
||||
};
|
||||
|
||||
WebTablet.prototype.pickle = function () {
|
||||
return JSON.stringify({ webOverlayID: this.webOverlayID, tabletEntityID: this.tabletEntityID });
|
||||
};
|
||||
|
||||
WebTablet.prototype.register = function() {
|
||||
Messages.subscribe("home");
|
||||
Messages.messageReceived.connect(this.receive);
|
||||
};
|
||||
|
||||
WebTablet.prototype.cleanUpOldTabletsOnJoint = function(jointIndex) {
|
||||
var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, jointIndex);
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.SELF_ID, jointIndex));
|
||||
children.forEach(function(childID) {
|
||||
var props = Entities.getEntityProperties(childID, ["name"]);
|
||||
if (props.name === "WebTablet Tablet") {
|
||||
Entities.deleteEntity(childID);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WebTablet.prototype.cleanUpOldTablets = function() {
|
||||
this.cleanUpOldTabletsOnJoint(-1);
|
||||
this.cleanUpOldTabletsOnJoint(SENSOR_TO_ROOM_MATRIX);
|
||||
this.cleanUpOldTabletsOnJoint(CAMERA_MATRIX);
|
||||
this.cleanUpOldTabletsOnJoint(65529);
|
||||
this.cleanUpOldTabletsOnJoint(65534);
|
||||
};
|
||||
|
||||
WebTablet.prototype.unregister = function() {
|
||||
Messages.unsubscribe("home");
|
||||
Messages.messageReceived.disconnect(this.receive);
|
||||
};
|
||||
|
||||
WebTablet.unpickle = function (string) {
|
||||
if (!string) {
|
||||
return;
|
||||
}
|
||||
var tablet = JSON.parse(string);
|
||||
tablet.__proto__ = WebTablet.prototype;
|
||||
return tablet;
|
||||
};
|
||||
|
||||
WebTablet.prototype.getPosition = function () {
|
||||
return Overlays.getProperty(this.webOverlayID, "position");
|
||||
};
|
||||
|
||||
WebTablet.prototype.mousePressEvent = function (event) {
|
||||
if (!HMD.active) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var tabletBackPickResults = Overlays.findRayIntersection(pickRay, true, [this.tabletEntityID]);
|
||||
if (tabletBackPickResults.intersects) {
|
||||
var overlayPickResults = Overlays.findRayIntersection(pickRay, true, [this.webOverlayID, this.homeButtonID]);
|
||||
if (!overlayPickResults.intersects) {
|
||||
this.dragging = true;
|
||||
var invCameraXform = new Xform(Camera.orientation, Camera.position).inv();
|
||||
this.initialLocalIntersectionPoint = invCameraXform.xformPoint(tabletBackPickResults.intersection);
|
||||
this.initialLocalPosition = Overlays.getProperty(this.tabletEntityID, "localPosition");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WebTablet.prototype.cameraModeChanged = function (newMode) {
|
||||
;
|
||||
};
|
||||
|
||||
function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) {
|
||||
var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal);
|
||||
if (rayDirectionDotPlaneNormal > 0.00001 || rayDirectionDotPlaneNormal < -0.00001) {
|
||||
var rayStartDotPlaneNormal = Vec3.dot(Vec3.subtract(planePosition, rayStart), planeNormal);
|
||||
var distance = rayStartDotPlaneNormal / rayDirectionDotPlaneNormal;
|
||||
return {hit: true, distance: distance};
|
||||
} else {
|
||||
// ray is parallel to the plane
|
||||
return {hit: false, distance: 0};
|
||||
}
|
||||
}
|
||||
|
||||
WebTablet.prototype.scheduleMouseMoveProcessor = function() {
|
||||
var _this = this;
|
||||
if (!this.moveEventTimer) {
|
||||
this.moveEventTimer = Script.setTimeout(function() {
|
||||
_this.mouseMoveProcessor();
|
||||
}, DELAY_FOR_30HZ);
|
||||
}
|
||||
};
|
||||
|
||||
WebTablet.prototype.handleHomeButtonHover = function(x, y) {
|
||||
var pickRay = Camera.computePickRay(x, y);
|
||||
var homePickResult = Overlays.findRayIntersection(pickRay, true, [this.homeButtonID]);
|
||||
Overlays.editOverlay(this.homeButtonHighlightID, { alpha: homePickResult.intersects ? 1.0 : 0.0 });
|
||||
};
|
||||
|
||||
WebTablet.prototype.mouseMoveEvent = function (event) {
|
||||
if (this.dragging) {
|
||||
this.currentMouse = {
|
||||
x: event.x,
|
||||
y: event.y
|
||||
};
|
||||
this.scheduleMouseMoveProcessor();
|
||||
} else {
|
||||
this.handleHomeButtonHover(event.x, event.y);
|
||||
}
|
||||
};
|
||||
|
||||
WebTablet.prototype.mouseMoveProcessor = function () {
|
||||
this.moveEventTimer = null;
|
||||
if (this.dragging) {
|
||||
var pickRay = Camera.computePickRay(this.currentMouse.x, this.currentMouse.y);
|
||||
|
||||
// transform pickRay into camera local coordinates
|
||||
var invCameraXform = new Xform(Camera.orientation, Camera.position).inv();
|
||||
var localPickRay = {
|
||||
origin: invCameraXform.xformPoint(pickRay.origin),
|
||||
direction: invCameraXform.xformVector(pickRay.direction)
|
||||
};
|
||||
|
||||
var NORMAL = {x: 0, y: 0, z: -1};
|
||||
var result = rayIntersectPlane(this.initialLocalIntersectionPoint, NORMAL, localPickRay.origin, localPickRay.direction);
|
||||
if (result.hit) {
|
||||
var localIntersectionPoint = Vec3.sum(localPickRay.origin, Vec3.multiply(localPickRay.direction, result.distance));
|
||||
var localOffset = Vec3.subtract(localIntersectionPoint, this.initialLocalIntersectionPoint);
|
||||
var localPosition = Vec3.sum(this.initialLocalPosition, localOffset);
|
||||
Overlays.editOverlay(this.tabletEntityID, {
|
||||
localPosition: localPosition
|
||||
});
|
||||
}
|
||||
this.scheduleMouseMoveProcessor();
|
||||
} else {
|
||||
this.handleHomeButtonHover(this.currentMouse.x, this.currentMouse.y);
|
||||
}
|
||||
};
|
||||
|
||||
WebTablet.prototype.mouseReleaseEvent = function (event) {
|
||||
this.dragging = false;
|
||||
};
|
48
scripts/simplifiedUI/system/libraries/Xform.js
Normal file
48
scripts/simplifiedUI/system/libraries/Xform.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// Created by Anthony J. Thibault on 2016/06/21
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
// ctor
|
||||
Xform = function(rot, pos) {
|
||||
this.rot = rot;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
Xform.ident = function() {
|
||||
return new Xform({x: 0, y: 0, z: 0, w: 1}, {x: 0, y: 0, z: 0});
|
||||
};
|
||||
|
||||
Xform.mul = function(lhs, rhs) {
|
||||
var rot = Quat.multiply(lhs.rot, rhs.rot);
|
||||
var pos = Vec3.sum(lhs.pos, Vec3.multiplyQbyV(lhs.rot, rhs.pos));
|
||||
return new Xform(rot, pos);
|
||||
};
|
||||
|
||||
Xform.prototype.inv = function() {
|
||||
var invRot = Quat.inverse(this.rot);
|
||||
var invPos = Vec3.multiply(-1, this.pos);
|
||||
return new Xform(invRot, Vec3.multiplyQbyV(invRot, invPos));
|
||||
};
|
||||
|
||||
Xform.prototype.mirrorX = function() {
|
||||
return new Xform({x: this.rot.x, y: -this.rot.y, z: -this.rot.z, w: this.rot.w},
|
||||
{x: -this.pos.x, y: this.pos.y, z: this.pos.z});
|
||||
};
|
||||
|
||||
Xform.prototype.xformVector = function (vector) {
|
||||
return Vec3.multiplyQbyV(this.rot, vector);
|
||||
}
|
||||
|
||||
Xform.prototype.xformPoint = function (point) {
|
||||
return Vec3.sum(Vec3.multiplyQbyV(this.rot, point), this.pos);
|
||||
}
|
||||
|
||||
Xform.prototype.toString = function() {
|
||||
var rot = this.rot;
|
||||
var pos = this.pos;
|
||||
return "Xform rot = (" + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "), pos = (" + pos.x + ", " + pos.y + ", " + pos.z + ")";
|
||||
};
|
10
scripts/simplifiedUI/system/libraries/accountUtils.js
Normal file
10
scripts/simplifiedUI/system/libraries/accountUtils.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
//
|
||||
// accountUtils.js
|
||||
// scripts/system/libraries/libraries
|
||||
//
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
|
||||
openLoginWindow = function openLoginWindow() {
|
||||
Menu.triggerOption("Login/Sign Up");
|
||||
};
|
57
scripts/simplifiedUI/system/libraries/cloneEntityUtils.js
Normal file
57
scripts/simplifiedUI/system/libraries/cloneEntityUtils.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
"use strict";
|
||||
|
||||
// cloneEntity.js
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
/* global entityIsCloneable:true, cloneEntity:true, propsAreCloneDynamic:true, Script,
|
||||
propsAreCloneDynamic:true, Entities, Uuid */
|
||||
|
||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||
|
||||
// Object assign polyfill
|
||||
if (typeof Object.assign !== 'function') {
|
||||
Object.assign = function(target, varArgs) {
|
||||
if (target === null) {
|
||||
throw new TypeError('Cannot convert undefined or null to object');
|
||||
}
|
||||
var to = Object(target);
|
||||
for (var index = 1; index < arguments.length; index++) {
|
||||
var nextSource = arguments[index];
|
||||
if (nextSource !== null) {
|
||||
for (var nextKey in nextSource) {
|
||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||
to[nextKey] = nextSource[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
};
|
||||
}
|
||||
|
||||
entityIsCloneable = function(props) {
|
||||
if (props) {
|
||||
return props.cloneable;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
propsAreCloneDynamic = function(props) {
|
||||
var cloneable = entityIsCloneable(props);
|
||||
if (cloneable) {
|
||||
return props.cloneDynamic;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
cloneEntity = function(props) {
|
||||
var entityIDToClone = props.id;
|
||||
if (entityIsCloneable(props) &&
|
||||
(Uuid.isNull(props.certificateID) || props.certificateType.indexOf('domainUnlimited') >= 0)) {
|
||||
var cloneID = Entities.cloneEntity(entityIDToClone);
|
||||
return cloneID;
|
||||
}
|
||||
return null;
|
||||
};
|
94
scripts/simplifiedUI/system/libraries/connectionUtils.js
Normal file
94
scripts/simplifiedUI/system/libraries/connectionUtils.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// connectionUtils.js
|
||||
// scripts/system/libraries
|
||||
//
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
|
||||
// Function Names:
|
||||
// - requestJSON
|
||||
// - getAvailableConnections
|
||||
// - getInfoAboutUser
|
||||
// - getConnectionData
|
||||
//
|
||||
// Description:
|
||||
// - Update all the usernames that I am entitled to see, using my login but not dependent on canKick.
|
||||
var request = Script.require('request').request;
|
||||
var METAVERSE_BASE = Account.metaverseServerURL;
|
||||
function requestJSON(url, callback) { // callback(data) if successfull. Logs otherwise.
|
||||
request({
|
||||
uri: url
|
||||
}, function (error, response) {
|
||||
if (error || (response.status !== 'success')) {
|
||||
print("Error: unable to get URL", error || response.status);
|
||||
return;
|
||||
}
|
||||
callback(response.data);
|
||||
});
|
||||
}
|
||||
function getAvailableConnections(domain, callback) { // callback([{usename, location}...]) if successful. (Logs otherwise)
|
||||
url = METAVERSE_BASE + '/api/v1/users?per_page=400&'
|
||||
if (domain) {
|
||||
url += 'status=' + domain.slice(1, -1); // without curly braces
|
||||
} else {
|
||||
url += 'filter=connections'; // regardless of whether online
|
||||
}
|
||||
requestJSON(url, function (connectionsData) {
|
||||
callback(connectionsData.users);
|
||||
});
|
||||
}
|
||||
function getInfoAboutUser(specificUsername, callback) {
|
||||
url = METAVERSE_BASE + '/api/v1/users?filter=connections'
|
||||
requestJSON(url, function (connectionsData) {
|
||||
for (user in connectionsData.users) {
|
||||
if (connectionsData.users[user].username === specificUsername) {
|
||||
callback(connectionsData.users[user]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback(false);
|
||||
});
|
||||
}
|
||||
getConnectionData = function getConnectionData(specificUsername, domain) {
|
||||
function frob(user) { // get into the right format
|
||||
var formattedSessionId = user.location.node_id || '';
|
||||
if (formattedSessionId !== '' && formattedSessionId.indexOf("{") != 0) {
|
||||
formattedSessionId = "{" + formattedSessionId + "}";
|
||||
}
|
||||
return {
|
||||
sessionId: formattedSessionId,
|
||||
userName: user.username,
|
||||
connection: user.connection,
|
||||
profileUrl: user.images.thumbnail,
|
||||
placeName: (user.location.root || user.location.domain || {}).name || ''
|
||||
};
|
||||
}
|
||||
if (specificUsername) {
|
||||
getInfoAboutUser(specificUsername, function (user) {
|
||||
if (user) {
|
||||
updateUser(frob(user));
|
||||
} else {
|
||||
print('Error: Unable to find information about ' + specificUsername + ' in connectionsData!');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
getAvailableConnections(domain, function (users) {
|
||||
if (domain) {
|
||||
users.forEach(function (user) {
|
||||
updateUser(frob(user));
|
||||
});
|
||||
} else {
|
||||
sendToQml({ method: 'updateConnections', connections: users.map(frob) });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Function Name: sendToQml()
|
||||
//
|
||||
// Description:
|
||||
// -Use this function to send a message to the QML (i.e. to change appearances). The "message" argument is what is sent to
|
||||
// the QML in the format "{method, params}", like json-rpc. See also fromQml().
|
||||
function sendToQml(message) {
|
||||
Tablet.getTablet("com.highfidelity.interface.tablet.system").sendToQml(message);
|
||||
}
|
|
@ -0,0 +1,629 @@
|
|||
"use strict";
|
||||
|
||||
// controllerDispatcherUtils.js
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
/* global module, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4,
|
||||
Selection, Uuid,
|
||||
MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true,
|
||||
HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true,
|
||||
DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true,
|
||||
TRIGGER_OFF_VALUE:true,
|
||||
TRIGGER_ON_VALUE:true,
|
||||
PICK_MAX_DISTANCE:true,
|
||||
DEFAULT_SEARCH_SPHERE_DISTANCE:true,
|
||||
NEAR_GRAB_PICK_RADIUS:true,
|
||||
COLORS_GRAB_SEARCHING_HALF_SQUEEZE:true,
|
||||
COLORS_GRAB_SEARCHING_FULL_SQUEEZE:true,
|
||||
COLORS_GRAB_DISTANCE_HOLD:true,
|
||||
NEAR_GRAB_RADIUS:true,
|
||||
DISPATCHER_PROPERTIES:true,
|
||||
HAPTIC_PULSE_STRENGTH:true,
|
||||
HAPTIC_PULSE_DURATION:true,
|
||||
DISPATCHER_HOVERING_LIST:true,
|
||||
DISPATCHER_HOVERING_STYLE:true,
|
||||
Entities,
|
||||
makeDispatcherModuleParameters:true,
|
||||
makeRunningValues:true,
|
||||
enableDispatcherModule:true,
|
||||
disableDispatcherModule:true,
|
||||
getEnabledModuleByName:true,
|
||||
getGrabbableData:true,
|
||||
isAnothersAvatarEntity:true,
|
||||
isAnothersChildEntity:true,
|
||||
entityIsEquippable:true,
|
||||
entityIsGrabbable:true,
|
||||
entityIsDistanceGrabbable:true,
|
||||
getControllerJointIndexCacheTime:true,
|
||||
getControllerJointIndexCache:true,
|
||||
getControllerJointIndex:true,
|
||||
propsArePhysical:true,
|
||||
controllerDispatcherPluginsNeedSort:true,
|
||||
projectOntoXYPlane:true,
|
||||
projectOntoEntityXYPlane:true,
|
||||
projectOntoOverlayXYPlane:true,
|
||||
makeLaserLockInfo:true,
|
||||
entityHasActions:true,
|
||||
ensureDynamic:true,
|
||||
findGrabbableGroupParent:true,
|
||||
BUMPER_ON_VALUE:true,
|
||||
getEntityParents:true,
|
||||
findHandChildEntities:true,
|
||||
findFarGrabJointChildEntities:true,
|
||||
makeLaserParams:true,
|
||||
TEAR_AWAY_DISTANCE:true,
|
||||
TEAR_AWAY_COUNT:true,
|
||||
TEAR_AWAY_CHECK_TIME:true,
|
||||
NEAR_GRAB_DISTANCE: true,
|
||||
distanceBetweenPointAndEntityBoundingBox:true,
|
||||
entityIsEquipped:true,
|
||||
highlightTargetEntity:true,
|
||||
clearHighlightedEntities:true,
|
||||
unhighlightTargetEntity:true,
|
||||
distanceBetweenEntityLocalPositionAndBoundingBox: true,
|
||||
worldPositionToRegistrationFrameMatrix: true
|
||||
*/
|
||||
|
||||
MSECS_PER_SEC = 1000.0;
|
||||
INCHES_TO_METERS = 1.0 / 39.3701;
|
||||
|
||||
HAPTIC_PULSE_STRENGTH = 1.0;
|
||||
HAPTIC_PULSE_DURATION = 13.0;
|
||||
|
||||
ZERO_VEC = { x: 0, y: 0, z: 0 };
|
||||
ONE_VEC = { x: 1, y: 1, z: 1 };
|
||||
|
||||
LEFT_HAND = 0;
|
||||
RIGHT_HAND = 1;
|
||||
|
||||
FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"];
|
||||
|
||||
HAPTIC_PULSE_STRENGTH = 1.0;
|
||||
HAPTIC_PULSE_DURATION = 13.0;
|
||||
|
||||
DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
|
||||
TRIGGER_OFF_VALUE = 0.1;
|
||||
TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab
|
||||
BUMPER_ON_VALUE = 0.5;
|
||||
|
||||
PICK_MAX_DISTANCE = 500; // max length of pick-ray
|
||||
DEFAULT_SEARCH_SPHERE_DISTANCE = 1000; // how far from camera to search intersection?
|
||||
NEAR_GRAB_PICK_RADIUS = 0.25; // radius used for search ray vs object for near grabbing.
|
||||
|
||||
COLORS_GRAB_SEARCHING_HALF_SQUEEZE = { red: 10, green: 10, blue: 255 };
|
||||
COLORS_GRAB_SEARCHING_FULL_SQUEEZE = { red: 250, green: 10, blue: 10 };
|
||||
COLORS_GRAB_DISTANCE_HOLD = { red: 238, green: 75, blue: 214 };
|
||||
|
||||
NEAR_GRAB_RADIUS = 1.0;
|
||||
|
||||
TEAR_AWAY_DISTANCE = 0.15; // ungrab an entity if its bounding-box moves this far from the hand
|
||||
TEAR_AWAY_COUNT = 2; // multiply by TEAR_AWAY_CHECK_TIME to know how long the item must be away
|
||||
TEAR_AWAY_CHECK_TIME = 0.15; // seconds, duration between checks
|
||||
|
||||
TELEPORT_DEADZONE = 0.15;
|
||||
|
||||
NEAR_GRAB_DISTANCE = 0.14; // Grab an entity if its bounding box is within this distance.
|
||||
// Smaller than TEAR_AWAY_DISTANCE for hysteresis.
|
||||
|
||||
DISPATCHER_HOVERING_LIST = "dispatcherHoveringList";
|
||||
DISPATCHER_HOVERING_STYLE = {
|
||||
isOutlineSmooth: true,
|
||||
outlineWidth: 0,
|
||||
outlineUnoccludedColor: {red: 255, green: 128, blue: 128},
|
||||
outlineUnoccludedAlpha: 0.0,
|
||||
outlineOccludedColor: {red: 255, green: 128, blue: 128},
|
||||
outlineOccludedAlpha:0.0,
|
||||
fillUnoccludedColor: {red: 255, green: 255, blue: 255},
|
||||
fillUnoccludedAlpha: 0.12,
|
||||
fillOccludedColor: {red: 255, green: 255, blue: 255},
|
||||
fillOccludedAlpha: 0.0
|
||||
};
|
||||
|
||||
DISPATCHER_PROPERTIES = [
|
||||
"position",
|
||||
"registrationPoint",
|
||||
"rotation",
|
||||
"gravity",
|
||||
"collidesWith",
|
||||
"dynamic",
|
||||
"collisionless",
|
||||
"locked",
|
||||
"name",
|
||||
"shapeType",
|
||||
"parentID",
|
||||
"parentJointIndex",
|
||||
"density",
|
||||
"dimensions",
|
||||
"type",
|
||||
"href",
|
||||
"cloneable",
|
||||
"cloneDynamic",
|
||||
"localPosition",
|
||||
"localRotation",
|
||||
"grab.grabbable",
|
||||
"grab.grabKinematic",
|
||||
"grab.grabFollowsController",
|
||||
"grab.triggerable",
|
||||
"grab.equippable",
|
||||
"grab.grabDelegateToParent",
|
||||
"grab.equippableLeftPosition",
|
||||
"grab.equippableLeftRotation",
|
||||
"grab.equippableRightPosition",
|
||||
"grab.equippableRightRotation",
|
||||
"grab.equippableIndicatorURL",
|
||||
"grab.equippableIndicatorScale",
|
||||
"grab.equippableIndicatorOffset",
|
||||
"userData",
|
||||
"avatarEntity",
|
||||
"owningAvatarID",
|
||||
"certificateID",
|
||||
"certificateType"
|
||||
];
|
||||
|
||||
// priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step
|
||||
// activitySlots -- indicates which "slots" must not yet be in use for this module to start
|
||||
// requiredDataForReady -- which "situation" parts this module looks at to decide if it will start
|
||||
// sleepMSBetweenRuns -- how long to wait between calls to this module's "run" method
|
||||
makeDispatcherModuleParameters = function (priority, activitySlots, requiredDataForReady, sleepMSBetweenRuns, enableLaserForHand) {
|
||||
if (enableLaserForHand === undefined) {
|
||||
enableLaserForHand = -1;
|
||||
}
|
||||
|
||||
return {
|
||||
priority: priority,
|
||||
activitySlots: activitySlots,
|
||||
requiredDataForReady: requiredDataForReady,
|
||||
sleepMSBetweenRuns: sleepMSBetweenRuns,
|
||||
handLaser: enableLaserForHand
|
||||
};
|
||||
};
|
||||
|
||||
makeLaserLockInfo = function(targetID, isOverlay, hand, offset) {
|
||||
return {
|
||||
targetID: targetID,
|
||||
isOverlay: isOverlay,
|
||||
hand: hand,
|
||||
offset: offset
|
||||
};
|
||||
};
|
||||
|
||||
makeLaserParams = function(hand, alwaysOn) {
|
||||
if (alwaysOn === undefined) {
|
||||
alwaysOn = false;
|
||||
}
|
||||
|
||||
return {
|
||||
hand: hand,
|
||||
alwaysOn: alwaysOn
|
||||
};
|
||||
};
|
||||
|
||||
makeRunningValues = function (active, targets, requiredDataForRun, laserLockInfo) {
|
||||
return {
|
||||
active: active,
|
||||
targets: targets,
|
||||
requiredDataForRun: requiredDataForRun,
|
||||
laserLockInfo: laserLockInfo
|
||||
};
|
||||
};
|
||||
|
||||
enableDispatcherModule = function (moduleName, module, priority) {
|
||||
if (!controllerDispatcherPlugins) {
|
||||
controllerDispatcherPlugins = {};
|
||||
}
|
||||
controllerDispatcherPlugins[moduleName] = module;
|
||||
controllerDispatcherPluginsNeedSort = true;
|
||||
};
|
||||
|
||||
disableDispatcherModule = function (moduleName) {
|
||||
delete controllerDispatcherPlugins[moduleName];
|
||||
controllerDispatcherPluginsNeedSort = true;
|
||||
};
|
||||
|
||||
getEnabledModuleByName = function (moduleName) {
|
||||
if (controllerDispatcherPlugins.hasOwnProperty(moduleName)) {
|
||||
return controllerDispatcherPlugins[moduleName];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
getGrabbableData = function (ggdProps) {
|
||||
// look in userData for a "grabbable" key, return the value or some defaults
|
||||
var grabbableData = {};
|
||||
var userDataParsed = null;
|
||||
try {
|
||||
if (!ggdProps.userDataParsed) {
|
||||
ggdProps.userDataParsed = JSON.parse(ggdProps.userData);
|
||||
}
|
||||
userDataParsed = ggdProps.userDataParsed;
|
||||
} catch (err) {
|
||||
userDataParsed = {};
|
||||
}
|
||||
|
||||
if (userDataParsed.grabbableKey) {
|
||||
grabbableData = userDataParsed.grabbableKey;
|
||||
} else {
|
||||
grabbableData = ggdProps.grab;
|
||||
}
|
||||
|
||||
// extract grab-related properties, provide defaults if any are missing
|
||||
if (!grabbableData.hasOwnProperty("grabbable")) {
|
||||
grabbableData.grabbable = true;
|
||||
}
|
||||
// kinematic has been renamed to grabKinematic
|
||||
if (!grabbableData.hasOwnProperty("grabKinematic") &&
|
||||
!grabbableData.hasOwnProperty("kinematic")) {
|
||||
grabbableData.grabKinematic = true;
|
||||
}
|
||||
if (!grabbableData.hasOwnProperty("grabKinematic")) {
|
||||
grabbableData.grabKinematic = grabbableData.kinematic;
|
||||
}
|
||||
// ignoreIK has been renamed to grabFollowsController
|
||||
if (!grabbableData.hasOwnProperty("grabFollowsController") &&
|
||||
!grabbableData.hasOwnProperty("ignoreIK")) {
|
||||
grabbableData.grabFollowsController = true;
|
||||
}
|
||||
if (!grabbableData.hasOwnProperty("grabFollowsController")) {
|
||||
grabbableData.grabFollowsController = grabbableData.ignoreIK;
|
||||
}
|
||||
// wantsTrigger has been renamed to triggerable
|
||||
if (!grabbableData.hasOwnProperty("triggerable") &&
|
||||
!grabbableData.hasOwnProperty("wantsTrigger")) {
|
||||
grabbableData.triggerable = false;
|
||||
}
|
||||
if (!grabbableData.hasOwnProperty("triggerable")) {
|
||||
grabbableData.triggerable = grabbableData.wantsTrigger;
|
||||
}
|
||||
if (!grabbableData.hasOwnProperty("equippable")) {
|
||||
grabbableData.equippable = false;
|
||||
}
|
||||
if (!grabbableData.hasOwnProperty("equippableLeftPosition")) {
|
||||
grabbableData.equippableLeftPosition = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
if (!grabbableData.hasOwnProperty("equippableLeftRotation")) {
|
||||
grabbableData.equippableLeftPosition = { x: 0, y: 0, z: 0, w: 1 };
|
||||
}
|
||||
if (!grabbableData.hasOwnProperty("equippableRightPosition")) {
|
||||
grabbableData.equippableRightPosition = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
if (!grabbableData.hasOwnProperty("equippableRightRotation")) {
|
||||
grabbableData.equippableRightPosition = { x: 0, y: 0, z: 0, w: 1 };
|
||||
}
|
||||
return grabbableData;
|
||||
};
|
||||
|
||||
isAnothersAvatarEntity = function (iaaeProps) {
|
||||
if (!iaaeProps.avatarEntity) {
|
||||
return false;
|
||||
}
|
||||
if (iaaeProps.owningAvatarID === MyAvatar.sessionUUID) {
|
||||
return false;
|
||||
}
|
||||
if (iaaeProps.owningAvatarID === MyAvatar.SELF_ID) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
isAnothersChildEntity = function (iaceProps) {
|
||||
while (iaceProps.parentID && iaceProps.parentID !== Uuid.NULL) {
|
||||
if (Entities.getNestableType(iaceProps.parentID) == "avatar") {
|
||||
if (iaceProps.parentID == MyAvatar.SELF_ID || iaceProps.parentID == MyAvatar.sessionUUID) {
|
||||
return false; // not another's, it's mine.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Entities.getNestableType(iaceProps.parentID) == "entity") {
|
||||
var parentProps = Entities.getEntityProperties(iaceProps.parentID, DISPATCHER_PROPERTIES);
|
||||
if (!parentProps) {
|
||||
break;
|
||||
}
|
||||
parentProps.id = iaceProps.parentID;
|
||||
iaceProps = parentProps;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
entityIsEquippable = function (eieProps) {
|
||||
var grabbable = getGrabbableData(eieProps).grabbable;
|
||||
if (!grabbable ||
|
||||
isAnothersAvatarEntity(eieProps) ||
|
||||
isAnothersChildEntity(eieProps) ||
|
||||
FORBIDDEN_GRAB_TYPES.indexOf(eieProps.type) >= 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
entityIsGrabbable = function (eigProps) {
|
||||
var grabbable = getGrabbableData(eigProps).grabbable;
|
||||
if (!grabbable ||
|
||||
eigProps.locked ||
|
||||
FORBIDDEN_GRAB_TYPES.indexOf(eigProps.type) >= 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
clearHighlightedEntities = function() {
|
||||
Selection.clearSelectedItemsList(DISPATCHER_HOVERING_LIST);
|
||||
};
|
||||
|
||||
highlightTargetEntity = function(entityID) {
|
||||
Selection.addToSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", entityID);
|
||||
};
|
||||
|
||||
unhighlightTargetEntity = function(entityID) {
|
||||
Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", entityID);
|
||||
};
|
||||
|
||||
entityIsDistanceGrabbable = function(eidgProps) {
|
||||
if (!entityIsGrabbable(eidgProps)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we can't distance-grab non-physical
|
||||
var isPhysical = propsArePhysical(eidgProps);
|
||||
if (!isPhysical) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
getControllerJointIndexCacheTime = [0, 0];
|
||||
getControllerJointIndexCache = [-1, -1];
|
||||
|
||||
getControllerJointIndex = function (hand) {
|
||||
var GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME = 3000; // msecs
|
||||
|
||||
var now = Date.now();
|
||||
if (now - getControllerJointIndexCacheTime[hand] > GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME) {
|
||||
if (HMD.isHandControllerAvailable()) {
|
||||
var controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
|
||||
"_CONTROLLER_RIGHTHAND" :
|
||||
"_CONTROLLER_LEFTHAND");
|
||||
getControllerJointIndexCacheTime[hand] = now;
|
||||
getControllerJointIndexCache[hand] = controllerJointIndex;
|
||||
return controllerJointIndex;
|
||||
}
|
||||
} else {
|
||||
return getControllerJointIndexCache[hand];
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
propsArePhysical = function (papProps) {
|
||||
if (!papProps.dynamic) {
|
||||
return false;
|
||||
}
|
||||
var isPhysical = (papProps.shapeType && papProps.shapeType !== 'none');
|
||||
return isPhysical;
|
||||
};
|
||||
|
||||
projectOntoXYPlane = function (worldPos, position, rotation, dimensions, registrationPoint) {
|
||||
var invRot = Quat.inverse(rotation);
|
||||
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, position));
|
||||
var invDimensions = {
|
||||
x: 1 / dimensions.x,
|
||||
y: 1 / dimensions.y,
|
||||
z: 1 / dimensions.z
|
||||
};
|
||||
|
||||
var normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), registrationPoint);
|
||||
return {
|
||||
x: normalizedPos.x * dimensions.x,
|
||||
y: (1 - normalizedPos.y) * dimensions.y // flip y-axis
|
||||
};
|
||||
};
|
||||
|
||||
projectOntoEntityXYPlane = function (entityID, worldPos, popProps) {
|
||||
return projectOntoXYPlane(worldPos, popProps.position, popProps.rotation,
|
||||
popProps.dimensions, popProps.registrationPoint);
|
||||
};
|
||||
|
||||
projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldPos) {
|
||||
var position = Overlays.getProperty(overlayID, "position");
|
||||
var rotation = Overlays.getProperty(overlayID, "rotation");
|
||||
var dimensions = Overlays.getProperty(overlayID, "dimensions");
|
||||
dimensions.z = 0.01; // we are projecting onto the XY plane of the overlay, so ignore the z dimension
|
||||
|
||||
return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT);
|
||||
};
|
||||
|
||||
entityHasActions = function (entityID) {
|
||||
return Entities.getActionIDs(entityID).length > 0;
|
||||
};
|
||||
|
||||
ensureDynamic = function (entityID) {
|
||||
// if we distance hold something and keep it very still before releasing it, it ends up
|
||||
// non-dynamic in bullet. If it's too still, give it a little bounce so it will fall.
|
||||
var edProps = Entities.getEntityProperties(entityID, ["velocity", "dynamic", "parentID"]);
|
||||
if (edProps.dynamic && edProps.parentID === Uuid.NULL) {
|
||||
var velocity = edProps.velocity;
|
||||
if (Vec3.length(velocity) < 0.05) { // see EntityMotionState.cpp DYNAMIC_LINEAR_VELOCITY_THRESHOLD
|
||||
velocity = { x: 0.0, y: 0.2, z: 0.0 };
|
||||
Entities.editEntity(entityID, { velocity: velocity });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
findGrabbableGroupParent = function (controllerData, targetProps) {
|
||||
while (targetProps.grab.grabDelegateToParent &&
|
||||
targetProps.parentID &&
|
||||
targetProps.parentID !== Uuid.NULL &&
|
||||
Entities.getNestableType(targetProps.parentID) == "entity") {
|
||||
var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES);
|
||||
if (!parentProps) {
|
||||
break;
|
||||
}
|
||||
if (!entityIsGrabbable(parentProps)) {
|
||||
break;
|
||||
}
|
||||
parentProps.id = targetProps.parentID;
|
||||
targetProps = parentProps;
|
||||
controllerData.nearbyEntityPropertiesByID[targetProps.id] = targetProps;
|
||||
}
|
||||
|
||||
return targetProps;
|
||||
};
|
||||
|
||||
getEntityParents = function(targetProps) {
|
||||
var parentProperties = [];
|
||||
while (targetProps.parentID &&
|
||||
targetProps.parentID !== Uuid.NULL &&
|
||||
Entities.getNestableType(targetProps.parentID) == "entity") {
|
||||
var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES);
|
||||
if (!parentProps) {
|
||||
break;
|
||||
}
|
||||
parentProps.id = targetProps.parentID;
|
||||
targetProps = parentProps;
|
||||
parentProperties.push(parentProps);
|
||||
}
|
||||
|
||||
return parentProperties;
|
||||
};
|
||||
|
||||
|
||||
findHandChildEntities = function(hand) {
|
||||
// find children of avatar's hand joint
|
||||
var handJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? "RightHand" : "LeftHand");
|
||||
var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, handJointIndex);
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.SELF_ID, handJointIndex));
|
||||
|
||||
// find children of faux controller joint
|
||||
var controllerJointIndex = getControllerJointIndex(hand);
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, controllerJointIndex));
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.SELF_ID, controllerJointIndex));
|
||||
|
||||
// find children of faux camera-relative controller joint
|
||||
var controllerCRJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
|
||||
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
|
||||
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, controllerCRJointIndex));
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.SELF_ID, controllerCRJointIndex));
|
||||
|
||||
return children.filter(function (childID) {
|
||||
var childType = Entities.getNestableType(childID);
|
||||
return childType == "entity";
|
||||
});
|
||||
};
|
||||
|
||||
findFarGrabJointChildEntities = function(hand) {
|
||||
// find children of avatar's far-grab joint
|
||||
var farGrabJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? "_FARGRAB_RIGHTHAND" : "_FARGRAB_LEFTHAND");
|
||||
var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, farGrabJointIndex);
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.SELF_ID, farGrabJointIndex));
|
||||
|
||||
return children.filter(function (childID) {
|
||||
var childType = Entities.getNestableType(childID);
|
||||
return childType == "entity";
|
||||
});
|
||||
};
|
||||
|
||||
distanceBetweenEntityLocalPositionAndBoundingBox = function(entityProps, jointGrabOffset) {
|
||||
var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
var rotInv = Quat.inverse(entityProps.localRotation);
|
||||
var localPosition = Vec3.sum(entityProps.localPosition, jointGrabOffset);
|
||||
var localPoint = Vec3.multiplyQbyV(rotInv, Vec3.multiply(localPosition, -1.0));
|
||||
|
||||
var halfDims = Vec3.multiply(entityProps.dimensions, 0.5);
|
||||
var regRatio = Vec3.subtract(DEFAULT_REGISTRATION_POINT, entityProps.registrationPoint);
|
||||
var entityCenter = Vec3.multiplyVbyV(regRatio, entityProps.dimensions);
|
||||
var localMin = Vec3.subtract(entityCenter, halfDims);
|
||||
var localMax = Vec3.sum(entityCenter, halfDims);
|
||||
|
||||
|
||||
var v = {x: localPoint.x, y: localPoint.y, z: localPoint.z};
|
||||
v.x = Math.max(v.x, localMin.x);
|
||||
v.x = Math.min(v.x, localMax.x);
|
||||
v.y = Math.max(v.y, localMin.y);
|
||||
v.y = Math.min(v.y, localMax.y);
|
||||
v.z = Math.max(v.z, localMin.z);
|
||||
v.z = Math.min(v.z, localMax.z);
|
||||
|
||||
return Vec3.distance(v, localPoint);
|
||||
};
|
||||
|
||||
distanceBetweenPointAndEntityBoundingBox = function(point, entityProps) {
|
||||
var entityXform = new Xform(entityProps.rotation, entityProps.position);
|
||||
var localPoint = entityXform.inv().xformPoint(point);
|
||||
var minOffset = Vec3.multiplyVbyV(entityProps.registrationPoint, entityProps.dimensions);
|
||||
var maxOffset = Vec3.multiplyVbyV(Vec3.subtract(ONE_VEC, entityProps.registrationPoint), entityProps.dimensions);
|
||||
var localMin = Vec3.subtract(entityXform.trans, minOffset);
|
||||
var localMax = Vec3.sum(entityXform.trans, maxOffset);
|
||||
|
||||
var v = {x: localPoint.x, y: localPoint.y, z: localPoint.z};
|
||||
v.x = Math.max(v.x, localMin.x);
|
||||
v.x = Math.min(v.x, localMax.x);
|
||||
v.y = Math.max(v.y, localMin.y);
|
||||
v.y = Math.min(v.y, localMax.y);
|
||||
v.z = Math.max(v.z, localMin.z);
|
||||
v.z = Math.min(v.z, localMax.z);
|
||||
|
||||
return Vec3.distance(v, localPoint);
|
||||
};
|
||||
|
||||
entityIsEquipped = function(entityID) {
|
||||
var rightEquipEntity = getEnabledModuleByName("RightEquipEntity");
|
||||
var leftEquipEntity = getEnabledModuleByName("LeftEquipEntity");
|
||||
var equippedInRightHand = rightEquipEntity ? rightEquipEntity.targetEntityID === entityID : false;
|
||||
var equippedInLeftHand = leftEquipEntity ? leftEquipEntity.targetEntityID === entityID : false;
|
||||
return equippedInRightHand || equippedInLeftHand;
|
||||
};
|
||||
|
||||
worldPositionToRegistrationFrameMatrix = function(wptrProps, pos) {
|
||||
// get world matrix for intersection point
|
||||
var intersectionMat = new Xform({ x: 0, y: 0, z:0, w: 1 }, pos);
|
||||
|
||||
// calculate world matrix for registrationPoint addjusted entity
|
||||
var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
var regRatio = Vec3.subtract(DEFAULT_REGISTRATION_POINT, wptrProps.registrationPoint);
|
||||
var regOffset = Vec3.multiplyVbyV(regRatio, wptrProps.dimensions);
|
||||
var regOffsetRot = Vec3.multiplyQbyV(wptrProps.rotation, regOffset);
|
||||
var modelMat = new Xform(wptrProps.rotation, Vec3.sum(wptrProps.position, regOffsetRot));
|
||||
|
||||
// get inverse of model matrix
|
||||
var modelMatInv = modelMat.inv();
|
||||
|
||||
// transform world intersection point into object's registrationPoint frame
|
||||
var xformMat = Xform.mul(modelMatInv, intersectionMat);
|
||||
|
||||
// convert to Mat4
|
||||
var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos);
|
||||
return offsetMat;
|
||||
};
|
||||
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = {
|
||||
makeDispatcherModuleParameters: makeDispatcherModuleParameters,
|
||||
enableDispatcherModule: enableDispatcherModule,
|
||||
disableDispatcherModule: disableDispatcherModule,
|
||||
highlightTargetEntity: highlightTargetEntity,
|
||||
unhighlightTargetEntity: unhighlightTargetEntity,
|
||||
clearHighlightedEntities: clearHighlightedEntities,
|
||||
makeRunningValues: makeRunningValues,
|
||||
findGrabbableGroupParent: findGrabbableGroupParent,
|
||||
LEFT_HAND: LEFT_HAND,
|
||||
RIGHT_HAND: RIGHT_HAND,
|
||||
BUMPER_ON_VALUE: BUMPER_ON_VALUE,
|
||||
TEAR_AWAY_DISTANCE: TEAR_AWAY_DISTANCE,
|
||||
propsArePhysical: propsArePhysical,
|
||||
entityIsEquippable: entityIsEquippable,
|
||||
entityIsGrabbable: entityIsGrabbable,
|
||||
NEAR_GRAB_RADIUS: NEAR_GRAB_RADIUS,
|
||||
projectOntoOverlayXYPlane: projectOntoOverlayXYPlane,
|
||||
projectOntoEntityXYPlane: projectOntoEntityXYPlane,
|
||||
TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE,
|
||||
TRIGGER_ON_VALUE: TRIGGER_ON_VALUE,
|
||||
DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST,
|
||||
worldPositionToRegistrationFrameMatrix: worldPositionToRegistrationFrameMatrix
|
||||
};
|
||||
}
|
81
scripts/simplifiedUI/system/libraries/controllers.js
Normal file
81
scripts/simplifiedUI/system/libraries/controllers.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
// handControllerGrab.js
|
||||
//
|
||||
// Created by Seth Alves on 2016-9-7
|
||||
// Copyright 2016 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 MyAvatar, Vec3, HMD, Controller, Camera, Quat, Settings,
|
||||
getGrabPointSphereOffset:true,
|
||||
setGrabCommunications:true,
|
||||
getGrabCommunications:true,
|
||||
getControllerWorldLocation:true
|
||||
*/
|
||||
|
||||
var GRAB_COMMUNICATIONS_SETTING = "io.highfidelity.isFarGrabbing";
|
||||
setGrabCommunications = function setFarGrabCommunications(on) {
|
||||
Settings.setValue(GRAB_COMMUNICATIONS_SETTING, on ? "on" : "");
|
||||
};
|
||||
getGrabCommunications = function getFarGrabCommunications() {
|
||||
return !!Settings.getValue(GRAB_COMMUNICATIONS_SETTING, "");
|
||||
};
|
||||
|
||||
// this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378
|
||||
|
||||
getGrabPointSphereOffset = function(handController, ignoreSensorToWorldScale) {
|
||||
var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral
|
||||
var offset = GRAB_POINT_SPHERE_OFFSET;
|
||||
if (handController === Controller.Standard.LeftHand) {
|
||||
offset = {
|
||||
x: -GRAB_POINT_SPHERE_OFFSET.x,
|
||||
y: GRAB_POINT_SPHERE_OFFSET.y,
|
||||
z: GRAB_POINT_SPHERE_OFFSET.z
|
||||
};
|
||||
}
|
||||
if (ignoreSensorToWorldScale) {
|
||||
return offset;
|
||||
} else {
|
||||
return Vec3.multiply(MyAvatar.sensorToWorldScale, offset);
|
||||
}
|
||||
};
|
||||
|
||||
// controllerWorldLocation is where the controller would be, in-world, with an added offset
|
||||
getControllerWorldLocation = function (handController, doOffset) {
|
||||
var orientation;
|
||||
var position;
|
||||
var valid = false;
|
||||
|
||||
if (handController >= 0) {
|
||||
var pose = Controller.getPoseValue(handController);
|
||||
valid = pose.valid;
|
||||
var controllerJointIndex;
|
||||
if (pose.valid) {
|
||||
if (handController === Controller.Standard.RightHand) {
|
||||
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND");
|
||||
} else {
|
||||
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
|
||||
}
|
||||
orientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(controllerJointIndex));
|
||||
position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(controllerJointIndex)));
|
||||
|
||||
// add to the real position so the grab-point is out in front of the hand, a bit
|
||||
if (doOffset) {
|
||||
var offset = getGrabPointSphereOffset(handController);
|
||||
position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, offset));
|
||||
}
|
||||
|
||||
} else if (!HMD.isHandControllerAvailable()) {
|
||||
// NOTE: keep this offset in sync with scripts/system/controllers/handControllerPointer.js:493
|
||||
var VERTICAL_HEAD_LASER_OFFSET = 0.1 * MyAvatar.sensorToWorldScale;
|
||||
position = Vec3.sum(Camera.position, Vec3.multiplyQbyV(Camera.orientation, {x: 0, y: VERTICAL_HEAD_LASER_OFFSET, z: 0}));
|
||||
orientation = Quat.multiply(Camera.orientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }));
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {position: position,
|
||||
translation: position,
|
||||
orientation: orientation,
|
||||
rotation: orientation,
|
||||
valid: valid};
|
||||
};
|
62
scripts/simplifiedUI/system/libraries/dataViewHelpers.js
Normal file
62
scripts/simplifiedUI/system/libraries/dataViewHelpers.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// dataViewHelpers.js
|
||||
// examples/libraries
|
||||
//
|
||||
// Copyright 2014 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
|
||||
//
|
||||
|
||||
if (typeof DataView.prototype.indexOf !== "function") {
|
||||
DataView.prototype.indexOf = function (searchString, position) {
|
||||
var searchLength = searchString.length,
|
||||
byteArrayLength = this.byteLength,
|
||||
maxSearchIndex = byteArrayLength - searchLength,
|
||||
searchCharCodes = [],
|
||||
found,
|
||||
i,
|
||||
j;
|
||||
|
||||
searchCharCodes[searchLength] = 0;
|
||||
for (j = 0; j < searchLength; j += 1) {
|
||||
searchCharCodes[j] = searchString.charCodeAt(j);
|
||||
}
|
||||
|
||||
i = position;
|
||||
found = false;
|
||||
while (i < maxSearchIndex && !found) {
|
||||
j = 0;
|
||||
while (j < searchLength && this.getUint8(i + j) === searchCharCodes[j]) {
|
||||
j += 1;
|
||||
}
|
||||
found = (j === searchLength);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return found ? i - 1 : -1;
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof DataView.prototype.string !== "function") {
|
||||
DataView.prototype.string = function (start, length) {
|
||||
var charCodes = [],
|
||||
end,
|
||||
i;
|
||||
|
||||
if (start === undefined) {
|
||||
start = 0;
|
||||
}
|
||||
if (length === undefined) {
|
||||
length = this.length;
|
||||
}
|
||||
|
||||
end = start + length;
|
||||
for (i = start; i < end; i += 1) {
|
||||
charCodes.push(this.getUint8(i));
|
||||
}
|
||||
|
||||
return String.fromCharCode.apply(String, charCodes);
|
||||
};
|
||||
}
|
||||
|
657
scripts/simplifiedUI/system/libraries/entityCameraTool.js
Normal file
657
scripts/simplifiedUI/system/libraries/entityCameraTool.js
Normal file
|
@ -0,0 +1,657 @@
|
|||
//
|
||||
// entityCameraTool.js
|
||||
// examples
|
||||
//
|
||||
// Created by Ryan Huffman on 10/14/14.
|
||||
// Copyright 2014 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
|
||||
//
|
||||
|
||||
Script.include("overlayUtils.js");
|
||||
|
||||
var MOUSE_SENSITIVITY = 0.9;
|
||||
var SCROLL_SENSITIVITY = 0.05;
|
||||
var PAN_ZOOM_SCALE_RATIO = 0.4;
|
||||
|
||||
var KEY_ORBIT_SENSITIVITY = 90;
|
||||
var KEY_ZOOM_SENSITIVITY = 3;
|
||||
|
||||
// Scaling applied based on the size of the object being focused (Larger values focus further away)
|
||||
var FOCUS_ZOOM_SCALE = 2.3;
|
||||
|
||||
// Minimum zoom level when focusing on an object
|
||||
var FOCUS_MIN_ZOOM = 0.5;
|
||||
|
||||
// Scaling applied based on the current zoom level
|
||||
var ZOOM_SCALING = 0.02;
|
||||
|
||||
var MIN_ZOOM_DISTANCE = 0.01;
|
||||
|
||||
// The maximum usable zoom level is somewhere around 14km, further than that the edit handles will fade-out. (FIXME: MS17493)
|
||||
var MAX_ZOOM_DISTANCE = 14000;
|
||||
|
||||
var MODE_INACTIVE = 'inactive';
|
||||
var MODE_ORBIT = 'orbit';
|
||||
var MODE_PAN = 'pan';
|
||||
|
||||
var EASING_MULTIPLIER = 8;
|
||||
|
||||
var INITIAL_ZOOM_DISTANCE = 2;
|
||||
var INITIAL_ZOOM_DISTANCE_FIRST_PERSON = 3;
|
||||
|
||||
var easeOutCubic = function(t) {
|
||||
t--;
|
||||
return t * t * t + 1;
|
||||
};
|
||||
|
||||
EASE_TIME = 0.5;
|
||||
|
||||
function clamp(value, minimum, maximum) {
|
||||
return Math.min(Math.max(value, minimum), maximum);
|
||||
}
|
||||
|
||||
function mergeObjects(obj1, obj2) {
|
||||
var newObj = {};
|
||||
for (key in obj1) {
|
||||
newObj[key] = obj1[key];
|
||||
}
|
||||
for (key in obj2) {
|
||||
newObj[key] = obj2[key];
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
|
||||
CameraManager = function() {
|
||||
var that = {};
|
||||
|
||||
that.enabled = false;
|
||||
that.mode = MODE_INACTIVE;
|
||||
|
||||
var actions = {
|
||||
orbitLeft: 0,
|
||||
orbitRight: 0,
|
||||
orbitUp: 0,
|
||||
orbitDown: 0,
|
||||
orbitForward: 0,
|
||||
orbitBackward: 0,
|
||||
}
|
||||
|
||||
var keyToActionMapping = {
|
||||
65: "orbitLeft", // "a"
|
||||
68: "orbitRight", // "d"
|
||||
87: "orbitForward", // "w"
|
||||
83: "orbitBackward", // "s"
|
||||
69: "orbitUp", // "e"
|
||||
67: "orbitDown", // "c"
|
||||
|
||||
16777234: "orbitLeft", // "LEFT"
|
||||
16777236: "orbitRight", // "RIGHT"
|
||||
16777235: "orbitForward", // "UP"
|
||||
16777237: "orbitBackward", // "DOWN"
|
||||
}
|
||||
|
||||
var CAPTURED_KEYS = [];
|
||||
for (var key in keyToActionMapping) {
|
||||
CAPTURED_KEYS.push(key);
|
||||
}
|
||||
|
||||
function getActionForKeyEvent(event) {
|
||||
if (!event.isControl) {
|
||||
var action = keyToActionMapping[event.key];
|
||||
if (action !== undefined) {
|
||||
if (event.isShifted) {
|
||||
if (action === "orbitForward") {
|
||||
action = "orbitUp";
|
||||
} else if (action === "orbitBackward") {
|
||||
action = "orbitDown";
|
||||
}
|
||||
}
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
that.zoomDistance = INITIAL_ZOOM_DISTANCE;
|
||||
that.targetZoomDistance = INITIAL_ZOOM_DISTANCE;
|
||||
|
||||
that.yaw = 0;
|
||||
that.pitch = 0;
|
||||
that.targetYaw = 0;
|
||||
that.targetPitch = 0;
|
||||
|
||||
that.focalPoint = Vec3.ZERO;
|
||||
that.targetFocalPoint = Vec3.ZERO;
|
||||
|
||||
easing = false;
|
||||
easingTime = 0;
|
||||
startOrientation = Quat.IDENTITY;
|
||||
|
||||
that.previousCameraMode = null;
|
||||
|
||||
that.lastMousePosition = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
that.enable = function() {
|
||||
if (Camera.mode === "independent" || that.enabled || HMD.active) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
||||
Controller.captureKeyEvents({
|
||||
text: CAPTURED_KEYS[i]
|
||||
});
|
||||
}
|
||||
|
||||
for (var action in actions) {
|
||||
actions[action] = 0;
|
||||
}
|
||||
|
||||
that.enabled = true;
|
||||
that.mode = MODE_INACTIVE;
|
||||
|
||||
// Pick a point INITIAL_ZOOM_DISTANCE in front of the camera to use as a focal point
|
||||
that.zoomDistance = INITIAL_ZOOM_DISTANCE;
|
||||
that.targetZoomDistance = that.zoomDistance + 3.0;
|
||||
var focalPoint = Vec3.sum(Camera.getPosition(),
|
||||
Vec3.multiply(that.zoomDistance, Quat.getForward(Camera.getOrientation())));
|
||||
|
||||
// Determine the correct yaw and pitch to keep the camera in the same location
|
||||
var dPos = Vec3.subtract(focalPoint, Camera.getPosition());
|
||||
var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z);
|
||||
|
||||
that.targetPitch = -Math.atan2(dPos.y, xzDist) * 180 / Math.PI;
|
||||
that.targetPitch += (90 - that.targetPitch) / 3.0; // Swing camera "up" to look down at the focal point
|
||||
that.targetYaw = Math.atan2(dPos.x, dPos.z) * 180 / Math.PI;
|
||||
that.pitch = that.targetPitch;
|
||||
that.yaw = that.targetYaw;
|
||||
|
||||
that.focalPoint = focalPoint;
|
||||
that.setFocalPoint(focalPoint);
|
||||
that.previousCameraMode = Camera.mode;
|
||||
Camera.mode = "independent";
|
||||
|
||||
that.updateCamera();
|
||||
|
||||
cameraTool.setVisible(true);
|
||||
}
|
||||
|
||||
that.disable = function(ignoreCamera) {
|
||||
if (!that.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
||||
Controller.releaseKeyEvents({
|
||||
text: CAPTURED_KEYS[i]
|
||||
});
|
||||
}
|
||||
|
||||
that.enabled = false;
|
||||
that.mode = MODE_INACTIVE;
|
||||
|
||||
if (!ignoreCamera) {
|
||||
Camera.mode = that.previousCameraMode;
|
||||
}
|
||||
cameraTool.setVisible(false);
|
||||
}
|
||||
|
||||
that.focus = function(position, dimensions, easeOrientation) {
|
||||
if (dimensions) {
|
||||
var size = Math.max(dimensions.x, Math.max(dimensions.y, dimensions.z));
|
||||
that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM);
|
||||
} else {
|
||||
that.targetZoomDistance = Vec3.length(Vec3.subtract(Camera.getPosition(), position));
|
||||
}
|
||||
|
||||
if (easeOrientation) {
|
||||
// Do eased turning towards target
|
||||
that.focalPoint = that.targetFocalPoint = position;
|
||||
|
||||
that.zoomDistance = that.targetZoomDistance = Vec3.length(Vec3.subtract(Camera.getPosition(), position));
|
||||
|
||||
var dPos = Vec3.subtract(that.focalPoint, Camera.getPosition());
|
||||
var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z);
|
||||
|
||||
that.targetPitch = -Math.atan2(dPos.y, xzDist) * 180 / Math.PI;
|
||||
that.targetYaw = Math.atan2(dPos.x, dPos.z) * 180 / Math.PI;
|
||||
that.pitch = that.targetPitch;
|
||||
that.yaw = that.targetYaw;
|
||||
|
||||
startOrientation = Camera.getOrientation();
|
||||
|
||||
easing = true;
|
||||
easingTime = 0;
|
||||
} else {
|
||||
that.setFocalPoint(position);
|
||||
}
|
||||
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.setTargetPitchYaw = function(pitch, yaw) {
|
||||
that.targetPitch = pitch;
|
||||
that.targetYaw = yaw;
|
||||
}
|
||||
|
||||
that.moveFocalPoint = function(dPos) {
|
||||
that.setFocalPoint(Vec3.sum(that.focalPoint, dPos));
|
||||
}
|
||||
|
||||
that.setFocalPoint = function(pos) {
|
||||
that.targetFocalPoint = pos;
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.addYaw = function(yaw) {
|
||||
that.targetYaw += yaw;
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.addPitch = function(pitch) {
|
||||
that.targetPitch += pitch;
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.addZoom = function(zoom) {
|
||||
zoom *= that.targetZoomDistance * ZOOM_SCALING;
|
||||
that.targetZoomDistance = Math.min(Math.max(that.targetZoomDistance + zoom, MIN_ZOOM_DISTANCE), MAX_ZOOM_DISTANCE);
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.pan = function(offset) {
|
||||
var up = Quat.getUp(Camera.getOrientation());
|
||||
var right = Quat.getRight(Camera.getOrientation());
|
||||
|
||||
up = Vec3.multiply(up, offset.y * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);
|
||||
right = Vec3.multiply(right, offset.x * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);
|
||||
|
||||
var dPosition = Vec3.sum(up, right);
|
||||
|
||||
that.moveFocalPoint(dPosition);
|
||||
}
|
||||
|
||||
that.mouseMoveEvent = function(event) {
|
||||
if (that.enabled && that.mode !== MODE_INACTIVE) {
|
||||
var x = Reticle.getPosition().x;
|
||||
var y = Reticle.getPosition().y;
|
||||
if (!hasDragged) {
|
||||
that.lastMousePosition.x = x;
|
||||
that.lastMousePosition.y = y;
|
||||
hasDragged = true;
|
||||
}
|
||||
if (that.mode === MODE_ORBIT) {
|
||||
var diffX = x - that.lastMousePosition.x;
|
||||
var diffY = y - that.lastMousePosition.y;
|
||||
that.targetYaw -= MOUSE_SENSITIVITY * (diffX / 5.0);
|
||||
that.targetPitch += MOUSE_SENSITIVITY * (diffY / 10.0);
|
||||
|
||||
while (that.targetYaw > 180.0) that.targetYaw -= 360;
|
||||
while (that.targetYaw < -180.0) that.targetYaw += 360;
|
||||
|
||||
if (that.targetPitch > 90) that.targetPitch = 90;
|
||||
if (that.targetPitch < -90) that.targetPitch = -90;
|
||||
|
||||
that.updateCamera();
|
||||
} else if (that.mode === MODE_PAN) {
|
||||
var diffX = x - that.lastMousePosition.x;
|
||||
var diffY = y - that.lastMousePosition.y;
|
||||
|
||||
var up = Quat.getUp(Camera.getOrientation());
|
||||
var right = Quat.getRight(Camera.getOrientation());
|
||||
|
||||
up = Vec3.multiply(up, diffY * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);
|
||||
right = Vec3.multiply(right, -diffX * 0.01 * PAN_ZOOM_SCALE_RATIO * that.zoomDistance);
|
||||
|
||||
var dPosition = Vec3.sum(up, right);
|
||||
|
||||
that.moveFocalPoint(dPosition);
|
||||
}
|
||||
|
||||
var newX = x;
|
||||
var newY = y;
|
||||
var updatePosition = false;
|
||||
|
||||
if (x <= 0) {
|
||||
newX = Window.innerWidth;
|
||||
updatePosition = true;
|
||||
} else if (x >= Window.innerWidth) {
|
||||
newX = 0;
|
||||
updatePosition = true;
|
||||
}
|
||||
|
||||
if (y <= 0) {
|
||||
newY = Window.innerHeight;
|
||||
updatePosition = true;
|
||||
} else if (y >= Window.innerHeight) {
|
||||
newY = 0;
|
||||
updatePosition = true;
|
||||
}
|
||||
|
||||
if (updatePosition) {
|
||||
Reticle.setPosition({ x: newX, y: newY});
|
||||
}
|
||||
|
||||
that.lastMousePosition.x = newX;
|
||||
that.lastMousePosition.y = newY;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
hasDragged = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
that.mouseReleaseEvent = function(event) {
|
||||
|
||||
if (!that.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
that.mode = MODE_INACTIVE;
|
||||
Reticle.setVisible(true);
|
||||
|
||||
}
|
||||
|
||||
that.keyPressEvent = function(event) {
|
||||
var action = getActionForKeyEvent(event);
|
||||
if (action) {
|
||||
actions[action] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
that.keyReleaseEvent = function(event) {
|
||||
var action = getActionForKeyEvent(event);
|
||||
if (action) {
|
||||
actions[action] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
that.wheelEvent = function(event) {
|
||||
if (!that.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
var dZoom = -event.delta * SCROLL_SENSITIVITY;
|
||||
|
||||
// Scale based on current zoom level
|
||||
dZoom *= that.targetZoomDistance * ZOOM_SCALING;
|
||||
|
||||
that.targetZoomDistance = Math.min(Math.max(that.targetZoomDistance + dZoom, MIN_ZOOM_DISTANCE), MAX_ZOOM_DISTANCE);
|
||||
|
||||
that.updateCamera();
|
||||
}
|
||||
|
||||
that.updateCamera = function() {
|
||||
if (!that.enabled || Camera.mode !== "independent") {
|
||||
cameraTool.update();
|
||||
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 q = Quat.multiply(yRot, xRot);
|
||||
|
||||
var pos = Vec3.multiply(Quat.getForward(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
|
||||
});
|
||||
q = Quat.multiply(yRot, xRot);
|
||||
|
||||
if (easing) {
|
||||
var t = easeOutCubic(easingTime / EASE_TIME);
|
||||
q = Quat.slerp(startOrientation, q, t);
|
||||
}
|
||||
|
||||
Camera.setOrientation(q);
|
||||
|
||||
cameraTool.update();
|
||||
}
|
||||
|
||||
function normalizeDegrees(degrees) {
|
||||
while (degrees > 180) {
|
||||
degrees -= 360;
|
||||
}
|
||||
while (degrees < -180) {
|
||||
degrees += 360;
|
||||
}
|
||||
return degrees;
|
||||
}
|
||||
|
||||
// Ease the position and orbit of the camera
|
||||
that.update = function(dt) {
|
||||
if (Camera.mode !== "independent") {
|
||||
that.updateCamera();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update based on current actions
|
||||
that.targetYaw += (actions.orbitRight - actions.orbitLeft) * dt * KEY_ORBIT_SENSITIVITY;
|
||||
that.targetPitch += (actions.orbitUp - actions.orbitDown) * dt * KEY_ORBIT_SENSITIVITY;
|
||||
that.targetPitch = clamp(that.targetPitch, -90, 90);
|
||||
|
||||
var dZoom = actions.orbitBackward - actions.orbitForward;
|
||||
if (dZoom) {
|
||||
dZoom *= that.targetZoomDistance * dt * KEY_ZOOM_SENSITIVITY;
|
||||
that.targetZoomDistance += dZoom;
|
||||
that.targetZoomDistance = clamp(that.targetZoomDistance, MIN_ZOOM_DISTANCE, MAX_ZOOM_DISTANCE);
|
||||
}
|
||||
|
||||
if (easing) {
|
||||
easingTime = Math.min(EASE_TIME, easingTime + dt);
|
||||
}
|
||||
|
||||
var scale = Math.min(dt * EASING_MULTIPLIER, 1.0);
|
||||
|
||||
var dYaw = normalizeDegrees(that.targetYaw - that.yaw);
|
||||
var dPitch = normalizeDegrees(that.targetPitch - that.pitch);
|
||||
|
||||
that.yaw += scale * dYaw;
|
||||
that.pitch += scale * dPitch;
|
||||
|
||||
// Normalize between [-180, 180]
|
||||
that.yaw = normalizeDegrees(that.yaw);
|
||||
that.pitch = normalizeDegrees(that.pitch);
|
||||
|
||||
var dFocal = Vec3.subtract(that.targetFocalPoint, that.focalPoint);
|
||||
that.focalPoint = Vec3.sum(that.focalPoint, Vec3.multiply(scale, dFocal));
|
||||
|
||||
var dZoom = that.targetZoomDistance - that.zoomDistance;
|
||||
that.zoomDistance += scale * dZoom;
|
||||
|
||||
that.updateCamera();
|
||||
|
||||
if (easingTime >= 1) {
|
||||
easing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Last mode that was first or third person
|
||||
var lastAvatarCameraMode = "first person";
|
||||
Camera.modeUpdated.connect(function(newMode) {
|
||||
if (newMode != "independent") {
|
||||
lastAvatarCameraMode = newMode;
|
||||
that.disable(true);
|
||||
} else {
|
||||
that.enable();
|
||||
}
|
||||
});
|
||||
|
||||
Controller.keyReleaseEvent.connect(function(event) {
|
||||
if (event.text == "ESC" && that.enabled) {
|
||||
Camera.mode = lastAvatarCameraMode;
|
||||
cameraManager.disable(true);
|
||||
}
|
||||
});
|
||||
|
||||
Script.update.connect(that.update);
|
||||
Script.scriptEnding.connect(that.disable);
|
||||
|
||||
Controller.wheelEvent.connect(that.wheelEvent);
|
||||
|
||||
var cameraTool = new CameraTool(that);
|
||||
|
||||
return that;
|
||||
}
|
||||
|
||||
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 BORDER_WIDTH = 1;
|
||||
|
||||
var ORIENTATION_OVERLAY_SIZE = 26;
|
||||
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 UI_WIDTH = 70;
|
||||
var UI_HEIGHT = 70;
|
||||
var UI_PADDING = 10;
|
||||
|
||||
var lastKnownWidth = Window.innerWidth;
|
||||
|
||||
var uiPosition = {
|
||||
x: lastKnownWidth - UI_WIDTH - UI_PADDING,
|
||||
y: UI_PADDING,
|
||||
};
|
||||
|
||||
var backgroundBorder = Overlays.addOverlay("text", {
|
||||
x: uiPosition.x - BORDER_WIDTH,
|
||||
y: uiPosition.y - BORDER_WIDTH,
|
||||
width: UI_WIDTH + BORDER_WIDTH * 2,
|
||||
height: UI_HEIGHT + BORDER_WIDTH * 2,
|
||||
alpha: 0,
|
||||
text: "",
|
||||
backgroundColor: {
|
||||
red: 101,
|
||||
green: 101,
|
||||
blue: 101
|
||||
},
|
||||
backgroundAlpha: 1.0,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
var background = Overlays.addOverlay("text", {
|
||||
x: uiPosition.x,
|
||||
y: uiPosition.y,
|
||||
width: UI_WIDTH,
|
||||
height: UI_HEIGHT,
|
||||
alpha: 0,
|
||||
text: "",
|
||||
backgroundColor: {
|
||||
red: 51,
|
||||
green: 51,
|
||||
blue: 51
|
||||
},
|
||||
backgroundAlpha: 1.0,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
Overlays.deleteOverlay(background);
|
||||
Overlays.deleteOverlay(backgroundBorder);
|
||||
});
|
||||
|
||||
var flip = Quat.fromPitchYawRollDegrees(0, 180, 0);
|
||||
that.update = function() {
|
||||
if (Window.innerWidth != lastKnownWidth) {
|
||||
lastKnownWidth = Window.innerWidth;
|
||||
uiPosition = {
|
||||
x: lastKnownWidth - UI_WIDTH - UI_PADDING,
|
||||
y: UI_PADDING,
|
||||
};
|
||||
Overlays.editOverlay(backgroundBorder, {
|
||||
x: uiPosition.x - BORDER_WIDTH,
|
||||
y: uiPosition.y - BORDER_WIDTH,
|
||||
});
|
||||
Overlays.editOverlay(background, {
|
||||
x: uiPosition.x,
|
||||
y: uiPosition.y,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
that.mousePressEvent = function(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||
x: event.x,
|
||||
y: event.y
|
||||
});
|
||||
};
|
||||
|
||||
that.setVisible = function(visible) {
|
||||
Overlays.editOverlay(background, {
|
||||
visible: visible
|
||||
});
|
||||
Overlays.editOverlay(backgroundBorder, {
|
||||
visible: visible
|
||||
});
|
||||
};
|
||||
|
||||
that.setVisible(false);
|
||||
|
||||
return that;
|
||||
};
|
|
@ -0,0 +1,155 @@
|
|||
/* globals EntityIconOverlayManager:true */
|
||||
|
||||
EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) {
|
||||
|
||||
var visible = false;
|
||||
|
||||
// List of all created overlays
|
||||
var allOverlays = [];
|
||||
|
||||
// List of overlays not currently being used
|
||||
var unusedOverlays = [];
|
||||
|
||||
// Map from EntityItemID to overlay id
|
||||
var entityOverlays = {};
|
||||
|
||||
// Map from EntityItemID to EntityItemID object
|
||||
var entityIDs = {};
|
||||
|
||||
this.updatePositions = function(ids) {
|
||||
for (var id in entityIDs) {
|
||||
var entityID = entityIDs[id];
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
var overlayProperties = {
|
||||
position: properties.position
|
||||
};
|
||||
if (getOverlayPropertiesFunc) {
|
||||
var customProperties = getOverlayPropertiesFunc(entityID, properties);
|
||||
for (var key in customProperties) {
|
||||
overlayProperties[key] = customProperties[key];
|
||||
}
|
||||
}
|
||||
Overlays.editOverlay(entityOverlays[entityID], overlayProperties);
|
||||
}
|
||||
};
|
||||
|
||||
// Finds the id for the corresponding entity that is associated with an overlay id.
|
||||
// Returns null if the overlay id is not contained in this manager.
|
||||
this.findEntity = function(overlayId) {
|
||||
for (var id in entityOverlays) {
|
||||
if (overlayId === entityOverlays[id]) {
|
||||
return entityIDs[id];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
this.findRayIntersection = function(pickRay) {
|
||||
var result = Overlays.findRayIntersection(pickRay);
|
||||
|
||||
if (result.intersects) {
|
||||
result.entityID = this.findEntity(result.overlayID);
|
||||
|
||||
if (result.entityID === null) {
|
||||
result.intersects = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
this.setVisible = function(isVisible) {
|
||||
if (visible !== isVisible) {
|
||||
visible = isVisible;
|
||||
for (var id in entityOverlays) {
|
||||
Overlays.editOverlay(entityOverlays[id], {
|
||||
visible: visible,
|
||||
ignorePickIntersection: !visible
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Allocate or get an unused overlay
|
||||
function getOverlay() {
|
||||
var overlay;
|
||||
if (unusedOverlays.length === 0) {
|
||||
overlay = Overlays.addOverlay("image3d", {});
|
||||
allOverlays.push(overlay);
|
||||
} else {
|
||||
overlay = unusedOverlays.pop();
|
||||
}
|
||||
return overlay;
|
||||
}
|
||||
|
||||
function releaseOverlay(overlay) {
|
||||
unusedOverlays.push(overlay);
|
||||
Overlays.editOverlay(overlay, {
|
||||
visible: false,
|
||||
ignorePickIntersection: true
|
||||
});
|
||||
}
|
||||
|
||||
function addEntity(entityID) {
|
||||
var properties = Entities.getEntityProperties(entityID, ['position', 'type']);
|
||||
if (entityTypes.indexOf(properties.type) > -1 && !(entityID in entityOverlays)) {
|
||||
var overlay = getOverlay();
|
||||
entityOverlays[entityID] = overlay;
|
||||
entityIDs[entityID] = entityID;
|
||||
var overlayProperties = {
|
||||
position: properties.position,
|
||||
rotation: Quat.fromPitchYawRollDegrees(0, 0, 270),
|
||||
visible: visible,
|
||||
ignorePickIntersection: !visible,
|
||||
alpha: 0.9,
|
||||
scale: 0.5,
|
||||
drawInFront: true,
|
||||
isFacingAvatar: true,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
};
|
||||
if (getOverlayPropertiesFunc) {
|
||||
var customProperties = getOverlayPropertiesFunc(entityID, properties);
|
||||
for (var key in customProperties) {
|
||||
overlayProperties[key] = customProperties[key];
|
||||
}
|
||||
}
|
||||
Overlays.editOverlay(overlay, overlayProperties);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteEntity(entityID) {
|
||||
if (entityID in entityOverlays) {
|
||||
releaseOverlay(entityOverlays[entityID]);
|
||||
delete entityOverlays[entityID];
|
||||
}
|
||||
}
|
||||
|
||||
function clearEntities() {
|
||||
for (var id in entityOverlays) {
|
||||
releaseOverlay(entityOverlays[id]);
|
||||
}
|
||||
entityOverlays = {};
|
||||
entityIDs = {};
|
||||
}
|
||||
|
||||
Entities.addingEntity.connect(addEntity);
|
||||
Entities.deletingEntity.connect(deleteEntity);
|
||||
Entities.clearingEntities.connect(clearEntities);
|
||||
|
||||
// Add existing entities
|
||||
var ids = Entities.findEntities(MyAvatar.position, 64000);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
addEntity(ids[i]);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
for (var i = 0; i < allOverlays.length; i++) {
|
||||
Overlays.deleteOverlay(allOverlays[i]);
|
||||
}
|
||||
});
|
||||
};
|
330
scripts/simplifiedUI/system/libraries/entityList.js
Normal file
330
scripts/simplifiedUI/system/libraries/entityList.js
Normal file
|
@ -0,0 +1,330 @@
|
|||
"use strict";
|
||||
|
||||
// entityList.js
|
||||
//
|
||||
// Copyright 2014 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 EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages,
|
||||
cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible,
|
||||
keyUpEventFromUIWindow, Script, SelectionDisplay, SelectionManager, Clipboard */
|
||||
|
||||
var PROFILING_ENABLED = false;
|
||||
var profileIndent = '';
|
||||
const PROFILE_NOOP = function(_name, fn, args) {
|
||||
fn.apply(this, args);
|
||||
};
|
||||
const PROFILE = !PROFILING_ENABLED ? PROFILE_NOOP : function(name, fn, args) {
|
||||
console.log("PROFILE-Script " + profileIndent + "(" + name + ") Begin");
|
||||
var previousIndent = profileIndent;
|
||||
profileIndent += ' ';
|
||||
var before = Date.now();
|
||||
fn.apply(this, args);
|
||||
var delta = Date.now() - before;
|
||||
profileIndent = previousIndent;
|
||||
console.log("PROFILE-Script " + profileIndent + "(" + name + ") End " + delta + "ms");
|
||||
};
|
||||
|
||||
EntityListTool = function(shouldUseEditTabletApp) {
|
||||
var that = {};
|
||||
|
||||
var CreateWindow = Script.require('../modules/createWindow.js');
|
||||
|
||||
var TITLE_OFFSET = 60;
|
||||
var ENTITY_LIST_WIDTH = 495;
|
||||
var MAX_DEFAULT_CREATE_TOOLS_HEIGHT = 778;
|
||||
var entityListWindow = new CreateWindow(
|
||||
Script.resolvePath("EditEntityList.qml"),
|
||||
'Entity List',
|
||||
'com.highfidelity.create.entityListWindow',
|
||||
function () {
|
||||
var windowHeight = Window.innerHeight - TITLE_OFFSET;
|
||||
if (windowHeight > MAX_DEFAULT_CREATE_TOOLS_HEIGHT) {
|
||||
windowHeight = MAX_DEFAULT_CREATE_TOOLS_HEIGHT;
|
||||
}
|
||||
return {
|
||||
size: {
|
||||
x: ENTITY_LIST_WIDTH,
|
||||
y: windowHeight
|
||||
},
|
||||
position: {
|
||||
x: Window.x,
|
||||
y: Window.y + TITLE_OFFSET
|
||||
}
|
||||
};
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
var webView = null;
|
||||
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
webView.setVisible = function(value){ };
|
||||
|
||||
var filterInView = false;
|
||||
var searchRadius = 100;
|
||||
|
||||
var visible = false;
|
||||
|
||||
that.webView = webView;
|
||||
|
||||
that.setVisible = function(newVisible) {
|
||||
visible = newVisible;
|
||||
webView.setVisible(shouldUseEditTabletApp() && visible);
|
||||
entityListWindow.setVisible(!shouldUseEditTabletApp() && visible);
|
||||
};
|
||||
|
||||
that.isVisible = function() {
|
||||
return entityListWindow.isVisible();
|
||||
};
|
||||
|
||||
that.setVisible(false);
|
||||
|
||||
function emitJSONScriptEvent(data) {
|
||||
var dataString;
|
||||
PROFILE("Script-JSON.stringify", function() {
|
||||
dataString = JSON.stringify(data);
|
||||
});
|
||||
PROFILE("Script-emitScriptEvent", function() {
|
||||
webView.emitScriptEvent(dataString);
|
||||
if (entityListWindow.window) {
|
||||
entityListWindow.window.emitScriptEvent(dataString);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
that.toggleVisible = function() {
|
||||
that.setVisible(!visible);
|
||||
};
|
||||
|
||||
selectionManager.addEventListener(function(isSelectionUpdate, caller) {
|
||||
if (caller === that) {
|
||||
// ignore events that we emitted from the entity list itself
|
||||
return;
|
||||
}
|
||||
var selectedIDs = [];
|
||||
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
selectedIDs.push(selectionManager.selections[i]);
|
||||
}
|
||||
|
||||
emitJSONScriptEvent({
|
||||
type: 'selectionUpdate',
|
||||
selectedIDs: selectedIDs
|
||||
});
|
||||
});
|
||||
|
||||
that.setSpaceMode = function(spaceMode) {
|
||||
emitJSONScriptEvent({
|
||||
type: 'setSpaceMode',
|
||||
spaceMode: spaceMode
|
||||
});
|
||||
};
|
||||
|
||||
that.clearEntityList = function() {
|
||||
emitJSONScriptEvent({
|
||||
type: 'clearEntityList'
|
||||
});
|
||||
};
|
||||
|
||||
that.removeEntities = function (deletedIDs, selectedIDs) {
|
||||
emitJSONScriptEvent({
|
||||
type: 'removeEntities',
|
||||
deletedIDs: deletedIDs,
|
||||
selectedIDs: selectedIDs
|
||||
});
|
||||
};
|
||||
|
||||
that.deleteEntities = function (deletedIDs) {
|
||||
emitJSONScriptEvent({
|
||||
type: "deleted",
|
||||
ids: deletedIDs
|
||||
});
|
||||
};
|
||||
|
||||
function valueIfDefined(value) {
|
||||
return value !== undefined ? value : "";
|
||||
}
|
||||
|
||||
function entityIsBaked(properties) {
|
||||
if (properties.type === "Model") {
|
||||
var lowerModelURL = properties.modelURL.toLowerCase();
|
||||
return lowerModelURL.endsWith(".baked.fbx") || lowerModelURL.endsWith(".baked.fst");
|
||||
} else if (properties.type === "Zone") {
|
||||
var lowerSkyboxURL = properties.skybox ? properties.skybox.url.toLowerCase() : "";
|
||||
var lowerAmbientURL = properties.ambientLight ? properties.ambientLight.ambientURL.toLowerCase() : "";
|
||||
return (lowerSkyboxURL === "" || lowerSkyboxURL.endsWith(".texmeta.json")) &&
|
||||
(lowerAmbientURL === "" || lowerAmbientURL.endsWith(".texmeta.json"));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
that.sendUpdate = function() {
|
||||
PROFILE('Script-sendUpdate', function() {
|
||||
var entities = [];
|
||||
|
||||
var ids;
|
||||
PROFILE("findEntities", function() {
|
||||
if (filterInView) {
|
||||
ids = Entities.findEntitiesInFrustum(Camera.frustum);
|
||||
} else {
|
||||
ids = Entities.findEntities(MyAvatar.position, searchRadius);
|
||||
}
|
||||
});
|
||||
|
||||
var cameraPosition = Camera.position;
|
||||
PROFILE("getMultipleProperties", function () {
|
||||
var multipleProperties = Entities.getMultipleEntityProperties(ids, ['name', 'type', 'locked',
|
||||
'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID',
|
||||
'skybox.url', 'ambientLight.url']);
|
||||
for (var i = 0; i < multipleProperties.length; i++) {
|
||||
var properties = multipleProperties[i];
|
||||
|
||||
if (!filterInView || Vec3.distance(properties.position, cameraPosition) <= searchRadius) {
|
||||
var url = "";
|
||||
if (properties.type === "Model") {
|
||||
url = properties.modelURL;
|
||||
} else if (properties.type === "Material") {
|
||||
url = properties.materialURL;
|
||||
} else if (properties.type === "Image") {
|
||||
url = properties.imageURL;
|
||||
}
|
||||
entities.push({
|
||||
id: ids[i],
|
||||
name: properties.name,
|
||||
type: properties.type,
|
||||
url: url,
|
||||
locked: properties.locked,
|
||||
visible: properties.visible,
|
||||
certificateID: properties.certificateID,
|
||||
verticesCount: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.verticesCount) : ""),
|
||||
texturesCount: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.texturesCount) : ""),
|
||||
texturesSize: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.texturesSize) : ""),
|
||||
hasTransparent: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.hasTransparent) : ""),
|
||||
isBaked: entityIsBaked(properties),
|
||||
drawCalls: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.drawCalls) : ""),
|
||||
hasScript: properties.script !== ""
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var selectedIDs = [];
|
||||
for (var j = 0; j < selectionManager.selections.length; j++) {
|
||||
selectedIDs.push(selectionManager.selections[j]);
|
||||
}
|
||||
|
||||
emitJSONScriptEvent({
|
||||
type: "update",
|
||||
entities: entities,
|
||||
selectedIDs: selectedIDs,
|
||||
spaceMode: SelectionDisplay.getSpaceMode(),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function onFileSaveChanged(filename) {
|
||||
Window.saveFileChanged.disconnect(onFileSaveChanged);
|
||||
if (filename !== "") {
|
||||
var success = Clipboard.exportEntities(filename, selectionManager.selections);
|
||||
if (!success) {
|
||||
Window.notifyEditError("Export failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var onWebEventReceived = function(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
print("entityList.js: Error parsing JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type === "selectionUpdate") {
|
||||
var ids = data.entityIds;
|
||||
var entityIDs = [];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
entityIDs.push(ids[i]);
|
||||
}
|
||||
selectionManager.setSelections(entityIDs, that);
|
||||
if (data.focus) {
|
||||
cameraManager.enable();
|
||||
cameraManager.focus(selectionManager.worldPosition,
|
||||
selectionManager.worldDimensions,
|
||||
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
}
|
||||
} else if (data.type === "refresh") {
|
||||
that.sendUpdate();
|
||||
} else if (data.type === "teleport") {
|
||||
if (selectionManager.hasSelection()) {
|
||||
MyAvatar.position = selectionManager.worldPosition;
|
||||
}
|
||||
} else if (data.type === "export") {
|
||||
if (!selectionManager.hasSelection()) {
|
||||
Window.notifyEditError("No entities have been selected.");
|
||||
} else {
|
||||
Window.saveFileChanged.connect(onFileSaveChanged);
|
||||
Window.saveAsync("Select Where to Save", "", "*.json");
|
||||
}
|
||||
} else if (data.type === "pal") {
|
||||
var sessionIds = {}; // Collect the sessionsIds of all selected entities, w/o duplicates.
|
||||
selectionManager.selections.forEach(function (id) {
|
||||
var lastEditedBy = Entities.getEntityProperties(id, 'lastEditedBy').lastEditedBy;
|
||||
if (lastEditedBy) {
|
||||
sessionIds[lastEditedBy] = true;
|
||||
}
|
||||
});
|
||||
var dedupped = Object.keys(sessionIds);
|
||||
if (!selectionManager.selections.length) {
|
||||
Window.alert('No objects selected.');
|
||||
} else if (!dedupped.length) {
|
||||
Window.alert('There were no recent users of the ' + selectionManager.selections.length + ' selected objects.');
|
||||
} else {
|
||||
// No need to subscribe if we're just sending.
|
||||
Messages.sendMessage('com.highfidelity.pal', JSON.stringify({method: 'select', params: [dedupped, true, false]}), 'local');
|
||||
}
|
||||
} else if (data.type === "delete") {
|
||||
deleteSelectedEntities();
|
||||
} else if (data.type === "toggleLocked") {
|
||||
toggleSelectedEntitiesLocked();
|
||||
} else if (data.type === "toggleVisible") {
|
||||
toggleSelectedEntitiesVisible();
|
||||
} else if (data.type === "filterInView") {
|
||||
filterInView = data.filterInView === true;
|
||||
} else if (data.type === "radius") {
|
||||
searchRadius = data.radius;
|
||||
} else if (data.type === "cut") {
|
||||
SelectionManager.cutSelectedEntities();
|
||||
} else if (data.type === "copy") {
|
||||
SelectionManager.copySelectedEntities();
|
||||
} else if (data.type === "paste") {
|
||||
SelectionManager.pasteEntities();
|
||||
} else if (data.type === "duplicate") {
|
||||
SelectionManager.duplicateSelection();
|
||||
that.sendUpdate();
|
||||
} else if (data.type === "rename") {
|
||||
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();
|
||||
} else if (data.type === 'keyUpEvent') {
|
||||
keyUpEventFromUIWindow(data.keyUpEvent);
|
||||
}
|
||||
};
|
||||
|
||||
webView.webEventReceived.connect(onWebEventReceived);
|
||||
entityListWindow.webEventReceived.addListener(onWebEventReceived);
|
||||
that.interactiveWindowHidden = entityListWindow.interactiveWindowHidden;
|
||||
|
||||
return that;
|
||||
};
|
2925
scripts/simplifiedUI/system/libraries/entitySelectionTool.js
Normal file
2925
scripts/simplifiedUI/system/libraries/entitySelectionTool.js
Normal file
File diff suppressed because it is too large
Load diff
13
scripts/simplifiedUI/system/libraries/globals.js
Normal file
13
scripts/simplifiedUI/system/libraries/globals.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// globals.js
|
||||
// examples/libraries
|
||||
//
|
||||
// Copyright 2014 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
|
||||
//
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
|
||||
isInterstitialOverlaysVisible = false;
|
311
scripts/simplifiedUI/system/libraries/gridTool.js
Normal file
311
scripts/simplifiedUI/system/libraries/gridTool.js
Normal file
|
@ -0,0 +1,311 @@
|
|||
/* global keyUpEventFromUIWindow */
|
||||
|
||||
var GRID_CONTROLS_HTML_URL = Script.resolvePath('../html/gridControls.html');
|
||||
|
||||
Grid = function() {
|
||||
var that = {};
|
||||
var gridColor = { red: 0, green: 0, blue: 0 };
|
||||
var gridAlpha = 0.6;
|
||||
var origin = { x: 0, y: +MyAvatar.getJointPosition('LeftToeBase').y.toFixed(1) + 0.1, z: 0 };
|
||||
var scale = 500;
|
||||
var minorGridEvery = 1.0;
|
||||
var majorGridEvery = 5;
|
||||
var halfSize = 40;
|
||||
|
||||
var worldSize = 16384;
|
||||
|
||||
var snapToGrid = false;
|
||||
|
||||
var gridOverlay = Overlays.addOverlay("grid", {
|
||||
rotation: Quat.fromPitchYawRollDegrees(90, 0, 0),
|
||||
dimensions: { x: scale, y: scale, z: scale },
|
||||
position: origin,
|
||||
visible: false,
|
||||
drawInFront: false,
|
||||
color: gridColor,
|
||||
alpha: gridAlpha,
|
||||
minorGridEvery: minorGridEvery,
|
||||
majorGridEvery: majorGridEvery,
|
||||
ignorePickIntersection: true
|
||||
});
|
||||
|
||||
that.visible = false;
|
||||
that.enabled = false;
|
||||
|
||||
that.getOrigin = function() {
|
||||
return origin;
|
||||
};
|
||||
|
||||
that.getMinorIncrement = function() {
|
||||
return minorGridEvery;
|
||||
};
|
||||
|
||||
that.setMinorIncrement = function(value) {
|
||||
minorGridEvery = value;
|
||||
updateGrid();
|
||||
};
|
||||
|
||||
that.getMajorIncrement = function() {
|
||||
return majorGridEvery;
|
||||
};
|
||||
|
||||
that.setMajorIncrement = function(value) {
|
||||
majorGridEvery = value;
|
||||
updateGrid();
|
||||
};
|
||||
|
||||
that.getColor = function() {
|
||||
return gridColor;
|
||||
};
|
||||
|
||||
that.setColor = function(value) {
|
||||
gridColor = value;
|
||||
updateGrid();
|
||||
};
|
||||
|
||||
that.getSnapToGrid = function() {
|
||||
return snapToGrid;
|
||||
};
|
||||
that.setSnapToGrid = function(value) {
|
||||
snapToGrid = value;
|
||||
that.emitUpdate();
|
||||
};
|
||||
|
||||
that.setEnabled = function(enabled) {
|
||||
that.enabled = enabled;
|
||||
updateGrid();
|
||||
};
|
||||
|
||||
that.getVisible = function() {
|
||||
return that.visible;
|
||||
};
|
||||
that.setVisible = function(visible, noUpdate) {
|
||||
that.visible = visible;
|
||||
updateGrid();
|
||||
|
||||
if (!noUpdate) {
|
||||
that.emitUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
that.snapToSurface = function(position, dimensions, registration) {
|
||||
if (!snapToGrid) {
|
||||
return position;
|
||||
}
|
||||
|
||||
if (dimensions === undefined) {
|
||||
dimensions = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
|
||||
if (registration === undefined) {
|
||||
registration = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
}
|
||||
|
||||
return {
|
||||
x: position.x,
|
||||
y: origin.y + (registration.y * dimensions.y),
|
||||
z: position.z
|
||||
};
|
||||
};
|
||||
|
||||
that.snapToGrid = function(position, majorOnly, dimensions, registration) {
|
||||
if (!snapToGrid) {
|
||||
return position;
|
||||
}
|
||||
|
||||
if (dimensions === undefined) {
|
||||
dimensions = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
|
||||
if (registration === undefined) {
|
||||
registration = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
}
|
||||
|
||||
var spacing = majorOnly ? majorGridEvery : minorGridEvery;
|
||||
|
||||
position = Vec3.subtract(position, origin);
|
||||
|
||||
position.x = Math.round(position.x / spacing) * spacing;
|
||||
position.y = Math.round(position.y / spacing) * spacing;
|
||||
position.z = Math.round(position.z / spacing) * spacing;
|
||||
|
||||
return Vec3.sum(Vec3.sum(position, Vec3.multiplyVbyV(registration, dimensions)), origin);
|
||||
};
|
||||
|
||||
that.snapToSpacing = function(delta, majorOnly) {
|
||||
if (!snapToGrid) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
var spacing = majorOnly ? majorGridEvery : minorGridEvery;
|
||||
|
||||
var snappedDelta = {
|
||||
x: Math.round(delta.x / spacing) * spacing,
|
||||
y: Math.round(delta.y / spacing) * spacing,
|
||||
z: Math.round(delta.z / spacing) * spacing
|
||||
};
|
||||
|
||||
return snappedDelta;
|
||||
};
|
||||
|
||||
|
||||
that.setPosition = function(newPosition, noUpdate) {
|
||||
origin = { x: 0, y: newPosition.y, z: 0 };
|
||||
updateGrid();
|
||||
|
||||
if (!noUpdate) {
|
||||
that.emitUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
that.moveToSelection = function() {
|
||||
var newPosition = SelectionManager.worldPosition;
|
||||
newPosition = Vec3.subtract(newPosition, { x: 0, y: SelectionManager.worldDimensions.y * 0.5, z: 0 });
|
||||
that.setPosition(newPosition);
|
||||
};
|
||||
|
||||
that.emitUpdate = function() {
|
||||
if (that.onUpdate) {
|
||||
that.onUpdate({
|
||||
origin: origin,
|
||||
minorGridEvery: minorGridEvery,
|
||||
majorGridEvery: majorGridEvery,
|
||||
gridSize: halfSize,
|
||||
visible: that.visible,
|
||||
snapToGrid: snapToGrid
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
that.update = function(data) {
|
||||
if (data.snapToGrid !== undefined) {
|
||||
snapToGrid = data.snapToGrid;
|
||||
}
|
||||
|
||||
if (data.origin) {
|
||||
var pos = data.origin;
|
||||
pos.x = pos.x === undefined ? origin.x : parseFloat(pos.x);
|
||||
pos.y = pos.y === undefined ? origin.y : parseFloat(pos.y);
|
||||
pos.z = pos.z === undefined ? origin.z : parseFloat(pos.z);
|
||||
that.setPosition(pos, true);
|
||||
}
|
||||
|
||||
if (data.minorGridEvery) {
|
||||
minorGridEvery = data.minorGridEvery;
|
||||
}
|
||||
|
||||
if (data.majorGridEvery) {
|
||||
majorGridEvery = data.majorGridEvery;
|
||||
}
|
||||
|
||||
if (data.gridColor) {
|
||||
gridColor = data.gridColor;
|
||||
}
|
||||
|
||||
if (data.gridSize) {
|
||||
halfSize = data.gridSize;
|
||||
}
|
||||
|
||||
if (data.visible !== undefined) {
|
||||
that.setVisible(data.visible, true);
|
||||
}
|
||||
|
||||
updateGrid(true);
|
||||
};
|
||||
|
||||
function updateGrid(noUpdate) {
|
||||
Overlays.editOverlay(gridOverlay, {
|
||||
position: { x: 0, y: origin.y, z: 0 },
|
||||
visible: that.visible && that.enabled,
|
||||
minorGridEvery: minorGridEvery,
|
||||
majorGridEvery: majorGridEvery,
|
||||
color: gridColor,
|
||||
alpha: gridAlpha
|
||||
});
|
||||
|
||||
if (!noUpdate) {
|
||||
that.emitUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Overlays.deleteOverlay(gridOverlay);
|
||||
}
|
||||
|
||||
that.addListener = function(callback) {
|
||||
that.onUpdate = callback;
|
||||
};
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
updateGrid();
|
||||
|
||||
that.onUpdate = null;
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
GridTool = function(opts) {
|
||||
var that = {};
|
||||
|
||||
var horizontalGrid = opts.horizontalGrid;
|
||||
var verticalGrid = opts.verticalGrid;
|
||||
var createToolsWindow = opts.createToolsWindow;
|
||||
var shouldUseEditTabletApp = opts.shouldUseEditTabletApp;
|
||||
var listeners = [];
|
||||
|
||||
var webView = null;
|
||||
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
webView.setVisible = function(value) { };
|
||||
|
||||
horizontalGrid.addListener(function(data) {
|
||||
var dataString = JSON.stringify(data);
|
||||
webView.emitScriptEvent(dataString);
|
||||
createToolsWindow.emitScriptEvent(dataString);
|
||||
if (selectionDisplay) {
|
||||
selectionDisplay.updateHandles();
|
||||
}
|
||||
});
|
||||
|
||||
var webEventReceived = function(data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type === "init") {
|
||||
horizontalGrid.emitUpdate();
|
||||
} else if (data.type === "update") {
|
||||
horizontalGrid.update(data);
|
||||
for (var i = 0; i < listeners.length; i++) {
|
||||
listeners[i] && listeners[i](data);
|
||||
}
|
||||
} else if (data.type === "action") {
|
||||
var action = data.action;
|
||||
if (action === "moveToAvatar") {
|
||||
var position = MyAvatar.getJointPosition("LeftFoot");
|
||||
if (position.x === 0 && position.y === 0 && position.z === 0) {
|
||||
position = MyAvatar.position;
|
||||
}
|
||||
horizontalGrid.setPosition(position);
|
||||
} else if (action === "moveToSelection") {
|
||||
horizontalGrid.moveToSelection();
|
||||
}
|
||||
} else if (data.type === 'keyUpEvent') {
|
||||
keyUpEventFromUIWindow(data.keyUpEvent);
|
||||
}
|
||||
};
|
||||
|
||||
webView.webEventReceived.connect(webEventReceived);
|
||||
createToolsWindow.webEventReceived.addListener(webEventReceived);
|
||||
|
||||
that.addListener = function(callback) {
|
||||
listeners.push(callback);
|
||||
};
|
||||
|
||||
that.setVisible = function(visible) {
|
||||
webView.setVisible(shouldUseEditTabletApp() && visible);
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
74
scripts/simplifiedUI/system/libraries/overlayUtils.js
Normal file
74
scripts/simplifiedUI/system/libraries/overlayUtils.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// overlayUtils.js
|
||||
// examples/libraries
|
||||
//
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel.
|
||||
//
|
||||
// OverlayGroup provides a way to create composite overlays and control their
|
||||
// position relative to a settable rootPosition and rootRotation.
|
||||
//
|
||||
OverlayGroup = function(opts) {
|
||||
var that = {};
|
||||
|
||||
var overlays = {};
|
||||
|
||||
var rootPosition = opts.position || { x: 0, y: 0, z: 0 };
|
||||
var rootRotation = opts.rotation || Quat.IDENTITY;
|
||||
var visible = opts.visible == true;
|
||||
|
||||
function updateOverlays() {
|
||||
for (overlayID in overlays) {
|
||||
var overlay = overlays[overlayID];
|
||||
var newPosition = Vec3.multiplyQbyV(rootRotation, overlay.position);
|
||||
newPosition = Vec3.sum(rootPosition, newPosition);
|
||||
Overlays.editOverlay(overlayID, {
|
||||
visible: visible,
|
||||
position: newPosition,
|
||||
rotation: Quat.multiply(rootRotation, overlay.rotation),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
that.createOverlay = function(type, properties) {
|
||||
properties.position = properties.position || { x: 0, y: 0, z: 0 };
|
||||
properties.rotation = properties.rotation || Quat.IDENTITY;
|
||||
|
||||
var overlay = Overlays.addOverlay(type, properties);
|
||||
|
||||
overlays[overlay] = {
|
||||
position: properties.position,
|
||||
rotation: properties.rotation,
|
||||
};
|
||||
|
||||
updateOverlays();
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
that.setProperties = function(properties) {
|
||||
if (properties.position !== undefined) {
|
||||
rootPosition = properties.position;
|
||||
}
|
||||
if (properties.rotation !== undefined) {
|
||||
rootRotation = properties.rotation;
|
||||
}
|
||||
if (properties.visible !== undefined) {
|
||||
visible = properties.visible;
|
||||
}
|
||||
updateOverlays();
|
||||
};
|
||||
|
||||
that.destroy = function() {
|
||||
for (var overlay in overlays) {
|
||||
Overlays.deleteOverlay(overlay);
|
||||
}
|
||||
overlays = {};
|
||||
}
|
||||
|
||||
return that;
|
||||
};
|
204
scripts/simplifiedUI/system/libraries/pointersUtils.js
Normal file
204
scripts/simplifiedUI/system/libraries/pointersUtils.js
Normal file
|
@ -0,0 +1,204 @@
|
|||
"use strict";
|
||||
|
||||
// pointerUtils.js
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
/* jslint bitwise: true */
|
||||
|
||||
/* global Script, Pointers,
|
||||
DEFAULT_SEARCH_SPHERE_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE,
|
||||
COLORS_GRAB_DISTANCE_HOLD, TRIGGER_ON_VALUE,
|
||||
Pointer:true, PointerManager:true
|
||||
*/
|
||||
|
||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||
Pointer = function(hudLayer, pickType, pointerData) {
|
||||
this.SEARCH_SPHERE_SIZE = 0.0132;
|
||||
this.dim = {x: this.SEARCH_SPHERE_SIZE, y: this.SEARCH_SPHERE_SIZE, z: this.SEARCH_SPHERE_SIZE};
|
||||
this.halfPath = {
|
||||
type: "line3d",
|
||||
color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE,
|
||||
visible: true,
|
||||
alpha: 1,
|
||||
solid: true,
|
||||
glow: 1.0,
|
||||
ignoreRayIntersection: true, // always ignore this
|
||||
drawInFront: !hudLayer, // Even when burried inside of something, show it.
|
||||
drawHUDLayer: hudLayer,
|
||||
};
|
||||
this.halfEnd = {
|
||||
type: "sphere",
|
||||
dimensions: this.dim,
|
||||
solid: true,
|
||||
color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE,
|
||||
alpha: 0.9,
|
||||
ignoreRayIntersection: true,
|
||||
drawInFront: !hudLayer, // Even when burried inside of something, show it.
|
||||
drawHUDLayer: hudLayer,
|
||||
visible: true
|
||||
};
|
||||
this.fullPath = {
|
||||
type: "line3d",
|
||||
color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE,
|
||||
visible: true,
|
||||
alpha: 1,
|
||||
solid: true,
|
||||
glow: 1.0,
|
||||
ignoreRayIntersection: true, // always ignore this
|
||||
drawInFront: !hudLayer, // Even when burried inside of something, show it.
|
||||
drawHUDLayer: hudLayer,
|
||||
};
|
||||
this.fullEnd = {
|
||||
type: "sphere",
|
||||
dimensions: this.dim,
|
||||
solid: true,
|
||||
color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE,
|
||||
alpha: 0.9,
|
||||
ignoreRayIntersection: true,
|
||||
drawInFront: !hudLayer, // Even when burried inside of something, show it.
|
||||
drawHUDLayer: hudLayer,
|
||||
visible: true
|
||||
};
|
||||
this.holdPath = {
|
||||
type: "line3d",
|
||||
color: COLORS_GRAB_DISTANCE_HOLD,
|
||||
visible: true,
|
||||
alpha: 1,
|
||||
solid: true,
|
||||
glow: 1.0,
|
||||
ignoreRayIntersection: true, // always ignore this
|
||||
drawInFront: !hudLayer, // Even when burried inside of something, show it.
|
||||
drawHUDLayer: hudLayer,
|
||||
};
|
||||
|
||||
this.renderStates = [
|
||||
{name: "half", path: this.halfPath, end: this.halfEnd},
|
||||
{name: "full", path: this.fullPath, end: this.fullEnd},
|
||||
{name: "hold", path: this.holdPath}
|
||||
];
|
||||
|
||||
this.defaultRenderStates = [
|
||||
{name: "half", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: this.halfPath},
|
||||
{name: "full", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: this.fullPath},
|
||||
{name: "hold", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: this.holdPath}
|
||||
];
|
||||
|
||||
|
||||
this.pointerID = null;
|
||||
this.visible = false;
|
||||
this.locked = false;
|
||||
this.allwaysOn = false;
|
||||
this.hand = pointerData.hand;
|
||||
delete pointerData.hand;
|
||||
|
||||
function createPointer(pickType, pointerData) {
|
||||
var pointerID = Pointers.createPointer(pickType, pointerData);
|
||||
Pointers.setRenderState(pointerID, "");
|
||||
Pointers.enablePointer(pointerID);
|
||||
return pointerID;
|
||||
}
|
||||
|
||||
this.enable = function() {
|
||||
Pointers.enablePointer(this.pointerID);
|
||||
};
|
||||
|
||||
this.disable = function() {
|
||||
Pointers.disablePointer(this.pointerID);
|
||||
};
|
||||
|
||||
this.removePointer = function() {
|
||||
Pointers.removePointer(this.pointerID);
|
||||
};
|
||||
|
||||
this.makeVisible = function() {
|
||||
this.visible = true;
|
||||
};
|
||||
|
||||
this.makeInvisible = function() {
|
||||
this.visible = false;
|
||||
};
|
||||
|
||||
this.lockEnd = function(lockData) {
|
||||
if (lockData !== undefined) {
|
||||
if (this.visible && !this.locked && lockData.targetID !== null) {
|
||||
var targetID = lockData.targetID;
|
||||
var targetIsOverlay = lockData.isOverlay;
|
||||
if (lockData.offset === undefined) {
|
||||
Pointers.setLockEndUUID(this.pointerID, targetID, targetIsOverlay);
|
||||
} else {
|
||||
Pointers.setLockEndUUID(this.pointerID, targetID, targetIsOverlay, lockData.offset);
|
||||
}
|
||||
this.locked = targetID;
|
||||
}
|
||||
} else if (this.locked) {
|
||||
Pointers.setLockEndUUID(this.pointerID, null, false);
|
||||
this.locked = false;
|
||||
}
|
||||
};
|
||||
|
||||
this.updateRenderState = function(triggerClicks, triggerValues) {
|
||||
var mode = "";
|
||||
if (this.visible) {
|
||||
if (this.locked) {
|
||||
mode = "hold";
|
||||
} else if (triggerClicks[this.hand]) {
|
||||
mode = "full";
|
||||
} else if (triggerValues[this.hand] > TRIGGER_ON_VALUE || this.alwaysOn) {
|
||||
mode = "half";
|
||||
}
|
||||
}
|
||||
|
||||
Pointers.setRenderState(this.pointerID, mode);
|
||||
};
|
||||
pointerData.renderStates = this.renderStates;
|
||||
pointerData.defaultRenderStates = this.defaultRenderStates;
|
||||
this.pointerID = createPointer(pickType, pointerData);
|
||||
};
|
||||
|
||||
|
||||
PointerManager = function() {
|
||||
this.pointers = [];
|
||||
|
||||
this.createPointer = function(hudLayer, pickType, pointerData) {
|
||||
var pointer = new Pointer(hudLayer, pickType, pointerData);
|
||||
this.pointers.push(pointer);
|
||||
return pointer.pointerID;
|
||||
};
|
||||
|
||||
this.makePointerVisible = function(laserParams) {
|
||||
var index = laserParams.hand;
|
||||
if (index < this.pointers.length && index >= 0) {
|
||||
this.pointers[index].makeVisible();
|
||||
this.pointers[index].alwaysOn = laserParams.alwaysOn;
|
||||
}
|
||||
};
|
||||
|
||||
this.makePointerInvisible = function(laserParams) {
|
||||
var index = laserParams.hand;
|
||||
if (index < this.pointers.length && index >= 0) {
|
||||
this.pointers[index].makeInvisible();
|
||||
}
|
||||
};
|
||||
|
||||
this.lockPointerEnd = function(laserParams, lockData) {
|
||||
var index = laserParams.hand;
|
||||
if (index < this.pointers.length && index >= 0) {
|
||||
this.pointers[index].lockEnd(lockData);
|
||||
}
|
||||
};
|
||||
|
||||
this.updatePointersRenderState = function(triggerClicks, triggerValues) {
|
||||
for (var index = 0; index < this.pointers.length; index++) {
|
||||
this.pointers[index].updateRenderState(triggerClicks, triggerValues);
|
||||
}
|
||||
};
|
||||
|
||||
this.removePointers = function() {
|
||||
for (var index = 0; index < this.pointers.length; index++) {
|
||||
this.pointers[index].removePointer();
|
||||
}
|
||||
this.pointers = [];
|
||||
};
|
||||
};
|
147
scripts/simplifiedUI/system/libraries/progressDialog.js
Normal file
147
scripts/simplifiedUI/system/libraries/progressDialog.js
Normal file
|
@ -0,0 +1,147 @@
|
|||
//
|
||||
// progressDialog.js
|
||||
// examples/libraries
|
||||
//
|
||||
// Copyright 2014 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
|
||||
//
|
||||
|
||||
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/";
|
||||
|
||||
progressDialog = (function () {
|
||||
var that = {},
|
||||
progressBackground,
|
||||
progressMessage,
|
||||
cancelButton,
|
||||
displayed = false,
|
||||
backgroundWidth = 300,
|
||||
backgroundHeight = 100,
|
||||
messageHeight = 32,
|
||||
cancelWidth = 70,
|
||||
cancelHeight = 32,
|
||||
textColor = { red: 255, green: 255, blue: 255 },
|
||||
textBackground = { red: 52, green: 52, blue: 52 },
|
||||
backgroundUrl = toolIconUrl + "progress-background.svg",
|
||||
windowDimensions;
|
||||
|
||||
progressBackground = Overlays.addOverlay("image", {
|
||||
width: backgroundWidth,
|
||||
height: backgroundHeight,
|
||||
imageURL: backgroundUrl,
|
||||
alpha: 0.9,
|
||||
visible: false
|
||||
});
|
||||
|
||||
progressMessage = Overlays.addOverlay("text", {
|
||||
width: backgroundWidth - 40,
|
||||
height: messageHeight,
|
||||
text: "",
|
||||
textColor: textColor,
|
||||
backgroundColor: textBackground,
|
||||
alpha: 0.9,
|
||||
backgroundAlpha: 0.9,
|
||||
visible: false
|
||||
});
|
||||
|
||||
cancelButton = Overlays.addOverlay("text", {
|
||||
width: cancelWidth,
|
||||
height: cancelHeight,
|
||||
text: "Cancel",
|
||||
textColor: textColor,
|
||||
backgroundColor: textBackground,
|
||||
alpha: 0.9,
|
||||
backgroundAlpha: 0.9,
|
||||
visible: false
|
||||
});
|
||||
|
||||
function move() {
|
||||
var progressX,
|
||||
progressY;
|
||||
|
||||
if (displayed) {
|
||||
|
||||
if (windowDimensions.x === Window.innerWidth && windowDimensions.y === Window.innerHeight) {
|
||||
return;
|
||||
}
|
||||
windowDimensions.x = Window.innerWidth;
|
||||
windowDimensions.y = Window.innerHeight;
|
||||
|
||||
progressX = (windowDimensions.x - backgroundWidth) / 2; // Center.
|
||||
progressY = windowDimensions.y / 2 - backgroundHeight; // A little up from center.
|
||||
|
||||
Overlays.editOverlay(progressBackground, { x: progressX, y: progressY });
|
||||
Overlays.editOverlay(progressMessage, { x: progressX + 20, y: progressY + 15 });
|
||||
Overlays.editOverlay(cancelButton, {
|
||||
x: progressX + backgroundWidth - cancelWidth - 20,
|
||||
y: progressY + backgroundHeight - cancelHeight - 15
|
||||
});
|
||||
}
|
||||
}
|
||||
that.move = move;
|
||||
|
||||
that.onCancel = undefined;
|
||||
|
||||
function open(message) {
|
||||
if (!displayed) {
|
||||
windowDimensions = { x: 0, y : 0 };
|
||||
displayed = true;
|
||||
move();
|
||||
Overlays.editOverlay(progressBackground, { visible: true });
|
||||
Overlays.editOverlay(progressMessage, { visible: true, text: message });
|
||||
Overlays.editOverlay(cancelButton, { visible: true });
|
||||
} else {
|
||||
throw new Error("open() called on progressDialog when already open");
|
||||
}
|
||||
}
|
||||
that.open = open;
|
||||
|
||||
function isOpen() {
|
||||
return displayed;
|
||||
}
|
||||
that.isOpen = isOpen;
|
||||
|
||||
function update(message) {
|
||||
if (displayed) {
|
||||
Overlays.editOverlay(progressMessage, { text: message });
|
||||
} else {
|
||||
throw new Error("update() called on progressDialog when not open");
|
||||
}
|
||||
}
|
||||
that.update = update;
|
||||
|
||||
function close() {
|
||||
if (displayed) {
|
||||
Overlays.editOverlay(cancelButton, { visible: false });
|
||||
Overlays.editOverlay(progressMessage, { visible: false });
|
||||
Overlays.editOverlay(progressBackground, { visible: false });
|
||||
displayed = false;
|
||||
} else {
|
||||
throw new Error("close() called on progressDialog when not open");
|
||||
}
|
||||
}
|
||||
that.close = close;
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (Overlays.getOverlayAtPoint({ x: event.x, y: event.y }) === cancelButton) {
|
||||
if (typeof this.onCancel === "function") {
|
||||
close();
|
||||
this.onCancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
that.mousePressEvent = mousePressEvent;
|
||||
|
||||
function cleanup() {
|
||||
Overlays.deleteOverlay(cancelButton);
|
||||
Overlays.deleteOverlay(progressMessage);
|
||||
Overlays.deleteOverlay(progressBackground);
|
||||
}
|
||||
that.cleanup = cleanup;
|
||||
|
||||
return that;
|
||||
}());
|
42
scripts/simplifiedUI/system/libraries/soundArray.js
Normal file
42
scripts/simplifiedUI/system/libraries/soundArray.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* An array for sounds, allows you to randomly play a sound
|
||||
* taken from the removed editVoxels.js
|
||||
*/
|
||||
SoundArray = function(audioOptions, autoUpdateAudioPosition) {
|
||||
this.audioOptions = audioOptions !== undefined ? audioOptions : {};
|
||||
this.autoUpdateAudioPosition = autoUpdateAudioPosition !== undefined ? autoUpdateAudioPosition : false;
|
||||
if (this.audioOptions.position === undefined) {
|
||||
this.audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0});
|
||||
}
|
||||
if (this.audioOptions.volume === undefined) {
|
||||
this.audioOptions.volume = 1.0;
|
||||
}
|
||||
this.sounds = new Array();
|
||||
this.addSound = function (soundURL) {
|
||||
this.sounds[this.sounds.length] = SoundCache.getSound(soundURL);
|
||||
};
|
||||
this.play = function (index) {
|
||||
if (0 <= index && index < this.sounds.length) {
|
||||
if (this.autoUpdateAudioPosition) {
|
||||
this.updateAudioPosition();
|
||||
}
|
||||
if (this.sounds[index].downloaded) {
|
||||
Audio.playSound(this.sounds[index], this.audioOptions);
|
||||
}
|
||||
} else {
|
||||
print("[ERROR] libraries/soundArray.js:play() : Index " + index + " out of range.");
|
||||
}
|
||||
};
|
||||
this.playRandom = function () {
|
||||
if (this.sounds.length > 0) {
|
||||
this.play(Math.floor(Math.random() * this.sounds.length));
|
||||
} else {
|
||||
print("[ERROR] libraries/soundArray.js:playRandom() : Array is empty.");
|
||||
}
|
||||
};
|
||||
this.updateAudioPosition = function() {
|
||||
var position = MyAvatar.position;
|
||||
var forwardVector = Quat.getForward(MyAvatar.orientation);
|
||||
this.audioOptions.position = Vec3.sum(position, forwardVector);
|
||||
};
|
||||
};
|
476
scripts/simplifiedUI/system/libraries/stringHelpers.js
Normal file
476
scripts/simplifiedUI/system/libraries/stringHelpers.js
Normal file
|
@ -0,0 +1,476 @@
|
|||
if (typeof String.prototype.fileName !== "function") {
|
||||
String.prototype.fileName = function() {
|
||||
return this.replace(/^(.*[\/\\])*/, "");
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof String.prototype.fileBase !== "function") {
|
||||
String.prototype.fileBase = function() {
|
||||
var filename = this.fileName();
|
||||
return filename.slice(0, filename.indexOf("."));
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof String.prototype.fileType !== "function") {
|
||||
String.prototype.fileType = function() {
|
||||
return this.slice(this.lastIndexOf(".") + 1);
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof String.prototype.path !== "function") {
|
||||
String.prototype.path = function() {
|
||||
return this.replace(/[\\\/][^\\\/]*$/, "");
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof String.prototype.regExpEscape !== "function") {
|
||||
String.prototype.regExpEscape = function() {
|
||||
return this.replace(/([$\^.+*?|\\\/{}()\[\]])/g, '\\$1');
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof String.prototype.toArrayBuffer !== "function") {
|
||||
String.prototype.toArrayBuffer = function() {
|
||||
var length,
|
||||
buffer,
|
||||
view,
|
||||
charCode,
|
||||
charCodes,
|
||||
i;
|
||||
|
||||
charCodes = [];
|
||||
|
||||
length = this.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
charCode = this.charCodeAt(i);
|
||||
if (charCode <= 255) {
|
||||
charCodes.push(charCode);
|
||||
} else {
|
||||
charCodes.push(charCode / 256);
|
||||
charCodes.push(charCode % 256);
|
||||
}
|
||||
}
|
||||
|
||||
length = charCodes.length;
|
||||
buffer = new ArrayBuffer(length);
|
||||
view = new Uint8Array(buffer);
|
||||
for (i = 0; i < length; i += 1) {
|
||||
view[i] = charCodes[i];
|
||||
}
|
||||
|
||||
return buffer;
|
||||
};
|
||||
}
|
||||
// Copyright Mathias Bynens <https://mathiasbynens.be/>
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
/*! https://mths.be/includes v1.0.0 by @mathias */
|
||||
if (!String.prototype.includes) {
|
||||
(function() {
|
||||
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
|
||||
var toString = {}.toString;
|
||||
var defineProperty = (function() {
|
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try {
|
||||
var object = {};
|
||||
var $defineProperty = Object.defineProperty;
|
||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
||||
} catch (error) {}
|
||||
return result;
|
||||
}());
|
||||
var indexOf = ''.indexOf;
|
||||
var includes = function(search) {
|
||||
if (this == null) {
|
||||
throw TypeError();
|
||||
}
|
||||
var string = String(this);
|
||||
if (search && toString.call(search) == '[object RegExp]') {
|
||||
throw TypeError();
|
||||
}
|
||||
var stringLength = string.length;
|
||||
var searchString = String(search);
|
||||
var searchLength = searchString.length;
|
||||
var position = arguments.length > 1 ? arguments[1] : undefined;
|
||||
// `ToInteger`
|
||||
var pos = position ? Number(position) : 0;
|
||||
if (pos != pos) { // better `isNaN`
|
||||
pos = 0;
|
||||
}
|
||||
var start = Math.min(Math.max(pos, 0), stringLength);
|
||||
// Avoid the `indexOf` call if no match is possible
|
||||
if (searchLength + start > stringLength) {
|
||||
return false;
|
||||
}
|
||||
return indexOf.call(string, searchString, pos) != -1;
|
||||
};
|
||||
if (defineProperty) {
|
||||
defineProperty(String.prototype, 'includes', {
|
||||
'value': includes,
|
||||
'configurable': true,
|
||||
'writable': true
|
||||
});
|
||||
} else {
|
||||
String.prototype.includes = includes;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
/*! https://mths.be/startswith v0.2.0 by @mathias */
|
||||
if (!String.prototype.startsWith) {
|
||||
(function() {
|
||||
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
|
||||
var defineProperty = (function() {
|
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try {
|
||||
var object = {};
|
||||
var $defineProperty = Object.defineProperty;
|
||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
||||
} catch (error) {}
|
||||
return result;
|
||||
}());
|
||||
var toString = {}.toString;
|
||||
var startsWith = function(search) {
|
||||
if (this == null) {
|
||||
throw TypeError();
|
||||
}
|
||||
var string = String(this);
|
||||
if (search && toString.call(search) == '[object RegExp]') {
|
||||
throw TypeError();
|
||||
}
|
||||
var stringLength = string.length;
|
||||
var searchString = String(search);
|
||||
var searchLength = searchString.length;
|
||||
var position = arguments.length > 1 ? arguments[1] : undefined;
|
||||
// `ToInteger`
|
||||
var pos = position ? Number(position) : 0;
|
||||
if (pos != pos) { // better `isNaN`
|
||||
pos = 0;
|
||||
}
|
||||
var start = Math.min(Math.max(pos, 0), stringLength);
|
||||
// Avoid the `indexOf` call if no match is possible
|
||||
if (searchLength + start > stringLength) {
|
||||
return false;
|
||||
}
|
||||
var index = -1;
|
||||
while (++index < searchLength) {
|
||||
if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (defineProperty) {
|
||||
defineProperty(String.prototype, 'startsWith', {
|
||||
'value': startsWith,
|
||||
'configurable': true,
|
||||
'writable': true
|
||||
});
|
||||
} else {
|
||||
String.prototype.startsWith = startsWith;
|
||||
}
|
||||
}());
|
||||
}
|
||||
if (!String.prototype.endsWith) {
|
||||
(function() {
|
||||
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
|
||||
var defineProperty = (function() {
|
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try {
|
||||
var object = {};
|
||||
var $defineProperty = Object.defineProperty;
|
||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
||||
} catch (error) {}
|
||||
return result;
|
||||
}());
|
||||
var toString = {}.toString;
|
||||
var endsWith = function(search) {
|
||||
if (this == null) {
|
||||
throw TypeError();
|
||||
}
|
||||
var string = String(this);
|
||||
if (search && toString.call(search) == '[object RegExp]') {
|
||||
throw TypeError();
|
||||
}
|
||||
var stringLength = string.length;
|
||||
var searchString = String(search);
|
||||
var searchLength = searchString.length;
|
||||
var pos = stringLength;
|
||||
if (arguments.length > 1) {
|
||||
var position = arguments[1];
|
||||
if (position !== undefined) {
|
||||
// `ToInteger`
|
||||
pos = position ? Number(position) : 0;
|
||||
if (pos != pos) { // better `isNaN`
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
var end = Math.min(Math.max(pos, 0), stringLength);
|
||||
var start = end - searchLength;
|
||||
if (start < 0) {
|
||||
return false;
|
||||
}
|
||||
var index = -1;
|
||||
while (++index < searchLength) {
|
||||
if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (defineProperty) {
|
||||
defineProperty(String.prototype, 'endsWith', {
|
||||
'value': endsWith,
|
||||
'configurable': true,
|
||||
'writable': true
|
||||
});
|
||||
} else {
|
||||
String.prototype.endsWith = endsWith;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
/*! https://mths.be/repeat v0.2.0 by @mathias */
|
||||
if (!String.prototype.repeat) {
|
||||
(function() {
|
||||
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
|
||||
var defineProperty = (function() {
|
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try {
|
||||
var object = {};
|
||||
var $defineProperty = Object.defineProperty;
|
||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
||||
} catch (error) {}
|
||||
return result;
|
||||
}());
|
||||
var repeat = function(count) {
|
||||
if (this == null) {
|
||||
throw TypeError();
|
||||
}
|
||||
var string = String(this);
|
||||
// `ToInteger`
|
||||
var n = count ? Number(count) : 0;
|
||||
if (n != n) { // better `isNaN`
|
||||
n = 0;
|
||||
}
|
||||
// Account for out-of-bounds indices
|
||||
if (n < 0 || n == Infinity) {
|
||||
throw RangeError();
|
||||
}
|
||||
var result = '';
|
||||
while (n) {
|
||||
if (n % 2 == 1) {
|
||||
result += string;
|
||||
}
|
||||
if (n > 1) {
|
||||
string += string;
|
||||
}
|
||||
n >>= 1;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
if (defineProperty) {
|
||||
defineProperty(String.prototype, 'repeat', {
|
||||
'value': repeat,
|
||||
'configurable': true,
|
||||
'writable': true
|
||||
});
|
||||
} else {
|
||||
String.prototype.repeat = repeat;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
if (!String.prototype.at) {
|
||||
(function() {
|
||||
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
|
||||
var defineProperty = (function() {
|
||||
// IE 8 only supports `Object.defineProperty` on DOM elements.
|
||||
try {
|
||||
var object = {};
|
||||
var $defineProperty = Object.defineProperty;
|
||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
||||
} catch (exception) {}
|
||||
return result;
|
||||
}());
|
||||
var at = function(position) {
|
||||
if (this == null) {
|
||||
throw TypeError();
|
||||
}
|
||||
var string = String(this);
|
||||
var size = string.length;
|
||||
// `ToInteger`
|
||||
var index = position ? Number(position) : 0;
|
||||
if (index != index) { // better `isNaN`
|
||||
index = 0;
|
||||
}
|
||||
// Account for out-of-bounds indices
|
||||
// The odd lower bound is because the ToInteger operation is
|
||||
// going to round `n` to `0` for `-1 < n <= 0`.
|
||||
if (index <= -1 || index >= size) {
|
||||
return '';
|
||||
}
|
||||
// Second half of `ToInteger`
|
||||
index = index | 0;
|
||||
// Get the first code unit and code unit value
|
||||
var cuFirst = string.charCodeAt(index);
|
||||
var cuSecond;
|
||||
var nextIndex = index + 1;
|
||||
var len = 1;
|
||||
if ( // Check if it’s the start of a surrogate pair.
|
||||
cuFirst >= 0xD800 && cuFirst <= 0xDBFF && // high surrogate
|
||||
size > nextIndex // there is a next code unit
|
||||
) {
|
||||
cuSecond = string.charCodeAt(nextIndex);
|
||||
if (cuSecond >= 0xDC00 && cuSecond <= 0xDFFF) { // low surrogate
|
||||
len = 2;
|
||||
}
|
||||
}
|
||||
return string.slice(index, index + len);
|
||||
};
|
||||
if (defineProperty) {
|
||||
defineProperty(String.prototype, 'at', {
|
||||
'value': at,
|
||||
'configurable': true,
|
||||
'writable': true
|
||||
});
|
||||
} else {
|
||||
String.prototype.at = at;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
/*! https://mths.be/codepointat v0.2.0 by @mathias */
|
||||
if (!String.prototype.codePointAt) {
|
||||
(function() {
|
||||
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
|
||||
var defineProperty = (function() {
|
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try {
|
||||
var object = {};
|
||||
var $defineProperty = Object.defineProperty;
|
||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
||||
} catch (error) {}
|
||||
return result;
|
||||
}());
|
||||
var codePointAt = function(position) {
|
||||
if (this == null) {
|
||||
throw TypeError();
|
||||
}
|
||||
var string = String(this);
|
||||
var size = string.length;
|
||||
// `ToInteger`
|
||||
var index = position ? Number(position) : 0;
|
||||
if (index != index) { // better `isNaN`
|
||||
index = 0;
|
||||
}
|
||||
// Account for out-of-bounds indices:
|
||||
if (index < 0 || index >= size) {
|
||||
return undefined;
|
||||
}
|
||||
// Get the first code unit
|
||||
var first = string.charCodeAt(index);
|
||||
var second;
|
||||
if ( // check if it’s the start of a surrogate pair
|
||||
first >= 0xD800 && first <= 0xDBFF && // high surrogate
|
||||
size > index + 1 // there is a next code unit
|
||||
) {
|
||||
second = string.charCodeAt(index + 1);
|
||||
if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
|
||||
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
|
||||
return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
};
|
||||
if (defineProperty) {
|
||||
defineProperty(String.prototype, 'codePointAt', {
|
||||
'value': codePointAt,
|
||||
'configurable': true,
|
||||
'writable': true
|
||||
});
|
||||
} else {
|
||||
String.prototype.codePointAt = codePointAt;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
/*! https://mths.be/fromcodepoint v0.2.1 by @mathias */
|
||||
if (!String.fromCodePoint) {
|
||||
(function() {
|
||||
var defineProperty = (function() {
|
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try {
|
||||
var object = {};
|
||||
var $defineProperty = Object.defineProperty;
|
||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
||||
} catch (error) {}
|
||||
return result;
|
||||
}());
|
||||
var stringFromCharCode = String.fromCharCode;
|
||||
var floor = Math.floor;
|
||||
var fromCodePoint = function(_) {
|
||||
var MAX_SIZE = 0x4000;
|
||||
var codeUnits = [];
|
||||
var highSurrogate;
|
||||
var lowSurrogate;
|
||||
var index = -1;
|
||||
var length = arguments.length;
|
||||
if (!length) {
|
||||
return '';
|
||||
}
|
||||
var result = '';
|
||||
while (++index < length) {
|
||||
var codePoint = Number(arguments[index]);
|
||||
if (!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
|
||||
codePoint < 0 || // not a valid Unicode code point
|
||||
codePoint > 0x10FFFF || // not a valid Unicode code point
|
||||
floor(codePoint) != codePoint // not an integer
|
||||
) {
|
||||
throw RangeError('Invalid code point: ' + codePoint);
|
||||
}
|
||||
if (codePoint <= 0xFFFF) { // BMP code point
|
||||
codeUnits.push(codePoint);
|
||||
} else { // Astral code point; split in surrogate halves
|
||||
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
|
||||
codePoint -= 0x10000;
|
||||
highSurrogate = (codePoint >> 10) + 0xD800;
|
||||
lowSurrogate = (codePoint % 0x400) + 0xDC00;
|
||||
codeUnits.push(highSurrogate, lowSurrogate);
|
||||
}
|
||||
if (index + 1 == length || codeUnits.length > MAX_SIZE) {
|
||||
result += stringFromCharCode.apply(null, codeUnits);
|
||||
codeUnits.length = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
if (defineProperty) {
|
||||
defineProperty(String, 'fromCodePoint', {
|
||||
'value': fromCodePoint,
|
||||
'configurable': true,
|
||||
'writable': true
|
||||
});
|
||||
} else {
|
||||
String.fromCodePoint = fromCodePoint;
|
||||
}
|
||||
}());
|
||||
}
|
493
scripts/simplifiedUI/system/libraries/toolBars.js
Normal file
493
scripts/simplifiedUI/system/libraries/toolBars.js
Normal file
|
@ -0,0 +1,493 @@
|
|||
//
|
||||
// toolBars.js
|
||||
// examples
|
||||
//
|
||||
// Created by Clément Brisset on 5/7/14.
|
||||
// Persistable drag position by HRS 6/11/15.
|
||||
// Copyright 2014 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
|
||||
//
|
||||
|
||||
Overlay2D = function(properties, overlay) { // overlay is an optional variable
|
||||
if (!(typeof(properties) === 'undefined')) {
|
||||
if(typeof(overlay) === 'undefined') {
|
||||
overlay = Overlays.addOverlay("image", properties);
|
||||
} else {
|
||||
Overlays.editOverlay(overlay, properties);
|
||||
}
|
||||
}
|
||||
|
||||
this.overlay = function() {
|
||||
return overlay;
|
||||
}
|
||||
this.x = function() {
|
||||
return properties.x;
|
||||
}
|
||||
this.y = function() {
|
||||
return properties.y;
|
||||
}
|
||||
this.width = function() {
|
||||
return properties.width;
|
||||
}
|
||||
this.height = function() {
|
||||
return properties.height;
|
||||
}
|
||||
this.alpha = function() {
|
||||
return properties.alpha;
|
||||
}
|
||||
this.visible = function() {
|
||||
return properties.visible;
|
||||
}
|
||||
|
||||
|
||||
this.move = function(x, y) {
|
||||
properties.x = x;
|
||||
properties.y = y;
|
||||
Overlays.editOverlay(overlay, { x: x, y: y });
|
||||
}
|
||||
this.resize = function(width, height) {
|
||||
properties.width = width;
|
||||
properties.height = height;
|
||||
Overlays.editOverlay(overlay, { width: width, height: height });
|
||||
}
|
||||
this.setAlpha = function(alpha) {
|
||||
properties.alpha = alpha;
|
||||
Overlays.editOverlay(overlay, { alpha: alpha });
|
||||
}
|
||||
this.setImageURL = function(imageURL) {
|
||||
properties.imageURL = imageURL;
|
||||
Overlays.editOverlay(overlay, { imageURL: imageURL });
|
||||
}
|
||||
this.show = function(doShow) {
|
||||
properties.visible = doShow;
|
||||
Overlays.editOverlay(overlay, { visible: doShow });
|
||||
}
|
||||
|
||||
this.clicked = function(clickedOverlay) {
|
||||
return overlay === clickedOverlay;
|
||||
}
|
||||
|
||||
this.cleanup = function() {
|
||||
Overlays.deleteOverlay(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Tool = function(properties, selectable, selected) { // selectable and selected are optional variables.
|
||||
Overlay2D.call(this, properties);
|
||||
|
||||
if(typeof(selectable)==='undefined') {
|
||||
selectable = false;
|
||||
if(typeof(selected)==='undefined') {
|
||||
selected = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.selectable = function() {
|
||||
return selectable;
|
||||
}
|
||||
|
||||
this.selected = function() {
|
||||
return selected;
|
||||
}
|
||||
this.select = function(doSelect) {
|
||||
if (!selectable) {
|
||||
return;
|
||||
}
|
||||
|
||||
selected = doSelect;
|
||||
properties.subImage.y = (selected ? 0 : 1) * properties.subImage.height;
|
||||
Overlays.editOverlay(this.overlay(), { subImage: properties.subImage });
|
||||
}
|
||||
this.toggle = function() {
|
||||
if (!selectable) {
|
||||
return;
|
||||
}
|
||||
selected = !selected;
|
||||
properties.subImage.y = (selected ? 0 : 1) * properties.subImage.height;
|
||||
Overlays.editOverlay(this.overlay(), { subImage: properties.subImage });
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
this.select(selected);
|
||||
|
||||
this.isButtonDown = false;
|
||||
this.buttonDown = function (down) {
|
||||
if (down !== this.isButtonDown) {
|
||||
properties.subImage.y = (down ? 0 : 1) * properties.subImage.height;
|
||||
Overlays.editOverlay(this.overlay(), { subImage: properties.subImage });
|
||||
this.isButtonDown = down;
|
||||
}
|
||||
}
|
||||
|
||||
this.baseClicked = this.clicked;
|
||||
this.clicked = function(clickedOverlay, update) {
|
||||
if (this.baseClicked(clickedOverlay)) {
|
||||
if (update) {
|
||||
if (selectable) {
|
||||
this.toggle();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Tool.prototype = new Overlay2D;
|
||||
Tool.IMAGE_HEIGHT = 50;
|
||||
Tool.IMAGE_WIDTH = 50;
|
||||
|
||||
ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPositionFunction, optionalOffset) {
|
||||
this.tools = new Array();
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.offset = optionalOffset ? optionalOffset : { x: 0, y: 0 };
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
this.backAlpha = 1.0;
|
||||
this.back = Overlays.addOverlay("rectangle", {
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
radius: 4,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
alpha: this.backAlpha,
|
||||
visible: false
|
||||
});
|
||||
this.spacing = [];
|
||||
this.onMove = null;
|
||||
|
||||
this.addTool = function(properties, selectable, selected) {
|
||||
if (direction == ToolBar.HORIZONTAL) {
|
||||
properties.x = this.x + this.width;
|
||||
properties.y = this.y;
|
||||
this.width += properties.width + ToolBar.SPACING;
|
||||
this.height = Math.max(properties.height, this.height);
|
||||
} else {
|
||||
properties.x = this.x;
|
||||
properties.y = this.y + this.height;
|
||||
this.width = Math.max(properties.width, this.width);
|
||||
this.height += properties.height + ToolBar.SPACING;
|
||||
}
|
||||
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
width: this.width +
|
||||
((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING,
|
||||
height: this.height +
|
||||
((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING,
|
||||
});
|
||||
}
|
||||
|
||||
this.tools.push(new Tool(properties, selectable, selected));
|
||||
return ((this.tools.length) - 1);
|
||||
}
|
||||
|
||||
this.addSpacing = function(size) {
|
||||
if (direction == ToolBar.HORIZONTAL) {
|
||||
this.width += size;
|
||||
} else {
|
||||
this.height += size;
|
||||
}
|
||||
this.spacing[this.tools.length] = size;
|
||||
|
||||
return (this.tools.length);
|
||||
}
|
||||
|
||||
this.changeSpacing = function(size, id) {
|
||||
if (this.spacing[id] === null) {
|
||||
this.spacing[id] = 0;
|
||||
}
|
||||
var diff = size - this.spacing[id];
|
||||
this.spacing[id] = size;
|
||||
|
||||
var dx = (direction == ToolBar.HORIZONTAL) ? diff : 0;
|
||||
var dy = (direction == ToolBar.VERTICAL) ? diff : 0;
|
||||
this.width += dx;
|
||||
this.height += dy;
|
||||
|
||||
for(i = id; i < this.tools.length; i++) {
|
||||
this.tools[i].move(this.tools[i].x() + dx,
|
||||
this.tools[i].y() + dy);
|
||||
}
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
width: this.width +
|
||||
((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING,
|
||||
height: this.height +
|
||||
((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.removeLastTool = function() {
|
||||
this.tools.pop().cleanup();
|
||||
|
||||
if (direction == ToolBar.HORIZONTAL) {
|
||||
this.width -= Tool.IMAGE_WIDTH + ToolBar.SPACING;
|
||||
} else {
|
||||
this.height -= Tool.IMAGE_HEIGHT + ToolBar.SPACING;
|
||||
}
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
width: this.width + 2 * ToolBar.SPACING,
|
||||
height: this.height + 2 * ToolBar.SPACING
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.move = function (x, y) {
|
||||
var dx = x - this.x;
|
||||
var dy = y - this.y;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
for(var tool in this.tools) {
|
||||
this.tools[tool].move(this.tools[tool].x() + dx, this.tools[tool].y() + dy);
|
||||
}
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
x: x - ToolBar.SPACING,
|
||||
y: y - ToolBar.SPACING
|
||||
});
|
||||
}
|
||||
if (this.onMove !== null) {
|
||||
this.onMove(x, y, dx, dy);
|
||||
};
|
||||
}
|
||||
|
||||
this.setAlpha = function(alpha, tool) {
|
||||
if(typeof(tool) === 'undefined') {
|
||||
for(var tool in this.tools) {
|
||||
this.tools[tool].setAlpha(alpha);
|
||||
}
|
||||
if (this.back != null) {
|
||||
this.backAlpha = alpha;
|
||||
Overlays.editOverlay(this.back, { alpha: alpha });
|
||||
}
|
||||
} else {
|
||||
this.tools[tool].setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
this.setImageURL = function(imageURL, tool) {
|
||||
this.tools[tool].setImageURL(imageURL);
|
||||
}
|
||||
|
||||
this.setBack = function(color, alpha) {
|
||||
if (color == null) {
|
||||
Overlays.editOverlay(this.back, { visible: false });
|
||||
} else {
|
||||
Overlays.editOverlay(this.back, {
|
||||
width: this.width +
|
||||
((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING,
|
||||
height: this.height +
|
||||
((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING,
|
||||
visible: true,
|
||||
color: color,
|
||||
alpha: alpha
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.showTool = function(tool, doShow) {
|
||||
this.tools[tool].show(doShow);
|
||||
}
|
||||
|
||||
this.show = function(doShow) {
|
||||
for(var tool in this.tools) {
|
||||
this.tools[tool].show(doShow);
|
||||
}
|
||||
if (this.back != null) {
|
||||
Overlays.editOverlay(this.back, { visible: doShow});
|
||||
}
|
||||
}
|
||||
|
||||
this.clicked = function(clickedOverlay, update) {
|
||||
if(typeof(update) === 'undefined') {
|
||||
update = true;
|
||||
}
|
||||
|
||||
for(var tool in this.tools) {
|
||||
if (this.tools[tool].visible() && this.tools[tool].clicked(clickedOverlay, update)) {
|
||||
return parseInt(tool);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
this.numberOfTools = function() {
|
||||
return this.tools.length;
|
||||
}
|
||||
|
||||
this.selectTool = function (tool, select) {
|
||||
this.tools[tool].select(select);
|
||||
}
|
||||
|
||||
this.toolSelected = function (tool) {
|
||||
return this.tools[tool].selected();
|
||||
}
|
||||
|
||||
this.cleanup = function() {
|
||||
for(var tool in this.tools) {
|
||||
this.tools[tool].cleanup();
|
||||
}
|
||||
|
||||
if (this.back != null) {
|
||||
Overlays.deleteOverlay(this.back);
|
||||
this.back = null;
|
||||
}
|
||||
|
||||
this.tools = [];
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
this.contains = function (xOrPoint, optionalY) { // All four margins are draggable.
|
||||
var x = (optionalY === undefined) ? xOrPoint.x : xOrPoint,
|
||||
y = (optionalY === undefined) ? xOrPoint.y : optionalY;
|
||||
return ((that.x - ToolBar.SPACING) <= x) && (x <= (that.x + that.width + ToolBar.SPACING)) &&
|
||||
((that.y - ToolBar.SPACING) <= y) && (y <= (that.y + that.height));
|
||||
}
|
||||
that.hover = function (enable) { // Can be overridden or extended by clients.
|
||||
that.isHovering = enable;
|
||||
if (that.back) {
|
||||
Overlays.editOverlay(this.back, {
|
||||
visible: enable,
|
||||
alpha: enable ? 0.5 : that.backAlpha
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
var recommendedRect = Controller.getRecommendedHUDRect();
|
||||
var recommendedDimmensions = { x: recommendedRect.width, y: recommendedRect.height };
|
||||
that.windowDimensions = recommendedDimmensions; // Controller.getViewportDimensions();
|
||||
that.origin = { x: recommendedRect.x, y: recommendedRect.y };
|
||||
// Maybe fixme: Keeping the same percent of the window size isn't always the right thing.
|
||||
// For example, maybe we want "keep the same percentage to whatever two edges are closest to the edge of screen".
|
||||
// If we change that, the places to do so are onResizeViewport, save (maybe), and the initial move based on Settings, below.
|
||||
that.onResizeViewport = function (newSize) { // Can be overridden or extended by clients.
|
||||
var recommendedRect = Controller.getRecommendedHUDRect();
|
||||
var recommendedDimmensions = { x: recommendedRect.width, y: recommendedRect.height };
|
||||
var originRelativeX = (that.x - that.origin.x - that.offset.x);
|
||||
var originRelativeY = (that.y - that.origin.y - that.offset.y);
|
||||
var fractionX = clamp(originRelativeX / that.windowDimensions.x, 0, 1);
|
||||
var fractionY = clamp(originRelativeY / that.windowDimensions.y, 0, 1);
|
||||
that.windowDimensions = newSize || recommendedDimmensions;
|
||||
that.origin = { x: recommendedRect.x, y: recommendedRect.y };
|
||||
var newX = (fractionX * that.windowDimensions.x) + recommendedRect.x + that.offset.x;
|
||||
var newY = (fractionY * that.windowDimensions.y) + recommendedRect.y + that.offset.y;
|
||||
that.move(newX, newY);
|
||||
};
|
||||
if (optionalPersistenceKey) {
|
||||
this.fractionKey = optionalPersistenceKey + '.fraction';
|
||||
// FIXME: New default position in RC8 is bottom center of screen instead of right. Can remove this key and associated
|
||||
// code once the new toolbar position is well established with users.
|
||||
this.isNewPositionKey = optionalPersistenceKey + '.isNewPosition';
|
||||
this.save = function () {
|
||||
var recommendedRect = Controller.getRecommendedHUDRect();
|
||||
var screenSize = { x: recommendedRect.width, y: recommendedRect.height };
|
||||
if (screenSize.x > 0 && screenSize.y > 0) {
|
||||
// Guard against invalid screen size that can occur at shut-down.
|
||||
var fraction = {x: (that.x - that.offset.x) / screenSize.x, y: (that.y - that.offset.y) / screenSize.y};
|
||||
Settings.setValue(this.fractionKey, JSON.stringify(fraction));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.save = function () { }; // Called on move. Can be overriden or extended by clients.
|
||||
}
|
||||
// These are currently only doing that which is necessary for toolbar hover and toolbar drag.
|
||||
// They have not yet been extended to tool hover/click/release, etc.
|
||||
this.mousePressEvent = function (event) {
|
||||
if (Overlays.getOverlayAtPoint({ x: event.x, y: event.y }) == that.back) {
|
||||
that.mightBeDragging = true;
|
||||
that.dragOffsetX = that.x - event.x;
|
||||
that.dragOffsetY = that.y - event.y;
|
||||
} else {
|
||||
that.mightBeDragging = false;
|
||||
}
|
||||
};
|
||||
this.mouseReleaseEvent = function (event) {
|
||||
for (var tool in that.tools) {
|
||||
that.tools[tool].buttonDown(false);
|
||||
}
|
||||
if (that.mightBeDragging) {
|
||||
that.save();
|
||||
}
|
||||
}
|
||||
this.mouseMove = function (event) {
|
||||
if (!that.mightBeDragging || !event.isLeftButton) {
|
||||
that.mightBeDragging = false;
|
||||
if (!that.contains(event)) {
|
||||
if (that.isHovering) {
|
||||
that.hover(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!that.isHovering) {
|
||||
that.hover(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y);
|
||||
};
|
||||
that.checkResize = function () { // Can be overriden or extended, but usually not. See onResizeViewport.
|
||||
var recommendedRect = Controller.getRecommendedHUDRect();
|
||||
var currentWindowSize = { x: recommendedRect.width, y: recommendedRect.height };
|
||||
|
||||
if ((currentWindowSize.x !== that.windowDimensions.x) || (currentWindowSize.y !== that.windowDimensions.y)) {
|
||||
that.onResizeViewport(currentWindowSize);
|
||||
}
|
||||
};
|
||||
Controller.mousePressEvent.connect(this.mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(this.mouseReleaseEvent);
|
||||
Controller.mouseMoveEvent.connect(this.mouseMove);
|
||||
Script.update.connect(that.checkResize);
|
||||
// This compatability hack breaks the model, but makes converting existing scripts easier:
|
||||
this.addOverlay = function (ignored, oldSchoolProperties) {
|
||||
var properties = JSON.parse(JSON.stringify(oldSchoolProperties)); // a copy
|
||||
if ((that.numberOfTools() === 0) && (properties.x != undefined) && (properties.y != undefined)) {
|
||||
that.move(properties.x, properties.y);
|
||||
}
|
||||
delete properties.x;
|
||||
delete properties.y;
|
||||
var index = that.addTool(properties);
|
||||
var id = that.tools[index].overlay();
|
||||
return id;
|
||||
}
|
||||
if (this.fractionKey || optionalInitialPositionFunction) {
|
||||
var isNewPosition = Settings.getValue(this.isNewPositionKey);
|
||||
var savedFraction = isNewPosition ? JSON.parse(Settings.getValue(this.fractionKey) || "0") : 0;
|
||||
Settings.setValue(this.isNewPositionKey, true);
|
||||
|
||||
var recommendedRect = Controller.getRecommendedHUDRect();
|
||||
var screenSize = { x: recommendedRect.width, y: recommendedRect.height };
|
||||
if (savedFraction) {
|
||||
// If we have saved data, keep the toolbar at the same proportion of the screen width/height.
|
||||
that.move(savedFraction.x * screenSize.x + that.offset.x, savedFraction.y * screenSize.y + that.offset.y);
|
||||
} else if (!optionalInitialPositionFunction) {
|
||||
print("No initPosition(screenSize, intializedToolbar) specified for ToolBar");
|
||||
} else {
|
||||
// Call the optionalInitialPositionFunctinon() AFTER the client has had a chance to set up.
|
||||
var that = this;
|
||||
Script.setTimeout(function () {
|
||||
var position = optionalInitialPositionFunction(screenSize, that);
|
||||
that.move(position.x + that.offset.x, position.y + that.offset.y);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
ToolBar.SPACING = 6;
|
||||
ToolBar.VERTICAL = 0;
|
||||
ToolBar.HORIZONTAL = 1;
|
250
scripts/simplifiedUI/system/libraries/touchEventUtils.js
Normal file
250
scripts/simplifiedUI/system/libraries/touchEventUtils.js
Normal file
|
@ -0,0 +1,250 @@
|
|||
"use strict";
|
||||
|
||||
// touchEventUtils.js
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND,
|
||||
enableDispatcherModule, disableDispatcherModule, makeRunningValues,
|
||||
Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, controllerDispatcher.ZERO_VEC,
|
||||
HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset
|
||||
*/
|
||||
|
||||
var controllerDispatcher = Script.require("/~/system/libraries/controllerDispatcherUtils.js");
|
||||
function touchTargetHasKeyboardFocus(touchTarget) {
|
||||
if (touchTarget.entityID && touchTarget.entityID !== Uuid.NULL) {
|
||||
return Entities.keyboardFocusEntity === touchTarget.entityID;
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== Uuid.NULL) {
|
||||
return Overlays.keyboardFocusOverlay === touchTarget.overlayID;
|
||||
}
|
||||
}
|
||||
|
||||
function setKeyboardFocusOnTouchTarget(touchTarget) {
|
||||
if (touchTarget.entityID && touchTarget.entityID !== Uuid.NULL &&
|
||||
Entities.wantsHandControllerPointerEvents(touchTarget.entityID)) {
|
||||
Overlays.keyboardFocusOverlay = Uuid.NULL;
|
||||
Entities.keyboardFocusEntity = touchTarget.entityID;
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== Uuid.NULL) {
|
||||
Overlays.keyboardFocusOverlay = touchTarget.overlayID;
|
||||
Entities.keyboardFocusEntity = Uuid.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
function sendHoverEnterEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "None"
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== Uuid.NULL) {
|
||||
Entities.sendHoverEnterEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== Uuid.NULL) {
|
||||
Overlays.sendHoverEnterOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendHoverOverEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "None"
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== Uuid.NULL) {
|
||||
Entities.sendMouseMoveOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendHoverOverEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== Uuid.NULL) {
|
||||
Overlays.sendMouseMoveOnOverlay(touchTarget.overlayID, pointerEvent);
|
||||
Overlays.sendHoverOverOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchStartEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Press",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "Primary",
|
||||
isPrimaryHeld: true
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== Uuid.NULL) {
|
||||
Entities.sendMousePressOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendClickDownOnEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== Uuid.NULL) {
|
||||
Overlays.sendMousePressOnOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchEndEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Release",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "Primary"
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== Uuid.NULL) {
|
||||
Entities.sendMouseReleaseOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendClickReleaseOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendHoverLeaveEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== Uuid.NULL) {
|
||||
Overlays.sendMouseReleaseOnOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function sendTouchMoveEventToTouchTarget(hand, touchTarget) {
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: touchTarget.position2D,
|
||||
pos3D: touchTarget.position,
|
||||
normal: touchTarget.normal,
|
||||
direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal),
|
||||
button: "Primary",
|
||||
isPrimaryHeld: true
|
||||
};
|
||||
|
||||
if (touchTarget.entityID && touchTarget.entityID !== Uuid.NULL) {
|
||||
Entities.sendMouseMoveOnEntity(touchTarget.entityID, pointerEvent);
|
||||
Entities.sendHoldingClickOnEntity(touchTarget.entityID, pointerEvent);
|
||||
} else if (touchTarget.overlayID && touchTarget.overlayID !== Uuid.NULL) {
|
||||
Overlays.sendMouseMoveOnOverlay(touchTarget.overlayID, pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function composeTouchTargetFromIntersection(intersection) {
|
||||
var isEntity = (intersection.type === Picks.INTERSECTED_ENTITY);
|
||||
var objectID = intersection.objectID;
|
||||
var worldPos = intersection.intersection;
|
||||
var props = null;
|
||||
if (isEntity) {
|
||||
props = Entities.getProperties(intersection.objectID);
|
||||
}
|
||||
|
||||
var position2D =(isEntity ? controllerDispatcher.projectOntoEntityXYPlane(objectID, worldPos, props) :
|
||||
controllerDispatcher.projectOntoOverlayXYPlane(objectID, worldPos));
|
||||
return {
|
||||
entityID: isEntity ? objectID : null,
|
||||
overlayID: isEntity ? null : objectID,
|
||||
distance: intersection.distance,
|
||||
position: worldPos,
|
||||
position2D: position2D,
|
||||
normal: intersection.surfaceNormal
|
||||
};
|
||||
}
|
||||
|
||||
// will return undefined if overlayID does not exist.
|
||||
function calculateTouchTargetFromOverlay(touchTip, overlayID) {
|
||||
var overlayPosition = Overlays.getProperty(overlayID, "position");
|
||||
if (overlayPosition === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// project touchTip onto overlay plane.
|
||||
var overlayRotation = Overlays.getProperty(overlayID, "rotation");
|
||||
if (overlayRotation === undefined) {
|
||||
return;
|
||||
}
|
||||
var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1});
|
||||
var distance = Vec3.dot(Vec3.subtract(touchTip.position, overlayPosition), normal);
|
||||
var position = Vec3.subtract(touchTip.position, Vec3.multiply(normal, distance));
|
||||
|
||||
// calclulate normalized position
|
||||
var invRot = Quat.inverse(overlayRotation);
|
||||
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition));
|
||||
|
||||
var dimensions = Overlays.getProperty(overlayID, "dimensions");
|
||||
if (dimensions === undefined) {
|
||||
return;
|
||||
}
|
||||
dimensions.z = 0.01; // we are projecting onto the XY plane of the overlay, so ignore the z dimension
|
||||
var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z };
|
||||
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT);
|
||||
|
||||
// 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner.
|
||||
var position2D = {
|
||||
x: normalizedPosition.x * dimensions.x,
|
||||
y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis
|
||||
};
|
||||
|
||||
return {
|
||||
entityID: null,
|
||||
overlayID: overlayID,
|
||||
distance: distance,
|
||||
position: position,
|
||||
position2D: position2D,
|
||||
normal: normal,
|
||||
normalizedPosition: normalizedPosition,
|
||||
dimensions: dimensions,
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
|
||||
// will return undefined if entity does not exist.
|
||||
function calculateTouchTargetFromEntity(touchTip, props) {
|
||||
if (props.rotation === undefined) {
|
||||
// if rotation is missing from props object, then this entity has probably been deleted.
|
||||
return;
|
||||
}
|
||||
|
||||
// project touch tip onto entity plane.
|
||||
var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1});
|
||||
Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0});
|
||||
var distance = Vec3.dot(Vec3.subtract(touchTip.position, props.position), normal);
|
||||
var position = Vec3.subtract(touchTip.position, Vec3.multiply(normal, distance));
|
||||
|
||||
// generate normalized coordinates
|
||||
var invRot = Quat.inverse(props.rotation);
|
||||
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position));
|
||||
var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z };
|
||||
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint);
|
||||
|
||||
// 2D position on entity plane in meters, relative to the bounding box upper-left hand corner.
|
||||
var position2D = {
|
||||
x: normalizedPosition.x * props.dimensions.x,
|
||||
y: (1 - normalizedPosition.y) * props.dimensions.y // flip y-axis
|
||||
};
|
||||
|
||||
return {
|
||||
entityID: props.id,
|
||||
entityProps: props,
|
||||
overlayID: null,
|
||||
distance: distance,
|
||||
position: position,
|
||||
position2D: position2D,
|
||||
normal: normal,
|
||||
normalizedPosition: normalizedPosition,
|
||||
dimensions: props.dimensions,
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
calculateTouchTargetFromEntity: calculateTouchTargetFromEntity,
|
||||
calculateTouchTargetFromOverlay: calculateTouchTargetFromOverlay,
|
||||
touchTargetHasKeyboardFocus: touchTargetHasKeyboardFocus,
|
||||
setKeyboardFocusOnTouchTarget: setKeyboardFocusOnTouchTarget,
|
||||
sendHoverEnterEventToTouchTarget: sendHoverEnterEventToTouchTarget,
|
||||
sendHoverOverEventToTouchTarget: sendHoverOverEventToTouchTarget,
|
||||
sendTouchStartEventToTouchTarget: sendTouchStartEventToTouchTarget,
|
||||
sendTouchEndEventToTouchTarget: sendTouchEndEventToTouchTarget,
|
||||
sendTouchMoveEventToTouchTarget: sendTouchMoveEventToTouchTarget,
|
||||
composeTouchTargetFromIntersection: composeTouchTargetFromIntersection
|
||||
};
|
504
scripts/simplifiedUI/system/libraries/utils.js
Normal file
504
scripts/simplifiedUI/system/libraries/utils.js
Normal file
|
@ -0,0 +1,504 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/08/29
|
||||
// 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
|
||||
//
|
||||
|
||||
// note: this constant is currently duplicated in edit.js and ambientSound.js
|
||||
EDIT_SETTING = "io.highfidelity.isEditing";
|
||||
isInEditMode = function isInEditMode() {
|
||||
return Settings.getValue(EDIT_SETTING);
|
||||
};
|
||||
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function(oThis) {
|
||||
if (typeof this !== 'function') {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function() {},
|
||||
fBound = function() {
|
||||
return fToBind.apply(this instanceof fNOP
|
||||
? this
|
||||
: oThis,
|
||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
if (this.prototype) {
|
||||
// Function.prototype doesn't have a prototype property
|
||||
fNOP.prototype = this.prototype;
|
||||
}
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
vec3toStr = function(v, digits) {
|
||||
if (!digits) { digits = 3; }
|
||||
return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }";
|
||||
}
|
||||
|
||||
quatToStr = function(q, digits) {
|
||||
if (!digits) { digits = 3; }
|
||||
return "{ " + q.w.toFixed(digits) + ", " + q.x.toFixed(digits) + ", " +
|
||||
q.y.toFixed(digits) + ", " + q.z.toFixed(digits)+ " }";
|
||||
}
|
||||
|
||||
vec3equal = function(v0, v1) {
|
||||
return (v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z);
|
||||
}
|
||||
|
||||
colorMix = function(colorA, colorB, mix) {
|
||||
var result = {};
|
||||
for (var key in colorA) {
|
||||
result[key] = (colorA[key] * (1 - mix)) + (colorB[key] * mix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
scaleLine = function (start, end, scale) {
|
||||
var v = Vec3.subtract(end, start);
|
||||
var length = Vec3.length(v);
|
||||
v = Vec3.multiply(scale, v);
|
||||
return Vec3.sum(start, v);
|
||||
}
|
||||
|
||||
findAction = function(name) {
|
||||
return Controller.findAction(name);
|
||||
}
|
||||
|
||||
addLine = function(origin, vector, color) {
|
||||
if (!color) {
|
||||
color = COLORS.WHITE
|
||||
}
|
||||
return Entities.addEntity(mergeObjects(LINE_PROTOTYPE, {
|
||||
position: origin,
|
||||
linePoints: [
|
||||
ZERO_VECTOR,
|
||||
vector,
|
||||
],
|
||||
color: color
|
||||
}));
|
||||
}
|
||||
|
||||
// FIXME fetch from a subkey of user data to support non-destructive modifications
|
||||
setEntityUserData = function(id, data) {
|
||||
var json = JSON.stringify(data)
|
||||
Entities.editEntity(id, { userData: json });
|
||||
}
|
||||
|
||||
// FIXME do non-destructive modification of the existing user data
|
||||
getEntityUserData = function(id) {
|
||||
var results = null;
|
||||
var properties = Entities.getEntityProperties(id, "userData");
|
||||
if (properties.userData) {
|
||||
try {
|
||||
results = JSON.parse(properties.userData);
|
||||
} catch(err) {
|
||||
logDebug(err);
|
||||
}
|
||||
}
|
||||
return results ? results : {};
|
||||
}
|
||||
|
||||
|
||||
// Non-destructively modify the user data of an entity.
|
||||
setEntityCustomData = function(customKey, id, data) {
|
||||
var userData = getEntityUserData(id);
|
||||
if (data == null) {
|
||||
delete userData[customKey];
|
||||
} else {
|
||||
userData[customKey] = data;
|
||||
}
|
||||
setEntityUserData(id, userData);
|
||||
}
|
||||
|
||||
getEntityCustomData = function(customKey, id, defaultValue) {
|
||||
var userData = getEntityUserData(id);
|
||||
if (undefined != userData[customKey]) {
|
||||
return userData[customKey];
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
mergeObjects = function(proto, custom) {
|
||||
var result = {};
|
||||
for (var attrname in proto) {
|
||||
result[attrname] = proto[attrname];
|
||||
}
|
||||
for (var attrname in custom) {
|
||||
result[attrname] = custom[attrname];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LOG_WARN = 1;
|
||||
|
||||
logWarn = function(str) {
|
||||
if (LOG_WARN) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR = 1;
|
||||
|
||||
logError = function(str) {
|
||||
if (LOG_ERROR) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO = 1;
|
||||
|
||||
logInfo = function(str) {
|
||||
if (LOG_INFO) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG = 0;
|
||||
|
||||
logDebug = function(str) {
|
||||
if (LOG_DEBUG) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_TRACE = 0;
|
||||
|
||||
logTrace = function(str) {
|
||||
if (LOG_TRACE) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the penetration between a point and a sphere (centered at the origin)
|
||||
// if point is inside sphere: returns true and stores the result in 'penetration'
|
||||
// (the vector that would move the point outside the sphere)
|
||||
// otherwise returns false
|
||||
findSphereHit = function(point, sphereRadius) {
|
||||
var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations
|
||||
var vectorLength = Vec3.length(point);
|
||||
if (vectorLength < EPSILON) {
|
||||
return true;
|
||||
}
|
||||
var distance = vectorLength - sphereRadius;
|
||||
if (distance < 0.0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
findSpherePointHit = function(sphereCenter, sphereRadius, point) {
|
||||
return findSphereHit(Vec3.subtract(point,sphereCenter), sphereRadius);
|
||||
}
|
||||
|
||||
findSphereSphereHit = function(firstCenter, firstRadius, secondCenter, secondRadius) {
|
||||
return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter);
|
||||
}
|
||||
|
||||
// Given a vec3 v, return a vec3 that is the same vector relative to the avatars
|
||||
// DEFAULT eye position, rotated into the avatars reference frame.
|
||||
getEyeRelativePosition = function(v) {
|
||||
return Vec3.sum(MyAvatar.getDefaultEyePosition(), Vec3.multiplyQbyV(MyAvatar.orientation, v));
|
||||
}
|
||||
|
||||
getAvatarRelativeRotation = function(q) {
|
||||
return Quat.multiply(MyAvatar.orientation, q);
|
||||
}
|
||||
|
||||
pointInExtents = function(point, minPoint, maxPoint) {
|
||||
return (point.x >= minPoint.x && point.x <= maxPoint.x) &&
|
||||
(point.y >= minPoint.y && point.y <= maxPoint.y) &&
|
||||
(point.z >= minPoint.z && point.z <= maxPoint.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an HSL color value to RGB. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||
* returns r, g, and b in the set [0, 255].
|
||||
*
|
||||
* @param Number h The hue
|
||||
* @param Number s The saturation
|
||||
* @param Number l The lightness
|
||||
* @return Array The RGB representation
|
||||
*/
|
||||
hslToRgb = function(hsl) {
|
||||
var r, g, b;
|
||||
if (hsl.s == 0) {
|
||||
r = g = b = hsl.l; // achromatic
|
||||
} else {
|
||||
var hue2rgb = function hue2rgb(p, q, t) {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
var q = hsl.l < 0.5 ? hsl.l * (1 + hsl.s) : hsl.l + hsl.s - hsl.l * hsl.s;
|
||||
var p = 2 * hsl.l - q;
|
||||
r = hue2rgb(p, q, hsl.h + 1 / 3);
|
||||
g = hue2rgb(p, q, hsl.h);
|
||||
b = hue2rgb(p, q, hsl.h - 1 / 3);
|
||||
}
|
||||
|
||||
return {
|
||||
red: Math.round(r * 255),
|
||||
green: Math.round(g * 255),
|
||||
blue: Math.round(b * 255)
|
||||
};
|
||||
}
|
||||
|
||||
map = function(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
orientationOf = function(vector) {
|
||||
var Y_AXIS = {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
};
|
||||
var X_AXIS = {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var theta = 0.0;
|
||||
|
||||
var RAD_TO_DEG = 180.0 / Math.PI;
|
||||
var direction, yaw, pitch;
|
||||
direction = Vec3.normalize(vector);
|
||||
yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
|
||||
pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
|
||||
return Quat.multiply(yaw, pitch);
|
||||
}
|
||||
|
||||
randFloat = function(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
||||
|
||||
randInt = function(low, high) {
|
||||
return Math.floor(randFloat(low, high));
|
||||
}
|
||||
|
||||
|
||||
randomColor = function() {
|
||||
return {
|
||||
red: randInt(0, 255),
|
||||
green: randInt(0, 255),
|
||||
blue: randInt(0, 255)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hexToRgb = function(hex) {
|
||||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ? {
|
||||
red: parseInt(result[1], 16),
|
||||
green: parseInt(result[2], 16),
|
||||
blue: parseInt(result[3], 16)
|
||||
} : null;
|
||||
}
|
||||
|
||||
calculateHandSizeRatio = function() {
|
||||
// Get the ratio of the current avatar's hand to Owen's hand
|
||||
|
||||
var standardCenterHandPoint = 0.11288;
|
||||
var jointNames = MyAvatar.getJointNames();
|
||||
//get distance from handJoint up to leftHandIndex3 as a proxy for center of hand
|
||||
var wristToFingertipDistance = 0;;
|
||||
for (var i = 0; i < jointNames.length; i++) {
|
||||
var jointName = jointNames[i];
|
||||
print(jointName)
|
||||
if (jointName.indexOf("LeftHandIndex") !== -1) {
|
||||
// translations are relative to parent joint, so simply add them together
|
||||
// joints face down the y-axis
|
||||
var translation = MyAvatar.getDefaultJointTranslation(i).y;
|
||||
wristToFingertipDistance += translation;
|
||||
}
|
||||
}
|
||||
// Right now units are in cm, so convert to meters
|
||||
wristToFingertipDistance /= 100;
|
||||
|
||||
var centerHandPoint = wristToFingertipDistance/2;
|
||||
|
||||
// Compare against standard hand (Owen)
|
||||
var handSizeRatio = centerHandPoint/standardCenterHandPoint;
|
||||
return handSizeRatio;
|
||||
}
|
||||
|
||||
clamp = function(val, min, max){
|
||||
return Math.max(min, Math.min(max, val))
|
||||
}
|
||||
|
||||
// flattens an array of arrays into a single array
|
||||
// example: flatten([[1], [3, 4], []) => [1, 3, 4]
|
||||
// NOTE: only does one level of flattening, it is not recursive.
|
||||
flatten = function(array) {
|
||||
return [].concat.apply([], array);
|
||||
}
|
||||
|
||||
getTabletWidthFromSettings = function () {
|
||||
var DEFAULT_TABLET_WIDTH = 0.4375;
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var toolbarMode = tablet.toolbarMode;
|
||||
var DEFAULT_DESKTOP_TABLET_SCALE = 75;
|
||||
var DEFAULT_HMD_TABLET_SCALE = 60;
|
||||
var tabletScalePercentage = DEFAULT_HMD_TABLET_SCALE;
|
||||
if (!toolbarMode) {
|
||||
if (HMD.active) {
|
||||
tabletScalePercentage = Settings.getValue("hmdTabletScale") || DEFAULT_HMD_TABLET_SCALE;
|
||||
} else {
|
||||
tabletScalePercentage = Settings.getValue("desktopTabletScale") || DEFAULT_DESKTOP_TABLET_SCALE;
|
||||
}
|
||||
}
|
||||
return DEFAULT_TABLET_WIDTH * (tabletScalePercentage / 100);
|
||||
};
|
||||
|
||||
resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) {
|
||||
|
||||
if (!HMD.tabletID || !HMD.tabletScreenID || !HMD.homeButtonID || !HMD.homeButtonHighlightID) {
|
||||
return;
|
||||
}
|
||||
var sensorScaleFactor = sensorToWorldScaleOverride || MyAvatar.sensorToWorldScale;
|
||||
var sensorScaleOffsetOverride = 1;
|
||||
var SENSOR_TO_ROOM_MATRIX = 65534;
|
||||
var parentJointIndex = newParentJointIndex || Overlays.getProperty(HMD.tabletID, "parentJointIndex");
|
||||
if (parentJointIndex === SENSOR_TO_ROOM_MATRIX) {
|
||||
sensorScaleOffsetOverride = 1 / sensorScaleFactor;
|
||||
}
|
||||
|
||||
|
||||
// will need to be recaclulated if dimensions of fbx model change.
|
||||
var TABLET_NATURAL_DIMENSIONS = {x: 32.083, y: 48.553, z: 2.269};
|
||||
var DEFAULT_DPI = 31;
|
||||
var DEFAULT_WIDTH = 0.4375;
|
||||
|
||||
// scale factor of natural tablet dimensions.
|
||||
var tabletWidth = (width || DEFAULT_WIDTH) * sensorScaleFactor;
|
||||
var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x;
|
||||
var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor;
|
||||
var tabletDepth = TABLET_NATURAL_DIMENSIONS.z * tabletScaleFactor;
|
||||
var tabletDpi = DEFAULT_DPI * (DEFAULT_WIDTH / tabletWidth);
|
||||
|
||||
// update tablet model dimensions
|
||||
Entities.editEntity(HMD.tabletID, {
|
||||
dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth }
|
||||
});
|
||||
|
||||
// update webOverlay
|
||||
var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.5) * sensorScaleOffsetOverride;
|
||||
var WEB_ENTITY_Y_OFFSET = 1.25 * tabletScaleFactor * sensorScaleOffsetOverride;
|
||||
var screenWidth = 0.9367 * tabletWidth;
|
||||
var screenHeight = 0.9000 * tabletHeight;
|
||||
var landscape = Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape;
|
||||
Entities.editEntity(HMD.tabletScreenID, {
|
||||
localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET},
|
||||
dimensions: {x: landscape ? screenHeight : screenWidth, y: landscape ? screenWidth : screenHeight, z: 1.0},
|
||||
dpi: tabletDpi
|
||||
});
|
||||
|
||||
// update homeButton
|
||||
var homeButtonDim = 4.0 * tabletScaleFactor / 1.5;
|
||||
var HOME_BUTTON_X_OFFSET = 0.00079 * sensorScaleOffsetOverride * sensorScaleFactor;
|
||||
var HOME_BUTTON_Y_OFFSET = -1 * ((tabletHeight / 2) - (4.0 * tabletScaleFactor / 2)) * sensorScaleOffsetOverride;
|
||||
var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride;
|
||||
Entities.editEntity(HMD.homeButtonID, {
|
||||
localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
|
||||
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
|
||||
});
|
||||
|
||||
Entities.editEntity(HMD.homeButtonHighlightID, {
|
||||
localPosition: { x: -HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
|
||||
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
reparentAndScaleTablet = function(width, reparentProps) {
|
||||
|
||||
if (!HMD.tabletID || !HMD.tabletScreenID || !HMD.homeButtonID || !HMD.homeButtonHighlightID) {
|
||||
return;
|
||||
}
|
||||
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
|
||||
var sensorScaleOffsetOverride = 1;
|
||||
var SENSOR_TO_ROOM_MATRIX = 65534;
|
||||
var parentJointIndex = reparentProps.parentJointIndex;
|
||||
if (parentJointIndex === SENSOR_TO_ROOM_MATRIX) {
|
||||
sensorScaleOffsetOverride = 1 / sensorScaleFactor;
|
||||
}
|
||||
|
||||
|
||||
// will need to be recaclulated if dimensions of fbx model change.
|
||||
var TABLET_NATURAL_DIMENSIONS = {x: 32.083, y: 48.553, z: 2.269};
|
||||
var DEFAULT_DPI = 31;
|
||||
var DEFAULT_WIDTH = 0.4375;
|
||||
|
||||
// scale factor of natural tablet dimensions.
|
||||
var tabletWidth = (width || DEFAULT_WIDTH) * sensorScaleFactor;
|
||||
var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x;
|
||||
var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor;
|
||||
var tabletDepth = TABLET_NATURAL_DIMENSIONS.z * tabletScaleFactor;
|
||||
var tabletDpi = DEFAULT_DPI * (DEFAULT_WIDTH / tabletWidth);
|
||||
|
||||
// update tablet model dimensions
|
||||
|
||||
Entities.editEntity(HMD.tabletID, {
|
||||
parentID: reparentProps.parentID,
|
||||
parentJointIndex: reparentProps.parentJointIndex,
|
||||
dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth}
|
||||
});
|
||||
// update webOverlay
|
||||
var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.5) * sensorScaleOffsetOverride;
|
||||
var WEB_ENTITY_Y_OFFSET = 1.25 * tabletScaleFactor * sensorScaleOffsetOverride;
|
||||
var screenWidth = 0.9367 * tabletWidth;
|
||||
var screenHeight = 0.9000 * tabletHeight;
|
||||
var landscape = Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape;
|
||||
Entities.editEntity(HMD.tabletScreenID, {
|
||||
localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET},
|
||||
dimensions: {x: landscape ? screenHeight : screenWidth, y: landscape ? screenWidth : screenHeight, z: 1.0},
|
||||
dpi: tabletDpi
|
||||
});
|
||||
|
||||
// update homeButton
|
||||
var homeButtonDim = 4.0 * tabletScaleFactor / 1.5;
|
||||
var HOME_BUTTON_X_OFFSET = 0.00079 * sensorScaleOffsetOverride * sensorScaleFactor;
|
||||
var HOME_BUTTON_Y_OFFSET = -1 * ((tabletHeight / 2) - (4.0 * tabletScaleFactor / 2)) * sensorScaleOffsetOverride;
|
||||
var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride;
|
||||
Entities.editEntity(HMD.homeButtonID, {
|
||||
localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
|
||||
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
|
||||
});
|
||||
|
||||
Entities.editEntity(HMD.homeButtonHighlightID, {
|
||||
localPosition: { x: -HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
|
||||
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
|
||||
});
|
||||
}
|
||||
|
||||
getMainTabletIDs = function () {
|
||||
var tabletIDs = [];
|
||||
if (HMD.tabletID) {
|
||||
tabletIDs.push(HMD.tabletID);
|
||||
}
|
||||
if (HMD.tabletScreenID) {
|
||||
tabletIDs.push(HMD.tabletScreenID);
|
||||
}
|
||||
if (HMD.homeButtonID) {
|
||||
tabletIDs.push(HMD.homeButtonID);
|
||||
}
|
||||
return tabletIDs;
|
||||
};
|
Loading…
Reference in a new issue