content/hifi-content/davidback/oldstuff/hackathonn/NewFleshWatch.js
2022-02-13 22:49:05 +01:00

640 lines
No EOL
19 KiB
JavaScript

//
// NewFleshWatch.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 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/HackathonMaterial.json");
// https://poly.google.com/view/3SRO1k1Brxe
const SKULL_MODEL = Script.resolvePath("models/skull.obj");
// 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 end = {
type: "sphere",
dimensions: { x: 0.05, y: 0.05, z: 0.05 },
solid: true,
color: { red: 255, green: 0, blue: 0 },
alpha: 0.9,
ignorePickIntersection: true,
visible: true
};
var renderStates = [
{name: "on", end: end}
];
var pointer = Pointers.createPointer(PickType.Ray, {
joint: "Mouse",
filter: Picks.PICK_OVERLAYS,
renderStates: renderStates,
triggers: [ {action: Controller.Hardware.Keyboard.LeftMouseButton, button: "Focus"}, {action: Controller.Hardware.Keyboard.LeftMouseButton, button: "Primary"} ],
hover: true,
enabled: true
});
Pointers.setRenderState(pointer, "on");
var watchOpen = false;
var debugWatchOpen = 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)
}, 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
}, 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
}, 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
}, 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
}, 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
}, 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
}, 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
}, 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
}
}, 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
}, 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;
}
}
];
function openApp(i) {
if (buttons[i].open !== undefined) {
buttons[i].open();
}
}
function updateApp(i, dt) {
if (buttons[i].update !== undefined) {
buttons[i].update(dt);
}
}
function closeApp(i) {
if (buttons[i].close !== undefined) {
buttons[i].close();
}
for (var i = 0; i < appEntities.length; i++) {
Entities.deleteEntity(appEntities[i]);
}
appEntities = [];
}
var 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: 0, y: WATCH_FOREARM_HAND_DISTANCE * Vec3.distance(MyAvatar.getJointPosition(LEFT_FOREARM_JOINT), MyAvatar.getJointPosition(LEFT_HAND_JOINT)), z: WATCH_Z_OFFSET }
}, true);
entities.push(watchEntity);
var projectionEffect = Entities.addEntity({
name: ENTITY_NAME,
type: "ParticleEffect",
parentID: watchEntity,
textures: PARTICLE_TEXTURE,
maxParticles: 10,
lifespan: 1.0,
emitRate: 10,
emitSpeed: 0.1,
speedSpread: 0.1,
alpha: 0.5,
alphaStart: 0,
alphaFinish: 0,
particleRadius: 0.05,
radiusFinish: 0.1,
radiusStart: 0,
color: { red: 18, green: 164, blue: 222 },
colorSpread: { red: 50, green: 50, blue: 100 },
emitAcceleration: { x: 0, y: -0.8, z: 0 },
emitOrientation: Quat.fromPitchYawRollDegrees(180, 0, 0),
isEmitting: false
}, true);
entities.push(projectionEffect);
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 }
}
}
var 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, 90),
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 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 checkActivation() {
var watchPosition = Entities.getEntityProperties(watchEntity, ['position']).position;
var rightHandPosition = MyAvatar.getJointPosition(RIGHT_HAND_JOINT);
var distance = Vec3.distance(watchPosition, rightHandPosition);
const ACTIVATION_DISTANCE = 0.25;
const DEACTIVATION_DISTANCE = 0.3;
if (watchOpen && distance > DEACTIVATION_DISTANCE && !debugWatchOpen) {
closeWatch();
} else if (!watchOpen && distance < ACTIVATION_DISTANCE) {
openWatch();
}
}
function update(dt) {
checkActivation();
if (currentSelectionIndex != -1) {
updateApp(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);
function keyPressEvent(event) {
if (event.text === "8") {
scrollRate = scrollRate - 1.0;
slowScrolling = false;
} else if (event.text === "9") {
scrollRate = scrollRate + 1.0;
slowScrolling = false;
} else if (event.text === "0") {
scrollRate = 0;
slowScrolling = false;
} else if (event.text === "u") {
scroll = scroll - 1;
slowScrolling = false;
} else if (event.text === "i") {
scroll = scroll + 1;
slowScrolling = false;
} else if (event.text === "o") {
if (watchOpen) {
debugWatchOpen = false;
closeWatch();
} else {
debugWatchOpen = true;
openWatch();
}
}
}
Controller.keyPressEvent.connect(keyPressEvent);
var startPress;
var prevPos2D = undefined;
var avgScroll = 0;
function mousePressOnOverlay(id, event) {
if ((Pointers.isRightHand(event.id) || event.id === pointer) &&
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) || event.id === pointer) &&
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) {
closeApp(currentSelectionIndex);
}
currentSelectionIndex = index;
openApp(currentSelectionIndex);
}
scrollRate = 1000.0 * avgScroll;
prevPos2D = undefined;
avgScroll = 0;
}
}
Overlays.mouseReleaseOnOverlay.connect(mouseReleaseOnOverlay);
function mouseMoveOnOverlay(id, event) {
if ((Pointers.isRightHand(event.id) || event.id === pointer) &&
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) {
closeApp(currentSelectionIndex);
openApp(currentSelectionIndex);
}
}
MyAvatar.skeletonModelURLChanged.connect(skeletonModelURLChanged);
function shutdown() {
closeWatch();
for (var i = 0; i < entities.length; i++) {
Entities.deleteEntity(entities[i]);
}
Pointers.removePointer(pointer);
entities = [];
overlays = [];
}
Script.scriptEnding.connect(shutdown);
}()); // END LOCAL_SCOPE