mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
735 lines
24 KiB
JavaScript
735 lines
24 KiB
JavaScript
// botProceduralWayPoints.js
|
|
|
|
// modified by Adrian McCarlie for worklist job hifi: #19977
|
|
// 1 September 2014.
|
|
// this script enables a user to define unlimited number of way points on the Domain,
|
|
// these points form a path that the bot will follow, once the final way point is reached,
|
|
// the bot will return to the start and continue until script is stopped.
|
|
// pause times for each way point can be set individually.
|
|
// User must input the x, y, z co-ords for wayPoints[] and time in milliseconds for pauseTimes[].
|
|
//
|
|
// original script
|
|
// 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(Script.getExternalPath(Script.ExternalPaths.Assets, "scripts/acScripts/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);
|
|
}
|
|
|
|
//input co-ords for start position and 7 other positions
|
|
|
|
var AVATAR_PELVIS_HEIGHT = 0.84;//only change this if you have an odd size avatar
|
|
|
|
var wayPoints = []; //input locations for all the waypoints
|
|
wayPoints[0] = {x:8131.5, y:202.0, z:8261.5}; //input the location of the start position
|
|
wayPoints[1] = {x: 8160.5, y: 202.0, z: 8261.5}; //input the location of the first way point
|
|
wayPoints[2] = {x: 8160.5, y: 203.0, z: 8270.5};
|
|
wayPoints[3] = {x: 8142.5, y: 204.0, z: 8270.5};
|
|
wayPoints[4] = {x: 8142.5, y: 204.0, z: 8272.5};
|
|
wayPoints[5] = {x: 8160.5, y: 203.0, z: 8272.5};
|
|
wayPoints[6] = {x: 8160.5, y: 202.0, z: 8284.5};
|
|
wayPoints[7] = {x: 8111.5, y: 202.0, z: 8284.5};// continue to add locations and add or remove lines as needed.
|
|
|
|
var pauseTimes = []; // the number of pauseTimes must equal the number of wayPoints. Time is in milliseconds
|
|
pauseTimes[0] = 5000; //waiting to go to wayPoint0 (startPoint)
|
|
pauseTimes[1] = 10000; //waiting to go to wayPoint1
|
|
pauseTimes[2] = 3000;
|
|
pauseTimes[3] = 3000;
|
|
pauseTimes[4] = 8000;
|
|
pauseTimes[5] = 6000;
|
|
pauseTimes[6] = 3000;
|
|
pauseTimes[7] = 3000;// add or delete to match way points.
|
|
|
|
|
|
wayPoints[0].y = wayPoints[0].y + AVATAR_PELVIS_HEIGHT;
|
|
wayPoints[1].y = wayPoints[1].y + AVATAR_PELVIS_HEIGHT;
|
|
wayPoints[2].y = wayPoints[2].y + AVATAR_PELVIS_HEIGHT;
|
|
wayPoints[3].y = wayPoints[3].y + AVATAR_PELVIS_HEIGHT;
|
|
wayPoints[4].y = wayPoints[4].y + AVATAR_PELVIS_HEIGHT;
|
|
wayPoints[5].y = wayPoints[5].y + AVATAR_PELVIS_HEIGHT;
|
|
wayPoints[6].y = wayPoints[6].y + AVATAR_PELVIS_HEIGHT;
|
|
wayPoints[7].y = wayPoints[7].y + AVATAR_PELVIS_HEIGHT;
|
|
|
|
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 AVATAR_PELVIS_HEIGHT = 1.84;
|
|
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 = wayPoints[0];
|
|
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 = Script.getExternalPath(Script.ExternalPaths.Assets, "meshes/" + newFaceFilePrefix + ".fst");
|
|
Avatar.skeletonModelURL = Script.getExternalPath(Script.ExternalPaths.Assets, "meshes/" + newBodyFilePrefix + "_a.fst");
|
|
Avatar.billboardURL = Script.getExternalPath(Script.ExternalPaths.Assets, "meshes/billboards/bot" + botNumber + ".png");
|
|
|
|
Agent.isAvatar = true;
|
|
Agent.isListeningToAudioStream = true;
|
|
|
|
// change the avatar's position to wayPoints[0]
|
|
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 = Script.getExternalPath(Script.ExternalPaths.Assets, "sounds/Cocktail+Party+Snippets/Raws/");
|
|
|
|
var FOOTSTEP_BASE_URL = Script.getExternalPath(Script.ExternalPaths.Assets, "sounds/Footsteps/");
|
|
|
|
for (var i = 0; i < sound_filenames.length; i++) {
|
|
sounds.push(SoundCache.getSound(SOUND_BASE_URL + sound_filenames[i]));
|
|
}
|
|
|
|
for (var i = 0; i < footstep_filenames.length; i++) {
|
|
footstepSounds.push(SoundCache.getSound(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));
|
|
Audio.playSound(footstepSounds[whichSound], {
|
|
position: Avatar.position,
|
|
volume: 1.0
|
|
});
|
|
}
|
|
|
|
// 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 pauseTimer;
|
|
|
|
function pause(checkPoint, rotation, delay){
|
|
|
|
pauseTimer = Script.setTimeout(function() {
|
|
targetPosition = checkPoint;
|
|
targetOrientation = rotation;
|
|
|
|
isMoving = true;
|
|
}, delay);
|
|
|
|
|
|
}
|
|
|
|
|
|
function handleWalking(deltaTime) {
|
|
|
|
|
|
if (!isMoving){
|
|
if(targetPosition.x == 0){targetPosition = wayPoints[1]; isMoving = true;} //Start by heading for wayPoint1
|
|
|
|
else{
|
|
for (var j = 0; j <= wayPoints.length; j++) {
|
|
if (targetPosition == wayPoints[j]) {
|
|
|
|
if(j == wayPoints.length -1){ j= -1;}
|
|
var k = j + 1;
|
|
var toTarget = Vec3.normalize(Vec3.subtract(wayPoints[k], Avatar.position));
|
|
var localVector = Vec3.multiplyQbyV(Avatar.orientation, { x: 0, y: 0, z: -1 });
|
|
toTarget.y = 0; // I recommend doing that if you don't want weird rotation to occur that are not around Y.
|
|
|
|
var axis = Vec3.normalize(Vec3.cross(toTarget, localVector));
|
|
var angle = Math.acos(Vec3.dot(toTarget, localVector)) * 180 / Math.PI;
|
|
|
|
if (Vec3.dot(Vec3.cross(axis, localVector), toTarget) < 0) {
|
|
angle = -angle;
|
|
}
|
|
var delta = 1;
|
|
|
|
var quat = Quat.angleAxis(angle, axis);
|
|
Avatar.orientation = Quat.multiply(quat, Avatar.orientation);
|
|
|
|
|
|
pause(wayPoints[k], Avatar.orientation, pauseTimes[k]);
|
|
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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));
|
|
|
|
|
|
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);
|
|
|