text: /**
   * @author alteredq / http://alteredqualia.com/
   */
  
  THREE.BufferGeometry = function () {
  
  	THREE.EventDispatcher.call( this );
  
  	this.id = THREE.GeometryIdCount ++;
  
  	// attributes
  
  	this.attributes = {};
  
  	// attributes typed arrays are kept only if dynamic flag is set
  
  	this.dynamic = false;
  
  	// offsets for chunks when using indexed elements
  
  	this.offsets = [];
  
  	// boundings
  
  	this.boundingBox = null;
  	this.boundingSphere = null;
  
  	this.hasTangents = false;
  
  	// for compatibility
  
  	this.morphTargets = [];
  
  };
  
  THREE.BufferGeometry.prototype = {
  
  	constructor : THREE.BufferGeometry,
  
  	applyMatrix: function ( matrix ) {
  
  		var positionArray;
  		var normalArray;
  
  		if ( this.attributes[ "position" ] ) positionArray = this.attributes[ "position" ].array;
  		if ( this.attributes[ "normal" ] ) normalArray = this.attributes[ "normal" ].array;
  
  		if ( positionArray !== undefined ) {
  
  			matrix.multiplyVector3Array( positionArray );
  			this.verticesNeedUpdate = true;
  
  		}
  
  		if ( normalArray !== undefined ) {
  
  			var normalMatrix = new THREE.Matrix3();
  			normalMatrix.getInverse( matrix ).transpose();
  
  			normalMatrix.multiplyVector3Array( normalArray );
  
  			this.normalizeNormals();
  
  			this.normalsNeedUpdate = true;
  
  		}
  
  	},
  
  	computeBoundingBox: function () {
  
  		if ( this.boundingBox === null ) {
  
  			this.boundingBox = new THREE.Box3();
  
  		}
  
  		var positions = this.attributes[ "position" ].array;
  
  		if ( positions ) {
  
  			var bb = this.boundingBox;
  			var x, y, z;
  
  			if( positions.length >= 3 ) {
  				bb.min.x = bb.max.x = positions[ 0 ];
  				bb.min.y = bb.max.y = positions[ 1 ];
  				bb.min.z = bb.max.z = positions[ 2 ];
  			}
  
  			for ( var i = 3, il = positions.length; i < il; i += 3 ) {
  
  				x = positions[ i ];
  				y = positions[ i + 1 ];
  				z = positions[ i + 2 ];
  
  				// bounding box
  
  				if ( x < bb.min.x ) {
  
  					bb.min.x = x;
  
  				} else if ( x > bb.max.x ) {
  
  					bb.max.x = x;
  
  				}
  
  				if ( y < bb.min.y ) {
  
  					bb.min.y = y;
  
  				} else if ( y > bb.max.y ) {
  
  					bb.max.y = y;
  
  				}
  
  				if ( z < bb.min.z ) {
  
  					bb.min.z = z;
  
  				} else if ( z > bb.max.z ) {
  
  					bb.max.z = z;
  
  				}
  
  			}
  
  		}
  
  		if ( positions === undefined || positions.length === 0 ) {
  
  			this.boundingBox.min.set( 0, 0, 0 );
  			this.boundingBox.max.set( 0, 0, 0 );
  
  		}
  
  	},
  
  	computeBoundingSphere: function () {
  
  		if ( this.boundingSphere === null ) {
  
  			this.boundingSphere = new THREE.Sphere();
  
  		}
  
  		var positions = this.attributes[ "position" ].array;
  
  		if ( positions ) {
  
  			var radiusSq, maxRadiusSq = 0;
  			var x, y, z;
  
  			for ( var i = 0, il = positions.length; i < il; i += 3 ) {
  
  				x = positions[ i ];
  				y = positions[ i + 1 ];
  				z = positions[ i + 2 ];
  
  				radiusSq =  x * x + y * y + z * z;
  				if ( radiusSq > maxRadiusSq ) maxRadiusSq = radiusSq;
  
  			}
  
  			this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
  
  		}
  
  	},
  
  	computeVertexNormals: function () {
  
  		if ( this.attributes[ "position" ] ) {
  
  			var i, il;
  			var j, jl;
  
  			var nVertexElements = this.attributes[ "position" ].array.length;
  
  			if ( this.attributes[ "normal" ] === undefined ) {
  
  				this.attributes[ "normal" ] = {
  
  					itemSize: 3,
  					array: new Float32Array( nVertexElements ),
  					numItems: nVertexElements
  
  				};
  
  			} else {
  
  				// reset existing normals to zero
  
  				for ( i = 0, il = this.attributes[ "normal" ].array.length; i < il; i ++ ) {
  
  					this.attributes[ "normal" ].array[ i ] = 0;
  
  				}
  
  			}
  
  			var positions = this.attributes[ "position" ].array;
  			var normals = this.attributes[ "normal" ].array;
  
  			var vA, vB, vC, x, y, z,
  
  			pA = new THREE.Vector3(),
  			pB = new THREE.Vector3(),
  			pC = new THREE.Vector3(),
  
  			cb = new THREE.Vector3(),
  			ab = new THREE.Vector3();
  
  			// indexed elements
  
  			if ( this.attributes[ "index" ] ) {
  
  				var indices = this.attributes[ "index" ].array;
  
  				var offsets = this.offsets;
  
  				for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
  
  					var start = offsets[ j ].start;
  					var count = offsets[ j ].count;
  					var index = offsets[ j ].index;
  
  					for ( i = start, il = start + count; i < il; i += 3 ) {
  
  						vA = index + indices[ i ];
  						vB = index + indices[ i + 1 ];
  						vC = index + indices[ i + 2 ];
  
  						x = positions[ vA * 3 ];
  						y = positions[ vA * 3 + 1 ];
  						z = positions[ vA * 3 + 2 ];
  						pA.set( x, y, z );
  
  						x = positions[ vB * 3 ];
  						y = positions[ vB * 3 + 1 ];
  						z = positions[ vB * 3 + 2 ];
  						pB.set( x, y, z );
  
  						x = positions[ vC * 3 ];
  						y = positions[ vC * 3 + 1 ];
  						z = positions[ vC * 3 + 2 ];
  						pC.set( x, y, z );
  
  						cb.subVectors( pC, pB );
  						ab.subVectors( pA, pB );
  						cb.cross( ab );
  
  						normals[ vA * 3 ]     += cb.x;
  						normals[ vA * 3 + 1 ] += cb.y;
  						normals[ vA * 3 + 2 ] += cb.z;
  
  						normals[ vB * 3 ]     += cb.x;
  						normals[ vB * 3 + 1 ] += cb.y;
  						normals[ vB * 3 + 2 ] += cb.z;
  
  						normals[ vC * 3 ]     += cb.x;
  						normals[ vC * 3 + 1 ] += cb.y;
  						normals[ vC * 3 + 2 ] += cb.z;
  
  					}
  
  				}
  
  			// non-indexed elements (unconnected triangle soup)
  
  			} else {
  
  				for ( i = 0, il = positions.length; i < il; i += 9 ) {
  
  					x = positions[ i ];
  					y = positions[ i + 1 ];
  					z = positions[ i + 2 ];
  					pA.set( x, y, z );
  
  					x = positions[ i + 3 ];
  					y = positions[ i + 4 ];
  					z = positions[ i + 5 ];
  					pB.set( x, y, z );
  
  					x = positions[ i + 6 ];
  					y = positions[ i + 7 ];
  					z = positions[ i + 8 ];
  					pC.set( x, y, z );
  
  					cb.subVectors( pC, pB );
  					ab.subVectors( pA, pB );
  					cb.cross( ab );
  
  					normals[ i ] 	 = cb.x;
  					normals[ i + 1 ] = cb.y;
  					normals[ i + 2 ] = cb.z;
  
  					normals[ i + 3 ] = cb.x;
  					normals[ i + 4 ] = cb.y;
  					normals[ i + 5 ] = cb.z;
  
  					normals[ i + 6 ] = cb.x;
  					normals[ i + 7 ] = cb.y;
  					normals[ i + 8 ] = cb.z;
  
  				}
  
  			}
  
  			this.normalizeNormals();
  
  			this.normalsNeedUpdate = true;
  
  		}
  
  	},
  
  	normalizeNormals: function () {
  
  		var normals = this.attributes[ "normal" ].array;
  
  		var x, y, z, n;
  
  		for ( var i = 0, il = normals.length; i < il; i += 3 ) {
  
  			x = normals[ i ];
  			y = normals[ i + 1 ];
  			z = normals[ i + 2 ];
  
  			n = 1.0 / Math.sqrt( x * x + y * y + z * z );
  
  			normals[ i ] 	 *= n;
  			normals[ i + 1 ] *= n;
  			normals[ i + 2 ] *= n;
  
  		}
  
  	},
  
  	computeTangents: function () {
  
  		// based on http://www.terathon.com/code/tangent.html
  		// (per vertex tangents)
  
  		if ( this.attributes[ "index" ] === undefined ||
  			 this.attributes[ "position" ] === undefined ||
  			 this.attributes[ "normal" ] === undefined ||
  			 this.attributes[ "uv" ] === undefined ) {
  
  			console.warn( "Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()" );
  			return;
  
  		}
  
  		var indices = this.attributes[ "index" ].array;
  		var positions = this.attributes[ "position" ].array;
  		var normals = this.attributes[ "normal" ].array;
  		var uvs = this.attributes[ "uv" ].array;
  
  		var nVertices = positions.length / 3;
  
  		if ( this.attributes[ "tangent" ] === undefined ) {
  
  			var nTangentElements = 4 * nVertices;
  
  			this.attributes[ "tangent" ] = {
  
  				itemSize: 4,
  				array: new Float32Array( nTangentElements ),
  				numItems: nTangentElements
  
  			};
  
  		}
  
  		var tangents = this.attributes[ "tangent" ].array;
  
  		var tan1 = [], tan2 = [];
  
  		for ( var k = 0; k < nVertices; k ++ ) {
  
  			tan1[ k ] = new THREE.Vector3();
  			tan2[ k ] = new THREE.Vector3();
  
  		}
  
  		var xA, yA, zA,
  			xB, yB, zB,
  			xC, yC, zC,
  
  			uA, vA,
  			uB, vB,
  			uC, vC,
  
  			x1, x2, y1, y2, z1, z2,
  			s1, s2, t1, t2, r;
  
  		var sdir = new THREE.Vector3(), tdir = new THREE.Vector3();
  
  		function handleTriangle( a, b, c ) {
  
  			xA = positions[ a * 3 ];
  			yA = positions[ a * 3 + 1 ];
  			zA = positions[ a * 3 + 2 ];
  
  			xB = positions[ b * 3 ];
  			yB = positions[ b * 3 + 1 ];
  			zB = positions[ b * 3 + 2 ];
  
  			xC = positions[ c * 3 ];
  			yC = positions[ c * 3 + 1 ];
  			zC = positions[ c * 3 + 2 ];
  
  			uA = uvs[ a * 2 ];
  			vA = uvs[ a * 2 + 1 ];
  
  			uB = uvs[ b * 2 ];
  			vB = uvs[ b * 2 + 1 ];
  
  			uC = uvs[ c * 2 ];
  			vC = uvs[ c * 2 + 1 ];
  
  			x1 = xB - xA;
  			x2 = xC - xA;
  
  			y1 = yB - yA;
  			y2 = yC - yA;
  
  			z1 = zB - zA;
  			z2 = zC - zA;
  
  			s1 = uB - uA;
  			s2 = uC - uA;
  
  			t1 = vB - vA;
  			t2 = vC - vA;
  
  			r = 1.0 / ( s1 * t2 - s2 * t1 );
  
  			sdir.set(
  				( t2 * x1 - t1 * x2 ) * r,
  				( t2 * y1 - t1 * y2 ) * r,
  				( t2 * z1 - t1 * z2 ) * r
  			);
  
  			tdir.set(
  				( s1 * x2 - s2 * x1 ) * r,
  				( s1 * y2 - s2 * y1 ) * r,
  				( s1 * z2 - s2 * z1 ) * r
  			);
  
  			tan1[ a ].add( sdir );
  			tan1[ b ].add( sdir );
  			tan1[ c ].add( sdir );
  
  			tan2[ a ].add( tdir );
  			tan2[ b ].add( tdir );
  			tan2[ c ].add( tdir );
  
  		}
  
  		var i, il;
  		var j, jl;
  		var iA, iB, iC;
  
  		var offsets = this.offsets;
  
  		for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
  
  			var start = offsets[ j ].start;
  			var count = offsets[ j ].count;
  			var index = offsets[ j ].index;
  
  			for ( i = start, il = start + count; i < il; i += 3 ) {
  
  				iA = index + indices[ i ];
  				iB = index + indices[ i + 1 ];
  				iC = index + indices[ i + 2 ];
  
  				handleTriangle( iA, iB, iC );
  
  			}
  
  		}
  
  		var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3();
  		var n = new THREE.Vector3(), n2 = new THREE.Vector3();
  		var w, t, test;
  		var nx, ny, nz;
  
  		function handleVertex( v ) {
  
  			n.x = normals[ v * 3 ];
  			n.y = normals[ v * 3 + 1 ];
  			n.z = normals[ v * 3 + 2 ];
  
  			n2.copy( n );
  
  			t = tan1[ v ];
  
  			// Gram-Schmidt orthogonalize
  
  			tmp.copy( t );
  			tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();
  
  			// Calculate handedness
  
  			tmp2.crossVectors( n2, t );
  			test = tmp2.dot( tan2[ v ] );
  			w = ( test < 0.0 ) ? -1.0 : 1.0;
  
  			tangents[ v * 4 ] 	  = tmp.x;
  			tangents[ v * 4 + 1 ] = tmp.y;
  			tangents[ v * 4 + 2 ] = tmp.z;
  			tangents[ v * 4 + 3 ] = w;
  
  		}
  
  		for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
  
  			var start = offsets[ j ].start;
  			var count = offsets[ j ].count;
  			var index = offsets[ j ].index;
  
  			for ( i = start, il = start + count; i < il; i += 3 ) {
  
  				iA = index + indices[ i ];
  				iB = index + indices[ i + 1 ];
  				iC = index + indices[ i + 2 ];
  
  				handleVertex( iA );
  				handleVertex( iB );
  				handleVertex( iC );
  
  			}
  
  		}
  
  		this.hasTangents = true;
  		this.tangentsNeedUpdate = true;
  
  	},
  
  	dispose: function () {
  
  		this.dispatchEvent( { type: 'dispose' } );
  
  	}
  
  };
  
  