diff --git a/unpublishedScripts/DomainContent/Home/fishTank/entityLocalFish.js b/unpublishedScripts/DomainContent/Home/fishTank/entityLocalFish.js new file mode 100644 index 0000000000..9d5464d53e --- /dev/null +++ b/unpublishedScripts/DomainContent/Home/fishTank/entityLocalFish.js @@ -0,0 +1,166 @@ +// +// entityLocalFish.js +// examples +// +// Philip Rosedale +// Copyright 2016 High Fidelity, Inc. +// Fish smimming around in a space in front of you +// These fish are overlays, meaning they can only be seen by you. +// Attach this entity script to an object to create fish that stay within the +// dimensions of the object. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +(function(){ + + var NUM_FISH = 25; + var FISH_SCALE = 0.45; + var MAX_SIGHT_DISTANCE = 0.5; + var MIN_SEPARATION = 0.08; + var AVOIDANCE_FORCE = 0.05; + var COHESION_FORCE = 0.025; + var ALIGNMENT_FORCE = 0.025; + var SWIMMING_FORCE = 0.025; + var SWIMMING_SPEED = 0.5; + var VELOCITY_FOLLOW_RATE = 0.25; + var FISH_MODEL_URL = "http://hifi-content.s3.amazonaws.com/DomainContent/Home/fishTank/goodfish5.fbx"; + + var fishLoaded = false; + var fish = []; + + var lowerCorner = { x: 0, y: 0, z: 0 }; + var upperCorner = { x: 0, y: 0, z: 0 }; + + var entityID; + + function update(deltaTime) { + + var averageVelocity = { x: 0, y: 0, z: 0 }; + var averagePosition = { x: 0, y: 0, z: 0 }; + + for (var i = 0; i < fish.length; i++) { + + // Update position by velocity + fish[i].position = Vec3.sum(fish[i].position, Vec3.multiply(deltaTime, fish[i].velocity)); + + averageVelocity = { x: 0, y: 0, z: 0 }; + averagePosition = { x: 0, y: 0, z: 0 }; + + var othersCounted = 0; + for (var j = 0; j < fish.length; j++) { + if (i != j) { + // Get only the properties we need, because that is faster + var separation = Vec3.distance(fish[i].position, fish[j].position); + if (separation < MAX_SIGHT_DISTANCE) { + averageVelocity = Vec3.sum(averageVelocity, fish[j].velocity); + averagePosition = Vec3.sum(averagePosition, fish[j].position); + othersCounted++; + } + if (separation < MIN_SEPARATION) { + // Separation: Push away if you are too close to someone else + var pushAway = Vec3.multiply(Vec3.normalize(Vec3.subtract(fish[i].position, fish[j].position)), AVOIDANCE_FORCE); + fish[i].velocity = Vec3.sum(fish[i].velocity, pushAway); + } + } + } + + if (othersCounted > 0) { + averageVelocity = Vec3.multiply(averageVelocity, 1.0 / othersCounted); + averagePosition = Vec3.multiply(averagePosition, 1.0 / othersCounted); + // Alignment: Follow group's direction and speed + fish[i].velocity = Vec3.mix(fish[i].velocity, Vec3.multiply(Vec3.normalize(averageVelocity), Vec3.length(fish[i].velocity)), ALIGNMENT_FORCE); + // Cohesion: Steer towards center of flock + var towardCenter = Vec3.subtract(averagePosition, fish[i].position); + fish[i].velocity = Vec3.mix(fish[i].velocity, Vec3.multiply(Vec3.normalize(towardCenter), Vec3.length(fish[i].velocity)), COHESION_FORCE); + } + + // Try to swim at a constant speed + fish[i].velocity = Vec3.mix(fish[i].velocity, Vec3.multiply(Vec3.normalize(fish[i].velocity), SWIMMING_SPEED), SWIMMING_FORCE); + + // Keep fish in their 'tank' + if (fish[i].position.x < lowerCorner.x) { + fish[i].position.x = lowerCorner.x; + fish[i].velocity.x *= -1.0; + } else if (fish[i].position.x > upperCorner.x) { + fish[i].position.x = upperCorner.x; + fish[i].velocity.x *= -1.0; + } + if (fish[i].position.y < lowerCorner.y) { + fish[i].position.y = lowerCorner.y; + fish[i].velocity.y *= -1.0; + } else if (fish[i].position.y > upperCorner.y) { + fish[i].position.y = upperCorner.y; + fish[i].velocity.y *= -1.0; + } + if (fish[i].position.z < lowerCorner.z) { + fish[i].position.z = lowerCorner.z; + fish[i].velocity.z *= -1.0; + } else if (fish[i].position.z > upperCorner.z) { + fish[i].position.z = upperCorner.z; + fish[i].velocity.z *= -1.0; + } + + // Orient in direction of velocity + var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, fish[i].velocity); + fish[i].rotation = Quat.slerp(fish[i].rotation, rotation, VELOCITY_FOLLOW_RATE); + + // Update the actual 3D overlay + Overlays.editOverlay(fish[i].overlayID, { position: fish[i].position, + velocity: fish[i].velocity, + rotation: fish[i].rotation }); + } + } + + 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 loadFish(entityID, howMany) { + var STARTING_FRACTION = 0.25; + var properties = Entities.getEntityProperties(entityID); + lowerCorner = { x: properties.position.x - properties.dimensions.x / 2, + y: properties.position.y - properties.dimensions.y / 2, + z: properties.position.z - properties.dimensions.z / 2 }; + upperCorner = { x: properties.position.x + properties.dimensions.x / 2, + y: properties.position.y + properties.dimensions.y / 2, + z: properties.position.z + properties.dimensions.z / 2 }; + + for (var i = 0; i < howMany; i++) { + 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 + }; + var rotation = { x: 0, y: 0, z: 0, w: 1 }; + fish.push({ + overlayID: Overlays.addOverlay("model", { + url: FISH_MODEL_URL, + position: position, + rotation: rotation, + scale: FISH_SCALE, + visible: true }), + position: position, + rotation: rotation, + velocity: randomVector(SWIMMING_SPEED) + }); + } + print("Fish loaded"); + } + + this.preload = function(entityID) { + print("Fish Preload v3"); + loadFish(entityID, NUM_FISH); + fishLoaded = true; + Script.update.connect(update); + + }; + + this.unload = function() { + Script.update.disconnect(update); + print("Fish Unload"); + for (var i = 0; i < fish.length; i++) { + Overlays.deleteOverlay(fish[i].overlayID); + } + }; +}) diff --git a/unpublishedScripts/DomainContent/Home/fishTank/singleSpawner.js b/unpublishedScripts/DomainContent/Home/fishTank/singleSpawner.js index 0930652c92..ffd1ccda4e 100644 --- a/unpublishedScripts/DomainContent/Home/fishTank/singleSpawner.js +++ b/unpublishedScripts/DomainContent/Home/fishTank/singleSpawner.js @@ -11,6 +11,6 @@ z: 0 }); - Script.scriptEnding.connect(function(){ + Script.scriptEnding.connect(function() { fishtank.cleanup() }) \ No newline at end of file diff --git a/unpublishedScripts/DomainContent/Home/fishTank/wrapper.js b/unpublishedScripts/DomainContent/Home/fishTank/wrapper.js index efd2700266..a7da4e5960 100644 --- a/unpublishedScripts/DomainContent/Home/fishTank/wrapper.js +++ b/unpublishedScripts/DomainContent/Home/fishTank/wrapper.js @@ -22,7 +22,6 @@ FishTank = function(spawnPosition, spawnRotation) { z: 2.1404 }; - var INNER_TANK_SCALE = 0.7; var INNER_TANK_DIMENSIONS = Vec3.multiply(INNER_TANK_SCALE, TANK_DIMENSIONS); INNER_TANK_DIMENSIONS.y = INNER_TANK_DIMENSIONS.y - 0.4;