Browse Source

Merging with @alteredq's dev branch.

Mr.doob 14 years ago
parent
commit
20e155ae35

File diff suppressed because it is too large
+ 91 - 91
build/Three.js


+ 1 - 1
build/custom/ThreeCanvas.js

@@ -53,7 +53,7 @@ THREE.Quaternion.prototype={set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;thi
 d;this.z=a.z*d;this.w=Math.cos(c);return this},calculateW:function(){this.w=-Math.sqrt(Math.abs(1-this.x*this.x-this.y*this.y-this.z*this.z));return this},inverse:function(){this.x*=-1;this.y*=-1;this.z*=-1;return this},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},normalize:function(){var a=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);a==0?this.w=this.z=this.y=this.x=0:(a=1/a,this.x*=a,this.y*=a,this.z*=a,this.w*=a);return this},
 multiplySelf:function(a){var b=this.x,c=this.y,d=this.z,f=this.w,g=a.x,e=a.y,h=a.z,a=a.w;this.x=b*a+f*g+c*h-d*e;this.y=c*a+f*e+d*g-b*h;this.z=d*a+f*h+b*e-c*g;this.w=f*a-b*g-c*e-d*h;return this},multiply:function(a,b){this.x=a.x*b.w+a.y*b.z-a.z*b.y+a.w*b.x;this.y=-a.x*b.z+a.y*b.w+a.z*b.x+a.w*b.y;this.z=a.x*b.y-a.y*b.x+a.z*b.w+a.w*b.z;this.w=-a.x*b.x-a.y*b.y-a.z*b.z+a.w*b.w;return this},multiplyVector3:function(a,b){b||(b=a);var c=a.x,d=a.y,f=a.z,g=this.x,e=this.y,h=this.z,i=this.w,l=i*c+e*f-h*d,m=
 i*d+h*c-g*f,j=i*f+g*d-e*c,c=-g*c-e*d-h*f;b.x=l*i+c*-g+m*-h-j*-e;b.y=m*i+c*-e+j*-g-l*-h;b.z=j*i+c*-h+l*-e-m*-g;return b}};
-THREE.Quaternion.slerp=function(a,b,c,d){var f=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(f)>=1)return c.w=a.w,c.x=a.x,c.y=a.y,c.z=a.z,c;var g=Math.acos(f),e=Math.sqrt(1-f*f);if(Math.abs(e)<0.001)return c.w=0.5*(a.w+b.w),c.x=0.5*(a.x+b.x),c.y=0.5*(a.y+b.y),c.z=0.5*(a.z+b.z),c;f=Math.sin((1-d)*g)/e;d=Math.sin(d*g)/e;c.w=a.w*f+b.w*d;c.x=a.x*f+b.x*d;c.y=a.y*f+b.y*d;c.z=a.z*f+b.z*d;return c};THREE.Vertex=function(a){this.position=a||new THREE.Vector3};
+THREE.Quaternion.slerp=function(a,b,c,d){var f=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(f)>=1)return c.w=a.w,c.x=a.x,c.y=a.y,c.z=a.z,c;var g=Math.acos(f),e=Math.sqrt(1-f*f);if(Math.abs(e)<0.0010)return c.w=0.5*(a.w+b.w),c.x=0.5*(a.x+b.x),c.y=0.5*(a.y+b.y),c.z=0.5*(a.z+b.z),c;f=Math.sin((1-d)*g)/e;d=Math.sin(d*g)/e;c.w=a.w*f+b.w*d;c.x=a.x*f+b.x*d;c.y=a.y*f+b.y*d;c.z=a.z*f+b.z*d;return c};THREE.Vertex=function(a){this.position=a||new THREE.Vector3};
 THREE.Face3=function(a,b,c,d,f,g){this.a=a;this.b=b;this.c=c;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.color=f instanceof THREE.Color?f:new THREE.Color;this.vertexColors=f instanceof Array?f:[];this.vertexTangents=[];this.materials=g instanceof Array?g:[g];this.centroid=new THREE.Vector3};
 THREE.Face4=function(a,b,c,d,f,g,e){this.a=a;this.b=b;this.c=c;this.d=d;this.normal=f instanceof THREE.Vector3?f:new THREE.Vector3;this.vertexNormals=f instanceof Array?f:[];this.color=g instanceof THREE.Color?g:new THREE.Color;this.vertexColors=g instanceof Array?g:[];this.vertexTangents=[];this.materials=e instanceof Array?e:[e];this.centroid=new THREE.Vector3};THREE.UV=function(a,b){this.set(a||0,b||0)};
 THREE.UV.prototype={set:function(a,b){this.u=a;this.v=b;return this},copy:function(a){this.set(a.u,a.v);return this}};THREE.Geometry=function(){this.id="Geometry"+THREE.GeometryIdCounter++;this.vertices=[];this.colors=[];this.faces=[];this.edges=[];this.faceUvs=[[]];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphColors=[];this.skinWeights=[];this.skinIndices=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1};

+ 1 - 1
build/custom/ThreeDOM.js

@@ -53,7 +53,7 @@ THREE.Quaternion.prototype={set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;thi
 d;this.z=a.z*d;this.w=Math.cos(c);return this},calculateW:function(){this.w=-Math.sqrt(Math.abs(1-this.x*this.x-this.y*this.y-this.z*this.z));return this},inverse:function(){this.x*=-1;this.y*=-1;this.z*=-1;return this},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},normalize:function(){var a=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);a==0?this.w=this.z=this.y=this.x=0:(a=1/a,this.x*=a,this.y*=a,this.z*=a,this.w*=a);return this},
 multiplySelf:function(a){var b=this.x,c=this.y,d=this.z,e=this.w,g=a.x,f=a.y,h=a.z,a=a.w;this.x=b*a+e*g+c*h-d*f;this.y=c*a+e*f+d*g-b*h;this.z=d*a+e*h+b*f-c*g;this.w=e*a-b*g-c*f-d*h;return this},multiply:function(a,b){this.x=a.x*b.w+a.y*b.z-a.z*b.y+a.w*b.x;this.y=-a.x*b.z+a.y*b.w+a.z*b.x+a.w*b.y;this.z=a.x*b.y-a.y*b.x+a.z*b.w+a.w*b.z;this.w=-a.x*b.x-a.y*b.y-a.z*b.z+a.w*b.w;return this},multiplyVector3:function(a,b){b||(b=a);var c=a.x,d=a.y,e=a.z,g=this.x,f=this.y,h=this.z,j=this.w,i=j*c+f*e-h*d,k=
 j*d+h*c-g*e,l=j*e+g*d-f*c,c=-g*c-f*d-h*e;b.x=i*j+c*-g+k*-h-l*-f;b.y=k*j+c*-f+l*-g-i*-h;b.z=l*j+c*-h+i*-f-k*-g;return b}};
-THREE.Quaternion.slerp=function(a,b,c,d){var e=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(e)>=1)return c.w=a.w,c.x=a.x,c.y=a.y,c.z=a.z,c;var g=Math.acos(e),f=Math.sqrt(1-e*e);if(Math.abs(f)<0.001)return c.w=0.5*(a.w+b.w),c.x=0.5*(a.x+b.x),c.y=0.5*(a.y+b.y),c.z=0.5*(a.z+b.z),c;e=Math.sin((1-d)*g)/f;d=Math.sin(d*g)/f;c.w=a.w*e+b.w*d;c.x=a.x*e+b.x*d;c.y=a.y*e+b.y*d;c.z=a.z*e+b.z*d;return c};THREE.Vertex=function(a){this.position=a||new THREE.Vector3};
+THREE.Quaternion.slerp=function(a,b,c,d){var e=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(e)>=1)return c.w=a.w,c.x=a.x,c.y=a.y,c.z=a.z,c;var g=Math.acos(e),f=Math.sqrt(1-e*e);if(Math.abs(f)<0.0010)return c.w=0.5*(a.w+b.w),c.x=0.5*(a.x+b.x),c.y=0.5*(a.y+b.y),c.z=0.5*(a.z+b.z),c;e=Math.sin((1-d)*g)/f;d=Math.sin(d*g)/f;c.w=a.w*e+b.w*d;c.x=a.x*e+b.x*d;c.y=a.y*e+b.y*d;c.z=a.z*e+b.z*d;return c};THREE.Vertex=function(a){this.position=a||new THREE.Vector3};
 THREE.Face3=function(a,b,c,d,e,g){this.a=a;this.b=b;this.c=c;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.color=e instanceof THREE.Color?e:new THREE.Color;this.vertexColors=e instanceof Array?e:[];this.vertexTangents=[];this.materials=g instanceof Array?g:[g];this.centroid=new THREE.Vector3};
 THREE.Face4=function(a,b,c,d,e,g,f){this.a=a;this.b=b;this.c=c;this.d=d;this.normal=e instanceof THREE.Vector3?e:new THREE.Vector3;this.vertexNormals=e instanceof Array?e:[];this.color=g instanceof THREE.Color?g:new THREE.Color;this.vertexColors=g instanceof Array?g:[];this.vertexTangents=[];this.materials=f instanceof Array?f:[f];this.centroid=new THREE.Vector3};THREE.UV=function(a,b){this.set(a||0,b||0)};
 THREE.UV.prototype={set:function(a,b){this.u=a;this.v=b;return this},copy:function(a){this.set(a.u,a.v);return this}};THREE.Camera=function(a,b,c,d,e){THREE.Object3D.call(this);this.fov=a||50;this.aspect=b||1;this.near=c||0.1;this.far=d||2E3;this.target=e||new THREE.Object3D;this.useTarget=!0;this.matrixWorldInverse=new THREE.Matrix4;this.projectionMatrix=null;this.updateProjectionMatrix()};THREE.Camera.prototype=new THREE.Object3D;THREE.Camera.prototype.constructor=THREE.Camera;

File diff suppressed because it is too large
+ 6 - 6
build/custom/ThreeExtras.js


+ 1 - 1
build/custom/ThreeSVG.js

@@ -53,7 +53,7 @@ THREE.Quaternion.prototype={set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;thi
 d;this.z=a.z*d;this.w=Math.cos(c);return this},calculateW:function(){this.w=-Math.sqrt(Math.abs(1-this.x*this.x-this.y*this.y-this.z*this.z));return this},inverse:function(){this.x*=-1;this.y*=-1;this.z*=-1;return this},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},normalize:function(){var a=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);a==0?this.w=this.z=this.y=this.x=0:(a=1/a,this.x*=a,this.y*=a,this.z*=a,this.w*=a);return this},
 multiplySelf:function(a){var b=this.x,c=this.y,d=this.z,e=this.w,g=a.x,f=a.y,h=a.z,a=a.w;this.x=b*a+e*g+c*h-d*f;this.y=c*a+e*f+d*g-b*h;this.z=d*a+e*h+b*f-c*g;this.w=e*a-b*g-c*f-d*h;return this},multiply:function(a,b){this.x=a.x*b.w+a.y*b.z-a.z*b.y+a.w*b.x;this.y=-a.x*b.z+a.y*b.w+a.z*b.x+a.w*b.y;this.z=a.x*b.y-a.y*b.x+a.z*b.w+a.w*b.z;this.w=-a.x*b.x-a.y*b.y-a.z*b.z+a.w*b.w;return this},multiplyVector3:function(a,b){b||(b=a);var c=a.x,d=a.y,e=a.z,g=this.x,f=this.y,h=this.z,k=this.w,l=k*c+f*e-h*d,i=
 k*d+h*c-g*e,j=k*e+g*d-f*c,c=-g*c-f*d-h*e;b.x=l*k+c*-g+i*-h-j*-f;b.y=i*k+c*-f+j*-g-l*-h;b.z=j*k+c*-h+l*-f-i*-g;return b}};
-THREE.Quaternion.slerp=function(a,b,c,d){var e=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(e)>=1)return c.w=a.w,c.x=a.x,c.y=a.y,c.z=a.z,c;var g=Math.acos(e),f=Math.sqrt(1-e*e);if(Math.abs(f)<0.001)return c.w=0.5*(a.w+b.w),c.x=0.5*(a.x+b.x),c.y=0.5*(a.y+b.y),c.z=0.5*(a.z+b.z),c;e=Math.sin((1-d)*g)/f;d=Math.sin(d*g)/f;c.w=a.w*e+b.w*d;c.x=a.x*e+b.x*d;c.y=a.y*e+b.y*d;c.z=a.z*e+b.z*d;return c};THREE.Vertex=function(a){this.position=a||new THREE.Vector3};
+THREE.Quaternion.slerp=function(a,b,c,d){var e=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(e)>=1)return c.w=a.w,c.x=a.x,c.y=a.y,c.z=a.z,c;var g=Math.acos(e),f=Math.sqrt(1-e*e);if(Math.abs(f)<0.0010)return c.w=0.5*(a.w+b.w),c.x=0.5*(a.x+b.x),c.y=0.5*(a.y+b.y),c.z=0.5*(a.z+b.z),c;e=Math.sin((1-d)*g)/f;d=Math.sin(d*g)/f;c.w=a.w*e+b.w*d;c.x=a.x*e+b.x*d;c.y=a.y*e+b.y*d;c.z=a.z*e+b.z*d;return c};THREE.Vertex=function(a){this.position=a||new THREE.Vector3};
 THREE.Face3=function(a,b,c,d,e,g){this.a=a;this.b=b;this.c=c;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.color=e instanceof THREE.Color?e:new THREE.Color;this.vertexColors=e instanceof Array?e:[];this.vertexTangents=[];this.materials=g instanceof Array?g:[g];this.centroid=new THREE.Vector3};
 THREE.Face4=function(a,b,c,d,e,g,f){this.a=a;this.b=b;this.c=c;this.d=d;this.normal=e instanceof THREE.Vector3?e:new THREE.Vector3;this.vertexNormals=e instanceof Array?e:[];this.color=g instanceof THREE.Color?g:new THREE.Color;this.vertexColors=g instanceof Array?g:[];this.vertexTangents=[];this.materials=f instanceof Array?f:[f];this.centroid=new THREE.Vector3};THREE.UV=function(a,b){this.set(a||0,b||0)};
 THREE.UV.prototype={set:function(a,b){this.u=a;this.v=b;return this},copy:function(a){this.set(a.u,a.v);return this}};THREE.Geometry=function(){this.id="Geometry"+THREE.GeometryIdCounter++;this.vertices=[];this.colors=[];this.faces=[];this.edges=[];this.faceUvs=[[]];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphColors=[];this.skinWeights=[];this.skinIndices=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1};

File diff suppressed because it is too large
+ 50 - 50
build/custom/ThreeWebGL.js


+ 349 - 0
examples/canvas_geometry_shape.html

@@ -0,0 +1,349 @@
+<!DOCTYPE HTML>
+<html lang="en">
+	<head>
+		<title>three.js canvas - geometry - shapes</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
+		<style type="text/css">
+			body {
+				font-family: Monospace;
+				background-color: #f0f0f0;
+				margin: 0px;
+				overflow: hidden;
+			}
+		</style>
+	</head>
+	<body>
+		<canvas id="debug" style="position:absolute; left:100px"></canvas>
+
+		<script type="text/javascript" src="../build/Three.js"></script>
+
+		<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>
+		<script type="text/javascript" src="js/Stats.js"></script>
+
+
+		<script type="text/javascript">
+
+			var container, stats;
+
+			var camera, scene, renderer;
+
+			var text, plane;
+
+			var targetRotation = 0;
+			var targetRotationOnMouseDown = 0;
+
+			var mouseX = 0;
+			var mouseXOnMouseDown = 0;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				var info = document.createElement( 'div' );
+				info.style.position = 'absolute';
+				info.style.top = '10px';
+				info.style.width = '100%';
+				info.style.textAlign = 'center';
+				info.innerHTML = 'Simple procedurally generated 3D shapes example by <a href="http://www.lab4games.net/zz85/blog">zz85</a><br/>Drag to spin';
+				container.appendChild( info );
+
+				camera = new THREE.Camera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.y = 150;
+				camera.position.z = 500;
+				camera.target.position.y = 150;
+
+				scene = new THREE.Scene();
+
+				parent = new THREE.Object3D();
+				parent.position.y = 50;
+				scene.addChild( parent );
+
+				function addGeometry( geometry, color, x, y, z, rx, ry, rz, s ) {
+
+					var mesh = new THREE.Mesh( geometry, [ new THREE.MeshBasicMaterial( { color: color } ), new THREE.MeshBasicMaterial( { color: 0x000000, wireframe: true } ) ] );
+					mesh.position.set( x, y, z );
+					mesh.rotation.set( rx, ry, rz );
+					mesh.scale.set( s, s, s );
+					parent.addChild( mesh );
+
+				}
+
+				var extrudeSettings = {	amount: 20 };
+
+				// California
+
+				var californiaPts = [];
+
+				californiaPts.push( new THREE.Vector2 ( 610, 320 ) );
+				californiaPts.push( new THREE.Vector2 ( 450, 300 ) );
+				californiaPts.push( new THREE.Vector2 ( 392, 392 ) );
+				californiaPts.push( new THREE.Vector2 ( 266, 438 ) );
+				californiaPts.push( new THREE.Vector2 ( 190, 570 ) );
+				californiaPts.push( new THREE.Vector2 ( 190, 600 ) );
+				californiaPts.push( new THREE.Vector2 ( 160, 620 ) );
+				californiaPts.push( new THREE.Vector2 ( 160, 650 ) );
+				californiaPts.push( new THREE.Vector2 ( 180, 640 ) );
+				californiaPts.push( new THREE.Vector2 ( 165, 680 ) );
+				californiaPts.push( new THREE.Vector2 ( 150, 670 ) );
+				californiaPts.push( new THREE.Vector2 (  90, 737 ) );
+				californiaPts.push( new THREE.Vector2 (  80, 795 ) );
+				californiaPts.push( new THREE.Vector2 (  50, 835 ) );
+				californiaPts.push( new THREE.Vector2 (  64, 870 ) );
+				californiaPts.push( new THREE.Vector2 (  60, 945 ) );
+				californiaPts.push( new THREE.Vector2 ( 300, 945 ) );
+				californiaPts.push( new THREE.Vector2 ( 300, 743 ) );
+				californiaPts.push( new THREE.Vector2 ( 600, 473 ) );
+				californiaPts.push( new THREE.Vector2 ( 626, 425 ) );
+				californiaPts.push( new THREE.Vector2 ( 600, 370 ) );
+				californiaPts.push( new THREE.Vector2 ( 610, 320 ) );
+
+				var californiaShape = new THREE.Shape( californiaPts );
+				var california3d = new THREE.ExtrudeGeometry( californiaShape, { amount: 20	} );
+
+				// Triangle
+
+				var triangleShape = new THREE.Shape();
+				triangleShape.moveTo(  80, 20 );
+				triangleShape.lineTo(  40, 80 );
+				triangleShape.lineTo( 120, 80 );
+				triangleShape.lineTo(  80, 20 ); // close path
+
+				var triangle3d = triangleShape.extrude( extrudeSettings );
+
+
+				// Heart
+
+				var x = 0, y = 0;
+
+				var heartShape = new THREE.Shape(); // From http://blog.burlock.org/html5/130-paths
+
+				heartShape.moveTo( x + 25, y + 25 );
+				heartShape.bezierCurveTo( x + 25, y + 25, x + 20, y, x, y );
+				heartShape.bezierCurveTo( x - 30, y, x - 30, y + 35,x - 30,y + 35 );
+				heartShape.bezierCurveTo( x - 30, y + 55, x - 10, y + 77, x + 25, y + 95 );
+				heartShape.bezierCurveTo( x + 60, y + 77, x + 80, y + 55, x + 80, y + 35 );
+				heartShape.bezierCurveTo( x + 80, y + 35, x + 80, y, x + 50, y );
+				heartShape.bezierCurveTo( x + 35, y, x + 25, y + 25, x + 25, y + 25 );
+
+				var heart3d = heartShape.extrude( extrudeSettings );
+
+				//heartShape.debug( document.getElementById("debug") );
+
+				// Square
+
+				var sqLength = 80;
+
+				var squareShape = new THREE.Shape();
+				squareShape.moveTo( 0,0 );
+				squareShape.lineTo( 0, sqLength );
+				squareShape.lineTo( sqLength, sqLength );
+				squareShape.lineTo( sqLength, 0 );
+				squareShape.lineTo( 0, 0 );
+
+				var square3d = squareShape.extrude( extrudeSettings );
+
+				// Rectangle
+
+				var rectLength = 120, rectWidth = 40;
+
+				var rectShape = new THREE.Shape();
+				rectShape.moveTo( 0,0 );
+				rectShape.lineTo( 0, rectWidth );
+				rectShape.lineTo( rectLength, rectWidth );
+				rectShape.lineTo( rectLength, 0 );
+				rectShape.lineTo( 0, 0 );
+
+				var rect3d = rectShape.extrude( extrudeSettings );
+
+				// Rounded rectangle
+
+				var roundedRectShape = new THREE.Shape();
+				roundedRect( roundedRectShape, 0, 0, 50, 50, 20 );
+
+				var roundedRect3d = roundedRectShape.extrude( extrudeSettings );
+
+				function roundedRect( ctx, x, y, width, height, radius ){
+
+					ctx.moveTo( x, y + radius );
+					ctx.lineTo( x, y + height - radius );
+					ctx.quadraticCurveTo( x, y + height, x + radius, y + height );
+					ctx.lineTo( x + width - radius, y + height) ;
+					ctx.quadraticCurveTo( x + width, y + height, x + width, y + height - radius );
+					ctx.lineTo( x + width, y + radius );
+					ctx.quadraticCurveTo( x + width, y, x + width - radius, y );
+					ctx.lineTo( x + radius, y );
+					ctx.quadraticCurveTo( x, y, x, y + radius );
+
+				}
+
+				// Circle
+
+				var circleRadius = 40;
+				var circleShape = new THREE.Shape();
+				circleShape.moveTo( 0, circleRadius );
+				circleShape.quadraticCurveTo( circleRadius, circleRadius, circleRadius, 0 );
+				circleShape.quadraticCurveTo( circleRadius, -circleRadius, 0, -circleRadius );
+				circleShape.quadraticCurveTo( -circleRadius, -circleRadius, -circleRadius, 0 );
+				circleShape.quadraticCurveTo( -circleRadius, circleRadius, 0, circleRadius );
+
+				var circle3d = circleShape.extrude( extrudeSettings );
+
+				// Fish
+
+				x = y = 0;
+
+				var fishShape = new THREE.Shape();
+
+				fishShape.moveTo( x, y );
+				fishShape.quadraticCurveTo( x + 50, y + 80, x + 90, y + 10 );
+				fishShape.quadraticCurveTo( x + 100, y + 10, x + 115, y + 40 );
+				fishShape.quadraticCurveTo( x + 100, y, x + 115, y - 40 );
+				fishShape.quadraticCurveTo( x + 100, y + 10, x + 90, y - 10 );
+				fishShape.quadraticCurveTo( x + 50, y - 80, x, y );
+
+				var fish3d = fishShape.extrude( extrudeSettings );
+
+				// Spline shape + Path Extrusion
+
+				var splinepts = [];
+				splinepts.push( new THREE.Vector2 ( 350, 100 ) );
+				splinepts.push( new THREE.Vector2 ( 400, 450 ) );
+				splinepts.push( new THREE.Vector2 ( -140, 350 ) );
+				splinepts.push( new THREE.Vector2 ( 0, 0 ) );
+
+				var splineShape = new THREE.Shape(  );
+				splineShape.moveTo( 0, 0 );
+				splineShape.splineThru( splinepts );
+
+				//splineShape.debug( document.getElementById("debug") );
+
+				var extrudePath = new THREE.Path();
+
+				extrudePath.lineTo( 10, 10 );
+				extrudePath.quadraticCurveTo( 80, 60, 160, 10 );
+				extrudePath.quadraticCurveTo( 240, -40, 320, 10 );
+
+				extrudeSettings.path = extrudePath;
+
+				var splineShape3d = splineShape.extrude( extrudeSettings );
+
+				addGeometry( california3d, 	0xffaa00, -300, -100, 0,     0, 0, 0, 0.25 );
+				addGeometry( triangle3d, 	0xffee00, -180,    0, 0,     0, 0, 0, 1 );
+				addGeometry( roundedRect3d, 0x005500, -150,  150, 0,     0, 0, 0, 1 );
+				addGeometry( square3d, 		0x0055ff,  150,  100, 0,     0, 0, 0, 1 );
+				addGeometry( heart3d, 		0xff1100,    0,  100, 0,   3.14, 0, 0, 1 );
+				addGeometry( circle3d, 		0x00ff11,  150,    0, 0,     0, 0, 0, 1 );
+				addGeometry( fish3d, 		0x222222,  -50,  200, 0,     0, 0, 0, 1 );
+				addGeometry( splineShape3d, 0x888888,  -50,  -100, -50,     0, 0, 0, 0.2 );
+
+				renderer = new THREE.CanvasRenderer();
+                renderer.setSize( window.innerWidth, window.innerHeight );
+
+				container.appendChild( renderer.domElement );
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.domElement );
+
+				document.addEventListener( 'mousedown', onDocumentMouseDown, false );
+				document.addEventListener( 'touchstart', onDocumentTouchStart, false );
+				document.addEventListener( 'touchmove', onDocumentTouchMove, false );
+
+			}
+
+			//
+
+			function onDocumentMouseDown( event ) {
+
+				event.preventDefault();
+
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+				document.addEventListener( 'mouseup', onDocumentMouseUp, false );
+				document.addEventListener( 'mouseout', onDocumentMouseOut, false );
+
+				mouseXOnMouseDown = event.clientX - windowHalfX;
+				targetRotationOnMouseDown = targetRotation;
+
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				mouseX = event.clientX - windowHalfX;
+
+				targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.02;
+
+			}
+
+			function onDocumentMouseUp( event ) {
+
+				document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
+				document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
+				document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
+
+			}
+
+			function onDocumentMouseOut( event ) {
+
+				document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
+				document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
+				document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
+
+			}
+
+			function onDocumentTouchStart( event ) {
+
+				if ( event.touches.length == 1 ) {
+
+					event.preventDefault();
+
+					mouseXOnMouseDown = event.touches[ 0 ].pageX - windowHalfX;
+					targetRotationOnMouseDown = targetRotation;
+
+				}
+
+			}
+
+			function onDocumentTouchMove( event ) {
+
+				if ( event.touches.length == 1 ) {
+
+					event.preventDefault();
+
+					mouseX = event.touches[ 0 ].pageX - windowHalfX;
+					targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.05;
+
+				}
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				parent.rotation.y += ( targetRotation - parent.rotation.y ) * 0.05;
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 4 - 2
examples/canvas_sandbox.html

@@ -62,7 +62,6 @@
 		<script type="text/javascript" src="../src/objects/Bone.js"></script>
 		<script type="text/javascript" src="../src/objects/SkinnedMesh.js"></script>
 		<script type="text/javascript" src="../src/objects/Ribbon.js"></script>
-		<script type="text/javascript" src="../src/objects/Sound.js"></script>
 		<script type="text/javascript" src="../src/objects/LOD.js"></script>
 		<script type="text/javascript" src="../src/objects/ShadowVolume.js"></script>
 		<script type="text/javascript" src="../src/objects/Sprite.js"></script>
@@ -76,7 +75,6 @@
 		<script type="text/javascript" src="../src/renderers/WebGLShaders.js"></script>
 		<script type="text/javascript" src="../src/renderers/WebGLRenderer.js"></script>
 		<script type="text/javascript" src="../src/renderers/WebGLRenderTarget.js"></script>
-		<script type="text/javascript" src="../src/renderers/SoundRenderer.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableVertex.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace3.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace4.js"></script>
@@ -95,8 +93,12 @@
 		<script type="text/javascript" src="../src/extras/cameras/FlyCamera.js"></script>
 		<script type="text/javascript" src="../src/extras/cameras/RollCamera.js"></script>
 		<script type="text/javascript" src="../src/extras/cameras/TrackballCamera.js"></script>
+		<script type="text/javascript" src="../src/extras/cameras/QuakeCamera.js"></script>
+		<script type="text/javascript" src="../src/extras/geometries/Path.js"></script>
+		<script type="text/javascript" src="../src/extras/geometries/Shape.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/CubeGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/CylinderGeometry.js"></script>
+		<script type="text/javascript" src="../src/extras/geometries/ExtrudeGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/IcosahedronGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/LatheGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/PlaneGeometry.js"></script>

+ 6 - 4
examples/misc_ubiquity_test.html

@@ -13,7 +13,7 @@
 	</head>
 	<body>
 
-		<!-- 
+		<!--
 		<script type="text/javascript" src="../build/Three.js"></script>
 		-->
 
@@ -63,7 +63,6 @@
 		<script type="text/javascript" src="../src/objects/Bone.js"></script>
 		<script type="text/javascript" src="../src/objects/SkinnedMesh.js"></script>
 		<script type="text/javascript" src="../src/objects/Ribbon.js"></script>
-		<script type="text/javascript" src="../src/objects/Sound.js"></script>
 		<script type="text/javascript" src="../src/objects/LOD.js"></script>
 		<script type="text/javascript" src="../src/objects/ShadowVolume.js"></script>
 		<script type="text/javascript" src="../src/objects/Sprite.js"></script>
@@ -77,7 +76,6 @@
 		<script type="text/javascript" src="../src/renderers/WebGLShaders.js"></script>
 		<script type="text/javascript" src="../src/renderers/WebGLRenderer.js"></script>
 		<script type="text/javascript" src="../src/renderers/WebGLRenderTarget.js"></script>
-		<script type="text/javascript" src="../src/renderers/SoundRenderer.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableVertex.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace3.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace4.js"></script>
@@ -96,8 +94,12 @@
 		<script type="text/javascript" src="../src/extras/cameras/FlyCamera.js"></script>
 		<script type="text/javascript" src="../src/extras/cameras/RollCamera.js"></script>
 		<script type="text/javascript" src="../src/extras/cameras/TrackballCamera.js"></script>
+		<script type="text/javascript" src="../src/extras/cameras/QuakeCamera.js"></script>
+		<script type="text/javascript" src="../src/extras/geometries/Path.js"></script>
+		<script type="text/javascript" src="../src/extras/geometries/Shape.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/CubeGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/CylinderGeometry.js"></script>
+		<script type="text/javascript" src="../src/extras/geometries/ExtrudeGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/IcosahedronGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/LatheGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/PlaneGeometry.js"></script>
@@ -115,7 +117,7 @@
 		<script type="text/javascript" src="../src/extras/physics/CollisionUtils.js"></script>
 		<script type="text/javascript" src="../src/extras/renderers/AnaglyphWebGLRenderer.js"></script>
 		<script type="text/javascript" src="../src/extras/renderers/CrosseyedWebGLRenderer.js"></script>
-		
+
 		<script type="text/javascript" src="obj/Qrcode.js"></script>
 
 		<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>

BIN
examples/textures/sprites/disc.png


BIN
examples/textures/sprites/spark1.png


+ 3 - 0
examples/webgl_custom_attributes.html

@@ -31,6 +31,7 @@
 		<div id="container"></div>
 
 		<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>
+		<script type="text/javascript" src="js/Detector.js"></script>
 		<script type="text/javascript" src="js/Stats.js"></script>
 
 		<script src="../build/Three.js"></script>
@@ -84,6 +85,8 @@
 
 		<script type="text/javascript">
 
+		if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
 		var renderer, scene, camera, stats;
 
 		var sphere, uniforms, attributes;

+ 224 - 0
examples/webgl_custom_attributes_particles.html

@@ -0,0 +1,224 @@
+<!doctype html>
+<html>
+	<head>
+		<meta charset="utf-8" />
+		<title>three.js webgl - custom attributes [particles]</title>
+		<style>
+			body {
+				color: #ffffff;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				font-weight: bold;
+
+				background-color: #000000;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				color: #fff;
+				background-color: rgba( 0, 0, 0, 0.75 );
+				position: relative;
+				top: 0px; width: 100%;
+				padding: 5px;
+				z-index:100;
+				width:33em;
+				margin:0 auto -2em;
+			}
+			a { color: #ff0000 }
+		</style>
+	</head>
+
+	<body>
+		<div id="info"><a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> - custom attributes example - particles</div>
+		<div id="container"></div>
+
+		<script src="../build/Three.js"></script>
+
+		<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>
+		<script type="text/javascript" src="js/Detector.js"></script>
+		<script type="text/javascript" src="js/Stats.js"></script>
+
+		<script type="x-shader/x-vertex" id="vertexshader">
+
+			uniform float amplitude;
+			attribute float size;
+			attribute vec3 customColor;
+
+			varying vec3 vColor;
+
+			void main() {
+
+				vColor = customColor;
+
+				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
+
+				//gl_PointSize = size;
+				gl_PointSize = size * ( 300.0 / length( mvPosition.xyz ) );
+
+				gl_Position = projectionMatrix * mvPosition;
+
+			}
+
+		</script>
+
+		<script type="x-shader/x-fragment" id="fragmentshader">
+
+			uniform vec3 color;
+			uniform sampler2D texture;
+
+			varying vec3 vColor;
+
+			void main() {
+
+				gl_FragColor = vec4( color * vColor, 1.0 );
+				gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
+
+			}
+
+		</script>
+
+
+		<script type="text/javascript">
+
+		if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+		var renderer, scene, camera, stats;
+
+		var sphere, uniforms, attributes;
+
+		var noise = [];
+
+		var WIDTH = window.innerWidth,
+			HEIGHT = window.innerHeight;
+
+		init();
+		animate();
+
+		function init() {
+
+			camera = new THREE.Camera( 40, WIDTH / HEIGHT, 1, 10000 );
+			camera.position.z = 300;
+
+			scene = new THREE.Scene();
+
+			attributes = {
+
+				size: {	type: 'f', value: [] },
+				customColor: { type: 'c', value: [] }
+
+			};
+
+			uniforms = {
+
+				amplitude: { type: "f", value: 1.0 },
+				color:     { type: "c", value: new THREE.Color( 0xffffff ) },
+				texture:   { type: "t", value: 0, texture: THREE.ImageUtils.loadTexture( "textures/sprites/spark1.png" ) },
+
+			};
+
+			var shaderMaterial = new THREE.MeshShaderMaterial( {
+
+				uniforms: 		uniforms,
+				attributes:     attributes,
+				vertexShader:   document.getElementById( 'vertexshader' ).textContent,
+				fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
+
+				blending: 		THREE.AdditiveBlending,
+				depthTest: 		false,
+				transparent:	true
+
+			});
+
+
+			var radius = 200;
+			var geometry = new THREE.Geometry();
+
+			for ( var i = 0; i < 100000; i++ ) {
+
+				vector = new THREE.Vector3( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
+				vector.multiplyScalar( radius );
+
+				geometry.vertices.push( new THREE.Vertex( vector ) );
+
+			}
+
+			sphere = new THREE.ParticleSystem( geometry, shaderMaterial );
+
+			sphere.dynamic = true;
+			//sphere.sortParticles = true;
+
+			var vertices = sphere.geometry.vertices;
+			var values_size = attributes.size.value;
+			var values_color = attributes.customColor.value;
+
+
+			for( var v = 0; v < vertices.length; v++ ) {
+
+				values_size[ v ] = 10;
+				values_color[ v ] = new THREE.Color( 0xffaa00 );
+
+				if ( vertices[ v ].position.x < 0 )
+					values_color[ v ].setHSV( 0.5 + 0.1 * ( v / vertices.length ), 0.7, 0.9 );
+				else
+					values_color[ v ].setHSV( 0.0 + 0.1 * ( v / vertices.length ), 0.9, 0.9 );
+
+			}
+
+			scene.addChild( sphere );
+
+			renderer = new THREE.WebGLRenderer( { clearColor: 0x000000, clearAlpha: 1 } );
+			renderer.setSize( WIDTH, HEIGHT );
+
+			var container = document.getElementById( 'container' );
+			container.appendChild( renderer.domElement );
+
+			stats = new Stats();
+			stats.domElement.style.position = 'absolute';
+			stats.domElement.style.top = '0px';
+			container.appendChild( stats.domElement );
+
+		}
+
+		function cap( x, a, b ) {
+
+			return ( x < a ) ? a : ( ( x > b ) ? b : x );
+
+		}
+
+		var i, value;
+
+		function animate() {
+
+			requestAnimationFrame( animate );
+
+			render();
+			stats.update();
+
+		}
+
+		function render() {
+
+			var time = new Date().getTime() * 0.005;
+
+			sphere.rotation.z = 0.01 * time;
+
+			for( i = 0; i < attributes.size.value.length; i++ ) {
+
+				attributes.size.value[ i ] = 14 + 13 * Math.sin( 0.1 * i + time );
+
+
+			}
+
+			attributes.size.needsUpdate = true;
+
+			renderer.render( scene, camera );
+
+		}
+
+
+	</script>
+
+</body>
+
+</html>

+ 223 - 0
examples/webgl_custom_attributes_particles2.html

@@ -0,0 +1,223 @@
+<!doctype html>
+<html>
+	<head>
+		<meta charset="utf-8" />
+		<title>three.js webgl - custom attributes [particles][billboards]</title>
+		<style>
+			body {
+				color: #ffffff;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				font-weight: bold;
+
+				background-color: #000000;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+				z-index:100;
+			}
+
+		</style>
+	</head>
+
+	<body>
+		<div id="info"><a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> - custom attributes example - particles - billboards</div>
+		<div id="container"></div>
+
+		<script src="../build/Three.js"></script>
+
+		<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>
+		<script type="text/javascript" src="js/Detector.js"></script>
+		<script type="text/javascript" src="js/Stats.js"></script>
+
+		<script type="x-shader/x-vertex" id="vertexshader">
+
+			attribute float size;
+			attribute vec3 ca;
+
+			varying vec3 vColor;
+
+			void main() {
+
+				vColor = ca;
+
+				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
+
+				//gl_PointSize = size;
+				gl_PointSize = size * ( 300.0 / length( mvPosition.xyz ) );
+
+				gl_Position = projectionMatrix * mvPosition;
+
+			}
+
+		</script>
+
+		<script type="x-shader/x-fragment" id="fragmentshader">
+
+			uniform vec3 color;
+			uniform sampler2D texture;
+
+			varying vec3 vColor;
+
+			void main() {
+
+				gl_FragColor = vec4( color * vColor, 1.0 );
+				gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
+
+			}
+
+		</script>
+
+
+		<script type="text/javascript">
+
+		if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+		var renderer, scene, camera, stats;
+
+		var sphere, uniforms, attributes;
+
+		var vc1;
+
+		var WIDTH = window.innerWidth,
+			HEIGHT = window.innerHeight;
+
+		init();
+		animate();
+
+		function init() {
+
+			camera = new THREE.Camera( 45, WIDTH / HEIGHT, 1, 10000 );
+			camera.position.z = 300;
+
+			scene = new THREE.Scene();
+
+			attributes = {
+
+				size: {	type: 'f', value: [] },
+				ca:   {	type: 'c', value: [] }
+
+			};
+
+			uniforms = {
+
+				amplitude: { type: "f", value: 1.0 },
+				color:     { type: "c", value: new THREE.Color( 0xffffff ) },
+				texture:   { type: "t", value: 0, texture: THREE.ImageUtils.loadTexture( "textures/sprites/disc.png" ) },
+
+			};
+
+			uniforms.texture.texture.wrapS = uniforms.texture.texture.wrapT = THREE.RepeatWrapping;
+
+			var shaderMaterial = new THREE.MeshShaderMaterial( {
+
+				uniforms: 		uniforms,
+				attributes:     attributes,
+				vertexShader:   document.getElementById( 'vertexshader' ).textContent,
+				fragmentShader: document.getElementById( 'fragmentshader' ).textContent
+
+			});
+
+
+			var radius = 100, segments = 68, rings = 38;
+			var geometry = new THREE.SphereGeometry( radius, segments, rings );
+
+			vc1 = geometry.vertices.length;
+
+			var geometry2 = new THREE.CubeGeometry( 0.8 * radius, 0.8 * radius, 0.8 * radius, 10, 10, 10 );
+
+			THREE.GeometryUtils.merge( geometry, geometry2 );
+
+			sphere = new THREE.ParticleSystem( geometry, shaderMaterial );
+
+			sphere.dynamic = true;
+			sphere.sortParticles = true;
+
+			var vertices = sphere.geometry.vertices;
+			var values_size = attributes.size.value;
+			var values_color = attributes.ca.value;
+
+
+			for( var v = 0; v < vertices.length; v++ ) {
+
+				values_size[ v ] = 10;
+				values_color[ v ] = new THREE.Color( 0xffffff );
+
+				if ( v < vc1 ) {
+
+					values_color[ v ].setHSV( 0.01 + 0.1 * ( v / vc1 ), 0.99, ( vertices[ v ].position.y + radius ) / ( 2 *radius ) );
+
+				} else {
+
+					values_size[ v ] = 40;
+					values_color[ v ].setHSV( 0.6, 0.75, 0.5 + vertices[ v ].position.y / ( 0.8 * radius ) );
+
+				}
+
+			}
+
+			scene.addChild( sphere );
+
+			renderer = new THREE.WebGLRenderer( { clearColor: 0x000000, clearAlpha: 1 } );
+			renderer.setSize( WIDTH, HEIGHT );
+
+			var container = document.getElementById( 'container' );
+			container.appendChild( renderer.domElement );
+
+			stats = new Stats();
+			stats.domElement.style.position = 'absolute';
+			stats.domElement.style.top = '0px';
+			container.appendChild( stats.domElement );
+
+		}
+
+		function cap( x, a, b ) {
+
+			return ( x < a ) ? a : ( ( x > b ) ? b : x );
+
+		}
+
+		var i, value;
+
+		function animate() {
+
+			requestAnimationFrame( animate );
+
+			render();
+			stats.update();
+
+		}
+
+		function render() {
+
+			var time = new Date().getTime() * 0.005;
+
+			sphere.rotation.y = 0.02 * time;
+			sphere.rotation.z = 0.02 * time;
+
+			for( i = 0; i < attributes.size.value.length; i++ ) {
+
+				if ( i < vc1 )
+					attributes.size.value[ i ] = 16 + 12 * Math.sin( 0.1 * i + time );
+
+
+			}
+
+			attributes.size.needsUpdate = true;
+
+			renderer.render( scene, camera );
+
+		}
+
+
+	</script>
+
+</body>
+
+</html>

+ 282 - 0
examples/webgl_custom_attributes_particles3.html

@@ -0,0 +1,282 @@
+<!doctype html>
+<html>
+	<head>
+		<meta charset="utf-8" />
+		<title>three.js webgl - custom attributes [particles][billboards][alphatest]</title>
+		<style>
+			body {
+				color: #ffffff;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				font-weight: bold;
+
+				background-color: #000000;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+				z-index:100;
+			}
+
+		</style>
+	</head>
+
+	<body>
+		<div id="info"><a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> - custom attributes example - particles - billboards - alphatest</div>
+		<div id="container"></div>
+
+		<script src="../build/Three.js"></script>
+
+		<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>
+		<script type="text/javascript" src="js/Detector.js"></script>
+		<script type="text/javascript" src="js/Stats.js"></script>
+
+		<script type="x-shader/x-vertex" id="vertexshader">
+
+			attribute float size;
+			attribute vec4 ca;
+
+			varying vec4 vColor;
+
+			void main() {
+
+				vColor = ca;
+
+				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
+
+				gl_PointSize = size * ( 150.0 / length( mvPosition.xyz ) );
+
+				gl_Position = projectionMatrix * mvPosition;
+
+			}
+
+		</script>
+
+		<script type="x-shader/x-fragment" id="fragmentshader">
+
+			uniform vec3 color;
+			uniform sampler2D texture;
+
+			varying vec4 vColor;
+
+			void main() {
+
+				vec4 outColor = texture2D( texture, gl_PointCoord );
+
+				if ( outColor.a < 0.5 ) discard;
+
+				gl_FragColor = outColor * vec4( color * vColor.xyz, 1.0 );
+
+				float depth = gl_FragCoord.z / gl_FragCoord.w;
+				const vec3 fogColor = vec3( 0.0 );
+
+				float fogFactor = smoothstep( 200.0, 600.0, depth );
+				gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );
+
+			}
+
+		</script>
+
+
+		<script type="text/javascript">
+
+		if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+		var renderer, scene, camera, stats;
+
+		var object, uniforms, attributes;
+
+		var vc1;
+
+		var WIDTH = window.innerWidth,
+			HEIGHT = window.innerHeight;
+
+		init();
+		animate();
+
+		function init() {
+
+			camera = new THREE.Camera( 40, WIDTH / HEIGHT, 1, 1000 );
+			camera.position.z = 500;
+
+			scene = new THREE.Scene();
+
+			attributes = {
+
+				size: {	type: 'f', value: [] },
+				ca:   {	type: 'c', value: [] }
+
+			};
+
+			uniforms = {
+
+				amplitude: { type: "f", value: 1.0 },
+				color:     { type: "c", value: new THREE.Color( 0xffffff ) },
+				texture:   { type: "t", value: 0, texture: THREE.ImageUtils.loadTexture( "textures/sprites/ball.png" ) },
+
+			};
+
+			uniforms.texture.texture.wrapS = uniforms.texture.texture.wrapT = THREE.RepeatWrapping;
+
+			var shaderMaterial = new THREE.MeshShaderMaterial( {
+
+				uniforms: 		uniforms,
+				attributes:     attributes,
+				vertexShader:   document.getElementById( 'vertexshader' ).textContent,
+				fragmentShader: document.getElementById( 'fragmentshader' ).textContent
+
+			});
+
+
+			var radius = 100, inner = 0.6 * radius;
+			var geometry = new THREE.Geometry();
+
+			for ( var i = 0; i < 100000; i++ ) {
+
+				vector = new THREE.Vector3( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
+
+				vector.multiplyScalar( radius );
+
+				if ( ( vector.x > inner || vector.x < -inner ) ||
+				     ( vector.y > inner || vector.y < -inner ) ||
+				     ( vector.z > inner || vector.z < -inner )  )
+
+				geometry.vertices.push( new THREE.Vertex( vector ) );
+
+			}
+
+			vc1 = geometry.vertices.length;
+
+			var m, dummyMaterial = new THREE.MeshFaceMaterial();
+
+			radius = 200;
+			var geometry2 = new THREE.CubeGeometry( radius, 0.1 * radius, 0.1 * radius, 50, 5, 5 );
+
+			function addGeo( geo, x, y, z, ry ) {
+
+				m = new THREE.Mesh( geo, dummyMaterial );
+				m.rotation.y = ry;
+				m.position.set( x, y, z );
+				THREE.GeometryUtils.merge( geometry, m );
+
+			}
+
+			// side 1
+
+			addGeo( geometry2, 0,  110,  110, 0 );
+			addGeo( geometry2, 0,  110, -110, 0 );
+			addGeo( geometry2, 0, -110,  110, 0 );
+			addGeo( geometry2, 0, -110, -110, 0 );
+
+			// side 2
+
+			addGeo( geometry2,  110,  110, 0, 1.57 );
+			addGeo( geometry2,  110, -110, 0, 1.57 );
+			addGeo( geometry2, -110,  110, 0, 1.57 );
+			addGeo( geometry2, -110, -110, 0, 1.57 );
+
+			// corner edges
+
+			var geometry3 = new THREE.CubeGeometry( 0.1 * radius, radius * 1.2, 0.1 * radius, 5, 60, 5 );
+
+			addGeo( geometry3,  110, 0,  110, 0 );
+			addGeo( geometry3,  110, 0, -110, 0 );
+			addGeo( geometry3, -110, 0,  110, 0 );
+			addGeo( geometry3, -110, 0, -110, 0 );
+
+			// particle system
+
+			object = new THREE.ParticleSystem( geometry, shaderMaterial );
+			object.dynamic = true;
+
+			// custom attributes
+
+			var vertices = object.geometry.vertices;
+
+			var values_size = attributes.size.value;
+			var values_color = attributes.ca.value;
+
+			for( var v = 0; v < vertices.length; v++ ) {
+
+				values_size[ v ] = 10;
+				values_color[ v ] = new THREE.Color( 0xffffff );
+
+				if ( v < vc1 ) {
+
+					values_color[ v ].setHSV( 0.5 + 0.2 * ( v / vc1 ), 0.99, 1.0 );
+
+				} else {
+
+					values_size[ v ] = 55;
+					values_color[ v ].setHSV( 0.1, 0.99, 1.0 );
+
+				}
+
+			}
+
+			console.log( vertices.length );
+
+			scene.addObject( object );
+
+			renderer = new THREE.WebGLRenderer( { clearColor: 0x000000, clearAlpha: 1 } );
+			renderer.setSize( WIDTH, HEIGHT );
+
+			var container = document.getElementById( 'container' );
+			container.appendChild( renderer.domElement );
+
+			stats = new Stats();
+			stats.domElement.style.position = 'absolute';
+			stats.domElement.style.top = '0px';
+			container.appendChild( stats.domElement );
+
+		}
+
+		function cap( x, a, b ) {
+
+			return ( x < a ) ? a : ( ( x > b ) ? b : x );
+
+		}
+
+		var i, value;
+
+		function animate() {
+
+			requestAnimationFrame( animate );
+
+			render();
+			stats.update();
+
+		}
+
+		function render() {
+
+			var time = new Date().getTime() * 0.01;
+
+			object.rotation.y = 0.02 * time;
+			object.rotation.z = 0.02 * time;
+
+			for( i = 0; i < attributes.size.value.length; i++ ) {
+
+				if ( i < vc1 )
+					attributes.size.value[ i ] = 26 + 32 * Math.sin( 0.1 * i + 0.6 * time );
+
+
+			}
+
+			attributes.size.needsUpdate = true;
+
+			renderer.render( scene, camera );
+
+		}
+
+
+	</script>
+
+</body>
+
+</html>

+ 1 - 1
examples/webgl_geometry_large_mesh.html

@@ -142,7 +142,7 @@
 
 				if ( render_gl ) {
 					try {
-						webglRenderer = new THREE.WebGLRenderer();
+						webglRenderer = new THREE.WebGLRenderer( { antialias: true } );
 						webglRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
 						webglRenderer.domElement.style.position = "relative";
 						container.appendChild( webglRenderer.domElement );

+ 1 - 1
examples/webgl_geometry_minecraft.html

@@ -144,7 +144,7 @@
 						mesh.position.y = h * 100;
 						mesh.position.z = z * 100 - worldHalfDepth * 100;
 
-						GeometryUtils.merge( geometry, mesh );
+						THREE.GeometryUtils.merge( geometry, mesh );
 
 					}
 

+ 3 - 3
examples/webgl_geometry_minecraft_ao.html

@@ -431,7 +431,7 @@
 						mesh.position.y = h * 100;
 						mesh.position.z = z * 100 - worldHalfDepth * 100;
 
-						GeometryUtils.merge( geometry, mesh );
+						THREE.GeometryUtils.merge( geometry, mesh );
 
 					}
 
@@ -521,7 +521,7 @@
 				canvas.width = canvas.height = size;
 
 				var texture = new THREE.Texture( canvas, new THREE.UVMapping(), THREE.ClampToEdgeWrapping, THREE.ClampToEdgeWrapping, THREE.NearestFilter, THREE.LinearMipMapLinearFilter );
-				
+
 				function generateTexture() {
 
 					if( count == 3 ) {
@@ -578,7 +578,7 @@
 
 				var texture = new THREE.Texture( canvas, new THREE.UVMapping(), THREE.ClampToEdgeWrapping, THREE.ClampToEdgeWrapping, THREE.NearestFilter, THREE.LinearMipMapLinearFilter );
 				texture.needsUpdate = true;
-				
+
 				return new THREE.MeshLambertMaterial( { map: texture } );
 
 			}

+ 5 - 3
examples/webgl_sandbox.html

@@ -20,7 +20,7 @@
 				font-family:Monospace;
 				font-size:13px;
 				text-align:center;
-				z-index:1000; 
+				z-index:1000;
 			}
 
 			a {
@@ -81,7 +81,6 @@
 		<script type="text/javascript" src="../src/objects/Bone.js"></script>
 		<script type="text/javascript" src="../src/objects/SkinnedMesh.js"></script>
 		<script type="text/javascript" src="../src/objects/Ribbon.js"></script>
-		<script type="text/javascript" src="../src/objects/Sound.js"></script>
 		<script type="text/javascript" src="../src/objects/LOD.js"></script>
 		<script type="text/javascript" src="../src/objects/ShadowVolume.js"></script>
 		<script type="text/javascript" src="../src/objects/Sprite.js"></script>
@@ -95,7 +94,6 @@
 		<script type="text/javascript" src="../src/renderers/WebGLShaders.js"></script>
 		<script type="text/javascript" src="../src/renderers/WebGLRenderer.js"></script>
 		<script type="text/javascript" src="../src/renderers/WebGLRenderTarget.js"></script>
-		<script type="text/javascript" src="../src/renderers/SoundRenderer.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableVertex.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace3.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace4.js"></script>
@@ -114,8 +112,12 @@
 		<script type="text/javascript" src="../src/extras/cameras/FlyCamera.js"></script>
 		<script type="text/javascript" src="../src/extras/cameras/RollCamera.js"></script>
 		<script type="text/javascript" src="../src/extras/cameras/TrackballCamera.js"></script>
+		<script type="text/javascript" src="../src/extras/cameras/QuakeCamera.js"></script>
+		<script type="text/javascript" src="../src/extras/geometries/Path.js"></script>
+		<script type="text/javascript" src="../src/extras/geometries/Shape.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/CubeGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/CylinderGeometry.js"></script>
+		<script type="text/javascript" src="../src/extras/geometries/ExtrudeGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/IcosahedronGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/LatheGeometry.js"></script>
 		<script type="text/javascript" src="../src/extras/geometries/PlaneGeometry.js"></script>

+ 5 - 1
src/extras/GeometryUtils.js

@@ -1,4 +1,8 @@
-var GeometryUtils = {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.GeometryUtils = {
 
 	merge: function ( geometry1, object2 /* mesh | geometry */ ) {
 

+ 2 - 2
src/extras/ShaderUtils.js

@@ -202,7 +202,7 @@ THREE.ShaderUtils = {
 						"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
 
 							"vec3 pointVector = normalize( vPointLight[ i ].xyz );",
-							"vec3 pointHalfVector = normalize( vPointLight[ i ].xyz + vViewPosition );",
+							"vec3 pointHalfVector = normalize( vPointLight[ i ].xyz + viewPosition );",
 							"float pointDistance = vPointLight[ i ].w;",
 
 							"float pointDotNormalHalf = dot( normal, pointHalfVector );",
@@ -229,7 +229,7 @@ THREE.ShaderUtils = {
 							"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
 
 							"vec3 dirVector = normalize( lDirection.xyz );",
-							"vec3 dirHalfVector = normalize( lDirection.xyz + vViewPosition );",
+							"vec3 dirHalfVector = normalize( lDirection.xyz + viewPosition );",
 
 							"float dirDotNormalHalf = dot( normal, dirHalfVector );",
 							"float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",

+ 241 - 0
src/extras/geometries/ExtrudeGeometry.js

@@ -0,0 +1,241 @@
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Creates extruded geometry form path.
+ **/
+
+THREE.ExtrudeGeometry = function( shape, options ) {
+
+	var amount = options.amount !== undefined ? options.amount : 100;
+
+	// todo: bezel
+
+	var bezelThickness = options.bezelThickness !== undefined ? options.bezelThickness : 10;
+	var bezelSize = options.bezelSize !== undefined ? options.bezelSize : 8;
+	var bezelEnabled = options.bezelEnabled !== undefined ? options.bezelEnabled : false;
+
+	var steps = options.steps !== undefined ? options.steps : 1;
+	var extrudePath = options.path !== undefined ? options.path : null;
+
+	var extrudePts, extrudeByPath = false;
+
+	if ( extrudePath ) {
+
+		extrudePts = extrudePath.getPoints();
+		steps = extrudePts.length;
+		extrudeByPath = true;
+
+	}
+
+	// TODO, extrude by path's tangents? also via 3d path?
+
+	THREE.Geometry.call( this );
+
+    var vertices = shape.getPoints();
+    var faces = shape.triangulate();
+    var contour = vertices;
+
+    var scope = this;
+
+	var bezelPoints = [];
+
+	var reverse = THREE.FontUtils.Triangulate.area( vertices ) > 0 ;
+
+	//console.log(reverse);
+
+	var i,
+		vert, vlen = vertices.length,
+		face, flen = faces.length,
+		bezelPt, blen = bezelPoints.length;
+
+	// Back facing vertices
+
+	for ( i = 0; i < vlen; i++ ) {
+
+		vert = vertices[ i ];
+		v( vert.x, vert.y, 0 );
+
+	}
+
+	// Add steped vertices...
+	// Including front facing vertices
+
+	var s = 1;
+	for ( ; s <= steps; s++ ) {
+
+		for ( i = 0; i < vlen; i ++ ) {
+
+			vert = vertices[ i ];
+
+			if ( !extrudeByPath ) {
+
+				v( vert.x, vert.y, amount/steps * s );
+
+			} else {
+
+				v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
+
+			}
+
+		}
+
+	}
+
+	/*
+	// Front facing vertices
+
+	for ( i = 0; i < vlen; i++ ) {
+
+		vert = vertices[ i ];
+		v( vert.x, vert.y, amount );
+
+	}
+	*/
+
+
+	if ( bezelEnabled ) {
+
+		for ( i = 0; i < blen; i++ ) {
+
+			bezelPt = bezelPoints[ i ];
+			v( bezelPt.x, bezelPt.y, bezelThickness );
+
+		}
+
+		for ( i = 0; i < blen; i++ ) {
+
+			bezelPt = bezelPoints[ i ];
+			v( bezelPt.x, bezelPt.y, amount - bezelThickness );
+
+		}
+
+	}
+
+	// Bottom faces
+
+	for ( i = 0; i < flen; i++ ) {
+
+		face = faces[ i ];
+		f3( face[ 2 ], face[ 1 ], face[ 0 ] );
+
+	}
+
+	// Top faces
+
+	for ( i = 0; i < flen; i++ ) {
+
+		face = faces[ i ];
+		f3( face[ 0 ] + vlen* steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
+
+	}
+
+	var lastV;
+	var j, k, l, m;
+
+	// Sides faces
+
+	//contour.push( contour[ 0 ] ); // in order not to check for boundary indices every time
+
+	i = contour.length;
+
+	while ( --i >= 0 ) {
+
+		lastV = contour[ i ];
+
+		// TO OPTIMISE. Reduce this step of checking vertices.
+
+		/*
+		for ( j = 0; j < vlen; j++ ) {
+
+			if ( vertices[ j ].equals( contour[ i ] ) ) break;
+
+		}
+
+		for ( k = 0; k < vlen; k++ ) {
+
+			if ( vertices[ k ].equals( contour[ i - 1 ] ) ) break;
+
+		}
+		*/
+
+		//TOREMOVE
+		//console.log('a', i,j, i-1, k);
+
+		j = i;
+		//if (j==vertices.length) j = 0;
+
+		k = i - 1;
+
+		if ( k < 0 ) k = vertices.length - 1;
+
+		//console.log('b', i,j, i-1, k,vertices.length);
+
+		// Create faces for the z-sides of the text
+
+		//f4( j, k, k + vlen, j + vlen );
+
+		// Reverse
+		//f4( k, j, j + vlen, k + vlen);
+
+		//
+
+		var s = 0;
+
+		for ( ; s < steps; s++ ) {
+
+			var slen1 = vlen * s;
+			var slen2 = vlen * ( s + 1 );
+
+			f4( j + slen1, k + slen1, k + slen2, j + slen2 );
+
+		}
+
+		//
+
+	}
+
+
+	// UVs to be added
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+	//this.computeVertexNormals();
+
+	function v( x, y, z ) {
+
+		scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) );
+
+	}
+
+	function f3( a, b, c ) {
+
+		if ( reverse ) {
+
+			scope.faces.push( new THREE.Face3( c, b, a ) );
+
+		} else {
+
+			scope.faces.push( new THREE.Face3( a, b, c ) );
+
+		}
+
+	}
+
+	function f4( a, b, c, d ) {
+
+		if ( reverse ) {
+
+			scope.faces.push( new THREE.Face4( d, c, b, a ) );
+
+		} else {
+
+			scope.faces.push( new THREE.Face4( a, b, c, d ) );
+
+		}
+
+	}
+
+};
+
+
+THREE.ExtrudeGeometry.prototype = new THREE.Geometry();
+THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry;

+ 396 - 0
src/extras/geometries/Path.js

@@ -0,0 +1,396 @@
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Creates free form path.
+ **/
+
+THREE.Path = function ( points ) {
+
+	this.actions = [];
+
+	if ( points ) {
+
+		this.fromPoints( points );
+
+	}
+
+};
+
+THREE.PathActions = {
+
+	MOVE_TO: 'moveTo',
+	LINE_TO: 'lineTo',
+	QUADRATIC_CURVE_TO: 'quadraticCurveTo', // BEZIER quadratic CURVE
+	BEZIER_CURVE_TO: 'bezierCurveTo', 		// BEZIER cubic CURVE
+	CSPLINE_THRU: 'splineThru' 				// TODO cardinal splines
+
+};
+
+/* Create path using straight lines to connect all points */
+
+THREE.Path.prototype.fromPoints = function( vectors /*Array of Vector*/ ) {
+
+	var v = 0, vlen = vectors.length;
+
+	this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
+
+	for ( v = 1; v < vlen; v++ ) {
+
+		this.lineTo( vectors[ v ].x, vectors[ v ].y );
+
+	};
+
+};
+
+THREE.Path.prototype.moveTo = function( x, y ) {
+
+	var args = Array.prototype.slice.call( arguments );
+	this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
+
+};
+
+THREE.Path.prototype.lineTo = function( x, y ) {
+
+	var args = Array.prototype.slice.call( arguments );
+	this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
+
+};
+
+THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
+
+	var args = Array.prototype.slice.call( arguments );
+	this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args });
+
+};
+
+THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
+                                               aCP2x, aCP2y,
+                                               aX, aY) {
+
+	var args = Array.prototype.slice.call( arguments );
+	this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } );
+
+};
+
+THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
+
+	var args = Array.prototype.slice.call( arguments );
+	this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
+
+}
+
+// TODO ARC
+
+/* Return an array of vectors based on contour of the path */
+
+THREE.Path.prototype.getPoints = function( divisions ) {
+
+	divisions = divisions || 12;
+
+	var points = [];
+
+	var i, il, item, action, args;
+	var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
+		laste, j,
+		t, tx, ty;
+
+	for ( i = 0, il = this.actions.length; i < il; i++ ) {
+
+		item = this.actions[ i ];
+
+		action = item.action;
+		args = item.args;
+
+		switch( action ) {
+
+		case THREE.PathActions.MOVE_TO:
+
+			//points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
+
+			break;
+
+		case THREE.PathActions.LINE_TO:
+
+			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
+
+			break;
+
+		case THREE.PathActions.QUADRATIC_CURVE_TO:
+
+			cpx  = args[ 2 ];
+			cpy  = args[ 3 ];
+
+			cpx1 = args[ 0 ];
+			cpy1 = args[ 1 ];
+
+			if ( points.length > 0 ) {
+
+				laste = points[ points.length - 1 ];
+
+				cpx0 = laste.x;
+				cpy0 = laste.y;
+
+			} else {
+
+				laste = this.actions[ i - 1 ].args;
+
+				cpx0 = laste[ laste.length - 2 ];
+				cpy0 = laste[ laste.length - 1 ];
+
+			}
+
+			for ( j = 1; j <= divisions; j ++ ) {
+
+				// TODO use LOD for divisions
+
+				t = j / divisions;
+
+				tx = THREE.FontUtils.b2( t, cpx0, cpx1, cpx );
+				ty = THREE.FontUtils.b2( t, cpy0, cpy1, cpy );
+
+				points.push( new THREE.Vector2( tx, ty ) );
+
+		  	}
+
+			break;
+
+		case THREE.PathActions.BEZIER_CURVE_TO:
+
+			cpx  = args[ 4 ];
+			cpy  = args[ 5 ];
+
+			cpx1 = args[ 0 ];
+			cpy1 = args[ 1 ];
+
+			cpx2 = args[ 2 ];
+			cpy2 = args[ 3 ];
+
+			if ( points.length > 0 ) {
+
+				laste = points[ points.length - 1 ];
+
+				cpx0 = laste.x;
+				cpy0 = laste.y;
+
+			} else {
+
+				laste = this.actions[ i - 1 ].args;
+
+				cpx0 = laste[ laste.length - 2 ];
+				cpy0 = laste[ laste.length - 1 ];
+
+			}
+
+
+			for ( j = 1; j <= divisions; j ++ ) {
+
+				t = j / divisions;
+
+				tx = THREE.FontUtils.b3( t, cpx0, cpx1, cpx2, cpx );
+				ty = THREE.FontUtils.b3( t, cpy0, cpy1, cpy2, cpy );
+
+				points.push( new THREE.Vector2( tx, ty ) );
+
+			}
+
+			break;
+
+		case THREE.PathActions.CSPLINE_THRU:
+
+			laste = this.actions[ i - 1 ].args;
+			var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
+			var spts = args[ 0 ];
+			var n = divisions * spts.length;
+
+			spts.unshift( last );
+
+			var spline = new Spline2();
+
+			for ( j = 0; j < n; j ++ ) {
+
+				points.push( spline.get2DPoint( spts, j / n ) ) ;
+
+			}
+
+			break;
+
+		}
+
+	}
+
+	return points;
+
+};
+
+var Spline2 = function () {
+
+	var c = [], v2,
+		point, intPoint, weight;
+
+	this.get2DPoint = function ( points, k ) {
+
+		v2 = new THREE.Vector2();
+		point = ( points.length - 1 ) * k;
+
+		intPoint = Math.floor( point );
+		weight = point - intPoint;
+
+		c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
+		c[ 1 ] = intPoint;
+		c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
+		c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
+
+		v2.x = interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
+		v2.y = interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
+
+		//console.log('point',point, v2);
+
+		return v2;
+
+	}
+
+	// Catmull-Rom
+
+	function interpolate( p0, p1, p2, p3, t ) {
+
+		var v0 = ( p2 - p0 ) * 0.5;
+		var v1 = ( p3 - p1 ) * 0.5;
+		var t2 = t * t;
+		var t3 = t * t2;
+		return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+
+	}
+
+};
+
+
+THREE.Path.prototype.getMinAndMax = function() {
+
+	var points = this.getPoints();
+
+	var maxX, maxY;
+	var minX, minY;
+
+	maxX = maxY = Number.NEGATIVE_INFINITY;
+	minX = minY = Number.POSITIVE_INFINITY;
+
+	var p, i, il;
+
+	for ( i = 0, il = points.length; i < il; i ++ ) {
+
+		p = points[ i ];
+
+		if ( p.x > maxX ) maxX = p.x;
+		else if ( p.x < minX ) minX = p.x;
+
+		if ( p.y > maxY ) maxY = p.y;
+		else if ( p.y < maxY ) minY = p.y;
+
+	}
+
+	// TODO find mid-pt?
+
+	return {
+
+		minX: minX,
+		minY: minY,
+		maxX: maxX,
+		maxY: maxY
+
+	};
+
+};
+
+/* Draws this path onto a 2d canvas easily */
+
+THREE.Path.prototype.debug = function( canvas ) {
+
+	// JUST A STUB
+
+	var bounds = this.getMinAndMax();
+
+	if ( !canvas ) {
+
+		canvas = document.createElement( "canvas" );
+
+		canvas.setAttribute( 'width',  bounds.maxX + 100 );
+		canvas.setAttribute( 'height', bounds.maxY + 100 );
+
+		document.body.appendChild( canvas );
+
+	}
+
+	var ctx = canvas.getContext( "2d" );
+	ctx.fillStyle = "white";
+	ctx.fillRect( 0, 0, canvas.width, canvas.height );
+
+	ctx.strokeStyle = "black";
+	ctx.beginPath();
+
+	var i, il, item, action, args;
+
+	// Debug Path
+
+	for ( i = 0, il = this.actions.length; i < il; i ++ ) {
+
+		item = this.actions[ i ];
+
+		args = item.args;
+		action = item.action;
+
+		// Short hand for now
+
+		if ( action != THREE.PathActions.CSPLINE_THRU ) {
+
+			ctx[ action ].apply( ctx, args );
+
+		}
+
+		/*
+		switch ( action ) {
+
+			case THREE.PathActions.MOVE_TO:
+
+				ctx[ action ]( args[ 0 ], args[ 1 ] );
+				break;
+
+			case THREE.PathActions.LINE_TO:
+
+				ctx[ action ]( args[ 0 ], args[ 1 ] );
+				break;
+
+			case THREE.PathActions.QUADRATIC_CURVE_TO:
+
+				ctx[ action ]( args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ] );
+				break;
+
+			case THREE.PathActions.CUBIC_CURVE_TO:
+
+				ctx[ action ]( args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], args[ 4 ], args[ 5 ] );
+				break;
+
+		}
+		*/
+
+	}
+
+	ctx.stroke();
+	ctx.closePath();
+
+	// DebugPoints
+
+	ctx.strokeStyle = "red";
+
+	var p, points = this.getPoints();
+
+	for ( i = 0, il = points.length; i < il; i ++ ) {
+
+		p = points[ i ];
+
+		ctx.beginPath();
+		ctx.arc( p.x, p.y, 1.5, 0, Math.PI * 2, false );
+		ctx.stroke();
+		ctx.closePath();
+
+	}
+
+};

+ 45 - 0
src/extras/geometries/Shape.js

@@ -0,0 +1,45 @@
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Defines a 2d shape plane using paths.
+ **/
+
+// STEP 1 Create a path.
+// STEP 2 Turn path into shape.
+// STEP 3 Extrude Geometry takes in Shape/Shapes
+// STEP 3a - Extract points from each shape, turn to vertices
+// STEP 3b - Triangulate Each Shape
+
+THREE.Shape = function ( ) {
+
+	THREE.Path.apply( this, arguments );
+	this.holes = [];
+
+};
+
+THREE.Shape.prototype = new THREE.Path();
+THREE.Shape.prototype.constructor = THREE.Path;
+
+/* Returns vertices of triangulated faces | get faces */
+
+THREE.Shape.prototype.triangulate = function() {
+
+	var pts = this.getPoints();
+
+	if ( THREE.FontUtils.Triangulate.area( pts ) > 0 ) {
+
+		pts = pts.reverse();
+
+	};
+
+	return THREE.FontUtils.Triangulate( pts, true );
+
+};
+
+/* Convenience method to return ExtrudeGeometry */
+
+THREE.Shape.prototype.extrude = function( options ) {
+
+	var extruded =  new THREE.ExtrudeGeometry( this, options );
+	return extruded;
+
+};

+ 88 - 73
src/extras/geometries/TextGeometry.js

@@ -19,22 +19,22 @@
  *  bezelThickness: <float>, 		// how deep into text bezel goes
  *  bezelSize:		<float>, 		// how far from text outline is bezel
  *  }
- *  
- * It uses techniques used in
- * 
+ *
+ * It uses techniques used in:
+ *
  * 	typeface.js and canvastext
- * 		For converting fonts and rendering with javascript 
+ * 		For converting fonts and rendering with javascript
  *		http://typeface.neocracy.org
- * 
+ *
  *	Triangulation ported from AS3
  *		Simple Polygon Triangulation
  *		http://actionsnippet.com/?p=1462
  *
- * 	A Method to triangulate shapes with holes 
+ * 	A Method to triangulate shapes with holes
  *		http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
  *
  */
- 
+
 THREE.TextGeometry = function ( text, parameters ) {
 
 	THREE.Geometry.call( this );
@@ -87,8 +87,8 @@ THREE.TextGeometry.prototype.set = function ( text, parameters ) {
 	scope.vertices = [];
 	scope.faces = [];
 
-	var i, 
-		vert, vlen = vertices.length, 
+	var i,
+		vert, vlen = vertices.length,
 		face, flen = faces.length,
 		bezelPt, blen = bezelPoints.length;
 
@@ -104,7 +104,7 @@ THREE.TextGeometry.prototype.set = function ( text, parameters ) {
 	// Front facing vertices
 
 	for ( i = 0; i < vlen; i++ ) {
-	
+
 		vert = vertices[ i ];
 		v( vert.x, vert.y, height );
 
@@ -247,13 +247,13 @@ THREE.TextGeometry.prototype.set = function ( text, parameters ) {
 
 	function f3( a, b, c ) {
 
-		scope.faces.push( new THREE.Face3( a, b, c) );
+		scope.faces.push( new THREE.Face3( a, b, c ) );
 
 	}
 
 	function f4( a, b, c, d ) {
 
-		scope.faces.push( new THREE.Face4( a, b, c, d) );
+		scope.faces.push( new THREE.Face4( a, b, c, d ) );
 
 	}
 
@@ -285,7 +285,7 @@ THREE.FontUtils = {
 		var ThreeFont = this;
 
 		ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
-	
+
 		ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
 		ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
 
@@ -358,7 +358,7 @@ THREE.FontUtils = {
 				firstPt = all[ firstIndex ];
 				endPt = all.slice( firstIndex + 1 ).indexOf( firstPt ) + firstIndex;
 
-				if ( endPt <= firstIndex ) break; 
+				if ( endPt <= firstIndex ) break;
 
 				var contours = singleCharPoints.slice( firstIndex, endPt + 1 );
 
@@ -472,7 +472,7 @@ THREE.FontUtils = {
 				prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
 				nextShapeVert = ( shapeIndex + 1 ) < shape.length ? shapeIndex + 1 : 0;
 
-				prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; 
+				prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
 				nextHoleVert = ( holeIndex + 1 ) < hole.length ? holeIndex + 1 : 0 ;
 
 				var areaapts = [];
@@ -495,7 +495,7 @@ THREE.FontUtils = {
 				var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex;
 				shapeIndex += shapeOffset;
 				holeIndex += holeOffset;
-				
+
 				if ( shapeIndex < 0 ) { shapeIndex += shape.length;  }
 				shapeIndex %= shape.length;
 
@@ -505,7 +505,7 @@ THREE.FontUtils = {
 				prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
 				nextShapeVert = ( shapeIndex + 1 ) < shape.length ? shapeIndex + 1 : 0;
 
-				prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; 
+				prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
 				nextHoleVert = ( holeIndex + 1 ) < hole.length ? holeIndex + 1 : 0 ;
 
 
@@ -538,7 +538,7 @@ THREE.FontUtils = {
 					prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
 					nextShapeVert = ( shapeIndex + 1 ) < shape.length ? shapeIndex + 1 : 0;
 
-					prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; 
+					prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
 					nextHoleVert = ( holeIndex + 1 ) < hole.length ? holeIndex + 1 : 0 ;
 
 				}
@@ -563,7 +563,7 @@ THREE.FontUtils = {
 			shapeGroup.shape = shape;
 
 		}
-	
+
 		var triangulatedPoints = [];
 		var triangulatedFaces = [];
 		var lastTriangles = 0;
@@ -593,7 +593,7 @@ THREE.FontUtils = {
 			lastTriangles += shape.length;
 
 		}
-  
+
 
 		// Now we push the "cut" vertices back to the triangulated indices.
 
@@ -660,20 +660,23 @@ THREE.FontUtils = {
 		var i, p,
 			face = this.getFace(),
 			scale = this.size / face.resolution,
-			offset = 0, 
-			chars = String( text ).split( '' ), 
+			offset = 0,
+			chars = String( text ).split( '' ),
 			length = chars.length;
 
+		var fontPaths = [];
+
 		for ( i = 0; i < length; i++ ) {
 
 			var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset );
 			offset += ret.offset;
 			characterPts.push( ret.points );
 			allPts = allPts.concat( ret.points );
+			fontPaths.push( ret.path );
 
 		}
 
-		// get the width 
+		// get the width
 
 		var width = offset / 2;
 
@@ -849,6 +852,8 @@ THREE.FontUtils = {
 	extractGlyphPoints : function( c, face, scale, offset ) {
 
 		var pts = [];
+		var path = new THREE.Path();
+
 
 		var i, i2,
 			outline, action, length,
@@ -856,21 +861,21 @@ THREE.FontUtils = {
 			x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
 			laste,
 			glyph = face.glyphs[ c ] || face.glyphs[ ctxt.options.fallbackCharacter ];
-	
+
 		if ( !glyph ) return;
-  
+
 		if ( glyph.o ) {
-	  
+
 			outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
 			length = outline.length;
-		
+
 			scaleX = scale;
 			scaleY = scale;
-		 
+
 			for ( i = 0; i < length; ) {
 
 				action = outline[ i++ ];
-  
+
 				switch( action ) {
 
 				case 'm':
@@ -880,6 +885,8 @@ THREE.FontUtils = {
 					x = outline[ i++ ] * scaleX + offset;
 					y = outline[ i++ ] * scaleY;
 					pts.push( new THREE.Vector2( x, y ) );
+
+					path.moveTo(x,y);
 					break;
 
 				case 'l':
@@ -889,24 +896,27 @@ THREE.FontUtils = {
 					x = outline[ i++ ] * scaleX + offset;
 					y = outline[ i++ ] * scaleY;
 					pts.push( new THREE.Vector2( x, y ) );
+					path.lineTo(x,y);
 					break;
-					
+
 				case 'q':
 
 					// QuadraticCurveTo
-				  
+
 					cpx  = outline[ i++ ] * scaleX + offset;
 					cpy  = outline[ i++ ] * scaleY;
 					cpx1 = outline[ i++ ] * scaleX + offset;
 					cpy1 = outline[ i++ ] * scaleY;
-			  
+
+					path.quadraticCurveTo(cpx1, cpy1, cpx, cpy);
+
 					laste = pts[ pts.length - 1 ];
-			  
+
 					if ( laste ) {
 
 						cpx0 = laste.x;
 						cpy0 = laste.y;
-				
+
 						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2++ ) {
 
 							var t = i2 / divisions;
@@ -916,28 +926,30 @@ THREE.FontUtils = {
 
 					  }
 
-				  }              
-			  
+				  }
+
 				  break;
 
 				case 'b':
 
 					// Cubic Bezier Curve
-				  
+
 					cpx  = outline[ i++ ] *  scaleX + offset;
 					cpy  = outline[ i++ ] *  scaleY;
 					cpx1 = outline[ i++ ] *  scaleX + offset;
 					cpy1 = outline[ i++ ] * -scaleY;
 					cpx2 = outline[ i++ ] *  scaleX + offset;
 					cpy2 = outline[ i++ ] * -scaleY;
-			  
+
+					path.quadraticCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 );
+
 					laste = pts[ pts.length - 1 ];
-			  
+
 					if ( laste ) {
 
 						cpx0 = laste.x;
 						cpy0 = laste.y;
-				
+
 						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2++ ) {
 
 							var t = i2 / divisions;
@@ -956,7 +968,10 @@ THREE.FontUtils = {
 			}
 		}
 
-		return { offset: glyph.ha*scale, points:pts };
+		path.debug(document.getElementById("boo"));
+		console.log(path);
+
+		return { offset: glyph.ha*scale, points:pts, path:path};
 	}
 
 };
@@ -964,17 +979,17 @@ THREE.FontUtils = {
 
 
 /**
- * This code is a quick port of code written in C++ which was submitted to 
- * flipcode.com by John W. Ratcliff  // July 22, 2000 
+ * This code is a quick port of code written in C++ which was submitted to
+ * flipcode.com by John W. Ratcliff  // July 22, 2000
  * See original code and more information here:
  * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
- * 
+ *
  * ported to actionscript by Zevan Rosser
  * www.actionsnippet.com
- * 
+ *
  * ported to javascript by Joshua Koo
  * http://www.lab4games.net/zz85/blog
- * 
+ *
  */
 
 
@@ -982,7 +997,7 @@ THREE.FontUtils = {
 
 	var EPSILON = 0.0000000001;
 
-	// takes in an contour array and returns 
+	// takes in an contour array and returns
 
 	var process = function( contour, indices ) {
 
@@ -991,7 +1006,7 @@ THREE.FontUtils = {
 		if ( n < 3 ) return null;
 
 		var result = [],
-			verts = [], 
+			verts = [],
 			vertIndices = [];
 
 		/* we want a counter-clockwise polygon in verts */
@@ -1024,7 +1039,7 @@ THREE.FontUtils = {
 
 				//throw ( "Warning, unable to triangulate polygon!" );
 				//return null;
-
+				// Sometimes warning is fine, especially polygons are triangulated in reverse.
 				console.log( "Warning, unable to triangulate polygon!" );
 
 				if ( indices ) return vertIndices;
@@ -1033,19 +1048,19 @@ THREE.FontUtils = {
 			}
 
 			/* three consecutive vertices in current polygon, <u,v,w> */
-			
+
 			u = v; 	 	if ( nv <= u ) u = 0;     /* previous */
 			v = u + 1;  if ( nv <= v ) v = 0;     /* new v    */
 			w = v + 1;  if ( nv <= w ) w = 0;     /* next     */
-			
+
 			if ( snip( contour, u, v, w, nv, verts ) ) {
 
 				var a, b, c, s, t;
-			
+
 				/* true names of the vertices */
 
-				a = verts[ u ]; 
-				b = verts[ v ]; 
+				a = verts[ u ];
+				b = verts[ v ];
 				c = verts[ w ];
 
 				/* output Triangle */
@@ -1055,17 +1070,17 @@ THREE.FontUtils = {
 				result.push( contour[ c ] );
 
 				vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
-			
+
 				/* remove v from the remaining polygon */
 
 				for( s = v, t = v + 1; t < nv; s++, t++ ) {
-					
-					verts[ s ] = verts[ t ]; 
+
+					verts[ s ] = verts[ t ];
 
 				}
 
 				nv--;
-			
+
 				/* reset error detection counter */
 
 				count = 2 * nv;
@@ -1073,19 +1088,19 @@ THREE.FontUtils = {
 			}
 
 		}
-		
+
 		if ( indices ) return vertIndices;
 		return result;
 
 	};
-	
+
 	// calculate area of the contour polygon
 
 	var area = function ( contour ) {
 
 		var n = contour.length;
 		var a = 0.0;
-		
+
 		for( var p = n - 1, q = 0; q < n; p = q++ ) {
 
 			a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
@@ -1095,7 +1110,7 @@ THREE.FontUtils = {
 		return a * 0.5;
 
 	};
-	
+
 	// see if p is inside triangle abc
 
 	var insideTriangle = function( ax, ay,
@@ -1107,23 +1122,23 @@ THREE.FontUtils = {
 		  var cX, cY, apx, apy;
 		  var bpx, bpy, cpx, cpy;
 		  var cCROSSap, bCROSScp, aCROSSbp;
-		
+
 		  aX = cx - bx;  aY = cy - by;
 		  bX = ax - cx;  bY = ay - cy;
 		  cX = bx - ax;  cY = by - ay;
 		  apx= px  -ax;  apy= py - ay;
 		  bpx= px - bx;  bpy= py - by;
 		  cpx= px - cx;  cpy= py - cy;
-		
+
 		  aCROSSbp = aX*bpy - aY*bpx;
 		  cCROSSap = cX*apy - cY*apx;
 		  bCROSScp = bX*cpy - bY*cpx;
-		
+
 		  return ( (aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0) );
 
 	};
-	
-	
+
+
 	var snip = function ( contour, u, v, w, n, verts ) {
 
 		var p;
@@ -1138,13 +1153,13 @@ THREE.FontUtils = {
 
 		cx = contour[ verts[ w ] ].x;
 		cy = contour[ verts[ w ] ].y;
-		
+
 		if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false;
-		
+
 			for ( p = 0; p < n; p++ ) {
 
 				if( (p == u) || (p == v) || (p == w) ) continue;
-			
+
 				px = contour[ verts[ p ] ].x
 				py = contour[ verts[ p ] ].y
 
@@ -1155,11 +1170,11 @@ THREE.FontUtils = {
 		  return true;
 
 	};
-	
-	
+
+
 	namespace.Triangulate = process;
 	namespace.Triangulate.area = area;
-	
+
 	return namespace;
 
 })(THREE.FontUtils);

+ 261 - 6
src/renderers/WebGLRenderer.js

@@ -569,7 +569,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	};
 
-	function initParticleBuffers ( geometry ) {
+	function initParticleBuffers ( geometry, object ) {
 
 		var nvertices = geometry.vertices.length;
 
@@ -580,6 +580,72 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		geometry.__webglParticleCount = nvertices;
 
+		geometry.__materials = object.materials;
+
+		// custom attributes
+
+		var m, ml, material;
+
+		for ( m = 0, ml = object.materials.length; m < ml; m ++ ) {
+
+			material = object.materials[ m ];
+
+			if ( material.attributes ) {
+
+				if ( geometry.__webglCustomAttributes === undefined ) {
+
+					geometry.__webglCustomAttributes = {};
+
+				}
+
+				for ( a in material.attributes ) {
+
+					// Do a shallow copy of the attribute object so different geometryGroup chunks use different
+					// attribute buffers which are correctly indexed in the setMeshBuffers function
+
+					// Not sure how to best translate this into non-indexed arrays
+					// used for particles, as there are no geometry chunks here
+					// Probably could be simplified
+
+					originalAttribute = material.attributes[ a ];
+
+					attribute = {};
+
+					for ( property in originalAttribute ) {
+
+						attribute[ property ] = originalAttribute[ property ];
+
+					}
+
+					if( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
+
+						attribute.__webglInitialized = true;
+
+						size = 1;		// "f" and "i"
+
+						if( attribute.type === "v2" ) size = 2;
+						else if( attribute.type === "v3" ) size = 3;
+						else if( attribute.type === "v4" ) size = 4;
+						else if( attribute.type === "c"  ) size = 3;
+
+						attribute.size = size;
+						attribute.array = new Float32Array( nvertices * size );
+						attribute.buffer = _gl.createBuffer();
+						attribute.buffer.belongsToAttribute = a;
+
+						originalAttribute.needsUpdate = true;
+						attribute.__original = originalAttribute;
+
+					}
+
+					geometry.__webglCustomAttributes[ a ] = attribute;
+
+				}
+
+			}
+
+		}
+
 	};
 
 	function initMeshBuffers ( geometryGroup, object ) {
@@ -713,7 +779,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( material.attributes ) {
 
-				geometryGroup.__webglCustomAttributes = {};
+				if ( geometryGroup.__webglCustomAttributes === undefined ) {
+
+					geometryGroup.__webglCustomAttributes = {};
+
+				}
 
 				for ( a in material.attributes ) {
 
@@ -1386,6 +1456,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 									customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ offset_customSrc + 3 ];
 
 									customAttribute.offsetSrc += 4;
+
 								}
 
 								customAttribute.offset += 4;
@@ -1416,6 +1487,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 									v4 = customAttribute.value[ offset_customSrc + 3 ];
 
 									customAttribute.offsetSrc += 4;
+
 								}
 
 
@@ -2079,7 +2151,22 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		dirtyVertices = geometry.__dirtyVertices,
 		dirtyElements = geometry.__dirtyElements,
-		dirtyColors = geometry.__dirtyColors;
+		dirtyColors = geometry.__dirtyColors,
+
+		customAttributes = geometry.__webglCustomAttributes,
+		a, ca, cal, v1,
+		offset_custom,
+		customAttribute;
+
+		if ( customAttributes ) {
+
+			for ( a in customAttributes ) {
+
+				customAttributes[ a ].offset = 0;
+
+			}
+
+		}
 
 		if ( object.sortParticles ) {
 
@@ -2096,7 +2183,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			}
 
-			sortArray.sort( function(a,b) { return b[0] - a[0]; } );
+			sortArray.sort( function( a, b ) { return b[ 0 ] - a[ 0 ]; } );
 
 			for ( v = 0; v < vl; v++ ) {
 
@@ -2122,6 +2209,76 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			}
 
+			if ( customAttributes ) {
+
+				for ( a in customAttributes ) {
+
+					customAttribute = customAttributes[ a ];
+
+					cal = customAttribute.value.length;
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						index = sortArray[ca][1];
+
+						offset_custom = customAttribute.offset;
+
+						if ( customAttribute.size === 1 ) {
+
+							if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+								customAttribute.array[ offset_custom ] = customAttribute.value[ index ];
+
+							}
+
+						} else {
+
+							if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+								v1 = customAttribute.value[ index ];
+
+							}
+
+							if ( customAttribute.size === 2 ) {
+
+								customAttribute.array[ offset_custom ] 	   = v1.x;
+								customAttribute.array[ offset_custom + 1 ] = v1.y;
+
+							} else if ( customAttribute.size === 3 ) {
+
+								if ( customAttribute.type === "c" ) {
+
+									customAttribute.array[ offset_custom ] 	   = v1.r;
+									customAttribute.array[ offset_custom + 1 ] = v1.g;
+									customAttribute.array[ offset_custom + 2 ] = v1.b;
+
+								} else {
+
+									customAttribute.array[ offset_custom ] 	   = v1.x;
+									customAttribute.array[ offset_custom + 1 ] = v1.y;
+									customAttribute.array[ offset_custom + 2 ] = v1.z;
+
+								}
+
+							} else {
+
+								customAttribute.array[ offset_custom ] 		= v1.x;
+								customAttribute.array[ offset_custom + 1  ] = v1.y;
+								customAttribute.array[ offset_custom + 2  ] = v1.z;
+								customAttribute.array[ offset_custom + 3  ] = v1.w;
+
+							}
+
+						}
+
+						customAttribute.offset += customAttribute.size;
+
+					}
+
+				}
+
+			}
+
 
 		} else {
 
@@ -2157,6 +2314,79 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			}
 
+			if ( customAttributes ) {
+
+				for ( a in customAttributes ) {
+
+					customAttribute = customAttributes[ a ];
+
+					if ( customAttribute.__original.needsUpdate ) {
+
+						cal = customAttribute.value.length;
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							offset_custom = customAttribute.offset;
+
+							if ( customAttribute.size === 1 ) {
+
+								if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+									customAttribute.array[ offset_custom ] = customAttribute.value[ ca ];
+
+								}
+
+							} else {
+
+								if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+									v1 = customAttribute.value[ ca ];
+
+								}
+
+								if ( customAttribute.size === 2 ) {
+
+									customAttribute.array[ offset_custom ] 	   = v1.x;
+									customAttribute.array[ offset_custom + 1 ] = v1.y;
+
+								} else if ( customAttribute.size === 3 ) {
+
+									if ( customAttribute.type === "c" ) {
+
+										customAttribute.array[ offset_custom ] 	   = v1.r;
+										customAttribute.array[ offset_custom + 1 ] = v1.g;
+										customAttribute.array[ offset_custom + 2 ] = v1.b;
+
+
+									} else {
+
+										customAttribute.array[ offset_custom ] 	   = v1.x;
+										customAttribute.array[ offset_custom + 1 ] = v1.y;
+										customAttribute.array[ offset_custom + 2 ] = v1.z;
+
+									}
+
+								} else {
+
+									customAttribute.array[ offset_custom ] 		= v1.x;
+									customAttribute.array[ offset_custom + 1  ] = v1.y;
+									customAttribute.array[ offset_custom + 2  ] = v1.z;
+									customAttribute.array[ offset_custom + 3  ] = v1.w;
+
+								}
+
+							}
+
+							customAttribute.offset += customAttribute.size;
+
+						}
+
+					}
+
+				}
+
+			}
+
 		}
 
 		if ( dirtyVertices || object.sortParticles ) {
@@ -2173,6 +2403,24 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
+		if ( customAttributes ) {
+
+			for ( a in customAttributes ) {
+
+				customAttribute = customAttributes[ a ];
+
+				if ( customAttribute.__original.needsUpdate || object.sortParticles ) {
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
+					_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
+
+				}
+
+			}
+
+		}
+
+
 	};
 
 	function setMaterialShaders( material, shaders ) {
@@ -3557,6 +3805,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		// draw darkening polygon
 
 		_oldBlending = -1;
+		_oldDepth = -1;
 		_currentProgram = _stencilShadow.program;
 
 		_gl.useProgram( _stencilShadow.program );
@@ -3606,6 +3855,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		_gl.useProgram( _sprite.program );
 		_currentProgram = _sprite.program;
 		_oldBlending = -1;
+		_oldDepth = -1;
 
 		if ( !_spriteAttributesEnabled ) {
 
@@ -3770,6 +4020,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		_gl.useProgram( _lensFlare.program );
 		_currentProgram = _lensFlare.program;
 		_oldBlending = -1;
+		_oldDepth = -1;
 
 
 		if ( ! _lensFlareAttributesEnabled ) {
@@ -4097,7 +4348,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 			if ( ! geometry.__webglVertexBuffer ) {
 
 				createParticleBuffers( geometry );
-				initParticleBuffers( geometry );
+				initParticleBuffers( geometry, object );
 
 				geometry.__dirtyVertices = true;
 				geometry.__dirtyColors = true;
@@ -4239,7 +4490,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			geometry = object.geometry;
 
-			if ( geometry.__dirtyVertices || geometry.__dirtyColors || object.sortParticles ) {
+			customAttributeDirty = areCustomAttributesDirty( geometry );
+
+			if ( geometry.__dirtyVertices || geometry.__dirtyColors || object.sortParticles || customAttributeDirty ) {
 
 				setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object );
 
@@ -4248,6 +4501,8 @@ THREE.WebGLRenderer = function ( parameters ) {
 			geometry.__dirtyVertices = false;
 			geometry.__dirtyColors = false;
 
+			clearCustomAttributes( geometry );
+
 		}/* else if ( THREE.MarchingCubes !== undefined && object instanceof THREE.MarchingCubes ) {
 
 			// it updates itself in render callback

+ 2 - 2
src/renderers/WebGLShaders.js

@@ -336,7 +336,7 @@ THREE.ShaderChunk = {
 		"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
 
 			"vec3 pointVector = normalize( vPointLight[ i ].xyz );",
-			"vec3 pointHalfVector = normalize( vPointLight[ i ].xyz + vViewPosition );",
+			"vec3 pointHalfVector = normalize( vPointLight[ i ].xyz + viewPosition );",
 			"float pointDistance = vPointLight[ i ].w;",
 
 			"float pointDotNormalHalf = dot( normal, pointHalfVector );",
@@ -364,7 +364,7 @@ THREE.ShaderChunk = {
 			"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
 
 			"vec3 dirVector = normalize( lDirection.xyz );",
-			"vec3 dirHalfVector = normalize( lDirection.xyz + vViewPosition );",
+			"vec3 dirHalfVector = normalize( lDirection.xyz + viewPosition );",
 
 			"float dirDotNormalHalf = dot( normal, dirHalfVector );",
 

+ 3 - 0
utils/build.py

@@ -93,8 +93,11 @@ EXTRAS_FILES = [
 'extras/cameras/RollCamera.js',
 'extras/cameras/TrackballCamera.js',
 'extras/cameras/QuakeCamera.js',
+'extras/geometries/Path.js',
+'extras/geometries/Shape.js',
 'extras/geometries/CubeGeometry.js',
 'extras/geometries/CylinderGeometry.js',
+'extras/geometries/ExtrudeGeometry.js',
 'extras/geometries/IcosahedronGeometry.js',
 'extras/geometries/LatheGeometry.js',
 'extras/geometries/PlaneGeometry.js',

Some files were not shown because too many files changed in this diff