content/hifi-content/angus/stepApp/step_bak.js
2022-02-13 21:18:16 +01:00

589 lines
24 KiB
JavaScript

/* jslint bitwise: true */
/* global Script, Vec3, MyAvatar, Tablet, Messages, Quat, DebugDraw, Mat4, Entities, Xform, Controller, Camera*/
Script.registerValue("STEPAPP", true);
var MESSAGE_CHANNEL = "Hifi-Step-Cg";
var messageFrequency = 3;
var LEFT = 0;
var RIGHT = 1;
var DEFAULT_AVATAR_HEIGHT = 1.64;
// in centimeters
var DEFAULT_ANTERIOR = 14.0;
var DEFAULT_POSTERIOR = 14.0;
var DEFAULT_LATERAL = 10.0;
var DEFAULT_HEIGHT_DIFFERENCE = -1.0;
// zero to ten
var DEFAULT_ANGULAR_VELOCITY = 7.0;
var DEFAULT_HAND_VELOCITY = -10.0;
var DEFAULT_ANGULAR_HAND_VELOCITY = 7.0;
var angularVelocityThreshold = 0.3;
var ROT_Y180 = {x: 0, y: 1, z: 0, w: 0};
// Pitch and Roll more than these number of degrees from long-term average prevent stepping.
var MAX_LEVEL_PITCH = 3;
var MAX_LEVEL_ROLL = 3;
// How far can the head move down before blocking stepping?
// .0075 was old value.
var maxHeightChange = 0.010;
// this should be changed by the actual base of support of the person? or Avatar?
// You must have moved at least this far laterally to take a step
var MIN_STEP_DISTANCE = 0.03;
var DONE_STEPPING_DISTANCE = 0.01;
var AVERAGING_RATE = 1.0;
var HEIGHT_AVERAGING_RATE = 0.0025;
// The velocity of head and hands in same direction that triggers stepping
var STEP_VELOCITY_THRESHOLD = 0.05;
// The time required for observed velocity to continue to trigger stepping
var STEP_TIME_SECS = 0.15;
var AZIMUTH_TURN_MIN_DEGREES = 22;
var AZIMUTH_TIME_SECS = 2.5;
var HEAD_HAND_BALANCE = 2.0;
var RECENTER = false;
MyAvatar.hmdLeanRecenterEnabled = RECENTER;
var STEPTURN = true;
var RESET_MODE = false;
var MY_HEIGHT = 1.15;
var modeArray = new Array(100);
var modeHeight = -10.0;
var stepTimer = -1.0;
var leaningDot = 1.0;
var leaningDotAverage = 1.0;
var torsoLength = 1.0;
var defaultLength = 1.0;
var hipsRotation = {x: 0, y: 0, z: 0, w: 1};
var hipsPosition = {x: 0, y: 0, z: 0};
var spine2Rotation = {x: 0, y: -0.85, z: 0, w: 0.5};
var spine2Position = { x: 0, y: 0, z: 0 };
var lateralEdge = 0.14;// was 0.09
var frontEdge = -0.10;// fmi was -0.07
var backEdge = 0.13;
var frontLeft = { x: -lateralEdge, y: 0, z: frontEdge };
var frontRight = { x: lateralEdge, y: 0, z: frontEdge };
var backLeft = { x: -lateralEdge, y: 0, z: backEdge };
var backRight = { x: lateralEdge, y: 0, z: backEdge };
var inFront = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(MyAvatar.orientation)));
var handDotHead = [];
var headAveragePosition = Vec3.sum(inFront, { x: 0, y: 0.3, z: 0 });
var headPosition = headAveragePosition;
var headAverageOrientation = MyAvatar.orientation;
var averageHeight = 1.0;
var handPosition;
var handOrientation;
var headOrientation;
var isStepping = false;
var isTurning = false;
var handSteppingDetect = false;
var handSteppingTimer = 0;
var lastHeadAzimuth = 0;
var azimuthChangeTimer = 0;
var hipToHandAverage = [];
var averageAzimuth = 0.0;
var headPosAvatarSpace;
var rightHandPosAvatarSpace;
var leftHandPosAvatarSpace;
var hands = [];
var head, headAverage, lastAzimuthMarker, headAzimuthMarker, leftAzimuthMarker, rightAzimuthMarker;
// createMarkers();
var headEulers;
var headAverageEulers;
var oldAngularVelocity = { x: 0.0, y: 0.0, z: 0.0 };
var headSwayCount = 0;
var accelerationArray = new Array(30);
var debugDrawBase = true;
var handVelocityThreshold = -1.0;
var handAngularVelocityThreshold = 0.3;
function drawBase() {
// transform corners into world space, for rendering.
// var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position);
var worldPointLf = Vec3.sum(MyAvatar.position,Vec3.multiplyQbyV(MyAvatar.orientation, frontLeft));
var worldPointRf = Vec3.sum(MyAvatar.position,Vec3.multiplyQbyV(MyAvatar.orientation, frontRight));
var worldPointLb = Vec3.sum(MyAvatar.position,Vec3.multiplyQbyV(MyAvatar.orientation, backLeft));
var worldPointRb = Vec3.sum(MyAvatar.position,Vec3.multiplyQbyV(MyAvatar.orientation, backRight));
// print("world point is left front is: " + worldPointLf.x + " " + worldPointLf.y + " " + worldPointLf.z);
print("front left point is " + frontLeft.x + " " + frontLeft.y + " " + frontLeft.z);
var GREEN = { r: 0, g: 1, b: 0, a: 1 };
// draw border
DebugDraw.drawRay(worldPointLf, worldPointRf, GREEN);
DebugDraw.drawRay(worldPointRf, worldPointRb, GREEN);
DebugDraw.drawRay(worldPointRb, worldPointLb, GREEN);
DebugDraw.drawRay(worldPointLb, worldPointLf, GREEN);
}
function onKeyPress(event) {
if (event.text === "'") {
// when the sensors are reset, then reset the mode.
RESET_MODE = false;
}
}
var TABLET_BUTTON_NAME = "STEP";
var HTML_URL = Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/stepApp.html");
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var tabletButton = tablet.addButton({
text: TABLET_BUTTON_NAME,
icon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/step2.svg"),
activeIcon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/step2.svg")
});
var activated = false;
var documentLoaded = false;
function manageClick() {
if (activated) {
tablet.gotoHomeScreen();
} else {
tablet.gotoWebScreen(HTML_URL);
}
}
tabletButton.clicked.connect(manageClick);
function onWebEventReceived(msg) {
var message = JSON.parse(msg);
print(" we have a message from html dialog " + message.type);
switch (message.type) {
case "onAnteriorBaseSlider":
print("anterior slider " + message.data.value);
setAnteriorDistance(message.data.value);
break;
case "onPosteriorBaseSlider":
print("posterior slider " + message.data.value);
setPosteriorDistance(message.data.value);
break;
case "onLateralBaseSlider":
print("lateral slider " + message.data.value);
setLateralDistance(message.data.value);
break;
case "onAngularVelocitySlider":
print("angular velocity value " + message.data.value);
setAngularThreshold(message.data.value);
break;
case "onHeightDifferenceSlider":
print("height slider " + message.data.value);
setHeightThreshold(message.data.value);
break;
case "onHandsVelocitySlider":
print("hands velocity slider " + message.data.value);
setHandVelocityThreshold(message.data.value);
break;
case "onHandsAngularVelocitySlider":
print("hands angular velocity slider " + message.data.value);
setHandAngularVelocityThreshold(message.data.value);
break;
case "onCreateStepApp":
print("on create step app " + message.data.value);
break;
default:
print("unknown message from step html!!");
break;
}
}
function onScreenChanged(type, url) {
print("Screen changed");
if (type === "Web" && url === HTML_URL) {
// tabletButton.editProperties({isActive: true});
if (!activated) {
// hook up to event bridge
tablet.webEventReceived.connect(onWebEventReceived);
print("after connect web event");
Script.setTimeout(function() {
print("step app is loaded: " + documentLoaded);
}, 500);
}
activated = true;
} else {
// tabletButton.editProperties({isActive: false});
if (activated) {
setHandAngularVelocityThreshold(DEFAULT_ANGULAR_HAND_VELOCITY);
setHandVelocityThreshold(DEFAULT_HAND_VELOCITY);
setHeightThreshold(DEFAULT_HEIGHT_DIFFERENCE);
setAnteriorDistance(DEFAULT_ANTERIOR);
setPosteriorDistance(DEFAULT_POSTERIOR);
setLateralDistance(DEFAULT_LATERAL);
setAngularThreshold(DEFAULT_ANGULAR_VELOCITY);
// disconnect from event bridge
tablet.webEventReceived.disconnect(onWebEventReceived);
}
activated = false;
}
}
tablet.screenChanged.connect(onScreenChanged);
function isInsideLine(a, b, c) {
return (((b.x - a.x)*(c.z - a.z) - (b.z - a.z)*(c.x - a.x)) > 0);
}
function setAngularThreshold(num) {
angularVelocityThreshold = (10.0 - num) / 10.0;
print("angular threshold " + angularVelocityThreshold);
}
function setHeightThreshold(num) {
maxHeightChange = -num/100.0;
print("height threshold " + maxHeightChange);
}
function setLateralDistance(num) {
lateralEdge = num/100.0;
frontLeft.x = -lateralEdge;
frontRight.x = lateralEdge;
backLeft.x = -lateralEdge;
backRight.x = lateralEdge;
print("lateral distance " + lateralEdge);
}
function setAnteriorDistance(num) {
frontEdge = num/100.0;
frontLeft.z = -frontEdge;
frontRight.z = -frontEdge;
print("anterior distance " + frontEdge);
}
function setPosteriorDistance(num) {
backEdge = num / 100.0;
backLeft.z = backEdge;
backRight.z = backEdge;
print("posterior distance " + frontEdge);
}
function setHandAngularVelocityThreshold(num) {
handAngularVelocityThreshold = (10.0 - num) / 10.0;
print("hand angular velocity threshold " + handAngularVelocityThreshold);
}
function setHandVelocityThreshold(num) {
handVelocityThreshold = num/10.0;
print("hand velocity threshold " + handVelocityThreshold);
}
function withinBaseOfSupport(pos) {
var userScale = 1.0;
if (HMD.active) {
// print("hmd height " + HMD.position.y);
// to do: get the scaling correct here. AA
// userScale = HMD.position.y / DEFAULT_AVATAR_HEIGHT;
}
return (isInsideLine(Vec3.multiply(userScale, frontLeft), Vec3.multiply(userScale, frontRight), pos)
&& isInsideLine(Vec3.multiply(userScale, frontRight), Vec3.multiply(userScale, backRight), pos)
&& isInsideLine(Vec3.multiply(userScale, backRight), Vec3.multiply(userScale, backLeft), pos)
&& isInsideLine(Vec3.multiply(userScale, backLeft), Vec3.multiply(userScale, frontLeft), pos));
}
function limitAngle(angle) {
return (angle + 180) % 360 - 180;
}
function findAverage(arr) {
var sum = arr.reduce(function (acc, val) {
return acc + val;
},0);
return sum / arr.length;
}
function addToAccelerationArray(arr, num) {
for (var i = 0 ; i < (arr.length - 1) ; i++) {
arr[i] = arr[i + 1];
}
arr[arr.length - 1] = num;
}
function addToModeArray(arr,num) {
for (var i = 0 ;i < (arr.length - 1); i++) {
arr[i] = arr[i+1];
}
arr[arr.length - 1] = (Math.floor(num*100))/100.00;
}
function findMode(ary, currentMode, backLength, defaultBack, currentHeight) {
var numMapping = {};
var greatestFreq = 0;
var mode;
ary.forEach(function (number) {
numMapping[number] = (numMapping[number] || 0) + 1;
if ((greatestFreq < numMapping[number]) || ((numMapping[number] === 100) && (number > currentMode) )) {
greatestFreq = numMapping[number];
mode = number;
}
});
if (mode > currentMode) {
return Number(mode);
} else {
if ((backLength > (defaultBack + 0.10)) || (!RESET_MODE && HMD.active)) {
print("resetting the mode....................default " + defaultBack + " head-origin " + backLength);
print("resetting the mode............................................. ");
print("resetting the mode............................................. ");
RESET_MODE = true;
return currentHeight - 0.02;
} else {
return currentMode;
}
}
}
function update(dt) {
if (debugDrawBase) {
drawBase();
}
var currentHipsPos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Hips"));
var currentHeadPos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Head"));
var defaultHipsPos = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(MyAvatar.getJointIndex("Hips"));
var defaultHeadPos = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(MyAvatar.getJointIndex("Head"));
defaultLength = Vec3.length(Vec3.subtract(defaultHeadPos, defaultHipsPos));
var headMinusHipLean = Vec3.subtract(currentHeadPos,defaultHipsPos);
torsoLength = Vec3.length(headMinusHipLean);
leaningDot = Vec3.dot(Vec3.normalize(headMinusHipLean),{x: 0,y: 1,z: 0});
leaningDotAverage = leaningDot * HEIGHT_AVERAGING_RATE + leaningDotAverage * (1.0 - HEIGHT_AVERAGING_RATE);
var sensorToWorldMatrix = MyAvatar.getSensorToWorldMatrix();
var worldToSensorMatrix = Mat4.inverse(sensorToWorldMatrix);
var avatarToWorldMatrix = Mat4.createFromRotAndTrans(MyAvatar.orientation, MyAvatar.position);
// Update head information
var headPose = Controller.getPoseValue(Controller.Standard.Head);
var rightHandPose = Controller.getPoseValue(Controller.Standard.RightHand);
var leftHandPose = Controller.getPoseValue(Controller.Standard.LeftHand);
headPosition = Camera.getPosition();
headOrientation = Camera.getOrientation();
headEulers = Quat.safeEulerAngles(headOrientation);
headAverageEulers = Quat.safeEulerAngles(headAverageOrientation);
var azimuth = headEulers.y;
// print("the head y euler is:" + headEulers.y);
if (!lastHeadAzimuth) {
lastHeadAzimuth = azimuth;
}
// head position in object space
headPosAvatarSpace = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Head"));
rightHandPosAvatarSpace = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("RightHand"));
leftHandPosAvatarSpace = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("LeftHand"));
var inSupport = withinBaseOfSupport(headPosAvatarSpace);
// print("are we in the base of support? " + inSupport);
addToModeArray(modeArray,headPose.translation.y);
modeHeight = findMode(modeArray, modeHeight, torsoLength, defaultLength, headPose.translation.y);
// print("the mode height is currently.... " + modeHeight + " user height " + headPose.translation.y);
// DebugDraw.addMyAvatarMarker("avatar_origin", { x: 0, y: 0, z: 0, w: 1 }, MyAvatar.position, COLOR_LEVEL);
DebugDraw.addMarker("avatar_origin", { x: 0, y: 0, z: 0, w: 1 }, MyAvatar.position, { x: 0, y: 1, z: 0 });
// print("current head x " + headPose.translation.x );// + " " + currentHipsPos.y + " " + currentHipsPos.z);
// print("current head height ....................... " + headPose.translation.y + ",,,,,,,,,,,,,,,,,,," + modeHeight);
var totalHeadVelocity = Vec3.length(headPose.velocity);
// print("current head velocity ....................... " + totalHeadVelocity);
// print("current head angular velocity ....................... " + (Math.floor(headPose.angularVelocity.x * 100)) / 100.00 + " " +
// (Math.floor(headPose.angularVelocity.y * 100)) / 100.00 + " " + (Math.floor(headPose.angularVelocity.z * 100)) / 100.00);
var xzAngularVelocity = Vec3.length({ x: headPose.angularVelocity.x, y: 0.0, z: headPose.angularVelocity.z });
var xzRHandAngularVelocity = Vec3.length({ x: rightHandPose.angularVelocity.x, y: 0.0, z: rightHandPose.angularVelocity.z });
var xzLHandAngularVelocity = Vec3.length({ x: leftHandPose.angularVelocity.x, y: 0.0, z: leftHandPose.angularVelocity.z });
var xzRHandVelocity = Vec3.length({ x: rightHandPose.velocity.x, y: 0.0, z: rightHandPose.velocity.z });
var xAcceleration = headPose.angularVelocity.x - oldAngularVelocity.x;
var zAcceleration = headPose.angularVelocity.z - oldAngularVelocity.z;
if (xAcceleration < 0.0) {
xAcceleration = 0.0;
}
if (zAcceleration < 0.0) {
zAcceleration = 0.0;
}
var xzAngularAcceleration = Vec3.length({ x: xAcceleration, y: 0.0, z: zAcceleration });
oldAngularVelocity = {x:headPose.angularVelocity.x, y: 0.0, z: headPose.angularVelocity.z};
// print("magnitude of roll pitch angular velocity------------ " + xzAngularVelocity);
// print("ratio of angular to translational velocity " + xzAngularVelocity / totalHeadVelocity);
// print("hand dot head left " + handDotHead[LEFT]);
// print("hand left velocity " + Vec3.length(leftHandPose.velocity));
// print("magnitude of roll pitch right hand angular velocity------------ " + xzRHandAngularVelocity);
// print("magnitude of right hand velocity------------ " + xzRHandVelocity);
// print("magnitude of roll pitch angular acceleration------------ " + averageAngularAcceleration);
// print("the angle of the head is ........... " + Vec3.length({ x: headEulers.x, y: 0.0, z: headEulers.z }));
// If the head is not level, we cannot step.
var isHeadLevel = (Math.abs(headEulers.z - headAverageEulers.z) < MAX_LEVEL_ROLL)
&& (Math.abs(headEulers.x - headAverageEulers.x) < MAX_LEVEL_PITCH);
var lateralDistanceFromAverage = { x: 0, y: 0, z: 0 };
var heightDifferenceFromAverage = modeHeight - headPose.translation.y;
// print("hand velocities " + Vec3.length(leftHandPose.velocity) + " " + Vec3.length(rightHandPose.velocity));
// are we off the base of support, losing height and tilting the head
// 1. off the base of support. 2) head is not rotating too much 3) head hasn't lost too much height 4) hands are not still.
// then we can translate
// && (heightDifferenceFromAverage < MAX_HEIGHT_CHANGE) && isHeadLevel &&
if (!inSupport && (xzAngularVelocity < angularVelocityThreshold) && (heightDifferenceFromAverage < maxHeightChange)
&& ((!leftHandPose.valid || (handDotHead[LEFT] > handVelocityThreshold)) && (Vec3.length(leftHandPose.velocity) > 0.00001))
&& ((!rightHandPose.valid || (handDotHead[RIGHT] > handVelocityThreshold)) && (Vec3.length(rightHandPose.velocity) > 0.00001))
&& (xzRHandAngularVelocity < handAngularVelocityThreshold) && (xzLHandAngularVelocity < handAngularVelocityThreshold)) {
isStepping = true;
if (STEPTURN && (stepTimer < 0.0) ) {
print("trigger recenter========================================================");
MyAvatar.triggerHorizontalRecenter();
// RESET_MODE = true;
// wait half a second to trigger again.
stepTimer = 0.6;
}
}
stepTimer -= dt;
if (isStepping && (lateralDistanceFromAverage < DONE_STEPPING_DISTANCE)) {
isStepping = false;
}
if (!HMD.active) {
RESET_MODE = false;
}
// Record averages
headAverageOrientation = Quat.slerp(headAverageOrientation, headOrientation, AVERAGING_RATE);
headAveragePosition = Vec3.mix(headAveragePosition, headPosition, AVERAGING_RATE);
// this will vary with the height of the person being tracked......
// one percent of a taller person will give you greater variance in height.
// the threshold for height loss is the same for both heights.
// I think most walking is 2Hz. So maybe we could try averaging over 30 frames instead of 100.
averageHeight = headPosition.y * HEIGHT_AVERAGING_RATE + averageHeight * (1.0 - HEIGHT_AVERAGING_RATE);
// Check for need to turn, by measuring if the head has turned past a threshold,
// AND the hands are on either side of the head and fairly evenly distributed,
// AND a few seconds has passed
var angleToLeft = limitAngle(azimuth - Quat.safeEulerAngles(hipToHandAverage[LEFT]).y);
var angleToRight = limitAngle(azimuth - Quat.safeEulerAngles(hipToHandAverage[RIGHT]).y);
var angleToMarker = limitAngle(azimuth - lastHeadAzimuth);
var leftRightMidpoint = (Quat.safeEulerAngles(hipToHandAverage[LEFT]).y + Quat.safeEulerAngles(hipToHandAverage[RIGHT]).y) / 2.0;
// lets try using this 100% of the time first.
// head has turned more than threshold minimum.
// the hands are on either side of the head.
// the difference between the head and right hand look at compared to
// head to left hand look at is greater than 1/2 and
// less than 2
if ((headPosition.y - modeHeight) > -0.02) {
// print("leftrightmidpoint " + leftRightMidpoint + " average azimuth " + average_azimuth);
// print("average azimuth " + average_azimuth);
averageAzimuth = leftRightMidpoint * (0.01) + averageAzimuth * (0.99);
// hipsRotation = {x: 0,y: 1,z: 0,w: 0};
hipsRotation = Quat.angleAxis(averageAzimuth, { x: 0, y: 1, z: 0 });
}
if (Math.abs(leftRightMidpoint) > 20) {
if (Math.abs(leftRightMidpoint) > 60) {
azimuthChangeTimer += 2 * dt;
} else {
azimuthChangeTimer += dt;
}
} else {
azimuthChangeTimer = 0;
}
if (azimuthChangeTimer > AZIMUTH_TIME_SECS) {
// Turn feet
isTurning = true;
if (STEPTURN) {
MyAvatar.triggerRotationRecenter();
// hipsRotation = {x: 0,y: 0,z: 0,w: 1};//Quat.angleAxis((leftRightMidpoint - average_azimuth),{x: 0,y: 1,z: 0});
// average_azimuth = (leftRightMidpoint - average_azimuth);
// print("js trigger rotation...................................................");
}
lastHeadAzimuth = averageAzimuth;
azimuthChangeTimer = 0;
}
// original azimuth code from Philip
/*
if ((Math.abs(angleToMarker) > AZIMUTH_TURN_MIN_DEGREES) &&
(angleToLeft * angleToRight < 0) &&
(Math.abs(angleToRight / angleToLeft) > 1 / HEAD_HAND_BALANCE) &&
(Math.abs(angleToRight / angleToLeft) < HEAD_HAND_BALANCE) ) {
headAzimuthMarkerColor = COLOR_IS_STEPPING;
azimuthChangeTimer += dt;
if (azimuthChangeTimer > AZIMUTH_TIME_SECS) {
// Actually turn
isTurning = true;
if (STEPTURN) {
// MyAvatar.triggerRotationRecenter();
}
lastHeadAzimuth = azimuth;
azimuthChangeTimer = 0;
}
} else {
isTurning = false;
headAzimuthMarkerColor = COLOR_RIGHT;
azimuthChangeTimer = 0;
}
*/
// Check the hands one at a time
for (var hand = LEFT; hand <= RIGHT; hand++) {
// Update hand object
var pose = Controller.getPoseValue((hand === 1) ? Controller.Standard.RightHand : Controller.Standard.LeftHand);
var lateralPoseVelocity = {x:0, y:0, z:0};
if (pose.valid && headPose.valid) {
lateralPoseVelocity = pose.velocity;
lateralPoseVelocity.y = 0;
var lateralHeadVelocity = headPose.velocity;
lateralHeadVelocity.y = 0;
handDotHead[hand] = Vec3.dot(Vec3.normalize(lateralPoseVelocity), Vec3.normalize(lateralHeadVelocity));
}
// handPosition = Mat4.transformPoint(avatarToWorldMatrix, pose.translation);
handPosition = (hand === 1) ? rightHandPosAvatarSpace : leftHandPosAvatarSpace;
handOrientation = Quat.multiply(MyAvatar.orientation, pose.rotation);
// Update angle from hips to hand, to be used for turning
var hipToHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: handPosition.x, y: 0, z: handPosition.z });
hipToHandAverage[hand] = Quat.slerp(hipToHandAverage[hand], hipToHand, AVERAGING_RATE);
}
// If the velocity of the hands in direction of head scaled by velocity of head is enough,
// trigger a step.
if ((handDotHead[LEFT] > STEP_VELOCITY_THRESHOLD) && (handDotHead[RIGHT] > STEP_VELOCITY_THRESHOLD)) {
handSteppingDetect = true;
if (STEPTURN) {
// MyAvatar.triggerHorizontalRecenter();
}
handSteppingTimer += dt;
} else {
handSteppingDetect = false;
handSteppingTimer = 0;
}
}
function shutdownTabletApp() {
// GlobalDebugger.stop();
tablet.removeButton(tabletButton);
if (activated) {
tablet.webEventReceived.disconnect(onWebEventReceived);
tablet.gotoHomeScreen();
}
tablet.screenChanged.disconnect(onScreenChanged);
}
Script.update.connect(update);
Controller.keyPressEvent.connect(onKeyPress);
Script.scriptEnding.connect(function () {
Script.update.disconnect(update);
shutdownTabletApp();
});