overte-HifiExperiments/examples/away.js
Anthony J. Thibault 8d7bf87d74 away.js: simplify kneel animation handling
Instead of manually setting anim vars to play the kneel animation
and enable/disable IK, use the MyAvatar.overrideAnimation() and
MyAvatar.restoreAnimtion() functions, which did not exist when
away.js was written.  This should make it more robust and eliminate
the issue with IK being stuck in the disabled state.
2016-04-12 14:52:22 -07:00

255 lines
8.7 KiB
JavaScript

"use strict";
/*jslint vars: true, plusplus: true*/
/*global HMD, AudioDevice, MyAvatar, Controller, Script, Overlays, print*/
//
// away.js
// examples
//
// Created by Howard Stearns 11/3/15
// 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
//
// Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key.
// See MAIN CONTROL, below, for what "paused" actually does.
var OVERLAY_WIDTH = 1920;
var OVERLAY_HEIGHT = 1080;
var OVERLAY_RATIO = OVERLAY_WIDTH / OVERLAY_HEIGHT;
var OVERLAY_DATA = {
width: OVERLAY_WIDTH,
height: OVERLAY_HEIGHT,
imageURL: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png",
color: {red: 255, green: 255, blue: 255},
alpha: 1
};
var lastOverlayPosition = { x: 0, y: 0, z: 0};
var OVERLAY_DATA_HMD = {
position: lastOverlayPosition,
width: OVERLAY_WIDTH,
height: OVERLAY_HEIGHT,
url: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png",
color: {red: 255, green: 255, blue: 255},
alpha: 1,
scale: 2,
isFacingAvatar: true,
drawInFront: true
};
var AWAY_INTRO = {
url: "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx",
playbackRate: 30.0,
loopFlag: false,
startFrame: 0.0,
endFrame: 83.0
};
function playAwayAnimation() {
MyAvatar.overrideAnimation(AWAY_INTRO.url, AWAY_INTRO.playbackRate, AWAY_INTRO.loopFlag, AWAY_INTRO.startFrame, AWAY_INTRO.endFrame);
}
function stopAwayAnimation() {
MyAvatar.restoreAnimation();
}
// OVERLAY
var overlay = Overlays.addOverlay("image", OVERLAY_DATA);
var overlayHMD = Overlays.addOverlay("image3d", OVERLAY_DATA_HMD);
function moveCloserToCamera(positionAtHUD) {
// we don't actually want to render at the slerped look at... instead, we want to render
// slightly closer to the camera than that.
var MOVE_CLOSER_TO_CAMERA_BY = -0.25;
var cameraFront = Quat.getFront(Camera.orientation);
var closerToCamera = Vec3.multiply(cameraFront, MOVE_CLOSER_TO_CAMERA_BY); // slightly closer to camera
var slightlyCloserPosition = Vec3.sum(positionAtHUD, closerToCamera);
return slightlyCloserPosition;
}
function showOverlay() {
var properties = {visible: true};
if (HMD.active) {
// make sure desktop version is hidden
Overlays.editOverlay(overlay, { visible: false });
lastOverlayPosition = HMD.getHUDLookAtPosition3D();
var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition);
Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon });
} else {
// make sure HMD is hidden
Overlays.editOverlay(overlayHMD, { visible: false });
// Update for current screen size, keeping overlay proportions constant.
var screen = Controller.getViewportDimensions();
// keep the overlay it's natural size and always center it...
Overlays.editOverlay(overlay, { visible: true,
x: ((screen.x - OVERLAY_WIDTH) / 2),
y: ((screen.y - OVERLAY_HEIGHT) / 2) });
}
}
function hideOverlay() {
Overlays.editOverlay(overlay, {visible: false});
Overlays.editOverlay(overlayHMD, {visible: false});
}
hideOverlay();
function maybeMoveOverlay() {
if (isAway) {
// if we switched from HMD to Desktop, make sure to hide our HUD overlay and show the
// desktop overlay
if (!HMD.active) {
showOverlay(); // this will also recenter appropriately
}
if (HMD.active) {
// Note: instead of moving it directly to the lookAt, we will move it slightly toward the
// new look at. This will result in a more subtle slerp toward the look at and reduce jerkiness
var EASE_BY_RATIO = 0.1;
var lookAt = HMD.getHUDLookAtPosition3D();
var lookAtChange = Vec3.subtract(lookAt, lastOverlayPosition);
var halfWayBetweenOldAndLookAt = Vec3.multiply(lookAtChange, EASE_BY_RATIO);
var newOverlayPosition = Vec3.sum(lastOverlayPosition, halfWayBetweenOldAndLookAt);
lastOverlayPosition = newOverlayPosition;
var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition);
Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon });
// make sure desktop version is hidden
Overlays.editOverlay(overlay, { visible: false });
}
}
}
// MAIN CONTROL
var wasMuted, isAway;
var wasOverlaysVisible = Menu.isOptionChecked("Overlays");
var eventMappingName = "io.highfidelity.away"; // goActive on hand controller button events, too.
var eventMapping = Controller.newMapping(eventMappingName);
// backward compatible version of getting HMD.mounted, so it works in old clients
function safeGetHMDMounted() {
if (HMD.mounted === undefined) {
return true;
}
return HMD.mounted;
}
var wasHmdMounted = safeGetHMDMounted();
function goAway() {
if (isAway) {
return;
}
isAway = true;
print('going "away"');
wasMuted = AudioDevice.getMuted();
if (!wasMuted) {
AudioDevice.toggleMute();
}
MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view
playAwayAnimation(); // animation is still seen by others
showOverlay();
// remember the View > Overlays state...
wasOverlaysVisible = Menu.isOptionChecked("Overlays");
// show overlays so that people can see the "Away" message
Menu.setIsOptionChecked("Overlays", true);
// tell the Reticle, we want to stop capturing the mouse until we come back
Reticle.allowMouseCapture = false;
if (HMD.active) {
Reticle.visible = false;
}
wasHmdMounted = safeGetHMDMounted(); // always remember the correct state
}
function goActive() {
if (!isAway) {
return;
}
isAway = false;
print('going "active"');
if (!wasMuted) {
AudioDevice.toggleMute();
}
MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting.
stopAwayAnimation();
hideOverlay();
// restore overlays state to what it was when we went "away"
Menu.setIsOptionChecked("Overlays", wasOverlaysVisible);
// tell the Reticle, we are ready to capture the mouse again and it should be visible
Reticle.allowMouseCapture = true;
Reticle.visible = true;
if (HMD.active) {
Reticle.position = HMD.getHUDLookAtPosition2D();
}
wasHmdMounted = safeGetHMDMounted(); // always remember the correct state
}
function maybeGoActive(event) {
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
return;
}
if (!isAway && (event.text == 'ESC')) {
goAway();
} else {
goActive();
}
}
var wasHmdActive = HMD.active;
var wasMouseCaptured = Reticle.mouseCaptured;
function maybeGoAway() {
if (HMD.active !== wasHmdActive) {
wasHmdActive = !wasHmdActive;
if (wasHmdActive) {
goAway();
}
}
// If the mouse has gone from captured, to non-captured state, then it likely means the person is still in the HMD, but
// tabbed away from the application (meaning they don't have mouse control) and they likely want to go into an away state
if (Reticle.mouseCaptured !== wasMouseCaptured) {
wasMouseCaptured = !wasMouseCaptured;
if (!wasMouseCaptured) {
goAway();
}
}
// If you've removed your HMD from your head, and we can detect it, we will also go away...
var hmdMounted = safeGetHMDMounted();
if (HMD.active && !hmdMounted && wasHmdMounted) {
wasHmdMounted = hmdMounted;
goAway();
}
}
Script.update.connect(maybeMoveOverlay);
Script.update.connect(maybeGoAway);
Controller.mousePressEvent.connect(goActive);
Controller.keyPressEvent.connect(maybeGoActive);
// Note peek() so as to not interfere with other mappings.
eventMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(goActive);
Controller.enableMapping(eventMappingName);
Script.scriptEnding.connect(function () {
Script.update.disconnect(maybeGoAway);
goActive();
Controller.disableMapping(eventMappingName);
Controller.mousePressEvent.disconnect(goActive);
Controller.keyPressEvent.disconnect(maybeGoActive);
});