From cf9365e616c66fd4a4154d74e6ea7c394cdc12eb Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 16 Jul 2015 22:46:41 -0700 Subject: [PATCH 1/5] Add new sample script with flocking --- examples/FlockOfbirds.js | 258 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 examples/FlockOfbirds.js diff --git a/examples/FlockOfbirds.js b/examples/FlockOfbirds.js new file mode 100644 index 0000000000..a3ecd77311 --- /dev/null +++ b/examples/FlockOfbirds.js @@ -0,0 +1,258 @@ +// +// flockOfbirds.js +// examples +// +// Copyright 2014 High Fidelity, Inc. +// Creates a flock of birds that fly around and chirp, staying inside the corners of the box defined +// at the start of the script. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + + +// The area over which the birds will fly +var lowerCorner = { x: 1, y: 1, z: 1 }; +var upperCorner = { x: 10, y: 10, z: 10 }; +var STARTING_FRACTION = 0.25; + +var NUM_BIRDS = 50; +var playSounds = true; +var SOUND_PROBABILITY = 0.001; +var numPlaying = 0; +var BIRD_SIZE = 0.08; +var BIRD_MASTER_VOLUME = 0.1; +var FLAP_PROBABILITY = 0.005; +var RANDOM_FLAP_VELOCITY = 1.0; +var FLAP_UP = 1.0; +var BIRD_GRAVITY = -0.5; +var LINEAR_DAMPING = 0.2; +var FLAP_FALLING_PROBABILITY = 0.025; +var MIN_ALIGNMENT_VELOCITY = 0.0; +var MAX_ALIGNMENT_VELOCITY = 1.0; +var VERTICAL_ALIGNMENT_COUPLING = 0.0; +var ALIGNMENT_FORCE = 1.5; +var COHESION_FORCE = 1.0; +var MAX_COHESION_VELOCITY = 0.5; + +var floor = false; +var MAKE_FLOOR = false; + +var averageVelocity = { x: 0, y: 0, z: 0 }; +var averagePosition = { x: 0, y: 0, z: 0 }; + +var birds = []; +var playing = []; + +function randomVector(scale) { + return { x: Math.random() * scale - scale / 2.0, y: Math.random() * scale - scale / 2.0, z: Math.random() * scale - scale / 2.0 }; +} + +function updateBirds(deltaTime) { + var sumVelocity = { x: 0, y: 0, z: 0 }; + var sumPosition = { x: 0, y: 0, z: 0 }; + var birdPositionsCounted = 0; + var birdVelocitiesCounted = 0; + for (var i = 0; i < birds.length; i++) { + if (birds[i].entityId) { + var properties = Entities.getEntityProperties(birds[i].entityId); + // If Bird has been deleted, bail + if (properties.id != birds[i].entityId) { + birds[i].entityId = false; + return; + } + // Sum up average position and velocity + if (Vec3.length(properties.velocity) > MIN_ALIGNMENT_VELOCITY) { + sumVelocity = Vec3.sum(sumVelocity, properties.velocity); + birdVelocitiesCounted += 1; + } + sumPosition = Vec3.sum(sumPosition, properties.position); + birdPositionsCounted += 1; + + var downwardSpeed = (properties.velocity.y < 0) ? -properties.velocity.y : 0.0; + if ((properties.position.y < upperCorner.y) && (Math.random() < (FLAP_PROBABILITY + (downwardSpeed * FLAP_FALLING_PROBABILITY)))) { + // More likely to flap if falling + var randomVelocity = randomVector(RANDOM_FLAP_VELOCITY); + randomVelocity.y = FLAP_UP + Math.random() * FLAP_UP; + + // Alignment Velocity + var alignmentVelocityMagnitude = Math.min(MAX_ALIGNMENT_VELOCITY, Vec3.length(Vec3.multiply(ALIGNMENT_FORCE, averageVelocity))); + var alignmentVelocity = Vec3.multiply(alignmentVelocityMagnitude, Vec3.normalize(averageVelocity)); + alignmentVelocity.y *= VERTICAL_ALIGNMENT_COUPLING; + + // Cohesion + var distanceFromCenter = Vec3.length(Vec3.subtract(averagePosition, properties.position)); + var cohesionVelocitySize = Math.min(distanceFromCenter * COHESION_FORCE, MAX_COHESION_VELOCITY); + var cohesionVelocity = Vec3.multiply(cohesionVelocitySize, Vec3.normalize(Vec3.subtract(averagePosition, properties.position))); + + var newVelocity = Vec3.sum(randomVelocity, Vec3.sum(alignmentVelocity, cohesionVelocity)); + + Entities.editEntity(birds[i].entityId, { velocity: Vec3.sum(properties.velocity, newVelocity) }); + + } + + // Check whether to play a chirp + if (playSounds && (!birds[i].audioId || !birds[i].audioId.isPlaying) && (Math.random() < ((numPlaying > 0) ? SOUND_PROBABILITY / numPlaying : SOUND_PROBABILITY))) { + var options = { + position: properties.position, + volume: BIRD_MASTER_VOLUME + }; + // Play chirp + if (birds[i].audioId) { + birds[i].audioId.setOptions(options); + birds[i].audioId.restart(); + } else { + birds[i].audioId = Audio.playSound(birds[i].sound, options); + } + numPlaying++; + // Change size + Entities.editEntity(birds[i].entityId, { dimensions: Vec3.multiply(1.5, properties.dimensions)}); + + } + + if (birds[i].audioId) { + // If bird is playing a chirp + if (!birds[i].audioId.isPlaying) { + // clear ID if playing has stopped + Entities.editEntity(birds[i].entityId, { dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }}); + birds[i].audioId = false; + numPlaying--; + print("num playing" + numPlaying); + } + } + + // Keep birds in their 'cage' + var bounce = false; + var newVelocity = properties.velocity; + var newPosition = properties.position; + if (properties.position.x < lowerCorner.x) { + newPosition.x = lowerCorner.x; + newVelocity.x *= -1.0; + bounce = true; + } else if (properties.position.x > upperCorner.x) { + newPosition.x = upperCorner.x; + newVelocity.x *= -1.0; + bounce = true; + } + if (properties.position.y < lowerCorner.y) { + newPosition.y = lowerCorner.y; + newVelocity.y *= -1.0; + bounce = true; + } else if (properties.position.y > upperCorner.y) { + newPosition.y = upperCorner.y; + newVelocity.y *= -1.0; + bounce = true; + } + if (properties.position.z < lowerCorner.z) { + newPosition.z = lowerCorner.z; + newVelocity.z *= -1.0; + bounce = true; + } else if (properties.position.z > upperCorner.z) { + newPosition.z = upperCorner.z; + newVelocity.z *= -1.0; + bounce = true; + } + if (bounce) { + Entities.editEntity(birds[i].entityId, { position: newPosition, velocity: newVelocity }); + } + } + } + // Update average velocity and position of flock + if (birdVelocitiesCounted > 0) { + averageVelocity = Vec3.multiply(1.0 / birdVelocitiesCounted, sumVelocity); + //print(Vec3.length(averageVelocity)); + } + if (birdPositionsCounted > 0) { + averagePosition = Vec3.multiply(1.0 / birdPositionsCounted, sumPosition); + } +} + +loadBirds(NUM_BIRDS); +if (MAKE_FLOOR) { + var FLOOR_THICKNESS = 0.05; + floor = Entities.addEntity({ type: "Box", position: { x: lowerCorner.x + (upperCorner.x - lowerCorner.x) / 2.0, + y: lowerCorner.y, + z: lowerCorner.z + (upperCorner.z - lowerCorner.z) / 2.0 }, + dimensions: { x: (upperCorner.x - lowerCorner.x), y: FLOOR_THICKNESS, z: (upperCorner.z - lowerCorner.z)}, + color: {red: 100, green: 100, blue: 100} + }); +} +// Connect a call back that happens every frame +Script.update.connect(updateBirds); + +// Delete our little friends if script is stopped +Script.scriptEnding.connect(function() { + for (var i = 0; i < birds.length; i++) { + Entities.deleteEntity(birds[i].entityId); + } + if (floor) { + Entities.deleteEntity(floor); + } +}); + +function loadBirds(howMany) { + var sound_filenames = ["bushtit_1.raw", "bushtit_2.raw", "bushtit_3.raw"]; + /* Here are more sounds/species you can use + , "mexicanWhipoorwill.raw", + "rosyfacedlovebird.raw", "saysphoebe.raw", "westernscreechowl.raw", "bandtailedpigeon.wav", "bridledtitmouse.wav", + "browncrestedflycatcher.wav", "commonnighthawk.wav", "commonpoorwill.wav", "doublecrestedcormorant.wav", + "gambelsquail.wav", "goldcrownedkinglet.wav", "greaterroadrunner.wav","groovebilledani.wav","hairywoodpecker.wav", + "housewren.wav","hummingbird.wav", "mountainchickadee.wav", "nightjar.wav", "piebilledgrieb.wav", "pygmynuthatch.wav", + "whistlingduck.wav", "woodpecker.wav"]; + */ + + var colors = [ + { red: 242, green: 207, blue: 013 }, + { red: 238, green: 94, blue: 11 }, + { red: 81, green: 30, blue: 7 }, + { red: 195, green: 176, blue: 81 }, + { red: 235, green: 190, blue: 152 }, + { red: 167, green: 99, blue: 52 }, + { red: 199, green: 122, blue: 108 }, + { red: 246, green: 220, blue: 189 }, + { red: 208, green: 145, blue: 65 }, + { red: 173, green: 120 , blue: 71 }, + { red: 132, green: 147, blue: 174 }, + { red: 164, green: 74, blue: 40 }, + { red: 131, green: 127, blue: 134 }, + { red: 209, green: 157, blue: 117 }, + { red: 205, green: 191, blue: 193 }, + { red: 193, green: 154, blue: 118 }, + { red: 205, green: 190, blue: 169 }, + { red: 199, green: 111, blue: 69 }, + { red: 221, green: 223, blue: 228 }, + { red: 115, green: 92, blue: 87 }, + { red: 214, green: 165, blue: 137 }, + { red: 160, green: 124, blue: 33 }, + { red: 117, green: 91, blue: 86 }, + { red: 113, green: 104, blue: 107 }, + { red: 216, green: 153, blue: 99 }, + { red: 242, green: 226, blue: 64 } + ]; + + var SOUND_BASE_URL = "http://public.highfidelity.io/sounds/Animals/"; + + for (var i = 0; i < howMany; i++) { + var whichBird = Math.floor(Math.random() * sound_filenames.length); + var position = { + x: lowerCorner.x + (upperCorner.x - lowerCorner.x) / 2.0 + (Math.random() - 0.5) * (upperCorner.x - lowerCorner.x) * STARTING_FRACTION, + y: lowerCorner.y + (upperCorner.y - lowerCorner.y) / 2.0 + (Math.random() - 0.5) * (upperCorner.y - lowerCorner.y) * STARTING_FRACTION, + z: lowerCorner.z + (upperCorner.z - lowerCorner.x) / 2.0 + (Math.random() - 0.5) * (upperCorner.z - lowerCorner.z) * STARTING_FRACTION + }; + + birds.push({ + sound: SoundCache.getSound(SOUND_BASE_URL + sound_filenames[whichBird]), + entityId: Entities.addEntity({ + type: "Sphere", + position: position, + dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }, + gravity: { x: 0, y: BIRD_GRAVITY, z: 0 }, + velocity: { x: 0, y: -0.1, z: 0 }, + linearDamping: LINEAR_DAMPING, + collisionsWillMove: true, + color: colors[whichBird] + }), + audioId: false + }); + } +} \ No newline at end of file From 5c29eb97efda45c0f81cdba65df6934f627cd8f6 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 16 Jul 2015 23:32:40 -0700 Subject: [PATCH 2/5] remove debug --- examples/FlockOfbirds.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/FlockOfbirds.js b/examples/FlockOfbirds.js index a3ecd77311..e15f6764a0 100644 --- a/examples/FlockOfbirds.js +++ b/examples/FlockOfbirds.js @@ -117,7 +117,6 @@ function updateBirds(deltaTime) { Entities.editEntity(birds[i].entityId, { dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }}); birds[i].audioId = false; numPlaying--; - print("num playing" + numPlaying); } } From db219905f3cce527a0822041cf8193c04d3397ec Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 17 Jul 2015 00:30:31 -0700 Subject: [PATCH 3/5] re-use audio injectors, don't check if isPlaying immediately after starting play out --- examples/FlockOfbirds.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/FlockOfbirds.js b/examples/FlockOfbirds.js index e15f6764a0..de30f2d374 100644 --- a/examples/FlockOfbirds.js +++ b/examples/FlockOfbirds.js @@ -108,14 +108,11 @@ function updateBirds(deltaTime) { // Change size Entities.editEntity(birds[i].entityId, { dimensions: Vec3.multiply(1.5, properties.dimensions)}); - } - - if (birds[i].audioId) { + } else if (birds[i].audioId) { // If bird is playing a chirp if (!birds[i].audioId.isPlaying) { // clear ID if playing has stopped Entities.editEntity(birds[i].entityId, { dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }}); - birds[i].audioId = false; numPlaying--; } } @@ -251,7 +248,8 @@ function loadBirds(howMany) { collisionsWillMove: true, color: colors[whichBird] }), - audioId: false + audioId: false, + isPlaying: false }); } } \ No newline at end of file From 689b0e257158a452c64c7e687b7e4ca2e4da64e5 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 17 Jul 2015 09:41:48 -0700 Subject: [PATCH 4/5] Improve to run correctly on startup as AC --- examples/FlockOfbirds.js | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/examples/FlockOfbirds.js b/examples/FlockOfbirds.js index de30f2d374..a8a5aafe6d 100644 --- a/examples/FlockOfbirds.js +++ b/examples/FlockOfbirds.js @@ -41,6 +41,8 @@ var MAKE_FLOOR = false; var averageVelocity = { x: 0, y: 0, z: 0 }; var averagePosition = { x: 0, y: 0, z: 0 }; +var birdsLoaded = false; + var birds = []; var playing = []; @@ -49,6 +51,14 @@ function randomVector(scale) { } function updateBirds(deltaTime) { + if (!Entities.serversExist() || !Entities.canRez()) { + return; + } + if (!birdsLoaded) { + loadBirds(NUM_BIRDS); + birdsLoaded = true; + return; + } var sumVelocity = { x: 0, y: 0, z: 0 }; var sumPosition = { x: 0, y: 0, z: 0 }; var birdPositionsCounted = 0; @@ -163,16 +173,6 @@ function updateBirds(deltaTime) { } } -loadBirds(NUM_BIRDS); -if (MAKE_FLOOR) { - var FLOOR_THICKNESS = 0.05; - floor = Entities.addEntity({ type: "Box", position: { x: lowerCorner.x + (upperCorner.x - lowerCorner.x) / 2.0, - y: lowerCorner.y, - z: lowerCorner.z + (upperCorner.z - lowerCorner.z) / 2.0 }, - dimensions: { x: (upperCorner.x - lowerCorner.x), y: FLOOR_THICKNESS, z: (upperCorner.z - lowerCorner.z)}, - color: {red: 100, green: 100, blue: 100} - }); -} // Connect a call back that happens every frame Script.update.connect(updateBirds); @@ -187,6 +187,8 @@ Script.scriptEnding.connect(function() { }); function loadBirds(howMany) { + while (!Entities.serversExist() || !Entities.canRez()) { + } var sound_filenames = ["bushtit_1.raw", "bushtit_2.raw", "bushtit_3.raw"]; /* Here are more sounds/species you can use , "mexicanWhipoorwill.raw", @@ -251,5 +253,14 @@ function loadBirds(howMany) { audioId: false, isPlaying: false }); - } + } + if (MAKE_FLOOR) { + var FLOOR_THICKNESS = 0.05; + floor = Entities.addEntity({ type: "Box", position: { x: lowerCorner.x + (upperCorner.x - lowerCorner.x) / 2.0, + y: lowerCorner.y, + z: lowerCorner.z + (upperCorner.z - lowerCorner.z) / 2.0 }, + dimensions: { x: (upperCorner.x - lowerCorner.x), y: FLOOR_THICKNESS, z: (upperCorner.z - lowerCorner.z)}, + color: {red: 100, green: 100, blue: 100} + }); + } } \ No newline at end of file From ee435d9732faccda4ea62479ef4d058ec4bcde36 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 17 Jul 2015 09:43:33 -0700 Subject: [PATCH 5/5] remove comment --- examples/FlockOfbirds.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/FlockOfbirds.js b/examples/FlockOfbirds.js index a8a5aafe6d..0e8c6d4731 100644 --- a/examples/FlockOfbirds.js +++ b/examples/FlockOfbirds.js @@ -121,7 +121,6 @@ function updateBirds(deltaTime) { } else if (birds[i].audioId) { // If bird is playing a chirp if (!birds[i].audioId.isPlaying) { - // clear ID if playing has stopped Entities.editEntity(birds[i].entityId, { dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }}); numPlaying--; }