From 4da6af96bb808491f6e8fa5765597d7721a7d941 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 25 Feb 2014 22:08:56 -0800 Subject: [PATCH 1/3] Bots will move --- examples/bot.js | 34 +++++++++++++++++++++++++++++----- examples/editVoxels.js | 3 ++- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/examples/bot.js b/examples/bot.js index c8280e063d..504f16826e 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -17,12 +17,19 @@ function getRandomInt (min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } -// choose a random x and y in the range of 0 to 50 -positionX = getRandomFloat(0, 18); -positionZ = getRandomFloat(0, 18); +var CHANCE_OF_MOVING = 0.05; +var isMoving = false; + +var STARTING_RANGE = 18.0; +var MOVE_RANGE = 1.0; +var STOP_TOLERANCE = 0.05; +var MOVE_RATE = 0.10; +var firstPosition = { x: getRandomFloat(0, STARTING_RANGE), y: 0, z: getRandomFloat(0, STARTING_RANGE) }; +var targetPosition = { x: 0, y: 0, z: 0 }; + // change the avatar's position to the random one -Avatar.position = {x: positionX, y: 0, z: positionZ}; +Avatar.position = firstPosition; // pick an integer between 1 and 20 for the face model for this bot botNumber = getRandomInt(1, 100); @@ -52,4 +59,21 @@ Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/me Avatar.skeletonModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + ".fst"; Avatar.billboardURL = "https://dl.dropboxusercontent.com/u/1864924/bot-billboard.png"; -Agent.isAvatar = true; \ No newline at end of file +Agent.isAvatar = true; + +function updateBehavior() { + if (!isMoving && (Math.random() < CHANCE_OF_MOVING)) { + // Set new target location + targetPosition = { x: Avatar.position.x + getRandomFloat(-MOVE_RANGE, MOVE_RANGE), + y: Avatar.position.y, + z: Avatar.position.z + getRandomFloat(-MOVE_RANGE, MOVE_RANGE) }; + isMoving = true; + } else { + + Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE)); + if (Vec3.length(Vec3.subtract(Avatar.position, targetPosition)) < STOP_TOLERANCE) { + isMoving = false; + } + } +} +Script.willSendVisualDataCallback.connect(updateBehavior); diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 53d3869075..f05cc85816 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -79,7 +79,8 @@ var addSound3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-publi var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+delete+2.raw"); var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+edit+2.raw"); var clickSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Switches+and+sliders/toggle+switch+-+medium.raw"); -var audioOptions = new AudioInjectionOptions();
 +var audioOptions = new AudioInjectionOptions(); + audioOptions.volume = 0.5; audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0 } ); // start with audio slightly above the avatar From 0a1e840d10b34bcb7537876d503b9892309df821 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 26 Feb 2014 13:50:04 -0800 Subject: [PATCH 2/3] Bot.js NPCs wander around and make sounds --- examples/bot.js | 74 +++++++++++++++++++++++++--- interface/src/Util.cpp | 29 ----------- interface/src/Util.h | 2 - libraries/script-engine/src/Quat.cpp | 7 ++- libraries/script-engine/src/Quat.h | 1 + libraries/shared/src/SharedUtil.cpp | 32 ++++++++++++ libraries/shared/src/SharedUtil.h | 2 + 7 files changed, 108 insertions(+), 39 deletions(-) diff --git a/examples/bot.js b/examples/bot.js index 504f16826e..553c18bf92 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -3,6 +3,7 @@ // hifi // // Created by Stephen Birarda on 2/20/14. +// Modified by Philip on 2/26/14 // Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // // This is an example script that demonstrates an NPC avatar. @@ -17,16 +18,53 @@ function getRandomInt (min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } -var CHANCE_OF_MOVING = 0.05; +var CHANCE_OF_MOVING = 0.005; +var CHANCE_OF_SOUND = 0.005; +var CHANCE_OF_HEAD_TURNING = 0.05; +var CHANCE_OF_BIG_MOVE = 0.1; + var isMoving = false; +var isTurningHead = false; var STARTING_RANGE = 18.0; -var MOVE_RANGE = 1.0; +var MAX_RANGE = 25.0; +var MOVE_RANGE_SMALL = 0.5; +var MOVE_RANGE_BIG = 5.0; +var TURN_RANGE = 45.0; var STOP_TOLERANCE = 0.05; -var MOVE_RATE = 0.10; +var MOVE_RATE = 0.05; +var TURN_RATE = 0.15; +var PITCH_RATE = 0.20; +var PITCH_RANGE = 30.0; + + var firstPosition = { x: getRandomFloat(0, STARTING_RANGE), y: 0, z: getRandomFloat(0, STARTING_RANGE) }; 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 sounds = []; +sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh1.raw")); +sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh2.raw")); +sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh3.raw")); +sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh4.raw")); +sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh5.raw")); +sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh6.raw")); +sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh7.raw")); +sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh8.raw")); + +// Play a random sound from a list of conversational audio clips +function playRandomSound(position) { + var whichSound = Math.floor((Math.random() * sounds.length) % sounds.length); + var audioOptions = new AudioInjectionOptions(); + audioOptions.volume = 0.25 + (Math.random() * 0.75); + audioOptions.position = position; + Audio.playSound(sounds[whichSound], audioOptions); + print("PlaySound " + whichSound); +} // change the avatar's position to the random one Avatar.position = firstPosition; @@ -62,18 +100,40 @@ Avatar.billboardURL = "https://dl.dropboxusercontent.com/u/1864924/bot-billboard Agent.isAvatar = true; function updateBehavior() { + if (Math.random() < CHANCE_OF_SOUND) { + playRandomSound(Avatar.position); + } + + 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; + } + } if (!isMoving && (Math.random() < CHANCE_OF_MOVING)) { // Set new target location - targetPosition = { x: Avatar.position.x + getRandomFloat(-MOVE_RANGE, MOVE_RANGE), - y: Avatar.position.y, - z: Avatar.position.z + getRandomFloat(-MOVE_RANGE, MOVE_RANGE) }; + targetDirection = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 })); + var front = Quat.getFront(targetDirection); + if (Math.random() < CHANCE_OF_BIG_MOVE) { + targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_BIG))); + } else { + targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL))); + } isMoving = true; } else { - Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE)); + Avatar.orientation = Quat.mix(Avatar.orientation, targetDirection, TURN_RATE); if (Vec3.length(Vec3.subtract(Avatar.position, targetPosition)) < STOP_TOLERANCE) { isMoving = false; } } + if (Vec3.length(Avatar.position) > MAX_RANGE) { + // Don't let our happy little person get out of the cage + isMoving = false; + Avatar.position = { x: 0, y: 0, z: 0 }; + } } Script.willSendVisualDataCallback.connect(updateBehavior); diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index f601618eae..2e1ed98b87 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -104,36 +104,7 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { return glm::angleAxis(angle, axis); } -// Safe version of glm::mix; based on the code in Nick Bobick's article, -// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, -// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) -glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) { - float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; - float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1; - // adjust signs if necessary - if (cosa < 0.0f) { - cosa = -cosa; - ox = -ox; - oy = -oy; - oz = -oz; - ow = -ow; - } - - // calculate coefficients; if the angle is too close to zero, we must fall back - // to linear interpolation - if ((1.0f - cosa) > EPSILON) { - float angle = acosf(cosa), sina = sinf(angle); - s0 = sinf((1.0f - proportion) * angle) / sina; - s1 = sinf(proportion * angle) / sina; - - } else { - s0 = 1.0f - proportion; - s1 = proportion; - } - - return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz)); -} glm::vec3 extractTranslation(const glm::mat4& matrix) { return glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]); diff --git a/interface/src/Util.h b/interface/src/Util.h index 04403aea16..a89a01e71a 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -45,8 +45,6 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2); glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); -glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); - glm::vec3 extractTranslation(const glm::mat4& matrix); void setTranslation(glm::mat4& matrix, const glm::vec3& translation); diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 2f1c39f9e3..1019276d37 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -13,8 +13,10 @@ #include #include +#include #include "Quat.h" + glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) { return q1 * q2; } @@ -31,7 +33,6 @@ glm::quat Quat::inverse(const glm::quat& q) { return glm::inverse(q); } - glm::vec3 Quat::getFront(const glm::quat& orientation) { return orientation * IDENTITY_FRONT; } @@ -52,3 +53,7 @@ glm::quat Quat::angleAxis(float angle, const glm::vec3& v) { return glm::angleAxis(angle, v); } +glm::quat Quat::mix(const glm::quat& q1, const glm::quat& q2, float alpha) { + return safeMix(q1, q2, alpha); +} + diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 867069d6d6..ac12aaa351 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -29,6 +29,7 @@ public slots: glm::vec3 getUp(const glm::quat& orientation); glm::vec3 safeEulerAngles(const glm::quat& orientation); glm::quat angleAxis(float angle, const glm::vec3& v); + glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha); }; #endif /* defined(__hifi__Quat__) */ diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index f22c81a71a..2c531bab91 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -65,6 +65,38 @@ bool shouldDo(float desiredInterval, float deltaTime) { return randFloat() < deltaTime / desiredInterval; } +// Safe version of glm::mix; based on the code in Nick Bobick's article, +// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, +// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) +glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) { + float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; + float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1; + + // adjust signs if necessary + if (cosa < 0.0f) { + cosa = -cosa; + ox = -ox; + oy = -oy; + oz = -oz; + ow = -ow; + } + + // calculate coefficients; if the angle is too close to zero, we must fall back + // to linear interpolation + if ((1.0f - cosa) > EPSILON) { + float angle = acosf(cosa), sina = sinf(angle); + s0 = sinf((1.0f - proportion) * angle) / sina; + s1 = sinf(proportion * angle) / sina; + + } else { + s0 = 1.0f - proportion; + s1 = proportion; + } + + return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz)); +} + + void outputBufferBits(const unsigned char* buffer, int length, QDebug* continuedDebug) { diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 603a09a0c3..c18b2fca08 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -75,6 +75,8 @@ float randFloatInRange (float min,float max); unsigned char randomColorValue(int minimum); bool randomBoolean(); +glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); + bool shouldDo(float desiredInterval, float deltaTime); void outputBufferBits(const unsigned char* buffer, int length, QDebug* continuedDebug = NULL); From b27e9b03d4ae01c33e68c337e4b1d90a75dea86d Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 26 Feb 2014 14:15:00 -0800 Subject: [PATCH 3/3] removed bad include --- libraries/script-engine/src/Quat.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 1019276d37..7271f06af6 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -13,7 +13,6 @@ #include #include -#include #include "Quat.h"