Browse Source

Added Loader class providing both asynchronous JS and web worker based loading of models.

The idea is that later there will be more loaders which would load different formats (like OBJLoader, ColladaLoader).

Usage (async JS):

var loader = new THREE.Loader();
loader.loadAsync( "obj/torus/Torus.js", function() { createScene( new Torus() ) } );

Usage (web worker):

var loader = new THREE.Loader();
loader.loadWorker( "obj/torus/Torus_slim.js", function( geometry ) { createScene( geometry ) } );

Web worker loader is useful for large meshes, where it allows browser to stay responsive for longer time and also it can handle larger meshes than async JS loader.

Web worker loader needs a simpler format of the model. Web workers can communicate with the main application only via message passing, where messages are JSON objects.

There is a new version of OBJ -> Three.js converter (convert_obj_threejs_slim.py) which can produce model in format needed for web workers.

All examples which were using models from OBJ converter were refactored to use Loader.

Except large mesh example, all examples are using just async JS loading. Web worker loading is there, it's just commented out, as it's a bit pain for local development.

Chrome doesn't allow to run web workers from pages accessed via file://, so you need either to run it with "--allow-file-access-from-files" flag, or access examples via local server (http://localhost/example.html).
alteredq 14 years ago
parent
commit
5192428f25

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


File diff suppressed because it is too large
+ 0 - 1
build/ThreeDebug.js


+ 20 - 26
examples/large_mesh_test.html

@@ -63,6 +63,7 @@
 		<script type="text/javascript" src="../src/core/UV.js"></script>
 		<script type="text/javascript" src="../src/core/Geometry.js"></script>
 		<script type="text/javascript" src="../src/cameras/Camera.js"></script>
+		<script type="text/javascript" src="../src/io/Loader.js"></script>
 		<script type="text/javascript" src="../src/lights/Light.js"></script>
 		<script type="text/javascript" src="../src/lights/AmbientLight.js"></script>
 		<script type="text/javascript" src="../src/lights/DirectionalLight.js"></script>
@@ -88,6 +89,7 @@
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace4.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableParticle.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableLine.js"></script>
+		
 		-->
 		
 		<script type="text/javascript" src="geometry/primitives/Sphere.js"></script>
@@ -107,7 +109,7 @@
 			var scene;
 			var canvasRenderer, webglRenderer;
 
-			var mesh, zmesh, lightMesh, geometry;
+			var mesh, zmesh, lightMesh;
 			
 			var directionalLight, pointLight;
 			
@@ -217,36 +219,28 @@
 				bcanvas.addEventListener("click", toggleCanvas, false);
 				bwebgl.addEventListener("click", toggleWebGL, false);
 
-				function createModel() {
-					
-					geometry = new Lucy100k( );
-					
-					addMesh( geometry, 0.75, 900, 0, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0x990000, 30, 1.0 ) );
-					addMesh( geometry, 0.75, 300, 0, 0, 0,0,0, new THREE.MeshFaceMaterial( ) );
-					addMesh( geometry, 0.75, -300, 0, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x111111, 0xffaa00, 10, 1.0 )  );
-					addMesh( geometry, 0.75, -900, 0, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x555555, 0x666666, 10, 1.0 )  );
-					
-					/*
-					log( "geometry.vertices: " + geometry.vertices.length );
-					log( "geometry.faces: " + geometry.faces.length );
-					*/
-
-				}
-
-				loadAsync( "obj/lucy/Lucy100k.js", createModel );
 
+				var s = (new Date).getTime();
+				
+				var loader = new THREE.Loader();
+				//loader.loadAsync( "obj/lucy/Lucy100k.js", function() { createScene( new Lucy100k(), s ) } );
+				loader.loadWorker( 'obj/lucy/Lucy100k_slim.js', function( geometry ) { createScene( geometry, s ) } );
+				
 			}
-
-			function loadAsync( url, callback ) {
+			
+			function createScene( geometry, start ) {
 				
-				var el = document.createElement( 'script' );
-				el.type = 'text/javascript';
-				el.onload = callback;
-				el.src = url;
-				document.getElementsByTagName("head")[0].appendChild(el);
+				addMesh( geometry, 0.75, 900, 0, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0x990000, 30, 1.0 ) );
+				addMesh( geometry, 0.75, 300, 0, 0, 0,0,0, new THREE.MeshFaceMaterial( ) );
+				addMesh( geometry, 0.75, -300, 0, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x111111, 0xffaa00, 10, 1.0 )  );
+				addMesh( geometry, 0.75, -900, 0, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x555555, 0x666666, 10, 1.0 )  );
+				
+				log( "geometry.vertices: " + geometry.vertices.length );
+				log( "geometry.faces: " + geometry.faces.length );
 
+				log( "model loaded and created in " + ((new Date).getTime() - start) + " ms" );
+				
 			}
-
 			
 			function onDocumentMouseMove(event) {
 

+ 55 - 39
examples/lights_pointlights_gl.html

@@ -38,10 +38,10 @@
 			Walt Disney head by <a href="http://www.davidoreilly.com/2009/01/walt-disneys-head-on-a-plate" target="_blank">David OReilly</a>
 		</div>
 
-        <!--
+		<!--
 		<script type="text/javascript" src="../build/Three.js"></script> 
-        -->
-        
+		-->
+		
 		<script type="text/javascript" src="../src/Three.js"></script>
 		<script type="text/javascript" src="../src/core/Color.js"></script>
 		<script type="text/javascript" src="../src/core/Vector2.js"></script>
@@ -56,6 +56,7 @@
 		<script type="text/javascript" src="../src/core/UV.js"></script>
 		<script type="text/javascript" src="../src/core/Geometry.js"></script>
 		<script type="text/javascript" src="../src/cameras/Camera.js"></script>
+		<script type="text/javascript" src="../src/io/Loader.js"></script>
 		<script type="text/javascript" src="../src/lights/Light.js"></script>
 		<script type="text/javascript" src="../src/lights/AmbientLight.js"></script>
 		<script type="text/javascript" src="../src/lights/DirectionalLight.js"></script>
@@ -81,12 +82,11 @@
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace4.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableParticle.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableLine.js"></script>
-        <!--
+		<!--
 
-        -->
-		<script type="text/javascript" src="obj/walt/WaltHead.js"></script> 
+		-->
 		<script type="text/javascript" src="geometry/primitives/Sphere.js"></script> 
-        
+		
 		<script type="text/javascript" src="js/Stats.js"></script>
 
 		<script type="text/javascript">
@@ -108,24 +108,40 @@
 
 				scene = new THREE.Scene();
 
-				//object = new THREE.Mesh( new WaltHead(), [ new THREE.MeshColorStrokeMaterial( 0xffffff ) ] );
-                //object = new THREE.Mesh( new WaltHead(), new THREE.MeshColorFillMaterial( 0xffffff ) );
-                object = new THREE.Mesh( new WaltHead(), new THREE.MeshPhongMaterial( 0x555555, 0x555555, 0xffffff, 50, 1.0  )  );
-                object.scale.x = object.scale.y = object.scale.z = 0.80;
-				object.overdraw = true;
-				scene.addObject( object );
-                
-                /*
-                var directionalLight = new THREE.DirectionalLight( 0x111111, 0.9 );
-                directionalLight.position.x = 1;
-                directionalLight.position.y = 1;
-                directionalLight.position.z = 2;
+				var loader = new THREE.Loader();
+				loader.loadAsync( "obj/walt/WaltHead.js", function() {
+				
+					//object = new THREE.Mesh( new WaltHead(), [ new THREE.MeshColorStrokeMaterial( 0xffffff ) ] );
+					//object = new THREE.Mesh( new WaltHead(), new THREE.MeshColorFillMaterial( 0xffffff ) );
+					object = new THREE.Mesh( new WaltHead(), new THREE.MeshPhongMaterial( 0x555555, 0x555555, 0xffffff, 50, 1.0  )  );
+					object.scale.x = object.scale.y = object.scale.z = 0.80;
+					object.overdraw = true;
+					scene.addObject( object );
+					
+				});
+				
+				/*
+				loader.loadWorker( "obj/walt/WaltHead_slim.js", function( geometry ) {
+				
+					object = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial( 0x555555, 0x555555, 0xffffff, 50, 1.0  )  );
+					object.scale.x = object.scale.y = object.scale.z = 0.80;
+					object.overdraw = true;
+					scene.addObject( object );
+					
+				});
+				*/
+				
+				/*
+				var directionalLight = new THREE.DirectionalLight( 0x111111, 0.9 );
+				directionalLight.position.x = 1;
+				directionalLight.position.y = 1;
+				directionalLight.position.z = 2;
 				directionalLight.position.normalize();
 				scene.addLight( directionalLight );
-                */
-                
+				*/
+				
 				scene.addLight( new THREE.AmbientLight( 0x00020 ) );
-                scene.addLight( new THREE.AmbientLight( 0x00000 ) );
+				scene.addLight( new THREE.AmbientLight( 0x00000 ) );
 
 				light1 = new THREE.PointLight( 0xff0040 );
 				scene.addLight( light1 );
@@ -136,30 +152,30 @@
 				light3 = new THREE.PointLight( 0x80ff80 );
 				scene.addLight( light3 );
 
-                var l1 = new THREE.Mesh( new Sphere( 1, 16, 8, 1 ), new THREE.MeshColorFillMaterial( 0xff0040 ) );
-                l1.position = light1.position;
-                scene.addObject( l1 );
-                
-                var l2 = new THREE.Mesh( new Sphere( 1, 16, 8, 1 ), new THREE.MeshColorFillMaterial( 0x0040ff ) );
-                l2.position = light2.position;
-                scene.addObject( l2 );
-     
-                var l3 = new THREE.Mesh( new Sphere( 1, 16, 8, 1 ), new THREE.MeshColorFillMaterial( 0x80ff80 ) );
-                l3.position = light3.position;
-                scene.addObject( l3 );
+				var l1 = new THREE.Mesh( new Sphere( 1, 16, 8, 1 ), new THREE.MeshColorFillMaterial( 0xff0040 ) );
+				l1.position = light1.position;
+				scene.addObject( l1 );
+				
+				var l2 = new THREE.Mesh( new Sphere( 1, 16, 8, 1 ), new THREE.MeshColorFillMaterial( 0x0040ff ) );
+				l2.position = light2.position;
+				scene.addObject( l2 );
+	 
+				var l3 = new THREE.Mesh( new Sphere( 1, 16, 8, 1 ), new THREE.MeshColorFillMaterial( 0x80ff80 ) );
+				l3.position = light3.position;
+				scene.addObject( l3 );
  
 				//renderer = new THREE.CanvasRenderer();
 				renderer = new THREE.WebGLRenderer( );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				container.appendChild( renderer.domElement );
-                
-                /*
+				
+				/*
 				stats = new Stats();
 				stats.domElement.style.position = 'absolute';
 				stats.domElement.style.top = '0px';
-                stats.domElement.style.zIndex = 100;
+				stats.domElement.style.zIndex = 100;
 				container.appendChild( stats.domElement );
-                */
+				*/
 
 			}
 
@@ -182,8 +198,8 @@
 				light3.position.z = Math.sin( time * 0.5 ) * 30;
 
 				renderer.render(scene, camera);
-                
-                //stats.update();
+				
+				//stats.update();
 
 			}
 

+ 15 - 24
examples/lights_test.html

@@ -33,7 +33,7 @@
 			<p>Using a modified version of <a href="http://github.com/alteredq/three.js">Three.js</a> by mrdoob.
 			
 			<br/>
-			<p>Best viewed in Chrome 7/8 or Firefox 4 using WebGL renderer.
+			<p>Best viewed in Chrome 8/9 or Firefox 4 using WebGL renderer.
 			<p>Canvas renderer is very slow on anything other than Chrome.
 		</div>
 		
@@ -58,6 +58,7 @@
 		<script type="text/javascript" src="../src/core/UV.js"></script>
 		<script type="text/javascript" src="../src/core/Geometry.js"></script>
 		<script type="text/javascript" src="../src/cameras/Camera.js"></script>
+		<script type="text/javascript" src="../src/io/Loader.js"></script>
 		<script type="text/javascript" src="../src/lights/Light.js"></script>
 		<script type="text/javascript" src="../src/lights/AmbientLight.js"></script>
 		<script type="text/javascript" src="../src/lights/DirectionalLight.js"></script>
@@ -211,34 +212,24 @@
 				bcanvas.addEventListener("click", toggleCanvas, false);
 				bwebgl.addEventListener("click", toggleWebGL, false);
 
-				function createModel() {
-					
-					// MESH
-					
-					geometry = new Torus( );
-					zmesh = new THREE.Mesh( geometry, new THREE.MeshColorFillMaterial( 0xffffff ) );
-					zmesh.position.x = 0;
-					zmesh.position.z = 0;
-					zmesh.position.y = 0;
-					zmesh.scale.x = zmesh.scale.y = zmesh.scale.z = 100;
-					zmesh.overdraw = true;
-					zmesh.updateMatrix();
-					scene.addObject(zmesh);
-					
-				}
 
-				loadAsync( "obj/torus/Torus.js", createModel);
+				var loader = new THREE.Loader();
+				loader.loadAsync( "obj/torus/Torus.js", function() { createScene( new Torus() ) } );
+				//loader.loadWorker( "obj/torus/Torus_slim.js", function( geometry ) { createScene( geometry ) } );
 
 			}
 
-			function loadAsync( url, callback ) {
+			function createScene( geometry ) {
+				
+				zmesh = new THREE.Mesh( geometry, new THREE.MeshColorFillMaterial( 0xffffff ) );
+				zmesh.position.x = 0;
+				zmesh.position.z = 0;
+				zmesh.position.y = 0;
+				zmesh.scale.x = zmesh.scale.y = zmesh.scale.z = 100;
+				zmesh.overdraw = true;
+				zmesh.updateMatrix();
+				scene.addObject(zmesh);
 				
-				var el = document.createElement( 'script' );
-				el.type = 'text/javascript';
-				el.onload = callback;
-				el.src = url;
-				document.getElementsByTagName("head")[0].appendChild(el);
-
 			}
 
 			

+ 249 - 262
examples/materials_test.html

@@ -6,46 +6,47 @@
 		<style type="text/css">
 			body {
 				background:#fff;
-                padding:0;
+				padding:0;
 				margin:0;
 				overflow:hidden;
-                font-family:georgia;
-                text-align:center;
+				font-family:georgia;
+				text-align:center;
 			}
-            h1 { }
-            a { color:skyblue }
-            canvas { pointer-events:none; z-index:10; position:relative; }
-            #log { position:absolute; top:50px; text-align:left; display:block; z-index:100 }
-            #d { text-align:center; margin:1em 0 -15.7em 0; z-index:0; position:relative; display:block }
-            .button { background:#000; color:#fff; padding:0.2em 0.5em; cursor:pointer }
-            .inactive { background:#999; color:#eee }
+			h1 { }
+			a { color:skyblue }
+			canvas { pointer-events:none; z-index:10; position:relative; }
+			#log { position:absolute; top:50px; text-align:left; display:block; z-index:100 }
+			#d { text-align:center; margin:1em 0 -15.7em 0; z-index:0; position:relative; display:block }
+			.button { background:#000; color:#fff; padding:0.2em 0.5em; cursor:pointer }
+			.inactive { background:#999; color:#eee }
 		</style>
 	</head>
 	
-    <body>
-        <div id="d">
-            <h1>Multi-materials test</h1>
-
-            <span id="rcanvas" class="button inactive">2d canvas renderer</span>
-            <span id="rwebgl" class="button">WebGL renderer</span>
-            <br/>
-
-            <p>Model by <a href="http://sketchup.google.com/3dwarehouse/details?mid=2c6fd128fca34052adc5f5b98d513da1">Reallusion iClone</a> 
-            
-            <p>Using a modified version of <a href="http://github.com/alteredq/three.js">Three.js</a> by mrdoob.
-            
-            <br/>
-            <p>Best viewed in Chrome 7/8 or Firefox 4 using WebGL renderer.
-            <p>Canvas renderer is very slow on anything other than Chrome.
-            <p><a href="http://code.google.com/p/chromium/issues/detail?id=60124">Line width is not set properly</a> on Chrome in Windows.
-        </div>
-        
-        <div id="log"></div>
+	<body>
+		<div id="d">
+			<h1>Multi-materials test</h1>
+
+			<span id="rcanvas" class="button inactive">2d canvas renderer</span>
+			<span id="rwebgl" class="button">WebGL renderer</span>
+			<br/>
+
+			<p>Model by <a href="http://sketchup.google.com/3dwarehouse/details?mid=2c6fd128fca34052adc5f5b98d513da1">Reallusion iClone</a> 
+			
+			<p>Using a modified version of <a href="http://github.com/alteredq/three.js">Three.js</a> by mrdoob.
+			
+			<br/>
+			<p>Best viewed in Chrome 8/9 or Firefox 4 using WebGL renderer.
+			<p>Canvas renderer is very slow on anything other than Chrome.
+			<p><a href="http://code.google.com/p/chromium/issues/detail?id=60124">Line width is not set properly</a> on Chrome in Windows.
+		</div>
+		
+		<div id="log"></div>
 
+		<script type="text/javascript" src="../build/Three.js"></script> 
 		<!--
-        <script type="text/javascript" src="../build/Three.js"></script> 
-        -->
+		-->
 
+		<!--
 		<script type="text/javascript" src="../src/Three.js"></script>
 		<script type="text/javascript" src="../src/core/Color.js"></script>
 		<script type="text/javascript" src="../src/core/Vector2.js"></script>
@@ -60,6 +61,7 @@
 		<script type="text/javascript" src="../src/core/UV.js"></script>
 		<script type="text/javascript" src="../src/core/Geometry.js"></script>
 		<script type="text/javascript" src="../src/cameras/Camera.js"></script>
+		<script type="text/javascript" src="../src/io/Loader.js"></script>
 		<script type="text/javascript" src="../src/lights/Light.js"></script>
 		<script type="text/javascript" src="../src/lights/AmbientLight.js"></script>
 		<script type="text/javascript" src="../src/lights/DirectionalLight.js"></script>
@@ -85,8 +87,7 @@
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace4.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableParticle.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableLine.js"></script>
-		<!--
-        -->
+		-->
 		
 		<script type="text/javascript" src="geometry/primitives/Sphere.js"></script>
 		<script type="text/javascript" src="geometry/primitives/Plane.js"></script>
@@ -107,248 +108,234 @@
 			var canvasRenderer, webglRenderer;
 
 			var mesh, zmesh, geometry;
-            
-            var directionalLight, pointLight;
-            
+			
+			var directionalLight, pointLight;
+			
 			var mouseX = 0;
 			var mouseY = 0;
 
 			var windowHalfX = window.innerWidth >> 1;
 			var windowHalfY = window.innerHeight >> 1;
 
-            var render_canvas = 1, render_gl = 1;
-            var has_gl = 0;
-            
-            var bcanvas = document.getElementById("rcanvas");
-            var bwebgl = document.getElementById("rwebgl");
-            
+			var render_canvas = 1, render_gl = 1;
+			var has_gl = 0;
+			
+			var bcanvas = document.getElementById("rcanvas");
+			var bwebgl = document.getElementById("rwebgl");
+			
 			document.addEventListener('mousemove', onDocumentMouseMove, false);
 
 			init();
-            
+			
 			loop();
-            
-            //render_canvas = !has_gl;
-            bwebgl.style.display = has_gl ? "inline" : "none";
-            bcanvas.className = render_canvas ? "button" : "button inactive";
-            
+			
+			//render_canvas = !has_gl;
+			bwebgl.style.display = has_gl ? "inline" : "none";
+			bcanvas.className = render_canvas ? "button" : "button inactive";
+			
 			setInterval(loop, 1000/60);
-            
+			
 			function init() {
 
 				container = document.createElement('div');
 				document.body.appendChild(container);
 
 				camera = new THREE.Camera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 100000 );
-                camera.position.z = 500;
-                camera.updateMatrix();
+				camera.position.z = 500;
+				camera.updateMatrix();
 
 				scene = new THREE.Scene();
-                
-                // SPHERES
-                
-                sphere = new Sphere( 100, 16, 8, 1 );
-                for (var i=0; i<10; i++) {
-                    //mesh = new THREE.Mesh( sphere, new THREE.MeshColorFillMaterial( 0xffffff ) );
-                    mesh = new THREE.Mesh( sphere, [ new THREE.MeshColorFillMaterial( 0xffffff ), new THREE.MeshColorStrokeMaterial( 0x000000, 1, 1.5 ) ] );
-                    //mesh = new THREE.Mesh( sphere, new THREE.MeshColorStrokeMaterial( 0x00aa00, 1, 1.5 ) );
-                    //mesh = new THREE.Mesh( sphere, new THREE.MeshFaceColorStrokeMaterial( 2.5 ) );
-                    mesh.position.x = 500 * (Math.random() - 0.5);
-                    mesh.position.y = 500 * (Math.random() - 0.5);
-                    mesh.position.z = 500 * (Math.random() - 0.5);
-                    mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.25 * (Math.random() + 0.5);
-                    //mesh.doubleSided = true;
-                    mesh.overdraw = true;
-                    mesh.updateMatrix();
-                    scene.addObject(mesh);
-                }
-                
+				
+				// SPHERES
+				
+				sphere = new Sphere( 100, 16, 8, 1 );
+				for (var i=0; i<10; i++) {
+					//mesh = new THREE.Mesh( sphere, new THREE.MeshColorFillMaterial( 0xffffff ) );
+					mesh = new THREE.Mesh( sphere, [ new THREE.MeshColorFillMaterial( 0xffffff ), new THREE.MeshColorStrokeMaterial( 0x000000, 1, 1.5 ) ] );
+					//mesh = new THREE.Mesh( sphere, new THREE.MeshColorStrokeMaterial( 0x00aa00, 1, 1.5 ) );
+					//mesh = new THREE.Mesh( sphere, new THREE.MeshFaceColorStrokeMaterial( 2.5 ) );
+					mesh.position.x = 500 * (Math.random() - 0.5);
+					mesh.position.y = 500 * (Math.random() - 0.5);
+					mesh.position.z = 500 * (Math.random() - 0.5);
+					mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.25 * (Math.random() + 0.5);
+					//mesh.doubleSided = true;
+					mesh.overdraw = true;
+					mesh.updateMatrix();
+					scene.addObject(mesh);
+				}
+				
 
 				// LIGHTS
 
-                var ambient = new THREE.AmbientLight( 0x101010 );
+				var ambient = new THREE.AmbientLight( 0x101010 );
 				scene.addLight( ambient );
 
-                directionalLight = new THREE.DirectionalLight( 0xffffff );
-                directionalLight.position.y = -70;
-                directionalLight.position.z = 100;
+				directionalLight = new THREE.DirectionalLight( 0xffffff );
+				directionalLight.position.y = -70;
+				directionalLight.position.z = 100;
 				directionalLight.position.normalize();
 				scene.addLight( directionalLight );
 
 				pointLight = new THREE.PointLight( 0xffaa00 );
-                pointLight.position.x = 0;
-                pointLight.position.y = 0;
-                pointLight.position.z = 120;
+				pointLight.position.x = 0;
+				pointLight.position.y = 0;
+				pointLight.position.z = 120;
 				//scene.addLight( pointLight );
 
-
-                mesh = new THREE.Mesh( sphere, new THREE.MeshColorFillMaterial( 0xff0000 ) );
-                mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.1;
-                mesh.position = pointLight.position;
-                mesh.updateMatrix();
-                scene.addObject(mesh);
-
-
-                if( render_canvas ) {
-                    canvasRenderer = new THREE.CanvasRenderer();
-                    canvasRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-                    container.appendChild( canvasRenderer.domElement );
-                }
-
-                if ( render_gl ) {
-                    try {
-                        webglRenderer = new THREE.WebGLRenderer();
-                        webglRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-                        container.appendChild( webglRenderer.domElement );
-                        has_gl = 1;
-                    }
-                    catch (e) {
-                    }
-                }
+				mesh = new THREE.Mesh( sphere, new THREE.MeshColorFillMaterial( 0xff0000 ) );
+				mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.1;
+				mesh.position = pointLight.position;
+				mesh.updateMatrix();
+				scene.addObject(mesh);
+
+				if( render_canvas ) {
+					canvasRenderer = new THREE.CanvasRenderer();
+					canvasRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
+					container.appendChild( canvasRenderer.domElement );
+				}
+
+				if ( render_gl ) {
+					try {
+						webglRenderer = new THREE.WebGLRenderer();
+						webglRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
+						container.appendChild( webglRenderer.domElement );
+						has_gl = 1;
+					}
+					catch (e) {
+					}
+				}
 
 				stats = new Stats();
 				stats.domElement.style.position = 'absolute';
 				stats.domElement.style.top = '0px';
-                stats.domElement.style.zIndex = 100;
+				stats.domElement.style.zIndex = 100;
 				container.appendChild( stats.domElement );
-                
-                bcanvas.addEventListener("click", toggleCanvas, false);
-                bwebgl.addEventListener("click", toggleWebGL, false);
-
-                function createModel2() {
-                    
-                    // MESH
-                    
-                    geometry = new Female02( "obj/female02" );
-                    
-                    // PROCEDURAL TEXTURES (decals)
-                    
-                    var x1 = document.createElement( "canvas" );
-                    var xc1 = x1.getContext("2d");
-                    x1.width = x1.height = 256;
-                                    
-                    xc1.shadowBlur = 3;
-                    xc1.shadowColor = "#000";
-                    xc1.font = "7pt arial";
-                    xc1.fillStyle = "hsla("+0+",90%,50%,1);"
-                    xc1.fillText("Three", 57, 29);
-                    
-                    xc1.fillStyle = "hsla("+0+",90%,50%,0.15);"
-                    xc1.fillRect(40, 70, 60, 50);
-                    
-                    for(var i=0;i<500;i++) {
-                        xc1.fillStyle = "hsla("+60*Math.random()+",90%,50%,0.5);"
-                        xc1.fillRect(40+60*Math.random(), 118+10*Math.random(), 2, 10);
-                    }
-
-                    var x2 = document.createElement( "canvas" );
-                    var xc2 = x2.getContext("2d");
-                    x2.width = x2.height = 128;
-                    xc2.fillStyle = "rgba(0,0,0,0.5)";
-                    for(var i=0;i<14;i++) {
-                        xc2.fillRect(0, 5+i*4, 54, 2);
-                        xc2.fillRect(i*4, 5, 2, 54);
-                    }
-
-                    var xm1 = new THREE.MeshBitmapMaterial( x1 );
-                    xm1.loaded = 1; // this is procedurally generated texture
-
-                    var xm2 = new THREE.MeshBitmapMaterial( x2 );
-                    xm2.loaded = 1; // this is procedurally generated texture
-                    
-                    geometry.materials[0].push ( xm1 ); // goes to faces with material 0
-                    geometry.materials[1].push ( xm2 ); // goes to faces with material 1
-                    
-                    geometry.materials[4].push ( new THREE.MeshColorFillMaterial(0xff0000, 0.5) );
-
-                    var materials = [ new THREE.MeshFaceMaterial() ];
-                    
-                    // full-mesh wireframe overlay
-                    //materials.push( new THREE.MeshColorStrokeMaterial(0xff0000, 1, 1.5) );
-                    
-                    // full-mesh color overlay
-                    //materials.push( new THREE.MeshColorFillMaterial(0xff0000, 0.5) );
-                    
-                    zmesh = new THREE.Mesh( geometry, materials, 1 );
-                    zmesh.position.x = -80;
-                    zmesh.position.z = 50;
-                    zmesh.position.y = FLOOR;
-                    zmesh.scale.x = zmesh.scale.y = zmesh.scale.z = 3;
-                    zmesh.overdraw = true;
-                    zmesh.updateMatrix();
-                    scene.addObject(zmesh);
-
-                    // PLANES with all materials from the model
-                    
-                    createMaterialsPalette( geometry.materials, 100, 0 );
-                }
-
-                loadAsync( "obj/female02/female02.js", createModel2);
+				
+				bcanvas.addEventListener("click", toggleCanvas, false);
+				bwebgl.addEventListener("click", toggleWebGL, false);
+
+				var loader = new THREE.Loader();
+				loader.loadAsync( "obj/female02/female02.js", function() { createScene( new Female02( "obj/female02" ) ) } );
+				//loader.loadWorker( "obj/female02/Female02_slim.js", function( geometry ) { createScene( geometry) }, "obj/female02" );
 
 			}
 
-            function loadAsync( url, callback ) {
-                
-                var el = document.createElement( 'script' );
-                el.type = 'text/javascript';
-                el.onload = callback;
-                el.src = url;
-                document.getElementsByTagName("head")[0].appendChild(el);
-
-            }
-            
-            function createMaterialsPalette( materials, size, bottom ) {
-                
-                for ( var i=0; i<materials.length; ++i ) {
-                    
-                    // material
-                    mesh = new THREE.Mesh( new Plane( size, size ), materials[i] );
-                    mesh.position.x = i * (size + 5) - ( ( materials.length - 1 )* ( size + 5 )/2);
-                    mesh.position.y = FLOOR + size/2 + bottom;
-                    mesh.position.z = -100;
-                    mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
-                    mesh.doubleSided = true;
-                    mesh.updateMatrix();
-                    scene.addObject(mesh);
-                    
-                    // number
-                    var x = document.createElement( "canvas" );
-                    var xc = x.getContext("2d");
-                    x.width = x.height = 128;
-                    xc.shadowColor = "#000";
-                    xc.shadowBlur = 7;
-                    xc.fillStyle = "orange";
-                    xc.font = "50pt arial bold";
-                    xc.fillText(i, 10, 64);
-                    
-                    var xm = new THREE.MeshBitmapMaterial( x );
-                    xm.loaded = 1;
-                    
-                    mesh = new THREE.Mesh( new Plane( size, size ), xm );
-                    mesh.position.x = i * (size + 5) - ( ( materials.length - 1 )* ( size + 5 )/2);
-                    mesh.position.y = FLOOR + size/2 + bottom;
-                    mesh.position.z = -99;
-                    mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
-                    mesh.doubleSided = true;
-                    mesh.updateMatrix();
-                    scene.addObject(mesh);
-                }
-                
-            }
-            
+			function createScene( geometry ) {
+				
+				// PROCEDURAL TEXTURES (decals)
+				
+				var x1 = document.createElement( "canvas" );
+				var xc1 = x1.getContext("2d");
+				x1.width = x1.height = 256;
+								
+				xc1.shadowBlur = 3;
+				xc1.shadowColor = "#000";
+				xc1.font = "7pt arial";
+				xc1.fillStyle = "hsla("+0+",90%,50%,1);"
+				xc1.fillText("Three", 57, 29);
+				
+				xc1.fillStyle = "hsla("+0+",90%,50%,0.15);"
+				xc1.fillRect(40, 70, 60, 50);
+				
+				for(var i=0;i<500;i++) {
+					xc1.fillStyle = "hsla("+60*Math.random()+",90%,50%,0.5);"
+					xc1.fillRect(40+60*Math.random(), 118+10*Math.random(), 2, 10);
+				}
+
+				var x2 = document.createElement( "canvas" );
+				var xc2 = x2.getContext("2d");
+				x2.width = x2.height = 128;
+				xc2.fillStyle = "rgba(0,0,0,0.5)";
+				for(var i=0;i<14;i++) {
+					xc2.fillRect(0, 5+i*4, 54, 2);
+					xc2.fillRect(i*4, 5, 2, 54);
+				}
+
+				var xm1 = new THREE.MeshBitmapMaterial( x1 );
+				xm1.loaded = 1; // this is procedurally generated texture
+
+				var xm2 = new THREE.MeshBitmapMaterial( x2 );
+				xm2.loaded = 1; // this is procedurally generated texture
+				
+				geometry.materials[0].push ( xm1 ); // goes to faces with material 0
+				geometry.materials[1].push ( xm2 ); // goes to faces with material 1
+				
+				geometry.materials[4].push ( new THREE.MeshColorFillMaterial(0xff0000, 0.5) );
+
+				var materials = [ new THREE.MeshFaceMaterial() ];
+				
+				// full-mesh wireframe overlay
+				//materials.push( new THREE.MeshColorStrokeMaterial(0xff0000, 1, 1.5) );
+				
+				// full-mesh color overlay
+				//materials.push( new THREE.MeshColorFillMaterial(0xff0000, 0.5) );
+				
+				zmesh = new THREE.Mesh( geometry, materials, 1 );
+				zmesh.position.x = -80;
+				zmesh.position.z = 50;
+				zmesh.position.y = FLOOR;
+				zmesh.scale.x = zmesh.scale.y = zmesh.scale.z = 3;
+				zmesh.overdraw = true;
+				zmesh.updateMatrix();
+				scene.addObject(zmesh);
+
+				// PLANES with all materials from the model
+				
+				createMaterialsPalette( geometry.materials, 100, 0 );
+			}
+			
+			function createMaterialsPalette( materials, size, bottom ) {
+				
+				for ( var i=0; i<materials.length; ++i ) {
+					
+					// material
+					mesh = new THREE.Mesh( new Plane( size, size ), materials[i] );
+					mesh.position.x = i * (size + 5) - ( ( materials.length - 1 )* ( size + 5 )/2);
+					mesh.position.y = FLOOR + size/2 + bottom;
+					mesh.position.z = -100;
+					mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
+					mesh.doubleSided = true;
+					mesh.updateMatrix();
+					scene.addObject(mesh);
+					
+					// number
+					var x = document.createElement( "canvas" );
+					var xc = x.getContext("2d");
+					x.width = x.height = 128;
+					xc.shadowColor = "#000";
+					xc.shadowBlur = 7;
+					xc.fillStyle = "orange";
+					xc.font = "50pt arial bold";
+					xc.fillText(i, 10, 64);
+					
+					var xm = new THREE.MeshBitmapMaterial( x );
+					xm.loaded = 1;
+					
+					mesh = new THREE.Mesh( new Plane( size, size ), xm );
+					mesh.position.x = i * (size + 5) - ( ( materials.length - 1 )* ( size + 5 )/2);
+					mesh.position.y = FLOOR + size/2 + bottom;
+					mesh.position.z = -99;
+					mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
+					mesh.doubleSided = true;
+					mesh.updateMatrix();
+					scene.addObject(mesh);
+				}
+				
+			}
+			
 			function onDocumentMouseMove(event) {
 
 				mouseX = ( event.clientX - windowHalfX );
 				mouseY = ( event.clientY - windowHalfY );
 
 			}
-            
+			
 			function loop() {
 
 				camera.position.x += ( mouseX - camera.position.x ) * .05;
 				camera.position.y += ( - mouseY - camera.position.y ) * .05;
 				camera.updateMatrix();
-                
+				
 				if ( render_canvas ) canvasRenderer.render( scene, camera );
 				if ( render_gl && has_gl ) webglRenderer.render( scene, camera );
 
@@ -356,42 +343,42 @@
 
 			}
 
-            function log(text) {
-            
-                var e = document.getElementById("log");
-                e.innerHTML = text + "<br/>" + e.innerHTML;
-                
-            }
-            
-            function toggleCanvas() {
-            
-                render_canvas = !render_canvas;
-                bcanvas.className = render_canvas ? "button" : "button inactive";
-                
-                render_gl = !render_canvas;
-                bwebgl.className = render_gl ? "button" : "button inactive";
-                
-                if( has_gl )
-                    webglRenderer.domElement.style.display = render_gl ? "block" : "none";
-                
-                canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
-                
-            }
-            
-            function toggleWebGL() {
-            
-                render_gl = !render_gl;
-                bwebgl.className = render_gl ? "button" : "button inactive";
-                
-                render_canvas = !render_gl;
-                bcanvas.className = render_canvas ? "button" : "button inactive";
-                
-                if( has_gl )
-                    webglRenderer.domElement.style.display = render_gl ? "block" : "none";
-                    
-                canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
-                
-            }
+			function log(text) {
+			
+				var e = document.getElementById("log");
+				e.innerHTML = text + "<br/>" + e.innerHTML;
+				
+			}
+			
+			function toggleCanvas() {
+			
+				render_canvas = !render_canvas;
+				bcanvas.className = render_canvas ? "button" : "button inactive";
+				
+				render_gl = !render_canvas;
+				bwebgl.className = render_gl ? "button" : "button inactive";
+				
+				if( has_gl )
+					webglRenderer.domElement.style.display = render_gl ? "block" : "none";
+				
+				canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
+				
+			}
+			
+			function toggleWebGL() {
+			
+				render_gl = !render_gl;
+				bwebgl.className = render_gl ? "button" : "button inactive";
+				
+				render_canvas = !render_gl;
+				bcanvas.className = render_canvas ? "button" : "button inactive";
+				
+				if( has_gl )
+					webglRenderer.domElement.style.display = render_gl ? "block" : "none";
+					
+				canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
+				
+			}
 		</script>
 
 	</body>

File diff suppressed because it is too large
+ 94 - 0
examples/obj/female02/Female02_slim.js


File diff suppressed because it is too large
+ 18 - 0
examples/obj/lucy/Lucy100k_slim.js


File diff suppressed because it is too large
+ 80 - 0
examples/obj/male02/Male02_slim.js


File diff suppressed because it is too large
+ 16 - 0
examples/obj/torus/Torus_slim.js


File diff suppressed because it is too large
+ 23 - 0
examples/obj/walt/WaltHead_slim.js


+ 225 - 254
examples/obj_convert_test.html

@@ -6,45 +6,45 @@
 		<style type="text/css">
 			body {
 				background:#fff;
-                padding:0;
+				padding:0;
 				margin:0;
 				overflow:hidden;
-                font-family:georgia;
-                text-align:center;
+				font-family:georgia;
+				text-align:center;
 			}
-            h1 { }
-            a { color:skyblue }
-            canvas { pointer-events:none; z-index:10; position:relative; }
-            #log { position:absolute; top:0; display:none }
-            #d { text-align:center; margin:1em 0 -15.7em 0; z-index:0; position:relative; display:block }
-            .button { background:#000; color:#fff; padding:0.2em 0.5em; cursor:pointer }
-            .inactive { background:#999; color:#eee }
+			h1 { }
+			a { color:skyblue }
+			canvas { pointer-events:none; z-index:10; position:relative; }
+			#log { position:absolute; top:0; display:none }
+			#d { text-align:center; margin:1em 0 -15.7em 0; z-index:0; position:relative; display:block }
+			.button { background:#000; color:#fff; padding:0.2em 0.5em; cursor:pointer }
+			.inactive { background:#999; color:#eee }
 		</style>
 	</head>
 	
-    <body>
-        <div id="d">
-            <h1>OBJ to Three.js converter test</h1>
-
-            <span id="rcanvas" class="button inactive">2d canvas renderer</span>
-            <span id="rwebgl" class="button">WebGL renderer</span>
-            <br/>
-
-            <p>Models by <a href="http://sketchup.google.com/3dwarehouse/details?mid=2c6fd128fca34052adc5f5b98d513da1">Reallusion<a> 
-            <a href="http://sketchup.google.com/3dwarehouse/details?mid=f526cc4abf7cb68d76cab47c765b7255">iClone</a>.
-            
-            <p>Using a modified version of <a href="http://github.com/alteredq/three.js">Three.js</a> by mrdoob.
-            
-            <br/>
-            <p>Best viewed in Chrome 7/8 or Firefox 4 using WebGL renderer.
-            <p>Canvas renderer is very slow on anything other than Chrome.
-        </div>
-        
-        <div id="log"></div>
-
-        <script type="text/javascript" src="../build/Three.js"></script> 
+	<body>
+		<div id="d">
+			<h1>OBJ to Three.js converter test</h1>
+
+			<span id="rcanvas" class="button inactive">2d canvas renderer</span>
+			<span id="rwebgl" class="button">WebGL renderer</span>
+			<br/>
+
+			<p>Models by <a href="http://sketchup.google.com/3dwarehouse/details?mid=2c6fd128fca34052adc5f5b98d513da1">Reallusion<a> 
+			<a href="http://sketchup.google.com/3dwarehouse/details?mid=f526cc4abf7cb68d76cab47c765b7255">iClone</a>.
+			
+			<p>Using a modified version of <a href="http://github.com/alteredq/three.js">Three.js</a> by mrdoob.
+			
+			<br/>
+			<p>Best viewed in Chrome 8/9 or Firefox 4 using WebGL renderer.
+			<p>Canvas renderer is very slow on anything other than Chrome.
+		</div>
+		
+		<div id="log"></div>
+
+		<script type="text/javascript" src="../build/Three.js"></script> 
 		<!--
-        -->
+		-->
 
 		<!--
 		<script type="text/javascript" src="../src/Three.js"></script>
@@ -61,6 +61,7 @@
 		<script type="text/javascript" src="../src/core/UV.js"></script>
 		<script type="text/javascript" src="../src/core/Geometry.js"></script>
 		<script type="text/javascript" src="../src/cameras/Camera.js"></script>
+		<script type="text/javascript" src="../src/io/Loader.js"></script>
 		<script type="text/javascript" src="../src/lights/Light.js"></script>
 		<script type="text/javascript" src="../src/lights/AmbientLight.js"></script>
 		<script type="text/javascript" src="../src/lights/DirectionalLight.js"></script>
@@ -86,7 +87,7 @@
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace4.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableParticle.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableLine.js"></script>
-        -->
+		-->
 		
 
 		<script type="text/javascript" src="geometry/primitives/Sphere.js"></script>
@@ -108,29 +109,29 @@
 			var canvasRenderer, webglRenderer;
 
 			var mesh, zmesh, geometry;
-            
+			
 			var mouseX = 0;
 			var mouseY = 0;
 
 			var windowHalfX = window.innerWidth >> 1;
 			var windowHalfY = window.innerHeight >> 1;
 
-            var render_canvas = 1, render_gl = 1;
-            var has_gl = 0;
-            
-            var bcanvas = document.getElementById("rcanvas");
-            var bwebgl = document.getElementById("rwebgl");
-            
+			var render_canvas = 1, render_gl = 1;
+			var has_gl = 0;
+			
+			var bcanvas = document.getElementById("rcanvas");
+			var bwebgl = document.getElementById("rwebgl");
+			
 			document.addEventListener('mousemove', onDocumentMouseMove, false);
 
 			init();
-            
+			
 			loop();
-            
-            render_canvas = !has_gl;
-            bwebgl.style.display = has_gl ? "inline" : "none";
-            bcanvas.className = render_canvas ? "button" : "button inactive";
-            
+			
+			render_canvas = !has_gl;
+			bwebgl.style.display = has_gl ? "inline" : "none";
+			bcanvas.className = render_canvas ? "button" : "button inactive";
+			
 			setInterval(loop, 1000/60);
 
 
@@ -140,62 +141,62 @@
 				document.body.appendChild(container);
 
 				camera = new THREE.Camera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 100000 );
-                camera.position.z = 500;
-                camera.updateMatrix();
+				camera.position.z = 500;
+				camera.updateMatrix();
 
 				scene = new THREE.Scene();
-                
-                // GROUND
-                
-                var x = document.createElement( "canvas" );
-                var xc = x.getContext("2d");
-                x.width = x.height = 128;
-                xc.fillStyle = "#fff";
-                xc.fillRect(0, 0, 128, 128);
-                xc.fillStyle = "#000";
-                xc.fillRect(0, 0, 64, 64);
-                xc.fillStyle = "#999";
-                xc.fillRect(32, 32, 32, 32);
-                xc.fillStyle = "#000";
-                xc.fillRect(64, 64, 64, 64);
-                xc.fillStyle = "#555";
-                xc.fillRect(96, 96, 32, 32);
-                
-                var xm = new THREE.MeshBitmapMaterial( x );
-                xm.loaded = 1;
-                
-                geometry = new Plane( 100, 100, 15, 10 );
-                for(var i=0; i<geometry.uvs.length; i++) {
-                    var uvs = geometry.uvs[i];
-                    for ( j = 0, jl = uvs.length; j < jl; j++ ) {
-                        uvs[j].u *= 10;
-                        uvs[j].v *= 10;
-                    }
-                    
-                }
-                mesh = new THREE.Mesh( geometry, xm );
-                mesh.position.x = 0;
-                mesh.position.y = FLOOR;
-                mesh.position.z = 0;
-                mesh.rotation.x = 1.57;
-                mesh.scale.x = mesh.scale.y = mesh.scale.z = 10;
-                mesh.updateMatrix();
-                scene.addObject(mesh);
-                
-                // SPHERES
-                
-                sphere = new Sphere( 100, 16, 8, 1 );
-                for (var i=0; i<10; i++) {
-                    mesh = new THREE.Mesh( sphere, new THREE.MeshColorFillMaterial( 0xffdddddd ) );
-                    mesh.position.x = 500 * (Math.random() - 0.5);
-                    mesh.position.y = 300 * (Math.random() - 0) + FLOOR;
-                    mesh.position.z = 100 * (Math.random() - 1);
-                    mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.25 * (Math.random() + 0.5);
-                    mesh.overdraw = true;
-                    mesh.updateMatrix();
-                    scene.addObject(mesh);
-                }
-                
+				
+				// GROUND
+				
+				var x = document.createElement( "canvas" );
+				var xc = x.getContext("2d");
+				x.width = x.height = 128;
+				xc.fillStyle = "#fff";
+				xc.fillRect(0, 0, 128, 128);
+				xc.fillStyle = "#000";
+				xc.fillRect(0, 0, 64, 64);
+				xc.fillStyle = "#999";
+				xc.fillRect(32, 32, 32, 32);
+				xc.fillStyle = "#000";
+				xc.fillRect(64, 64, 64, 64);
+				xc.fillStyle = "#555";
+				xc.fillRect(96, 96, 32, 32);
+				
+				var xm = new THREE.MeshBitmapMaterial( x );
+				xm.loaded = 1;
+				
+				geometry = new Plane( 100, 100, 15, 10 );
+				for(var i=0; i<geometry.uvs.length; i++) {
+					var uvs = geometry.uvs[i];
+					for ( j = 0, jl = uvs.length; j < jl; j++ ) {
+						uvs[j].u *= 10;
+						uvs[j].v *= 10;
+					}
+					
+				}
+				mesh = new THREE.Mesh( geometry, xm );
+				mesh.position.x = 0;
+				mesh.position.y = FLOOR;
+				mesh.position.z = 0;
+				mesh.rotation.x = 1.57;
+				mesh.scale.x = mesh.scale.y = mesh.scale.z = 10;
+				mesh.updateMatrix();
+				scene.addObject(mesh);
+				
+				// SPHERES
+				
+				sphere = new Sphere( 100, 16, 8, 1 );
+				for (var i=0; i<10; i++) {
+					mesh = new THREE.Mesh( sphere, new THREE.MeshColorFillMaterial( 0xffdddddd ) );
+					mesh.position.x = 500 * (Math.random() - 0.5);
+					mesh.position.y = 300 * (Math.random() - 0) + FLOOR;
+					mesh.position.z = 100 * (Math.random() - 1);
+					mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.25 * (Math.random() + 0.5);
+					mesh.overdraw = true;
+					mesh.updateMatrix();
+					scene.addObject(mesh);
+				}
+				
 
 				// LIGHTS
 
@@ -203,8 +204,8 @@
 				scene.addLight( ambient );
 
 				var directionalLight = new THREE.DirectionalLight( 0xffeedd );
-                directionalLight.position.y = -70;
-                directionalLight.position.z = 100;
+				directionalLight.position.y = -70;
+				directionalLight.position.z = 100;
 				directionalLight.position.normalize();
 				scene.addLight( directionalLight );
 
@@ -212,125 +213,95 @@
 				//scene.addLight( pointLight );
 
 
-                if ( render_gl ) {
-                    try {
-                        webglRenderer = new THREE.WebGLRenderer();
-                        webglRenderer.setFaceCulling(0);
-                        webglRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-                        container.appendChild( webglRenderer.domElement );
-                        has_gl = 1;
-                    }
-                    catch (e) {
-                    }
-                }
-
-                if( render_canvas ) {
-                    canvasRenderer = new THREE.CanvasRenderer();
-                    canvasRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-                    container.appendChild( canvasRenderer.domElement );
-                }
+				if ( render_gl ) {
+					try {
+						webglRenderer = new THREE.WebGLRenderer();
+						webglRenderer.setFaceCulling(0);
+						webglRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
+						container.appendChild( webglRenderer.domElement );
+						has_gl = 1;
+					}
+					catch (e) {
+					}
+				}
+
+				if( render_canvas ) {
+					canvasRenderer = new THREE.CanvasRenderer();
+					canvasRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
+					container.appendChild( canvasRenderer.domElement );
+				}
 
 				stats = new Stats();
 				stats.domElement.style.position = 'absolute';
 				stats.domElement.style.top = '0px';
-                stats.domElement.style.zIndex = 100;
+				stats.domElement.style.zIndex = 100;
 				container.appendChild( stats.domElement );
-                
-                bcanvas.addEventListener("click", toggleCanvas, false);
-                bwebgl.addEventListener("click", toggleWebGL, false);
-                                
-                function createModel1() {
-                    
-                    // MESH
-                    
-                    geometry = new Male02( "obj/male02" );
-                    zmesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(), 1 );
-                    zmesh.position.x = 90;
-                    zmesh.position.z = 50;
-                    zmesh.position.y = FLOOR;
-                    zmesh.scale.x = zmesh.scale.y = zmesh.scale.z = 3;
-                    zmesh.overdraw = true;
-                    zmesh.updateMatrix();
-                    scene.addObject(zmesh);
-
-                    // PLANES with all materials from the model
-                    
-                    createMaterialsPalette( geometry.materials, 100, 105 );
-                }
-
-                function createModel2() {
-                    
-                    // MESH
-                    
-                    geometry = new Female02( "obj/female02" );
-                    zmesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(), 1 );
-                    zmesh.position.x = -80;
-                    zmesh.position.z = 50;
-                    zmesh.position.y = FLOOR;
-                    zmesh.scale.x = zmesh.scale.y = zmesh.scale.z = 3;
-                    zmesh.overdraw = true;
-                    zmesh.updateMatrix();
-                    scene.addObject(zmesh);
-
-                    // PLANES with all materials from the model
-                    
-                    createMaterialsPalette( geometry.materials, 100, 0 );
-                }
-
-                loadAsync( "obj/male02/male02.js", createModel1);
-                loadAsync( "obj/female02/female02.js", createModel2);
+				
+				bcanvas.addEventListener("click", toggleCanvas, false);
+				bwebgl.addEventListener("click", toggleWebGL, false);
+				
+				var loader = new THREE.Loader();
+				loader.loadAsync( "obj/male02/male02.js",     function() { createScene( new Male02( "obj/male02" ),      90, 50, FLOOR, 105 ) } );
+				loader.loadAsync( "obj/female02/female02.js", function() { createScene( new Female02( "obj/female02" ), -80, 50, FLOOR, 0 ) } );
+				
+				//loader.loadWorker( "obj/male02/Male02_slim.js",     function( geometry ) { createScene( geometry,  90, 50, FLOOR, 105 ) }, "obj/male02" );
+				//loader.loadWorker( "obj/female02/Female02_slim.js", function( geometry ) { createScene( geometry, -80, 50, FLOOR, 0 ) },   "obj/female02" );
+
+			}
 
+			function createScene( geometry, x, y, z, b ) {
+				
+				zmesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(), 1 );
+				zmesh.position.x = x;
+				zmesh.position.z = y;
+				zmesh.position.y = z;
+				zmesh.scale.x = zmesh.scale.y = zmesh.scale.z = 3;
+				zmesh.overdraw = true;
+				zmesh.updateMatrix();
+				scene.addObject(zmesh);
+				
+				createMaterialsPalette( geometry.materials, 100, b );
 			}
 
-            function loadAsync( url, callback ) {
-                
-                var el = document.createElement( 'script' );
-                el.type = 'text/javascript';
-                el.onload = callback;
-                el.src = url;
-                document.getElementsByTagName("head")[0].appendChild(el);
-
-            }
-            
-            function createMaterialsPalette( materials, size, bottom ) {
-                
-                for ( var i=0; i<materials.length; ++i ) {
-                    
-                    // material
-                    mesh = new THREE.Mesh( new Plane( size, size ), materials[i] );
-                    mesh.position.x = i * (size + 5) - ( ( materials.length - 1 )* ( size + 5 )/2);
-                    mesh.position.y = FLOOR + size/2 + bottom;
-                    mesh.position.z = -100;
-                    mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
-                    mesh.doubleSided = true;
-                    mesh.updateMatrix();
-                    scene.addObject(mesh);
-                    
-                    // number
-                    var x = document.createElement( "canvas" );
-                    var xc = x.getContext("2d");
-                    x.width = x.height = 128;
-                    xc.shadowColor = "#000";
-                    xc.shadowBlur = 7;
-                    xc.fillStyle = "orange";
-                    xc.font = "50pt arial bold";
-                    xc.fillText(i, 10, 64);
-                    
-                    var xm = new THREE.MeshBitmapMaterial( x );
-                    xm.loaded = 1;
-                    
-                    mesh = new THREE.Mesh( new Plane( size, size ), xm );
-                    mesh.position.x = i * (size + 5) - ( ( materials.length - 1 )* ( size + 5 )/2);
-                    mesh.position.y = FLOOR + size/2 + bottom;
-                    mesh.position.z = -99;
-                    mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
-                    mesh.doubleSided = true;
-                    mesh.updateMatrix();
-                    scene.addObject(mesh);
-                }
-                
-            }
-            
+			function createMaterialsPalette( materials, size, bottom ) {
+				
+				for ( var i=0; i<materials.length; ++i ) {
+					
+					// material
+					mesh = new THREE.Mesh( new Plane( size, size ), materials[i] );
+					mesh.position.x = i * (size + 5) - ( ( materials.length - 1 )* ( size + 5 )/2);
+					mesh.position.y = FLOOR + size/2 + bottom;
+					mesh.position.z = -100;
+					mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
+					mesh.doubleSided = true;
+					mesh.updateMatrix();
+					scene.addObject(mesh);
+					
+					// number
+					var x = document.createElement( "canvas" );
+					var xc = x.getContext("2d");
+					x.width = x.height = 128;
+					xc.shadowColor = "#000";
+					xc.shadowBlur = 7;
+					xc.fillStyle = "orange";
+					xc.font = "50pt arial bold";
+					xc.fillText(i, 10, 64);
+					
+					var xm = new THREE.MeshBitmapMaterial( x );
+					xm.loaded = 1;
+					
+					mesh = new THREE.Mesh( new Plane( size, size ), xm );
+					mesh.position.x = i * (size + 5) - ( ( materials.length - 1 )* ( size + 5 )/2);
+					mesh.position.y = FLOOR + size/2 + bottom;
+					mesh.position.z = -99;
+					mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
+					mesh.doubleSided = true;
+					mesh.updateMatrix();
+					scene.addObject(mesh);
+				}
+				
+			}
+			
 			function onDocumentMouseMove(event) {
 
 				mouseX = ( event.clientX - windowHalfX );
@@ -344,11 +315,11 @@
 				camera.position.y += ( - mouseY - camera.position.y ) * .05;
 				camera.updateMatrix();
 
-                if ( zmesh && 0 ) {
-                    zmesh.rotation.y += 0.005;
-                    zmesh.updateMatrix();
-                }
-                                
+				if ( zmesh && 0 ) {
+					zmesh.rotation.y += 0.005;
+					zmesh.updateMatrix();
+				}
+								
 				if ( render_canvas ) canvasRenderer.render( scene, camera );
 				if ( render_gl && has_gl ) webglRenderer.render( scene, camera );
 
@@ -356,42 +327,42 @@
 
 			}
 
-            function log(text) {
-            
-                var e = document.getElementById("log");
-                e.innerHTML = text + "<br/>" + e.innerHTML;
-                
-            }
-            
-            function toggleCanvas() {
-            
-                render_canvas = !render_canvas;
-                bcanvas.className = render_canvas ? "button" : "button inactive";
-                
-                render_gl = !render_canvas;
-                bwebgl.className = render_gl ? "button" : "button inactive";
-                
-                if( has_gl )
-                    webglRenderer.domElement.style.display = render_gl ? "block" : "none";
-                
-                canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
-                
-            }
-            
-            function toggleWebGL() {
-            
-                render_gl = !render_gl;
-                bwebgl.className = render_gl ? "button" : "button inactive";
-                
-                render_canvas = !render_gl;
-                bcanvas.className = render_canvas ? "button" : "button inactive";
-                
-                if( has_gl )
-                    webglRenderer.domElement.style.display = render_gl ? "block" : "none";
-                    
-                canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
-                
-            }
+			function log(text) {
+			
+				var e = document.getElementById("log");
+				e.innerHTML = text + "<br/>" + e.innerHTML;
+				
+			}
+			
+			function toggleCanvas() {
+			
+				render_canvas = !render_canvas;
+				bcanvas.className = render_canvas ? "button" : "button inactive";
+				
+				render_gl = !render_canvas;
+				bwebgl.className = render_gl ? "button" : "button inactive";
+				
+				if( has_gl )
+					webglRenderer.domElement.style.display = render_gl ? "block" : "none";
+				
+				canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
+				
+			}
+			
+			function toggleWebGL() {
+			
+				render_gl = !render_gl;
+				bwebgl.className = render_gl ? "button" : "button inactive";
+				
+				render_canvas = !render_gl;
+				bcanvas.className = render_canvas ? "button" : "button inactive";
+				
+				if( has_gl )
+					webglRenderer.domElement.style.display = render_gl ? "block" : "none";
+					
+				canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
+				
+			}
 		</script>
 
 	</body>

+ 210 - 209
examples/shader_test.html

@@ -6,44 +6,44 @@
 		<style type="text/css">
 			body {
 				background:#000;
-                color:#fff;
-                padding:0;
+				color:#fff;
+				padding:0;
 				margin:0;
 				overflow:hidden;
-                font-family:georgia;
-                text-align:center;
+				font-family:georgia;
+				text-align:center;
 			}
-            h1 { }
-            a { color:skyblue }
-            canvas { pointer-events:none; z-index:10; position:relative; }
-            #log { position:absolute; top:50px; text-align:left; display:block; z-index:100 }
-            #d { text-align:center; margin:1em 0 -19.7em 0; z-index:0; position:relative; display:block }
-            .button { background:orange; color:#fff; padding:0.2em 0.5em; cursor:pointer }
-            .inactive { background:#999; color:#eee }
+			h1 { }
+			a { color:skyblue }
+			canvas { pointer-events:none; z-index:10; position:relative; }
+			#log { position:absolute; top:50px; text-align:left; display:block; z-index:100 }
+			#d { text-align:center; margin:1em 0 -19.7em 0; z-index:0; position:relative; display:block }
+			.button { background:orange; color:#fff; padding:0.2em 0.5em; cursor:pointer }
+			.inactive { background:#999; color:#eee }
 		</style>
 	</head>
 	
-    <body>
-        <div id="d">
-            <h1>Shader test</h1>
-
-            <span id="rcanvas" class="button inactive">2d canvas renderer</span>
-            <span id="rwebgl" class="button">WebGL renderer</span>
-            <br/>
-            
-            <p>Using a modified version of <a href="http://github.com/alteredq/three.js">Three.js</a> by mrdoob.
-            
-            <br/>
-            <p>Best viewed in Chrome 7/8 or Firefox 4 using WebGL renderer.
-            <p>Canvas renderer is very slow on anything other than Chrome.
-            <p>Blinn-Phong shader only works in WebGL, canvas has only diffuse materials.
-        </div>
-        
-        <pre id="log"></pre>
-
-        <script type="text/javascript" src="../build/Three.js"></script> 
+	<body>
+		<div id="d">
+			<h1>Shader test</h1>
+
+			<span id="rcanvas" class="button inactive">2d canvas renderer</span>
+			<span id="rwebgl" class="button">WebGL renderer</span>
+			<br/>
+			
+			<p>Using a modified version of <a href="http://github.com/alteredq/three.js">Three.js</a> by mrdoob.
+			
+			<br/>
+			<p>Best viewed in Chrome 8/9 or Firefox 4 using WebGL renderer.
+			<p>Canvas renderer is very slow on anything other than Chrome.
+			<p>Blinn-Phong shader only works in WebGL, canvas has only diffuse materials.
+		</div>
+		
+		<pre id="log"></pre>
+
+		<script type="text/javascript" src="../build/Three.js"></script> 
 		<!--
-        -->
+		-->
 
 		<!--
 		<script type="text/javascript" src="../src/Three.js"></script>
@@ -60,6 +60,7 @@
 		<script type="text/javascript" src="../src/core/UV.js"></script>
 		<script type="text/javascript" src="../src/core/Geometry.js"></script>
 		<script type="text/javascript" src="../src/cameras/Camera.js"></script>
+		<script type="text/javascript" src="../src/io/Loader.js"></script>
 		<script type="text/javascript" src="../src/lights/Light.js"></script>
 		<script type="text/javascript" src="../src/lights/AmbientLight.js"></script>
 		<script type="text/javascript" src="../src/lights/DirectionalLight.js"></script>
@@ -85,7 +86,7 @@
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableFace4.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableParticle.js"></script>
 		<script type="text/javascript" src="../src/renderers/renderables/RenderableLine.js"></script>
-        -->
+		-->
 		
 		<script type="text/javascript" src="geometry/primitives/Sphere.js"></script>
 
@@ -105,171 +106,171 @@
 			var canvasRenderer, webglRenderer;
 
 			var mesh, zmesh, lightMesh, geometry;
-            
-            var directionalLight, pointLight;
-            
+			
+			var directionalLight, pointLight;
+			
 			var mouseX = 0;
 			var mouseY = 0;
 
 			var windowHalfX = window.innerWidth >> 1;
 			var windowHalfY = window.innerHeight >> 1;
 
-            var render_canvas = 1, render_gl = 1;
-            var has_gl = 0;
-            
-            var bcanvas = document.getElementById("rcanvas");
-            var bwebgl = document.getElementById("rwebgl");
-            
+			var render_canvas = 1, render_gl = 1;
+			var has_gl = 0;
+			
+			var bcanvas = document.getElementById("rcanvas");
+			var bwebgl = document.getElementById("rwebgl");
+			
 			document.addEventListener('mousemove', onDocumentMouseMove, false);
 
 			init();
-            
+			
 			loop();
-            
-            render_canvas = !has_gl;
-            bwebgl.style.display = has_gl ? "inline" : "none";
-            bcanvas.className = render_canvas ? "button" : "button inactive";
-            
+			
+			render_canvas = !has_gl;
+			bwebgl.style.display = has_gl ? "inline" : "none";
+			bcanvas.className = render_canvas ? "button" : "button inactive";
+			
 			setInterval(loop, 1000/60);
-            
-            function addMesh( geometry, scale, x, y, z, rx, ry, rz, material ) {
-                
-                mesh = new THREE.Mesh( geometry, material );
-                mesh.scale.x = mesh.scale.y = mesh.scale.z = scale;
-                mesh.position.x = x;
-                mesh.position.y = y;
-                mesh.position.z = z;
-                mesh.rotation.x = rx;
-                mesh.rotation.y = ry;
-                mesh.rotation.z = rz;
-                mesh.overdraw = true;
-                mesh.updateMatrix();
-                scene.addObject(mesh);
-                
-            }
-            
+			
+			function addMesh( geometry, scale, x, y, z, rx, ry, rz, material ) {
+				
+				mesh = new THREE.Mesh( geometry, material );
+				mesh.scale.x = mesh.scale.y = mesh.scale.z = scale;
+				mesh.position.x = x;
+				mesh.position.y = y;
+				mesh.position.z = z;
+				mesh.rotation.x = rx;
+				mesh.rotation.y = ry;
+				mesh.rotation.z = rz;
+				mesh.overdraw = true;
+				mesh.updateMatrix();
+				scene.addObject(mesh);
+				
+			}
+			
 			function init() {
 
 				container = document.createElement('div');
 				document.body.appendChild(container);
 
 				camera = new THREE.Camera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 100000 );
-                camera.position.z = 1000;
-                camera.updateMatrix();
+				camera.position.z = 1000;
+				camera.updateMatrix();
 
 				scene = new THREE.Scene();
 
 				// LIGHTS
 
-                var ambient = new THREE.AmbientLight( 0x101010 );
+				var ambient = new THREE.AmbientLight( 0x101010 );
 				scene.addLight( ambient );
 
-                directionalLight = new THREE.DirectionalLight( 0xffffff, 1.0 );
-                directionalLight.position.x = 1;
-                directionalLight.position.y = 1;
-                directionalLight.position.z = 2;
+				directionalLight = new THREE.DirectionalLight( 0xffffff, 1.0 );
+				directionalLight.position.x = 1;
+				directionalLight.position.y = 1;
+				directionalLight.position.z = 2;
 				directionalLight.position.normalize();
 				scene.addLight( directionalLight );
 
 				pointLight = new THREE.PointLight( 0xffffff );
-                pointLight.position.x = 0;
-                pointLight.position.y = 0;
-                pointLight.position.z = 0;
+				pointLight.position.x = 0;
+				pointLight.position.y = 0;
+				pointLight.position.z = 0;
 				scene.addLight( pointLight );
 
-                // light representation
-                sphere = new Sphere( 100, 16, 8, 1 );
-                lightMesh = new THREE.Mesh( sphere, new THREE.MeshColorFillMaterial( 0xffaa00 ) );
-                lightMesh.scale.x = lightMesh.scale.y = lightMesh.scale.z = 0.05;
-                lightMesh.position = pointLight.position;
-                lightMesh.overdraw = true;
-                lightMesh.updateMatrix();
-                scene.addObject(lightMesh);
-
-                // material samples
-                sphere = new Sphere( 100, 32, 32, 1 );
-
-                var y1 = 0, y2 = -200;
-                
-                addMesh( sphere, 1, -600, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x050505, 0x000000, 0x555555, 30, 1.0 ) );
-                addMesh( sphere, 1, -600, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0x050505 ) );
-
-                addMesh( sphere, 1, -400, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0xffffff, 0x555555, 30, 1.0 ) );
-                addMesh( sphere, 1, -400, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0xffffff ) );
-
-                addMesh( sphere, 1, -200, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0xff5500, 0x555555, 10, 1.0 ) );
-                addMesh( sphere, 1, -200, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0xff5500 ) );
-
-                addMesh( sphere, 1,    0, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0xffaa00, 0x555555, 30, 1.0 ) );
-                addMesh( sphere, 1,    0, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0xffaa00 ) );
-
-                addMesh( sphere, 1,  200, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0x55ff00, 0x555555, 30, 1.0 ) );
-                addMesh( sphere, 1,  200, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0x55ff00 ) );
-
-                addMesh( sphere, 1,  400, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0x0055ff, 0x555555, 30, 1.0 ) );
-                addMesh( sphere, 1,  400, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0x0055ff ) );
-
-                addMesh( sphere, 1,  600, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0x5500ff, 0x555555, 30, 1.0 ) );
-                addMesh( sphere, 1,  600, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0x5500ff ) );
-
-                if ( render_gl ) {
-                    try {
-                        webglRenderer = new THREE.WebGLRenderer( scene );
-                        webglRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-                        container.appendChild( webglRenderer.domElement );
-                        has_gl = 1;
-                    }
-                    catch (e) {
-                    }
-                }
-                
-                if( render_canvas ) {
-                    canvasRenderer = new THREE.CanvasRenderer();
-                    canvasRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-                    container.appendChild( canvasRenderer.domElement );
-                }
+				// light representation
+				sphere = new Sphere( 100, 16, 8, 1 );
+				lightMesh = new THREE.Mesh( sphere, new THREE.MeshColorFillMaterial( 0xffaa00 ) );
+				lightMesh.scale.x = lightMesh.scale.y = lightMesh.scale.z = 0.05;
+				lightMesh.position = pointLight.position;
+				lightMesh.overdraw = true;
+				lightMesh.updateMatrix();
+				scene.addObject(lightMesh);
+
+				// material samples
+				sphere = new Sphere( 100, 32, 32, 1 );
+
+				var y1 = 0, y2 = -200;
+				
+				addMesh( sphere, 1, -600, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x050505, 0x000000, 0x555555, 30, 1.0 ) );
+				addMesh( sphere, 1, -600, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0x050505 ) );
+
+				addMesh( sphere, 1, -400, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0xffffff, 0x555555, 30, 1.0 ) );
+				addMesh( sphere, 1, -400, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0xffffff ) );
+
+				addMesh( sphere, 1, -200, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0xff5500, 0x555555, 10, 1.0 ) );
+				addMesh( sphere, 1, -200, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0xff5500 ) );
+
+				addMesh( sphere, 1,    0, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0xffaa00, 0x555555, 30, 1.0 ) );
+				addMesh( sphere, 1,    0, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0xffaa00 ) );
+
+				addMesh( sphere, 1,  200, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0x55ff00, 0x555555, 30, 1.0 ) );
+				addMesh( sphere, 1,  200, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0x55ff00 ) );
+
+				addMesh( sphere, 1,  400, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0x0055ff, 0x555555, 30, 1.0 ) );
+				addMesh( sphere, 1,  400, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0x0055ff ) );
+
+				addMesh( sphere, 1,  600, y1, 0, 0,0,0, new THREE.MeshPhongMaterial( 0x000000, 0x5500ff, 0x555555, 30, 1.0 ) );
+				addMesh( sphere, 1,  600, y2, 0, 0,0,0, new THREE.MeshColorFillMaterial( 0x5500ff ) );
+
+				if ( render_gl ) {
+					try {
+						webglRenderer = new THREE.WebGLRenderer( scene );
+						webglRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
+						container.appendChild( webglRenderer.domElement );
+						has_gl = 1;
+					}
+					catch (e) {
+					}
+				}
+				
+				if( render_canvas ) {
+					canvasRenderer = new THREE.CanvasRenderer();
+					canvasRenderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
+					container.appendChild( canvasRenderer.domElement );
+				}
 
 
 				stats = new Stats();
 				stats.domElement.style.position = 'absolute';
 				stats.domElement.style.top = '0px';
-                stats.domElement.style.zIndex = 100;
+				stats.domElement.style.zIndex = 100;
 				container.appendChild( stats.domElement );
-                
-                bcanvas.addEventListener("click", toggleCanvas, false);
-                bwebgl.addEventListener("click", toggleWebGL, false);
-
-                function createModel() {
-                    
-                    geometry = new Torus( );
-                    
-                    var s = 80, t = s + 20, y = 200;
-                    
-                    addMesh( geometry, s, -6*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x000000, 0x000000, 0x333333, 10, 1.0 ) );
-                    addMesh( geometry, s, -4*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x888888, 0x333333, 10, 1.0 ) );
-                    addMesh( geometry, s, -2*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0xff5500, 10, 1.0 ) );
-                    addMesh( geometry, s,    0, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0xffaa00, 10, 1.0 ) );
-                    addMesh( geometry, s,  2*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0x55ff00, 10, 1.0 ) );
-                    addMesh( geometry, s,  4*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0x0055ff, 10, 1.0 ) );
-                    addMesh( geometry, s,  6*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0x5500ff, 10, 1.0 ) );
-
-                }
-
-                loadAsync( "obj/torus/Torus.js", createModel);
+				
+				bcanvas.addEventListener("click", toggleCanvas, false);
+				bwebgl.addEventListener("click", toggleWebGL, false);
+
+				var loader = new THREE.Loader();
+				loader.loadAsync( "obj/torus/Torus.js", function() { createScene( new Torus() ) } );
+				//loader.loadWorker( "obj/torus/Torus_slim.js", function( geometry ) { createScene( geometry ) } );
+
+			}
+
+			function createScene( geometry ) {
+				
+				var s = 80, t = s + 20, y = 200;
+				
+				addMesh( geometry, s, -6*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x000000, 0x000000, 0x333333, 10, 1.0 ) );
+				addMesh( geometry, s, -4*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x888888, 0x333333, 10, 1.0 ) );
+				addMesh( geometry, s, -2*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0xff5500, 10, 1.0 ) );
+				addMesh( geometry, s,    0, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0xffaa00, 10, 1.0 ) );
+				addMesh( geometry, s,  2*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0x55ff00, 10, 1.0 ) );
+				addMesh( geometry, s,  4*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0x0055ff, 10, 1.0 ) );
+				addMesh( geometry, s,  6*t, y, 0, 1.57,0,0, new THREE.MeshPhongMaterial( 0x030303, 0x030303, 0x5500ff, 10, 1.0 ) );
 
 			}
 
-            function loadAsync( url, callback ) {
-                
-                var el = document.createElement( 'script' );
-                el.type = 'text/javascript';
-                el.onload = callback;
-                el.src = url;
-                document.getElementsByTagName("head")[0].appendChild(el);
+			function loadAsync( url, callback ) {
+				
+				var el = document.createElement( 'script' );
+				el.type = 'text/javascript';
+				el.onload = callback;
+				el.src = url;
+				document.getElementsByTagName("head")[0].appendChild(el);
 
-            }
+			}
 
-            
+			
 			function onDocumentMouseMove(event) {
 
 				mouseX = ( event.clientX - windowHalfX );
@@ -277,28 +278,28 @@
 
 			}
 
-            var r = 0;
-            
+			var r = 0;
+			
 			function loop() {
 
 				camera.position.x += ( mouseX - camera.position.x ) * .05;
 				camera.position.y += ( - mouseY - camera.position.y ) * .05;
 				camera.updateMatrix();
-               
-                
-                /*
-                for(var i=0; i<scene.objects.length; ++i) {
-                    scene.objects[i].rotation.x += 0.0025;                        
-                    scene.objects[i].updateMatrix();
-                }
-                */
-                lightMesh.position.x = 700*Math.cos(r);
-                lightMesh.position.z = 700*Math.sin(r);
-                lightMesh.updateMatrix();
-
-                r += 0.01;
-                
-                
+			   
+				
+				/*
+				for(var i=0; i<scene.objects.length; ++i) {
+					scene.objects[i].rotation.x += 0.0025;                        
+					scene.objects[i].updateMatrix();
+				}
+				*/
+				lightMesh.position.x = 700*Math.cos(r);
+				lightMesh.position.z = 700*Math.sin(r);
+				lightMesh.updateMatrix();
+
+				r += 0.01;
+				
+				
 				if ( render_canvas ) canvasRenderer.render( scene, camera );
 				if ( render_gl && has_gl ) webglRenderer.render( scene, camera );
 
@@ -306,42 +307,42 @@
 
 			}
 
-            function log(text) {
-            
-                var e = document.getElementById("log");
-                e.innerHTML = text + "<br/>" + e.innerHTML;
-                
-            }
-            
-            function toggleCanvas() {
-            
-                render_canvas = !render_canvas;
-                bcanvas.className = render_canvas ? "button" : "button inactive";
-                
-                render_gl = !render_canvas;
-                bwebgl.className = render_gl ? "button" : "button inactive";
-                
-                if( has_gl )
-                    webglRenderer.domElement.style.display = render_gl ? "block" : "none";
-                
-                canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
-                
-            }
-            
-            function toggleWebGL() {
-            
-                render_gl = !render_gl;
-                bwebgl.className = render_gl ? "button" : "button inactive";
-                
-                render_canvas = !render_gl;
-                bcanvas.className = render_canvas ? "button" : "button inactive";
-                
-                if( has_gl )
-                    webglRenderer.domElement.style.display = render_gl ? "block" : "none";
-                    
-                canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
-                
-            }
+			function log(text) {
+			
+				var e = document.getElementById("log");
+				e.innerHTML = text + "<br/>" + e.innerHTML;
+				
+			}
+			
+			function toggleCanvas() {
+			
+				render_canvas = !render_canvas;
+				bcanvas.className = render_canvas ? "button" : "button inactive";
+				
+				render_gl = !render_canvas;
+				bwebgl.className = render_gl ? "button" : "button inactive";
+				
+				if( has_gl )
+					webglRenderer.domElement.style.display = render_gl ? "block" : "none";
+				
+				canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
+				
+			}
+			
+			function toggleWebGL() {
+			
+				render_gl = !render_gl;
+				bwebgl.className = render_gl ? "button" : "button inactive";
+				
+				render_canvas = !render_gl;
+				bcanvas.className = render_canvas ? "button" : "button inactive";
+				
+				if( has_gl )
+					webglRenderer.domElement.style.display = render_gl ? "block" : "none";
+					
+				canvasRenderer.domElement.style.display = render_canvas ? "block" : "none";
+				
+			}
 		</script>
 
 	</body>

+ 270 - 0
src/io/Loader.js

@@ -0,0 +1,270 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Loader = function() {
+};
+
+THREE.Loader.prototype = {
+	
+	loadAsync: function( url, callback ) {
+		
+		var element = document.createElement( 'script' );
+		element.type = 'text/javascript';
+		element.onload = callback;
+		element.src = url;
+		document.getElementsByTagName( "head" )[ 0 ].appendChild( element );
+
+	},
+
+	loadWorker: function ( url, callback, urlbase ) {
+		
+		var s = (new Date).getTime(),
+			worker = new Worker( url );
+
+		worker.onmessage = function( event ) {
+		
+			THREE.Loader.prototype.createModel( event.data, callback, urlbase );
+			
+		};
+		
+		worker.postMessage( s );
+		
+	},
+
+	createModel: function ( data, callback, urlbase ) {
+		
+		var Model = function ( urlbase ) {
+			
+			var scope = this;
+
+			THREE.Geometry.call(this);
+			
+			init_materials();
+			init_vertices();
+			init_uvs();
+			init_faces();
+			
+			this.computeCentroids();
+			this.computeNormals();
+			
+			function init_vertices() {
+			
+				var i, l, d;
+				
+				for( i = 0, l = data.vertices.length; i < l; i++ ) {
+					
+					d = data.vertices[i];
+					v( d[0], d[1], d[2] );
+					
+				}
+			
+			}
+
+			function init_uvs() {
+			
+				var i, l, d;
+				
+				for( i = 0, l = data.uvs.length; i < l; i++ ) {
+					
+					d = data.uvs[i];
+					if ( d.length == 6 ) {
+						
+						uv( d[0], d[1], d[2], d[3], d[4], d[5] );
+						
+					} else if ( d.length == 8 ) {
+					
+						uv( d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7] );
+					
+					}
+						
+				}
+			
+			}
+
+			function init_faces() {
+			
+				var i, l, d;
+				
+				for( i = 0, l = data.faces.length; i < l; i++ ) {
+					
+					d = data.faces[i];
+					
+					if ( d.length == 4 ) {
+						
+						f3( d[0], d[1], d[2], d[3] );
+						
+					} else if ( d.length == 5 ) {
+					
+						f4( d[0], d[1], d[2], d[3], d[4] );
+					
+					} else if ( d.length == 7 ) {
+					
+						f3n( d[0], d[1], d[2], d[3], d[4], d[5], d[6] );
+					
+					} else if ( d.length == 9 ) {
+					
+						f4n( d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8] );
+					
+					}
+						
+				}
+			
+			}
+			
+			function v( x, y, z ) {
+				
+				scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) );
+				
+			}
+
+			function f3( a, b, c, mi ) {
+				
+				var material = scope.materials[ mi ];
+				scope.faces.push( new THREE.Face3( a, b, c, null, material ) );
+				
+			}
+
+			function f4( a, b, c, d, mi ) {
+				
+				var material = scope.materials[ mi ];
+				scope.faces.push( new THREE.Face4( a, b, c, d, null, material ) );
+				
+			}
+
+			function f3n( a, b, c, mi, n1, n2, n3 ) {
+				
+				var material = scope.materials[ mi ],
+					n1x = data.normals[n1][0],
+					n1y = data.normals[n1][1],
+					n1z = data.normals[n1][2],
+					n2x = data.normals[n2][0],
+					n2y = data.normals[n2][1],
+					n2z = data.normals[n2][2],
+					n3x = data.normals[n3][0],
+					n3y = data.normals[n3][1],
+					n3z = data.normals[n3][2];
+				
+				scope.faces.push( new THREE.Face3( a, b, c, 
+								  [new THREE.Vector3( n1x, n1y, n1z ), new THREE.Vector3( n2x, n2y, n2z ), new THREE.Vector3( n3x, n3y, n3z )], 
+								  material ) );
+				
+			}
+
+			function f4n( a, b, c, d, mi, n1, n2, n3, n4 ) {
+				
+				var material = scope.materials[ mi ],
+					n1x = data.normals[n1][0],
+					n1y = data.normals[n1][1],
+					n1z = data.normals[n1][2],
+					n2x = data.normals[n2][0],
+					n2y = data.normals[n2][1],
+					n2z = data.normals[n2][2],
+					n3x = data.normals[n3][0],
+					n3y = data.normals[n3][1],
+					n3z = data.normals[n3][2],
+					n4x = data.normals[n4][0],
+					n4y = data.normals[n4][1],
+					n4z = data.normals[n4][2];
+				
+				scope.faces.push( new THREE.Face4( a, b, c, d,
+								  [new THREE.Vector3( n1x, n1y, n1z ), new THREE.Vector3( n2x, n2y, n2z ), new THREE.Vector3( n3x, n3y, n3z ), new THREE.Vector3( n4x, n4y, n4z )], 
+								  material ) );
+				
+			}
+
+			function uv( u1, v1, u2, v2, u3, v3, u4, v4 ) {
+				
+				var uv = [];
+				uv.push( new THREE.UV( u1, v1 ) );
+				uv.push( new THREE.UV( u2, v2 ) );
+				uv.push( new THREE.UV( u3, v3 ) );
+				if ( u4 && v4 ) uv.push( new THREE.UV( u4, v4 ) );
+				scope.uvs.push( uv );
+				
+			}
+			
+			function init_materials() {
+				
+				scope.materials = [];
+				for( var i = 0; i < data.materials.length; ++i ) {
+					scope.materials[i] = [ create_material( data.materials[i], urlbase ) ];
+				}
+				
+			}
+		
+			function is_pow2( n ) {
+				
+				var l = Math.log(n) / Math.LN2;
+				return Math.floor(l) == l;
+				
+			}
+			
+			function nearest_pow2(n) {
+				
+				var l = Math.log(n) / Math.LN2;
+				return Math.pow( 2, Math.round(l) );
+				
+			}
+			
+			function create_material( m ) {
+				
+				var material, texture, image, color;
+				
+				if( m.map_diffuse && urlbase ) {
+					
+					texture = document.createElement( 'canvas' );
+					material = new THREE.MeshBitmapMaterial( texture );
+					
+					image = new Image();
+					image.onload = function () {
+						
+						if ( !is_pow2( this.width ) || !is_pow2( this.height ) ) {
+						
+							var w = nearest_pow2( this.width ),
+								h = nearest_pow2( this.height );
+							
+							material.bitmap.width = w;
+							material.bitmap.height = h;
+							material.bitmap.getContext("2d").drawImage( this, 0, 0, w, h );
+							
+						} else {
+							
+							material.bitmap = this;
+							
+						}
+						
+						material.loaded = 1;
+						
+					};
+					
+					image.src = urlbase + "/" + m.map_diffuse;
+					
+				} else if( m.col_diffuse ) {
+					
+					color = (m.col_diffuse[0]*255 << 16) + (m.col_diffuse[1]*255 << 8) + m.col_diffuse[2]*255;
+					material = new THREE.MeshColorFillMaterial( color, m.transparency );
+					
+				} else if( m.a_dbg_color ) {
+					
+					material = new THREE.MeshColorFillMaterial( m.a_dbg_color );
+					
+				} else {
+					
+					material = new THREE.MeshColorFillMaterial( 0xffeeeeee );
+					
+				}
+
+				return material;
+			}
+			
+		}
+
+		Model.prototype = new THREE.Geometry();
+		Model.prototype.constructor = Model;
+		
+		callback( new Model( urlbase ) );
+
+	}
+	
+};

+ 1 - 0
utils/Builder.py

@@ -20,6 +20,7 @@ files.append('core/Face4.js')
 files.append('core/UV.js')
 files.append('core/Geometry.js')
 files.append('cameras/Camera.js')
+files.append('io/Loader.js')
 files.append('lights/Light.js')
 files.append('lights/AmbientLight.js')
 files.append('lights/DirectionalLight.js')

+ 1 - 0
utils/BuilderCanvas.py

@@ -20,6 +20,7 @@ files.append('core/Face4.js')
 files.append('core/UV.js')
 files.append('core/Geometry.js')
 files.append('cameras/Camera.js')
+files.append('io/Loader.js')
 files.append('lights/Light.js')
 files.append('lights/AmbientLight.js')
 files.append('lights/DirectionalLight.js')

+ 1 - 0
utils/BuilderDOM.py

@@ -19,6 +19,7 @@ files.append('core/Face3.js')
 files.append('core/Face4.js')
 files.append('core/UV.js')
 files.append('cameras/Camera.js')
+files.append('io/Loader.js')
 files.append('objects/Object3D.js')
 files.append('objects/Particle.js')
 files.append('materials/ParticleDOMMaterial.js')

+ 1 - 0
utils/BuilderDebug.py

@@ -20,6 +20,7 @@ files.append('core/Face4.js')
 files.append('core/UV.js')
 files.append('core/Geometry.js')
 files.append('cameras/Camera.js')
+files.append('io/Loader.js')
 files.append('lights/Light.js')
 files.append('lights/AmbientLight.js')
 files.append('lights/DirectionalLight.js')

+ 1 - 0
utils/BuilderSVG.py

@@ -20,6 +20,7 @@ files.append('core/Face4.js')
 files.append('core/UV.js')
 files.append('core/Geometry.js')
 files.append('cameras/Camera.js')
+files.append('io/Loader.js')
 files.append('lights/Light.js')
 files.append('lights/AmbientLight.js')
 files.append('lights/DirectionalLight.js')

+ 1 - 0
utils/BuilderWebGL.py

@@ -20,6 +20,7 @@ files.append('core/Face4.js')
 files.append('core/UV.js')
 files.append('core/Geometry.js')
 files.append('cameras/Camera.js')
+files.append('io/Loader.js')
 files.append('lights/Light.js')
 files.append('lights/AmbientLight.js')
 files.append('lights/DirectionalLight.js')

+ 1 - 1
utils/REVISION

@@ -1 +1 @@
-27
+28

+ 723 - 0
utils/exporters/convert_obj_threejs_slim.py

@@ -0,0 +1,723 @@
+"""Convert Wavefront OBJ / MTL files into Three.js (slim models version, to be used with web worker loader)
+
+-------------------------
+How to use this converter
+-------------------------
+
+python convert_obj_threejs_slim.py -i filename.obj -o filename.js [-a center|top|bottom] [-s smooth|flat]
+
+Note: by default, model is centered (middle of bounding box goes to 0,0,0) 
+      and uses smooth shading (if there were vertex normals in the original 
+      model).
+ 
+--------------------------------------------------
+How to use generated JS file in your HTML document
+--------------------------------------------------
+
+    <script type="text/javascript" src="Three.js"></script>
+    
+    ...
+    
+    <script type="text/javascript">
+        ...
+        
+        var loader = new THREE.Loader();
+        loader.loadWorker( "Model.js", function( geometry ) { createScene( geometry) }, path_to_textures );
+        
+        function createScene( geometry ) {
+            
+            var normalizeUVsFlag = 1; // set to 1 if canvas render has missing materials
+            var mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(), normalizeUVsFlag );
+            
+        }
+        
+        ...
+    </script>
+    
+-------------------------------------
+Parsers based on formats descriptions
+-------------------------------------
+
+    http://en.wikipedia.org/wiki/Obj
+    http://en.wikipedia.org/wiki/Material_Template_Library
+    
+-------------------
+Current limitations
+-------------------
+
+    - for the moment, only diffuse color and texture are used 
+      (will need to extend shaders / renderers / materials in Three)
+     
+    - models can have more than 65,536 vertices,
+      but in most cases it will not work well with browsers,
+      which currently seem to have troubles with handling
+      large JS files
+       
+    - texture coordinates can be wrong in canvas renderer
+      (there is crude normalization, but it doesn't
+       work for all cases)
+       
+    - smoothing can be turned on/off only for the whole mesh
+
+---------------------------------------------- 
+How to get proper OBJ + MTL files with Blender
+----------------------------------------------
+
+    0. Remove default cube (press DEL and ENTER)
+    
+    1. Import / create model
+    
+    2. Select all meshes (Select -> Select All by Type -> Mesh)
+    
+    3. Export to OBJ (File -> Export -> Wavefront .obj) [*]
+        - enable following options in exporter
+            Material Groups
+            Rotate X90
+            Apply Modifiers
+            High Quality Normals
+            Copy Images
+            Selection Only
+            Objects as OBJ Objects
+            UVs
+            Normals
+            Materials
+            Edges
+            
+        - select empty folder
+        - give your exported file name with "obj" extension
+        - click on "Export OBJ" button
+        
+    4. Your model is now all files in this folder (OBJ, MTL, number of images)
+        - this converter assumes all files staying in the same folder,
+          (OBJ / MTL files use relative paths)
+          
+        - for WebGL, textures must be power of 2 sized
+
+    [*] If OBJ export fails (Blender 2.54 beta), patch your Blender installation 
+        following instructions here:
+            
+            http://www.blendernation.com/2010/09/12/blender-2-54-beta-released/
+            
+------
+Author
+------
+AlteredQualia http://alteredqualia.com
+
+"""
+
+import fileinput
+import operator
+import random
+import os.path
+import getopt
+import sys
+
+# #####################################################
+# Configuration
+# #####################################################
+ALIGN = "center" # center bottom top none
+
+SHADING = "smooth" # flat smooth
+
+# default colors for debugging (each material gets one distinct color): 
+# white, red, green, blue, yellow, cyan, magenta
+COLORS = [0xffeeeeee, 0xffee0000, 0xff00ee00, 0xff0000ee, 0xffeeee00, 0xff00eeee, 0xffee00ee]
+
+# #####################################################
+# Templates
+# #####################################################
+TEMPLATE_FILE = u"""\
+// Converted from: %(fname)s
+//  vertices: %(nvertex)d
+//  faces: %(nface)d 
+//  materials: %(nmaterial)d
+//
+//  Generated with OBJ -> Three.js converter
+//  http://github.com/alteredq/three.js/blob/master/utils/exporters/convert_obj_threejs_slim.py
+
+
+var model = {
+    'materials': [%(materials)s],
+
+    'normals': [%(normals)s],
+
+    'vertices': [%(vertices)s],
+
+    'uvs': [%(uvs)s],
+
+    'faces': [%(faces)s],
+    
+    'end': (new Date).getTime()
+    }
+    
+postMessage( model );
+"""
+
+TEMPLATE_VERTEX = "[%f,%f,%f]"
+
+TEMPLATE_UV3 = "[%f,%f,%f,%f,%f,%f]"
+TEMPLATE_UV4 = "[%f,%f,%f,%f,%f,%f,%f,%f]"
+
+TEMPLATE_FACE3  = "[%d,%d,%d,%d]"
+TEMPLATE_FACE4  = "[%d,%d,%d,%d,%d]"
+
+TEMPLATE_FACE3N  = "[%d,%d,%d,%d,%d,%d,%d]"
+TEMPLATE_FACE4N  = "[%d,%d,%d,%d,%d,%d,%d,%d,%d]"
+
+TEMPLATE_N = "[%f,%f,%f]"
+
+# #####################################################
+# Utils
+# #####################################################
+def file_exists(filename):
+    """Return true if file exists and is accessible for reading.
+    
+    Should be safer than just testing for existence due to links and 
+    permissions magic on Unix filesystems.
+    
+    @rtype: boolean
+    """
+    
+    try:
+        f = open(filename, 'r')
+        f.close()
+        return True
+    except IOError:
+        return False
+
+    
+def get_name(fname):
+    """Create model name based of filename ("path/fname.js" -> "fname").
+    """
+    
+    return os.path.basename(fname).split(".")[0]
+  
+def bbox(vertices):
+    """Compute bounding box of vertex array.
+    """
+    
+    if len(vertices)>0:
+        minx = maxx = vertices[0][0]
+        miny = maxy = vertices[0][1]
+        minz = maxz = vertices[0][2]
+        
+        for v in vertices[1:]:
+            if v[0]<minx:
+                minx = v[0]
+            elif v[0]>maxx:
+                maxx = v[0]
+            
+            if v[1]<miny:
+                miny = v[1]
+            elif v[1]>maxy:
+                maxy = v[1]
+
+            if v[2]<minz:
+                minz = v[2]
+            elif v[2]>maxz:
+                maxz = v[2]
+
+        return { 'x':[minx,maxx], 'y':[miny,maxy], 'z':[minz,maxz] }
+    
+    else:
+        return { 'x':[0,0], 'y':[0,0], 'z':[0,0] }
+
+def translate(vertices, t):
+    """Translate array of vertices by vector t.
+    """
+    
+    for i in xrange(len(vertices)):
+        vertices[i][0] += t[0]
+        vertices[i][1] += t[1]
+        vertices[i][2] += t[2]
+        
+def center(vertices):
+    """Center model (middle of bounding box).
+    """
+    
+    bb = bbox(vertices)
+    
+    cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
+    cy = bb['y'][0] + (bb['y'][1] - bb['y'][0])/2.0
+    cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
+    
+    translate(vertices, [-cx,-cy,-cz])
+
+def top(vertices):
+    """Align top of the model with the floor (Y-axis) and center it around X and Z.
+    """
+    
+    bb = bbox(vertices)
+    
+    cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
+    cy = bb['y'][1]
+    cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
+    
+    translate(vertices, [-cx,-cy,-cz])
+    
+def bottom(vertices):
+    """Align bottom of the model with the floor (Y-axis) and center it around X and Z.
+    """
+    
+    bb = bbox(vertices)
+    
+    cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
+    cy = bb['y'][0] 
+    cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
+    
+    translate(vertices, [-cx,cy,-cz])
+
+def normalize(v):
+    """Normalize 3d vector"""
+    
+    l = math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
+    v[0] /= l
+    v[1] /= l
+    v[2] /= l
+
+# #####################################################
+# MTL parser
+# #####################################################
+def parse_mtl(fname):
+    """Parse MTL file.
+    """
+    
+    materials = {}
+    
+    for line in fileinput.input(fname):
+        chunks = line.split()
+        if len(chunks) > 0:
+            
+            # Material start
+            # newmtl identifier
+            if chunks[0] == "newmtl" and len(chunks) == 2:
+                identifier = chunks[1]
+                if not identifier in materials:
+                    materials[identifier] = {}
+
+            # Diffuse color
+            # Kd 1.000 1.000 1.000
+            if chunks[0] == "Kd" and len(chunks) == 4:                
+                materials[identifier]["col_diffuse"] = [float(chunks[1]), float(chunks[2]), float(chunks[3])]
+
+            # Ambient color
+            # Ka 1.000 1.000 1.000
+            if chunks[0] == "Ka" and len(chunks) == 4:                
+                materials[identifier]["col_ambient"] = [float(chunks[1]), float(chunks[2]), float(chunks[3])]
+
+            # Specular color
+            # Ks 1.000 1.000 1.000
+            if chunks[0] == "Ks" and len(chunks) == 4:                
+                materials[identifier]["col_specular"] = [float(chunks[1]), float(chunks[2]), float(chunks[3])]
+
+            # Specular coefficient
+            # Ns 154.000
+            if chunks[0] == "Ns" and len(chunks) == 2:                
+                materials[identifier]["specular_coef"] = float(chunks[1])
+
+            # Transparency
+            # Tr 0.9 or d 0.9
+            if (chunks[0] == "Tr" or chunks[0] == "d") and len(chunks) == 2:                
+                materials[identifier]["transparency"] = float(chunks[1])
+
+            # Optical density
+            # Ni 1.0
+            if chunks[0] == "Ni" and len(chunks) == 2:                
+                materials[identifier]["optical_density"] = float(chunks[1])
+
+            # Diffuse texture
+            # map_Kd texture_diffuse.jpg
+            if chunks[0] == "map_Kd" and len(chunks) == 2:                
+                materials[identifier]["map_diffuse"] = chunks[1]
+
+            # Ambient texture
+            # map_Ka texture_ambient.jpg
+            if chunks[0] == "map_Ka" and len(chunks) == 2:
+                materials[identifier]["map_ambient"] = chunks[1]
+
+            # Specular texture
+            # map_Ks texture_specular.jpg
+            if chunks[0] == "map_Ks" and len(chunks) == 2:
+                materials[identifier]["map_specular"] = chunks[1]
+
+            # Alpha texture
+            # map_d texture_alpha.png
+            if chunks[0] == "map_d" and len(chunks) == 2:
+                materials[identifier]["map_alpha"] = chunks[1]
+
+            # Bump texture
+            # map_bump texture_bump.jpg or bump texture_bump.jpg
+            if (chunks[0] == "map_bump" or chunks[0] == "bump") and len(chunks) == 2:
+                materials[identifier]["map_bump"] = chunks[1]
+
+            # Illumination
+            # illum 2
+            #
+            # 0. Color on and Ambient off
+            # 1. Color on and Ambient on
+            # 2. Highlight on
+            # 3. Reflection on and Ray trace on
+            # 4. Transparency: Glass on, Reflection: Ray trace on
+            # 5. Reflection: Fresnel on and Ray trace on
+            # 6. Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
+            # 7. Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
+            # 8. Reflection on and Ray trace off
+            # 9. Transparency: Glass on, Reflection: Ray trace off
+            # 10. Casts shadows onto invisible surfaces
+            if chunks[0] == "illum" and len(chunks) == 2:
+                materials[identifier]["illumination"] = int(chunks[1])
+
+    return materials
+    
+# #####################################################
+# OBJ parser
+# #####################################################
+def parse_vertex(text):
+    """Parse text chunk specifying single vertex.
+    
+    Possible formats:
+        vertex index
+        vertex index / texture index
+        vertex index / texture index / normal index
+        vertex index / / normal index
+    """
+    
+    v = 0
+    t = 0
+    n = 0
+    
+    chunks = text.split("/")
+    
+    v = int(chunks[0])
+    if len(chunks) > 1:
+        if chunks[1]:
+            t = int(chunks[1])
+    if len(chunks) > 2:
+        if chunks[2]:
+            n = int(chunks[2])
+            
+    return { 'v':v, 't':t, 'n':n }
+    
+def parse_obj(fname):
+    """Parse OBJ file.
+    """
+    
+    vertices = []
+    normals = []
+    uvs = []
+    
+    faces = []
+    
+    materials = {}
+    mcounter = 0
+    mcurrent = 0
+    
+    mtllib = ""
+    
+    # current face state
+    group = 0
+    object = 0
+    smooth = 0
+    
+    for line in fileinput.input(fname):
+        chunks = line.split()
+        if len(chunks) > 0:
+            
+            # Vertices as (x,y,z) coordinates
+            # v 0.123 0.234 0.345
+            if chunks[0] == "v" and len(chunks) == 4:
+                x = float(chunks[1])
+                y = float(chunks[2])
+                z = float(chunks[3])
+                vertices.append([x,y,z])
+
+            # Normals in (x,y,z) form; normals might not be unit
+            # vn 0.707 0.000 0.707
+            if chunks[0] == "vn" and len(chunks) == 4:
+                x = float(chunks[1])
+                y = float(chunks[2])
+                z = float(chunks[3])
+                normals.append([x,y,z])
+
+            # Texture coordinates in (u,v[,w]) coordinates, w is optional
+            # vt 0.500 -1.352 [0.234]
+            if chunks[0] == "vt" and len(chunks) >= 3:
+                u = float(chunks[1])
+                v = float(chunks[2])
+                w = 0
+                if len(chunks)>3:
+                    w = float(chunks[3])
+                uvs.append([u,v,w])
+
+            # Face
+            if chunks[0] == "f" and len(chunks) >= 4:
+                vertex_index = []
+                uv_index = []
+                normal_index = []
+                
+                for v in chunks[1:]:
+                    vertex = parse_vertex(v)
+                    if vertex['v']:
+                        vertex_index.append(vertex['v'])
+                    if vertex['t']:
+                        uv_index.append(vertex['t'])
+                    if vertex['n']:
+                        normal_index.append(vertex['n'])
+                
+                faces.append({
+                    'vertex':vertex_index, 
+                    'uv':uv_index,
+                    'normal':normal_index,
+                    
+                    'material':mcurrent,
+                    'group':group, 
+                    'object':object, 
+                    'smooth':smooth,
+                    })
+    
+            # Group
+            if chunks[0] == "g" and len(chunks) == 2:
+                group = chunks[1]
+
+            # Object
+            if chunks[0] == "o" and len(chunks) == 2:
+                object = chunks[1]
+
+            # Materials definition
+            if chunks[0] == "mtllib" and len(chunks) == 2:
+                mtllib = chunks[1]
+                
+            # Material
+            if chunks[0] == "usemtl" and len(chunks) == 2:
+                material = chunks[1]
+                if not material in materials:
+                    mcurrent = mcounter
+                    materials[material] = mcounter
+                    mcounter += 1
+                else:
+                    mcurrent = materials[material]
+
+            # Smooth shading
+            if chunks[0] == "s" and len(chunks) == 2:
+                smooth = chunks[1]
+
+    return faces, vertices, uvs, normals, materials, mtllib
+    
+# #####################################################
+# Generator
+# #####################################################
+def generate_vertex(v):
+    return TEMPLATE_VERTEX % (v[0], v[1], v[2])
+    
+def generate_uv(f, uvs):
+    ui = f['uv']
+    if len(ui) == 3:
+        return TEMPLATE_UV3 % (uvs[ui[0]-1][0], 1.0 - uvs[ui[0]-1][1],
+                               uvs[ui[1]-1][0], 1.0 - uvs[ui[1]-1][1],
+                               uvs[ui[2]-1][0], 1.0 - uvs[ui[2]-1][1])
+    elif len(ui) == 4:
+        return TEMPLATE_UV4 % (uvs[ui[0]-1][0], 1.0 - uvs[ui[0]-1][1],
+                               uvs[ui[1]-1][0], 1.0 - uvs[ui[1]-1][1],
+                               uvs[ui[2]-1][0], 1.0 - uvs[ui[2]-1][1],
+                               uvs[ui[3]-1][0], 1.0 - uvs[ui[3]-1][1])
+    return ""
+    
+def generate_face(f):
+    vi = f['vertex']
+    if f["normal"] and SHADING == "smooth":
+        ni = f['normal']
+        if len(vi) == 3:
+            return TEMPLATE_FACE3N % (vi[0]-1, vi[1]-1, vi[2]-1, f['material'], ni[0]-1, ni[1]-1, ni[2]-1)
+        elif len(vi) == 4:
+            return TEMPLATE_FACE4N % (vi[0]-1, vi[1]-1, vi[2]-1, vi[3]-1, f['material'],  ni[0]-1, ni[1]-1, ni[2]-1, ni[3]-1)
+    else:
+        if len(vi) == 3:
+            return TEMPLATE_FACE3 % (vi[0]-1, vi[1]-1, vi[2]-1, f['material'])
+        elif len(vi) == 4:
+            return TEMPLATE_FACE4 % (vi[0]-1, vi[1]-1, vi[2]-1, vi[3]-1, f['material'])
+    return ""
+
+def generate_normal(n):
+    return TEMPLATE_N % (n[0], n[1], n[2])
+
+def generate_color(i):
+    """Generate hex color corresponding to integer.
+    
+    Colors should have well defined ordering.
+    First N colors are hardcoded, then colors are random 
+    (must seed random number  generator with deterministic value 
+    before getting colors).
+    """
+    
+    if i < len(COLORS):
+        return "0x%x" % COLORS[i]
+    else:
+        return "0x%x" % (int(0xffffff * random.random()) + 0xff000000)
+        
+def value2string(v):
+    if type(v)==str and v[0] != "0":
+        return '"%s"' % v
+    return str(v)
+    
+def generate_materials(mtl, materials):
+    """Generate JS array of materials objects
+    
+    JS material objects are basically prettified one-to-one 
+    mappings of MTL properties in JSON format.
+    """
+    
+    mtl_array = []
+    for m in mtl:
+        index = materials[m]
+        
+        # add debug information
+        #  materials should be sorted according to how
+        #  they appeared in OBJ file (for the first time)
+        #  this index is identifier used in face definitions
+        mtl[m]['a_dbg_name'] = m
+        mtl[m]['a_dbg_index'] = index
+        mtl[m]['a_dbg_color'] = generate_color(index)
+        
+        mtl_raw = ",\n".join(['\t"%s" : %s' % (n, value2string(v)) for n,v in sorted(mtl[m].items())])
+        mtl_string = "\t{\n%s\n\t}" % mtl_raw
+        mtl_array.append([index, mtl_string])
+        
+    return ",\n\n".join([m for i,m in sorted(mtl_array)])
+
+def generate_mtl(materials):
+    """Generate dummy materials (if there is no MTL file).
+    """
+    
+    mtl = {}
+    for m in materials:
+        index = materials[m]
+        mtl[m] = {
+            'a_dbg_name': m,
+            'a_dbg_index': index,
+            'a_dbg_color': generate_color(index)
+        }
+    return mtl
+    
+# #####################################################
+# API
+# #####################################################
+def convert(infile, outfile):
+    """Convert infile.obj to outfile.js
+    
+    Here is where everything happens. If you need to automate conversions,
+    just import this file as Python module and call this method.
+    """
+    
+    if not file_exists(infile):
+        print "Couldn't find [%s]" % infile
+        return
+        
+    faces, vertices, uvs, normals, materials, mtllib = parse_obj(infile)
+    
+    if ALIGN == "center":
+        center(vertices)
+    elif ALIGN == "bottom":
+        bottom(vertices)
+    elif ALIGN == "top":
+        top(vertices)
+    
+    random.seed(42) # to get well defined color order for materials
+    
+    uv_string = ""
+    if len(uvs)>0:
+        uv_string = ",".join([generate_uv(f, uvs) for f in faces])
+            
+
+    # default materials with debug colors for when
+    # there is no specified MTL / MTL loading failed,
+    # or if there were no materials / null materials
+    if not materials:
+        materials = { 'default':0 }
+    mtl = generate_mtl(materials)
+    
+    if mtllib:
+        # create full pathname for MTL (included from OBJ)
+        path = os.path.dirname(infile)
+        fname = os.path.join(path, mtllib)
+        
+        if file_exists(fname):
+            # override default materials with real ones from MTL
+            # (where they exist, otherwise keep defaults)
+            mtl.update(parse_mtl(fname))
+        
+        else:
+            print "Couldn't find [%s]" % fname
+    
+    normals_string = ""
+    if SHADING == "smooth":
+        normals_string = ",".join(generate_normal(n) for n in normals)
+        
+    text = TEMPLATE_FILE % {
+    "name"      : get_name(outfile),
+    "vertices"  : ",".join([generate_vertex(v) for v in vertices]),
+    "faces"     : ",".join([generate_face(f)   for f in faces]),
+    "uvs"       : uv_string,
+    "normals"   : normals_string,
+    
+    "materials" : generate_materials(mtl, materials),
+    
+    "fname"     : infile,
+    "nvertex"   : len(vertices),
+    "nface"     : len(faces),
+    "nmaterial" : len(materials)
+    }
+    
+    out = open(outfile, "w")
+    out.write(text)
+    out.close()
+    
+    print "%d vertices, %d faces, %d materials" % (len(vertices), len(faces), len(materials))
+        
+# #############################################################################
+# Helpers
+# #############################################################################
+def usage():
+    print "Usage: %s -i filename.obj -o filename.js [-a center|top|bottom] [-s flat|smooth]" % os.path.basename(sys.argv[0])
+        
+# #####################################################
+# Main
+# #####################################################
+if __name__ == "__main__":
+    
+    # get parameters from the command line
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], "hi:o:a:s:", ["help", "input=", "output=", "align=", "shading="])
+    
+    except getopt.GetoptError:
+        usage()
+        sys.exit(2)
+        
+    infile = outfile = ""
+    
+    for o, a in opts:
+        if o in ("-h", "--help"):
+            usage()
+            sys.exit()
+        
+        elif o in ("-i", "--input"):
+            infile = a
+
+        elif o in ("-o", "--output"):
+            outfile = a
+
+        elif o in ("-a", "--align"):
+            if a in ("top", "bottom", "center"):
+                ALIGN = a
+
+        elif o in ("-s", "--shading"):
+            if a in ("flat", "smooth"):
+                SHADING = a
+
+    if infile == "" or outfile == "":
+        usage()
+        sys.exit(2)
+    
+    print "Converting [%s] into [%s] ..." % (infile, outfile)
+    convert(infile, outfile)
+    
+    

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