Jackalope-Mocap


Person dancing.


Golan stumbling around.


Person sitting.


A still of my program

The screen recording software lagged a lot, but the actual program runs very smoothly. I wasn’t sure how to correct this.

For this week’s assignment, I started out thinking about the idea of disappearing. I imagined using Three.js to represent the mocap person as a point cloud like thing of birds or some particles and then having those appear fade away when the person stops moving, so that a still person could just fade away. Then I thought the meditation game from the Wii Fit. The game has you sit on your Wii Fit Board and the screen shows a candle. If you moved too much, the candle would go out and the game would end. Basically I wanted to make something similar to those two ideas but more visually pleasing and less hellishly infuriating to play/use.

The things I set out to do that I successfully managed: flocks of birds (done by relying heavily on Three.js example code), varying speed of birds with the movement of the person, the birds following the location of the figure(kind of), the birds fading with less motion from the person. The things that I wanted but didn’t manage because of time and a surprising amount of difficulty trying to learn to use Three.js: birds flocking in a human-ish shape and very smooth total disappearance of the “figure”.

<!--special thanks to golan for the bvh skeleton template code and to mr doob for the code for the birds
	which can be found at https://github.com/mrdoob/three.js/blob/master/examples/canvas_geometry_birds.html-->
 
 
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js canvas - geometry - birds</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<style>
			body {
				color: #808080;
				font-family:Monospace;
				font-size:13px;
				text-align:center;
				background-color: #ffffff;
				margin: 0px;
				overflow: hidden;
			}
			#info {
				position: absolute;
				top: 0px; width: 100%;
				padding: 5px;
			}
		</style>
	</head>
	<body>
 
		<div id="container"></div>
 
		<script src="build/three.js"></script>
		<script src="js/Detector.js"></script>
 
		<script src="js/renderers/Projector.js"></script>
		<script src="js/renderers/CanvasRenderer.js"></script>
 
		<script src="js/libs/stats.min.js"></script>
		<script src="js/loaders/BVHLoader.js"></script>
 
		<script>
			var Bird = function () {
				var scope = this;
				THREE.Geometry.call( this );
				v(   5,   0,   0 );
				v( - 5, - 2,   1 );
				v( - 5,   0,   0 );
				v( - 5, - 2, - 1 );
				v(   0,   2, - 6 );
				v(   0,   2,   6 );
				v(   2,   0,   0 );
				v( - 3,   0,   0 );
				f3( 0, 2, 1 );
				f3( 4, 7, 6 );
				f3( 5, 6, 7 );
				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 ) );
				}
			}
			Bird.prototype = Object.create( THREE.Geometry.prototype );
			Bird.prototype.constructor = Bird;
 
			// Based on https://www.openprocessing.org/sketch/6910
			var Boid = function () {
				var vector = new THREE.Vector3(),
				_acceleration, _width = 500, _height = 500, _depth = 200, _goal, _neighborhoodRadius = 100,
				_maxSpeed = 6, _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 count = 0;
					var velSum = new THREE.Vector3();
					for ( var i = 0, il = boids.length; i < il; i++ ) {
						if ( Math.random() > 0.6 ) continue;
						var boid = boids[ i ];
						var 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 count = 0;
					var posSum = new THREE.Vector3();
					var steer = new THREE.Vector3();
					for ( var i = 0, il = boids.length; i < il; i ++ ) {
						if ( Math.random() > 0.6 ) continue;
						var boid = boids[ i ];
						var 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 posSum = new THREE.Vector3();
					var repulse = new THREE.Vector3();
					for ( var i = 0, il = boids.length; i < il; i ++ ) {
						if ( Math.random() > 0.6 ) continue;
						var boid = boids[ i ];
						var 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;
				}
			}
		</script>
 
		<script>
 
			var clock = new THREE.Clock();
			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,
			birds, bird;
			var boid, boids;
			var x0,y0, z0;
			var mixer, skeletonHelper;
			init();
			animate();
 
			var loader = new THREE.BVHLoader();
			loader.load("bvh/dance.bvh", function( result ) {
 
				skeletonHelper = new THREE.SkeletonHelper( result.skeleton.bones[ 0 ] );
				skeletonHelper.skeleton = result.skeleton; // allow animation mixer to bind to SkeletonHelper directly
 
				var boneContainer = new THREE.Group();
				boneContainer.add( result.skeleton.bones[ 0 ] );
 
				bones0 = new THREE.SkeletonHelper(result.skeleton.bones[0]);
				// scene.add( skeletonHelper );
				// scene.add( boneContainer );
 
				// play animation
				mixer = new THREE.AnimationMixer( skeletonHelper );
				mixer.clipAction( result.clip ).setEffectiveWeight( 1.0 ).play();
 
			} );
 
			function init() {
				camera = new THREE.PerspectiveCamera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 10000 );
				camera.position.z = 350;
				scene = new THREE.Scene();
				scene.background = new THREE.Color( 0xffffff );
				birds = [];
				boids = [];
				dist = [];
				var goal;
				for ( var i = 0; i < 400; 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;
					boid.velocity.x = Math.random() * 2 - 1;
					boid.velocity.y = Math.random() * 2 - 1;
					boid.velocity.z = Math.random() * 2 - 1;
					var index = Math.floor(i/400*62);
					if(skeletonHelper) goal = new THREE.Vector3(skeletonHelper.bones[index].getWorldPosition().x,skeletonHelper.bones[index].getWorldPosition().y,
						skeletonHelper.bones[index].getWorldPosition().z+400);
					else goal = new THREE.Vector3(0,0,0);
					boid.setGoal(goal);
					boid.setAvoidWalls( true );
					boid.setWorldSize( 500, 500, 400 );
					bird = birds[ i ] = new THREE.Mesh( new Bird(), new THREE.MeshBasicMaterial( { color:Math.random() * 0xffffff, side: THREE.DoubleSide } ) );
					bird.phase = Math.floor( Math.random() * 62.83 );
					scene.add( bird );
				}
				if(skeletonHelper){
					x0 = skeletonHelper.bones[14].position.x;
					y0 = skeletonHelper.bones[14].position.y;
					z0 = skeletonHelper.bones[14].position.z;
				}
				renderer = new THREE.WebGLRenderer();
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
				document.body.appendChild( renderer.domElement );
				//
				window.addEventListener( 'resize', onWindowResize, false );
			}
			function onWindowResize() {
				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();
				renderer.setSize( window.innerWidth, window.innerHeight );
			}
			//
			function animate() {
				requestAnimationFrame( animate );
				var delta = clock.getDelta();
 
				if ( skeletonHelper )
					skeletonHelper.update();
				if ( mixer ) mixer.update( delta );
 
 
				render();
 
			}
			function render() {
				var v = 0 ;
					if(skeletonHelper&&skeletonHelper.bones&&bones0){
						var bone = skeletonHelper.bones[14]
						var prev = new THREE.Vector3(x0,y0,z0);
						v = (bone.position.distanceTo(prev))/8.0;
					}
					// if(v!=0) console.log(v);
				for ( var i = 0, il = birds.length; i < il; i++ ) {
					var index = Math.floor(i*62/birds.length);
					boid = boids[ i ];
					boid.run( boids );
					var vector = new THREE.Vector3( x0 - SCREEN_WIDTH_HALF, - y0 + SCREEN_HEIGHT_HALF, 0 );
					if(v<.9&&skeletonHelper) boid.repulse(vector);
					bird = birds[ i ];
					bird.position.copy( boids[ i ].position );
					var color = bird.material.color;
					color.r = color.g = color.b = ( 500 - bird.position.z ) / 1000;
					bird.maxSpeed = 8;
					if(v<.6){ color.r=(color.r+1.1)/2.0; color.g=(color.g+1.1)/2.0; color.b=(color.b+1.1)/2.0;bird.maxSpeed = 5; bird.maxSteerForce = .001}
					if(v<.5){ color.r=.9; color.g=.9; color.b=.9; bird.maxSpeed =3; bird.maxSteerForce = .01}
					if(v<.4){ color.r=.99; color.g=.99; color.b=.99; bird.maxSpeed = 1; bird.maxSteerForce = .1}
					bird.rotation.y = Math.atan2( - boid.velocity.z, boid.velocity.x );
					bird.rotation.z = Math.asin( boid.velocity.y / boid.velocity.length() );
					bird.phase = ( bird.phase + ( Math.max( 0, bird.rotation.z ) + 0.1 )  ) % 62.83;
					bird.geometry.vertices[ 5 ].y = bird.geometry.vertices[ 4 ].y = Math.sin( bird.phase ) * 5;
				}
				renderer.render( scene, camera );
			}
		</script>
 
	</body>
</html>