350 lines
No EOL
11 KiB
JavaScript
350 lines
No EOL
11 KiB
JavaScript
//
|
|
// watchScript.js
|
|
//
|
|
// Created by David Back 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 LEFT_FOREARM_JOINT = "LeftForeArm";
|
|
const LEFT_HAND_JOINT = "LeftHand";
|
|
const RIGHT_HAND_JOINT = "RightHand";
|
|
const ACTIVATION_DISTANCE = 0.25;
|
|
const DEACTIVATION_DISTANCE = 0.6;
|
|
const SWIPE_THRESHOLD = 0.125;
|
|
const ACTIVATION_INTERVAL = 50;
|
|
const WATCH_Z_OFFSET = -0.05;
|
|
const WATCH_FOREARM_HAND_DISTANCE = 0.8;
|
|
const LEFT_HAND = 0;
|
|
const RIGHT_HAND = 1;
|
|
const WATCH_MODEL = Script.resolvePath("watchModel.fbx");
|
|
const GHOST_EFFECT = Script.resolvePath("ghost-effect.png");
|
|
const PARTICLE_EFFECT = Script.resolvePath("particle-effect.png");
|
|
const RESET = Script.resolvePath("reset.png");
|
|
const SELECTION_ORDER = [ GHOST_EFFECT, PARTICLE_EFFECT, RESET ];
|
|
const DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 };
|
|
const DEBUG_ACTIVATION_KEY = 70;
|
|
|
|
var materialEntities = [];
|
|
var watchOpen = false;
|
|
var debugWatchOpen = false;
|
|
var activationInterval = null;
|
|
var watchEntity = null;
|
|
var projectionEffect = null;
|
|
var selectionOverlay = null;
|
|
var selectionIndex = 0;
|
|
var pointer = null;
|
|
var mousePressPickPos2D = null;
|
|
var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
|
|
|
function cleanupFromBefore() {
|
|
var toDelete = Entities.findEntitiesByName("Hackathon_GhostMaterial", MyAvatar.position, 1000);
|
|
for (var i = 0; i < toDelete.length; i++) {
|
|
Entities.deleteEntity(toDelete[i]);
|
|
}
|
|
|
|
var toDelete = Entities.findEntitiesByName("Hackathon_Particle", MyAvatar.position, 1000);
|
|
for (var i = 0; i < toDelete.length; i++) {
|
|
Entities.deleteEntity(toDelete[i]);
|
|
}
|
|
}
|
|
|
|
function openWatch() {
|
|
print("WATCH - OPEN");
|
|
|
|
projectionEffect = Entities.addEntity({
|
|
alpha: 0,
|
|
alphaFinish: 0,
|
|
alphaStart: 0.5,
|
|
color: {
|
|
"blue": 222,
|
|
"green": 164,
|
|
"red": 18
|
|
},
|
|
colorSpread: {
|
|
"blue": 100,
|
|
"green": 50,
|
|
"red": 50
|
|
},
|
|
emitAcceleration: {
|
|
"x": 0,
|
|
"y": 0.8,
|
|
"z": 0
|
|
},
|
|
emitOrientation: Quat.fromPitchYawRollDegrees(-180, 0, 0),
|
|
emitRate: 10,
|
|
emitSpeed: 1,
|
|
lifespan: 0.8,
|
|
maxParticles: 10,
|
|
name: "Watch Projection Effect",
|
|
parentID: watchEntity,
|
|
particleRadius: 0.2,
|
|
radiusFinish: 0.1,
|
|
radiusStart: 0,
|
|
speedSpread: 0,
|
|
spinFinish: 0,
|
|
spinStart: 0,
|
|
textures: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png",
|
|
type: "ParticleEffect"
|
|
}, true);
|
|
|
|
selectionOverlay = Overlays.addOverlay("image3d", {
|
|
name: "Watch Selection",
|
|
url: SELECTION_ORDER[selectionIndex],
|
|
alpha: 1,
|
|
dimensions: { x: 0.4, y: 0.4 },
|
|
visible: true,
|
|
emissive: true,
|
|
ignoreRayIntersection: false,
|
|
parentID: watchEntity,
|
|
localPosition: { x: 0, y: 0, z: -0.3 },
|
|
localRotation: Quat.fromPitchYawRollDegrees(-90, 0, -90)
|
|
});
|
|
|
|
pointer = Pointers.createPointer(PickType.Stylus, {
|
|
hand: RIGHT_HAND,
|
|
filter: Picks.PICK_OVERLAYS,
|
|
enabled: true
|
|
});
|
|
Pointers.setIncludeItems(pointer, [selectionOverlay]);
|
|
|
|
Overlays.mousePressOnOverlay.connect(mousePressOnOverlay);
|
|
Overlays.mouseReleaseOnOverlay.connect(mouseReleaseOnOverlay);
|
|
|
|
triggerMapping.enable();
|
|
|
|
cleanupFromBefore();
|
|
|
|
watchOpen = true;
|
|
}
|
|
|
|
function closeWatch() {
|
|
print("WATCH - CLOSE");
|
|
|
|
if (projectionEffect !== null) {
|
|
Entities.deleteEntity(projectionEffect);
|
|
projectionEffect = null;
|
|
}
|
|
if (selectionOverlay !== null) {
|
|
Overlays.deleteOverlay(selectionOverlay);
|
|
selectionOverlay = null;
|
|
}
|
|
if (pointer !== null) {
|
|
Pointers.removePointer(pointer);
|
|
pointer = null;
|
|
}
|
|
|
|
for (var i = 0; i < materialEntities.length; i++) {
|
|
Entities.deleteEntity(materialEntities[i]);
|
|
}
|
|
|
|
Overlays.mousePressOnOverlay.disconnect(mousePressOnOverlay);
|
|
Overlays.mouseReleaseOnOverlay.disconnect(mouseReleaseOnOverlay);
|
|
|
|
triggerMapping.disable();
|
|
|
|
watchOpen = false;
|
|
}
|
|
|
|
function triggerSelection() {
|
|
print("WATCH - TRIGGER SELECTION");
|
|
var model = Graphics.getModel(MyAvatar.SELF_ID);
|
|
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++) {
|
|
materialEntities.push(Entities.addEntity({
|
|
name: "Hackathon_GhostMaterial",
|
|
type: "Material",
|
|
position: MyAvatar.position,
|
|
materialURL: "materialData",
|
|
priority: 1,
|
|
parentID: MyAvatar.SELF_ID,
|
|
parentMaterialName: partID,
|
|
materialData: JSON.stringify({
|
|
materials: {
|
|
albedoMap: "https://drive.google.com/file/d/1zvVO63hpdz5d4zvmT3d5pZG-12X3_KYW/view?usp=sharing",
|
|
opacity: 0.2
|
|
}
|
|
})
|
|
}, true));
|
|
partID++;
|
|
}
|
|
}
|
|
}
|
|
|
|
function makeClickHandler(hand) {
|
|
return function(clicked) {
|
|
if (clicked === 1.0) {
|
|
triggerSelection();
|
|
}
|
|
};
|
|
}
|
|
|
|
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
|
|
|
|
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), DEFAULT_REGISTRATION_POINT);
|
|
return {
|
|
x: normalizedPos.x * dimensions.x,
|
|
y: (1 - normalizedPos.y) * dimensions.y // flip y-axis
|
|
};
|
|
}
|
|
|
|
function mousePressOnOverlay(overlayID, event) {
|
|
if (overlayID === selectionOverlay && event.id === pointer) {
|
|
//print("mousePressOnOverlay event.button " + event.button + " event.pos2D " + event.pos2D.x + " " + event.pos2D.y);
|
|
//mousePressEventPos = event.pos2D;
|
|
mousePressPickPos2D = projectOntoOverlayXYPlane(selectionOverlay, Pointers.getPrevPickResult(pointer).intersection);
|
|
print("mousePressPickPos2D " + mousePressPickPos2D.x + " " + mousePressPickPos2D.y);
|
|
}
|
|
}
|
|
|
|
function mouseReleaseOnOverlay(overlayID, event) {
|
|
/*
|
|
if (overlayID === selectionOverlay && event.id === pointer) {
|
|
print("mouseReleaseOnOverlay event.button " + event.button + " event.pos2D " + event.pos2D.x + " " + event.pos2D.y);
|
|
var mouseReleaseEventPos = event.pos2D;
|
|
var posDifference = Vec3.subtract(mousePressEventPos, mouseReleaseEventPos);
|
|
print("posDifference " + posDifference.x + " " + posDifference.y);
|
|
}
|
|
*/
|
|
}
|
|
|
|
function checkActivation() {
|
|
var watchPosition = Entities.getEntityProperties(watchEntity, ['position']).position;
|
|
var rightHandPosition = MyAvatar.getJointPosition(RIGHT_HAND_JOINT);
|
|
var distance = Vec3.distance(watchPosition, rightHandPosition);
|
|
if (watchOpen && distance > DEACTIVATION_DISTANCE && !debugWatchOpen) {
|
|
closeWatch();
|
|
} else if (!watchOpen && distance < ACTIVATION_DISTANCE) {
|
|
openWatch();
|
|
}
|
|
}
|
|
|
|
function keyPressEvent(event) {
|
|
if (DEBUG_ACTIVATION_KEY === event.key) {
|
|
print("WATCH - DEBUG_ACTIVATION_KEY triggered - watchOpen was " + watchOpen);
|
|
if (watchOpen) {
|
|
closeWatch();
|
|
debugWatchOpen = false;
|
|
} else {
|
|
openWatch();
|
|
debugWatchOpen = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
function update() {
|
|
/*
|
|
if (watchEntity) {
|
|
var handRotation = MyAvatar.getJointRotation(MyAvatar.getJointIndex(LEFT_HAND_JOINT));
|
|
var handPosition = MyAvatar.getJointPosition(MyAvatar.getJointIndex(LEFT_HAND_JOINT));
|
|
Entities.editEntity(watchEntity, {
|
|
position: handPosition,
|
|
rotation: handRotation
|
|
});
|
|
}
|
|
*/
|
|
|
|
if (mousePressPickPos2D) {
|
|
var pickResult = Pointers.getPrevPickResult(pointer);
|
|
if (pickResult.type === Picks.INTERSECTED_NONE) {
|
|
mousePressPickPos2D = null;
|
|
} else {
|
|
var pickPos2D = projectOntoOverlayXYPlane(selectionOverlay, Pointers.getPrevPickResult(pointer).intersection);
|
|
var positionDifference = Vec3.subtract(pickPos2D, mousePressPickPos2D);
|
|
var length = Vec3.length(positionDifference);
|
|
if (length >= SWIPE_THRESHOLD) {
|
|
var xDifference = pickPos2D.x - mousePressPickPos2D.x;
|
|
if (xDifference > 0) {
|
|
if (selectionIndex > 0) {
|
|
selectionIndex--;
|
|
Overlays.editOverlay(selectionOverlay, { url: SELECTION_ORDER[selectionIndex]} );
|
|
}
|
|
print("WATCH - SWIPE LEFT - new selection is " + selectionIndex);
|
|
} else {
|
|
if (selectionIndex < SELECTION_ORDER.length - 1) {
|
|
selectionIndex++;
|
|
Overlays.editOverlay(selectionOverlay, { url: SELECTION_ORDER[selectionIndex]} );
|
|
}
|
|
print("WATCH - SWIPE RIGHT - new selection is " + selectionIndex);
|
|
}
|
|
mousePressPickPos2D = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < materialEntities.length; i++) {
|
|
Entities.editEntity(materialEntities[i], { materialMappingPos: { x: Math.cos(t * 0.03), y: Math.sin(t * 0.02) },
|
|
materialMappingScale: {x: 2.0 * Math.sin(t * 0.01) + 0.1, y: 2.0 * Math.sin(t * 0.01) + 0.1 } });
|
|
t = t + dt;
|
|
}
|
|
|
|
Entities.editEntity(entities[entities.length - 2], {
|
|
isEmitting: Audio.inputLevel > 0.01,
|
|
emitRate: 70 * Audio.inputLevel
|
|
});
|
|
Entities.editEntity(entities[entities.length - 1], {
|
|
isEmitting: Audio.inputLevel > 0.01,
|
|
emitRate: 70 * Audio.inputLevel
|
|
});
|
|
}
|
|
|
|
|
|
function shutdown() {
|
|
closeWatch();
|
|
|
|
if (watchEntity !== null) {
|
|
Entities.deleteEntity(watchEntity);
|
|
}
|
|
|
|
Controller.keyPressEvent.disconnect(keyPressEvent);
|
|
}
|
|
|
|
function startup() {
|
|
watchEntity = Entities.addEntity({
|
|
collidesWith: "static,dynamic,kinematic,otherAvatar,",
|
|
dimensions: {
|
|
x: 0.1,
|
|
y: 0.1,
|
|
z: 0.02
|
|
},
|
|
name: "Watch Entity",
|
|
parentID: MyAvatar.SELF_ID,
|
|
//parentJointIndex: MyAvatar.getJointIndex(LEFT_HAND_JOINT),
|
|
//localPosition: { x: 0, y: -0.3 * Vec3.distance(MyAvatar.getJointPosition(WATCH_JOINT), MyAvatar.getJointPosition(LEFT_HAND_JOINT)), z: WATCH_Z_OFFSET},
|
|
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},
|
|
modelURL: WATCH_MODEL,
|
|
type: "Model"
|
|
}, true);
|
|
|
|
Script.update.connect(update);
|
|
|
|
activationInterval = Script.setInterval(function() {
|
|
checkActivation();
|
|
}, ACTIVATION_INTERVAL);
|
|
|
|
Controller.keyPressEvent.connect(keyPressEvent);
|
|
|
|
triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand));
|
|
}
|
|
|
|
Script.scriptEnding.connect(shutdown);
|
|
startup();
|
|
}()); // END LOCAL_SCOPE
|