diff --git a/examples/particleBird.js b/examples/particleBird.js deleted file mode 100644 index 5241b3550b..0000000000 --- a/examples/particleBird.js +++ /dev/null @@ -1,202 +0,0 @@ -// -// particleBird.js -// examples -// -// Copyright 2014 High Fidelity, Inc. -// -// This sample script moves a voxel around like a bird and sometimes makes tweeting noises -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -function vLength(v) { - return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); -} -function printVector(v) { - print(v.x + ", " + v.y + ", " + v.z + "\n"); -} -// Create a random vector with individual lengths between a,b -function randVector(a, b) { - var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) }; - return rval; -} - -function vMinus(a, b) { - var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; - return rval; -} - -function vPlus(a, b) { - var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; - return rval; -} - -function vCopy(a, b) { - a.x = b.x; - a.y = b.y; - a.z = b.z; - return; -} - -// Returns a vector which is fraction of the way between a and b -function vInterpolate(a, b, fraction) { - var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction }; - return rval; -} - -// Decide what kind of bird we are -var tweet; -var color; -var size; -var which = Math.random(); -if (which < 0.2) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw"); - color = { r: 100, g: 50, b: 120 }; - size = 0.08; -} else if (which < 0.4) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/rosyfacedlovebird.raw"); - color = { r: 100, g: 150, b: 75 }; - size = 0.09; -} else if (which < 0.6) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/saysphoebe.raw"); - color = { r: 84, g: 121, b: 36 }; - size = 0.05; -} else if (which < 0.8) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); - color = { r: 23, g: 197, b: 230 }; - size = 0.12; -} else { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/westernscreechowl.raw"); - color = { r: 50, g: 67, b: 144 }; - size = 0.15; -} - - -var startTimeInSeconds = new Date().getTime() / 1000; - -var birdLifetime = 20; // lifetime of the bird in seconds! -var position = { x: 0, y: 0, z: 0 }; -var targetPosition = { x: 0, y: 0, z: 0 }; -var range = 1.0; // Over what distance in meters do you want your bird to fly around -var frame = 0; -var moving = false; -var tweeting = 0; -var moved = false; - -var CHANCE_OF_MOVING = 0.00; -var CHANCE_OF_FLAPPING = 0.05; -var CHANCE_OF_TWEETING = 0.05; -var START_HEIGHT_ABOVE_ME = 1.5; -var BIRD_GRAVITY = -0.1; -var BIRD_FLAP = 1.0; -var myPosition = MyAvatar.position; -var properties = { - lifetime: birdLifetime, - position: { x: myPosition.x, y: myPosition.y + START_HEIGHT_ABOVE_ME, z: myPosition.z }, - velocity: { x: 0, y: Math.random() * BIRD_FLAP, z: 0 }, - gravity: { x: 0, y: BIRD_GRAVITY, z: 0 }, - radius : 0.1, - color: { red: 0, - green: 255, - blue: 0 } -}; -var range = 1.0; // Distance around avatar where I can move -// Create the actual bird -var particleID = Particles.addParticle(properties); -function moveBird(deltaTime) { - - // check to see if we've been running long enough that our bird is dead - var nowTimeInSeconds = new Date().getTime() / 1000; - if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) { - - print("our bird is dying, stop our script"); - Script.stop(); - return; - } - - myPosition = MyAvatar.position; - frame++; - if (frame % 3 == 0) { - // Tweeting behavior - if (tweeting == 0) { - if (Math.random() < CHANCE_OF_TWEETING) { - //print("tweet!" + "\n"); - var options = new AudioInjectionOptions(); - options.position = position; - options.volume = 0.75; - Audio.playSound(tweet, options); - tweeting = 10; - } - } else { - tweeting -= 1; - } - if (Math.random() < CHANCE_OF_FLAPPING) { - // Add a little upward impulse to our bird - // TODO: Get velocity - // - var newProperties = { - velocity: { x:0.0, y: Math.random() * BIRD_FLAP, z: 0.0 } - }; - Particles.editParticle(particleID, newProperties); - print("flap!"); - } - // Moving behavior - if (moving == false) { - if (Math.random() < CHANCE_OF_MOVING) { - targetPosition = randVector(-range, range); - targetPosition = vPlus(targetPosition, myPosition); - - if (targetPosition.x < 0) { - targetPosition.x = 0; - } - if (targetPosition.y < 0) { - targetPosition.y = 0; - } - if (targetPosition.z < 0) { - targetPosition.z = 0; - } - if (targetPosition.x > TREE_SCALE) { - targetPosition.x = TREE_SCALE; - } - if (targetPosition.y > TREE_SCALE) { - targetPosition.y = TREE_SCALE; - } - if (targetPosition.z > TREE_SCALE) { - targetPosition.z = TREE_SCALE; - } - //printVector(position); - moving = true; - } - } - if (moving) { - position = vInterpolate(position, targetPosition, 0.5); - if (vLength(vMinus(position, targetPosition)) < (size / 5.0)) { - moved = false; - moving = false; - } else { - moved = true; - } - } - if (moved || (tweeting > 0)) { - if (tweeting > 0) { - var newProperties = { - position: position, - radius : size * 1.5, - color: { red: Math.random() * 255, green: 0, blue: 0 } - }; - } else { - var newProperties = { - position: position, - radius : size, - color: { red: color.r, green: color.g, blue: color.b } - }; - } - Particles.editParticle(particleID, newProperties); - moved = false; - } - } -} - -// register the call back so it fires before each data send -Script.update.connect(moveBird); diff --git a/examples/particleBirds.js b/examples/particleBirds.js new file mode 100644 index 0000000000..9492a321ce --- /dev/null +++ b/examples/particleBirds.js @@ -0,0 +1,199 @@ +// +// particleBirds.js +// examples +// +// Created by Benjamin Arnold on May 29, 2014 +// Copyright 2014 High Fidelity, Inc. +// +// This sample script creates a swarm of tweeting bird particles that fly around the avatar. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Multiply vector by scalar +function vScalarMult(v, s) { + var rval = { x: v.x * s, y: v.y * s, z: v.z * s }; + return rval; +} + +function printVector(v) { + print(v.x + ", " + v.y + ", " + v.z + "\n"); +} +// Create a random vector with individual lengths between a,b +function randVector(a, b) { + var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) }; + return rval; +} + +// Returns a vector which is fraction of the way between a and b +function vInterpolate(a, b, fraction) { + var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction }; + return rval; +} + +var startTimeInSeconds = new Date().getTime() / 1000; + +var birdLifetime = 20; // lifetime of the birds in seconds! +var range = 1.0; // Over what distance in meters do you want the flock to fly around +var frame = 0; + +var CHANCE_OF_MOVING = 0.1; +var CHANCE_OF_TWEETING = 0.05; +var BIRD_GRAVITY = -0.1; +var BIRD_FLAP_SPEED = 10.0; +var BIRD_VELOCITY = 0.5; +var myPosition = MyAvatar.position; + +var range = 1.0; // Distance around avatar where I can move + +// This is our Bird object +function Bird (particleID, tweetSound, targetPosition) { + this.particleID = particleID; + this.tweetSound = tweetSound; + this.previousFlapOffset = 0; + this.targetPosition = targetPosition; + this.moving = false; + this.tweeting = -1; +} + +// Array of birds +var birds = []; + +function addBird() +{ + // Decide what kind of bird we are + var tweet; + var color; + var size; + var which = Math.random(); + if (which < 0.2) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw"); + color = { red: 100, green: 50, blue: 120 }; + size = 0.08; + } else if (which < 0.4) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/rosyfacedlovebird.raw"); + color = { red: 100, green: 150, blue: 75 }; + size = 0.09; + } else if (which < 0.6) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/saysphoebe.raw"); + color = { red: 84, green: 121, blue: 36 }; + size = 0.05; + } else if (which < 0.8) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); + color = { red: 23, green: 197, blue: 230 }; + size = 0.12; + } else { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/westernscreechowl.raw"); + color = { red: 50, green: 67, blue: 144 }; + size = 0.15; + } + var properties = { + lifetime: birdLifetime, + position: Vec3.sum(randVector(-range, range), myPosition), + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: BIRD_GRAVITY, z: 0 }, + radius : size, + color: color + }; + + birds.push(new Bird(Particles.addParticle(properties), tweet, properties.position)); +} + +var numBirds = 30; + +// Generate the birds +for (var i = 0; i < numBirds; i++) { + addBird(); +} + +// Main update function +function updateBirds(deltaTime) { + + // Check to see if we've been running long enough that our birds are dead + var nowTimeInSeconds = new Date().getTime() / 1000; + if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) { + + print("our birds are dying, stop our script"); + Script.stop(); + return; + } + + frame++; + // Only update every third frame + if ((frame % 3) == 0) { + myPosition = MyAvatar.position; + + // Update all the birds + for (var i = 0; i < numBirds; i++) { + particleID = birds[i].particleID; + var properties = Particles.getParticleProperties(particleID); + + // Tweeting behavior + if (birds[i].tweeting == 0) { + if (Math.random() < CHANCE_OF_TWEETING) { + var options = new AudioInjectionOptions(); + options.position = properties.position; + options.volume = 0.75; + Audio.playSound(birds[i].tweetSound, options); + birds[i].tweeting = 10; + } + } else { + birds[i].tweeting -= 1; + } + + // Begin movement by getting a target + if (birds[i].moving == false) { + if (Math.random() < CHANCE_OF_MOVING) { + var targetPosition = Vec3.sum(randVector(-range, range), myPosition); + + if (targetPosition.x < 0) { + targetPosition.x = 0; + } + if (targetPosition.y < 0) { + targetPosition.y = 0; + } + if (targetPosition.z < 0) { + targetPosition.z = 0; + } + if (targetPosition.x > TREE_SCALE) { + targetPosition.x = TREE_SCALE; + } + if (targetPosition.y > TREE_SCALE) { + targetPosition.y = TREE_SCALE; + } + if (targetPosition.z > TREE_SCALE) { + targetPosition.z = TREE_SCALE; + } + + birds[i].targetPosition = targetPosition; + + birds[i].moving = true; + } + } + // If we are moving, move towards the target + if (birds[i].moving) { + var desiredVelocity = Vec3.subtract(birds[i].targetPosition, properties.position); + desiredVelocity = vScalarMult(Vec3.normalize(desiredVelocity), BIRD_VELOCITY); + + properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.2); + // If we are near the target, we should get a new target + if (Vec3.length(Vec3.subtract(properties.position, birds[i].targetPosition)) < (properties.radius / 5.0)) { + birds[i].moving = false; + } + } + + // Use a cosine wave offset to make it look like its flapping. + var offset = Math.cos(nowTimeInSeconds * BIRD_FLAP_SPEED) * properties.radius; + properties.position.y = properties.position.y + (offset - birds[i].previousFlapOffset); + // Change position relative to previous offset. + birds[i].previousFlapOffset = offset; + + // Update the particle + Particles.editParticle(particleID, properties); + } + } +} + +// register the call back so it fires before each data send +Script.update.connect(updateBirds); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0e731fde79..5f9a57f52b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2789,16 +2789,19 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation(); glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation(); - // get the eye positions relative to the neck and use them to set the face translation - glm::vec3 leftEyePosition, rightEyePosition; - _myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3()); - _myAvatar->getHead()->getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); - _myAvatar->getHead()->getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); - - // get the neck position relative to the body and use it to set the skeleton translation + // get the neck position so we can translate the face relative to it glm::vec3 neckPosition; _myAvatar->getSkeletonModel().setTranslation(glm::vec3()); _myAvatar->getSkeletonModel().getNeckPosition(neckPosition); + + // get the eye position relative to the body + glm::vec3 eyePosition = _myAvatar->getHead()->calculateAverageEyePosition(); + float eyeHeight = eyePosition.y - _myAvatar->getPosition().y; + + // set the translation of the face relative to the neck position + _myAvatar->getHead()->getFaceModel().setTranslation(neckPosition - glm::vec3(0, eyeHeight, 0)); + + // translate the neck relative to the face _myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() - neckPosition);