From 552427061303986c4df53a5282e1d3e3cd96f8b3 Mon Sep 17 00:00:00 2001 From: DaveDubUK Date: Wed, 10 Sep 2014 23:57:10 +0700 Subject: [PATCH] walk.js script for https://worklist.net/19970 walk.js script for https://worklist.net/19970 --- examples/walk.js | 2540 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2540 insertions(+) create mode 100644 examples/walk.js diff --git a/examples/walk.js b/examples/walk.js new file mode 100644 index 0000000000..de349a95b8 --- /dev/null +++ b/examples/walk.js @@ -0,0 +1,2540 @@ +// +// 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)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é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 = 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(velocityMath.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); \ No newline at end of file