Created
June 3, 2015 15:02
-
-
Save brannondorsey/a80f7b673a924ef8d297 to your computer and use it in GitHub Desktop.
Butterfly Habitat in Three.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// by Joshua Koo @BlurSpline !!! http://www.joshuakoo.com/ | |
// http://www.lab4games.net/zz85/blog/2013/12/30/webgl-gpgpu-and-flocking-part-1/ | |
var Boid = function() { | |
var vector = new THREE.Vector3(), | |
_acceleration, _width = 500, _height = 500, _depth = 200, _goal, _neighborhoodRadius = 100, | |
_maxSpeed = 4, _maxSteerForce = 0.1, _avoidWalls = false; | |
this.position = new THREE.Vector3(); | |
this.velocity = new THREE.Vector3(); | |
_acceleration = new THREE.Vector3(); | |
this.setGoal = function ( target ) { | |
_goal = target; | |
} | |
this.setAvoidWalls = function ( value ) { | |
_avoidWalls = value; | |
} | |
this.setWorldSize = function ( width, height, depth ) { | |
_width = width; | |
_height = height; | |
_depth = depth; | |
} | |
this.run = function ( boids ) { | |
if ( _avoidWalls ) { | |
vector.set( - _width, this.position.y, this.position.z ); | |
vector = this.avoid( vector ); | |
vector.multiplyScalar( 5 ); | |
_acceleration.add( vector ); | |
vector.set( _width, this.position.y, this.position.z ); | |
vector = this.avoid( vector ); | |
vector.multiplyScalar( 5 ); | |
_acceleration.add( vector ); | |
vector.set( this.position.x, - _height, this.position.z ); | |
vector = this.avoid( vector ); | |
vector.multiplyScalar( 5 ); | |
_acceleration.add( vector ); | |
vector.set( this.position.x, _height, this.position.z ); | |
vector = this.avoid( vector ); | |
vector.multiplyScalar( 5 ); | |
_acceleration.add( vector ); | |
vector.set( this.position.x, this.position.y, - _depth ); | |
vector = this.avoid( vector ); | |
vector.multiplyScalar( 5 ); | |
_acceleration.add( vector ); | |
vector.set( this.position.x, this.position.y, _depth ); | |
vector = this.avoid( vector ); | |
vector.multiplyScalar( 5 ); | |
_acceleration.add( vector ); | |
}/* else { | |
this.checkBounds(); | |
} | |
*/ | |
if ( Math.random() > 0.5 ) { | |
this.flock( boids ); | |
} | |
this.move(); | |
} | |
this.flock = function ( boids ) { | |
if ( _goal ) { | |
_acceleration.add( this.reach( _goal, 0.005 ) ); | |
} | |
_acceleration.add( this.alignment( boids ) ); | |
_acceleration.add( this.cohesion( boids ) ); | |
_acceleration.add( this.separation( boids ) ); | |
} | |
this.move = function () { | |
this.velocity.add( _acceleration ); | |
var l = this.velocity.length(); | |
if ( l > _maxSpeed ) { | |
this.velocity.divideScalar( l / _maxSpeed ); | |
} | |
this.position.add( this.velocity ); | |
_acceleration.set( 0, 0, 0 ); | |
} | |
this.checkBounds = function () { | |
if ( this.position.x > _width ) this.position.x = - _width; | |
if ( this.position.x < - _width ) this.position.x = _width; | |
if ( this.position.y > _height ) this.position.y = - _height; | |
if ( this.position.y < - _height ) this.position.y = _height; | |
if ( this.position.z > _depth ) this.position.z = - _depth; | |
if ( this.position.z < - _depth ) this.position.z = _depth; | |
} | |
// | |
this.avoid = function ( target ) { | |
var steer = new THREE.Vector3(); | |
steer.copy( this.position ); | |
steer.sub( target ); | |
steer.multiplyScalar( 1 / this.position.distanceToSquared( target ) ); | |
return steer; | |
} | |
this.repulse = function ( target ) { | |
var distance = this.position.distanceTo( target ); | |
if ( distance < 150 ) { | |
var steer = new THREE.Vector3(); | |
steer.subVectors( this.position, target ); | |
steer.multiplyScalar( 0.5 / distance ); | |
_acceleration.add( steer ); | |
} | |
} | |
this.reach = function ( target, amount ) { | |
var steer = new THREE.Vector3(); | |
steer.subVectors( target, this.position ); | |
steer.multiplyScalar( amount ); | |
return steer; | |
} | |
this.alignment = function ( boids ) { | |
var boid, distance, velSum = new THREE.Vector3(), | |
count = 0; | |
for ( var i = 0, il = boids.length; i < il; i++ ) { | |
if ( Math.random() > 0.6 ) continue; | |
boid = boids[ i ]; | |
distance = boid.position.distanceTo( this.position ); | |
if ( distance > 0 && distance <= _neighborhoodRadius ) { | |
velSum.add( boid.velocity ); | |
count++; | |
} | |
} | |
if ( count > 0 ) { | |
velSum.divideScalar( count ); | |
var l = velSum.length(); | |
if ( l > _maxSteerForce ) { | |
velSum.divideScalar( l / _maxSteerForce ); | |
} | |
} | |
return velSum; | |
} | |
this.cohesion = function ( boids ) { | |
var boid, distance, | |
posSum = new THREE.Vector3(), | |
steer = new THREE.Vector3(), | |
count = 0; | |
for ( var i = 0, il = boids.length; i < il; i ++ ) { | |
if ( Math.random() > 0.6 ) continue; | |
boid = boids[ i ]; | |
distance = boid.position.distanceTo( this.position ); | |
if ( distance > 0 && distance <= _neighborhoodRadius ) { | |
posSum.add( boid.position ); | |
count++; | |
} | |
} | |
if ( count > 0 ) { | |
posSum.divideScalar( count ); | |
} | |
steer.subVectors( posSum, this.position ); | |
var l = steer.length(); | |
if ( l > _maxSteerForce ) { | |
steer.divideScalar( l / _maxSteerForce ); | |
} | |
return steer; | |
} | |
this.separation = function ( boids ) { | |
var boid, distance, | |
posSum = new THREE.Vector3(), | |
repulse = new THREE.Vector3(); | |
for ( var i = 0, il = boids.length; i < il; i ++ ) { | |
if ( Math.random() > 0.6 ) continue; | |
boid = boids[ i ]; | |
distance = boid.position.distanceTo( this.position ); | |
if ( distance > 0 && distance <= _neighborhoodRadius ) { | |
repulse.subVectors( this.position, boid.position ); | |
repulse.normalize(); | |
repulse.divideScalar( distance ); | |
posSum.add( repulse ); | |
} | |
} | |
return posSum; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Butterfly = function () { | |
var scope = this; | |
THREE.Geometry.call( this ); | |
v( - 3, 3, -6 ); | |
v( 3, 3, -6 ); | |
v( - 3, 0, 0 ); | |
v( 3, 0, 0 ); | |
v( - 3, 3, 6 ); | |
v( 3, 3, 6 ); | |
f3( 0, 1, 3 ); | |
f3( 3, 2, 0 ); | |
f3( 4, 2, 3 ); | |
f3( 3, 5, 4 ); | |
this.faceVertexUvs[ 0 ].push([ | |
new THREE.Vector2( 0, 0 ), | |
new THREE.Vector2( 1, 0 ), | |
new THREE.Vector2( 1, .5 ) | |
]); | |
this.faceVertexUvs[ 0 ].push([ | |
new THREE.Vector2( 1, .5 ), | |
new THREE.Vector2( 0, .5 ), | |
new THREE.Vector2( 0, 0 ) | |
]); | |
this.faceVertexUvs[ 0 ].push([ | |
new THREE.Vector2( 0, 1 ), | |
new THREE.Vector2( 0, .5 ), | |
new THREE.Vector2( 1, .5 ) | |
]); | |
this.faceVertexUvs[ 0 ].push([ | |
new THREE.Vector2( 1, .5 ), | |
new THREE.Vector2( 1, 1 ), | |
new THREE.Vector2( 0, 1 ) | |
]); | |
// this.computeFaceNormals(); | |
function v( x, y, z ) { | |
scope.vertices.push( new THREE.Vector3( x, y, z ) ); | |
} | |
function f3( a, b, c ) { | |
scope.faces.push( new THREE.Face3( a, b, c ) ); | |
} | |
} | |
Butterfly.prototype = Object.create( THREE.Geometry.prototype ); | |
Butterfly.prototype.constructor = Butterfly; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<title> Butterfly Habitat </title> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> | |
<style> | |
html,body{ margin:0; padding:0; overflow: hidden;} | |
</style> | |
</head> | |
<body> | |
<div id="container"></div> | |
<script src="js/three.min.js"></script> | |
<script src="js/stats.min.js"></script> | |
<script src="js/boid.js"></script> | |
<script src="js/butterfly.js"></script> | |
<script> | |
// MAD PROPS to Joshua Koo @BlurSpline !!! http://www.joshuakoo.com/ | |
// all the s!ck boid math is his ++ there's plenty more on his site | |
// + specifically on webGL + flocking here: | |
// http://www.lab4games.net/zz85/blog/2013/12/30/webgl-gpgpu-and-flocking-part-1/ | |
// HABITAT ----------------------------------------------------------------------- | |
var SCREEN_WIDTH = window.innerWidth, | |
SCREEN_HEIGHT = window.innerHeight, | |
SCREEN_WIDTH_HALF = SCREEN_WIDTH / 2, | |
SCREEN_HEIGHT_HALF = SCREEN_HEIGHT / 2; | |
var camera, scene, renderer, stats; | |
var butterfly, butterflies, boid, boids; | |
init(); | |
animate(); | |
function init() { | |
camera = new THREE.PerspectiveCamera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 10000 ); | |
camera.position.z = 450; | |
scene = new THREE.Scene(); | |
butterflies = []; | |
boids = []; | |
for ( var i = 0; i < 750; i ++ ) { | |
boid = boids[ i ] = new Boid(); | |
boid.position.x = Math.random() * 400 - 200; | |
boid.position.y = Math.random() * 400 - 200; | |
boid.position.z = Math.random() * 400 - 200; | |
boid.velocity.x = Math.random() * 2 - 1; | |
boid.velocity.y = Math.random() * 2 - 1; | |
boid.velocity.z = Math.random() * 2 - 1; | |
boid.setAvoidWalls( true ); | |
boid.setWorldSize( 500, 500, 400 ); | |
var alpha = THREE.ImageUtils.loadTexture('mask'+Math.floor(Math.random()*4)+'.jpg'); | |
var mat = new THREE.MeshBasicMaterial({ | |
shading: THREE.FlatShading, color: Math.random()*0xffffff, | |
side: THREE.DoubleSide//, alphaMap:alpha, alphaTest: 0.5 | |
}); | |
butterfly = butterflies[ i ] = new THREE.Mesh(new Butterfly(), mat ); | |
butterfly.phase = Math.floor( Math.random() * 62.83 ); | |
butterfly.original = {r:butterfly.material.color.r,g:butterfly.material.color.g,b:butterfly.material.color.b} | |
scene.add( butterfly ); | |
} | |
renderer = new THREE.WebGLRenderer({ | |
antialias: true | |
}); | |
renderer.setClearColor( 0xffffff ); | |
renderer.setPixelRatio( window.devicePixelRatio ); | |
renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT ); | |
renderer.autoClear = true; | |
// document.addEventListener( 'mousemove', onDocumentMouseMove, false ); | |
document.body.appendChild( renderer.domElement ); | |
// | |
stats = new Stats(); | |
stats.domElement.style.position = 'absolute'; | |
stats.domElement.style.left = '0px'; | |
stats.domElement.style.top = '0px'; | |
document.getElementById( 'container' ).appendChild(stats.domElement); | |
// | |
window.addEventListener( 'resize', onWindowResize, false ); | |
} | |
function onWindowResize() { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize( window.innerWidth, window.innerHeight ); | |
} | |
// make butterflies react to mouse move | |
// ... if we decide we want this later ... | |
// ----------------------------------------------- | |
function onDocumentMouseMove( event ) { | |
var vector = new THREE.Vector3( event.clientX - SCREEN_WIDTH_HALF, - event.clientY + SCREEN_HEIGHT_HALF, 0 ); | |
for ( var i = 0, il = boids.length; i < il; i++ ) { | |
boid = boids[ i ]; | |
vector.z = boid.position.z; | |
boid.repulse( vector ); | |
} | |
} | |
// ----------------------------------------------- | |
function animate() { | |
requestAnimationFrame( animate ); | |
render(); | |
stats.update(); | |
} | |
// setInterval(animate,1000/30); | |
// animate(); | |
function render() { | |
for ( var i = 0, il = butterflies.length; i < il; i++ ) { | |
boid = boids[ i ]; | |
boid.run( boids ); | |
butterfly = butterflies[ i ]; | |
butterfly.position.copy( boids[ i ].position ); | |
butterfly.geometry.verticesNeedUpdate = true; | |
o = butterfly.original; // custom object to keep track of original color | |
color = butterfly.material.color; | |
var shift = (400-butterfly.position.z)/1000; | |
color.r = (1-o.r) * shift + o.r; | |
color.g = (1-o.g) * shift + o.g; | |
color.b = (1-o.b) * shift + o.b; | |
butterfly.rotation.y = Math.atan2( - boid.velocity.z, boid.velocity.x ); | |
butterfly.rotation.z = Math.asin( boid.velocity.y / boid.velocity.length() ); | |
butterfly.phase = ( butterfly.phase + ( Math.max( 0, butterfly.rotation.z ) + 0.3 ) ) % 62.83; | |
butterfly.geometry.vertices[ 0 ].y = butterfly.geometry.vertices[ 1 ].y = 6 * Math.cos( butterfly.phase ); | |
butterfly.geometry.vertices[ 5 ].y = butterfly.geometry.vertices[ 4 ].y = 6 * Math.sin( butterfly.phase ); | |
butterfly.geometry.vertices[ 0 ].z = butterfly.geometry.vertices[ 1 ].z = 6 * Math.sin( butterfly.phase ); | |
butterfly.geometry.vertices[ 5 ].z = butterfly.geometry.vertices[ 4 ].z = 6 * Math.cos( butterfly.phase ); | |
} | |
renderer.render( scene, camera ); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello,
I know this is old one but i was checking on it and its giving error on this line.
var alpha = THREE.ImageUtils.loadTexture('mask'+Math.floor(Math.random()*4)+'.jpg');
Can you provide mask files you used into project. I am trying to integrate it in custom html.
Thanks