diff --git a/examples/bot_procedural.js. b/examples/bot_procedural.js. deleted file mode 100644 index 265b887e0a..0000000000 --- a/examples/bot_procedural.js. +++ /dev/null @@ -1,674 +0,0 @@ -// -// bot_procedural.js -// hifi -// -// Created by Ben Arnold on 7/29/2013 -// -// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. -// -// This is an example script that demonstrates an NPC avatar. -// -// - -//For procedural walk animation -Script.include("http://s3-us-west-1.amazonaws.com/highfidelity-public/scripts/proceduralAnimationAPI.js"); - -var procAnimAPI = new ProcAnimAPI(); - -function getRandomFloat(min, max) { - return Math.random() * (max - min) + min; -} - -function getRandomInt (min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} - -function printVector(string, vector) { - print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); -} - -var CHANCE_OF_MOVING = 0.005; -var CHANCE_OF_SOUND = 0.005; -var CHANCE_OF_HEAD_TURNING = 0.01; -var CHANCE_OF_BIG_MOVE = 1.0; - -var isMoving = false; -var isTurningHead = false; -var isPlayingAudio = false; - -var X_MIN = 0.50; -var X_MAX = 15.60; -var Z_MIN = 0.50; -var Z_MAX = 15.10; -var Y_FEET = 0.0; -var AVATAR_PELVIS_HEIGHT = 0.84; -var Y_PELVIS = Y_FEET + AVATAR_PELVIS_HEIGHT; -var MAX_PELVIS_DELTA = 2.5; - -var MOVE_RANGE_SMALL = 3.0; -var MOVE_RANGE_BIG = 10.0; -var TURN_RANGE = 70.0; -var STOP_TOLERANCE = 0.05; -var MOVE_RATE = 0.05; -var TURN_RATE = 0.2; -var HEAD_TURN_RATE = 0.05; -var PITCH_RANGE = 15.0; -var YAW_RANGE = 35.0; - -var firstPosition = { x: getRandomFloat(X_MIN, X_MAX), y: Y_PELVIS, z: getRandomFloat(Z_MIN, Z_MAX) }; -var targetPosition = { x: 0, y: 0, z: 0 }; -var targetOrientation = { x: 0, y: 0, z: 0, w: 0 }; -var currentOrientation = { x: 0, y: 0, z: 0, w: 0 }; -var targetHeadPitch = 0.0; -var targetHeadYaw = 0.0; - -var basePelvisHeight = 0.0; -var pelvisOscillatorPosition = 0.0; -var pelvisOscillatorVelocity = 0.0; - -function clamp(val, min, max){ - return Math.max(min, Math.min(max, val)) -} - -//Array of all valid bot numbers -var validBotNumbers = []; - -// right now we only use bot 63, since many other bots have messed up skeletons and LOD issues -var botNumber = 63;//getRandomInt(0, 99); - -var newFaceFilePrefix = "ron"; - -var newBodyFilePrefix = "bot" + botNumber; - -// set the face model fst using the bot number -// there is no need to change the body model - we're using the default -Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix + ".fst"; -Avatar.skeletonModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + "_a.fst"; -Avatar.billboardURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/billboards/bot" + botNumber + ".png"; - -Agent.isAvatar = true; -Agent.isListeningToAudioStream = true; - -// change the avatar's position to the random one -Avatar.position = firstPosition; -basePelvisHeight = firstPosition.y; -printVector("New dancer, position = ", Avatar.position); - -function loadSounds() { - var sound_filenames = ["AB1.raw", "Anchorman2.raw", "B1.raw", "B1.raw", "Bale1.raw", "Bandcamp.raw", - "Big1.raw", "Big2.raw", "Brian1.raw", "Buster1.raw", "CES1.raw", "CES2.raw", "CES3.raw", "CES4.raw", - "Carrie1.raw", "Carrie3.raw", "Charlotte1.raw", "EN1.raw", "EN2.raw", "EN3.raw", "Eugene1.raw", "Francesco1.raw", - "Italian1.raw", "Japanese1.raw", "Leigh1.raw", "Lucille1.raw", "Lucille2.raw", "MeanGirls.raw", "Murray2.raw", - "Nigel1.raw", "PennyLane.raw", "Pitt1.raw", "Ricardo.raw", "SN.raw", "Sake1.raw", "Samantha1.raw", "Samantha2.raw", - "Spicoli1.raw", "Supernatural.raw", "Swearengen1.raw", "TheDude.raw", "Tony.raw", "Triumph1.raw", "Uma1.raw", - "Walken1.raw", "Walken2.raw", "Z1.raw", "Z2.raw" - ]; - - var footstep_filenames = ["FootstepW2Left-12db.wav", "FootstepW2Right-12db.wav", "FootstepW3Left-12db.wav", "FootstepW3Right-12db.wav", - "FootstepW5Left-12db.wav", "FootstepW5Right-12db.wav"]; - - var SOUND_BASE_URL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/"; - - var FOOTSTEP_BASE_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Footsteps/"; - - for (var i = 0; i < sound_filenames.length; i++) { - sounds.push(new Sound(SOUND_BASE_URL + sound_filenames[i])); - } - - for (var i = 0; i < footstep_filenames.length; i++) { - footstepSounds.push(new Sound(FOOTSTEP_BASE_URL + footstep_filenames[i])); - } -} - -var sounds = []; -var footstepSounds = []; -loadSounds(); - - -function playRandomSound() { - if (!Agent.isPlayingAvatarSound) { - var whichSound = Math.floor((Math.random() * sounds.length)); - Agent.playAvatarSound(sounds[whichSound]); - } -} - -function playRandomFootstepSound() { - - var whichSound = Math.floor((Math.random() * footstepSounds.length)); - var options = new AudioInjectionOptions(); - options.position = Avatar.position; - options.volume = 1.0; - Audio.playSound(footstepSounds[whichSound], options); - -} - -// ************************************ Facial Animation ********************************** -var allBlendShapes = []; -var targetBlendCoefficient = []; -var currentBlendCoefficient = []; - -//Blendshape constructor -function addBlendshapeToPose(pose, shapeIndex, val) { - var index = pose.blendShapes.length; - pose.blendShapes[index] = {shapeIndex: shapeIndex, val: val }; -} -//The mood of the avatar, determines face. 0 = happy, 1 = angry, 2 = sad. - -//Randomly pick avatar mood. 80% happy, 10% mad 10% sad -var randMood = Math.floor(Math.random() * 11); -var avatarMood; -if (randMood == 0) { - avatarMood = 1; -} else if (randMood == 2) { - avatarMood = 2; -} else { - avatarMood = 0; -} - -var currentExpression = -1; -//Face pose constructor -var happyPoses = []; - -happyPoses[0] = {blendShapes: []}; -addBlendshapeToPose(happyPoses[0], 28, 0.7); //MouthSmile_L -addBlendshapeToPose(happyPoses[0], 29, 0.7); //MouthSmile_R - -happyPoses[1] = {blendShapes: []}; -addBlendshapeToPose(happyPoses[1], 28, 1.0); //MouthSmile_L -addBlendshapeToPose(happyPoses[1], 29, 1.0); //MouthSmile_R -addBlendshapeToPose(happyPoses[1], 21, 0.2); //JawOpen - -happyPoses[2] = {blendShapes: []}; -addBlendshapeToPose(happyPoses[2], 28, 1.0); //MouthSmile_L -addBlendshapeToPose(happyPoses[2], 29, 1.0); //MouthSmile_R -addBlendshapeToPose(happyPoses[2], 21, 0.5); //JawOpen -addBlendshapeToPose(happyPoses[2], 46, 1.0); //CheekSquint_L -addBlendshapeToPose(happyPoses[2], 47, 1.0); //CheekSquint_R -addBlendshapeToPose(happyPoses[2], 17, 1.0); //BrowsU_L -addBlendshapeToPose(happyPoses[2], 18, 1.0); //BrowsU_R - -var angryPoses = []; - -angryPoses[0] = {blendShapes: []}; -addBlendshapeToPose(angryPoses[0], 26, 0.6); //MouthFrown_L -addBlendshapeToPose(angryPoses[0], 27, 0.6); //MouthFrown_R -addBlendshapeToPose(angryPoses[0], 14, 0.6); //BrowsD_L -addBlendshapeToPose(angryPoses[0], 15, 0.6); //BrowsD_R - -angryPoses[1] = {blendShapes: []}; -addBlendshapeToPose(angryPoses[1], 26, 0.9); //MouthFrown_L -addBlendshapeToPose(angryPoses[1], 27, 0.9); //MouthFrown_R -addBlendshapeToPose(angryPoses[1], 14, 0.9); //BrowsD_L -addBlendshapeToPose(angryPoses[1], 15, 0.9); //BrowsD_R - -angryPoses[2] = {blendShapes: []}; -addBlendshapeToPose(angryPoses[2], 26, 1.0); //MouthFrown_L -addBlendshapeToPose(angryPoses[2], 27, 1.0); //MouthFrown_R -addBlendshapeToPose(angryPoses[2], 14, 1.0); //BrowsD_L -addBlendshapeToPose(angryPoses[2], 15, 1.0); //BrowsD_R -addBlendshapeToPose(angryPoses[2], 21, 0.5); //JawOpen -addBlendshapeToPose(angryPoses[2], 46, 1.0); //CheekSquint_L -addBlendshapeToPose(angryPoses[2], 47, 1.0); //CheekSquint_R - -var sadPoses = []; - -sadPoses[0] = {blendShapes: []}; -addBlendshapeToPose(sadPoses[0], 26, 0.6); //MouthFrown_L -addBlendshapeToPose(sadPoses[0], 27, 0.6); //MouthFrown_R -addBlendshapeToPose(sadPoses[0], 16, 0.2); //BrowsU_C -addBlendshapeToPose(sadPoses[0], 2, 0.6); //EyeSquint_L -addBlendshapeToPose(sadPoses[0], 3, 0.6); //EyeSquint_R - -sadPoses[1] = {blendShapes: []}; -addBlendshapeToPose(sadPoses[1], 26, 0.9); //MouthFrown_L -addBlendshapeToPose(sadPoses[1], 27, 0.9); //MouthFrown_R -addBlendshapeToPose(sadPoses[1], 16, 0.6); //BrowsU_C -addBlendshapeToPose(sadPoses[1], 2, 0.9); //EyeSquint_L -addBlendshapeToPose(sadPoses[1], 3, 0.9); //EyeSquint_R - -sadPoses[2] = {blendShapes: []}; -addBlendshapeToPose(sadPoses[2], 26, 1.0); //MouthFrown_L -addBlendshapeToPose(sadPoses[2], 27, 1.0); //MouthFrown_R -addBlendshapeToPose(sadPoses[2], 16, 0.1); //BrowsU_C -addBlendshapeToPose(sadPoses[2], 2, 1.0); //EyeSquint_L -addBlendshapeToPose(sadPoses[2], 3, 1.0); //EyeSquint_R -addBlendshapeToPose(sadPoses[2], 21, 0.3); //JawOpen - -var facePoses = []; -facePoses[0] = happyPoses; -facePoses[1] = angryPoses; -facePoses[2] = sadPoses; - - -function addBlendShape(s) { - allBlendShapes[allBlendShapes.length] = s; -} - -//It is imperative that the following blendshapes are all present and are in the correct order -addBlendShape("EyeBlink_L"); //0 -addBlendShape("EyeBlink_R"); //1 -addBlendShape("EyeSquint_L"); //2 -addBlendShape("EyeSquint_R"); //3 -addBlendShape("EyeDown_L"); //4 -addBlendShape("EyeDown_R"); //5 -addBlendShape("EyeIn_L"); //6 -addBlendShape("EyeIn_R"); //7 -addBlendShape("EyeOpen_L"); //8 -addBlendShape("EyeOpen_R"); //9 -addBlendShape("EyeOut_L"); //10 -addBlendShape("EyeOut_R"); //11 -addBlendShape("EyeUp_L"); //12 -addBlendShape("EyeUp_R"); //13 -addBlendShape("BrowsD_L"); //14 -addBlendShape("BrowsD_R"); //15 -addBlendShape("BrowsU_C"); //16 -addBlendShape("BrowsU_L"); //17 -addBlendShape("BrowsU_R"); //18 -addBlendShape("JawFwd"); //19 -addBlendShape("JawLeft"); //20 -addBlendShape("JawOpen"); //21 -addBlendShape("JawChew"); //22 -addBlendShape("JawRight"); //23 -addBlendShape("MouthLeft"); //24 -addBlendShape("MouthRight"); //25 -addBlendShape("MouthFrown_L"); //26 -addBlendShape("MouthFrown_R"); //27 -addBlendShape("MouthSmile_L"); //28 -addBlendShape("MouthSmile_R"); //29 -addBlendShape("MouthDimple_L"); //30 -addBlendShape("MouthDimple_R"); //31 -addBlendShape("LipsStretch_L"); //32 -addBlendShape("LipsStretch_R"); //33 -addBlendShape("LipsUpperClose"); //34 -addBlendShape("LipsLowerClose"); //35 -addBlendShape("LipsUpperUp"); //36 -addBlendShape("LipsLowerDown"); //37 -addBlendShape("LipsUpperOpen"); //38 -addBlendShape("LipsLowerOpen"); //39 -addBlendShape("LipsFunnel"); //40 -addBlendShape("LipsPucker"); //41 -addBlendShape("ChinLowerRaise"); //42 -addBlendShape("ChinUpperRaise"); //43 -addBlendShape("Sneer"); //44 -addBlendShape("Puff"); //45 -addBlendShape("CheekSquint_L"); //46 -addBlendShape("CheekSquint_R"); //47 - -for (var i = 0; i < allBlendShapes.length; i++) { - targetBlendCoefficient[i] = 0; - currentBlendCoefficient[i] = 0; -} - -function setRandomExpression() { - - //Clear all expression data for current expression - if (currentExpression != -1) { - var expression = facePoses[avatarMood][currentExpression]; - for (var i = 0; i < expression.blendShapes.length; i++) { - targetBlendCoefficient[expression.blendShapes[i].shapeIndex] = 0.0; - } - } - //Get a new current expression - currentExpression = Math.floor(Math.random() * facePoses[avatarMood].length); - var expression = facePoses[avatarMood][currentExpression]; - for (var i = 0; i < expression.blendShapes.length; i++) { - targetBlendCoefficient[expression.blendShapes[i].shapeIndex] = expression.blendShapes[i].val; - } -} - -var expressionChangeSpeed = 0.1; -function updateBlendShapes(deltaTime) { - - for (var i = 0; i < allBlendShapes.length; i++) { - currentBlendCoefficient[i] += (targetBlendCoefficient[i] - currentBlendCoefficient[i]) * expressionChangeSpeed; - Avatar.setBlendshape(allBlendShapes[i], currentBlendCoefficient[i]); - } -} - -var BLINK_SPEED = 0.15; -var CHANCE_TO_BLINK = 0.0025; -var MAX_BLINK = 0.85; -var blink = 0.0; -var isBlinking = false; -function updateBlinking(deltaTime) { - if (isBlinking == false) { - if (Math.random() < CHANCE_TO_BLINK) { - isBlinking = true; - } else { - blink -= BLINK_SPEED; - if (blink < 0.0) blink = 0.0; - } - } else { - blink += BLINK_SPEED; - if (blink > MAX_BLINK) { - blink = MAX_BLINK; - isBlinking = false; - } - } - - currentBlendCoefficient[0] = blink; - currentBlendCoefficient[1] = blink; - targetBlendCoefficient[0] = blink; - targetBlendCoefficient[1] = blink; -} - -// ************************************************************************************* - -//Procedural walk animation using two keyframes -//We use a separate array for front and back joints -//Pitch, yaw, and roll for the joints -var rightAngles = []; -var leftAngles = []; -//for non mirrored joints such as the spine -var middleAngles = []; - -//Actual joint mappings -var SHOULDER_JOINT_NUMBER = 15; -var ELBOW_JOINT_NUMBER = 16; -var JOINT_R_HIP = 1; -var JOINT_R_KNEE = 2; -var JOINT_L_HIP = 6; -var JOINT_L_KNEE = 7; -var JOINT_R_ARM = 15; -var JOINT_R_FOREARM = 16; -var JOINT_L_ARM = 39; -var JOINT_L_FOREARM = 40; -var JOINT_SPINE = 11; -var JOINT_R_FOOT = 3; -var JOINT_L_FOOT = 8; -var JOINT_R_TOE = 4; -var JOINT_L_TOE = 9; - -// ******************************* Animation Is Defined Below ************************************* - -var NUM_FRAMES = 2; -for (var i = 0; i < NUM_FRAMES; i++) { - rightAngles[i] = []; - leftAngles[i] = []; - middleAngles[i] = []; -} -//Joint order for actual joint mappings, should be interleaved R,L,R,L,...S,S,S for R = right, L = left, S = single -var JOINT_ORDER = []; -//*** right / left joints *** -var HIP = 0; -JOINT_ORDER.push(JOINT_R_HIP); -JOINT_ORDER.push(JOINT_L_HIP); -var KNEE = 1; -JOINT_ORDER.push(JOINT_R_KNEE); -JOINT_ORDER.push(JOINT_L_KNEE); -var ARM = 2; -JOINT_ORDER.push(JOINT_R_ARM); -JOINT_ORDER.push(JOINT_L_ARM); -var FOREARM = 3; -JOINT_ORDER.push(JOINT_R_FOREARM); -JOINT_ORDER.push(JOINT_L_FOREARM); -var FOOT = 4; -JOINT_ORDER.push(JOINT_R_FOOT); -JOINT_ORDER.push(JOINT_L_FOOT); -var TOE = 5; -JOINT_ORDER.push(JOINT_R_TOE); -JOINT_ORDER.push(JOINT_L_TOE); -//*** middle joints *** -var SPINE = 0; -JOINT_ORDER.push(JOINT_SPINE); - -//We have to store the angles so we can invert yaw and roll when making the animation -//symmetrical - -//Front refers to leg, not arm. -//Legs Extending -rightAngles[0][HIP] = [30.0, 0.0, 8.0]; -rightAngles[0][KNEE] = [-15.0, 0.0, 0.0]; -rightAngles[0][ARM] = [85.0, -25.0, 0.0]; -rightAngles[0][FOREARM] = [0.0, 0.0, -15.0]; -rightAngles[0][FOOT] = [0.0, 0.0, 0.0]; -rightAngles[0][TOE] = [0.0, 0.0, 0.0]; - -leftAngles[0][HIP] = [-15, 0.0, 8.0]; -leftAngles[0][KNEE] = [-26, 0.0, 0.0]; -leftAngles[0][ARM] = [85.0, 20.0, 0.0]; -leftAngles[0][FOREARM] = [10.0, 0.0, -25.0]; -leftAngles[0][FOOT] = [-13.0, 0.0, 0.0]; -leftAngles[0][TOE] = [34.0, 0.0, 0.0]; - -middleAngles[0][SPINE] = [0.0, -15.0, 5.0]; - -//Legs Passing -rightAngles[1][HIP] = [6.0, 0.0, 8.0]; -rightAngles[1][KNEE] = [-12.0, 0.0, 0.0]; -rightAngles[1][ARM] = [85.0, 0.0, 0.0]; -rightAngles[1][FOREARM] = [0.0, 0.0, -15.0]; -rightAngles[1][FOOT] = [6.0, -8.0, 0.0]; -rightAngles[1][TOE] = [0.0, 0.0, 0.0]; - -leftAngles[1][HIP] = [10.0, 0.0, 8.0]; -leftAngles[1][KNEE] = [-60.0, 0.0, 0.0]; -leftAngles[1][ARM] = [85.0, 0.0, 0.0]; -leftAngles[1][FOREARM] = [0.0, 0.0, -15.0]; -leftAngles[1][FOOT] = [0.0, 0.0, 0.0]; -leftAngles[1][TOE] = [0.0, 0.0, 0.0]; - -middleAngles[1][SPINE] = [0.0, 0.0, 0.0]; - -//Actual keyframes for the animation -var walkKeyFrames = procAnimAPI.generateKeyframes(rightAngles, leftAngles, middleAngles, NUM_FRAMES); - -// ******************************* Animation Is Defined Above ************************************* - -// ********************************** Standing Key Frame ****************************************** -//We don't have to do any mirroring or anything, since this is just a single pose. -var rightQuats = []; -var leftQuats = []; -var middleQuats = []; - -rightQuats[HIP] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 7.0); -rightQuats[KNEE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); -rightQuats[ARM] = Quat.fromPitchYawRollDegrees(85.0, 0.0, 0.0); -rightQuats[FOREARM] = Quat.fromPitchYawRollDegrees(0.0, 0.0, -10.0); -rightQuats[FOOT] = Quat.fromPitchYawRollDegrees(0.0, -8.0, 0.0); -rightQuats[TOE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); - -leftQuats[HIP] = Quat.fromPitchYawRollDegrees(0, 0.0, -7.0); -leftQuats[KNEE] = Quat.fromPitchYawRollDegrees(0, 0.0, 0.0); -leftQuats[ARM] = Quat.fromPitchYawRollDegrees(85.0, 0.0, 0.0); -leftQuats[FOREARM] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 10.0); -leftQuats[FOOT] = Quat.fromPitchYawRollDegrees(0.0, 8.0, 0.0); -leftQuats[TOE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); - -middleQuats[SPINE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); - -var standingKeyFrame = new procAnimAPI.KeyFrame(rightQuats, leftQuats, middleQuats); - -// ************************************************************************************************ - - -var currentFrame = 0; - -var walkTime = 0.0; - -var walkWheelRadius = 0.5; -var walkWheelRate = 2.0 * 3.141592 * walkWheelRadius / 8.0; - -var avatarAcceleration = 0.75; -var avatarVelocity = 0.0; -var avatarMaxVelocity = 1.4; - -function handleAnimation(deltaTime) { - - updateBlinking(deltaTime); - updateBlendShapes(deltaTime); - - if (Math.random() < 0.01) { - setRandomExpression(); - } - - if (avatarVelocity == 0.0) { - walkTime = 0.0; - currentFrame = 0; - } else { - walkTime += avatarVelocity * deltaTime; - if (walkTime > walkWheelRate) { - walkTime = 0.0; - currentFrame++; - if (currentFrame % 2 == 1) { - playRandomFootstepSound(); - } - if (currentFrame > 3) { - currentFrame = 0; - } - } - } - - var frame = walkKeyFrames[currentFrame]; - - var walkInterp = walkTime / walkWheelRate; - var animInterp = avatarVelocity / (avatarMaxVelocity / 1.3); - if (animInterp > 1.0) animInterp = 1.0; - - for (var i = 0; i < JOINT_ORDER.length; i++) { - var walkJoint = procAnimAPI.deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], walkInterp); - var standJoint = standingKeyFrame.rotations[i]; - var finalJoint = Quat.mix(standJoint, walkJoint, animInterp); - Avatar.setJointData(JOINT_ORDER[i], finalJoint); - } -} - -function jumpWithLoudness(deltaTime) { - // potentially change pelvis height depending on trailing average loudness - - pelvisOscillatorVelocity += deltaTime * Agent.lastReceivedAudioLoudness * 700.0 ; - - pelvisOscillatorVelocity -= pelvisOscillatorPosition * 0.75; - pelvisOscillatorVelocity *= 0.97; - pelvisOscillatorPosition += deltaTime * pelvisOscillatorVelocity; - Avatar.headPitch = pelvisOscillatorPosition * 60.0; - - var pelvisPosition = Avatar.position; - pelvisPosition.y = (Y_PELVIS - 0.35) + pelvisOscillatorPosition; - - if (pelvisPosition.y < Y_PELVIS) { - pelvisPosition.y = Y_PELVIS; - } else if (pelvisPosition.y > Y_PELVIS + 1.0) { - pelvisPosition.y = Y_PELVIS + 1.0; - } - - Avatar.position = pelvisPosition; -} - -var forcedMove = false; - -var wasMovingLastFrame = false; - -function handleHeadTurn() { - if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) { - targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE); - targetHeadYaw = getRandomFloat(-YAW_RANGE, YAW_RANGE); - isTurningHead = true; - } else { - Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * HEAD_TURN_RATE; - Avatar.headYaw = Avatar.headYaw + (targetHeadYaw - Avatar.headYaw) * HEAD_TURN_RATE; - if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE && - Math.abs(Avatar.headYaw - targetHeadYaw) < STOP_TOLERANCE) { - isTurningHead = false; - } - } -} - -function stopWalking() { - avatarVelocity = 0.0; - isMoving = false; -} - -var MAX_ATTEMPTS = 40; -function handleWalking(deltaTime) { - - if (forcedMove || (!isMoving && Math.random() < CHANCE_OF_MOVING)) { - // Set new target location - - var moveRange; - if (Math.random() < CHANCE_OF_BIG_MOVE) { - moveRange = MOVE_RANGE_BIG; - } else { - moveRange = MOVE_RANGE_SMALL; - } - - //Keep trying new orientations if the desired target location is out of bounds - var attempts = 0; - do { - targetOrientation = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 })); - var front = Quat.getFront(targetOrientation); - - targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, moveRange))); - } - while ((targetPosition.x < X_MIN || targetPosition.x > X_MAX || targetPosition.z < Z_MIN || targetPosition.z > Z_MAX) - && attempts < MAX_ATTEMPTS); - - targetPosition.x = clamp(targetPosition.x, X_MIN, X_MAX); - targetPosition.z = clamp(targetPosition.z, Z_MIN, Z_MAX); - targetPosition.y = Y_PELVIS; - - wasMovingLastFrame = true; - isMoving = true; - forcedMove = false; - } else if (isMoving) { - - var targetVector = Vec3.subtract(targetPosition, Avatar.position); - var distance = Vec3.length(targetVector); - if (distance <= avatarVelocity * deltaTime) { - Avatar.position = targetPosition; - stopWalking(); - } else { - var direction = Vec3.normalize(targetVector); - //Figure out if we should be slowing down - var t = avatarVelocity / avatarAcceleration; - var d = (avatarVelocity / 2.0) * t; - if (distance < d) { - avatarVelocity -= avatarAcceleration * deltaTime; - if (avatarVelocity <= 0) { - stopWalking(); - } - } else { - avatarVelocity += avatarAcceleration * deltaTime; - if (avatarVelocity > avatarMaxVelocity) avatarVelocity = avatarMaxVelocity; - } - Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(direction, avatarVelocity * deltaTime)); - Avatar.orientation = Quat.mix(Avatar.orientation, targetOrientation, TURN_RATE); - - wasMovingLastFrame = true; - - } - } -} - -function handleTalking() { - if (Math.random() < CHANCE_OF_SOUND) { - playRandomSound(); - } -} - -function changePelvisHeight(newHeight) { - var newPosition = Avatar.position; - newPosition.y = newHeight; - Avatar.position = newPosition; -} - -function updateBehavior(deltaTime) { - - if (AvatarList.containsAvatarWithDisplayName("mrdj")) { - if (wasMovingLastFrame) { - isMoving = false; - } - - // we have a DJ, shouldn't we be dancing? - jumpWithLoudness(deltaTime); - } else { - - // no DJ, let's just chill on the dancefloor - randomly walking and talking - handleHeadTurn(); - handleAnimation(deltaTime); - handleWalking(deltaTime); - handleTalking(); - } -} - -Script.update.connect(updateBehavior); \ No newline at end of file diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index 7abc42d0e3..e548d5c276 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -372,7 +372,6 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser QString incomingEarlyString = locale.toString((uint)seqStats.getEarly()); QString incomingLikelyLostString = locale.toString((uint)seqStats.getLost()); QString incomingRecovered = locale.toString((uint)seqStats.getRecovered()); - QString incomingDuplicateString = locale.toString((uint)seqStats.getDuplicate()); int clockSkewInMS = node->getClockSkewUsec() / (int)USECS_PER_MSEC; QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage()); @@ -386,8 +385,7 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser serverDetails << "
" << " Out of Order: " << qPrintable(incomingOutOfOrderString) << "/ Early: " << qPrintable(incomingEarlyString) << "/ Late: " << qPrintable(incomingLateString) << - "/ Unreasonable: " << qPrintable(incomingUnreasonableString) << - "/ Duplicate: " << qPrintable(incomingDuplicateString); + "/ Unreasonable: " << qPrintable(incomingUnreasonableString); serverDetails << "
" << " Average Flight Time: " << qPrintable(incomingFlightTimeString) << " msecs"; diff --git a/libraries/networking/src/SequenceNumberStats.cpp b/libraries/networking/src/SequenceNumberStats.cpp index 28ea06787a..6a7f5e1964 100644 --- a/libraries/networking/src/SequenceNumberStats.cpp +++ b/libraries/networking/src/SequenceNumberStats.cpp @@ -14,70 +14,23 @@ #include SequenceNumberStats::SequenceNumberStats(int statsHistoryLength, bool canDetectOutOfSync) - : _received(0), - _lastReceivedSequence(0), + : _lastReceivedSequence(0), _missingSet(), _stats(), _lastSenderUUID(), _statsHistory(statsHistoryLength), - _canHaveChild(canDetectOutOfSync), - _childInstance(NULL), - _consecutiveReasonable(0) + _lastUnreasonableSequence(0), + _consecutiveUnreasonableOnTime(0) { } -SequenceNumberStats::SequenceNumberStats(const SequenceNumberStats& other) - : _received(other._received), - _lastReceivedSequence(other._lastReceivedSequence), - _missingSet(other._missingSet), - _stats(other._stats), - _lastSenderUUID(other._lastSenderUUID), - _statsHistory(other._statsHistory), - _childInstance(NULL), - _canHaveChild(other._canHaveChild), - _consecutiveReasonable(other._consecutiveReasonable) -{ - if (other._childInstance) { - _childInstance = new SequenceNumberStats(*other._childInstance); - } -} - -SequenceNumberStats& SequenceNumberStats::operator=(const SequenceNumberStats& rhs) { - _received = rhs._received; - _lastReceivedSequence = rhs._lastReceivedSequence; - _missingSet = rhs._missingSet; - _stats = rhs._stats; - _lastSenderUUID = rhs._lastSenderUUID; - _statsHistory = rhs._statsHistory; - _canHaveChild = rhs._canHaveChild; - _consecutiveReasonable = rhs._consecutiveReasonable; - - if (rhs._childInstance) { - _childInstance = new SequenceNumberStats(*rhs._childInstance); - } else { - _childInstance = NULL; - } - return *this; -} - -SequenceNumberStats::~SequenceNumberStats() { - if (_childInstance) { - delete _childInstance; - } -} - void SequenceNumberStats::reset() { - _received = 0; _missingSet.clear(); _stats = PacketStreamStats(); _lastSenderUUID = QUuid(); _statsHistory.clear(); - - if (_childInstance) { - delete _childInstance; - _childInstance = NULL; - } - _consecutiveReasonable = 0; + _lastUnreasonableSequence = 0; + _consecutiveUnreasonableOnTime = 0; } static const int UINT16_RANGE = std::numeric_limits::max() + 1; @@ -88,7 +41,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui // if the sender node has changed, reset all stats if (senderUUID != _lastSenderUUID) { - if (_received > 0) { + if (_stats._received > 0) { qDebug() << "sequence number stats was reset due to new sender node"; qDebug() << "previous:" << _lastSenderUUID << "current:" << senderUUID; reset(); @@ -97,9 +50,9 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui } // determine our expected sequence number... handle rollover appropriately - quint16 expected = _received > 0 ? _lastReceivedSequence + (quint16)1 : incoming; + quint16 expected = _stats._received > 0 ? _lastReceivedSequence + (quint16)1 : incoming; - _received++; + _stats._received++; if (incoming == expected) { // on time arrivalInfo._status = OnTime; @@ -130,72 +83,11 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui qDebug() << "unreasonable sequence number:" << incoming << "previous:" << _lastReceivedSequence; _stats._unreasonable++; - _consecutiveReasonable = 0; - - // if _canHaveChild, create a child instance of SequenceNumberStats to track this unreasonable seq num and ones in the future. - // if the child instance detects a valid stream of seq nums up to some length, the seq nums sender probably - // fell out of sync with us. - - if (_canHaveChild) { - - if (!_childInstance) { - _childInstance = new SequenceNumberStats(0, false); - } - - ArrivalInfo unreasonableTrackerArrivalInfo = _childInstance->sequenceNumberReceived(incoming); - - // the child instance will be used to detect some threshold number seq nums in a row that are perfectly - // in order. - - const int UNREASONABLE_TRACKER_RECEIVED_THRESHOLD = 8; - - if (unreasonableTrackerArrivalInfo._status != OnTime) { - _childInstance->reset(); - - } else if (_childInstance->getReceived() >= UNREASONABLE_TRACKER_RECEIVED_THRESHOLD) { - - // the child instance has detected a threshold number of consecutive seq nums. - // copy its state to this instance. - - _received = _childInstance->_received; - _lastReceivedSequence = _childInstance->_lastReceivedSequence; - _missingSet = _childInstance->_missingSet; - _stats = _childInstance->_stats; - - // don't copy _lastSenderUUID; _unreasonableTracker always has null UUID for that member. - // ours should be up-to-date. - - // don't copy _statsHistory; _unreasonableTracker keeps a history of length 0. - // simply clear ours. - _statsHistory.clear(); - - arrivalInfo = unreasonableTrackerArrivalInfo; - - // delete child instance; - delete _childInstance; - _childInstance = NULL; - } - } - + receivedUnreasonable(incoming); return arrivalInfo; } - _consecutiveReasonable++; - - // if we got a reasonable seq num but have a child instance tracking unreasonable seq nums, - // reset it. if many consecutive reasonable seq nums have occurred (implying the unreasonable seq num - // that caused the creation of the child instance was probably a fluke), delete our child instance. - if (_childInstance) { - const int CONSECUTIVE_REASONABLE_CHILD_DELETE_THRESHOLD = 8; - if (_consecutiveReasonable >= CONSECUTIVE_REASONABLE_CHILD_DELETE_THRESHOLD) { - _childInstance->reset(); - } else { - delete _childInstance; - _childInstance = NULL; - } - } - // now that rollover has been corrected for (if it occurred), incomingInt and expectedInt can be // compared to each other directly, though one of them might be negative @@ -219,7 +111,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui for (int missingInt = expectedInt; missingInt < incomingInt; missingInt++) { _missingSet.insert((quint16)(missingInt < 0 ? missingInt + UINT16_RANGE : missingInt)); } - + // prune missing sequence list if it gets too big; sequence numbers that are older than MAX_REASONABLE_SEQUENCE_GAP // will be removed. if (_missingSet.size() > MAX_REASONABLE_SEQUENCE_GAP) { @@ -236,7 +128,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui // remove this from missing sequence number if it's in there if (_missingSet.remove(incoming)) { - arrivalInfo._status = Late; + arrivalInfo._status = Recovered; if (wantExtraDebugging) { qDebug() << "found it in _missingSet"; @@ -244,19 +136,62 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui _stats._lost--; _stats._recovered++; } else { - arrivalInfo._status = Duplicate; + // this late seq num is not in our missing set. it is possibly a duplicate, or possibly a late + // packet that should have arrived before our first received packet. we'll count these + // as unreasonable. - if (wantExtraDebugging) { - qDebug() << "sequence:" << incoming << "was NOT found in _missingSet and is probably a duplicate"; - } - _stats._duplicate++; + arrivalInfo._status = Unreasonable; + + qDebug() << "unreasonable sequence number:" << incoming << "(possible duplicate)"; + + _stats._unreasonable++; + + receivedUnreasonable(incoming); + return arrivalInfo; } } } + // if we've made it here, we received a reasonable seq number. + _consecutiveUnreasonableOnTime = 0; + return arrivalInfo; } +void SequenceNumberStats::receivedUnreasonable(quint16 incoming) { + + const int CONSECUTIVE_UNREASONABLE_ON_TIME_THRESHOLD = 8; + + quint16 expected = _consecutiveUnreasonableOnTime > 0 ? _lastUnreasonableSequence + (quint16)1 : incoming; + if (incoming == expected) { + _consecutiveUnreasonableOnTime++; + _lastUnreasonableSequence = incoming; + + if (_consecutiveUnreasonableOnTime >= CONSECUTIVE_UNREASONABLE_ON_TIME_THRESHOLD) { + // we've received many unreasonable seq numbers in a row, all in order. we're probably out of sync with + // the seq num sender. update our state to get back in sync with the sender. + + _lastReceivedSequence = incoming; + _missingSet.clear(); + + _stats._received = CONSECUTIVE_UNREASONABLE_ON_TIME_THRESHOLD; + _stats._unreasonable = 0; + _stats._early = 0; + _stats._late = 0; + _stats._lost = 0; + _stats._recovered = 0; + _stats._expectedReceived = CONSECUTIVE_UNREASONABLE_ON_TIME_THRESHOLD; + + _statsHistory.clear(); + _consecutiveUnreasonableOnTime = 0; + + qDebug() << "re-synced with sequence number sender"; + } + } else { + _consecutiveUnreasonableOnTime = 0; + } +} + void SequenceNumberStats::pruneMissingSet(const bool wantExtraDebugging) { if (wantExtraDebugging) { qDebug() << "pruning _missingSet! size:" << _missingSet.size(); @@ -311,7 +246,7 @@ PacketStreamStats SequenceNumberStats::getStatsForHistoryWindow() const { const PacketStreamStats* newestStats = _statsHistory.getNewestEntry(); const PacketStreamStats* oldestStats = _statsHistory.get(_statsHistory.getNumEntries() - 1); - + // this catches cases where history is length 1 or 0 (both are NULL in case of 0) if (newestStats == oldestStats) { return PacketStreamStats(); @@ -319,13 +254,13 @@ PacketStreamStats SequenceNumberStats::getStatsForHistoryWindow() const { // calculate difference between newest stats and oldest stats to get window stats PacketStreamStats windowStats; - windowStats._expectedReceived = newestStats->_expectedReceived - oldestStats->_expectedReceived; + windowStats._received = newestStats->_received - oldestStats->_received; windowStats._unreasonable = newestStats->_unreasonable - oldestStats->_unreasonable; windowStats._early = newestStats->_early - oldestStats->_early; windowStats._late = newestStats->_late - oldestStats->_late; windowStats._lost = newestStats->_lost - oldestStats->_lost; windowStats._recovered = newestStats->_recovered - oldestStats->_recovered; - windowStats._duplicate = newestStats->_duplicate - oldestStats->_duplicate; + windowStats._expectedReceived = newestStats->_expectedReceived - oldestStats->_expectedReceived; return windowStats; } diff --git a/libraries/networking/src/SequenceNumberStats.h b/libraries/networking/src/SequenceNumberStats.h index fb3ff68f70..5e02dbc850 100644 --- a/libraries/networking/src/SequenceNumberStats.h +++ b/libraries/networking/src/SequenceNumberStats.h @@ -22,27 +22,24 @@ const int MAX_REASONABLE_SEQUENCE_GAP = 1000; class PacketStreamStats { public: PacketStreamStats() - : _expectedReceived(0), + : _received(0), _unreasonable(0), _early(0), _late(0), _lost(0), _recovered(0), - _duplicate(0) + _expectedReceived(0) {} - float getOutOfOrderRate() const { return (float)(_early + _late) / _expectedReceived; } - float getEaryRate() const { return (float)_early / _expectedReceived; } - float getLateRate() const { return (float)_late / _expectedReceived; } float getLostRate() const { return (float)_lost / _expectedReceived; } - quint32 _expectedReceived; + quint32 _received; quint32 _unreasonable; quint32 _early; quint32 _late; quint32 _lost; quint32 _recovered; - quint32 _duplicate; + quint32 _expectedReceived; }; class SequenceNumberStats { @@ -51,8 +48,7 @@ public: OnTime, Unreasonable, Early, - Late, // recovered - Duplicate + Recovered, }; class ArrivalInfo { @@ -63,18 +59,13 @@ public: SequenceNumberStats(int statsHistoryLength = 0, bool canDetectOutOfSync = true); - SequenceNumberStats(const SequenceNumberStats& other); - SequenceNumberStats& operator=(const SequenceNumberStats& rhs); - ~SequenceNumberStats(); void reset(); ArrivalInfo sequenceNumberReceived(quint16 incoming, QUuid senderUUID = QUuid(), const bool wantExtraDebugging = false); void pruneMissingSet(const bool wantExtraDebugging = false); void pushStatsToHistory() { _statsHistory.insert(_stats); } - quint32 getReceived() const { return _received; } - float getUnreasonableRate() const { return _stats._unreasonable / _received; } - + quint32 getReceived() const { return _stats._received; } quint32 getExpectedReceived() const { return _stats._expectedReceived; } quint32 getUnreasonable() const { return _stats._unreasonable; } quint32 getOutOfOrder() const { return _stats._early + _stats._late; } @@ -82,15 +73,15 @@ public: quint32 getLate() const { return _stats._late; } quint32 getLost() const { return _stats._lost; } quint32 getRecovered() const { return _stats._recovered; } - quint32 getDuplicate() const { return _stats._duplicate; } const PacketStreamStats& getStats() const { return _stats; } PacketStreamStats getStatsForHistoryWindow() const; const QSet& getMissingSet() const { return _missingSet; } private: - int _received; + void receivedUnreasonable(quint16 incoming); +private: quint16 _lastReceivedSequence; QSet _missingSet; @@ -100,13 +91,8 @@ private: RingBufferHistory _statsHistory; - - // to deal with the incoming seq nums going out of sync with this tracker, we'll create another instance - // of this class when we encounter an unreasonable - bool _canHaveChild; - SequenceNumberStats* _childInstance; - - int _consecutiveReasonable; + quint16 _lastUnreasonableSequence; + int _consecutiveUnreasonableOnTime; }; #endif // hifi_SequenceNumberStats_h diff --git a/tests/networking/src/SequenceNumberStatsTests.cpp b/tests/networking/src/SequenceNumberStatsTests.cpp index 1df48b781b..901a018235 100644 --- a/tests/networking/src/SequenceNumberStatsTests.cpp +++ b/tests/networking/src/SequenceNumberStatsTests.cpp @@ -20,7 +20,7 @@ void SequenceNumberStatsTests::runAllTests() { earlyLateTest(); duplicateTest(); pruneTest(); - recursiveTest(); + resyncTest(); } const quint32 UINT16_RANGE = std::numeric_limits::max() + 1; @@ -38,7 +38,6 @@ void SequenceNumberStatsTests::rolloverTest() { stats.sequenceNumberReceived(seq); seq = seq + (quint16)1; - assert(stats.getDuplicate() == 0); assert(stats.getEarly() == 0); assert(stats.getLate() == 0); assert(stats.getLost() == 0); @@ -69,7 +68,6 @@ void SequenceNumberStatsTests::earlyLateTest() { seq = seq + (quint16)1; numSent++; - assert(stats.getDuplicate() == 0); assert(stats.getEarly() == numEarly); assert(stats.getLate() == numLate); assert(stats.getLost() == numLost); @@ -89,7 +87,6 @@ void SequenceNumberStatsTests::earlyLateTest() { seq = seq + (quint16)1; numSent++; - assert(stats.getDuplicate() == 0); assert(stats.getEarly() == numEarly); assert(stats.getLate() == numLate); assert(stats.getLost() == numLost); @@ -106,7 +103,6 @@ void SequenceNumberStatsTests::earlyLateTest() { numLost--; numRecovered++; - assert(stats.getDuplicate() == 0); assert(stats.getEarly() == numEarly); assert(stats.getLate() == numLate); assert(stats.getLost() == numLost); @@ -135,7 +131,7 @@ void SequenceNumberStatsTests::duplicateTest() { quint32 numLost = 0; for (int R = 0; R < 2; R++) { - for (int T = 0; T < 10000; T++) { + for (int T = 0; T < 5; T++) { quint16 duplicate = seq; @@ -145,7 +141,7 @@ void SequenceNumberStatsTests::duplicateTest() { seq = seq + (quint16)1; numSent++; - assert(stats.getDuplicate() == numDuplicate); + assert(stats.getUnreasonable() == numDuplicate); assert(stats.getEarly() == numEarly); assert(stats.getLate() == numLate); assert(stats.getLost() == numLost); @@ -167,7 +163,7 @@ void SequenceNumberStatsTests::duplicateTest() { seq = seq + (quint16)1; numSent++; - assert(stats.getDuplicate() == numDuplicate); + assert(stats.getUnreasonable() == numDuplicate); assert(stats.getEarly() == numEarly); assert(stats.getLate() == numLate); assert(stats.getLost() == numLost); @@ -183,7 +179,7 @@ void SequenceNumberStatsTests::duplicateTest() { numDuplicate++; numLate++; - assert(stats.getDuplicate() == numDuplicate); + assert(stats.getUnreasonable() == numDuplicate); assert(stats.getEarly() == numEarly); assert(stats.getLate() == numLate); assert(stats.getLost() == numLost); @@ -199,7 +195,7 @@ void SequenceNumberStatsTests::duplicateTest() { numDuplicate++; numLate++; - assert(stats.getDuplicate() == numDuplicate); + assert(stats.getUnreasonable() == numDuplicate); assert(stats.getEarly() == numEarly); assert(stats.getLate() == numLate); assert(stats.getLost() == numLost); @@ -279,7 +275,7 @@ void SequenceNumberStatsTests::pruneTest() { } } -void SequenceNumberStatsTests::recursiveTest() { +void SequenceNumberStatsTests::resyncTest() { SequenceNumberStats stats(0); @@ -302,9 +298,20 @@ void SequenceNumberStatsTests::recursiveTest() { sequence = 0; for (int R = 0; R < 7; R++) { stats.sequenceNumberReceived(sequence); - sequence += (quint16)2000; + sequence += (quint16)1; } - + + assert(stats.getUnreasonable() == 7); + + sequence = 6000; + for (int R = 0; R < 7; R++) { + stats.sequenceNumberReceived(sequence); + sequence += (quint16)1; + } + + assert(stats.getUnreasonable() == 14); + + sequence = 9000; for (int i = 0; i < 10; i++) { stats.sequenceNumberReceived(sequence); sequence += (quint16)1; diff --git a/tests/networking/src/SequenceNumberStatsTests.h b/tests/networking/src/SequenceNumberStatsTests.h index 80053fd822..6b1fa3dde7 100644 --- a/tests/networking/src/SequenceNumberStatsTests.h +++ b/tests/networking/src/SequenceNumberStatsTests.h @@ -23,7 +23,7 @@ namespace SequenceNumberStatsTests { void earlyLateTest(); void duplicateTest(); void pruneTest(); - void recursiveTest(); + void resyncTest(); }; #endif // hifi_SequenceNumberStatsTests_h