overte/examples/walk.js
2014-09-10 23:57:10 +07:00

2540 lines
No EOL
117 KiB
JavaScript
Raw Blame History

//
// walk.js
//
//
// Created by Davedub, August / September 2014
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Set asset paths here:
// path to the animation files
//var pathToAnimFiles = 'http://localhost/downloads/hf/scripts/animation-files/'; // loads fine (files must be present on localhost)
//var pathToAnimFiles = 'http://highfidelity.davedub.co.uk/procedural/walk/animation-files/'; // files present, but load with errors - weird
var pathToAnimFiles = 'http://s3-us-west-1.amazonaws.com/highfidelity-public/procedural-animator/files/'; // working (but only without https)
// path to the images used for the overlays
//var pathToOverlays = 'http://localhost/downloads/hf/overlays/'; // loads fine (files must be present on localhost)
//var pathToOverlays = 'http://highfidelity.davedub.co.uk/procedural/walk/overlays/'; // files present, but won't load - weird
var pathToOverlays = 'http://s3-us-west-1.amazonaws.com/highfidelity-public/procedural-animator/images/'; // working (but only without https)
// path to the sounds used for the footsteps
var pathToSounds = 'http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Footsteps/';
//var pathToSounds = 'http://localhost/downloads/hf/sounds/Footsteps/';
// load all the animation datafiles ( 15 female, 15 male ~ 240k )
Script.include(pathToAnimFiles+"dd-female-cool-walk-animation.js");
Script.include(pathToAnimFiles+"dd-female-elderly-walk-animation.js");
Script.include(pathToAnimFiles+"dd-female-power-walk-animation.js");
Script.include(pathToAnimFiles+"dd-female-run-animation.js");
Script.include(pathToAnimFiles+"dd-female-sexy-walk-animation.js");
Script.include(pathToAnimFiles+"dd-female-shuffle-animation.js");
Script.include(pathToAnimFiles+"dd-female-random-walk-animation.js");
Script.include(pathToAnimFiles+"dd-female-strut-walk-animation.js");
Script.include(pathToAnimFiles+"dd-female-tough-walk-animation.js");
Script.include(pathToAnimFiles+"dd-female-flying-up-animation.js");
Script.include(pathToAnimFiles+"dd-female-flying-animation.js");
Script.include(pathToAnimFiles+"dd-female-flying-down-animation.js");
Script.include(pathToAnimFiles+"dd-female-standing-one-animation.js");
Script.include(pathToAnimFiles+"dd-female-standing-two-animation.js");
Script.include(pathToAnimFiles+"dd-female-standing-three-animatiom.js");
Script.include(pathToAnimFiles+"dd-male-cool-walk-animation.js");
Script.include(pathToAnimFiles+"dd-male-elderly-walk-animation.js");
Script.include(pathToAnimFiles+"dd-male-power-walk-animation.js");
Script.include(pathToAnimFiles+"dd-male-run-animation.js");
Script.include(pathToAnimFiles+"dd-male-sexy-walk-animation.js");
Script.include(pathToAnimFiles+"dd-male-shuffle-animation.js");
Script.include(pathToAnimFiles+"dd-male-random-walk-animation.js");
Script.include(pathToAnimFiles+"dd-male-strut-walk-animation.js");
Script.include(pathToAnimFiles+"dd-male-tough-walk-animation.js");
Script.include(pathToAnimFiles+"dd-male-flying-up-animation.js");
Script.include(pathToAnimFiles+"dd-male-flying-animation.js");
Script.include(pathToAnimFiles+"dd-male-flying-down-animation.js");
Script.include(pathToAnimFiles+"dd-male-standing-one-animation.js");
Script.include(pathToAnimFiles+"dd-male-standing-two-animation.js");
Script.include(pathToAnimFiles+"dd-male-standing-three-animation.js");
// read in the data from the animation files
var FemaleCoolWalkFile = new FemaleCoolWalk();
var femaleCoolWalk = FemaleCoolWalkFile.loadAnimation();
var FemaleElderlyWalkFile = new FemaleElderlyWalk();
var femaleElderlyWalk = FemaleElderlyWalkFile.loadAnimation();
var FemalePowerWalkFile = new FemalePowerWalk();
var femalePowerWalk = FemalePowerWalkFile.loadAnimation();
var FemaleRunFile = new FemaleRun();
var femaleRun = FemaleRunFile.loadAnimation();
var FemaleSexyWalkFile = new FemaleSexyWalk();
var femaleSexyWalk = FemaleSexyWalkFile.loadAnimation();
var FemaleShuffleFile = new FemaleShuffle();
var femaleShuffle = FemaleShuffleFile.loadAnimation();
var FemaleRandomWalkFile = new FemaleRandomWalk();
var femaleRandomWalk = FemaleRandomWalkFile.loadAnimation();
var FemaleStrutWalkFile = new FemaleStrutWalk();
var femaleStrutWalk = FemaleStrutWalkFile.loadAnimation();
var FemaleToughWalkFile = new FemaleToughWalk();
var femaleToughWalk = FemaleToughWalkFile.loadAnimation();
var FemaleFlyingUpFile = new FemaleFlyingUp();
var femaleFlyingUp = FemaleFlyingUpFile.loadAnimation();
var FemaleFlyingFile = new FemaleFlying();
var femaleFlying = FemaleFlyingFile.loadAnimation();
var FemaleFlyingDownFile = new FemaleFlyingDown();
var femaleFlyingDown = FemaleFlyingDownFile.loadAnimation();
var FemaleStandOneFile = new FemaleStandingOne();
var femaleStandOne = FemaleStandOneFile.loadAnimation();
var FemaleStandTwoFile = new FemaleStandingTwo();
var femaleStandTwo = FemaleStandTwoFile.loadAnimation();
var FemaleStandThreeFile = new FemaleStandingThree();
var femaleStandThree = FemaleStandThreeFile.loadAnimation();
var MaleCoolWalkFile = new MaleCoolWalk();
var maleCoolWalk = MaleCoolWalkFile.loadAnimation();
var MaleElderlyWalkFile = new MaleElderlyWalk();
var maleElderlyWalk = MaleElderlyWalkFile.loadAnimation();
var MalePowerWalkFile = new MalePowerWalk();
var malePowerWalk = MalePowerWalkFile.loadAnimation();
var MaleRunFile = new MaleRun();
var maleRun = MaleRunFile.loadAnimation();
var MaleSexyWalkFile = new MaleSexyWalk();
var maleSexyWalk = MaleSexyWalkFile.loadAnimation();
var MaleShuffleFile = new MaleShuffle();
var maleShuffle = MaleShuffleFile.loadAnimation();
var MaleRandomWalkFile = new MaleRandomWalk();
var maleRandomWalk = MaleRandomWalkFile.loadAnimation();
var MaleStrutWalkFile = new MaleStrutWalk();
var maleStrutWalk = MaleStrutWalkFile.loadAnimation();
var MaleToughWalkFile = new MaleToughWalk();
var maleToughWalk = MaleToughWalkFile.loadAnimation();
var MaleFlyingUpFile = new MaleFlyingUp();
var maleFlyingUp = MaleFlyingUpFile.loadAnimation();
var MaleFlyingFile = new MaleFlying();
var maleFlying = MaleFlyingFile.loadAnimation();
var MaleFlyingDownFile = new MaleFlyingDown();
var maleFlyingDown = MaleFlyingDownFile.loadAnimation();
var MaleStandOneFile = new MaleStandingOne();
var maleStandOne = MaleStandOneFile.loadAnimation();
var MaleStandTwoFile = new MaleStandingTwo();
var maleStandTwo = MaleStandTwoFile.loadAnimation();
var MaleStandThreeFile = new MaleStandingThree();
var maleStandThree = MaleStandThreeFile.loadAnimation();
// read in the sounds
var footsteps = [];
footsteps.push(new Sound(pathToSounds+"FootstepW2Left-12db.wav"));
footsteps.push(new Sound(pathToSounds+"FootstepW2Right-12db.wav"));
footsteps.push(new Sound(pathToSounds+"FootstepW3Left-12db.wav"));
footsteps.push(new Sound(pathToSounds+"FootstepW3Right-12db.wav"));
footsteps.push(new Sound(pathToSounds+"FootstepW5Left-12db.wav"));
footsteps.push(new Sound(pathToSounds+"FootstepW5Right-12db.wav"));
// all slider controls have a range (with the exception of phase controls (always +-180))
var sliderRanges =
{
"joints":[
{
"name":"hips",
"pitchRange":25,
"yawRange":25,
"rollRange":25,
"pitchOffsetRange":25,
"yawOffsetRange":25,
"rollOffsetRange":25,
"thrustRange":0.1,
"bobRange":0.5,
"swayRange":0.08
},
{
"name":"upperLegs",
"pitchRange":90,
"yawRange":35,
"rollRange":35,
"pitchOffsetRange":60,
"yawOffsetRange":20,
"rollOffsetRange":20
},
{
"name":"lowerLegs",
"pitchRange":90,
"yawRange":20,
"rollRange":20,
"pitchOffsetRange":90,
"yawOffsetRange":20,
"rollOffsetRange":20
},
{
"name":"feet",
"pitchRange":60,
"yawRange":20,
"rollRange":20,
"pitchOffsetRange":60,
"yawOffsetRange":50,
"rollOffsetRange":50
},
{
"name":"toes",
"pitchRange":90,
"yawRange":20,
"rollRange":20,
"pitchOffsetRange":90,
"yawOffsetRange":20,
"rollOffsetRange":20
},
{
"name":"spine",
"pitchRange":40,
"yawRange":40,
"rollRange":40,
"pitchOffsetRange":90,
"yawOffsetRange":50,
"rollOffsetRange":50
},
{
"name":"spine1",
"pitchRange":20,
"yawRange":40,
"rollRange":20,
"pitchOffsetRange":90,
"yawOffsetRange":50,
"rollOffsetRange":50
},
{
"name":"spine2",
"pitchRange":20,
"yawRange":40,
"rollRange":20,
"pitchOffsetRange":90,
"yawOffsetRange":50,
"rollOffsetRange":50
},
{
"name":"shoulders",
"pitchRange":35,
"yawRange":40,
"rollRange":20,
"pitchOffsetRange":180,
"yawOffsetRange":180,
"rollOffsetRange":180
},
{
"name":"upperArms",
"pitchRange":90,
"yawRange":90,
"rollRange":90,
"pitchOffsetRange":180,
"yawOffsetRange":180,
"rollOffsetRange":180
},
{
"name":"lowerArms",
"pitchRange":90,
"yawRange":90,
"rollRange":120,
"pitchOffsetRange":180,
"yawOffsetRange":180,
"rollOffsetRange":180
},
{
"name":"hands",
"pitchRange":90,
"yawRange":180,
"rollRange":90,
"pitchOffsetRange":180,
"yawOffsetRange":180,
"rollOffsetRange":180
},
{
"name":"head",
"pitchRange":20,
"yawRange":20,
"rollRange":20,
"pitchOffsetRange":90,
"yawOffsetRange":90,
"rollOffsetRange":90
}
]
}
// internal state (FSM based) constants
var STANDING = 2;
var WALKING = 4;
var FLYING = 8;
var CONFIG_WALK_STYLES = 16;
var CONFIG_WALK_TWEAKS = 32;
var CONFIG_WALK_JOINTS = 64;
var CONFIG_STANDING = 128;
var CONFIG_FLYING = 256;
var INTERNAL_STATE = STANDING;
// status
var powerOn = true;
var paused = false; // pause animation playback whilst adjusting certain parameters
var minimised = false;
var armsFree = false; // set true for hydra support - experimental
var statsOn = false;
// constants
var MAX_WALK_SPEED = 1257; // max oscillation speed
var FLYING_SPEED = 12.5; // m/s - real humans can't run any faster
var TERMINAL_VELOCITY = 300;
var DIRECTION_UP = 1;
var DIRECTION_DOWN = 2;
var DIRECTION_LEFT = 4;
var DIRECTION_RIGHT = 8;
var DIRECTION_FORWARDS = 16;
var DIRECTION_BACKWARDS = 32;
var MALE = 64;
var FEMALE = 128;
// start of animation control section
var cumulativeTime = 0.0;
var lastOrientation;
var movementDirection = DIRECTION_FORWARDS;
var playFootStepSounds = true;
var avatarGender = FEMALE;
var selectedWalk = femaleStrutWalk; // the currently selected animation walk file
var selectedStand = femaleStandOne;
var selectedFlyUp = femaleFlyingUp;
var selectedFly = femaleFlying;
var selectedFlyDown = femaleFlyingDown;
var currentAnimation = selectedStand; // the current animation
var selectedJointIndex = 0; // the index of the joint currently selected for editing
// stride calibration
var maxFootForward = 0;
var maxFootBackwards = 0;
var strideLength = 0;
// walkwheel (foot / ground speed matching)
var walkCycleStart = 105; // best foot forwards - TODO: if different for different anims, add as setting to anim files
var walkWheelPosition = walkCycleStart;
// for showing walk wheel stats
var nFrames = 0;
// convert hips translations to global (i.e. take account of avi orientation)
function translateHips(localHipsTranslation) {
var aviOrientation = MyAvatar.orientation;
var front = Quat.getFront(aviOrientation);
var right = Quat.getRight(aviOrientation);
var up = Quat.getUp (aviOrientation);
var aviFront = Vec3.multiply(front,localHipsTranslation.y);
var aviRight = Vec3.multiply(right,localHipsTranslation.x);
var aviUp = Vec3.multiply(up ,localHipsTranslation.z);
var AviTranslationOffset = {x:0,y:0,z:0}; // final value
AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviFront);
AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviRight);
AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviUp);
//MyAvatar.addThrust(AviTranslationOffset * 10);
MyAvatar.position = {x: MyAvatar.position.x + AviTranslationOffset.x,
y: MyAvatar.position.y + AviTranslationOffset.y,
z: MyAvatar.position.z + AviTranslationOffset.z };
}
// convert a local (to the avi) translation to a global one
function globalToLocal(localTranslation) {
var aviOrientation = MyAvatar.orientation;
var front = Quat.getFront(aviOrientation);
var right = Quat.getRight(aviOrientation);
var up = Quat.getUp (aviOrientation);
var aviFront = Vec3.multiply(front,localTranslation.z);
var aviRight = Vec3.multiply(right,localTranslation.x);
var aviUp = Vec3.multiply(up ,localTranslation.y);
var globalTranslation = {x:0,y:0,z:0}; // final value
globalTranslation = Vec3.sum(globalTranslation, aviFront);
globalTranslation = Vec3.sum(globalTranslation, aviRight);
globalTranslation = Vec3.sum(globalTranslation, aviUp);
return globalTranslation;
}
// zero out all joints
function resetJoints() {
var avatarJointNames = MyAvatar.getJointNames();
for (var i = 0; i < avatarJointNames.length; i++) {
//MyAvatar.setJointData(avatarJointNames[i], Quat.fromPitchYawRollDegrees(0,0,0));
MyAvatar.clearJointData(avatarJointNames[i]);
}
}
// play footstep sound
function playFootstep(side) {
var options = new AudioInjectionOptions();
options.position = Camera.getPosition();
options.volume = 0.7;
var walkNumber = 2; // 0 to 2
if(side===DIRECTION_RIGHT && playFootStepSounds) {
//print('playing right footstep - if you can not hear sound, try turning your mic on then off again.');
Audio.playSound(footsteps[walkNumber+1], options);
}
else if(side===DIRECTION_LEFT && playFootStepSounds) {
//print('playing left footstep - if you can not hear sound, try turning your mic on then off again.');
Audio.playSound(footsteps[walkNumber], options);
}
}
// this is work in progress
// currently, it's not known if there are working finger joints on the avi
function curlFingers() {
MyAvatar.setJointData("RightHandMiddle1", Quat.fromPitchYawRollDegrees(90,0,0));
for(var i = 24 ; i < 44 ; i++) {
MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(0,90,0));
}
for(var i = 48 ; i < 68 ; i++) {
MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(10,0,90));
}
}
// maths functions
function toRadians(degreesValue) {
return degreesValue * Math.PI / 180;
}
function toDegrees(radiansValue) {
return radiansValue * 180 / Math.PI;
}
function cubicRoot(x) {
var y = Math.pow(Math.abs(x), 1/3);
return x < 0 ? -y : y;
}
// animateAvatar - animates the avatar - TODO doxygen comments?
var nextStep = DIRECTION_RIGHT; // first step is always right, because the sine waves say so
function animateAvatar(deltaTime, velocity, principleDirection) {
// some slider adjustemnts cause a nasty flicker when adjusting
// pausing the animation stops this
if(paused) return;
var adjustedFrequency = currentAnimation.settings.baseFrequency; // now only relevant for standing and flying
// simple legs phase reversal for walking backwards
var forwardModifier = 1;
if(principleDirection===DIRECTION_BACKWARDS) {
forwardModifier = -1;
}
// no need to lean forwards with speed increase if going directly upwards
var leanPitchModifier = 1;
if(principleDirection===DIRECTION_UP) {
leanPitchModifier = 0;
}
if(currentAnimation === selectedWalk) {
if(INTERNAL_STATE===CONFIG_WALK_STYLES ||
INTERNAL_STATE===CONFIG_WALK_TWEAKS ||
INTERNAL_STATE===CONFIG_WALK_JOINTS) {
var footPos = globalToLocal(MyAvatar.getJointPosition("RightFoot"));
var hipsPos = globalToLocal(MyAvatar.getJointPosition("Hips"));
// calibrate stride length whilst not actually moving (for accuracy)
if((hipsPos.z - footPos.z)<maxFootBackwards) maxFootBackwards = (hipsPos.z - footPos.z);
else if ((hipsPos.z - footPos.z)>maxFootForward) maxFootForward = (hipsPos.z - footPos.z);
strideLength = 2 * (maxFootForward-maxFootBackwards);
// TODO: take note of the avi's stride length (can store in anim file or recalculate each time worn or edited)
print('Stride length calibration: Your stride length is ' + strideLength + ' metres'); // ~ 0.8211
}
else {
if(strideLength===0) strideLength = 1.6422; // default for if not calibrated yet
// wrap the stride length around a 'surveyor's wheel' twice and calculate the angular velocity at the given (linear) velocity
// omega = v / r , where r = circumference / 2 PI , where circumference = 2 * stride
var wheelRadius = strideLength / Math.PI;
var angularVelocity = velocity / wheelRadius;
// calculate the degrees turned (at this angular velocity) since last frame
var radiansTurnedSinceLastFrame = deltaTime * angularVelocity;
var degreesTurnedSinceLastFrame = toDegrees(radiansTurnedSinceLastFrame);
// advance the walk wheel the appropriate amount
// TODO: use radians, as Math.sin needs radians below anyway!
walkWheelPosition += degreesTurnedSinceLastFrame;
if( walkWheelPosition >= 360 )
walkWheelPosition = walkWheelPosition % 360;
// set the new value for the exact correct walking speed for this velocity
adjustedFrequency = 1;
cumulativeTime = walkWheelPosition;
// show stats and walk wheel?
if(statsOn) {
nFrames++;
var distanceTravelled = velocity * deltaTime;
var deltaTimeMS = deltaTime * 1000;
// draw the walk wheel
var yOffset = hipsToFeetDistance - wheelRadius;
var sinWalkWheelPosition = wheelRadius * Math.sin(toRadians((forwardModifier*-1) * walkWheelPosition));
var cosWalkWheelPosition = wheelRadius * Math.cos(toRadians((forwardModifier*-1) * -walkWheelPosition));
var wheelZPos = {x:0, y:-sinWalkWheelPosition - yOffset, z: cosWalkWheelPosition};
var wheelZEnd = {x:0, y:sinWalkWheelPosition - yOffset, z: -cosWalkWheelPosition};
sinWalkWheelPosition = wheelRadius * Math.sin(toRadians(forwardModifier * walkWheelPosition+90));
cosWalkWheelPosition = wheelRadius * Math.cos(toRadians(forwardModifier * walkWheelPosition+90));
var wheelYPos = {x:0, y:sinWalkWheelPosition - yOffset, z: cosWalkWheelPosition};
var wheelYEnd = {x:0, y:-sinWalkWheelPosition - yOffset, z: -cosWalkWheelPosition};
Overlays.editOverlay(walkWheelYLine, {position:wheelYPos, end:wheelYEnd});
Overlays.editOverlay(walkWheelZLine, {position:wheelZPos, end:wheelZEnd});
// populate debug overlay
var debugInfo = 'Frame number: '+nFrames
+ '\nFrame time: '+deltaTimeMS.toFixed(2)
+ ' mS\nVelocity: '+velocity.toFixed(2)
+ ' m/s\nDistance: '+distanceTravelled.toFixed(3)
+ ' m\nOmega: '+angularVelocity.toFixed(3)
+ ' rad / s\nDeg to turn: '+degreesTurnedSinceLastFrame.toFixed(2)
+ ' deg\nStride: '+strideLength.toFixed(3)
+ ' m\nWheel position: '+cumulativeTime.toFixed(1)
+ ' deg\n';
Overlays.editOverlay(debugText, {text: debugInfo});
//print('strideLength '+strideLength.toFixed(4)+' deltaTime '+deltaTimeMS.toFixed(4)+' distanceTravelled '+distanceTravelled.toFixed(3)+' velocity '+velocity.toFixed(3)+' angularVelocity '+angularVelocity.toFixed(3)+' degreesTurnedSinceLastFrame '+degreesTurnedSinceLastFrame.toFixed(3) + ' cumulativeTime '+cumulativeTime.toFixed(3));
}
}
} else {
nFrames = 0;
walkWheelPosition = walkCycleStart; // best foot forwards for next time we walk
}
// TODO: optimise by precalculating and re-using Math.sin((cumulativeTime * femaleSexyWalk.settings.baseFrequency) when there is no phase to be applied
// TODO: optimise by 'baking' offsets and phases after editing and use during normal playback
// calcualte hips translation
//var motorOscillation = Math.sin(toRadians((cumulativeTime * adjustedFrequency * 2) + currentAnimation.joints[0].thrustPhase)) + currentAnimation.joints[0].thrustOffset;
//var swayOscillation = Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[0].swayPhase)) + currentAnimation.joints[0].swayOffset;
//var bobOscillation = Math.sin(toRadians((cumulativeTime * adjustedFrequency * 2) + currentAnimation.joints[0].bobPhase)) + currentAnimation.joints[0].bobOffset;
// calculate hips rotation
var pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(toRadians((cumulativeTime * adjustedFrequency * 2)
+ currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset;
var yawOscillation = currentAnimation.joints[0].yaw * Math.sin(toRadians((cumulativeTime * adjustedFrequency )
+ currentAnimation.joints[0].yawPhase)) + currentAnimation.joints[0].yawOffset;
var rollOscillation = (currentAnimation.joints[0].roll * Math.sin(toRadians((cumulativeTime * adjustedFrequency )
+ currentAnimation.joints[0].rollPhase)) + currentAnimation.joints[0].rollOffset);
// apply hips translation TODO: get this working!
//translateHips({x:swayOscillation*currentAnimation.joints[0].sway, y:motorOscillation*currentAnimation.joints[0].thrust, z:bobOscillation*currentAnimation.joints[0].bob});
// apply hips rotation
MyAvatar.setJointData("Hips", Quat.fromPitchYawRollDegrees(-pitchOscillation + (forwardModifier * (leanPitchModifier*getLeanPitch(velocity))), // getLeanPitch - lean forwards as velocity increased
yawOscillation, // Yup, that's correct ;-)
rollOscillation + getLeanRoll(deltaTime,velocity))); // getLeanRoll - banking on cornering
// calculate upper leg rotations
// TODO: clean up here - increase stride a bit as velocity increases
var runningModifier = velocity / currentAnimation.settings.takeFlightVelocity;
if(runningModifier>1) {
runningModifier *= 2;
runningModifier += 0.5;
}
else if(runningModifier>0) {
runningModifier *= 2;
runningModifier += 0.5;
}
else runningModifier = 1; // standing
runningModifier = 1; // TODO - remove this little disabling hack!
pitchOscillation = runningModifier * currentAnimation.joints[1].pitch * Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase)));
yawOscillation = currentAnimation.joints[1].yaw * Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[1].yawPhase));
rollOscillation = currentAnimation.joints[1].roll * Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[1].rollPhase));
// apply upper leg rotations
var strideAdjusterPitch = 0;
var strideAdjusterPitchOffset = 0;
var strideAdjusterSeparationAngle = 0;
if(INTERNAL_STATE===WALKING ||
INTERNAL_STATE===CONFIG_WALK_STYLES ||
INTERNAL_STATE===CONFIG_WALK_TWEAKS ||
INTERNAL_STATE===CONFIG_WALK_JOINTS) {
strideAdjusterPitch = currentAnimation.adjusters.stride.strength*currentAnimation.adjusters.stride.upperLegsPitch;
strideAdjusterPitchOffset = currentAnimation.adjusters.stride.strength*currentAnimation.adjusters.stride.upperLegsPitchOffset;
strideAdjusterSeparationAngle = currentAnimation.adjusters.legsSeparation.strength * currentAnimation.adjusters.legsSeparation.separationAngle;
}
MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(
pitchOscillation + currentAnimation.joints[1].pitchOffset + strideAdjusterPitch * (Math.sin(toRadians((cumulativeTime * adjustedFrequency) + currentAnimation.joints[1].pitchPhase)) + strideAdjusterPitchOffset),
yawOscillation + currentAnimation.joints[1].yawOffset,
-rollOscillation - strideAdjusterSeparationAngle - currentAnimation.joints[1].rollOffset ));
MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees(
- pitchOscillation + currentAnimation.joints[1].pitchOffset - strideAdjusterPitch * (Math.sin(toRadians((cumulativeTime * adjustedFrequency) + currentAnimation.joints[1].pitchPhase)) - strideAdjusterPitchOffset),
yawOscillation - currentAnimation.joints[1].yawOffset,
-rollOscillation + strideAdjusterSeparationAngle + currentAnimation.joints[1].rollOffset ));
// calculate lower leg joint rotations
strideAdjusterPitch = 0;
strideAdjusterPitchOffset = 0;
if(INTERNAL_STATE===WALKING ||
INTERNAL_STATE===CONFIG_WALK_STYLES ||
INTERNAL_STATE===CONFIG_WALK_TWEAKS ||
INTERNAL_STATE===CONFIG_WALK_JOINTS) {
strideAdjusterPitch = currentAnimation.adjusters.stride.strength * currentAnimation.adjusters.stride.lowerLegsPitch;
strideAdjusterPitchOffset = currentAnimation.adjusters.stride.strength*currentAnimation.adjusters.stride.lowerLegsPitchOffset;
}
pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase));
yawOscillation = currentAnimation.joints[2].yaw * Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[2].yawPhase));
rollOscillation = currentAnimation.joints[2].roll * Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[2].rollPhase));
// apply lower leg joint rotations
MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees(
-pitchOscillation + currentAnimation.joints[2].pitchOffset - strideAdjusterPitch * (Math.sin(toRadians((cumulativeTime * adjustedFrequency) + currentAnimation.joints[2].pitchPhase)) + strideAdjusterPitchOffset),
yawOscillation - currentAnimation.joints[2].yawOffset,
rollOscillation - currentAnimation.joints[2].rollOffset)); // TODO: needs a kick just before fwd peak
MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees(
pitchOscillation + currentAnimation.joints[2].pitchOffset + strideAdjusterPitch * (Math.sin(toRadians((cumulativeTime * adjustedFrequency) + currentAnimation.joints[2].pitchPhase)) - strideAdjusterPitchOffset),
yawOscillation + currentAnimation.joints[2].yawOffset,
rollOscillation + currentAnimation.joints[2].rollOffset));
// foot joint oscillation is a hard curve to replicate
var wave = 1;//(baseOscillation + 1)/2; // TODO: finish this - +ve num between 0 and 1 gives a kick at the forward part of the swing
pitchOscillation = wave * currentAnimation.joints[3].pitch * Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase));
yawOscillation = currentAnimation.joints[3].yaw * Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[3].yawPhase));
rollOscillation = currentAnimation.joints[3].roll * Math.sin(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[3].rollPhase));
MyAvatar.setJointData("RightFoot", Quat.fromPitchYawRollDegrees( pitchOscillation + currentAnimation.joints[3].pitchOffset, yawOscillation + currentAnimation.joints[3].yawOffset, rollOscillation + currentAnimation.joints[3].rollOffset));
MyAvatar.setJointData("LeftFoot", Quat.fromPitchYawRollDegrees(-pitchOscillation + currentAnimation.joints[3].pitchOffset, yawOscillation - currentAnimation.joints[3].yawOffset, rollOscillation - currentAnimation.joints[3].rollOffset));
if(INTERNAL_STATE===WALKING ||
INTERNAL_STATE===CONFIG_WALK_STYLES ||
INTERNAL_STATE===CONFIG_WALK_TWEAKS ||
INTERNAL_STATE===CONFIG_WALK_JOINTS) {
// play footfall sound yet? To determine this, we take the differential of the foot's pitch curve to decide
// when the foot hits the ground. As luck would have it, we're using a sine wave, so finding dy/dx is as
// simple as determining the cosine wave for the foot's pitch function...
var feetPitchDifferential = Math.cos(toRadians((cumulativeTime * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase));
var threshHold = 0.9; // sets the audio trigger point. with accuracy.
if(feetPitchDifferential<-threshHold &&
nextStep===DIRECTION_LEFT &&
principleDirection!==DIRECTION_UP &&
principleDirection!==DIRECTION_DOWN) {
playFootstep(DIRECTION_LEFT);
nextStep = DIRECTION_RIGHT;
}
else if(feetPitchDifferential>threshHold &&
nextStep===DIRECTION_RIGHT &&
principleDirection!==DIRECTION_UP &&
principleDirection!==DIRECTION_DOWN) {
playFootstep(DIRECTION_RIGHT);
nextStep = DIRECTION_LEFT;
}
}
// toes joint oscillation
pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(toRadians((cumulativeTime * adjustedFrequency) + currentAnimation.joints[4].pitchPhase));
yawOscillation = currentAnimation.joints[4].yaw * Math.sin(toRadians((cumulativeTime * adjustedFrequency) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset;
rollOscillation = currentAnimation.joints[4].roll * Math.sin(toRadians((cumulativeTime * adjustedFrequency) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset;
MyAvatar.setJointData("RightToeBase", Quat.fromPitchYawRollDegrees(-pitchOscillation + currentAnimation.joints[4].pitchOffset, yawOscillation, rollOscillation));
MyAvatar.setJointData("LeftToeBase", Quat.fromPitchYawRollDegrees( pitchOscillation + currentAnimation.joints[4].pitchOffset, yawOscillation, rollOscillation));
// calculate spine joint rotations
pitchOscillation = currentAnimation.joints[5].pitch * Math.sin(toRadians(( cumulativeTime * adjustedFrequency * 2) + currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset;
yawOscillation = currentAnimation.joints[5].yaw * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[5].yawPhase)) + currentAnimation.joints[5].yawOffset;
rollOscillation = currentAnimation.joints[5].roll * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[5].rollPhase)) + currentAnimation.joints[5].rollOffset;
// apply spine joint rotations
MyAvatar.setJointData("Spine", Quat.fromPitchYawRollDegrees(-pitchOscillation, yawOscillation, rollOscillation));
// calcualte spine 1 rotatations
pitchOscillation = currentAnimation.joints[6].pitch * Math.sin(toRadians(( cumulativeTime * adjustedFrequency * 2) + currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset;
yawOscillation = currentAnimation.joints[6].yaw * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[6].yawPhase)) + currentAnimation.joints[6].yawOffset;
rollOscillation = currentAnimation.joints[6].roll * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[6].rollPhase)) + currentAnimation.joints[6].rollOffset;
// apply spine1 joint rotations
MyAvatar.setJointData("Spine1", Quat.fromPitchYawRollDegrees( pitchOscillation, -yawOscillation, rollOscillation));
// spine 2
pitchOscillation = currentAnimation.joints[7].pitch * Math.sin(toRadians(( cumulativeTime * adjustedFrequency * 2) + currentAnimation.joints[7].pitchPhase)) + currentAnimation.joints[7].pitchOffset;
yawOscillation = currentAnimation.joints[7].yaw * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[7].yawPhase)) + currentAnimation.joints[7].yawOffset;
rollOscillation = currentAnimation.joints[7].roll * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[7].rollPhase)) + currentAnimation.joints[7].rollOffset;
// apply spine2 joint rotations
MyAvatar.setJointData("Spine2", Quat.fromPitchYawRollDegrees(-pitchOscillation, yawOscillation, -rollOscillation));
if(!armsFree) {
// shoulders
pitchOscillation = currentAnimation.joints[8].pitch * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset;
yawOscillation = currentAnimation.joints[8].yaw * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[8].yawPhase));
rollOscillation = currentAnimation.joints[8].roll * Math.sin(toRadians(( cumulativeTime * adjustedFrequency * 2) + currentAnimation.joints[8].rollPhase)) + currentAnimation.joints[8].rollOffset;
MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees(pitchOscillation, yawOscillation + currentAnimation.joints[8].yawOffset, rollOscillation ));
MyAvatar.setJointData("LeftShoulder", Quat.fromPitchYawRollDegrees(pitchOscillation, yawOscillation - currentAnimation.joints[8].yawOffset, -rollOscillation ));
// upper arms
pitchOscillation = currentAnimation.joints[9].pitch * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[9].pitchPhase)) + currentAnimation.joints[9].pitchOffset;
yawOscillation = currentAnimation.joints[9].yaw * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[9].yawPhase));
rollOscillation = currentAnimation.joints[9].roll * Math.sin(toRadians(( cumulativeTime * adjustedFrequency * 2) + currentAnimation.joints[9].rollPhase)) + currentAnimation.joints[9].rollOffset;
MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation - currentAnimation.joints[9].yawOffset, rollOscillation ));
MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation + currentAnimation.joints[9].yawOffset, -rollOscillation ));
// forearms
pitchOscillation = currentAnimation.joints[10].pitch * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[10].pitchPhase)) + currentAnimation.joints[10].pitchOffset;
yawOscillation = currentAnimation.joints[10].yaw * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[10].yawPhase));
rollOscillation = currentAnimation.joints[10].roll * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[10].rollPhase));
MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation + currentAnimation.joints[10].yawOffset, rollOscillation + currentAnimation.joints[10].rollOffset ));
MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation - currentAnimation.joints[10].yawOffset, rollOscillation - currentAnimation.joints[10].rollOffset ));
// hands
pitchOscillation = currentAnimation.joints[11].pitch * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[11].pitchPhase)) + currentAnimation.joints[11].pitchOffset;
yawOscillation = currentAnimation.joints[11].yaw * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[11].yawPhase)) + currentAnimation.joints[11].yawOffset;
rollOscillation = currentAnimation.joints[11].roll * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[11].rollPhase)) ;
MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation + currentAnimation.joints[11].rollOffset));
MyAvatar.setJointData("LeftHand", Quat.fromPitchYawRollDegrees( pitchOscillation, -yawOscillation, rollOscillation - currentAnimation.joints[11].rollOffset));
} // if(!armsFree)
// head
pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(toRadians(( cumulativeTime * adjustedFrequency * 2) + currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset;
yawOscillation = 0.5 * currentAnimation.joints[12].yaw * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[12].yawPhase)) + currentAnimation.joints[12].yawOffset;
rollOscillation = 0.5 * currentAnimation.joints[12].roll * Math.sin(toRadians(( cumulativeTime * adjustedFrequency ) + currentAnimation.joints[12].rollPhase)) + currentAnimation.joints[12].rollOffset;
MyAvatar.setJointData("Head", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation));
MyAvatar.setJointData("Neck", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation));
}
// getBezier from: http://13thparallel.com/archive/bezier-curves/
//====================================\\
// 13thParallel.org Bezi<7A>r Curve Code \\
// by Dan Pupius (www.pupius.net) \\
//====================================\\
/*
coord = function (x,y) {
if(!x) var x=0;
if(!y) var y=0;
return {x: x, y: y};
}
function B1(t) { return t*t*t }
function B2(t) { return 3*t*t*(1-t) }
function B3(t) { return 3*t*(1-t)*(1-t) }
function B4(t) { return (1-t)*(1-t)*(1-t) }
function getBezier(percent,C1,C2,C3,C4) {
var pos = new coord();
pos.x = C1.x*B1(percent) + C2.x*B2(percent) + C3.x*B3(percent) + C4.x*B4(percent);
pos.y = C1.y*B1(percent) + C2.y*B2(percent) + C3.y*B3(percent) + C4.y*B4(percent);
return pos;
}
*/
// the faster we go, the further we lean forward. the angle is calcualted here
var leanAngles = [0,0,0,0,0,0,0,0,0,0]; // smooth out and add damping with simple averaging filter. 20 tap = too much damping, 10 pretty good
function getLeanPitch(velocity) {
if(velocity>TERMINAL_VELOCITY) velocity=TERMINAL_VELOCITY;
var leanAngle = velocity / TERMINAL_VELOCITY * currentAnimation.settings.flyingHipsPitch;
// simple averaging filter
leanAngles.push(leanAngle);
leanAngles.shift(); // FIFO
var totalLeanAngles = 0;
for(ea in leanAngles) totalLeanAngles += leanAngles[ea];
var finalLeanAngle = totalLeanAngles / leanAngles.length;
//print('final lean angle '+finalLeanAngle); // we found that native support already follows a curve - see graph in forum post
return finalLeanAngle;
// work in progress - apply bezier curve to lean / velocity response
/*var percentTotalLean = velocity / TERMINAL_VELOCITY;
no curve applied - used for checking results only
var linearLeanAngle = percentTotalLean * currentAnimation.settings.flyingHipsPitch;
// make the hips pitch / velocity curve follow a nice bezier
// bezier control points
Q1 = coord(0,0);
Q2 = coord(0.2,0.8);
Q3 = coord(0.8,0.2);
Q4 = coord(1,1);
var easedLean = getBezier(percentTotalLean, Q1, Q2, Q3, Q4);
var leanAngle = (1-easedLean.x) * currentAnimation.settings.flyingHipsPitch;
//print('before bezier: '+linearLeanAngle.toFixed(4)+' after bezier '+leanAngle.toFixed(4));
// simple averaging filter
leanAngles.push(leanAngle);
leanAngles.shift(); // FIFO
var totalLeanAngles = 0;
for(ea in leanAngles) totalLeanAngles += leanAngles[ea];
var finalLeanAngle = totalLeanAngles / leanAngles.length;
print('before bezier: '+linearLeanAngle.toFixed(4));//+' after bezier '+leanAngle.toFixed(4));
return finalLeanAngle;
*/
}
// calculate the angle at which to bank into corners when turning
var angularVelocities = [0,0,0,0,0,0,0,0,0,0]; // smooth out and add damping with simple averaging filter
function getLeanRoll(deltaTime,velocity) {
var angularVelocityMax = 70;
var currentOrientationVec3 = Quat.safeEulerAngles(MyAvatar.orientation);
var lastOrientationVec3 = Quat.safeEulerAngles(lastOrientation);
var deltaYaw = lastOrientationVec3.y-currentOrientationVec3.y;
var angularVelocity = deltaYaw / deltaTime;
if(angularVelocity>70) angularVelocity = angularVelocityMax;
if(angularVelocity<-70) angularVelocity = -angularVelocityMax;
angularVelocities.push(angularVelocity);
angularVelocities.shift(); // FIFO
var totalAngularVelocities = 0;
for(ea in angularVelocities) totalAngularVelocities += angularVelocities[ea];
var averageAngularVelocity = totalAngularVelocities / angularVelocities.length;
var velocityAdjuster = Math.sqrt(velocity/TERMINAL_VELOCITY); // put a little curvature on our otherwise linear velocity modifier
if(velocityAdjuster>1) velocityAdjuster = 1;
if(velocityAdjuster<0) velocityAdjuster = 0;
var leanRoll = velocityAdjuster * (averageAngularVelocity/angularVelocityMax) * currentAnimation.settings.maxBankingAngle;
//print('delta time is '+deltaTime.toFixed(4)+' and delta yaw is '+deltaYaw+' angular velocity is '+angularVelocity+' and average angular velocity is '+averageAngularVelocity+' and velocityAdjuster is '+velocityAdjuster+' and final value is '+leanRoll);
//print('array: '+angularVelocities.toString());
lastOrientation = MyAvatar.orientation;
return leanRoll;
}
// sets up the interface componenets and updates the internal state
function setInternalState(newInternalState) {
switch(newInternalState) {
case WALKING:
print('WALKING');
if(!minimised) doStandardMenu();
INTERNAL_STATE = WALKING;
currentAnimation = selectedWalk;
break;
case FLYING:
print('FLYING');
if(!minimised) doStandardMenu();
INTERNAL_STATE = FLYING;
currentAnimation = selectedFly;
break;
case CONFIG_WALK_STYLES:
INTERNAL_STATE = CONFIG_WALK_STYLES;
currentAnimation = selectedWalk;
if(!minimised) {
hidebuttonOverlays();
hideJointControls();
showFrontPanelButtons(false);
showWalkStyleButtons(true);
setBackground(controlsBackgroundWalkEditStyles);
setButtonOverlayVisible(onButton);
setButtonOverlayVisible(configWalkStylesButtonSelected);
setButtonOverlayVisible(configWalkTweaksButton);
setButtonOverlayVisible(configWalkJointsButton);
setButtonOverlayVisible(backButton);
setSliderthumbsVisible(false);
}
break;
case CONFIG_WALK_TWEAKS:
INTERNAL_STATE = CONFIG_WALK_TWEAKS;
currentAnimation = selectedWalk;
if(!minimised) {
hidebuttonOverlays();
hideJointControls();
showFrontPanelButtons(false);
showWalkStyleButtons(false);
setBackground(controlsBackgroundWalkEditTweaks);
setButtonOverlayVisible(onButton);
setButtonOverlayVisible(configWalkStylesButton);
setButtonOverlayVisible(configWalkTweaksButtonSelected);
setButtonOverlayVisible(configWalkJointsButton);
setButtonOverlayVisible(backButton);
initialiseWalkTweaks();
}
break;
case CONFIG_WALK_JOINTS:
INTERNAL_STATE = CONFIG_WALK_JOINTS;
currentAnimation = selectedWalk;
if(!minimised) {
hidebuttonOverlays();
showFrontPanelButtons(false);
showWalkStyleButtons(false);
setBackground(controlsBackgroundWalkEditJoints);
setButtonOverlayVisible(onButton);
setButtonOverlayVisible(configWalkStylesButton);
setButtonOverlayVisible(configWalkTweaksButton);
setButtonOverlayVisible(configWalkJointsButtonSelected);
setButtonOverlayVisible(backButton);
Overlays.editOverlay(hipsJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
initialiseWalkJointsPanel(selectedJointIndex);
}
break;
case CONFIG_STANDING:
INTERNAL_STATE = CONFIG_STANDING;
currentAnimation = selectedStand;
if(!minimised) {
hidebuttonOverlays();
hideJointControls();
doStandardMenu();
showFrontPanelButtons(false);
showWalkStyleButtons(false);
setBackground(controlsBackgroundWalkEditJoints);
setButtonOverlayVisible(configStandButtonSelected);
Overlays.editOverlay(hipsJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
initialiseWalkJointsPanel(selectedJointIndex);
}
break;
case CONFIG_FLYING:
INTERNAL_STATE = CONFIG_FLYING;
currentAnimation = selectedFly;
if(!minimised) {
hidebuttonOverlays();
hideJointControls();
doStandardMenu();
showFrontPanelButtons(false);
showWalkStyleButtons(false);
setBackground(controlsBackgroundWalkEditJoints);
setButtonOverlayVisible(configFlyingButtonSelected);
Overlays.editOverlay(hipsJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
initialiseWalkJointsPanel(selectedJointIndex);
}
break;
case STANDING:
default:
print('STANDING');
INTERNAL_STATE = STANDING;
if(!minimised) doStandardMenu();
currentAnimation = selectedStand;
break;
}
}
// Main loop
// stabilising vars - most state changes are preceded by a couple of hints that they are about to happen
// rather than momentarilly switching between states (causes flicker), we count the number of hints in a row before
// actually changing state - a system very similar to switch debouncing in electronics design
var standHints = 0;
var walkHints = 0;
var flyHints = 0;
var requiredHints = 3; // debounce state changes - how many times do we get a state change request before we actually change state?
var lastDirection = DIRECTION_FORWARDS;
Script.update.connect(function(deltaTime) {
if(powerOn) {
cumulativeTime += deltaTime;
// firstly test for user configuration states
switch(INTERNAL_STATE) {
case CONFIG_WALK_STYLES:
currentAnimation = selectedWalk;
animateAvatar(deltaTime, 0, DIRECTION_FORWARDS);
return;
case CONFIG_WALK_TWEAKS:
currentAnimation = selectedWalk;
animateAvatar(deltaTime, 0, DIRECTION_FORWARDS);
return;
case CONFIG_WALK_JOINTS:
currentAnimation = selectedWalk;
animateAvatar(deltaTime, 0, DIRECTION_FORWARDS);
return;
case CONFIG_STANDING:
currentAnimation = selectedStand;
animateAvatar(deltaTime, 0, DIRECTION_FORWARDS);
return;
case CONFIG_FLYING:
currentAnimation = selectedFly;
animateAvatar(deltaTime, 0, DIRECTION_FORWARDS);
return;
default:
break;
}
// calcualte (local) change in position and velocity
var velocityVector = MyAvatar.getVelocity();
var velocity = Vec3.length(velocityVector);
// determine the candidate animation to play
var actionToTake = 0;
if( velocity < 0.1) {
actionToTake = STANDING;
standHints++;
}
else if(velocity<FLYING_SPEED) {
actionToTake = WALKING;
walkHints++;
}
else if(velocity>=FLYING_SPEED) {
actionToTake = FLYING;
flyHints++;
}
// calculate overriding (local) direction of translation for use later when decide which animation should be played
var principleDirection = 0;
var localVelocity = globalToLocal(velocityVector);
var deltaX = localVelocity.x;
var deltaY = -localVelocity.y;
var deltaZ = -localVelocity.z;
// TODO: find out why there is a reported high up / down velocity as we near walking -> standing...
var directionChangeThreshold = 0.3; // this little hack makes it a bit better, but at the cost of delayed updated chagne in direction etc :-(
if(velocity<directionChangeThreshold)
principleDirection = lastDirection;
else {
// who's the biggest out of the x, y and z components taken from the difference vector?
if(Math.abs(deltaX)>Math.abs(deltaY)
&&Math.abs(deltaX)>Math.abs(deltaZ)) {
if(deltaX<0) {
principleDirection = DIRECTION_RIGHT;//print('velocity = '+velocity + ' DIRECTION_RIGHT: deltaX '+deltaX+' deltaY '+deltaY+' deltaZ '+deltaZ);
} else {
principleDirection = DIRECTION_LEFT;//print('velocity = '+velocity + ' DIRECTION_LEFT: deltaX '+deltaX+' deltaY '+deltaY+' deltaZ '+deltaZ);
}
}
else if(Math.abs(deltaY)>Math.abs(deltaX)
&&Math.abs(deltaY)>Math.abs(deltaZ)) {
if(deltaY>0) {
principleDirection = DIRECTION_DOWN;//print('velocity = '+velocity + ' DIRECTION_DOWN: deltaX '+deltaX+' deltaY '+deltaY+' deltaZ '+deltaZ);
}
else {
principleDirection = DIRECTION_UP;//print('velocity = '+velocity + ' DIRECTION_UP: deltaX '+deltaX+' deltaY '+deltaY+' deltaZ '+deltaZ);
}
}
else if(Math.abs(deltaZ)>Math.abs(deltaX)
&&Math.abs(deltaZ)>Math.abs(deltaY)) {
if(deltaZ>0) {
principleDirection = DIRECTION_BACKWARDS;//print('velocity = '+velocity + ' DIRECTION_BACKWARDS: deltaX '+deltaX+' deltaY '+deltaY+' deltaZ '+deltaZ);
} else {
principleDirection = DIRECTION_FORWARDS;//print('velocity = '+velocity + ' DIRECTION_FORWARDS: deltaX '+deltaX+' deltaY '+deltaY+' deltaZ '+deltaZ);
}
}
}
lastDirection = principleDirection;
// select appropriate animation
switch(actionToTake) {
case STANDING:
if( standHints > requiredHints || INTERNAL_STATE===STANDING) { // wait for a few consecutive hints (17mS each)
standHints = 0;
walkHints = 0;
flyHints = 0;
if(INTERNAL_STATE!==STANDING) setInternalState(STANDING);
currentAnimation = selectedStand;
animateAvatar(1,0,principleDirection);
}
return;
case WALKING:
if( walkHints > requiredHints || INTERNAL_STATE===WALKING) { // wait for few consecutive hints (17mS each)
standHints = 0;
walkHints = 0;
flyHints = 0;
if(INTERNAL_STATE!==WALKING) setInternalState(WALKING);
// change animation for flying directly up or down
if(principleDirection===DIRECTION_UP) {
currentAnimation = selectedFlyUp;
}
else if(principleDirection===DIRECTION_DOWN) {
currentAnimation = selectedFlyDown;
}
else {
currentAnimation = selectedWalk;
}
animateAvatar(deltaTime, velocity, principleDirection);
}
return;
case FLYING:
if( flyHints > requiredHints - 1 || INTERNAL_STATE===FLYING ) { // wait for a few consecutive hints (17mS each)
standHints = 0;
walkHints = 0;
flyHints = 0;
if(INTERNAL_STATE!==FLYING) setInternalState(FLYING);
// change animation for flying directly up or down
if(principleDirection===DIRECTION_UP) {
currentAnimation = selectedFlyUp;
}
else if(principleDirection===DIRECTION_DOWN) {
currentAnimation = selectedFlyDown;
}
else currentAnimation = selectedFly;
animateAvatar(deltaTime, velocity, principleDirection);
}
return;
}
}
});
// overlays start
// controller dimensions
var backgroundWidth = 350;
var backgroundHeight = 700;
var backgroundX = Window.innerWidth-backgroundWidth-50;
var backgroundY = Window.innerHeight/2 - backgroundHeight/2;
var minSliderX = backgroundX + 30;
var maxSliderX = backgroundX + 295;
var sliderRangeX = 295 - 30;
var jointsControlWidth = 200;
var jointsControlHeight = 300;
var jointsControlX = backgroundX/2 - jointsControlWidth/2;
var jointsControlY = backgroundY/2 - jointsControlHeight/2;
var buttonsY = 20; // distance from top of panel to buttons
// arrays of overlay names
var sliderthumbOverlays = []; // thumb sliders
var backgroundOverlays = [];
var buttonOverlays = [];
var jointsControlOverlays = [];
var bigButtonOverlays = [];
// take a deep breath then load up the overlays
// UI backgrounds
var controlsBackground = Overlays.addOverlay("image", {
bounds: { x: backgroundX, y: backgroundY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
backgroundOverlays.push(controlsBackground);
var controlsBackgroundWalkEditStyles = Overlays.addOverlay("image", {
bounds: { x: backgroundX, y: backgroundY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-styles.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
backgroundOverlays.push(controlsBackgroundWalkEditStyles);
var controlsBackgroundWalkEditTweaks = Overlays.addOverlay("image", {
bounds: { x: backgroundX, y: backgroundY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-tweaks.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
backgroundOverlays.push(controlsBackgroundWalkEditTweaks);
var controlsBackgroundWalkEditJoints = Overlays.addOverlay("image", {
bounds: { x: backgroundX, y: backgroundY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-joints.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
backgroundOverlays.push(controlsBackgroundWalkEditJoints);
var controlsBackgroundFlyingEdit = Overlays.addOverlay("image", {
bounds: { x: backgroundX, y: backgroundY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-flying-edit.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
backgroundOverlays.push(controlsBackgroundFlyingEdit);
// minimised tab - not put in array, as is a one off
var controlsMinimisedTab = Overlays.addOverlay("image", {
bounds: { x: Window.innerWidth - 35, y: Window.innerHeight/2 - 175, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-tab.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
// load character joint selection control images
var hipsJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-hips.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(hipsJointControl);
var upperLegsJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-upper-legs.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(upperLegsJointControl);
var lowerLegsJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-lower-legs.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(lowerLegsJointControl);
var feetJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-feet.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(feetJointControl);
var toesJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-toes.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(toesJointControl);
var spineJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-spine.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(spineJointControl);
var spine1JointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-spine1.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(spine1JointControl);
var spine2JointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-spine2.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(spine2JointControl);
var shouldersJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-shoulders.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(shouldersJointControl);
var upperArmsJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-upper-arms.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(upperArmsJointControl);
var forearmsJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-forearms.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(forearmsJointControl);
var handsJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-hands.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(handsJointControl);
var headJointControl = Overlays.addOverlay("image", {
bounds: { x: jointsControlX, y: jointsControlY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-background-edit-head.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
jointsControlOverlays.push(headJointControl);
// sider thumb overlays
var sliderOne = Overlays.addOverlay("image", {
bounds: { x: 0, y: 0, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-slider-handle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
sliderthumbOverlays.push(sliderOne);
var sliderTwo = Overlays.addOverlay("image", {
bounds: { x: 0, y: 0, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-slider-handle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
sliderthumbOverlays.push(sliderTwo);
var sliderThree = Overlays.addOverlay("image", {
bounds: { x: 0, y: 0, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-slider-handle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
sliderthumbOverlays.push(sliderThree);
var sliderFour = Overlays.addOverlay("image", {
bounds: { x: 0, y: 0, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-slider-handle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
sliderthumbOverlays.push(sliderFour);
var sliderFive = Overlays.addOverlay("image", {
bounds: { x: 0, y: 0, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-slider-handle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
sliderthumbOverlays.push(sliderFive);
var sliderSix = Overlays.addOverlay("image", {
bounds: { x: 0, y: 0, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-slider-handle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
sliderthumbOverlays.push(sliderSix);
var sliderSeven = Overlays.addOverlay("image", {
bounds: { x: 0, y: 0, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-slider-handle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
sliderthumbOverlays.push(sliderSeven);
var sliderEight = Overlays.addOverlay("image", {
bounds: { x: 0, y: 0, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-slider-handle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
sliderthumbOverlays.push(sliderEight);
var sliderNine = Overlays.addOverlay("image", {
bounds: { x: 0, y: 0, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-slider-handle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
sliderthumbOverlays.push(sliderNine);
// button overlays
var onButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+20, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-on-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(onButton);
var offButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+20, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-off-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(offButton);
var configWalkButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-edit-walk-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configWalkButton);
var configWalkButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-edit-walk-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configWalkButtonSelected);
var configStandButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-edit-stand-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configStandButton);
var configStandButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-edit-stand-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configStandButtonSelected);
var configFlyingButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-edit-fly-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configFlyingButton);
var configFlyingButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-edit-fly-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configFlyingButtonSelected);
var hideButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-hide-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(hideButton);
var hideButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-hide-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(hideButtonSelected);
var configWalkStylesButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-styles-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configWalkStylesButton);
var configWalkStylesButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-styles-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configWalkStylesButtonSelected);
var configWalkTweaksButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-tweaks-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configWalkTweaksButton);
var configWalkTweaksButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-tweaks-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configWalkTweaksButtonSelected);
var configWalkJointsButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-bones-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configWalkJointsButton);
var configWalkJointsButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-bones-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(configWalkJointsButtonSelected);
var backButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-back-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(backButton);
var backButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-back-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
buttonOverlays.push(backButtonSelected);
// big button overlays - front panel
var bigButtonYOffset = 408; // distance from top of panel to top of first button
var femaleBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-female-big-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(femaleBigButton);
var femaleBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36},
imageURL: pathToOverlays+"ddao-female-big-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(femaleBigButtonSelected);
var maleBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 60, width: 230, height: 36},
imageURL: pathToOverlays+"ddao-male-big-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(maleBigButton);
var maleBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 60, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-male-big-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(maleBigButtonSelected);
var armsFreeBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 120, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-arms-free-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(armsFreeBigButton);
var armsFreeBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 120, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-arms-free-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(armsFreeBigButtonSelected);
var footstepsBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 180, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-footsteps-big-button.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(footstepsBigButton);
var footstepsBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 180, width: 230, height: 36},
imageURL: pathToOverlays+"ddao-footsteps-big-button-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(footstepsBigButtonSelected);
// walk styles
bigButtonYOffset = 121;
var strutWalkBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-strut.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(strutWalkBigButton);
var strutWalkBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-strut-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(strutWalkBigButtonSelected);
bigButtonYOffset += 60
var sexyWalkBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-sexy.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(sexyWalkBigButton);
var sexyWalkBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-sexy-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(sexyWalkBigButtonSelected);
bigButtonYOffset += 60;
var powerWalkBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-power-walk.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(powerWalkBigButton);
var powerWalkBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-power-walk-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(powerWalkBigButtonSelected);
bigButtonYOffset += 60;
var shuffleBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-shuffle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(shuffleBigButton);
var shuffleBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-shuffle-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(shuffleBigButtonSelected);
bigButtonYOffset += 60;
var runBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-run.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(runBigButton);
var runBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-run-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(runBigButtonSelected);
bigButtonYOffset += 60;
var sneakyWalkBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-sneaky.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(sneakyWalkBigButton);
var sneakyWalkBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-sneaky-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(sneakyWalkBigButtonSelected);
bigButtonYOffset += 60;
var toughWalkBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-tough.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(toughWalkBigButton);
var toughWalkBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-tough-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(toughWalkBigButtonSelected);
bigButtonYOffset += 60;
var coolWalkBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-cool.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(coolWalkBigButton);
var coolWalkBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-cool-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(coolWalkBigButtonSelected);
bigButtonYOffset += 60;
var elderlyWalkBigButton = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-elderly.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(elderlyWalkBigButton);
var elderlyWalkBigButtonSelected = Overlays.addOverlay("image", {
bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 1, height: 1},
imageURL: pathToOverlays+"ddao-walk-select-button-elderly-selected.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
bigButtonOverlays.push(elderlyWalkBigButtonSelected);
// overlays to show the walk wheel stats
var walkWheelZLine = Overlays.addOverlay("line3d", {
position: { x: 0, y: 0, z:hipsToFeetDistance },
end: { x: 0, y: 0, z: -hipsToFeetDistance },
color: { red: 0, green: 255, blue: 255},
alpha: 1,
lineWidth: 5,
visible: false,
anchor: "MyAvatar"
});
var walkWheelYLine = Overlays.addOverlay("line3d", {
position: { x: 0, y: hipsToFeetDistance, z:0 },
end: { x: 0, y: -hipsToFeetDistance, z:0 },
color: { red: 255, green: 0, blue: 255},
alpha: 1,
lineWidth: 5,
visible: false,
anchor: "MyAvatar"
});
var debugText = Overlays.addOverlay("text", {
x: Window.innerWidth/2 + 200,
y: Window.innerHeight/2 - 300,
width: 200,
height: 130,
color: { red: 255, green: 255, blue: 255},
textColor: { red: 255, green: 255, blue: 255},
topMargin: 5,
leftMargin: 5,
visible: false,
backgroundColor: { red: 255, green: 255, blue: 255},
text: "Debug area\nNothing to report yet."
});
// various show / hide GUI element functions
function doStandardMenu() {
hidebuttonOverlays();
hideJointControls();
setBackground(controlsBackground);
if(powerOn) setButtonOverlayVisible(onButton);
else setButtonOverlayVisible(offButton);
setButtonOverlayVisible(configWalkButton);
setButtonOverlayVisible(configStandButton);
setButtonOverlayVisible(configFlyingButton);
setButtonOverlayVisible(hideButton);
setSliderthumbsVisible(false);
showFrontPanelButtons(true);
showWalkStyleButtons(false);
}
function showFrontPanelButtons(showButtons) {
var bigButtonWidth = 1;
var bigButtonHeight = 1;
if(showButtons) {
var bigButtonWidth = 230;
var bigButtonHeight = 36;
}
if(avatarGender===FEMALE) {
Overlays.editOverlay(femaleBigButtonSelected, { width: bigButtonWidth, height: bigButtonHeight } );
Overlays.editOverlay(femaleBigButton, { width: 1, height: 1 } );
Overlays.editOverlay(maleBigButtonSelected, { width: 1, height: 1 } );
Overlays.editOverlay(maleBigButton, { width: bigButtonWidth, height: bigButtonHeight } );
} else {
Overlays.editOverlay(femaleBigButtonSelected, { width: 1, height: 1 } );
Overlays.editOverlay(femaleBigButton, { width: bigButtonWidth, height: bigButtonHeight } );
Overlays.editOverlay(maleBigButtonSelected, { width: bigButtonWidth, height: bigButtonHeight } );
Overlays.editOverlay(maleBigButton, { width: 1, height: 1 } );
}
if(armsFree) {
Overlays.editOverlay(armsFreeBigButtonSelected, { width: bigButtonWidth, height: bigButtonHeight } );
Overlays.editOverlay(armsFreeBigButton, { width: 1, height: 1 } );
} else {
Overlays.editOverlay(armsFreeBigButtonSelected, { width: 1, height: 1 } );
Overlays.editOverlay(armsFreeBigButton, { width: bigButtonWidth, height: bigButtonHeight } );
}
if(playFootStepSounds) {
Overlays.editOverlay(footstepsBigButtonSelected, { width: bigButtonWidth, height: bigButtonHeight } );
Overlays.editOverlay(footstepsBigButton, { width: 1, height: 1 } );
} else {
Overlays.editOverlay(footstepsBigButtonSelected, { width: 1, height: 1 } );
Overlays.editOverlay(footstepsBigButton, { width: bigButtonWidth, height: bigButtonHeight } );
}
}
function minimiseDialog() {
if(minimised) {
setBackground();
hidebuttonOverlays();
setSliderthumbsVisible(false);
hideJointControls();
showFrontPanelButtons(false);
Overlays.editOverlay(controlsMinimisedTab, { width: 36, height: 351 } );
} else {
setInternalState(STANDING); // show all the controls again
Overlays.editOverlay(controlsMinimisedTab, { width: 1, height: 1 } );
}
}
function setBackground(backgroundName) {
for(var i in backgroundOverlays) {
if(backgroundOverlays[i] === backgroundName)
Overlays.editOverlay(backgroundName, {width: backgroundWidth, height: backgroundHeight } );
else Overlays.editOverlay(backgroundOverlays[i], { width: 1, height: 1} );
}
}
function setButtonOverlayVisible(buttonOverlayName) {
for(var i in buttonOverlays) {
if(buttonOverlays[i] === buttonOverlayName) {
Overlays.editOverlay(buttonOverlayName, { width: 60, height: 47 } );
}
}
}
// top row menu type buttons (smaller)
function hidebuttonOverlays() {
for(var i in buttonOverlays) {
Overlays.editOverlay(buttonOverlays[i], { width: 1, height: 1 } );
}
}
function hideJointControls() {
for(var i in jointsControlOverlays) {
Overlays.editOverlay(jointsControlOverlays[i], { width: 1, height: 1 } );
}
}
function setSliderthumbsVisible(visible) {
var sliderThumbSize = 0;
if(visible) sliderThumbSize = 25;
for(var i = 0 ; i < sliderthumbOverlays.length ; i++) {
Overlays.editOverlay(sliderthumbOverlays[i], { width: sliderThumbSize, height: sliderThumbSize} );
}
}
function initialiseWalkJointsPanel(propertyIndex) {
selectedJointIndex = propertyIndex;
// set the image for the selected joint on the character control
hideJointControls();
switch (selectedJointIndex) {
case 0:
Overlays.editOverlay(hipsJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 1:
Overlays.editOverlay(upperLegsJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 2:
Overlays.editOverlay(lowerLegsJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 3:
Overlays.editOverlay(feetJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 4:
Overlays.editOverlay(toesJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 5:
Overlays.editOverlay(spineJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 6:
Overlays.editOverlay(spine1JointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 7:
Overlays.editOverlay(spine2JointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 8:
Overlays.editOverlay(shouldersJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 9:
Overlays.editOverlay(upperArmsJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 10:
Overlays.editOverlay(forearmsJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 11:
Overlays.editOverlay(handsJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
case 12:
Overlays.editOverlay(headJointControl, { bounds: { x: backgroundX+75, y: backgroundY+92, width: 200, height: 300}} );
break;
}
// set sliders to adjust individual joint properties
var i = 0;
var yLocation = backgroundY+359;
// pitch your role
var sliderXPos = currentAnimation.joints[selectedJointIndex].pitch / sliderRanges.joints[selectedJointIndex].pitchRange * sliderRangeX;
Overlays.editOverlay(sliderthumbOverlays[i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=30, width: 25, height: 25}} );
sliderXPos = currentAnimation.joints[selectedJointIndex].yaw / sliderRanges.joints[selectedJointIndex].yawRange * sliderRangeX;
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=30, width: 25, height: 25}} );
sliderXPos = currentAnimation.joints[selectedJointIndex].roll / sliderRanges.joints[selectedJointIndex].rollRange * sliderRangeX;
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=30, width: 25, height: 25}} );
// set phases (full range, -180 to 180)
sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].pitchPhase/2)/180 * sliderRangeX;
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=30, width: 25, height: 25}} );
sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].yawPhase/2)/180 * sliderRangeX;
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=30, width: 25, height: 25}} );
sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].rollPhase/2)/180 * sliderRangeX;
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=30, width: 25, height: 25}} );
// offset ranges are also -ve thr' zero to +ve, so have to offset
sliderXPos = (((sliderRanges.joints[selectedJointIndex].pitchOffsetRange+currentAnimation.joints[selectedJointIndex].pitchOffset)/2)/sliderRanges.joints[selectedJointIndex].pitchOffsetRange) * sliderRangeX;
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=30, width: 25, height: 25}} );
sliderXPos = (((sliderRanges.joints[selectedJointIndex].yawOffsetRange+currentAnimation.joints[selectedJointIndex].yawOffset)/2)/sliderRanges.joints[selectedJointIndex].yawOffsetRange) * sliderRangeX;
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=30, width: 25, height: 25}} );
sliderXPos = (((sliderRanges.joints[selectedJointIndex].rollOffsetRange+currentAnimation.joints[selectedJointIndex].rollOffset)/2)/sliderRanges.joints[selectedJointIndex].rollOffsetRange) * sliderRangeX;
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=30, width: 25, height: 25}} );
}
function initialiseWalkTweaks() {
// set sliders to adjust walk properties
var i = 0;
var yLocation = backgroundY+71;
var sliderXPos = currentAnimation.settings.baseFrequency / MAX_WALK_SPEED * sliderRangeX; // walk speed
Overlays.editOverlay(sliderthumbOverlays[i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=60, width: 25, height: 25}} );
sliderXPos = currentAnimation.settings.takeFlightVelocity / 300 * sliderRangeX; // start flying speed
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=60, width: 25, height: 25}} );
sliderXPos = currentAnimation.joints[0].sway / sliderRanges.joints[0].swayRange * sliderRangeX; // Hips sway
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=60, width: 25, height: 25}} );
sliderXPos = currentAnimation.joints[0].bob / sliderRanges.joints[0].bobRange * sliderRangeX; // Hips bob
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=60, width: 25, height: 25}} );
sliderXPos = currentAnimation.joints[0].thrust / sliderRanges.joints[0].thrustRange * sliderRangeX; // Hips thrust
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=60, width: 25, height: 25}} );
sliderXPos = (0.5+(currentAnimation.adjusters.legsSeparation.strength/2)) * sliderRangeX; // legs separation
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=60, width: 25, height: 25}} );
sliderXPos = currentAnimation.adjusters.stride.strength * sliderRangeX; // stride
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=60, width: 25, height: 25}} );
sliderXPos = currentAnimation.joints[9].yaw / sliderRanges.joints[9].yawRange * sliderRangeX; // arms swing - is just upper arms yaw
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=60, width: 25, height: 25}} );
sliderXPos = (((sliderRanges.joints[9].pitchOffsetRange-currentAnimation.joints[9].pitchOffset)/2)/sliderRanges.joints[9].pitchOffsetRange) * sliderRangeX; // arms out - is just upper arms pitch offset
Overlays.editOverlay(sliderthumbOverlays[++i], { bounds: { x: minSliderX + sliderXPos, y: yLocation+=60, width: 25, height: 25}} );
}
function showWalkStyleButtons(showButtons) {
var bigButtonWidth = 230;
var bigButtonHeight = 36;
if(!showButtons) {
bigButtonWidth = 1;
bigButtonHeight = 1;
}
// set all big buttons to hidden, but skip the first 8, as are for the front panel
for(var i = 8 ; i < bigButtonOverlays.length ; i++) {
Overlays.editOverlay(bigButtonOverlays[i], {width: 1, height: 1});
}
if(!showButtons) return;
// set all the non-selected ones to showing
for(var i = 8 ; i < bigButtonOverlays.length ; i+=2) {
Overlays.editOverlay(bigButtonOverlays[i], {width: bigButtonWidth, height: bigButtonHeight});
}
// set the currently selected one
if(selectedWalk === femaleSexyWalk || selectedWalk === maleSexyWalk) {
Overlays.editOverlay(sexyWalkBigButtonSelected, {width: bigButtonWidth, height: bigButtonHeight});
Overlays.editOverlay(sexyWalkBigButton, {width: 1, height: 1});
}
else if(selectedWalk === femaleStrutWalk || selectedWalk === maleStrutWalk) {
Overlays.editOverlay(strutWalkBigButtonSelected, {width: bigButtonWidth, height: bigButtonHeight});
Overlays.editOverlay(strutWalkBigButton, {width: 1, height: 1});
}
else if(selectedWalk === femalePowerWalk || selectedWalk === malePowerWalk) {
Overlays.editOverlay(powerWalkBigButtonSelected, {width: bigButtonWidth, height: bigButtonHeight});
Overlays.editOverlay(powerWalkBigButton, {width: 1, height: 1});
}
else if(selectedWalk === femaleShuffle || selectedWalk === maleShuffle) {
Overlays.editOverlay(shuffleBigButtonSelected, {width: bigButtonWidth, height: bigButtonHeight});
Overlays.editOverlay(shuffleBigButton, {width: 1, height: 1});
}
else if(selectedWalk === femaleRun || selectedWalk === maleRun) {
Overlays.editOverlay(runBigButtonSelected, {width: bigButtonWidth, height: bigButtonHeight});
Overlays.editOverlay(runBigButton, {width: 1, height: 1});
}
else if(selectedWalk === femaleRandomWalk || selectedWalk === maleRandomWalk) {
Overlays.editOverlay(sneakyWalkBigButtonSelected, {width: bigButtonWidth, height: bigButtonHeight});
Overlays.editOverlay(sneakyWalkBigButton, {width: 1, height: 1});
}
else if(selectedWalk === femaleToughWalk || selectedWalk === maleToughWalk) {
Overlays.editOverlay(toughWalkBigButtonSelected, {width: bigButtonWidth, height: bigButtonHeight});
Overlays.editOverlay(toughWalkBigButton, {width: 1, height: 1});
}
else if(selectedWalk === femaleCoolWalk || selectedWalk === maleCoolWalk) {
Overlays.editOverlay(coolWalkBigButtonSelected, {width: bigButtonWidth, height: bigButtonHeight});
Overlays.editOverlay(coolWalkBigButton, {width: 1, height: 1});
}
else if(selectedWalk === femaleElderlyWalk || selectedWalk === maleElderlyWalk) {
Overlays.editOverlay(elderlyWalkBigButtonSelected, {width: bigButtonWidth, height: bigButtonHeight});
Overlays.editOverlay(elderlyWalkBigButton, {width: 1, height: 1});
}
}
// mouse event handlers
var movingSliderOne = false;
var movingSliderTwo = false;
var movingSliderThree = false;
var movingSliderFour = false;
var movingSliderFive = false;
var movingSliderSix = false;
var movingSliderSeven = false;
var movingSliderEight = false;
var movingSliderNine = false;
function mousePressEvent(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
// check for a character joint control click
switch (clickedOverlay) {
case hideButton:
Overlays.editOverlay(hideButton, { width: 1, height: 1 } );
Overlays.editOverlay(hideButtonSelected, { width: 60, height: 47 } );
return;
case backButton:
Overlays.editOverlay(backButton, { width: 1, height: 1 } );
Overlays.editOverlay(backButtonSelected, { width: 60, height: 47 } );
return;
case controlsMinimisedTab:
// TODO: add visual user feedback for tab click
return;
case footstepsBigButton:
playFootStepSounds = true;
Overlays.editOverlay(footstepsBigButtonSelected, { width: 230, height: 36 } );
Overlays.editOverlay(footstepsBigButton, { width: 1, height: 1 } );
return;
case footstepsBigButtonSelected:
playFootStepSounds = false;
Overlays.editOverlay(footstepsBigButton, { width: 230, height: 36 } );
Overlays.editOverlay(footstepsBigButtonSelected, { width: 1, height: 1 } );
return;
case femaleBigButton:
case maleBigButtonSelected:
avatarGender = FEMALE;
selectedWalk = femaleStrutWalk;
selectedStand = femaleStandOne;
selectedFlyUp = femaleFlyingUp;
selectedFly = femaleFlying;
selectedFlyDown = femaleFlyingDown;
Overlays.editOverlay(femaleBigButtonSelected, { width: 230, height: 36 } );
Overlays.editOverlay(femaleBigButton, { width: 1, height: 1 } );
Overlays.editOverlay(maleBigButton, { width: 230, height: 36 } );
Overlays.editOverlay(maleBigButtonSelected, { width: 1, height: 1 } );
return;
case armsFreeBigButton:
armsFree = true;
Overlays.editOverlay(armsFreeBigButtonSelected, { width: 230, height: 36 } );
Overlays.editOverlay(armsFreeBigButton, { width: 1, height: 1 } );
return;
case armsFreeBigButtonSelected:
armsFree = false;
Overlays.editOverlay(armsFreeBigButtonSelected, { width: 1, height: 1 } );
Overlays.editOverlay(armsFreeBigButton, { width: 230, height: 36 } );
return;
case maleBigButton:
case femaleBigButtonSelected:
avatarGender = MALE;
selectedWalk = maleStrutWalk;
selectedStand = maleStandOne;
selectedFlyUp = maleFlyingUp;
selectedFly = maleFlying;
selectedFlyDown = maleFlyingDown;
Overlays.editOverlay(femaleBigButton, { width: 230, height: 36 } );
Overlays.editOverlay(femaleBigButtonSelected, { width: 1, height: 1 } );
Overlays.editOverlay(maleBigButtonSelected, { width: 230, height: 36 } );
Overlays.editOverlay(maleBigButton, { width: 1, height: 1 } );
return;
case coolWalkBigButton:
if(avatarGender===FEMALE) selectedWalk = femaleCoolWalk;
else selectedWalk = maleCoolWalk;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case coolWalkBigButton:
if(avatarGender===FEMALE) selectedWalk = femaleCoolWalk;
else selectedWalk = maleCoolWalk;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case elderlyWalkBigButton:
if(avatarGender===FEMALE) selectedWalk = femaleElderlyWalk;
else selectedWalk = maleElderlyWalk;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case powerWalkBigButton:
if(avatarGender===FEMALE) selectedWalk = femalePowerWalk;
else selectedWalk = malePowerWalk;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case runBigButton:
if(avatarGender===FEMALE) selectedWalk = femaleRun;
else selectedWalk = maleRun;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case sexyWalkBigButton:
if(avatarGender===FEMALE) selectedWalk = femaleSexyWalk;
else selectedWalk = maleSexyWalk;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case shuffleBigButton:
if(avatarGender===FEMALE) selectedWalk = femaleShuffle;
else selectedWalk = maleShuffle;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case sneakyWalkBigButton:
if(avatarGender===FEMALE) selectedWalk = femaleRandomWalk;
else selectedWalk = maleRandomWalk;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case strutWalkBigButton:
if(avatarGender===FEMALE) selectedWalk = femaleStrutWalk;
else selectedWalk = maleStrutWalk;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case toughWalkBigButton:
if(avatarGender===FEMALE) selectedWalk = femaleToughWalk;
else selectedWalk = maleToughWalk;
currentAnimation = selectedWalk;
showWalkStyleButtons(true);
maxFootForward = 0;
maxFootBackwards = 0;
break;
case sliderOne:
movingSliderOne = true;
return;
case sliderTwo:
movingSliderTwo = true;
return;
case sliderThree:
movingSliderThree = true;
return;
case sliderFour:
movingSliderFour = true;
return;
case sliderFive:
movingSliderFive = true;
return;
case sliderSix:
movingSliderSix = true;
return;
case sliderSeven:
movingSliderSeven = true;
return;
case sliderEight:
movingSliderEight = true;
return;
case sliderNine:
movingSliderNine = true;
return;
}
if(INTERNAL_STATE===CONFIG_WALK_JOINTS ||
INTERNAL_STATE===CONFIG_STANDING ||
INTERNAL_STATE===CONFIG_FLYING) {
// check for new joint selection and update display accordingly
var clickX = event.x - backgroundX - 75;
var clickY = event.y - backgroundY - 92;
if(clickX>60&&clickX<120&&clickY>123&&clickY<155) {
initialiseWalkJointsPanel(0);
return;
}
else if(clickX>63&&clickX<132&&clickY>156&&clickY<202) {
initialiseWalkJointsPanel(1);
return;
}
else if(clickX>58&&clickX<137&&clickY>203&&clickY<250) {
initialiseWalkJointsPanel(2);
return;
}
else if(clickX>58&&clickX<137&&clickY>250&&clickY<265) {
initialiseWalkJointsPanel(3);
return;
}
else if(clickX>58&&clickX<137&&clickY>265&&clickY<280) {
initialiseWalkJointsPanel(4);
return;
}
else if(clickX>78&&clickX<121&&clickY>111&&clickY<128) {
initialiseWalkJointsPanel(5);
return;
}
else if(clickX>78&&clickX<128&&clickY>89&&clickY<111) {
initialiseWalkJointsPanel(6);
return;
}
else if(clickX>85&&clickX<118&&clickY>77&&clickY<94) {
initialiseWalkJointsPanel(7);
return;
}
else if(clickX>64&&clickX<125&&clickY>55&&clickY<77) {
initialiseWalkJointsPanel(8);
return;
}
else if((clickX>44&&clickX<73&&clickY>71&&clickY<94)
||(clickX>125&&clickX<144&&clickY>71&&clickY<94)) {
initialiseWalkJointsPanel(9);
return;
}
else if((clickX>28&&clickX<57&&clickY>94&&clickY<119)
||(clickX>137&&clickX<170&&clickY>97&&clickY<114)) {
initialiseWalkJointsPanel(10);
return;
}
else if((clickX>18&&clickX<37&&clickY>115&&clickY<136)
||(clickX>157&&clickX<182&&clickY>115&&clickY<136)) {
initialiseWalkJointsPanel(11);
return;
}
else if(clickX>81&&clickX<116&&clickY>12&&clickY<53) {
initialiseWalkJointsPanel(12);
return;
}
}
}
function mouseMoveEvent(event) {
// only need deal with slider changes
if(powerOn) {
if(INTERNAL_STATE===CONFIG_WALK_JOINTS ||
INTERNAL_STATE===CONFIG_STANDING ||
INTERNAL_STATE===CONFIG_FLYING) {
var thumbClickOffsetX = event.x - minSliderX;
var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
if(thumbPositionNormalised<0) thumbPositionNormalised = 0;
if(thumbPositionNormalised>1) thumbPositionNormalised = 1;
var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range
if(movingSliderOne) { // currently selected joint pitch
Overlays.editOverlay(sliderOne, { x: sliderX + minSliderX} );
currentAnimation.joints[selectedJointIndex].pitch = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].pitchRange;
}
else if(movingSliderTwo) { // currently selected joint yaw
Overlays.editOverlay(sliderTwo, { x: sliderX + minSliderX} );
currentAnimation.joints[selectedJointIndex].yaw = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].yawRange;
}
else if(movingSliderThree) { // currently selected joint roll
Overlays.editOverlay(sliderThree, { x: sliderX + minSliderX} );
currentAnimation.joints[selectedJointIndex].roll = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].rollRange;
}
else if(movingSliderFour) { // currently selected joint pitch phase
Overlays.editOverlay(sliderFour, { x: sliderX + minSliderX} );
var newPhase = 360 * thumbPositionNormalised - 180;
currentAnimation.joints[selectedJointIndex].pitchPhase = newPhase;
}
else if(movingSliderFive) { // currently selected joint yaw phase;
Overlays.editOverlay(sliderFive, { x: sliderX + minSliderX} );
var newPhase = 360 * thumbPositionNormalised - 180;
currentAnimation.joints[selectedJointIndex].yawPhase = newPhase;
}
else if(movingSliderSix) { // currently selected joint roll phase
Overlays.editOverlay(sliderSix, { x: sliderX + minSliderX} );
var newPhase = 360 * thumbPositionNormalised - 180;
currentAnimation.joints[selectedJointIndex].rollPhase = newPhase;
}
else if(movingSliderSeven) { // currently selected joint pitch offset
Overlays.editOverlay(sliderSeven, { x: sliderX + minSliderX} ); // currently selected joint pitch offset
var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].pitchOffsetRange;
currentAnimation.joints[selectedJointIndex].pitchOffset = newOffset;
}
else if(movingSliderEight) { // currently selected joint yaw offset
Overlays.editOverlay(sliderEight, { x: sliderX + minSliderX} ); // currently selected joint yaw offset
var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].yawOffsetRange;
currentAnimation.joints[selectedJointIndex].yawOffset = newOffset;
}
else if(movingSliderNine) { // currently selected joint roll offset
Overlays.editOverlay(sliderNine, { x: sliderX + minSliderX} ); // currently selected joint roll offset
var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].rollOffsetRange;
currentAnimation.joints[selectedJointIndex].rollOffset = newOffset;
}
}
else if(INTERNAL_STATE===CONFIG_WALK_TWEAKS) {
var thumbClickOffsetX = event.x - minSliderX;
var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
if(thumbPositionNormalised<0) thumbPositionNormalised = 0;
if(thumbPositionNormalised>1) thumbPositionNormalised = 1;
var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range
if(movingSliderOne) { // walk speed
paused = true; // avoid nasty jittering
Overlays.editOverlay(sliderOne, { x: sliderX + minSliderX} );
currentAnimation.settings.baseFrequency = thumbPositionNormalised * MAX_WALK_SPEED;
}
else if(movingSliderTwo) { // take flight speed
Overlays.editOverlay(sliderTwo, { x: sliderX + minSliderX} );
currentAnimation.settings.takeFlightVelocity = thumbPositionNormalised * 300;
}
else if(movingSliderThree) { // hips sway
Overlays.editOverlay(sliderThree, { x: sliderX + minSliderX} );
currentAnimation.joints[0].sway = thumbPositionNormalised * sliderRanges.joints[0].swayRange;
}
else if(movingSliderFour) { // hips bob
Overlays.editOverlay(sliderFour, { x: sliderX + minSliderX} );
currentAnimation.joints[0].bob = thumbPositionNormalised * sliderRanges.joints[0].bobRange;
}
else if(movingSliderFive) { // hips thrust
Overlays.editOverlay(sliderFive, { x: sliderX + minSliderX} );
currentAnimation.joints[0].thrust = thumbPositionNormalised * sliderRanges.joints[0].thrustRange;
}
else if(movingSliderSix) { // legs separation
Overlays.editOverlay(sliderSix, { x: sliderX + minSliderX} );
currentAnimation.adjusters.legsSeparation.strength = (thumbPositionNormalised-0.5)/2;
}
else if(movingSliderSeven) { // stride
Overlays.editOverlay(sliderSeven, { x: sliderX + minSliderX} );
currentAnimation.adjusters.stride.strength = thumbPositionNormalised;
}
else if(movingSliderEight) { // arms swing = upper arms yaw
Overlays.editOverlay(sliderEight, { x: sliderX + minSliderX} );
currentAnimation.joints[9].yaw = thumbPositionNormalised * sliderRanges.joints[9].yawRange;
}
else if(movingSliderNine) { // arms out = upper arms pitch offset
Overlays.editOverlay(sliderNine, { x: sliderX + minSliderX} );
currentAnimation.joints[9].pitchOffset = (thumbPositionNormalised-0.5) * -2 * sliderRanges.joints[9].pitchOffsetRange;
}
}
}
}
function mouseReleaseEvent(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
if(paused) paused = false;
if(clickedOverlay === offButton) {
powerOn = true;
Overlays.editOverlay(offButton, {width: 0, height: 0} );
Overlays.editOverlay(onButton, {width: 60, height: 47 } );
stand();
}
else if(clickedOverlay === hideButton || clickedOverlay === hideButtonSelected){
Overlays.editOverlay(hideButton, { width: 60, height: 47 } );
Overlays.editOverlay(hideButtonSelected, { width: 1, height: 1 } );
minimised = true;
minimiseDialog();
}
else if(clickedOverlay === controlsMinimisedTab) {
minimised = false;
minimiseDialog();
}
else if(powerOn) {
if(movingSliderOne) movingSliderOne = false;
else if(movingSliderTwo) movingSliderTwo = false;
else if(movingSliderThree) movingSliderThree = false;
else if(movingSliderFour) movingSliderFour = false;
else if(movingSliderFive) movingSliderFive = false;
else if(movingSliderSix) movingSliderSix = false;
else if(movingSliderSeven) movingSliderSeven = false;
else if(movingSliderEight) movingSliderEight = false;
else if(movingSliderNine) movingSliderNine = false;
else {
switch(clickedOverlay) {
case configWalkButtonSelected:
case configStandButtonSelected:
case configFlyingButtonSelected:
case configWalkStylesButtonSelected:
case configWalkTweaksButtonSelected:
case configWalkJointsButtonSelected:
setInternalState(STANDING);
break;
case onButton:
powerOn = false;
setInternalState(STANDING);
Overlays.editOverlay(offButton, {width: 60, height: 47 } );
Overlays.editOverlay(onButton, {width: 0, height: 0} );
resetJoints();
break;
case backButton:
case backButtonSelected:
Overlays.editOverlay(backButton, { width: 1, height: 1 } );
Overlays.editOverlay(backButtonSelected, { width: 1, height: 1 } );
setInternalState(STANDING);
break;
case configWalkStylesButton:
setInternalState(CONFIG_WALK_STYLES);
break;
case configWalkTweaksButton:
setInternalState(CONFIG_WALK_TWEAKS);
break;
case configWalkJointsButton:
setInternalState(CONFIG_WALK_JOINTS);
break;
case configWalkButton:
setInternalState(CONFIG_WALK_STYLES); // set the default walk adjustment panel here
break;
case configStandButton:
setInternalState(CONFIG_STANDING);
break;
case configFlyingButton:
setInternalState(CONFIG_FLYING);
break;
}
}
}
}
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
// Script ending
Script.scriptEnding.connect(function() {
// remove the background overlays
for(var i in backgroundOverlays) {
Overlays.deleteOverlay(backgroundOverlays[i]);
}
// remove the button overlays
for(var i in buttonOverlays) {
Overlays.deleteOverlay(buttonOverlays[i]);
}
// remove the slider thumb overlays
for(var i in sliderthumbOverlays) {
Overlays.deleteOverlay(sliderthumbOverlays[i]);
}
// remove the character joint control overlays
for(var i in bigButtonOverlays) {
Overlays.deleteOverlay(jointsControlOverlays[i]);
}
// remove the big button overlays
for(var i in bigButtonOverlays) {
Overlays.deleteOverlay(bigButtonOverlays[i]);
}
// remove the mimimised tab
Overlays.deleteOverlay(controlsMinimisedTab);
// remove the walk wheel overlays
Overlays.deleteOverlay(walkWheelYLine);
Overlays.deleteOverlay(walkWheelZLine);
Overlays.deleteOverlay(debugText);
});
function keyPressEvent(event) {
//print('keyPressEvent: '+event.text);
if (event.text == "q") {
// export currentAnimation as json string when q key is pressed.
// reformat result at http://www.freeformatter.com/json-formatter.html
print('\n');
print('walk.js dumping animation: '+currentAnimation.name+'\n');
print('\n');
print(JSON.stringify(currentAnimation), null, '\t');
}
if (event.text == "t") {
statsOn = !statsOn;
if(statsOn) {
print('wheel stats on (t to turn off again)');
Overlays.editOverlay(debugText, {visible: true});
Overlays.editOverlay(walkWheelYLine, {visible: true});
Overlays.editOverlay(walkWheelZLine, {visible: true});
} else {
print('wheel stats off (t to turn on again)');
Overlays.editOverlay(debugText, {visible: false});
Overlays.editOverlay(walkWheelYLine, {visible: false});
Overlays.editOverlay(walkWheelZLine, {visible: false});
}
}
}
Controller.keyPressEvent.connect(keyPressEvent);
// debug and other info
var VERBOSE = false;
// TODO: implement joint mapping using reg expressions to cover a wide range of avi bone structures
var jointList = MyAvatar.getJointNames();
var jointMappings = "\n# Avatar joint list start";
for (var i = 0; i < jointList.length; i++) {
jointMappings = jointMappings + "\njointIndex = " + jointList[i] + " = " + i;
}
print(jointMappings + "\n# walk.js avatar joint list end");
// clear the joint data so can calculate hips to feet distance
for(var i = 0 ; i < 5 ; i++) {
//MyAvatar.setJointData(i, Quat.fromPitchYawRollDegrees(0,0,0));
MyAvatar.clearJointData(jointList[i]);
}
var hipsToFeetDistance = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightFoot").y;
print('\nwalk.js: Hips to feet: '+hipsToFeetDistance);
////////////////////////////////////////////
// begin by setting the to state STANDING //
////////////////////////////////////////////
//curlFingers();
setInternalState(STANDING);