670 lines
No EOL
24 KiB
JavaScript
670 lines
No EOL
24 KiB
JavaScript
//
|
|
// NewFleshApp.js
|
|
//
|
|
// Created by David Back, Sam Gondelman, and Parker Richard on 10/26/18.
|
|
// Copyright 2018 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() { // BEGIN LOCAL_SCOPE
|
|
|
|
const APP_BASE = "NewFleshApp.html";
|
|
const APP_URL = Script.resolvePath(APP_BASE);
|
|
const APP_LABEL = "NEW FLESH";
|
|
const APP_SORT_ORDER = 12;
|
|
|
|
const ENTITY_NAME = "hackathon2018.newflesh";
|
|
|
|
const LEFT_FOREARM_JOINT = "LeftForeArm";
|
|
const LEFT_HAND_JOINT = "LeftHand";
|
|
const RIGHT_HAND_JOINT = "RightHand";
|
|
const WATCH_Z_OFFSET = -0.05;
|
|
const WATCH_FOREARM_HAND_DISTANCE = 0.8;
|
|
|
|
// Images by Parker Richard
|
|
const RESET_URL = Script.resolvePath("textures/reset.png");
|
|
const SKULL_URL = Script.resolvePath("textures/skull-effect.png");
|
|
const GHOST_URL = Script.resolvePath("textures/ghost-effect.png");
|
|
const PARTICLE_URL = Script.resolvePath("textures/particle-effect.png");
|
|
const DARKNESS_URL = Script.resolvePath("textures/darkness-effect.png");
|
|
const PARTICLE_TEXTURE = Script.resolvePath("textures/glow-particle.png");
|
|
|
|
// https://poly.google.com/view/dqkji8vm0-s
|
|
const WATCH_MODEL = Script.resolvePath("models/watchModel.fbx");
|
|
// https://www.cgbookcase.com/textures/rock-12
|
|
const MATERIAL_JSON = Script.resolvePath("materials/NewFleshMaterial.json");
|
|
// https://poly.google.com/view/3SRO1k1Brxe
|
|
const SKULL_MODEL = Script.resolvePath("models/skull.obj");
|
|
|
|
const APP_ICON_ACTIVE = Script.resolvePath("newflesh-a.svg");
|
|
const APP_ICON_INACTIVE = Script.resolvePath("newflesh-i.svg");
|
|
|
|
// Cleanup in case of crash before
|
|
function cleanupFromBefore() {
|
|
const ENTITY_SEARCH_DISTANCE = 1000;
|
|
var toDelete = Entities.findEntitiesByName(ENTITY_NAME, MyAvatar.position, ENTITY_SEARCH_DISTANCE);
|
|
for (var i = 0; i < toDelete.length; i++) {
|
|
Entities.deleteEntity(toDelete[i]);
|
|
}
|
|
}
|
|
cleanupFromBefore();
|
|
|
|
var entities = [];
|
|
var appEntities = [];
|
|
var overlays = [];
|
|
|
|
var watchEntity = null;
|
|
var projectionEffect = null;
|
|
var background = null;
|
|
|
|
var isActive = false;
|
|
var watchOpen = false;
|
|
var currentSelectionIndex = -1;
|
|
var t = 0;
|
|
const NUM_BUTTONS = 5;
|
|
const BUTTON_WIDTH = 0.1;
|
|
const BUTTON_HEIGHT = 0.15;
|
|
var buttons = [
|
|
{
|
|
url: RESET_URL
|
|
},
|
|
{
|
|
url: SKULL_URL,
|
|
open: function() {
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "Model",
|
|
modelURL: SKULL_MODEL,
|
|
parentID: MyAvatar.SELF_ID,
|
|
dimensions: { x: 0.6, y: 0.6, z: 0.6 },
|
|
parentJointIndex: MyAvatar.getJointIndex("Head"),
|
|
localPosition: { x: 0, y: 0.5 * Vec3.distance(MyAvatar.getJointPosition("HeadTop_End"), MyAvatar.getJointPosition("Head")), z: 0 },
|
|
localRotation: Quat.fromPitchYawRollDegrees(0, -20, 0),
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "ParticleEffect",
|
|
parentID: MyAvatar.SELF_ID,
|
|
parentJointIndex: MyAvatar.getJointIndex("HeadTop_End"),
|
|
textures: PARTICLE_TEXTURE,
|
|
maxParticles: 100,
|
|
emitRate: 100,
|
|
lifespan: 1.0,
|
|
emitSpeed: 0.3,
|
|
speedSpread: 0,
|
|
particleRadius: 0.03,
|
|
radiusStart: 0,
|
|
radiusFinish: 0.05,
|
|
color: { red: 255, blue: 0, green: 0},
|
|
colorSpread: { red: 100, blue: 100, green: 100},
|
|
colorStart: { red: 200, blue: 200, green: 200},
|
|
colorFinish: { red: 255, blue: 0, green: 255},
|
|
emitAcceleration: {x: 0, y: 0.5, z: 0},
|
|
alpha: 1,
|
|
alphaStart: 0,
|
|
alphaFinish: 0,
|
|
polarFinish: 3.141592653589793,
|
|
emitterShouldTrail: false,
|
|
isEmitting: true,
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
}
|
|
},
|
|
{
|
|
url: GHOST_URL,
|
|
open: function() {
|
|
t = 0;
|
|
var model = Graphics.getModel(MyAvatar.SELF_ID);
|
|
if (model) {
|
|
var partID = 0;
|
|
for (var i = 0; i < model.meshes.length; i++) {
|
|
var mesh = model.meshes[i];
|
|
for (var j = 0; j < mesh.parts.length; j++) {
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "Material",
|
|
position: MyAvatar.position,
|
|
materialURL: MATERIAL_JSON + "?ghost",
|
|
priority: 1,
|
|
parentID: MyAvatar.SELF_ID,
|
|
parentMaterialName: partID,
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
partID++;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
update: function(dt) {
|
|
for (var i = 0; i < appEntities.length; i++) {
|
|
Entities.editEntity(appEntities[i], {
|
|
materialMappingPos: { x: Math.cos(t * 0.03), y: Math.sin(t * 0.02) },
|
|
materialMappingScale: {x: 0.1 * Math.sin(t * 0.12) + 0.3, y: 0.1 * Math.sin(t * 0.09) + 0.3 }
|
|
});
|
|
}
|
|
t = t + dt;
|
|
}
|
|
},
|
|
{
|
|
url: PARTICLE_URL,
|
|
open: function() {
|
|
// Hide my avatar
|
|
var model = Graphics.getModel(MyAvatar.SELF_ID);
|
|
if (model) {
|
|
var partID = 0;
|
|
for (var i = 0; i < model.meshes.length; i++) {
|
|
var mesh = model.meshes[i];
|
|
for (var j = 0; j < mesh.parts.length; j++) {
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "Material",
|
|
position: MyAvatar.position,
|
|
materialURL: MATERIAL_JSON + "?invisible",
|
|
priority: 1,
|
|
parentID: MyAvatar.SELF_ID,
|
|
parentMaterialName: partID,
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
partID++;
|
|
}
|
|
}
|
|
}
|
|
|
|
const HALF_PARTICLE_EFFECT_COUNT = 15;
|
|
var jointNames = MyAvatar.getJointNames();
|
|
for (var i = 0; i < HALF_PARTICLE_EFFECT_COUNT; i++) {
|
|
if (jointNames.length == 0) {
|
|
break;
|
|
}
|
|
var index = Math.floor(Math.random() * jointNames.length);
|
|
var jointIndex = MyAvatar.getJointIndex(jointNames[index]);
|
|
jointNames.splice(index, 1);
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "ParticleEffect",
|
|
parentID: MyAvatar.SELF_ID,
|
|
parentJointIndex: jointIndex,
|
|
textures: PARTICLE_TEXTURE,
|
|
maxParticles: 25,
|
|
emitRate: 10,
|
|
lifespan: 2.5,
|
|
emitSpeed: 0.0,
|
|
speedSpread: 0.0,
|
|
particleRadius: 0.025,
|
|
radiusStart: 0.0,
|
|
radiusFinish: 0.0,
|
|
alpha: 0.5,
|
|
alphaStart: 0.0,
|
|
alphaFinish: 0.0,
|
|
color: { red: 255, green: 255, blue: 255 },
|
|
colorSpread: { red: 100, green: 100, blue: 100 },
|
|
emitAcceleration: { x: 0, y: 0, z: 0 },
|
|
emitDimensions: { x: 0.1, y: 0.1, z: 0.5 },
|
|
polarFinish: Math.PI / 2.0,
|
|
emitterShouldTrail: true,
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
|
|
index = Math.floor(Math.random() * jointNames.length);
|
|
jointIndex = MyAvatar.getJointIndex(jointNames[index]);
|
|
jointNames.splice(index, 1);
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "ParticleEffect",
|
|
parentID: MyAvatar.SELF_ID,
|
|
parentJointIndex: jointIndex,
|
|
textures: PARTICLE_TEXTURE,
|
|
maxParticles: 25,
|
|
emitRate: 10,
|
|
lifespan: 2.5,
|
|
emitSpeed: 0.0,
|
|
speedSpread: 0.0,
|
|
particleRadius: 0.025,
|
|
radiusStart: 0.0,
|
|
radiusFinish: 0.0,
|
|
alpha: 0.5,
|
|
alphaStart: 0.0,
|
|
alphaFinish: 0.0,
|
|
color: { red: 255, green: 255, blue: 255 },
|
|
colorSpread: { red: 100, green: 100, blue: 100 },
|
|
emitAcceleration: { x: 0, y: 0, z: 0 },
|
|
emitDimensions: { x: 0.1, y: 0.1, z: 0.5 },
|
|
polarFinish: Math.PI / 2.0,
|
|
emitterShouldTrail: false,
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
}
|
|
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "ParticleEffect",
|
|
parentID: MyAvatar.SELF_ID,
|
|
parentJointIndex: MyAvatar.getJointIndex("LeftEye"),
|
|
textures: PARTICLE_TEXTURE,
|
|
maxParticles: 100,
|
|
emitRate: 0,
|
|
lifespan: 1.0,
|
|
emitSpeed: 0.6,
|
|
speedSpread: 0,
|
|
emitDimensions: { x: 0.5, y: 0.5, z: 0.01 },
|
|
emitOrientation: Quat.fromPitchYawRollDegrees(10, 0, 90),
|
|
particleRadius: 0.03,
|
|
radiusStart: 0,
|
|
radiusFinish: 0.05,
|
|
color: { red: 255, blue: 0, green: 0},
|
|
colorSpread: { red: 100, blue: 100, green: 100},
|
|
colorStart: { red: 200, blue: 200, green: 200},
|
|
colorFinish: { red: 255, blue: 0, green: 255},
|
|
emitAcceleration: {x: 0, y: 0, z: 0},
|
|
alpha: 1,
|
|
alphaStart: 0,
|
|
alphaFinish: 0,
|
|
polarFinish: 0.0872664600610733,
|
|
emitterShouldTrail: false,
|
|
isEmitting: false,
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "ParticleEffect",
|
|
parentID: MyAvatar.SELF_ID,
|
|
parentJointIndex: MyAvatar.getJointIndex("RightEye"),
|
|
textures: PARTICLE_TEXTURE,
|
|
maxParticles: 100,
|
|
emitRate: 0,
|
|
lifespan: 1.0,
|
|
emitSpeed: 0.6,
|
|
speedSpread: 0,
|
|
emitDimensions: { x: 0.5, y: 0.5, z: 0.01 },
|
|
emitOrientation: Quat.fromPitchYawRollDegrees(-10, 0, 90),
|
|
particleRadius: 0.03,
|
|
radiusStart: 0,
|
|
radiusFinish: 0.05,
|
|
color: { red: 255, blue: 0, green: 0},
|
|
colorSpread: { red: 100, blue: 100, green: 100},
|
|
colorStart: { red: 200, blue: 200, green: 200},
|
|
colorFinish: { red: 255, blue: 0, green: 255},
|
|
emitAcceleration: {x: 0, y: 0, z: 0},
|
|
alpha: 1,
|
|
alphaStart: 0,
|
|
alphaFinish: 0,
|
|
polarFinish: 0.0872664600610733,
|
|
emitterShouldTrail: false,
|
|
isEmitting: false,
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
},
|
|
update: function() {
|
|
const PARTICLE_AUDIO_THRESHOLD = 0.01;
|
|
const MAX_PARTICLE_EMIT_RATE = 70;
|
|
Entities.editEntity(appEntities[appEntities.length - 2], {
|
|
isEmitting: Audio.inputLevel > PARTICLE_AUDIO_THRESHOLD,
|
|
emitRate: MAX_PARTICLE_EMIT_RATE * Audio.inputLevel
|
|
});
|
|
Entities.editEntity(appEntities[appEntities.length - 1], {
|
|
isEmitting: Audio.inputLevel > PARTICLE_AUDIO_THRESHOLD,
|
|
emitRate: MAX_PARTICLE_EMIT_RATE * Audio.inputLevel
|
|
});
|
|
}
|
|
},
|
|
{
|
|
url: DARKNESS_URL,
|
|
open: function() {
|
|
t = 0;
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "Zone",
|
|
parentID: MyAvatar.SELF_ID,
|
|
dimensions: { x: 50.0, y: 50.0, z: 50.0 },
|
|
hazeMode: "enabled",
|
|
haze: {
|
|
hazeColor: { red: 0, green: 0, blue: 0 },
|
|
hazeRange: 1.0
|
|
},
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
appEntities.push(Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "ParticleEffect",
|
|
parentID: MyAvatar.SELF_ID,
|
|
textures: PARTICLE_TEXTURE,
|
|
maxParticles: 100,
|
|
emitRate: 10,
|
|
lifespan: 10,
|
|
emitSpeed: 0.0,
|
|
speedSpread: 0.0,
|
|
particleRadius: 5,
|
|
radiusSpread: 2,
|
|
alpha: 1.0,
|
|
alphaStart: 0.0,
|
|
alphaFinish: 0.0,
|
|
color: { red: 255, green: 0, blue: 0 },
|
|
colorSpread: { red: 100, green: 0, blue: 0 },
|
|
spinSpread: 2.0 * Math.PI,
|
|
emitAcceleration: { x: 0, y: 0, z: 0 },
|
|
accelerationSpread: { x: 0.5, y: 0.5, z: 0.5 },
|
|
emitDimensions: { x: 50.0, y: 25.0, z: 50.0 },
|
|
polarFinish: Math.PI,
|
|
emitterShouldTrail: true,
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true));
|
|
},
|
|
update: function(dt) {
|
|
Entities.editEntity(appEntities[0], {
|
|
haze: {
|
|
hazeRange: 1.0 + 3.0 * Math.sin(t * 0.5) * Math.sin(t * 0.5)
|
|
}
|
|
});
|
|
t = t + dt;
|
|
}
|
|
}
|
|
];
|
|
|
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
|
var appButton = tablet.addButton({
|
|
text: APP_LABEL,
|
|
icon: APP_ICON_INACTIVE,
|
|
activeIcon: APP_ICON_ACTIVE,
|
|
sortOrder: APP_SORT_ORDER
|
|
});
|
|
|
|
function onClicked() {
|
|
if (isActive) {
|
|
tablet.gotoHomeScreen();
|
|
} else {
|
|
isActive = true;
|
|
tablet.gotoWebScreen(APP_URL);
|
|
}
|
|
}
|
|
|
|
function onScreenChanged(type, url) {
|
|
isActive = type === "Web" && (url.indexOf(APP_BASE) === url.length - APP_BASE.length);
|
|
appButton.editProperties({ isActive: isActive });
|
|
}
|
|
|
|
// Handle the events we're receiving from the web UI
|
|
function onWebEventReceived(event) {
|
|
// Converts the event to a JavasScript Object
|
|
if (typeof event === "string") {
|
|
event = JSON.parse(event);
|
|
}
|
|
if (event.type === "activate") {
|
|
activateApp();
|
|
} else if (event.type === "deactivate") {
|
|
deactivateApp();
|
|
}
|
|
}
|
|
|
|
appButton.clicked.connect(onClicked);
|
|
tablet.screenChanged.connect(onScreenChanged);
|
|
tablet.webEventReceived.connect(onWebEventReceived);
|
|
|
|
function activateApp() {
|
|
deactivateApp();
|
|
|
|
watchEntity = Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "Model",
|
|
modelURL: WATCH_MODEL,
|
|
dimensions: { x: 0.1, y: 0.1, z: 0.02 },
|
|
parentID: MyAvatar.SELF_ID,
|
|
parentJointIndex: MyAvatar.getJointIndex(LEFT_FOREARM_JOINT),
|
|
localPosition: { x: WATCH_Z_OFFSET, y: WATCH_FOREARM_HAND_DISTANCE * Vec3.distance(MyAvatar.getJointPosition(LEFT_FOREARM_JOINT), MyAvatar.getJointPosition(LEFT_HAND_JOINT)), z: WATCH_Z_OFFSET },
|
|
localRotation: Quat.fromPitchYawRollDegrees(0, 45, 0),
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true);
|
|
entities.push(watchEntity);
|
|
|
|
projectionEffect = Entities.addEntity({
|
|
name: ENTITY_NAME,
|
|
type: "ParticleEffect",
|
|
parentID: watchEntity,
|
|
textures: PARTICLE_TEXTURE,
|
|
maxParticles: 10,
|
|
lifespan: 0.4,
|
|
emitRate: 10,
|
|
emitSpeed: 0.5,
|
|
speedSpread: 0,
|
|
alpha: 0,
|
|
alphaStart: 0.5,
|
|
particleRadius: 0.15,
|
|
radiusStart: 0,
|
|
color: { red: 18, green: 164, blue: 222 },
|
|
colorSpread: { red: 100, green: 50, blue: 50 },
|
|
emitAcceleration: { x: 0, y: 0.5, z: 0 },
|
|
accelerationSpread: { x: 0.5, y: 0, z: 0.5 },
|
|
emitOrientation: Quat.fromPitchYawRollDegrees(-180, 0, 0),
|
|
isEmitting: false,
|
|
userData: JSON.stringify({ grabbableKey: { grabbable: false } })
|
|
}, true);
|
|
entities.push(projectionEffect);
|
|
|
|
background = Overlays.addOverlay("cube", {
|
|
dimensions: { x: (NUM_BUTTONS - 1) * BUTTON_WIDTH, y: BUTTON_HEIGHT, z: 0.01 },
|
|
alpha: 0.0,
|
|
visible: true,
|
|
solid: true,
|
|
localPosition: { x: 0, y: 0., z: -0.15 },
|
|
localOrientation: Quat.fromPitchYawRollDegrees(-100, 0, 120),
|
|
parentID: watchEntity,
|
|
ignorePickIntersection: true
|
|
});
|
|
overlays.push(background);
|
|
|
|
for (var i = 0; i < NUM_BUTTONS; i++) {
|
|
var buttonInfo = getButtonInfo(i);
|
|
overlays.push(Overlays.addOverlay("image3d", {
|
|
url: buttons[buttonInfo.index].url,
|
|
dimensions: { x: BUTTON_WIDTH, y: BUTTON_HEIGHT, z: 0.01 },
|
|
alpha: 0.0,
|
|
visible: true,
|
|
localPosition: buttonInfo.pos,
|
|
localOrientation: Quat.fromPitchYawRollDegrees(0, 0, 0),
|
|
parentID: background,
|
|
ignorePickIntersection: true
|
|
}));
|
|
}
|
|
}
|
|
|
|
function deactivateApp() {
|
|
if (currentSelectionIndex !== -1) {
|
|
deactivateEffect(currentSelectionIndex);
|
|
}
|
|
for (var i = 0; i < entities.length; i++) {
|
|
Entities.deleteEntity(entities[i]);
|
|
}
|
|
for (var i = 0; i < overlays.length; i++) {
|
|
Overlays.deleteOverlay(overlays[i]);
|
|
}
|
|
entities = [];
|
|
overlays = [];
|
|
watchEntity = null;
|
|
projectionEffect = null;
|
|
background = null;
|
|
watchOpen = false;
|
|
}
|
|
|
|
function openWatch() {
|
|
Overlays.editOverlay(background, {
|
|
ignorePickIntersection: false
|
|
});
|
|
|
|
Entities.editEntity(projectionEffect, {
|
|
isEmitting: true
|
|
});
|
|
|
|
watchOpen = true;
|
|
}
|
|
|
|
function closeWatch() {
|
|
Overlays.editOverlay(background, {
|
|
ignorePickIntersection: true
|
|
});
|
|
|
|
for (var i = 0; i < NUM_BUTTONS; i++) {
|
|
Overlays.editOverlay(overlays[i + 1], {
|
|
alpha: 0.0
|
|
});
|
|
}
|
|
|
|
Entities.editEntity(projectionEffect, {
|
|
isEmitting: false
|
|
});
|
|
|
|
watchOpen = false;
|
|
}
|
|
|
|
function checkWatchActivation() {
|
|
var watchPosition = Entities.getEntityProperties(watchEntity, ['position']).position;
|
|
var rightHandPosition = MyAvatar.getJointPosition(RIGHT_HAND_JOINT);
|
|
var distance = Vec3.distance(watchPosition, rightHandPosition);
|
|
|
|
const ACTIVATION_DISTANCE = 0.4;
|
|
const DEACTIVATION_DISTANCE = 0.5;
|
|
if (watchOpen && distance > DEACTIVATION_DISTANCE) {
|
|
closeWatch();
|
|
} else if (!watchOpen && distance < ACTIVATION_DISTANCE) {
|
|
openWatch();
|
|
}
|
|
}
|
|
|
|
function activateEffect(i) {
|
|
if (buttons[i].open !== undefined) {
|
|
buttons[i].open();
|
|
}
|
|
}
|
|
|
|
function updateEffect(i, dt) {
|
|
if (buttons[i].update !== undefined) {
|
|
buttons[i].update(dt);
|
|
}
|
|
}
|
|
|
|
function deactivateEffect(i) {
|
|
if (buttons[i].close !== undefined) {
|
|
buttons[i].close();
|
|
}
|
|
for (var i = 0; i < appEntities.length; i++) {
|
|
Entities.deleteEntity(appEntities[i]);
|
|
}
|
|
appEntities = [];
|
|
}
|
|
|
|
var scroll = 0.0;
|
|
var scrollRate = 0;
|
|
var slowScrolling = true;
|
|
function getButtonInfo(i) {
|
|
var index = ((buttons.length - 1) + i) % buttons.length;
|
|
|
|
var posOffset = BUTTON_WIDTH * (i + scroll);
|
|
if (posOffset < 0.0) {
|
|
posOffset = posOffset + NUM_BUTTONS * Math.abs(Math.floor(posOffset / BUTTON_WIDTH));
|
|
}
|
|
posOffset = posOffset % (BUTTON_WIDTH * NUM_BUTTONS);
|
|
|
|
var HALF_NAX_OFFSET = NUM_BUTTONS / 2 * BUTTON_WIDTH;
|
|
var alpha = Math.abs(posOffset - HALF_NAX_OFFSET) / HALF_NAX_OFFSET;
|
|
alpha = alpha * 1.2;
|
|
alpha = Math.min(1.0, Math.max(0.0, alpha));
|
|
return {
|
|
index: index,
|
|
alpha: 1.0 - alpha,
|
|
pos: { x: -NUM_BUTTONS * BUTTON_WIDTH / 2 + posOffset, y: 0, z: 0 }
|
|
}
|
|
}
|
|
|
|
function update(dt) {
|
|
checkWatchActivation();
|
|
if (currentSelectionIndex !== -1) {
|
|
updateEffect(currentSelectionIndex, dt);
|
|
}
|
|
scroll = scroll + scrollRate * dt;
|
|
if (watchOpen) {
|
|
for (var i = 0; i < NUM_BUTTONS; i++) {
|
|
var buttonInfo = getButtonInfo(i);
|
|
Overlays.editOverlay(overlays[i + 1], {
|
|
//url: buttons[buttonInfo.index].url,
|
|
alpha: buttonInfo.alpha,
|
|
localPosition: buttonInfo.pos
|
|
});
|
|
}
|
|
}
|
|
if (slowScrolling) {
|
|
scrollRate = 0.9 * scrollRate;
|
|
}
|
|
}
|
|
Script.update.connect(update);
|
|
|
|
var startPress;
|
|
var prevPos2D = undefined;
|
|
var avgScroll = 0;
|
|
function mousePressOnOverlay(id, event) {
|
|
if (Pointers.isRightHand(event.id) &&
|
|
id === background && event.isPrimaryButton) {
|
|
startPress = new Date();
|
|
prevPos2D = event.pos2D;
|
|
scrollRate = 0.0;
|
|
slowScrolling = true;
|
|
avgScroll = 0;
|
|
}
|
|
}
|
|
Overlays.mousePressOnOverlay.connect(mousePressOnOverlay);
|
|
|
|
function mouseReleaseOnOverlay(id, event) {
|
|
if (Pointers.isRightHand(event.id) &&
|
|
id === background && event.isPrimaryButton) {
|
|
var endPress = new Date();
|
|
if (endPress - startPress < 200 && avgScroll < 0.01) {
|
|
var index = prevPos2D.x / BUTTON_WIDTH - scroll;
|
|
if (index < 0.0) {
|
|
index = index + NUM_BUTTONS * Math.abs(Math.floor(index / NUM_BUTTONS));
|
|
}
|
|
index = Math.floor(index % NUM_BUTTONS);
|
|
|
|
if (currentSelectionIndex !== -1) {
|
|
deactivateEffect(currentSelectionIndex);
|
|
}
|
|
currentSelectionIndex = index;
|
|
activateEffect(currentSelectionIndex);
|
|
}
|
|
scrollRate = 1000.0 * avgScroll;
|
|
prevPos2D = undefined;
|
|
avgScroll = 0;
|
|
}
|
|
}
|
|
Overlays.mouseReleaseOnOverlay.connect(mouseReleaseOnOverlay);
|
|
|
|
function mouseMoveOnOverlay(id, event) {
|
|
if (Pointers.isRightHand(event.id) &&
|
|
id === background && prevPos2D !== undefined) {
|
|
var diff = event.pos2D.x - prevPos2D.x;
|
|
scroll = scroll + diff / BUTTON_WIDTH;
|
|
if (avgScroll == 0) {
|
|
avgScroll = diff;
|
|
} else {
|
|
avgScroll = 0.9 * avgScroll + 0.1 * diff;
|
|
}
|
|
prevPos2D = event.pos2D;
|
|
}
|
|
}
|
|
Overlays.mouseMoveOnOverlay.connect(mouseMoveOnOverlay);
|
|
|
|
function skeletonModelURLChanged() {
|
|
if (currentSelectionIndex !== -1) {
|
|
deactivateEffect(currentSelectionIndex);
|
|
activateEffect(currentSelectionIndex);
|
|
}
|
|
}
|
|
MyAvatar.skeletonModelURLChanged.connect(skeletonModelURLChanged);
|
|
|
|
function shutdown() {
|
|
deactivateApp();
|
|
tablet.removeButton(appButton);
|
|
}
|
|
Script.scriptEnding.connect(shutdown);
|
|
|
|
}()); // END LOCAL_SCOPE
|