mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 22:35:14 +02:00
Working procedural walk animation with two keyframes
This commit is contained in:
parent
e25ea64b9b
commit
5c47e9013c
3 changed files with 566 additions and 0 deletions
561
examples/dancer.js
Normal file
561
examples/dancer.js
Normal file
|
@ -0,0 +1,561 @@
|
|||
//
|
||||
// dancer.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/20/14.
|
||||
// Modified by Philip on 3/3/14
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates an NPC avatar.
|
||||
//
|
||||
//
|
||||
|
||||
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.01;
|
||||
var CHANCE_OF_SOUND = 0;
|
||||
var CHANCE_OF_HEAD_TURNING = 0.05;
|
||||
var CHANCE_OF_BIG_MOVE = 0.1;
|
||||
var CHANCE_OF_WAVING = 0.009;
|
||||
|
||||
var isMoving = true;
|
||||
var isTurningHead = false;
|
||||
var isPlay
|
||||
ingAudio = false;
|
||||
var isWaving = false;
|
||||
var waveFrequency = 0.0;
|
||||
var waveAmplitude = 0.0;
|
||||
|
||||
var X_MIN = 5.50;
|
||||
var X_MAX = 5.60;
|
||||
var Z_MIN = 5.00;
|
||||
var Z_MAX = 5.10;
|
||||
var Y_PELVIS = 1.0;
|
||||
var MAX_PELVIS_DELTA = 2.5;
|
||||
|
||||
var AVATAR_PELVIS_HEIGHT = 0.75;
|
||||
|
||||
var MOVE_RANGE_SMALL = 1.0;
|
||||
var TURN_RANGE = 70.0;
|
||||
var STOP_TOLERANCE = 0.05;
|
||||
var MOVE_RATE = 0.05;
|
||||
var TURN_RATE = 0.15;
|
||||
var PITCH_RATE = 0.20;
|
||||
var PITCH_RANGE = 30.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 targetDirection = { x: 0, y: 0, z: 0, w: 0 };
|
||||
var currentDirection = { x: 0, y: 0, z: 0, w: 0 };
|
||||
var targetHeadPitch = 0.0;
|
||||
|
||||
var cumulativeTime = 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))
|
||||
}
|
||||
|
||||
// pick an integer between 1 and 100 that is not 28 for the face model for this bot
|
||||
botNumber = 28;
|
||||
|
||||
while (botNumber == 28) {
|
||||
botNumber = getRandomInt(1, 100);
|
||||
}
|
||||
|
||||
if (botNumber <= 20) {
|
||||
newFaceFilePrefix = "ron";
|
||||
newBodyFilePrefix = "defaultAvatar_body"
|
||||
} else {
|
||||
if (botNumber <= 40) {
|
||||
newFaceFilePrefix = "superhero";
|
||||
} else if (botNumber <= 60) {
|
||||
newFaceFilePrefix = "amber";
|
||||
} else if (botNumber <= 80) {
|
||||
newFaceFilePrefix = "ron";
|
||||
} else {
|
||||
newFaceFilePrefix = "angie";
|
||||
}
|
||||
|
||||
newBodyFilePrefix = "bot" + botNumber;
|
||||
}
|
||||
|
||||
newFaceFilePrefix = "ron";
|
||||
newBodyFilePrefix = "bot" + 63;
|
||||
|
||||
// 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 SOUND_BASE_URL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/";
|
||||
|
||||
for (var i = 0; i < sound_filenames.length; i++) {
|
||||
sounds.push(new Sound(SOUND_BASE_URL + sound_filenames[i]));
|
||||
}
|
||||
}
|
||||
|
||||
var sounds = [];
|
||||
loadSounds();
|
||||
|
||||
function loadAnimations() {
|
||||
|
||||
var animation_filenames = [];
|
||||
var ANIMATION_BASE_URL = "http://highfidelity-dev.s3.amazonaws.com/animations/";
|
||||
|
||||
if (botNumber < 20) {
|
||||
animation_filenames = ["robot/wave_hip_hop_dance.fbx", "robot/robot_hip_hop_dance.fbx"];
|
||||
} else if (botNumber <= 40) {
|
||||
animation_filenames = ["superhero/house_dancing_2.fbx", "superhero/house_dancing_3.fbx", "superhero/house_dancing_4.fbx"];
|
||||
} else if (botNumber <= 60) {
|
||||
animation_filenames = ["amber/house_dancing.fbx"]
|
||||
} else if (botNumber <= 80) {
|
||||
animation_filenames = ["ron/hip_hop_dancing.fbx", "ron/gangnam_style.fbx"];
|
||||
} else {
|
||||
animation_filenames = ["angie/hip_hop_dancing_6.fbx"];
|
||||
}
|
||||
|
||||
for (var i = 0; i < animation_filenames.length; i++) {
|
||||
animations.push(AnimationCache.getAnimation(ANIMATION_BASE_URL + animation_filenames[i]));
|
||||
}
|
||||
}
|
||||
|
||||
var animations = [];
|
||||
loadAnimations();
|
||||
|
||||
function playRandomSound() {
|
||||
if (!Agent.isPlayingAvatarSound) {
|
||||
var whichSound = Math.floor((Math.random() * sounds.length) % sounds.length);
|
||||
Agent.playAvatarSound(sounds[whichSound]);
|
||||
}
|
||||
}
|
||||
|
||||
function stopWaving() {
|
||||
isWaving = false;
|
||||
Avatar.clearJointData(SHOULDER_JOINT_NUMBER);
|
||||
Avatar.clearJointData(ELBOW_JOINT_NUMBER);
|
||||
Avatar.clearJointData(JOINT_SPINE);
|
||||
}
|
||||
|
||||
//Animation KeyFrame constructor. rightJoints and leftJoints must be the same size
|
||||
function WalkKeyFrame(rightJoints, leftJoints, singleJoints) {
|
||||
this.rotations = [];
|
||||
|
||||
for (var i = 0; i < rightJoints.length; i++) {
|
||||
this.rotations[this.rotations.length] = rightJoints[i];
|
||||
this.rotations[this.rotations.length] = leftJoints[i];
|
||||
}
|
||||
for (var i = 0; i < singleJoints.length; i++) {
|
||||
this.rotations[this.rotations.length] = singleJoints[i];
|
||||
}
|
||||
}
|
||||
|
||||
//Procedural walk animation using two keyframes
|
||||
//We use a separate array for front and back joints
|
||||
var frontKeyFrames = [];
|
||||
var backKeyFrames = [];
|
||||
//for non mirrored joints such as the spine
|
||||
var singleKeyFrames = [];
|
||||
//Pitch, yaw, and roll for the joints
|
||||
var frontAngles = [];
|
||||
var backAngles = [];
|
||||
//for non mirrored joints such as the spine
|
||||
var singleAngles = [];
|
||||
|
||||
|
||||
|
||||
//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;
|
||||
|
||||
// ******************************* Animation Is Defined Below *************************************
|
||||
|
||||
var NUM_FRAMES = 2;
|
||||
for (var i = 0; i < NUM_FRAMES; i++) {
|
||||
frontAngles[i] = [];
|
||||
backAngles[i] = [];
|
||||
singleAngles[i] = [];
|
||||
frontKeyFrames[i] = [];
|
||||
backKeyFrames[i] = [];
|
||||
singleKeyFrames[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 = [JOINT_R_HIP, JOINT_L_HIP, JOINT_R_KNEE, JOINT_L_KNEE, JOINT_R_ARM, JOINT_L_ARM, JOINT_R_FOREARM, JOINT_L_FOREARM, JOINT_SPINE];
|
||||
|
||||
//Joint indices for joints that are duplicated, such as arms, It must match JOINT_ORDER
|
||||
var HIP = 0;
|
||||
var KNEE = 1;
|
||||
var ARM = 2;
|
||||
var FOREARM = 3;
|
||||
//Joint indices for single joints
|
||||
var SPINE = 0;
|
||||
|
||||
//Symmetry multipliers for dthe left half [pitch, roll, yaw]. -1 means reflect, 1 means no reflect
|
||||
var SYMMETRY = [];
|
||||
SYMMETRY[HIP] = [1, -1, -1];
|
||||
SYMMETRY[KNEE] = [1, -1, -1];
|
||||
SYMMETRY[ARM] = [1, -1, -1];
|
||||
SYMMETRY[FOREARM] = [1, -1, -1];
|
||||
|
||||
//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
|
||||
frontAngles[0][HIP] = [30.0, 0.0, 8.0];
|
||||
frontAngles[0][KNEE] = [-15.0, 0.0, 0.0];
|
||||
frontAngles[0][ARM] = [85.0, -25.0, 0.0];
|
||||
frontAngles[0][FOREARM] = [0.0, 0.0, -15.0];
|
||||
|
||||
backAngles[0][HIP] = [-15, 0.0, 8.0];
|
||||
backAngles[0][KNEE] = [-28, 0.0, 0.0];
|
||||
backAngles[0][ARM] = [85.0, 20.0, 0.0];
|
||||
backAngles[0][FOREARM] = [10.0, 0.0, -25.0];
|
||||
|
||||
singleAngles[0][SPINE] = [-0.0, 0.0, 0.0];
|
||||
|
||||
//Legs Passing
|
||||
frontAngles[1][HIP] = [6.0, 0.0, 8.0];
|
||||
frontAngles[1][KNEE] = [-12.0, 0.0, 0.0];
|
||||
frontAngles[1][ARM] = [85.0, 0.0, 0.0];
|
||||
frontAngles[1][FOREARM] = [0.0, 0.0, -15.0];
|
||||
|
||||
backAngles[1][HIP] = [10.0, 0.0, 8.0];
|
||||
backAngles[1][KNEE] = [-55.0, 0.0, 0.0];
|
||||
backAngles[1][ARM] = [85.0, 0.0, 0.0];
|
||||
backAngles[1][FOREARM] = [0.0, 0.0, -15.0];
|
||||
|
||||
singleAngles[1][SPINE] = [0.0, 0.0, 0.0];
|
||||
|
||||
// ******************************* Animation Is Defined Above *************************************
|
||||
|
||||
//Actual keyframes for the animation
|
||||
var walkKeyFrames = [];
|
||||
//Generate quaternions from the angles
|
||||
for (var i = 0; i < frontAngles.length; i++) {
|
||||
for (var j = 0; j < frontAngles[i].length; j++) {
|
||||
frontKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(frontAngles[i][j][0], frontAngles[i][j][1], frontAngles[i][j][2]);
|
||||
backKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(SYMMETRY[j][0] * backAngles[i][j][0], SYMMETRY[j][1] * backAngles[i][j][1], SYMMETRY[j][2] * backAngles[i][j][2]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < singleAngles.length; i++) {
|
||||
for (var j = 0; j < singleAngles[i].length; j++) {
|
||||
singleKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(singleAngles[i][j][0], singleAngles[i][j][1], singleAngles[i][j][2]);
|
||||
}
|
||||
}
|
||||
walkKeyFrames[0] = new WalkKeyFrame(frontKeyFrames[0], backKeyFrames[0], singleKeyFrames[0]);
|
||||
walkKeyFrames[1] = new WalkKeyFrame(frontKeyFrames[1], backKeyFrames[1], singleKeyFrames[1]);
|
||||
|
||||
//Generate mirrored quaternions for the other half of the body
|
||||
for (var i = 0; i < frontAngles.length; i++) {
|
||||
for (var j = 0; j < frontAngles[i].length; j++) {
|
||||
frontKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(SYMMETRY[j][0] * frontAngles[i][j][0], SYMMETRY[j][1] * frontAngles[i][j][1], SYMMETRY[j][2] * frontAngles[i][j][2]);
|
||||
backKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(backAngles[i][j][0], backAngles[i][j][1], backAngles[i][j][2]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < singleAngles.length; i++) {
|
||||
for (var j = 0; j < singleAngles[i].length; j++) {
|
||||
singleKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(-singleAngles[i][j][0], -singleAngles[i][j][1], -singleAngles[i][j][2]);
|
||||
}
|
||||
}
|
||||
walkKeyFrames[2] = new WalkKeyFrame(backKeyFrames[0], frontKeyFrames[0], singleKeyFrames[0]);
|
||||
walkKeyFrames[3] = new WalkKeyFrame(backKeyFrames[1], frontKeyFrames[1], singleKeyFrames[1]);
|
||||
|
||||
//Hook up pointers to the next keyframe
|
||||
for (var i = 0; i < walkKeyFrames.length - 1; i++) {
|
||||
walkKeyFrames[i].nextFrame = walkKeyFrames[i+1];
|
||||
}
|
||||
walkKeyFrames[walkKeyFrames.length-1].nextFrame = walkKeyFrames[0];
|
||||
|
||||
//Set up the bezier curve control points using technique described at
|
||||
//https://www.cs.tcd.ie/publications/tech-reports/reports.94/TCD-CS-94-18.pdf
|
||||
//Set up all C1
|
||||
for (var i = 0; i < walkKeyFrames.length; i++) {
|
||||
walkKeyFrames[i].nextFrame.controlPoints = [];
|
||||
for (var j = 0; j < walkKeyFrames[i].rotations.length; j++) {
|
||||
walkKeyFrames[i].nextFrame.controlPoints[j] = [];
|
||||
var R = Quat.slerp(walkKeyFrames[i].rotations[j], walkKeyFrames[i].nextFrame.rotations[j], 2.0);
|
||||
var T = Quat.slerp(R, walkKeyFrames[i].nextFrame.nextFrame.rotations[j], 0.5);
|
||||
walkKeyFrames[i].nextFrame.controlPoints[j][0] = Quat.slerp(walkKeyFrames[i].nextFrame.rotations[j], T, 0.33333);
|
||||
}
|
||||
}
|
||||
//Set up all C2
|
||||
for (var i = 0; i < walkKeyFrames.length; i++) {
|
||||
for (var j = 0; j < walkKeyFrames[i].rotations.length; j++) {
|
||||
walkKeyFrames[i].controlPoints[j][1] = Quat.slerp(walkKeyFrames[i].nextFrame.rotations[j], walkKeyFrames[i].nextFrame.controlPoints[j][0], -1.0);
|
||||
}
|
||||
}
|
||||
//DeCasteljau evaluation to evaluate the bezier curve
|
||||
function deCasteljau(k1, k2, c1, c2, f) {
|
||||
var a = Quat.slerp(k1, c1, f);
|
||||
var b = Quat.slerp(c1, c2, f);
|
||||
var c = Quat.slerp(c2, k2, f);
|
||||
var d = Quat.slerp(a, b, f);
|
||||
var e = Quat.slerp(b, c, f);
|
||||
return Quat.slerp(d, e, f);
|
||||
}
|
||||
|
||||
var currentFrame = 0;
|
||||
|
||||
var walkTime = 0.0;
|
||||
var walkFrequency = 3.0;
|
||||
|
||||
function keepWalking(deltaTime) {
|
||||
|
||||
walkTime += walkFrequency * deltaTime;
|
||||
if (walkTime > 1.0) {
|
||||
walkTime = 0.0;
|
||||
currentFrame++;
|
||||
if (currentFrame > 3) {
|
||||
currentFrame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var frame = walkKeyFrames[currentFrame];
|
||||
|
||||
for (var i = 0; i < JOINT_ORDER.length; i++) {
|
||||
Avatar.setJointData(JOINT_ORDER[i], deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], walkTime));
|
||||
}
|
||||
}
|
||||
|
||||
function stopWalking() {
|
||||
Avatar.clearJointData(JOINT_R_HIP);
|
||||
Avatar.clearJointData(JOINT_R_KNEE);
|
||||
Avatar.clearJointData(JOINT_L_HIP);
|
||||
Avatar.clearJointData(JOINT_L_KNEE);
|
||||
}
|
||||
|
||||
var trailingAverageLoudness = 0;
|
||||
var MAX_SAMPLE = 32767;
|
||||
var DB_METER_BASE = Math.log(MAX_SAMPLE);
|
||||
|
||||
var RAND_RATIO_LAST = getRandomFloat(0.1, 0.3);
|
||||
var RAND_TRAILING = 1 - RAND_RATIO_LAST;
|
||||
|
||||
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 jointMapping = null;
|
||||
var frameIndex = 0.0;
|
||||
var isPlayingDanceAnimation = false;
|
||||
var randomAnimation = null;
|
||||
var animationLoops = 1;
|
||||
var forcedMove = false;
|
||||
|
||||
var FRAME_RATE = 30.0;
|
||||
|
||||
var wasMovingLastFrame = false;
|
||||
var wasDancing = false;
|
||||
|
||||
function danceAnimation(deltaTime) {
|
||||
|
||||
var flooredFrame = Math.floor(frameIndex);
|
||||
|
||||
if (jointMapping === null || flooredFrame >= randomAnimation.frames.length * animationLoops) {
|
||||
// we've run our animation for our number of loops, start a new one
|
||||
frameIndex = 0.0;
|
||||
jointMapping = null;
|
||||
randomAnimation = null;
|
||||
}
|
||||
|
||||
if (isMoving || (!wasMovingLastFrame && frameIndex === 0)) {
|
||||
if (!isMoving) {
|
||||
forcedMove = true;
|
||||
possiblyStopDancing();
|
||||
}
|
||||
|
||||
wasMovingLastFrame = true;
|
||||
handleWalking();
|
||||
} else {
|
||||
if (jointMapping === null) {
|
||||
// pick a random animation
|
||||
var whichAnimation = Math.floor((Math.random() * animations.length) % animations.length);
|
||||
randomAnimation = animations[whichAnimation];
|
||||
|
||||
var avatarJointNames = Avatar.jointNames;
|
||||
var animationJointNames = randomAnimation.jointNames;
|
||||
if (avatarJointNames === 0 || animationJointNames.length === 0) {
|
||||
return;
|
||||
}
|
||||
jointMapping = new Array(avatarJointNames.length);
|
||||
for (var i = 0; i < avatarJointNames.length; i++) {
|
||||
jointMapping[i] = animationJointNames.indexOf(avatarJointNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
frameIndex += deltaTime * FRAME_RATE;
|
||||
var frames = randomAnimation.frames;
|
||||
var rotations = frames[flooredFrame % frames.length].rotations;
|
||||
for (var j = 0; j < jointMapping.length; j++) {
|
||||
var rotationIndex = jointMapping[j];
|
||||
if (rotationIndex != -1) {
|
||||
Avatar.setJointData(j, rotations[rotationIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
wasMovingLastFrame = false;
|
||||
wasDancing = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleHeadTurn() {
|
||||
if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) {
|
||||
targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE);
|
||||
isTurningHead = true;
|
||||
} else {
|
||||
Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * PITCH_RATE;
|
||||
if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE) {
|
||||
isTurningHead = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentShoulderQuat = Avatar.getJointRotation(SHOULDER_JOINT_NUMBER);
|
||||
var targetShoulderQuat = currentShoulderQuat;
|
||||
var idleShoulderQuat = currentShoulderQuat;
|
||||
var currentSpineQuat = Avatar.getJointRotation(JOINT_SPINE);
|
||||
var targetSpineQuat = currentSpineQuat;
|
||||
var idleSpineQuat = currentSpineQuat;
|
||||
var currentElbowQuat = Avatar.getJointRotation(ELBOW_JOINT_NUMBER);
|
||||
var targetElbowQuat = currentElbowQuat;
|
||||
var idleElbowQuat = currentElbowQuat;
|
||||
|
||||
function handleWalking(deltaTime) {
|
||||
if (forcedMove || (!isMoving && Math.random() < CHANCE_OF_MOVING)) {
|
||||
// Set new target location
|
||||
targetDirection = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 }));
|
||||
var front = Quat.getFront(targetDirection);
|
||||
|
||||
targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL)));
|
||||
|
||||
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) {
|
||||
keepWalking(deltaTime);
|
||||
// Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE));
|
||||
Avatar.orientation = Quat.slerp(Avatar.orientation, targetDirection, TURN_RATE);
|
||||
var diff = Vec3.subtract(Avatar.position, targetPosition);
|
||||
diff.y = 0.0;
|
||||
|
||||
wasMovingLastFrame = true;
|
||||
|
||||
if (Vec3.length(diff) < STOP_TOLERANCE) {
|
||||
isMoving = false;
|
||||
stopWalking();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleTalking() {
|
||||
if (Math.random() < CHANCE_OF_SOUND) {
|
||||
playRandomSound();
|
||||
}
|
||||
}
|
||||
|
||||
function changePelvisHeight(newHeight) {
|
||||
var newPosition = Avatar.position;
|
||||
newPosition.y = newHeight;
|
||||
Avatar.position = newPosition;
|
||||
}
|
||||
|
||||
function possiblyStopDancing() {
|
||||
if (wasDancing) {
|
||||
for (var j = 0; j < Avatar.jointNames.length; j++) {
|
||||
Avatar.clearJointData(j);
|
||||
}
|
||||
|
||||
changePelvisHeight(Y_PELVIS);
|
||||
}
|
||||
}
|
||||
|
||||
function updateBehavior(deltaTime) {
|
||||
cumulativeTime += deltaTime;
|
||||
|
||||
if (AvatarList.containsAvatarWithDisplayName("mrdj")) {
|
||||
if (wasMovingLastFrame && !wasDancing) {
|
||||
isMoving = false;
|
||||
}
|
||||
|
||||
// we have a DJ, shouldn't we be dancing?
|
||||
jumpWithLoudness(deltaTime);
|
||||
danceAnimation(deltaTime);
|
||||
} else {
|
||||
// make sure we're not dancing anymore
|
||||
possiblyStopDancing();
|
||||
|
||||
wasDancing = false;
|
||||
|
||||
// no DJ, let's just chill on the dancefloor - randomly walking and talking
|
||||
handleHeadTurn();
|
||||
handleWalking(deltaTime);
|
||||
handleTalking();
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(updateBehavior);
|
|
@ -76,6 +76,10 @@ glm::quat Quat::squad(const glm::quat& q1, const glm::quat& q2, const glm::quat&
|
|||
return glm::squad(q1, q2, s1, s2, h);
|
||||
}
|
||||
|
||||
float Quat::dot(const glm::quat& q1, const glm::quat& q2) {
|
||||
return glm::dot(q1, q2);
|
||||
}
|
||||
|
||||
void Quat::print(const QString& lable, const glm::quat& q) {
|
||||
qDebug() << qPrintable(lable) << q.x << "," << q.y << "," << q.z << "," << q.w;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public slots:
|
|||
glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||
glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||
glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h);
|
||||
float dot(const glm::quat& q1, const glm::quat& q2);
|
||||
void print(const QString& lable, const glm::quat& q);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue