overte-Armored-Dragon/script-archive/FlockOfbirds.js
2016-05-25 11:11:06 -07:00

292 lines
12 KiB
JavaScript

//
// 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 rectangular area in the domain where the flock will fly
var lowerCorner = { x: 0, y: 0, z: 0 };
var upperCorner = { x: 30, y: 10, z: 30 };
var STARTING_FRACTION = 0.25;
var NUM_BIRDS = 50;
var UPDATE_INTERVAL = 0.016;
var playSounds = true;
var SOUND_PROBABILITY = 0.001;
var STARTING_LIFETIME = (1.0 / SOUND_PROBABILITY) * UPDATE_INTERVAL * 10;
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 followBirds = false;
var AVATAR_FOLLOW_RATE = 0.001;
var AVATAR_FOLLOW_VELOCITY_TIMESCALE = 2.0;
var AVATAR_FOLLOW_ORIENTATION_RATE = 0.005;
var floor = false;
var MAKE_FLOOR = false;
var averageVelocity = { x: 0, y: 0, z: 0 };
var averagePosition = { x: 0, y: 0, z: 0 };
var birdsLoaded = false;
var oldAvatarOrientation;
var oldAvatarPosition;
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) {
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;
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.playing) && (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, and update lifetime to keep bird alive
Entities.editEntity(birds[i].entityId, { dimensions: Vec3.multiply(1.5, properties.dimensions),
lifetime: properties.ageInSeconds + STARTING_LIFETIME});
} else if (birds[i].audioId) {
// If bird is playing a chirp
if (!birds[i].audioId.playing) {
Entities.editEntity(birds[i].entityId, { dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }});
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 (followBirds) {
MyAvatar.motorVelocity = averageVelocity;
MyAvatar.motorTimescale = AVATAR_FOLLOW_VELOCITY_TIMESCALE;
var polarAngles = Vec3.toPolar(Vec3.normalize(averageVelocity));
if (!isNaN(polarAngles.x) && !isNaN(polarAngles.y)) {
var birdDirection = Quat.fromPitchYawRollRadians(polarAngles.x, polarAngles.y + Math.PI, polarAngles.z);
MyAvatar.orientation = Quat.mix(MyAvatar.orientation, birdDirection, AVATAR_FOLLOW_ORIENTATION_RATE);
}
}
}
if (birdPositionsCounted > 0) {
averagePosition = Vec3.multiply(1.0 / birdPositionsCounted, sumPosition);
// If Following birds, update position
if (followBirds) {
MyAvatar.position = Vec3.sum(Vec3.multiply(AVATAR_FOLLOW_RATE, MyAvatar.position), Vec3.multiply(1.0 - AVATAR_FOLLOW_RATE, averagePosition));
}
}
}
// 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);
}
MyAvatar.orientation = oldAvatarOrientation;
MyAvatar.position = oldAvatarPosition;
});
function loadBirds(howMany) {
oldAvatarOrientation = MyAvatar.orientation;
oldAvatarPosition = MyAvatar.position;
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 },
damping: LINEAR_DAMPING,
dynamic: true,
lifetime: STARTING_LIFETIME,
color: colors[whichBird]
}),
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}
});
}
}