Browse Source

Added experimental binary mesh loader. Updated OBJ converter and examples accordingly.

If you want to use binary format, convert OBJ models using "convert_obj_threejs_slim.py" with "-t binary" option.

This will now create two files:
    JS part with materials
    BIN part with binary buffers

Binary models are loaded like this:

loader.loadBinary( 'obj/lucy/Lucy250k_bin.js', function( geometry ) { createScene( geometry, s ) }, "obj/lucy" );

(difference to ascii format is that url root must be always specified so loader can find binary buffers file, like it already was for textures)

Good news:

- raw binary files are quite smaller than raw ascii files (about half size)
- loading times went down about 20-25%
- now also Firefox can handle 250k triangle mesh (it just bugs about long running script), with ascii format it threw "array initialiser too large" exception

Mixed news:

- gzipped binary files are only about 25% smaller than gzipped ascii files so actual benefits are smaller,
  also it's more likely server will have JS gzipping on by default, while you need to set it up for
  other formats (could be hacked around by naming binary files with JS extension instead of BIN?)

Bad news:

- browser blocking is back :( Not that it ever went completely away (for large models object creation is more costly than loading and it is blocking), but it was slightly better with ascii loader (where data is loaded as worker's JS body).

    - this comes from having to use Ajax to load binary data
    - loading binary data by Ajax from within worker didn't really help, it was in fact slightly slower and browser did freeze :(
    - can't easily embed binary data in JSON (would need to encode it which would make it bigger, defying the purpose)

- Loader got fatter

Also in this commit: renamed Loader.loadAsync() => Loader.loadAsciiOld() and Loader.loadWorker() = Loader.loadAscii() so that names correspond to model formats instead of underlying implementation.

loadAsciiOld - JS exported by Blender and old OBJ converter
loadAscii    - JSON created by slim OBJ converter (-t ascii)
loadBinary   - JSON + BIN created by slim OBJ converter (-t binary)

TODO:

 - look into UV coordinates, should be indexed the same way as vertices, normals and faces are (instead of unrolled per each face)
 - look into HTML5 File API, binary blobs could help
alteredq 14 years ago
parent
commit
0cb79310e5

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


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


+ 6 - 5
examples/geometry_large_mesh.html

@@ -34,7 +34,7 @@
 			<p>Lucy model from <a href="http://graphics.stanford.edu/data/3Dscanrep/">Stanford 3d scanning repository</a> 
 			<p>Lucy model from <a href="http://graphics.stanford.edu/data/3Dscanrep/">Stanford 3d scanning repository</a> 
 			(decimated with <a href="http://meshlab.sourceforge.net/">Meshlab</a>).
 			(decimated with <a href="http://meshlab.sourceforge.net/">Meshlab</a>).
 			
 			
-			<p>Please be patient while the mesh is loading. It may take a while, it's 3.5MB file.
+			<p>Please be patient while the mesh is loading. It may take a while, it's 2MB file.
 			
 			
 			<br/>
 			<br/>
 			<p>Best viewed in Chrome 8/9 or Firefox 4 using WebGL renderer.
 			<p>Best viewed in Chrome 8/9 or Firefox 4 using WebGL renderer.
@@ -45,7 +45,7 @@
 		<pre id="log"></pre>
 		<pre id="log"></pre>
 
 
 		<script type="text/javascript" src="../build/Three.js"></script>
 		<script type="text/javascript" src="../build/Three.js"></script>
-		
+
 		<script type="text/javascript" src="geometry/primitives/Sphere.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" src="js/Stats.js"></script>
@@ -177,9 +177,10 @@
 				var s = (new Date).getTime();
 				var s = (new Date).getTime();
 				
 				
 				var loader = new THREE.Loader();
 				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 ) } );
-				
+				//loader.loadAsciiOld( "obj/lucy/Lucy100k.js", function() { createScene( new Lucy100k(), s ) } );
+				//loader.loadAscii( 'obj/lucy/Lucy100k_slim.js', function( geometry ) { createScene( geometry, s ) } );
+				loader.loadBinary( 'obj/lucy/Lucy100k_bin.js', function( geometry ) { createScene( geometry, s ) }, "obj/lucy" );
+
 			}
 			}
 			
 			
 			function createScene( geometry, start ) {
 			function createScene( geometry, start ) {

+ 18 - 3
examples/lights_pointlights_gl.html

@@ -109,7 +109,8 @@
 				scene = new THREE.Scene();
 				scene = new THREE.Scene();
 
 
 				var loader = new THREE.Loader();
 				var loader = new THREE.Loader();
-				loader.loadAsync( "obj/walt/WaltHead.js", function() {
+                
+				loader.loadAsciiOld( "obj/walt/WaltHead.js", function() {
 				
 				
 					//object = new THREE.Mesh( new WaltHead(), [ new THREE.MeshColorStrokeMaterial( 0xffffff ) ] );
 					//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.MeshColorFillMaterial( 0xffffff ) );
@@ -119,9 +120,9 @@
 					scene.addObject( object );
 					scene.addObject( object );
 					
 					
 				});
 				});
-				
+                
 				/*
 				/*
-				loader.loadWorker( "obj/walt/WaltHead_slim.js", function( geometry ) {
+				loader.loadAscii( "obj/walt/WaltHead_slim.js", function( geometry ) {
 				
 				
 					object = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial( 0x555555, 0x555555, 0xffffff, 50, 1.0  )  );
 					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.scale.x = object.scale.y = object.scale.z = 0.80;
@@ -130,7 +131,18 @@
 					
 					
 				});
 				});
 				*/
 				*/
+
+                /*
+				loader.loadBinary( "obj/walt/WaltHead_bin.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 );
+					
+				}, "obj/walt");
+                */
+                
 				/*
 				/*
 				var directionalLight = new THREE.DirectionalLight( 0x111111, 0.9 );
 				var directionalLight = new THREE.DirectionalLight( 0x111111, 0.9 );
 				directionalLight.position.x = 1;
 				directionalLight.position.x = 1;
@@ -202,6 +214,9 @@
 				//stats.update();
 				//stats.update();
 
 
 			}
 			}
+            
+            function log() {
+            }
 
 
 		</script>
 		</script>
 	</body>
 	</body>

+ 3 - 2
examples/lights_test.html

@@ -214,8 +214,9 @@
 
 
 
 
 				var loader = new THREE.Loader();
 				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 ) } );
+				//loader.loadAsciiOld( "obj/torus/Torus.js", function() { createScene( new Torus() ) } );
+				//loader.loadAscii( "obj/torus/Torus_slim.js", function( geometry ) { createScene( geometry ) } );
+                loader.loadBinary( "obj/torus/Torus_bin.js", function( geometry ) { createScene( geometry ) }, "obj/torus" );
 
 
 			}
 			}
 
 

+ 4 - 3
examples/materials_test.html

@@ -37,7 +37,7 @@
 			<br/>
 			<br/>
 			<p>Best viewed in Chrome 8/9 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.
 			<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>
 		
 		
 		<div id="log"></div>
 		<div id="log"></div>
@@ -215,8 +215,9 @@
 				bwebgl.addEventListener("click", toggleWebGL, false);
 				bwebgl.addEventListener("click", toggleWebGL, false);
 
 
 				var loader = new THREE.Loader();
 				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" );
+				loader.loadAsciiOld( "obj/female02/female02.js", function() { createScene( new Female02( "obj/female02" ) ) } );
+				//loader.loadAscii( "obj/female02/Female02_slim.js", function( geometry ) { createScene( geometry) }, "obj/female02" );
+                //loader.loadBinary( "obj/female02/Female02_bin.js", function( geometry ) { createScene( geometry) }, "obj/female02" );
 
 
 			}
 			}
 
 

BIN
examples/obj/female02/Female02_bin.bin


+ 100 - 0
examples/obj/female02/Female02_bin.js

@@ -0,0 +1,100 @@
+// Converted from: ../../examples/obj/female02/female02.obj
+//  vertices: 3274
+//  faces: 6233 
+//  materials: 6
+//
+//  Generated with OBJ -> Three.js converter
+//  http://github.com/alteredq/three.js/blob/master/utils/exporters/convert_obj_threejs_slim.py
+
+
+var model = {
+    'materials': [	{
+	"a_dbg_color" : 0xffeeeeee,
+	"a_dbg_index" : 0,
+	"a_dbg_name" : "_03_-_Default1noCulli__03_-_Default1noCulli",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.64000000000000001, 0.64000000000000001, 0.64000000000000001],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "_03_-_Default1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	},
+
+	{
+	"a_dbg_color" : 0xffee0000,
+	"a_dbg_index" : 1,
+	"a_dbg_name" : "_02_-_Default1noCulli__02_-_Default1noCulli",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.64000000000000001, 0.64000000000000001, 0.64000000000000001],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "_02_-_Default1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	},
+
+	{
+	"a_dbg_color" : 0xff00ee00,
+	"a_dbg_index" : 2,
+	"a_dbg_name" : "FrontColorNoCullingID__02_-_Default1noCulli",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.80000000000000004, 0.80000000000000004, 0.80000000000000004],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "_02_-_Default1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	},
+
+	{
+	"a_dbg_color" : 0xff0000ee,
+	"a_dbg_index" : 3,
+	"a_dbg_name" : "FrontColorNoCullingID__03_-_Default1noCulli",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.80000000000000004, 0.80000000000000004, 0.80000000000000004],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "_03_-_Default1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	},
+
+	{
+	"a_dbg_color" : 0xffeeee00,
+	"a_dbg_index" : 4,
+	"a_dbg_name" : "_01_-_Default1noCulli__01_-_Default1noCulli",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.64000000000000001, 0.64000000000000001, 0.64000000000000001],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "_01_-_Default1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	},
+
+	{
+	"a_dbg_color" : 0xff00eeee,
+	"a_dbg_index" : 5,
+	"a_dbg_name" : "FrontColorNoCullingID__01_-_Default1noCulli",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.80000000000000004, 0.80000000000000004, 0.80000000000000004],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "_01_-_Default1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	}],
+
+    'buffers': 'Female02_bin.bin',
+
+    'end': (new Date).getTime()
+    }
+    
+postMessage( model );

BIN
examples/obj/lucy/Lucy100k_bin.bin


+ 22 - 0
examples/obj/lucy/Lucy100k_bin.js

@@ -0,0 +1,22 @@
+// Converted from: ../../examples/obj/lucy/lucy100k.obj
+//  vertices: 50002
+//  faces: 100000 
+//  materials: 0
+//
+//  Generated with OBJ -> Three.js converter
+//  http://github.com/alteredq/three.js/blob/master/utils/exporters/convert_obj_threejs_slim.py
+
+
+var model = {
+    'materials': [	{
+	"a_dbg_color" : 0xffeeeeee,
+	"a_dbg_index" : 0,
+	"a_dbg_name" : "default"
+	}],
+
+    'buffers': 'Lucy100k_bin.bin',
+
+    'end': (new Date).getTime()
+    }
+    
+postMessage( model );

+ 1 - 1
examples/obj/lucy/Lucy100k_slim.js

@@ -1,7 +1,7 @@
 // Converted from: ../../examples/obj/lucy/lucy100k.obj
 // Converted from: ../../examples/obj/lucy/lucy100k.obj
 //  vertices: 50002
 //  vertices: 50002
 //  faces: 100000 
 //  faces: 100000 
-//  materials: 1
+//  materials: 0
 //
 //
 //  Generated with OBJ -> Three.js converter
 //  Generated with OBJ -> Three.js converter
 //  http://github.com/alteredq/three.js/blob/master/utils/exporters/convert_obj_threejs_slim.py
 //  http://github.com/alteredq/three.js/blob/master/utils/exporters/convert_obj_threejs_slim.py

BIN
examples/obj/male02/Male02_bin.bin


+ 86 - 0
examples/obj/male02/Male02_bin.js

@@ -0,0 +1,86 @@
+// Converted from: ../../examples/obj/male02/male02.obj
+//  vertices: 2746
+//  faces: 5004 
+//  materials: 5
+//
+//  Generated with OBJ -> Three.js converter
+//  http://github.com/alteredq/three.js/blob/master/utils/exporters/convert_obj_threejs_slim.py
+
+
+var model = {
+    'materials': [	{
+	"a_dbg_color" : 0xffeeeeee,
+	"a_dbg_index" : 0,
+	"a_dbg_name" : "male-02-1noCullingID_male-02-1noCulling.JP",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.64000000000000001, 0.64000000000000001, 0.64000000000000001],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "male-02-1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	},
+
+	{
+	"a_dbg_color" : 0xffee0000,
+	"a_dbg_index" : 1,
+	"a_dbg_name" : "orig_02_-_Defaul1noCu_orig_02_-_Defaul1noCu",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.64000000000000001, 0.64000000000000001, 0.64000000000000001],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "orig_02_-_Defaul1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	},
+
+	{
+	"a_dbg_color" : 0xff00ee00,
+	"a_dbg_index" : 2,
+	"a_dbg_name" : "FrontColorNoCullingID_orig_02_-_Defaul1noCu",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.80000000000000004, 0.80000000000000004, 0.80000000000000004],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "orig_02_-_Defaul1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	},
+
+	{
+	"a_dbg_color" : 0xff0000ee,
+	"a_dbg_index" : 3,
+	"a_dbg_name" : "_01_-_Default1noCulli__01_-_Default1noCulli",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.64000000000000001, 0.64000000000000001, 0.64000000000000001],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "_01_-_Default1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	},
+
+	{
+	"a_dbg_color" : 0xffeeee00,
+	"a_dbg_index" : 4,
+	"a_dbg_name" : "FrontColorNoCullingID_male-02-1noCulling.JP",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.80000000000000004, 0.80000000000000004, 0.80000000000000004],
+	"col_specular" : [0.16500000000000001, 0.16500000000000001, 0.16500000000000001],
+	"illumination" : 2,
+	"map_diffuse" : "male-02-1noCulling.JPG",
+	"optical_density" : 1.0,
+	"specular_coef" : 154.901961,
+	"transparency" : 1.0
+	}],
+
+    'buffers': 'Male02_bin.bin',
+
+    'end': (new Date).getTime()
+    }
+    
+postMessage( model );

BIN
examples/obj/torus/Torus_bin.bin


+ 22 - 0
examples/obj/torus/Torus_bin.js

@@ -0,0 +1,22 @@
+// Converted from: ../../examples/obj/torus/Torus.obj
+//  vertices: 576
+//  faces: 576 
+//  materials: 1
+//
+//  Generated with OBJ -> Three.js converter
+//  http://github.com/alteredq/three.js/blob/master/utils/exporters/convert_obj_threejs_slim.py
+
+
+var model = {
+    'materials': [	{
+	"a_dbg_color" : 0xffeeeeee,
+	"a_dbg_index" : 0,
+	"a_dbg_name" : "(null)"
+	}],
+
+    'buffers': 'Torus_bin.bin',
+
+    'end': (new Date).getTime()
+    }
+    
+postMessage( model );

BIN
examples/obj/walt/WaltHead_bin.bin


+ 29 - 0
examples/obj/walt/WaltHead_bin.js

@@ -0,0 +1,29 @@
+// Converted from: ../../examples/obj/walt/WaltHead.obj
+//  vertices: 8146
+//  faces: 16160 
+//  materials: 1
+//
+//  Generated with OBJ -> Three.js converter
+//  http://github.com/alteredq/three.js/blob/master/utils/exporters/convert_obj_threejs_slim.py
+
+
+var model = {
+    'materials': [	{
+	"a_dbg_color" : 0xffeeeeee,
+	"a_dbg_index" : 0,
+	"a_dbg_name" : "lambert2SG.001",
+	"col_ambient" : [0.0, 0.0, 0.0],
+	"col_diffuse" : [0.64000000000000001, 0.64000000000000001, 0.64000000000000001],
+	"col_specular" : [0.25, 0.25, 0.25],
+	"illumination" : 2,
+	"optical_density" : 1.0,
+	"specular_coef" : 92.156863,
+	"transparency" : 1.0
+	}],
+
+    'buffers': 'WaltHead_bin.bin',
+
+    'end': (new Date).getTime()
+    }
+    
+postMessage( model );

+ 8 - 4
examples/obj_convert_test.html

@@ -241,11 +241,14 @@
 				bwebgl.addEventListener("click", toggleWebGL, false);
 				bwebgl.addEventListener("click", toggleWebGL, false);
 				
 				
 				var loader = new THREE.Loader();
 				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.loadAsciiOld( "obj/male02/male02.js",     function() { createScene( new Male02( "obj/male02" ),      90, 50, FLOOR, 105 ) } );
+				loader.loadAsciiOld( "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" );
+				//loader.loadAscii( "obj/male02/Male02_slim.js",     function( geometry ) { createScene( geometry,  90, 50, FLOOR, 105 ) }, "obj/male02" );
+				//loader.loadAscii( "obj/female02/Female02_slim.js", function( geometry ) { createScene( geometry, -80, 50, FLOOR, 0 ) },   "obj/female02" );
+                
+                //loader.loadBinary( "obj/male02/Male02_bin.js",     function( geometry ) { createScene( geometry,  90, 50, FLOOR, 105 ) }, "obj/male02" );
+				//loader.loadBinary( "obj/female02/Female02_bin.js", function( geometry ) { createScene( geometry, -80, 50, FLOOR, 0 ) },   "obj/female02" );
 
 
 			}
 			}
 
 
@@ -261,6 +264,7 @@
 				scene.addObject(zmesh);
 				scene.addObject(zmesh);
 				
 				
 				createMaterialsPalette( geometry.materials, 100, b );
 				createMaterialsPalette( geometry.materials, 100, b );
+                
 			}
 			}
 
 
 			function createMaterialsPalette( materials, size, bottom ) {
 			function createMaterialsPalette( materials, size, bottom ) {

+ 3 - 2
examples/shader_test.html

@@ -241,8 +241,9 @@
 				bwebgl.addEventListener("click", toggleWebGL, false);
 				bwebgl.addEventListener("click", toggleWebGL, false);
 
 
 				var loader = new THREE.Loader();
 				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 ) } );
+				loader.loadAsciiOld( "obj/torus/Torus.js", function() { createScene( new Torus() ) } );
+				//loader.loadAscii( "obj/torus/Torus_slim.js", function( geometry ) { createScene( geometry ) } );
+                //loader.loadBinary( "obj/torus/Torus_bin.js", function( geometry ) { createScene( geometry ) }, "obj/torus" );
 
 
 			}
 			}
 
 

+ 540 - 3
src/io/Loader.js

@@ -7,7 +7,9 @@ THREE.Loader = function() {
 
 
 THREE.Loader.prototype = {
 THREE.Loader.prototype = {
 	
 	
-	loadAsync: function( url, callback ) {
+	// Load models generated by Blender exporter and original OBJ converter (converter_obj_three.py)
+	
+	loadAsciiOld: function( url, callback ) {
 		
 		
 		var element = document.createElement( 'script' );
 		var element = document.createElement( 'script' );
 		element.type = 'text/javascript';
 		element.type = 'text/javascript';
@@ -17,7 +19,10 @@ THREE.Loader.prototype = {
 
 
 	},
 	},
 
 
-	loadWorker: function ( url, callback, urlbase ) {
+	// Load models generated by slim OBJ converter with ASCII option (converter_obj_three_slim.py -t ascii)
+	//  - urlbase parameter is optional (it only applies to models with textures)
+	
+	loadAscii: function ( url, callback, urlbase ) {
 		
 		
 		var s = (new Date).getTime(),
 		var s = (new Date).getTime(),
 			worker = new Worker( url );
 			worker = new Worker( url );
@@ -31,7 +36,539 @@ THREE.Loader.prototype = {
 		worker.postMessage( s );
 		worker.postMessage( s );
 		
 		
 	},
 	},
+	
+	// Load models generated by slim OBJ converter with BINARY option (converter_obj_three_slim.py -t binary)
+	//  - urlbase parameter is mandatory (it applies to all models, it tells where to find the file with binary buffers)
+	//  - binary models consist of two files: JS and BIN
+	
+	loadBinary: function( url, callback, urlbase ) {
+		
+		// #1 load JS part via web worker
+		
+		//  This isn't really necessary, JS part is tiny,
+		//  could be done by more ordinary means.
+		
+		var s = (new Date).getTime(),
+			worker = new Worker( url );
+
+		worker.onmessage = function( event ) {
+			
+			var materials = event.data.materials,
+				buffers = event.data.buffers;
+			
+			// #2 load BIN part via Ajax
+			
+			//  For some reason it is faster doing loading from here than from within the worker.
+			//  Maybe passing of ginormous string as message between threads is costly? 
+			//  Also, worker loading huge data by Ajax still freezes browser. Go figure, 
+			//  worker with baked ascii JSON data keeps browser more responsive.
+			
+			THREE.Loader.prototype.loadAjaxBuffers( buffers, materials, callback, urlbase );
+			
+		};
+		
+		worker.onerror = function (event) {
+			
+			alert( "worker.onerror: " + event.message + "\n" + event.data );
+			event.preventDefault();
+			
+		};
+		
+		worker.postMessage( s );
+		
+	},
+
+	// Binary AJAX parser based on Magi binary loader
+	// https://github.com/kig/magi
+	
+	// Should look more into HTML5 File API
+	// See also other suggestions by Gregg Tavares
+	// https://groups.google.com/group/o3d-discuss/browse_thread/thread/a8967bc9ce1e0978
+
+	loadAjaxBuffers: function( buffers, materials, callback, urlbase ) {
+	
+
+		var xhr = new XMLHttpRequest(),
+			url = urlbase + "/" + buffers;
+	
+		xhr.onreadystatechange = function() {
+	  
+			if ( xhr.readyState == 4 ) {
+				
+				if ( xhr.status == 200 || xhr.status == 0 ) {
+		  
+					THREE.Loader.prototype.createBinModel( xhr.responseText, callback, urlbase, materials );
+					
+				} else {
+		  
+					alert( "Couldn't load [" + url + "] [" + xhr.status + "]" );
+					
+				}
+			}
+		}
+		
+		xhr.open("GET", url, true);
+		xhr.overrideMimeType("text/plain; charset=x-user-defined");
+		xhr.setRequestHeader("Content-Type", "text/plain");
+		xhr.send(null);
+		
+	},
+
+	createBinModel: function ( data, callback, urlbase, materials ) {
+		
+		var Model = function ( urlbase ) {
+			
+			//var s = (new Date).getTime();
+			
+			var scope = this,
+				currentOffset = 0, 
+				md,
+				normals = [];
+
+			
+			THREE.Geometry.call(this);
+			
+			init_materials();
+			
+			md = parseMetaData( data, currentOffset );
+			currentOffset += md.header_bytes;
+			
+			currentOffset += init_vertices( currentOffset );
+			currentOffset += init_normals( currentOffset );
+			currentOffset += init_triangles_flat( currentOffset );
+			currentOffset += init_triangles_smooth( currentOffset );
+			currentOffset += init_quads_flat( currentOffset );
+			currentOffset += init_quads_smooth( currentOffset );
+			currentOffset += init_uvs_tri( currentOffset );
+			
+			init_uvs_quad( data, currentOffset, md.nuvtri );
+			
+			this.computeCentroids();
+			this.computeNormals();
+			
+			//var e = (new Date).getTime();
+			
+			//log( "binary data parse time: " + (e-s) + " ms" );
+			
+			function parseMetaData( data, offset ) {
+				
+				var metaData = {
+					'signature'               :parseString( data, offset, 8 ),
+					
+					'header_bytes'            :parseUChar8( data, offset + 8 ),
+					'vertex_coordinate_bytes' :parseUChar8( data, offset + 9 ),
+					'vertex_index_bytes'      :parseUChar8( data, offset + 10 ),
+					'normal_index_bytes'      :parseUChar8( data, offset + 11 ),
+					'material_index_bytes'    :parseUChar8( data, offset + 12 ),
+					'normal_coordinate_bytes' :parseUChar8( data, offset + 13 ),
+					'uv_coordinate_bytes'     :parseUChar8( data, offset + 14 ),
+				
+					'nvertices'    :parseUInt32( data, offset + 15 ),
+					'ntri_flat'    :parseUInt32( data, offset + 15 + 4*1 ),
+					'ntri_smooth'  :parseUInt32( data, offset + 15 + 4*2 ),
+					'nquad_flat'   :parseUInt32( data, offset + 15 + 4*3 ),
+					'nquad_smooth' :parseUInt32( data, offset + 15 + 4*4 ),
+					'nnormals'     :parseUInt32( data, offset + 15 + 4*5 ),
+					'nuvtri'       :parseUInt32( data, offset + 15 + 4*6 ),
+					'nuvquad'      :parseUInt32( data, offset + 15 + 4*7 ),
+				};
+
+				/*
+				log( "signature: " + metaData.signature );
+				
+				log( "header_bytes: " + metaData.header_bytes );
+				log( "vertex_coordinate_bytes: " + metaData.vertex_coordinate_bytes );
+				log( "vertex_index_bytes: " + metaData.vertex_index_bytes );
+				log( "normal_index_bytes: " + metaData.normal_index_bytes );
+				log( "material_index_bytes: " + metaData.material_index_bytes );
+				log( "normal_coordinate_bytes: " + metaData.normal_coordinate_bytes );
+				log( "uv_coordinate_bytes: " + metaData.uv_coordinate_bytes );
+				
+				log( "nvertices: " + metaData.nvertices );
+				log( "ntri_flat: " + metaData.ntri_flat );
+				log( "ntri_smooth: " + metaData.ntri_smooth );
+				log( "nquad_flat: " + metaData.nquad_flat );
+				log( "nquad_smooth: " + metaData.nquad_smooth );
+				log( "nnormals: " + metaData.nnormals );
+				log( "nuvtri: " + metaData.nuvtri );
+				log( "nuvquad: " + metaData.nuvquad );
+				*/
+				
+				return metaData;
+				
+			}
+			
+			function parseString( data, offset, length ) {
+				
+				return data.substr( offset, length );
+				
+			}
+			
+			function parseFloat32( data, offset ) {
+				
+				var b3 = parseUChar8( data, offset ),
+					b2 = parseUChar8( data, offset + 1 ),
+					b1 = parseUChar8( data, offset + 2 ),
+					b0 = parseUChar8( data, offset + 3 ),
+				
+					sign = 1 - ( 2 * ( b0 >> 7 ) ),
+					exponent = ((( b0 << 1 ) & 0xff) | ( b1 >> 7 )) - 127,
+					mantissa = (( b1 & 0x7f ) << 16) | (b2 << 8) | b3;
+					
+					if (mantissa == 0 && exponent == -127)
+						return 0.0;
+					
+					return sign * ( 1 + mantissa * Math.pow( 2, -23 ) ) * Math.pow( 2, exponent );
+
+			}
+			
+			function parseUInt32( data, offset ) {
+				
+				var b0 = parseUChar8( data, offset ),
+					b1 = parseUChar8( data, offset + 1 ),
+					b2 = parseUChar8( data, offset + 2 ),
+					b3 = parseUChar8( data, offset + 3 );
+				
+				return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
+			}
+			
+			function parseUInt16( data, offset ) {
+				
+				var b0 = parseUChar8( data, offset ),
+					b1 = parseUChar8( data, offset + 1 );
+				
+				return (b1 << 8) + b0;
+				
+			}
+			
+			function parseSChar8( data, offset ) {
+				
+				var b = parseUChar8( data, offset );
+				return b > 127 ? b - 256 : b;
+				
+			}
+			
+			function parseUChar8( data, offset ) {
+				
+				return data.charCodeAt( offset ) & 0xff;
+			}
+			
+			function init_vertices( start ) {
+				
+				var i, x, y, z, 
+					stride = md.vertex_coordinate_bytes * 3;
+				
+				for( i = 0; i < md.nvertices; ++i ) {
+					
+					x = parseFloat32( data, start + i*stride );
+					y = parseFloat32( data, start + i*stride + md.vertex_coordinate_bytes );
+					z = parseFloat32( data, start + i*stride + md.vertex_coordinate_bytes*2 );
+					scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) );
+					
+				}
+				
+				return md.nvertices * stride;
+				
+			}
+			
+			function init_triangles_flat( start ) {
+				
+				var i, a, b, c, m, material, 
+					stride = md.vertex_index_bytes * 3 + md.material_index_bytes;
+				
+				for( i = 0; i < md.ntri_flat; ++i ) {
+					
+					a = parseUInt32( data, start + i*stride );
+					b = parseUInt32( data, start + i*stride + md.vertex_index_bytes );
+					c = parseUInt32( data, start + i*stride + md.vertex_index_bytes*2 );
+					m = parseUInt16( data, start + i*stride + md.vertex_index_bytes*3 );
+					
+					material = scope.materials[ m ];
+					scope.faces.push( new THREE.Face3( a, b, c, null, material ) );
+					
+				}
+				
+				return md.ntri_flat * stride;
+				
+			}
+			
+			function init_triangles_smooth( start ) {
+			
+				var i, a, b, c, m, na, nb, nc, material,
+					nax, nay, naz, nbx, nby, nbz, ncx, ncy, ncz,
+					stride = md.vertex_index_bytes * 3 + md.material_index_bytes + md.normal_index_bytes * 3;
+				
+				for( i = 0; i < md.ntri_smooth; ++i ) {
+					
+					a  = parseUInt32( data, start + i*stride );
+					b  = parseUInt32( data, start + i*stride + md.vertex_index_bytes );
+					c  = parseUInt32( data, start + i*stride + md.vertex_index_bytes*2 );
+					m  = parseUInt16( data, start + i*stride + md.vertex_index_bytes*3 );
+					na = parseUInt32( data, start + i*stride + md.vertex_index_bytes*3 + md.material_index_bytes );
+					nb = parseUInt32( data, start + i*stride + md.vertex_index_bytes*3 + md.material_index_bytes + md.normal_index_bytes );
+					nc = parseUInt32( data, start + i*stride + md.vertex_index_bytes*3 + md.material_index_bytes + md.normal_index_bytes*2 );
+					
+					material = scope.materials[ m ];
+					
+					nax = normals[ na*3     ],
+					nay = normals[ na*3 + 1 ],
+					naz = normals[ na*3 + 2 ],
+				
+					nbx = normals[ nb*3     ],
+					nby = normals[ nb*3 + 1 ],
+					nbz = normals[ nb*3 + 2 ],
+				
+					ncx = normals[ nc*3     ],
+					ncy = normals[ nc*3 + 1 ],
+					ncz = normals[ nc*3 + 2 ];
+				
+					scope.faces.push( new THREE.Face3( a, b, c, 
+								  [new THREE.Vector3( nax, nay, naz ), 
+								   new THREE.Vector3( nbx, nby, nbz ), 
+								   new THREE.Vector3( ncx, ncy, ncz )], 
+								  material ) );
+					
+				}
+				
+				return md.ntri_smooth * stride;
+				
+			}
+
+			function init_quads_flat( start ) {
+				
+				var i, a, b, c, d, m, material,
+					stride = md.vertex_index_bytes * 4 + md.material_index_bytes;;
+				
+				for( i = 0; i < md.nquad_flat; ++i ) {
+					
+					a = parseUInt32( data, start + i*stride );
+					b = parseUInt32( data, start + i*stride + md.vertex_index_bytes );
+					c = parseUInt32( data, start + i*stride + md.vertex_index_bytes*2 );
+					d = parseUInt32( data, start + i*stride + md.vertex_index_bytes*3 );
+					m = parseUInt16( data, start + i*stride + md.vertex_index_bytes*4 );
+					
+					material = scope.materials[ m ];
+					scope.faces.push( new THREE.Face4( a, b, c, d, null, material ) );
+					
+				}
+				
+				return md.nquad_flat * stride;
+				
+			}
+
+			function init_quads_smooth( start ) {
+				
+				var i, a, b, c, d, m, na, nb, nc, nd, material,
+					nax, nay, naz, nbx, nby, nbz, ncx, ncy, ncz, ndx, ndy, ndz,
+					stride = md.vertex_index_bytes * 4 + md.material_index_bytes + md.normal_index_bytes * 4;
+				
+				for( i = 0; i < md.nquad_smooth; ++i ) {
+					
+					a  = parseUInt32( data, start + i*stride );
+					b  = parseUInt32( data, start + i*stride + md.vertex_index_bytes );
+					c  = parseUInt32( data, start + i*stride + md.vertex_index_bytes*2 );
+					d  = parseUInt32( data, start + i*stride + md.vertex_index_bytes*3 );
+					m  = parseUInt16( data, start + i*stride + md.vertex_index_bytes*4 );
+					na = parseUInt32( data, start + i*stride + md.vertex_index_bytes*4 + md.material_index_bytes );
+					nb = parseUInt32( data, start + i*stride + md.vertex_index_bytes*4 + md.material_index_bytes + md.normal_index_bytes );
+					nc = parseUInt32( data, start + i*stride + md.vertex_index_bytes*4 + md.material_index_bytes + md.normal_index_bytes*2 );
+					nd = parseUInt32( data, start + i*stride + md.vertex_index_bytes*4 + md.material_index_bytes + md.normal_index_bytes*3 );
+					
+					material = scope.materials[ m ];
+					
+					nax = normals[ na*3     ],
+					nay = normals[ na*3 + 1 ],
+					naz = normals[ na*3 + 2 ],
+				
+					nbx = normals[ nb*3     ],
+					nby = normals[ nb*3 + 1 ],
+					nbz = normals[ nb*3 + 2 ],
+				
+					ncx = normals[ nc*3     ],
+					ncy = normals[ nc*3 + 1 ],
+					ncz = normals[ nc*3 + 2 ];
+
+					ndx = normals[ nd*3     ],
+					ndy = normals[ nd*3 + 1 ],
+					ndz = normals[ nd*3 + 2 ];
+
+					scope.faces.push( new THREE.Face4( a, b, c, d,
+								  [new THREE.Vector3( nax, nay, naz ), 
+								   new THREE.Vector3( nbx, nby, nbz ), 
+								   new THREE.Vector3( ncx, ncy, ncz ),
+								   new THREE.Vector3( ndx, ndy, ndz )], 
+								  material ) );
+				}
+				
+				return md.nquad_smooth * stride;
+				
+			}
+			
+			function init_uvs_tri( start ) {
+				
+				var i, ua, ub, uc, va, vb, vc, uv,
+					stride = md.uv_coordinate_bytes * 6;
+				
+				for( i = 0; i < md.nuvtri; i++ ) {
+					
+					ua = parseFloat32( data, start + i*stride );
+					va = parseFloat32( data, start + i*stride + 4 );
+					
+					ub = parseFloat32( data, start + i*stride + 8 );
+					vb = parseFloat32( data, start + i*stride + 12 );
+					
+					uc = parseFloat32( data, start + i*stride + 16 );
+					vc = parseFloat32( data, start + i*stride + 20 );
+					
+					uv = [];
+					uv.push( new THREE.UV( ua, va ) );
+					uv.push( new THREE.UV( ub, vb ) );
+					uv.push( new THREE.UV( uc, vc ) );
+					scope.uvs.push( uv );
+					
+				}
+				
+				return md.nuvtri * stride;
+				
+			}
+
+			function init_uvs_quad( start ) {
+				
+				var i, ua, ub, uc, ud, va, vb, vc, vd, uv,
+					stride = md.uv_coordinate_bytes * 8;
+				
+				for( i = 0; i < md.nuvquad; i++ ) {
+					
+					ua = parseFloat32( data, start + i*stride );
+					va = parseFloat32( data, start + i*stride + 4 );
+					
+					ub = parseFloat32( data, start + i*stride + 8 );
+					vb = parseFloat32( data, start + i*stride + 12 );
+					
+					uc = parseFloat32( data, start + i*stride + 16 );
+					vc = parseFloat32( data, start + i*stride + 20 );
+
+					ud = parseFloat32( data, start + i*stride + 24 );
+					vd = parseFloat32( data, start + i*stride + 28 );
+					
+					uv = [];
+					uv.push( new THREE.UV( ua, va ) );
+					uv.push( new THREE.UV( ub, vb ) );
+					uv.push( new THREE.UV( uc, vc ) );
+					uv.push( new THREE.UV( ud, vd ) );
+					scope.uvs.push( uv );
+					
+				}
+				
+				return md.nuvquad * stride;
+				
+			}
+			
+			function init_normals( start ) {
+				
+				var i, x, y, z, 
+					stride = md.normal_coordinate_bytes * 3;
+				
+				for( i = 0; i < md.nnormals; ++i ) {
+					
+					x = parseSChar8( data, start + i*stride );
+					y = parseSChar8( data, start + i*stride + md.normal_coordinate_bytes );
+					z = parseSChar8( data, start + i*stride + md.normal_coordinate_bytes*2 );
+					
+					normals.push( x/127, y/127, z/127 );
+					
+				}
+				
+				return md.nnormals * stride;
+				
+			}
+			
+			function init_materials() {
+				
+				scope.materials = [];
+				for( var i = 0; i < materials.length; ++i ) {
+					scope.materials[i] = [ create_material( materials[i], urlbase ) ];
+				}
+				
+				//log( "materials: " + scope.materials.length );
+				
+			}
+			
+			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 ) );
+		
+	},
+	
 	createModel: function ( data, callback, urlbase ) {
 	createModel: function ( data, callback, urlbase ) {
 		
 		
 		var Model = function ( urlbase ) {
 		var Model = function ( urlbase ) {
@@ -65,7 +602,7 @@ THREE.Loader.prototype = {
 
 
 			function init_uvs() {
 			function init_uvs() {
 			
 			
-				var i, l, d, ua, ub, uc, ud, va, vb, vc, vd;
+				var i, l, ua, ub, uc, ud, va, vb, vc, vd;
 				
 				
 				for( i = 0, l = data.uvs_tri.length; i < l; i++ ) {
 				for( i = 0, l = data.uvs_tri.length; i < l; i++ ) {
 					
 					

+ 333 - 42
utils/exporters/convert_obj_threejs_slim.py

@@ -1,15 +1,21 @@
-"""Convert Wavefront OBJ / MTL files into Three.js (slim models version, to be used with web worker loader)
+"""Convert Wavefront OBJ / MTL files into Three.js (slim models version, to be used with web worker based ascii / binary loader)
 
 
 -------------------------
 -------------------------
 How to use this converter
 How to use this converter
 -------------------------
 -------------------------
 
 
-python convert_obj_threejs_slim.py -i filename.obj -o filename.js [-a center|top|bottom] [-s smooth|flat]
+python convert_obj_threejs_slim.py -i infile.obj -o outfile.js [-a center|top|bottom] [-s smooth|flat] [-t ascii|binary]
 
 
-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).
+Notes: 
+
+    - by default, model is centered (middle of bounding box goes to 0,0,0),
+      uses smooth shading (if there were vertex normals in the original 
+      model) and is in ASCII format.
  
  
+    - binary conversion will create two files: 
+        outfile.js  (materials)
+        outfile.bin (binary buffers)
+    
 --------------------------------------------------
 --------------------------------------------------
 How to use generated JS file in your HTML document
 How to use generated JS file in your HTML document
 --------------------------------------------------
 --------------------------------------------------
@@ -22,8 +28,13 @@ How to use generated JS file in your HTML document
         ...
         ...
         
         
         var loader = new THREE.Loader();
         var loader = new THREE.Loader();
-        loader.loadWorker( "Model.js", function( geometry ) { createScene( geometry) }, path_to_textures );
         
         
+        // load ascii model
+        loader.loadAscii( "Model_slim.js", function( geometry ) { createScene( geometry) }, path_to_textures );
+
+        // load binary model
+        loader.loadBinary( "Model_bin.js", function( geometry ) { createScene( geometry) }, path_to_textures );
+
         function createScene( geometry ) {
         function createScene( geometry ) {
             
             
             var normalizeUVsFlag = 1; // set to 1 if canvas render has missing materials
             var normalizeUVsFlag = 1; // set to 1 if canvas render has missing materials
@@ -111,13 +122,15 @@ import random
 import os.path
 import os.path
 import getopt
 import getopt
 import sys
 import sys
+import struct
+import math
 
 
 # #####################################################
 # #####################################################
 # Configuration
 # Configuration
 # #####################################################
 # #####################################################
-ALIGN = "center" # center bottom top none
-
-SHADING = "smooth" # flat smooth
+ALIGN = "center"    # center bottom top none
+SHADING = "smooth"  # smooth flat 
+TYPE = "ascii"      # ascii binary
 
 
 # default colors for debugging (each material gets one distinct color): 
 # default colors for debugging (each material gets one distinct color): 
 # white, red, green, blue, yellow, cyan, magenta
 # white, red, green, blue, yellow, cyan, magenta
@@ -126,7 +139,7 @@ COLORS = [0xffeeeeee, 0xffee0000, 0xff00ee00, 0xff0000ee, 0xffeeee00, 0xff00eeee
 # #####################################################
 # #####################################################
 # Templates
 # Templates
 # #####################################################
 # #####################################################
-TEMPLATE_FILE = u"""\
+TEMPLATE_FILE_ASCII = u"""\
 // Converted from: %(fname)s
 // Converted from: %(fname)s
 //  vertices: %(nvertex)d
 //  vertices: %(nvertex)d
 //  faces: %(nface)d 
 //  faces: %(nface)d 
@@ -158,6 +171,27 @@ var model = {
 postMessage( model );
 postMessage( model );
 """
 """
 
 
+TEMPLATE_FILE_BIN = 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],
+
+    'buffers': '%(buffers)s',
+
+    'end': (new Date).getTime()
+    }
+    
+postMessage( model );
+"""
+
 TEMPLATE_VERTEX = "%f,%f,%f"
 TEMPLATE_VERTEX = "%f,%f,%f"
 
 
 TEMPLATE_UV_TRI = "%f,%f,%f,%f,%f,%f"
 TEMPLATE_UV_TRI = "%f,%f,%f,%f,%f,%f"
@@ -196,7 +230,7 @@ def get_name(fname):
     """
     """
     
     
     return os.path.basename(fname).split(".")[0]
     return os.path.basename(fname).split(".")[0]
-  
+
 def bbox(vertices):
 def bbox(vertices):
     """Compute bounding box of vertex array.
     """Compute bounding box of vertex array.
     """
     """
@@ -609,6 +643,34 @@ def generate_mtl(materials):
         }
         }
     return mtl
     return mtl
     
     
+def generate_materials_string(materials, mtllib):
+    """Generate final materials string.
+    """
+
+    random.seed(42) # to get well defined color order for materials
+    
+    # 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
+    
+    return generate_materials(mtl, materials)
+    
 # #####################################################
 # #####################################################
 # Faces
 # Faces
 # #####################################################
 # #####################################################
@@ -625,9 +687,9 @@ def is_quad_smooth(f):
     return len(f['vertex'])==4 and f["normal"] and SHADING == "smooth"
     return len(f['vertex'])==4 and f["normal"] and SHADING == "smooth"
 
 
 # #####################################################
 # #####################################################
-# API
+# API - ASCII converter
 # #####################################################
 # #####################################################
-def convert(infile, outfile):
+def convert_ascii(infile, outfile):
     """Convert infile.obj to outfile.js
     """Convert infile.obj to outfile.js
     
     
     Here is where everything happens. If you need to automate conversions,
     Here is where everything happens. If you need to automate conversions,
@@ -646,41 +708,18 @@ def convert(infile, outfile):
         bottom(vertices)
         bottom(vertices)
     elif ALIGN == "top":
     elif ALIGN == "top":
         top(vertices)
         top(vertices)
-    
-    random.seed(42) # to get well defined color order for materials
-    
+        
     uv_string_tri = ""
     uv_string_tri = ""
     uv_string_quad = ""
     uv_string_quad = ""
     if len(uvs)>0:
     if len(uvs)>0:
         uv_string_tri  = ",".join([generate_uv_tri(f, uvs)  for f in faces if len(f['uv']) == 3])
         uv_string_tri  = ",".join([generate_uv_tri(f, uvs)  for f in faces if len(f['uv']) == 3])
         uv_string_quad = ",".join([generate_uv_quad(f, uvs) for f in faces if len(f['uv']) == 4])
         uv_string_quad = ",".join([generate_uv_quad(f, uvs) for f in faces if len(f['uv']) == 4])
-            
-
-    # 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 = ""
     normals_string = ""
     if SHADING == "smooth":
     if SHADING == "smooth":
         normals_string = ",".join(generate_normal(n) for n in normals)
         normals_string = ",".join(generate_normal(n) for n in normals)
         
         
-    text = TEMPLATE_FILE % {
+    text = TEMPLATE_FILE_ASCII % {
     "name"       : get_name(outfile),
     "name"       : get_name(outfile),
     "vertices"   : ",".join([generate_vertex(v) for v in vertices]),
     "vertices"   : ",".join([generate_vertex(v) for v in vertices]),
     "triangles"  : ",".join([generate_triangle(f) for f in faces if is_triangle_flat(f)]),
     "triangles"  : ",".join([generate_triangle(f) for f in faces if is_triangle_flat(f)]),
@@ -691,7 +730,7 @@ def convert(infile, outfile):
     "uvs_quad"   : uv_string_quad,
     "uvs_quad"   : uv_string_quad,
     "normals"    : normals_string,
     "normals"    : normals_string,
     
     
-    "materials" : generate_materials(mtl, materials),
+    "materials" : generate_materials_string(materials, mtllib),
     
     
     "fname"     : infile,
     "fname"     : infile,
     "nvertex"   : len(vertices),
     "nvertex"   : len(vertices),
@@ -705,11 +744,256 @@ def convert(infile, outfile):
     
     
     print "%d vertices, %d faces, %d materials" % (len(vertices), len(faces), len(materials))
     print "%d vertices, %d faces, %d materials" % (len(vertices), len(faces), len(materials))
         
         
+# #############################################################################
+# API - Binary converter
+# #############################################################################
+def convert_binary(infile, outfile):
+    """Convert infile.obj to outfile.js + outfile.bin    
+    """
+    
+    if not file_exists(infile):
+        print "Couldn't find [%s]" % infile
+        return
+    
+    binfile = get_name(outfile) + ".bin"
+    
+    faces, vertices, uvs, normals, materials, mtllib = parse_obj(infile)
+    
+    if ALIGN == "center":
+        center(vertices)
+    elif ALIGN == "bottom":
+        bottom(vertices)
+    elif ALIGN == "top":
+        top(vertices)    
+    
+    # ###################
+    # generate JS file
+    # ###################
+    
+    text = TEMPLATE_FILE_BIN % {
+    "name"       : get_name(outfile),
+    
+    "materials" : generate_materials_string(materials, mtllib),
+    "buffers"   : binfile,
+    
+    "fname"     : infile,
+    "nvertex"   : len(vertices),
+    "nface"     : len(faces),
+    "nmaterial" : len(materials)
+    }
+    
+    out = open(outfile, "w")
+    out.write(text)
+    out.close()
+    
+    # ###################
+    # generate BIN file
+    # ###################
+        
+    # preprocess faces
+    triangles_flat = []
+    triangles_smooth = []
+    quads_flat = []
+    quads_smooth = []
+    faces_uv_tri = []
+    faces_uv_quad = []
+    for f in faces:
+        if is_triangle_flat(f):
+            triangles_flat.append(f)
+        elif is_triangle_smooth(f):
+            triangles_smooth.append(f)
+        elif is_quad_flat(f):
+            quads_flat.append(f)
+        elif is_quad_smooth(f):
+            quads_smooth.append(f)
+            
+        ui = f['uv']
+        if len(ui) == 3:
+            faces_uv_tri.append(f)
+        elif len(ui) == 4:
+            faces_uv_quad.append(f)
+    
+    if SHADING == "smooth":
+        nnormals = len(normals)
+    else:
+        nnormals = 0
+        
+    buffer = []
+
+    # header
+    # ------
+    header_bytes  = struct.calcsize('<8s')
+    header_bytes += struct.calcsize('<BBBBBBB')
+    header_bytes += struct.calcsize('<IIIIIIII')
+    
+    # signature
+    signature = struct.pack('<8s', 'Three.js')
+    
+    # metadata (all data is little-endian)
+    vertex_coordinate_bytes = 4
+    vertex_index_bytes = 4
+    normal_index_bytes = 4
+    material_index_bytes = 2
+    normal_coordinate_bytes = 1
+    uv_coordinate_bytes = 4
+    
+    # header_bytes            unsigned char   1
+    # vertex_coordinate_bytes unsigned char   1
+    # vertex_index_bytes      unsigned char   1
+    # normal_index_bytes      unsigned char   1
+    # material_index_bytes    unsigned char   1
+    # normal_coordinate_bytes unsigned char   1
+    # uv_coordinate_bytes     unsigned char   1
+    bdata = struct.pack('<BBBBBBB', header_bytes,
+                               vertex_coordinate_bytes, 
+                               vertex_index_bytes, 
+                               normal_index_bytes,
+                               material_index_bytes,
+                               normal_coordinate_bytes,
+                               uv_coordinate_bytes)
+                                   
+    # nvertices    unsigned int    4
+    # ntri_flat    unsigned int    4
+    # ntri_smooth  unsigned int    4
+    # nquad_flat   unsigned int    4
+    # nquad_smooth unsigned int    4
+    # nnormals     unsigned int    4
+    # nuvtri       unsigned int    4
+    # nuvquad      unsigned int    4
+    ndata = struct.pack('<IIIIIIII', len(vertices), 
+                               len(triangles_flat), 
+                               len(triangles_smooth),
+                               len(quads_flat),
+                               len(quads_smooth),
+                               nnormals,
+                               len(faces_uv_tri),
+                               len(faces_uv_quad)) 
+    buffer.append(signature)
+    buffer.append(bdata)
+    buffer.append(ndata)
+        
+    # 1. vertices
+    # ------------
+    # x float   4
+    # y float   4
+    # z float   4
+    for v in vertices:
+        data = struct.pack('<fff', v[0], v[1], v[2]) 
+        buffer.append(data)
+
+    # 2. normals
+    # ---------------
+    # x signed char 1
+    # y signed char 1
+    # z signed char 1
+    if SHADING == "smooth":
+        for n in normals:
+            normalize(n)
+            data = struct.pack('<bbb', math.floor(n[0]*127+0.5), math.floor(n[1]*127+0.5), math.floor(n[2]*127+0.5))
+            buffer.append(data)
+
+    
+    # 3. flat triangles
+    # ------------------
+    # a unsigned int   4
+    # b unsigned int   4
+    # c unsigned int   4
+    # m unsigned short 2
+    for f in triangles_flat:
+        vi = f['vertex']
+        data = struct.pack('<IIIH', vi[0]-1, vi[1]-1, vi[2]-1, f['material'])
+        buffer.append(data)
+
+    # 4. smooth triangles
+    # -------------------
+    # a  unsigned int   4
+    # b  unsigned int   4
+    # c  unsigned int   4
+    # m  unsigned short 2
+    # na unsigned int   4
+    # nb unsigned int   4
+    # nc unsigned int   4
+    for f in triangles_smooth:
+        vi = f['vertex']
+        ni = f['normal']
+        data = struct.pack('<IIIHIII', vi[0]-1, vi[1]-1, vi[2]-1, f['material'], ni[0]-1, ni[1]-1, ni[2]-1)
+        buffer.append(data)
+    
+    # 5. flat quads
+    # ------------------
+    # a unsigned int   4
+    # b unsigned int   4
+    # c unsigned int   4
+    # d unsigned int   4
+    # m unsigned short 2
+    for f in quads_flat:
+        vi = f['vertex']
+        data = struct.pack('<IIIIH', vi[0]-1, vi[1]-1, vi[2]-1, vi[3]-1, f['material'])
+        buffer.append(data)
+            
+    # 6. smooth quads
+    # -------------------
+    # a  unsigned int   4
+    # b  unsigned int   4
+    # c  unsigned int   4
+    # d  unsigned int   4
+    # m  unsigned short 2
+    # na unsigned int   4
+    # nb unsigned int   4
+    # nc unsigned int   4
+    # nd unsigned int   4
+    for f in quads_smooth:
+        vi = f['vertex']
+        ni = f['normal']
+        data = struct.pack('<IIIIHIIII', 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)
+        buffer.append(data)
+    
+    # 7. uvs triangles
+    # ----------------
+    # ua float      4
+    # va float      4
+    # ub float      4
+    # vb float      4
+    # uc float      4
+    # vc float      4
+    for f in faces_uv_tri:
+        ui = f['uv']
+        for i in ui:
+            u = uvs[i-1][0]
+            v = 1.0 - uvs[i-1][1]
+            data = struct.pack('<ff', u,v)
+            buffer.append(data)
+    
+    # 8. uvs quads
+    # ----------------
+    # ua float      4
+    # va float      4
+    # ub float      4
+    # vb float      4
+    # uc float      4
+    # vc float      4
+    # ud float      4
+    # vd float      4
+    for f in faces_uv_quad:
+        ui = f['uv']
+        for i in ui:
+            u = uvs[i-1][0]
+            v = 1.0 - uvs[i-1][1]
+            data = struct.pack('<ff', u,v)
+            buffer.append(data)
+
+    path = os.path.dirname(outfile)
+    fname = os.path.join(path, binfile)
+
+    out = open(fname, "wb")
+    out.write("".join(buffer))
+    out.close()
+
 # #############################################################################
 # #############################################################################
 # Helpers
 # Helpers
 # #############################################################################
 # #############################################################################
 def usage():
 def usage():
-    print "Usage: %s -i filename.obj -o filename.js [-a center|top|bottom] [-s flat|smooth]" % os.path.basename(sys.argv[0])
+    print "Usage: %s -i filename.obj -o filename.js [-a center|top|bottom] [-s flat|smooth] [-t binary|ascii]" % os.path.basename(sys.argv[0])
         
         
 # #####################################################
 # #####################################################
 # Main
 # Main
@@ -718,7 +1002,7 @@ if __name__ == "__main__":
     
     
     # get parameters from the command line
     # get parameters from the command line
     try:
     try:
-        opts, args = getopt.getopt(sys.argv[1:], "hi:o:a:s:", ["help", "input=", "output=", "align=", "shading="])
+        opts, args = getopt.getopt(sys.argv[1:], "hi:o:a:s:t:", ["help", "input=", "output=", "align=", "shading=", "type="])
     
     
     except getopt.GetoptError:
     except getopt.GetoptError:
         usage()
         usage()
@@ -744,12 +1028,19 @@ if __name__ == "__main__":
         elif o in ("-s", "--shading"):
         elif o in ("-s", "--shading"):
             if a in ("flat", "smooth"):
             if a in ("flat", "smooth"):
                 SHADING = a
                 SHADING = a
+                
+        elif o in ("-t", "--type"):
+            if a in ("binary", "ascii"):
+                TYPE = a
 
 
     if infile == "" or outfile == "":
     if infile == "" or outfile == "":
         usage()
         usage()
         sys.exit(2)
         sys.exit(2)
     
     
     print "Converting [%s] into [%s] ..." % (infile, outfile)
     print "Converting [%s] into [%s] ..." % (infile, outfile)
-    convert(infile, outfile)
     
     
+    if TYPE == "ascii":
+        convert_ascii(infile, outfile)
+    elif TYPE == "binary":
+        convert_binary(infile, outfile)
     
     

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