Browse Source

Merge remote-tracking branch 'alteredq/dev' into dev

Mr.doob 13 years ago
parent
commit
6c0629db76
60 changed files with 1020 additions and 2324 deletions
  1. 4 4
      build/three.min.js
  2. 822 190
      examples/js/loaders/UTF8Loader.js
  3. 0 960
      examples/js/loaders/UTF8v2Loader.js
  4. 0 0
      examples/models/utf8/James_Body_Lores.jpg
  5. 0 0
      examples/models/utf8/James_EyeLashBotTran.png
  6. 0 0
      examples/models/utf8/James_EyeLashTopTran.png
  7. 0 0
      examples/models/utf8/James_Eye_Green.jpg
  8. 0 0
      examples/models/utf8/James_Eye_Inner_Green.jpg
  9. 0 0
      examples/models/utf8/James_Face_Color_Hair_Lores.jpg
  10. 0 0
      examples/models/utf8/James_Mouth_Gum_Lores.jpg
  11. 0 0
      examples/models/utf8/James_Tongue_Lores.jpg
  12. 0 0
      examples/models/utf8/MCasShoe1TEX_Lores.jpg
  13. 0 0
      examples/models/utf8/MJeans1TEX_Lores.jpg
  14. 0 0
      examples/models/utf8/MTshirt3TEX_Lores.jpg
  15. 0 0
      examples/models/utf8/Nail_Hand_01_Lores.jpg
  16. 0 0
      examples/models/utf8/ben.js
  17. BIN
      examples/models/utf8/ben.utf8
  18. 107 0
      examples/models/utf8/ben_dds.js
  19. BIN
      examples/models/utf8/dds/James_Body_Lores.dds
  20. BIN
      examples/models/utf8/dds/James_EyeLashBotTran.dds
  21. BIN
      examples/models/utf8/dds/James_EyeLashTopTran.dds
  22. BIN
      examples/models/utf8/dds/James_Eye_Green.dds
  23. BIN
      examples/models/utf8/dds/James_Eye_Inner_Green.dds
  24. BIN
      examples/models/utf8/dds/James_Face_Color_Hair_Lores.dds
  25. BIN
      examples/models/utf8/dds/James_Mouth_Gum_Lores.dds
  26. BIN
      examples/models/utf8/dds/James_Tongue_Lores.dds
  27. BIN
      examples/models/utf8/dds/MCasShoe1TEX_Lores.dds
  28. BIN
      examples/models/utf8/dds/MJeans1TEX_Lores.dds
  29. BIN
      examples/models/utf8/dds/MTshirt3TEX_Lores.dds
  30. BIN
      examples/models/utf8/dds/Nail_Hand_01_Lores.dds
  31. 0 0
      examples/models/utf8/hand.jpg
  32. 0 0
      examples/models/utf8/hand.js
  33. BIN
      examples/models/utf8/hand.utf8
  34. BIN
      examples/models/utf8_r104/ben.utf8
  35. BIN
      examples/models/utf8_r104/hand.utf8
  36. 0 2
      examples/webgl_loader_scene.html
  37. 81 30
      examples/webgl_loader_utf8.html
  38. 0 247
      examples/webgl_loader_utf8_r104.html
  39. 3 2
      src/extras/ImageUtils.js
  40. 0 3
      utils/exporters/utf8-r104/build.bat
  41. BIN
      utils/exporters/utf8-r104/objcompress.exe
  42. 0 37
      utils/exporters/utf8/README
  43. 3 1
      utils/exporters/utf8/build.bat
  44. 0 787
      utils/exporters/utf8/mesh.h
  45. 0 0
      utils/exporters/utf8/obj2utf8.exe
  46. 0 0
      utils/exporters/utf8/obj2utf8x.exe
  47. BIN
      utils/exporters/utf8/objcompress
  48. 0 61
      utils/exporters/utf8/objcompress.cc
  49. BIN
      utils/exporters/utf8/objcompress.exe
  50. 0 0
      utils/exporters/utf8/src/README
  51. 0 0
      utils/exporters/utf8/src/base.h
  52. 0 0
      utils/exporters/utf8/src/bounds.h
  53. 0 0
      utils/exporters/utf8/src/compress.h
  54. 0 0
      utils/exporters/utf8/src/mesh.h
  55. 0 0
      utils/exporters/utf8/src/obj2utf8.cc
  56. 0 0
      utils/exporters/utf8/src/obj2utf8x.cc
  57. 0 0
      utils/exporters/utf8/src/objcompress.cc
  58. 0 0
      utils/exporters/utf8/src/optimize.h
  59. 0 0
      utils/exporters/utf8/src/stream.h
  60. 0 0
      utils/exporters/utf8/src/utf8.h

+ 4 - 4
build/three.min.js

@@ -510,10 +510,10 @@ THREE.ImageUtils={crossOrigin:"anonymous",loadTexture:function(a,b,c,d){var f=ne
 f.loadCount+1;if(f.loadCount===6){e.needsUpdate=true;c&&c()}};f[b].crossOrigin=this.crossOrigin;f[b].src=a[b]}return e},loadCompressedTexture:function(a,b,c,d){var f=new THREE.CompressedTexture;f.mapping=b;var e=new XMLHttpRequest;e.onload=function(){var a=THREE.ImageUtils.parseDDS(e.response,true);f.format=a.format;f.mipmaps=a.mipmaps;f.image.width=a.width;f.image.height=a.height;f.generateMipmaps=false;f.needsUpdate=true;c&&c(f)};e.onerror=d;e.open("GET",a,true);e.responseType="arraybuffer";e.send(null);
 return f},parseDDS:function(a,b){function c(a){return a.charCodeAt(0)+(a.charCodeAt(1)<<8)+(a.charCodeAt(2)<<16)+(a.charCodeAt(3)<<24)}var d={mipmaps:[],width:0,height:0,format:null,mipmapCount:1},f=c("DXT1"),e=c("DXT3"),g=c("DXT5"),h=new Int32Array(a,0,31);if(h[0]!==542327876){console.error("ImageUtils.parseDDS(): Invalid magic number in DDS header");return d}if(!h[20]&4){console.error("ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code");return d}var i=h[21];switch(i){case f:f=
 8;d.format=THREE.RGB_S3TC_DXT1_Format;break;case e:f=16;d.format=THREE.RGBA_S3TC_DXT3_Format;break;case g:f=16;d.format=THREE.RGBA_S3TC_DXT5_Format;break;default:console.error("ImageUtils.parseDDS(): Unsupported FourCC code: ",String.fromCharCode(i&255,i>>8&255,i>>16&255,i>>24&255));return d}d.mipmapCount=1;if(h[2]&131072&&b!==false)d.mipmapCount=Math.max(1,h[7]);d.width=h[4];d.height=h[3];h=h[1]+4;e=d.width;g=d.height;for(i=0;i<d.mipmapCount;i++){var l=Math.max(4,e)/4*Math.max(4,g)/4*f,j={data:new Uint8Array(a,
-h,l),width:e,height:g};d.mipmaps.push(j);h=h+l;e=e*0.5;g=g*0.5}return d},getNormalMap:function(a,b){var c=function(a){var b=Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);return[a[0]/b,a[1]/b,a[2]/b]},b=b|1,d=a.width,f=a.height,e=document.createElement("canvas");e.width=d;e.height=f;var g=e.getContext("2d");g.drawImage(a,0,0);for(var h=g.getImageData(0,0,d,f).data,i=g.createImageData(d,f),l=i.data,j=0;j<d;j++)for(var n=0;n<f;n++){var m=n-1<0?0:n-1,q=n+1>f-1?f-1:n+1,p=j-1<0?0:j-1,o=j+1>d-1?d-1:j+1,r=[],
-t=[0,0,h[(n*d+j)*4]/255*b];r.push([-1,0,h[(n*d+p)*4]/255*b]);r.push([-1,-1,h[(m*d+p)*4]/255*b]);r.push([0,-1,h[(m*d+j)*4]/255*b]);r.push([1,-1,h[(m*d+o)*4]/255*b]);r.push([1,0,h[(n*d+o)*4]/255*b]);r.push([1,1,h[(q*d+o)*4]/255*b]);r.push([0,1,h[(q*d+j)*4]/255*b]);r.push([-1,1,h[(q*d+p)*4]/255*b]);m=[];p=r.length;for(q=0;q<p;q++){var o=r[q],u=r[(q+1)%p],o=[o[0]-t[0],o[1]-t[1],o[2]-t[2]],u=[u[0]-t[0],u[1]-t[1],u[2]-t[2]];m.push(c([o[1]*u[2]-o[2]*u[1],o[2]*u[0]-o[0]*u[2],o[0]*u[1]-o[1]*u[0]]))}r=[0,0,
-0];for(q=0;q<m.length;q++){r[0]=r[0]+m[q][0];r[1]=r[1]+m[q][1];r[2]=r[2]+m[q][2]}r[0]=r[0]/m.length;r[1]=r[1]/m.length;r[2]=r[2]/m.length;t=(n*d+j)*4;l[t]=(r[0]+1)/2*255|0;l[t+1]=(r[1]+1)/2*255|0;l[t+2]=r[2]*255|0;l[t+3]=255}g.putImageData(i,0,0);return e},generateDataTexture:function(a,b,c){for(var d=a*b,f=new Uint8Array(3*d),e=Math.floor(c.r*255),g=Math.floor(c.g*255),c=Math.floor(c.b*255),h=0;h<d;h++){f[h*3]=e;f[h*3+1]=g;f[h*3+2]=c}a=new THREE.DataTexture(f,a,b,THREE.RGBFormat);a.needsUpdate=true;
-return a}};
+h,l),width:e,height:g};d.mipmaps.push(j);h=h+l;e=Math.max(e*0.5,1);g=Math.max(g*0.5,1)}return d},getNormalMap:function(a,b){var c=function(a){var b=Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);return[a[0]/b,a[1]/b,a[2]/b]},b=b|1,d=a.width,f=a.height,e=document.createElement("canvas");e.width=d;e.height=f;var g=e.getContext("2d");g.drawImage(a,0,0);for(var h=g.getImageData(0,0,d,f).data,i=g.createImageData(d,f),l=i.data,j=0;j<d;j++)for(var n=0;n<f;n++){var m=n-1<0?0:n-1,q=n+1>f-1?f-1:n+1,p=j-1<0?0:j-1,
+o=j+1>d-1?d-1:j+1,r=[],t=[0,0,h[(n*d+j)*4]/255*b];r.push([-1,0,h[(n*d+p)*4]/255*b]);r.push([-1,-1,h[(m*d+p)*4]/255*b]);r.push([0,-1,h[(m*d+j)*4]/255*b]);r.push([1,-1,h[(m*d+o)*4]/255*b]);r.push([1,0,h[(n*d+o)*4]/255*b]);r.push([1,1,h[(q*d+o)*4]/255*b]);r.push([0,1,h[(q*d+j)*4]/255*b]);r.push([-1,1,h[(q*d+p)*4]/255*b]);m=[];p=r.length;for(q=0;q<p;q++){var o=r[q],u=r[(q+1)%p],o=[o[0]-t[0],o[1]-t[1],o[2]-t[2]],u=[u[0]-t[0],u[1]-t[1],u[2]-t[2]];m.push(c([o[1]*u[2]-o[2]*u[1],o[2]*u[0]-o[0]*u[2],o[0]*u[1]-
+o[1]*u[0]]))}r=[0,0,0];for(q=0;q<m.length;q++){r[0]=r[0]+m[q][0];r[1]=r[1]+m[q][1];r[2]=r[2]+m[q][2]}r[0]=r[0]/m.length;r[1]=r[1]/m.length;r[2]=r[2]/m.length;t=(n*d+j)*4;l[t]=(r[0]+1)/2*255|0;l[t+1]=(r[1]+1)/2*255|0;l[t+2]=r[2]*255|0;l[t+3]=255}g.putImageData(i,0,0);return e},generateDataTexture:function(a,b,c){for(var d=a*b,f=new Uint8Array(3*d),e=Math.floor(c.r*255),g=Math.floor(c.g*255),c=Math.floor(c.b*255),h=0;h<d;h++){f[h*3]=e;f[h*3+1]=g;f[h*3+2]=c}a=new THREE.DataTexture(f,a,b,THREE.RGBFormat);
+a.needsUpdate=true;return a}};
 THREE.SceneUtils={showHierarchy:function(a,b){THREE.SceneUtils.traverseHierarchy(a,function(a){a.visible=b})},traverseHierarchy:function(a,b){var c,d,f=a.children.length;for(d=0;d<f;d++){c=a.children[d];b(c);THREE.SceneUtils.traverseHierarchy(c,b)}},createMultiMaterialObject:function(a,b){var c,d=b.length,f=new THREE.Object3D;for(c=0;c<d;c++){var e=new THREE.Mesh(a,b[c]);f.add(e)}return f},cloneObject:function(a){var b;if(a instanceof THREE.MorphAnimMesh){b=new THREE.MorphAnimMesh(a.geometry,a.material);
 b.duration=a.duration;b.mirroredLoop=a.mirroredLoop;b.time=a.time;b.lastKeyframe=a.lastKeyframe;b.currentKeyframe=a.currentKeyframe;b.direction=a.direction;b.directionBackwards=a.directionBackwards}else if(a instanceof THREE.SkinnedMesh)b=new THREE.SkinnedMesh(a.geometry,a.material,a.useVertexTexture);else if(a instanceof THREE.Mesh)b=new THREE.Mesh(a.geometry,a.material);else if(a instanceof THREE.Line)b=new THREE.Line(a.geometry,a.material,a.type);else if(a instanceof THREE.Ribbon)b=new THREE.Ribbon(a.geometry,
 a.material);else if(a instanceof THREE.ParticleSystem){b=new THREE.ParticleSystem(a.geometry,a.material);b.sortParticles=a.sortParticles}else if(a instanceof THREE.Particle)b=new THREE.Particle(a.material);else if(a instanceof THREE.Sprite){b=new THREE.Sprite({});b.color.copy(a.color);b.map=a.map;b.blending=a.blending;b.useScreenCoordinates=a.useScreenCoordinates;b.mergeWith3D=a.mergeWith3D;b.affectedByDistance=a.affectedByDistance;b.scaleByViewport=a.scaleByViewport;b.alignment=a.alignment;b.rotation3d.copy(a.rotation3d);

+ 822 - 190
examples/js/loaders/UTF8Loader.js

@@ -1,328 +1,960 @@
 /**
- * Loader for UTF8 encoded models generated by:
+ * Loader for UTF8 version2 (after r51) encoded models generated by:
  *	http://code.google.com/p/webgl-loader/
  *
- * Limitations:
- *  - number of vertices < 65536 (this is after optimizations in compressor, input OBJ may have even less)
- *	- models must have normals and texture coordinates
- *  - texture coordinates must be only from <0,1>
- *  - no materials support yet
- *  - models are scaled and offset (copy numbers from compressor and use them as parameters in UTF8Loader.load() )
- *
- * @author alteredq / http://alteredqualia.com/
- * @author won3d / http://twitter.com/won3d
+ * Code to load/decompress mesh is taken from r100 of this webgl-loader
  */
 
 THREE.UTF8Loader = function () {};
 
-THREE.UTF8Loader.prototype.load = function ( url, callback, metadata ) {
+/**
+ * Load UTF8 encoded model
+ * @param jsonUrl - URL from which to load json containing information about model
+ * @param callback - Callback(THREE.Object3D) on successful loading of model
+ * @param options - options on how to load model (see THREE.MTLLoader.MaterialCreator for basic options)
+ *                  Additional options include
+ *                   geometryBase: Base url from which to load referenced geometries
+ *                   materialBase: Base url from which to load referenced textures
+ */
 
-	var xhr = new XMLHttpRequest();
-	var callbackProgress = null;
-	var length = 0;
+THREE.UTF8Loader.prototype.load = function ( jsonUrl, callback, options ) {
 
-	xhr.onreadystatechange = function() {
+    this.downloadModelJson( jsonUrl, options, callback );
 
-		if ( xhr.readyState == 4 ) {
+};
 
-			if ( xhr.status == 200 || xhr.status == 0 ) {
+// BufferGeometryCreator
 
-				var geometry = THREE.UTF8Loader.prototype.parse( xhr.responseText, metadata );
-				callback( geometry );
+THREE.UTF8Loader.BufferGeometryCreator = function () {
+};
 
-			} else {
+THREE.UTF8Loader.BufferGeometryCreator.prototype.create = function ( attribArray, indexArray ) {
 
-				console.error( "THREE.UTF8Loader: Couldn't load [" + url + "] [" + xhr.status + "]" );
+	var ntris = indexArray.length / 3;
 
-			}
+	var geometry = new THREE.BufferGeometry();
 
-		} else if ( xhr.readyState == 3 ) {
+	var positionArray = new Float32Array( 3 * 3 * ntris );
+	var normalArray = new Float32Array( 3 * 3 * ntris );
+	var uvArray = new Float32Array( 2 * 3 * ntris );
 
-			if ( callbackProgress ) {
+	var i, j, offset;
+	var x, y, z;
+	var u, v;
 
-				if ( length == 0 ) {
+	var end = attribArray.length;
+	var stride = 8;
 
-					length = xhr.getResponseHeader( "Content-Length" );
+	// extract positions
 
-				}
+	j = 0;
+	offset = 0;
 
-				callbackProgress( { total: length, loaded: xhr.responseText.length } );
+	for( i = offset; i < end; i += stride ) {
 
-			}
+		x = attribArray[ i ];
+		y = attribArray[ i + 1 ];
+		z = attribArray[ i + 2 ];
 
-		} else if ( xhr.readyState == 2 ) {
+		positionArray[ j++ ] = x;
+		positionArray[ j++ ] = y;
+		positionArray[ j++ ] = z;
 
-			length = xhr.getResponseHeader( "Content-Length" );
+	}
 
-		}
+	// extract uvs
+
+	j = 0;
+	offset = 3;
+
+	for( i = offset; i < end; i += stride ) {
+
+		u = attribArray[ i ];
+		v = attribArray[ i + 1 ];
+
+		uvArray[ j++ ] = u;
+		uvArray[ j++ ] = v;
+
+	}
+
+	// extract normals
+
+	j = 0;
+	offset = 5;
+
+	for( i = offset; i < end; i += stride ) {
+
+		x = attribArray[ i ];
+		y = attribArray[ i + 1 ];
+		z = attribArray[ i + 2 ];
+
+		normalArray[ j++ ] = x;
+		normalArray[ j++ ] = y;
+		normalArray[ j++ ] = z;
 
 	}
 
-	xhr.open( "GET", url, true );
-	xhr.send( null );
+	// create attributes
+
+	var attributes = geometry.attributes;
+
+	attributes[ "index" ]    = { itemSize: 1, array: indexArray, numItems: indexArray.length };
+	attributes[ "position" ] = { itemSize: 3, array: positionArray, numItems: positionArray.length };
+	attributes[ "normal" ]   = { itemSize: 3, array: normalArray, numItems: normalArray.length };
+	attributes[ "uv" ] 		 = { itemSize: 2, array: uvArray, numItems: uvArray.length };
+
+	// create offsets
+	// (all triangles should fit in a single chunk)
+
+	geometry.offsets = [ { start: 0, count: indexArray.length, index: 0 } ];
+
+	geometry.computeBoundingSphere();
+
+	return geometry;
+
+};
+
+// GeometryCreator
+
+THREE.UTF8Loader.GeometryCreator = function () {
+};
+
+THREE.UTF8Loader.GeometryCreator.prototype = {
+
+    create: function ( attribArray, indexArray ) {
+
+        var geometry = new THREE.Geometry();
+
+        this.init_vertices( geometry, attribArray, 8, 0 );
+
+        var uvs = this.init_uvs( attribArray, 8, 3 );
+        var normals = this.init_normals( attribArray, 8, 5 );
+
+        this.init_faces( geometry, normals, uvs, indexArray );
+
+        geometry.computeCentroids();
+        geometry.computeFaceNormals();
+
+        return geometry;
+
+    },
+
+    init_vertices: function ( scope, data, stride, offset ) {
+
+        var i, x, y, z;
+		var end = data.length;
+
+        for( i = offset; i < end; i += stride ) {
+
+            x = data[ i ];
+            y = data[ i + 1 ];
+            z = data[ i + 2 ];
+
+            this.addVertex( scope, x, y, z );
+
+        }
+
+    },
+
+    init_normals: function( data, stride, offset ) {
+
+        var normals = [];
+
+        var i, x, y, z;
+		var end = data.length;
+
+        for( i = offset; i < end; i += stride ) {
+
+            // Assumes already normalized to <-1,1> (unlike previous version of UTF8Loader)
+
+            x = data[ i ];
+            y = data[ i + 1 ];
+            z = data[ i + 2 ];
+
+            normals.push( x, y, z );
+
+        }
+
+        return normals;
+
+    },
+
+    init_uvs: function( data, stride, offset ) {
+
+        var uvs = [];
+
+        var i, u, v;
+		var end = data.length;
+
+        for( i = offset; i < end; i += stride ) {
+
+            // Assumes uvs are already normalized (unlike previous version of UTF8Loader)
+            // uvs can be negative, need to set wrap for texture map later on ...
+
+            u = data[ i ];
+            v = data[ i + 1 ];
+
+            uvs.push( u, v );
+        }
+
+        return uvs;
+
+    },
+
+    init_faces: function( scope, normals, uvs, indices ) {
+
+        var i,
+            a, b, c,
+            u1, v1, u2, v2, u3, v3;
+
+		var end = indices.length;
+
+        var m = 0; // all faces defaulting to material 0
+
+        for( i = 0; i < end; i += 3 ) {
+
+            a = indices[ i ];
+            b = indices[ i + 1 ];
+            c = indices[ i + 2 ];
+
+            this.f3n( scope, normals, a, b, c, m, a, b, c );
+
+            u1 = uvs[ a * 2 ];
+            v1 = uvs[ a * 2 + 1 ];
+
+            u2 = uvs[ b * 2 ];
+            v2 = uvs[ b * 2 + 1 ];
+
+            u3 = uvs[ c * 2 ];
+            v3 = uvs[ c * 2 + 1 ];
+
+            this.uv3( scope.faceVertexUvs[ 0 ], u1, v1, u2, v2, u3, v3 );
+
+        }
+
+    },
+
+    addVertex: function ( scope, x, y, z ) {
+
+        scope.vertices.push( new THREE.Vector3( x, y, z ) );
+
+    },
+
+    f3n: function( scope, normals, a, b, c, mi, nai, nbi, nci ) {
+
+        var nax = normals[ nai * 3 ],
+            nay = normals[ nai * 3 + 1 ],
+            naz = normals[ nai * 3 + 2 ],
+
+            nbx = normals[ nbi * 3 ],
+            nby = normals[ nbi * 3 + 1 ],
+            nbz = normals[ nbi * 3 + 2 ],
+
+            ncx = normals[ nci * 3 ],
+            ncy = normals[ nci * 3 + 1 ],
+            ncz = normals[ nci * 3 + 2 ];
+
+        var na = new THREE.Vector3( nax, nay, naz ),
+            nb = new THREE.Vector3( nbx, nby, nbz ),
+            nc = new THREE.Vector3( ncx, ncy, ncz );
+
+        scope.faces.push( new THREE.Face3( a, b, c, [ na, nb, nc ], null, mi ) );
+
+    },
+
+    uv3: function ( where, u1, v1, u2, v2, u3, v3 ) {
+
+        var uv = [];
+        uv.push( new THREE.UV( u1, v1 ) );
+        uv.push( new THREE.UV( u2, v2 ) );
+        uv.push( new THREE.UV( u3, v3 ) );
+        where.push( uv );
+
+    }
 
 };
 
-// UTF-8 decoder from webgl-loader
+
+// UTF-8 decoder from webgl-loader (r100)
 // http://code.google.com/p/webgl-loader/
 
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you
-// may not use this file except in compliance with the License. You
-// may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-// implied. See the License for the specific language governing
-// permissions and limitations under the License.
+// Model manifest description. Contains objects like:
+// name: {
+//   materials: { 'material_name': { ... } ... },
+//   decodeParams: {
+//     decodeOffsets: [ ... ],
+//     decodeScales: [ ... ],
+//   },
+//   urls: {
+//     'url': [
+//       { material: 'material_name',
+//         attribRange: [#, #],
+//         indexRange: [#, #],
+//         names: [ 'object names' ... ],
+//         lengths: [#, #, # ... ]
+//       }
+//     ],
+//     ...
+//   }
+// }
+
+var DEFAULT_DECODE_PARAMS = {
+
+    decodeOffsets: [-4095, -4095, -4095, 0, 0, -511, -511, -511],
+    decodeScales: [1/8191, 1/8191, 1/8191, 1/1023, 1/1023, 1/1023, 1/1023, 1/1023]
+
+    // TODO: normal decoding? (see walt.js)
+    // needs to know: input, output (from vertex format!)
+    //
+    // Should split attrib/index.
+    // 1) Decode position and non-normal attributes.
+    // 2) Decode indices, computing normals
+    // 3) Maybe normalize normals? Only necessary for refinement, or fixed?
+    // 4) Maybe refine normals? Should this be part of regular refinement?
+    // 5) Morphing
 
-THREE.UTF8Loader.prototype.decompressMesh = function ( str ) {
+};
 
-	var num_verts = str.charCodeAt( 0 );
+// Triangle strips!
 
-	if ( num_verts >= 0xE000 ) {
+// TODO: will it be an optimization to specialize this method at
+// runtime for different combinations of stride, decodeOffset and
+// decodeScale?
 
-		num_verts -= 0x0800;
+THREE.UTF8Loader.prototype.decompressAttribsInner_ = function ( str, inputStart, inputEnd,
+                                                                  output, outputStart, stride,
+                                                                  decodeOffset, decodeScale ) {
 
-	}
+    var prev = 0;
 
-	num_verts ++;
+    for ( var j = inputStart; j < inputEnd; j ++ ) {
 
-	var attribs_out = new Float32Array( 8 * num_verts );
+        var code = str.charCodeAt( j );
+        prev += ( code >> 1 ) ^ ( -( code & 1 ) );
 
-	var offset = 1;
+        output[ outputStart ] = decodeScale * ( prev + decodeOffset );
+        outputStart += stride;
 
-	for ( var i = 0; i < 8; i ++ ) {
+    }
 
-		var prev_attrib = 0;
+};
 
-		for ( var j = 0; j < num_verts; ++ j ) {
+THREE.UTF8Loader.prototype.decompressIndices_ = function( str, inputStart, numIndices,
+                                                            output, outputStart ) {
 
-			var code = str.charCodeAt( j + offset );
+	var highest = 0;
 
-			prev_attrib += ( code >> 1 ) ^ ( - ( code & 1 ) );
+    for ( var i = 0; i < numIndices; i ++ ) {
 
-			attribs_out[ 8 * j + i ] = prev_attrib;
+        var code = str.charCodeAt( inputStart ++ );
 
-		}
+        output[ outputStart ++ ] = highest - code;
 
-		offset += num_verts;
+        if ( code === 0 ) {
 
-	}
+            highest ++;
 
-	var num_indices = str.length - offset;
+        }
 
-	var indices_out = new Uint16Array( num_indices );
+    }
 
-	var index_high_water_mark = 0;
+};
 
-	for ( var i = 0; i < num_indices; i ++ ) {
+THREE.UTF8Loader.prototype.decompressAABBs_ = function ( str, inputStart, numBBoxen,
+                                                           decodeOffsets, decodeScales ) {
+    var numFloats = 6 * numBBoxen;
 
-		var code = str.charCodeAt( i + offset );
+    var inputEnd = inputStart + numFloats;
+    var outputStart = 0;
 
-		indices_out[ i ] = index_high_water_mark - code;
+    var bboxen = new Float32Array( numFloats );
 
-		if ( code == 0 ) {
+    for ( var i = inputStart; i < inputEnd; i += 6 ) {
 
-			index_high_water_mark ++;
+        var minX = str.charCodeAt(i + 0) + decodeOffsets[0];
+        var minY = str.charCodeAt(i + 1) + decodeOffsets[1];
+        var minZ = str.charCodeAt(i + 2) + decodeOffsets[2];
 
-		}
+        var radiusX = (str.charCodeAt(i + 3) + 1) >> 1;
+        var radiusY = (str.charCodeAt(i + 4) + 1) >> 1;
+        var radiusZ = (str.charCodeAt(i + 5) + 1) >> 1;
 
-	}
+        bboxen[ outputStart++ ] = decodeScales[0] * (minX + radiusX);
+        bboxen[ outputStart++ ] = decodeScales[1] * (minY + radiusY);
+        bboxen[ outputStart++ ] = decodeScales[2] * (minZ + radiusZ);
+
+        bboxen[ outputStart++ ] = decodeScales[0] * radiusX;
+        bboxen[ outputStart++ ] = decodeScales[1] * radiusY;
+        bboxen[ outputStart++ ] = decodeScales[2] * radiusZ;
+
+    }
+
+    return bboxen;
+
+};
+
+THREE.UTF8Loader.prototype.decompressMesh =  function ( str, meshParams, decodeParams, name, idx, callback ) {
+
+    // Extract conversion parameters from attribArrays.
+
+    var stride = decodeParams.decodeScales.length;
+
+    var decodeOffsets = decodeParams.decodeOffsets;
+    var decodeScales = decodeParams.decodeScales;
+
+    var attribStart = meshParams.attribRange[0];
+    var numVerts = meshParams.attribRange[1];
+
+    // Decode attributes.
+
+    var inputOffset = attribStart;
+    var attribsOut = new Float32Array( stride * numVerts );
+
+    for (var j = 0; j < stride; j ++ ) {
+
+        var end = inputOffset + numVerts;
+
+		var decodeScale = decodeScales[j];
+
+        if ( decodeScale ) {
+
+            // Assume if decodeScale is never set, simply ignore the
+            // attribute.
+
+            this.decompressAttribsInner_( str, inputOffset, end,
+                attribsOut, j, stride,
+                decodeOffsets[j], decodeScale );
+        }
+
+        inputOffset = end;
+
+    }
+
+    var indexStart = meshParams.indexRange[ 0 ];
+    var numIndices = 3 * meshParams.indexRange[ 1 ];
+
+    var indicesOut = new Uint16Array( numIndices );
+
+    this.decompressIndices_( str, inputOffset, numIndices, indicesOut, 0 );
+
+    // Decode bboxen.
+
+    var bboxen = undefined;
+    var bboxOffset = meshParams.bboxes;
+
+    if ( bboxOffset ) {
+
+        bboxen = this.decompressAABBs_( str, bboxOffset, meshParams.names.length, decodeOffsets, decodeScales );
+    }
+
+    callback( name, idx, attribsOut, indicesOut, bboxen, meshParams );
+
+};
+
+THREE.UTF8Loader.prototype.copyAttrib = function ( stride, attribsOutFixed, lastAttrib, index ) {
+
+    for ( var j = 0; j < stride; j ++ ) {
+
+        lastAttrib[ j ] = attribsOutFixed[ stride * index + j ];
+
+    }
+
+};
+
+THREE.UTF8Loader.prototype.decodeAttrib2 = function ( str, stride, decodeOffsets, decodeScales, deltaStart,
+                                                        numVerts, attribsOut, attribsOutFixed, lastAttrib,
+                                                        index ) {
 
-	return [ attribs_out, indices_out ];
+    for ( var j = 0; j < 5; j ++ ) {
+
+        var code = str.charCodeAt( deltaStart + numVerts*j + index );
+        var delta = ( code >> 1) ^ (-(code & 1));
+
+        lastAttrib[ j ] += delta;
+        attribsOutFixed[ stride * index + j ] = lastAttrib[ j ];
+        attribsOut[ stride * index + j ] = decodeScales[ j ] * ( lastAttrib[ j ] + decodeOffsets[ j ] );
+    }
 
 };
 
-THREE.UTF8Loader.prototype.parse = function ( data, metadata ) {
+THREE.UTF8Loader.prototype.accumulateNormal = function ( i0, i1, i2, attribsOutFixed, crosses ) {
+
+    var p0x = attribsOutFixed[ 8*i0 ];
+    var p0y = attribsOutFixed[ 8*i0 + 1 ];
+    var p0z = attribsOutFixed[ 8*i0 + 2 ];
+
+    var p1x = attribsOutFixed[ 8*i1 ];
+    var p1y = attribsOutFixed[ 8*i1 + 1 ];
+    var p1z = attribsOutFixed[ 8*i1 + 2 ];
+
+    var p2x = attribsOutFixed[ 8*i2 ];
+    var p2y = attribsOutFixed[ 8*i2 + 1 ];
+    var p2z = attribsOutFixed[ 8*i2 + 2 ];
+
+    p1x -= p0x;
+    p1y -= p0y;
+    p1z -= p0z;
+
+    p2x -= p0x;
+    p2y -= p0y;
+    p2z -= p0z;
+
+    p0x = p1y*p2z - p1z*p2y;
+    p0y = p1z*p2x - p1x*p2z;
+    p0z = p1x*p2y - p1y*p2x;
+
+    crosses[ 3*i0 ]     += p0x;
+    crosses[ 3*i0 + 1 ] += p0y;
+    crosses[ 3*i0 + 2 ] += p0z;
+
+    crosses[ 3*i1 ]     += p0x;
+    crosses[ 3*i1 + 1 ] += p0y;
+    crosses[ 3*i1 + 2 ] += p0z;
+
+    crosses[ 3*i2 ]     += p0x;
+    crosses[ 3*i2 + 1 ] += p0y;
+    crosses[ 3*i2 + 2 ] += p0z;
+
+};
+
+THREE.UTF8Loader.prototype.decompressMesh2 = function( str, meshParams, decodeParams, name, idx, callback ) {
+
+    var MAX_BACKREF = 96;
+
+    // Extract conversion parameters from attribArrays.
+
+    var stride = decodeParams.decodeScales.length;
+
+	var decodeOffsets = decodeParams.decodeOffsets;
+    var decodeScales = decodeParams.decodeScales;
+
+    var deltaStart = meshParams.attribRange[ 0 ];
+    var numVerts = meshParams.attribRange[ 1 ];
+
+    var codeStart = meshParams.codeRange[ 0 ];
+    var codeLength = meshParams.codeRange[ 1 ];
+
+    var numIndices = 3 * meshParams.codeRange[ 2 ];
+
+    var indicesOut = new Uint16Array( numIndices );
+
+    var crosses = new Int32Array( 3 * numVerts );
+
+    var lastAttrib = new Uint16Array( stride );
+
+    var attribsOutFixed = new Uint16Array( stride * numVerts );
+    var attribsOut = new Float32Array( stride * numVerts );
+
+    var highest = 0;
+    var outputStart = 0;
+
+    for ( var i = 0; i < numIndices; i += 3 ) {
+
+        var code = str.charCodeAt( codeStart ++ );
+
+        var max_backref = Math.min( i, MAX_BACKREF );
+
+        if ( code < max_backref ) {
+
+            // Parallelogram
+
+            var winding = code % 3;
+            var backref = i - ( code - winding );
+            var i0, i1, i2;
+
+            switch ( winding ) {
+
+                case 0:
+
+                    i0 = indicesOut[ backref + 2 ];
+                    i1 = indicesOut[ backref + 1 ];
+                    i2 = indicesOut[ backref + 0 ];
+                    break;
+
+                case 1:
+
+                    i0 = indicesOut[ backref + 0 ];
+                    i1 = indicesOut[ backref + 2 ];
+                    i2 = indicesOut[ backref + 1 ];
+                    break;
+
+                case 2:
+
+                    i0 = indicesOut[ backref + 1 ];
+                    i1 = indicesOut[ backref + 0 ];
+                    i2 = indicesOut[ backref + 2 ];
+                    break;
+
+            }
+
+            indicesOut[ outputStart ++ ] = i0;
+            indicesOut[ outputStart ++ ] = i1;
+
+            code = str.charCodeAt( codeStart ++ );
+
+            var index = highest - code;
+            indicesOut[ outputStart ++ ] = index;
+
+            if ( code === 0 ) {
+
+                for (var j = 0; j < 5; j ++ ) {
+
+                    var deltaCode = str.charCodeAt( deltaStart + numVerts * j + highest );
+
+                    var prediction = ((deltaCode >> 1) ^ (-(deltaCode & 1))) +
+                        attribsOutFixed[stride*i0 + j] +
+                        attribsOutFixed[stride*i1 + j] -
+                        attribsOutFixed[stride*i2 + j];
+
+                    lastAttrib[j] = prediction;
+
+                    attribsOutFixed[ stride * highest + j ] = prediction;
+                    attribsOut[ stride * highest + j ] = decodeScales[ j ] * ( prediction + decodeOffsets[ j ] );
+
+                }
+
+                highest ++;
+
+            } else {
+
+                this.copyAttrib( stride, attribsOutFixed, lastAttrib, index );
+
+            }
+
+            this.accumulateNormal( i0, i1, index, attribsOutFixed, crosses );
+
+        } else {
+
+            // Simple
+
+            var index0 = highest - ( code - max_backref );
+
+            indicesOut[ outputStart ++ ] = index0;
+
+            if ( code === max_backref ) {
+
+                this.decodeAttrib2( str, stride, decodeOffsets, decodeScales, deltaStart,
+                    numVerts, attribsOut, attribsOutFixed, lastAttrib,
+                    highest ++ );
+
+            } else {
+
+                this.copyAttrib(stride, attribsOutFixed, lastAttrib, index0);
+
+            }
+
+            code = str.charCodeAt( codeStart ++ );
+
+            var index1 = highest - code;
+            indicesOut[ outputStart ++ ] = index1;
+
+            if ( code === 0 ) {
+
+                this.decodeAttrib2( str, stride, decodeOffsets, decodeScales, deltaStart,
+                    numVerts, attribsOut, attribsOutFixed, lastAttrib,
+                    highest ++ );
 
-	if ( metadata === undefined ) metadata = {};
+            } else {
 
-	var scale = metadata.scale !== undefined ? metadata.scale : 1;
-	var offsetX = metadata.offsetX !== undefined ? metadata.offsetX : 0;
-	var offsetY = metadata.offsetY !== undefined ? metadata.offsetY : 0;
-	var offsetZ = metadata.offsetZ !== undefined ? metadata.offsetZ : 0;
+                this.copyAttrib( stride, attribsOutFixed, lastAttrib, index1 );
 
-	var Model = function ( texture_path ) {
+            }
 
-		//var s = (new Date).getTime();
+            code = str.charCodeAt( codeStart ++ );
 
-		var scope = this;
+            var index2 = highest - code;
+            indicesOut[ outputStart ++ ] = index2;
 
-		scope.materials = [];
+            if ( code === 0 ) {
 
-		THREE.Geometry.call( this );
+                for ( var j = 0; j < 5; j ++ ) {
 
-		var buffers = THREE.UTF8Loader.prototype.decompressMesh( data );
+                    lastAttrib[ j ] = ( attribsOutFixed[ stride * index0 + j ] + attribsOutFixed[ stride * index1 + j ] ) / 2;
 
-		var normals = [],
-			uvs = [];
+                }
 
-		init_vertices( buffers[ 0 ], 8, 0 );
-		init_uvs( buffers[ 0 ], 8, 3 );
-		init_normals( buffers[ 0 ], 8, 5 );
+                this.decodeAttrib2( str, stride, decodeOffsets, decodeScales, deltaStart,
+                    numVerts, attribsOut, attribsOutFixed, lastAttrib,
+                    highest ++ );
 
-		init_faces( buffers[ 1 ] );
+            } else {
 
-		this.computeCentroids();
-		this.computeFaceNormals();
-		//this.computeTangents();
+                this.copyAttrib( stride, attribsOutFixed, lastAttrib, index2 );
 
-		//var e = (new Date).getTime();
+            }
 
-		//console.log( "utf8 data parse time: " + (e-s) + " ms" );
+            this.accumulateNormal( index0, index1, index2, attribsOutFixed, crosses );
 
-		function init_vertices( data, stride, offset ) {
+        }
 
-			var i, x, y, z,
-				end = data.length;
+    }
 
-			for( i = offset; i < end; i += stride ) {
+    for ( var i = 0; i < numVerts; i ++ ) {
 
-				x = data[ i ];
-				y = data[ i + 1 ];
-				z = data[ i + 2 ];
+        var nx = crosses[ 3*i ];
+        var ny = crosses[ 3*i + 1 ];
+        var nz = crosses[ 3*i + 2 ];
 
-				// fix scale and offsets
+        var norm = 511.0 / Math.sqrt( nx*nx + ny*ny + nz*nz );
 
-				x = ( x / 16383 ) * scale;
-				y = ( y / 16383 ) * scale;
-				z = ( z / 16383 ) * scale;
+        var cx = str.charCodeAt( deltaStart + 5*numVerts + i );
+        var cy = str.charCodeAt( deltaStart + 6*numVerts + i );
+        var cz = str.charCodeAt( deltaStart + 7*numVerts + i );
 
-				x += offsetX;
-				y += offsetY;
-				z += offsetZ;
+        attribsOut[ stride*i + 5 ] = norm*nx + ((cx >> 1) ^ (-(cx & 1)));
+        attribsOut[ stride*i + 6 ] = norm*ny + ((cy >> 1) ^ (-(cy & 1)));
+        attribsOut[ stride*i + 7 ] = norm*nz + ((cz >> 1) ^ (-(cz & 1)));
+    }
 
-				vertex( scope, x, y, z );
+    callback( name, idx, attribsOut, indicesOut, undefined, meshParams );
 
-			}
+};
+
+THREE.UTF8Loader.prototype.downloadMesh = function ( path, name, meshEntry, decodeParams, callback ) {
+
+    var loader = this;
+    var idx = 0;
+
+    function onprogress( req, e ) {
+
+        while ( idx < meshEntry.length ) {
+
+            var meshParams = meshEntry[ idx ];
+            var indexRange = meshParams.indexRange;
+
+            if ( indexRange ) {
+
+                var meshEnd = indexRange[ 0 ] + 3 * indexRange[ 1 ];
+
+                if ( req.responseText.length < meshEnd ) break;
+
+                loader.decompressMesh( req.responseText, meshParams, decodeParams, name, idx, callback );
+
+            } else {
+
+                var codeRange = meshParams.codeRange;
+                var meshEnd = codeRange[ 0 ] + codeRange[ 1 ];
+
+                if ( req.responseText.length < meshEnd ) break;
+
+                loader.decompressMesh2( req.responseText, meshParams, decodeParams, name, idx, callback );
+            }
+
+            ++idx;
+
+        }
+
+    };
 
-		};
+    getHttpRequest( path, function( req, e ) {
 
-		function init_normals( data, stride, offset ) {
+        if ( req.status === 200 || req.status === 0 ) {
 
-			var i, x, y, z,
-				end = data.length;
+            onprogress( req, e );
 
-			for( i = offset; i < end; i += stride ) {
+        }
 
-				x = data[ i ];
-				y = data[ i + 1 ];
-				z = data[ i + 2 ];
+        // TODO: handle errors.
 
-				// normalize to <-1,1>
+    }, onprogress );
 
-				x = ( x - 512 ) / 511;
-				y = ( y - 512 ) / 511;
-				z = ( z - 512 ) / 511;
+};
+
+THREE.UTF8Loader.prototype.downloadMeshes = function ( path, meshUrlMap, decodeParams, callback ) {
+
+    for ( var url in meshUrlMap ) {
 
-				normals.push( x, y, z );
+        var meshEntry = meshUrlMap[url];
+        this.downloadMesh( path + url, url, meshEntry, decodeParams, callback );
 
-			}
+    }
+
+};
 
-		};
+THREE.UTF8Loader.prototype.createMeshCallback = function( materialBaseUrl, loadModelInfo, allDoneCallback ) {
 
-		function init_uvs( data, stride, offset ) {
+	var nCompletedUrls = 0;
+    var nExpectedUrls = 0;
 
-			var i, u, v,
-				end = data.length;
+    var expectedMeshesPerUrl = {};
+    var decodedMeshesPerUrl = {};
 
-			for( i = offset; i < end; i += stride ) {
+	var modelParts = {};
 
-				u = data[ i ];
-				v = data[ i + 1 ];
+	var meshUrlMap = loadModelInfo.urls;
 
-				// normalize to <0,1>
+    for ( var url in meshUrlMap ) {
 
-				u /= 1023;
-				v /= 1023;
+        expectedMeshesPerUrl[ url ] = meshUrlMap[ url ].length;
+        decodedMeshesPerUrl[ url ] = 0;
 
-				uvs.push( u, v );
+		nExpectedUrls ++;
 
-			}
+        modelParts[ url ] = new THREE.Object3D();
 
-		};
+    }
 
-		function init_faces( indices ) {
+    var model = new THREE.Object3D();
 
-			var i,
-				a, b, c,
-				u1, v1, u2, v2, u3, v3,
-				m,
-				end = indices.length;
+    // Prepare materials first...
 
-			m = 0; // all faces defaulting to material 0
+    var materialCreator = new THREE.MTLLoader.MaterialCreator( materialBaseUrl, loadModelInfo.options );
+    materialCreator.setMaterials( loadModelInfo.materials );
 
-			for( i = 0; i < end; i += 3 ) {
+    materialCreator.preload();
 
-				a = indices[ i ];
-				b = indices[ i + 1 ];
-				c = indices[ i + 2 ];
+	// Create callback for creating mesh parts
 
-				f3n( scope, normals, a, b, c, m, a, b, c );
+    var geometryCreator = new THREE.UTF8Loader.GeometryCreator();
+	var bufferGeometryCreator = new THREE.UTF8Loader.BufferGeometryCreator();
 
-				u1 = uvs[ a * 2 ];
-				v1 = uvs[ a * 2 + 1 ];
+	var meshCallback = function( name, idx, attribArray, indexArray, bboxen, meshParams ) {
 
-				u2 = uvs[ b * 2 ];
-				v2 = uvs[ b * 2 + 1 ];
+        // Got ourselves a new mesh
 
-				u3 = uvs[ c * 2 ];
-				v3 = uvs[ c * 2 + 1 ];
+        // name identifies this part of the model (url)
+        // idx is the mesh index of this mesh of the part
+        // attribArray defines the vertices
+        // indexArray defines the faces
+        // bboxen defines the bounding box
+        // meshParams contains the material info
 
-				uv3( scope.faceVertexUvs[ 0 ], u1, v1, u2, v2, u3, v3 );
+		var useBuffers = loadModelInfo.options.useBuffers !== undefined ? loadModelInfo.options.useBuffers : true;
 
-			}
+		if ( useBuffers ) {
 
+			var geometry = bufferGeometryCreator.create( attribArray, indexArray );
+
+		} else {
+
+			var geometry = geometryCreator.create( attribArray, indexArray );
 
 		}
 
-	};
+        var material = materialCreator.create( meshParams.material );
+
+		var mesh = new THREE.Mesh( geometry, material );
+        modelParts[ name ].add( mesh );
+
+        //model.add(new THREE.Mesh(geometry, material));
+
+        decodedMeshesPerUrl[ name ] ++;
+
+        if ( decodedMeshesPerUrl[ name ] === expectedMeshesPerUrl[ name ] ) {
+
+            nCompletedUrls ++;
+
+            model.add( modelParts[ name ] );
+
+            if ( nCompletedUrls === nExpectedUrls ) {
+
+                // ALL DONE!!!
+
+                allDoneCallback( model );
+
+            }
 
-	function vertex ( scope, x, y, z ) {
+        }
 
-		scope.vertices.push( new THREE.Vector3( x, y, z ) );
+    };
 
-	};
+	return meshCallback;
 
-	function f3n ( scope, normals, a, b, c, mi, nai, nbi, nci ) {
+};
+
+THREE.UTF8Loader.prototype.downloadModel = function ( geometryBase, materialBase, model, callback ) {
+
+    var meshCallback = this.createMeshCallback( materialBase, model, callback );
+    this.downloadMeshes( geometryBase, model.urls, model.decodeParams, meshCallback );
+
+};
+
+THREE.UTF8Loader.prototype.downloadModelJson = function ( jsonUrl, options, callback ) {
+
+    getJsonRequest( jsonUrl, function( loaded ) {
+
+        if ( ! loaded.decodeParams ) {
+
+            if ( options && options.decodeParams ) {
+
+                loaded.decodeParams = options.decodeParams;
+
+            } else {
+
+                loaded.decodeParams = DEFAULT_DECODE_PARAMS;
+
+            }
+
+        }
+
+        loaded.options = options;
+
+        var geometryBase = jsonUrl.substr( 0, jsonUrl.lastIndexOf( "/" ) + 1 );
+        var materialBase = geometryBase;
+
+        if ( options && options.geometryBase ) {
 
-		var nax = normals[ nai * 3     ],
-			nay = normals[ nai * 3 + 1 ],
-			naz = normals[ nai * 3 + 2 ],
+            geometryBase = options.geometryBase;
 
-			nbx = normals[ nbi * 3     ],
-			nby = normals[ nbi * 3 + 1 ],
-			nbz = normals[ nbi * 3 + 2 ],
+            if ( geometryBase.charAt( geometryBase.length - 1 ) !== "/" ) {
 
-			ncx = normals[ nci * 3     ],
-			ncy = normals[ nci * 3 + 1 ],
-			ncz = normals[ nci * 3 + 2 ];
+                geometryBase = geometryBase + "/";
 
-		var na = new THREE.Vector3( nax, nay, naz ),
-			nb = new THREE.Vector3( nbx, nby, nbz ),
-			nc = new THREE.Vector3( ncx, ncy, ncz );
+            }
 
-		scope.faces.push( new THREE.Face3( a, b, c, [ na, nb, nc ], null, mi ) );
+        }
 
-	};
+        if ( options && options.materialBase ) {
 
-	function uv3 ( where, u1, v1, u2, v2, u3, v3 ) {
+            materialBase = options.materialBase;
 
-		var uv = [];
-		uv.push( new THREE.UV( u1, v1 ) );
-		uv.push( new THREE.UV( u2, v2 ) );
-		uv.push( new THREE.UV( u3, v3 ) );
-		where.push( uv );
+            if ( materialBase.charAt( materialBase.length - 1 ) !== "/" ) {
 
-	};
+                materialBase = materialBase  + "/";
 
-	Model.prototype = Object.create( THREE.Geometry.prototype );
+            }
 
-	return new Model();
+        }
+
+        this.downloadModel( geometryBase, materialBase, loaded, callback );
+
+    }.bind( this ) );
 
 };
+
+// XMLHttpRequest stuff
+
+function getHttpRequest( url, onload, opt_onprogress ) {
+
+    var LISTENERS = {
+
+        load: function( e ) { onload( req, e ); },
+        progress: function( e ) { opt_onprogress( req, e ); }
+
+    };
+
+    var req = new XMLHttpRequest();
+    addListeners( req, LISTENERS );
+
+    req.open( 'GET', url, true );
+    req.send( null );
+}
+
+function getJsonRequest( url, onjson ) {
+
+    getHttpRequest( url,
+        function( e ) { onjson( JSON.parse( e.responseText ) ); },
+        function() {} );
+
+}
+
+function addListeners( dom, listeners ) {
+
+    // TODO: handle event capture, object binding.
+
+    for ( var key in listeners ) {
+
+        dom.addEventListener( key, listeners[ key ] );
+
+    }
+}

+ 0 - 960
examples/js/loaders/UTF8v2Loader.js

@@ -1,960 +0,0 @@
-/**
- * Loader for UTF8 version2 (after r51) encoded models generated by:
- *	http://code.google.com/p/webgl-loader/
- *
- * Code to load/decompress mesh is taken from r100 of this webgl-loader
- */
-
-THREE.UTF8v2Loader = function () {};
-
-/**
- * Load UTF8 encoded model
- * @param jsonUrl - URL from which to load json containing information about model
- * @param callback - Callback(THREE.Object3D) on successful loading of model
- * @param options - options on how to load model (see THREE.MTLLoader.MaterialCreator for basic options)
- *                  Additional options include
- *                   geometryBase: Base url from which to load referenced geometries
- *                   materialBase: Base url from which to load referenced textures
- */
-
-THREE.UTF8v2Loader.prototype.load = function ( jsonUrl, callback, options ) {
-
-    this.downloadModelJson( jsonUrl, options, callback );
-
-};
-
-// BufferGeometryCreator
-
-THREE.UTF8v2Loader.BufferGeometryCreator = function () {
-};
-
-THREE.UTF8v2Loader.BufferGeometryCreator.prototype.create = function ( attribArray, indexArray ) {
-
-	var ntris = indexArray.length / 3;
-
-	var geometry = new THREE.BufferGeometry();
-
-	var positionArray = new Float32Array( 3 * 3 * ntris );
-	var normalArray = new Float32Array( 3 * 3 * ntris );
-	var uvArray = new Float32Array( 2 * 3 * ntris );
-
-	var i, j, offset;
-	var x, y, z;
-	var u, v;
-
-	var end = attribArray.length;
-	var stride = 8;
-
-	// extract positions
-
-	j = 0;
-	offset = 0;
-
-	for( i = offset; i < end; i += stride ) {
-
-		x = attribArray[ i ];
-		y = attribArray[ i + 1 ];
-		z = attribArray[ i + 2 ];
-
-		positionArray[ j++ ] = x;
-		positionArray[ j++ ] = y;
-		positionArray[ j++ ] = z;
-
-	}
-
-	// extract uvs
-
-	j = 0;
-	offset = 3;
-
-	for( i = offset; i < end; i += stride ) {
-
-		u = attribArray[ i ];
-		v = attribArray[ i + 1 ];
-
-		uvArray[ j++ ] = u;
-		uvArray[ j++ ] = v;
-
-	}
-
-	// extract normals
-
-	j = 0;
-	offset = 5;
-
-	for( i = offset; i < end; i += stride ) {
-
-		x = attribArray[ i ];
-		y = attribArray[ i + 1 ];
-		z = attribArray[ i + 2 ];
-
-		normalArray[ j++ ] = x;
-		normalArray[ j++ ] = y;
-		normalArray[ j++ ] = z;
-
-	}
-
-	// create attributes
-
-	var attributes = geometry.attributes;
-
-	attributes[ "index" ]    = { itemSize: 1, array: indexArray, numItems: indexArray.length };
-	attributes[ "position" ] = { itemSize: 3, array: positionArray, numItems: positionArray.length };
-	attributes[ "normal" ]   = { itemSize: 3, array: normalArray, numItems: normalArray.length };
-	attributes[ "uv" ] 		 = { itemSize: 2, array: uvArray, numItems: uvArray.length };
-
-	// create offsets
-	// (all triangles should fit in a single chunk)
-
-	geometry.offsets = [ { start: 0, count: indexArray.length, index: 0 } ];
-
-	geometry.computeBoundingSphere();
-
-	return geometry;
-
-};
-
-// GeometryCreator
-
-THREE.UTF8v2Loader.GeometryCreator = function () {
-};
-
-THREE.UTF8v2Loader.GeometryCreator.prototype = {
-
-    create: function ( attribArray, indexArray ) {
-
-        var geometry = new THREE.Geometry();
-
-        this.init_vertices( geometry, attribArray, 8, 0 );
-
-        var uvs = this.init_uvs( attribArray, 8, 3 );
-        var normals = this.init_normals( attribArray, 8, 5 );
-
-        this.init_faces( geometry, normals, uvs, indexArray );
-
-        geometry.computeCentroids();
-        geometry.computeFaceNormals();
-
-        return geometry;
-
-    },
-
-    init_vertices: function ( scope, data, stride, offset ) {
-
-        var i, x, y, z;
-		var end = data.length;
-
-        for( i = offset; i < end; i += stride ) {
-
-            x = data[ i ];
-            y = data[ i + 1 ];
-            z = data[ i + 2 ];
-
-            this.addVertex( scope, x, y, z );
-
-        }
-
-    },
-
-    init_normals: function( data, stride, offset ) {
-
-        var normals = [];
-
-        var i, x, y, z;
-		var end = data.length;
-
-        for( i = offset; i < end; i += stride ) {
-
-            // Assumes already normalized to <-1,1> (unlike previous version of UTF8Loader)
-
-            x = data[ i ];
-            y = data[ i + 1 ];
-            z = data[ i + 2 ];
-
-            normals.push( x, y, z );
-
-        }
-
-        return normals;
-
-    },
-
-    init_uvs: function( data, stride, offset ) {
-
-        var uvs = [];
-
-        var i, u, v;
-		var end = data.length;
-
-        for( i = offset; i < end; i += stride ) {
-
-            // Assumes uvs are already normalized (unlike previous version of UTF8Loader)
-            // uvs can be negative, need to set wrap for texture map later on ...
-
-            u = data[ i ];
-            v = data[ i + 1 ];
-
-            uvs.push( u, v );
-        }
-
-        return uvs;
-
-    },
-
-    init_faces: function( scope, normals, uvs, indices ) {
-
-        var i,
-            a, b, c,
-            u1, v1, u2, v2, u3, v3;
-
-		var end = indices.length;
-
-        var m = 0; // all faces defaulting to material 0
-
-        for( i = 0; i < end; i += 3 ) {
-
-            a = indices[ i ];
-            b = indices[ i + 1 ];
-            c = indices[ i + 2 ];
-
-            this.f3n( scope, normals, a, b, c, m, a, b, c );
-
-            u1 = uvs[ a * 2 ];
-            v1 = uvs[ a * 2 + 1 ];
-
-            u2 = uvs[ b * 2 ];
-            v2 = uvs[ b * 2 + 1 ];
-
-            u3 = uvs[ c * 2 ];
-            v3 = uvs[ c * 2 + 1 ];
-
-            this.uv3( scope.faceVertexUvs[ 0 ], u1, v1, u2, v2, u3, v3 );
-
-        }
-
-    },
-
-    addVertex: function ( scope, x, y, z ) {
-
-        scope.vertices.push( new THREE.Vector3( x, y, z ) );
-
-    },
-
-    f3n: function( scope, normals, a, b, c, mi, nai, nbi, nci ) {
-
-        var nax = normals[ nai * 3 ],
-            nay = normals[ nai * 3 + 1 ],
-            naz = normals[ nai * 3 + 2 ],
-
-            nbx = normals[ nbi * 3 ],
-            nby = normals[ nbi * 3 + 1 ],
-            nbz = normals[ nbi * 3 + 2 ],
-
-            ncx = normals[ nci * 3 ],
-            ncy = normals[ nci * 3 + 1 ],
-            ncz = normals[ nci * 3 + 2 ];
-
-        var na = new THREE.Vector3( nax, nay, naz ),
-            nb = new THREE.Vector3( nbx, nby, nbz ),
-            nc = new THREE.Vector3( ncx, ncy, ncz );
-
-        scope.faces.push( new THREE.Face3( a, b, c, [ na, nb, nc ], null, mi ) );
-
-    },
-
-    uv3: function ( where, u1, v1, u2, v2, u3, v3 ) {
-
-        var uv = [];
-        uv.push( new THREE.UV( u1, v1 ) );
-        uv.push( new THREE.UV( u2, v2 ) );
-        uv.push( new THREE.UV( u3, v3 ) );
-        where.push( uv );
-
-    }
-
-};
-
-
-// UTF-8 decoder from webgl-loader (r100)
-// http://code.google.com/p/webgl-loader/
-
-// Model manifest description. Contains objects like:
-// name: {
-//   materials: { 'material_name': { ... } ... },
-//   decodeParams: {
-//     decodeOffsets: [ ... ],
-//     decodeScales: [ ... ],
-//   },
-//   urls: {
-//     'url': [
-//       { material: 'material_name',
-//         attribRange: [#, #],
-//         indexRange: [#, #],
-//         names: [ 'object names' ... ],
-//         lengths: [#, #, # ... ]
-//       }
-//     ],
-//     ...
-//   }
-// }
-
-var DEFAULT_DECODE_PARAMS = {
-
-    decodeOffsets: [-4095, -4095, -4095, 0, 0, -511, -511, -511],
-    decodeScales: [1/8191, 1/8191, 1/8191, 1/1023, 1/1023, 1/1023, 1/1023, 1/1023]
-
-    // TODO: normal decoding? (see walt.js)
-    // needs to know: input, output (from vertex format!)
-    //
-    // Should split attrib/index.
-    // 1) Decode position and non-normal attributes.
-    // 2) Decode indices, computing normals
-    // 3) Maybe normalize normals? Only necessary for refinement, or fixed?
-    // 4) Maybe refine normals? Should this be part of regular refinement?
-    // 5) Morphing
-
-};
-
-// Triangle strips!
-
-// TODO: will it be an optimization to specialize this method at
-// runtime for different combinations of stride, decodeOffset and
-// decodeScale?
-
-THREE.UTF8v2Loader.prototype.decompressAttribsInner_ = function ( str, inputStart, inputEnd,
-                                                                  output, outputStart, stride,
-                                                                  decodeOffset, decodeScale ) {
-
-    var prev = 0;
-
-    for ( var j = inputStart; j < inputEnd; j ++ ) {
-
-        var code = str.charCodeAt( j );
-        prev += ( code >> 1 ) ^ ( -( code & 1 ) );
-
-        output[ outputStart ] = decodeScale * ( prev + decodeOffset );
-        outputStart += stride;
-
-    }
-
-};
-
-THREE.UTF8v2Loader.prototype.decompressIndices_ = function( str, inputStart, numIndices,
-                                                            output, outputStart ) {
-
-	var highest = 0;
-
-    for ( var i = 0; i < numIndices; i ++ ) {
-
-        var code = str.charCodeAt( inputStart ++ );
-
-        output[ outputStart ++ ] = highest - code;
-
-        if ( code === 0 ) {
-
-            highest ++;
-
-        }
-
-    }
-
-};
-
-THREE.UTF8v2Loader.prototype.decompressAABBs_ = function ( str, inputStart, numBBoxen,
-                                                           decodeOffsets, decodeScales ) {
-    var numFloats = 6 * numBBoxen;
-
-    var inputEnd = inputStart + numFloats;
-    var outputStart = 0;
-
-    var bboxen = new Float32Array( numFloats );
-
-    for ( var i = inputStart; i < inputEnd; i += 6 ) {
-
-        var minX = str.charCodeAt(i + 0) + decodeOffsets[0];
-        var minY = str.charCodeAt(i + 1) + decodeOffsets[1];
-        var minZ = str.charCodeAt(i + 2) + decodeOffsets[2];
-
-        var radiusX = (str.charCodeAt(i + 3) + 1) >> 1;
-        var radiusY = (str.charCodeAt(i + 4) + 1) >> 1;
-        var radiusZ = (str.charCodeAt(i + 5) + 1) >> 1;
-
-        bboxen[ outputStart++ ] = decodeScales[0] * (minX + radiusX);
-        bboxen[ outputStart++ ] = decodeScales[1] * (minY + radiusY);
-        bboxen[ outputStart++ ] = decodeScales[2] * (minZ + radiusZ);
-
-        bboxen[ outputStart++ ] = decodeScales[0] * radiusX;
-        bboxen[ outputStart++ ] = decodeScales[1] * radiusY;
-        bboxen[ outputStart++ ] = decodeScales[2] * radiusZ;
-
-    }
-
-    return bboxen;
-
-};
-
-THREE.UTF8v2Loader.prototype.decompressMesh =  function ( str, meshParams, decodeParams, name, idx, callback ) {
-
-    // Extract conversion parameters from attribArrays.
-
-    var stride = decodeParams.decodeScales.length;
-
-    var decodeOffsets = decodeParams.decodeOffsets;
-    var decodeScales = decodeParams.decodeScales;
-
-    var attribStart = meshParams.attribRange[0];
-    var numVerts = meshParams.attribRange[1];
-
-    // Decode attributes.
-
-    var inputOffset = attribStart;
-    var attribsOut = new Float32Array( stride * numVerts );
-
-    for (var j = 0; j < stride; j ++ ) {
-
-        var end = inputOffset + numVerts;
-
-		var decodeScale = decodeScales[j];
-
-        if ( decodeScale ) {
-
-            // Assume if decodeScale is never set, simply ignore the
-            // attribute.
-
-            this.decompressAttribsInner_( str, inputOffset, end,
-                attribsOut, j, stride,
-                decodeOffsets[j], decodeScale );
-        }
-
-        inputOffset = end;
-
-    }
-
-    var indexStart = meshParams.indexRange[ 0 ];
-    var numIndices = 3 * meshParams.indexRange[ 1 ];
-
-    var indicesOut = new Uint16Array( numIndices );
-
-    this.decompressIndices_( str, inputOffset, numIndices, indicesOut, 0 );
-
-    // Decode bboxen.
-
-    var bboxen = undefined;
-    var bboxOffset = meshParams.bboxes;
-
-    if ( bboxOffset ) {
-
-        bboxen = this.decompressAABBs_( str, bboxOffset, meshParams.names.length, decodeOffsets, decodeScales );
-    }
-
-    callback( name, idx, attribsOut, indicesOut, bboxen, meshParams );
-
-};
-
-THREE.UTF8v2Loader.prototype.copyAttrib = function ( stride, attribsOutFixed, lastAttrib, index ) {
-
-    for ( var j = 0; j < stride; j ++ ) {
-
-        lastAttrib[ j ] = attribsOutFixed[ stride * index + j ];
-
-    }
-
-};
-
-THREE.UTF8v2Loader.prototype.decodeAttrib2 = function ( str, stride, decodeOffsets, decodeScales, deltaStart,
-                                                        numVerts, attribsOut, attribsOutFixed, lastAttrib,
-                                                        index ) {
-
-    for ( var j = 0; j < 5; j ++ ) {
-
-        var code = str.charCodeAt( deltaStart + numVerts*j + index );
-        var delta = ( code >> 1) ^ (-(code & 1));
-
-        lastAttrib[ j ] += delta;
-        attribsOutFixed[ stride * index + j ] = lastAttrib[ j ];
-        attribsOut[ stride * index + j ] = decodeScales[ j ] * ( lastAttrib[ j ] + decodeOffsets[ j ] );
-    }
-
-};
-
-THREE.UTF8v2Loader.prototype.accumulateNormal = function ( i0, i1, i2, attribsOutFixed, crosses ) {
-
-    var p0x = attribsOutFixed[ 8*i0 ];
-    var p0y = attribsOutFixed[ 8*i0 + 1 ];
-    var p0z = attribsOutFixed[ 8*i0 + 2 ];
-
-    var p1x = attribsOutFixed[ 8*i1 ];
-    var p1y = attribsOutFixed[ 8*i1 + 1 ];
-    var p1z = attribsOutFixed[ 8*i1 + 2 ];
-
-    var p2x = attribsOutFixed[ 8*i2 ];
-    var p2y = attribsOutFixed[ 8*i2 + 1 ];
-    var p2z = attribsOutFixed[ 8*i2 + 2 ];
-
-    p1x -= p0x;
-    p1y -= p0y;
-    p1z -= p0z;
-
-    p2x -= p0x;
-    p2y -= p0y;
-    p2z -= p0z;
-
-    p0x = p1y*p2z - p1z*p2y;
-    p0y = p1z*p2x - p1x*p2z;
-    p0z = p1x*p2y - p1y*p2x;
-
-    crosses[ 3*i0 ]     += p0x;
-    crosses[ 3*i0 + 1 ] += p0y;
-    crosses[ 3*i0 + 2 ] += p0z;
-
-    crosses[ 3*i1 ]     += p0x;
-    crosses[ 3*i1 + 1 ] += p0y;
-    crosses[ 3*i1 + 2 ] += p0z;
-
-    crosses[ 3*i2 ]     += p0x;
-    crosses[ 3*i2 + 1 ] += p0y;
-    crosses[ 3*i2 + 2 ] += p0z;
-
-};
-
-THREE.UTF8v2Loader.prototype.decompressMesh2 = function( str, meshParams, decodeParams, name, idx, callback ) {
-
-    var MAX_BACKREF = 96;
-
-    // Extract conversion parameters from attribArrays.
-
-    var stride = decodeParams.decodeScales.length;
-
-	var decodeOffsets = decodeParams.decodeOffsets;
-    var decodeScales = decodeParams.decodeScales;
-
-    var deltaStart = meshParams.attribRange[ 0 ];
-    var numVerts = meshParams.attribRange[ 1 ];
-
-    var codeStart = meshParams.codeRange[ 0 ];
-    var codeLength = meshParams.codeRange[ 1 ];
-
-    var numIndices = 3 * meshParams.codeRange[ 2 ];
-
-    var indicesOut = new Uint16Array( numIndices );
-
-    var crosses = new Int32Array( 3 * numVerts );
-
-    var lastAttrib = new Uint16Array( stride );
-
-    var attribsOutFixed = new Uint16Array( stride * numVerts );
-    var attribsOut = new Float32Array( stride * numVerts );
-
-    var highest = 0;
-    var outputStart = 0;
-
-    for ( var i = 0; i < numIndices; i += 3 ) {
-
-        var code = str.charCodeAt( codeStart ++ );
-
-        var max_backref = Math.min( i, MAX_BACKREF );
-
-        if ( code < max_backref ) {
-
-            // Parallelogram
-
-            var winding = code % 3;
-            var backref = i - ( code - winding );
-            var i0, i1, i2;
-
-            switch ( winding ) {
-
-                case 0:
-
-                    i0 = indicesOut[ backref + 2 ];
-                    i1 = indicesOut[ backref + 1 ];
-                    i2 = indicesOut[ backref + 0 ];
-                    break;
-
-                case 1:
-
-                    i0 = indicesOut[ backref + 0 ];
-                    i1 = indicesOut[ backref + 2 ];
-                    i2 = indicesOut[ backref + 1 ];
-                    break;
-
-                case 2:
-
-                    i0 = indicesOut[ backref + 1 ];
-                    i1 = indicesOut[ backref + 0 ];
-                    i2 = indicesOut[ backref + 2 ];
-                    break;
-
-            }
-
-            indicesOut[ outputStart ++ ] = i0;
-            indicesOut[ outputStart ++ ] = i1;
-
-            code = str.charCodeAt( codeStart ++ );
-
-            var index = highest - code;
-            indicesOut[ outputStart ++ ] = index;
-
-            if ( code === 0 ) {
-
-                for (var j = 0; j < 5; j ++ ) {
-
-                    var deltaCode = str.charCodeAt( deltaStart + numVerts * j + highest );
-
-                    var prediction = ((deltaCode >> 1) ^ (-(deltaCode & 1))) +
-                        attribsOutFixed[stride*i0 + j] +
-                        attribsOutFixed[stride*i1 + j] -
-                        attribsOutFixed[stride*i2 + j];
-
-                    lastAttrib[j] = prediction;
-
-                    attribsOutFixed[ stride * highest + j ] = prediction;
-                    attribsOut[ stride * highest + j ] = decodeScales[ j ] * ( prediction + decodeOffsets[ j ] );
-
-                }
-
-                highest ++;
-
-            } else {
-
-                this.copyAttrib( stride, attribsOutFixed, lastAttrib, index );
-
-            }
-
-            this.accumulateNormal( i0, i1, index, attribsOutFixed, crosses );
-
-        } else {
-
-            // Simple
-
-            var index0 = highest - ( code - max_backref );
-
-            indicesOut[ outputStart ++ ] = index0;
-
-            if ( code === max_backref ) {
-
-                this.decodeAttrib2( str, stride, decodeOffsets, decodeScales, deltaStart,
-                    numVerts, attribsOut, attribsOutFixed, lastAttrib,
-                    highest ++ );
-
-            } else {
-
-                this.copyAttrib(stride, attribsOutFixed, lastAttrib, index0);
-
-            }
-
-            code = str.charCodeAt( codeStart ++ );
-
-            var index1 = highest - code;
-            indicesOut[ outputStart ++ ] = index1;
-
-            if ( code === 0 ) {
-
-                this.decodeAttrib2( str, stride, decodeOffsets, decodeScales, deltaStart,
-                    numVerts, attribsOut, attribsOutFixed, lastAttrib,
-                    highest ++ );
-
-            } else {
-
-                this.copyAttrib( stride, attribsOutFixed, lastAttrib, index1 );
-
-            }
-
-            code = str.charCodeAt( codeStart ++ );
-
-            var index2 = highest - code;
-            indicesOut[ outputStart ++ ] = index2;
-
-            if ( code === 0 ) {
-
-                for ( var j = 0; j < 5; j ++ ) {
-
-                    lastAttrib[ j ] = ( attribsOutFixed[ stride * index0 + j ] + attribsOutFixed[ stride * index1 + j ] ) / 2;
-
-                }
-
-                this.decodeAttrib2( str, stride, decodeOffsets, decodeScales, deltaStart,
-                    numVerts, attribsOut, attribsOutFixed, lastAttrib,
-                    highest ++ );
-
-            } else {
-
-                this.copyAttrib( stride, attribsOutFixed, lastAttrib, index2 );
-
-            }
-
-            this.accumulateNormal( index0, index1, index2, attribsOutFixed, crosses );
-
-        }
-
-    }
-
-    for ( var i = 0; i < numVerts; i ++ ) {
-
-        var nx = crosses[ 3*i ];
-        var ny = crosses[ 3*i + 1 ];
-        var nz = crosses[ 3*i + 2 ];
-
-        var norm = 511.0 / Math.sqrt( nx*nx + ny*ny + nz*nz );
-
-        var cx = str.charCodeAt( deltaStart + 5*numVerts + i );
-        var cy = str.charCodeAt( deltaStart + 6*numVerts + i );
-        var cz = str.charCodeAt( deltaStart + 7*numVerts + i );
-
-        attribsOut[ stride*i + 5 ] = norm*nx + ((cx >> 1) ^ (-(cx & 1)));
-        attribsOut[ stride*i + 6 ] = norm*ny + ((cy >> 1) ^ (-(cy & 1)));
-        attribsOut[ stride*i + 7 ] = norm*nz + ((cz >> 1) ^ (-(cz & 1)));
-    }
-
-    callback( name, idx, attribsOut, indicesOut, undefined, meshParams );
-
-};
-
-THREE.UTF8v2Loader.prototype.downloadMesh = function ( path, name, meshEntry, decodeParams, callback ) {
-
-    var loader = this;
-    var idx = 0;
-
-    function onprogress( req, e ) {
-
-        while ( idx < meshEntry.length ) {
-
-            var meshParams = meshEntry[ idx ];
-            var indexRange = meshParams.indexRange;
-
-            if ( indexRange ) {
-
-                var meshEnd = indexRange[ 0 ] + 3 * indexRange[ 1 ];
-
-                if ( req.responseText.length < meshEnd ) break;
-
-                loader.decompressMesh( req.responseText, meshParams, decodeParams, name, idx, callback );
-
-            } else {
-
-                var codeRange = meshParams.codeRange;
-                var meshEnd = codeRange[ 0 ] + codeRange[ 1 ];
-
-                if ( req.responseText.length < meshEnd ) break;
-
-                loader.decompressMesh2( req.responseText, meshParams, decodeParams, name, idx, callback );
-            }
-
-            ++idx;
-
-        }
-
-    };
-
-    getHttpRequest( path, function( req, e ) {
-
-        if ( req.status === 200 || req.status === 0 ) {
-
-            onprogress( req, e );
-
-        }
-
-        // TODO: handle errors.
-
-    }, onprogress );
-
-};
-
-THREE.UTF8v2Loader.prototype.downloadMeshes = function ( path, meshUrlMap, decodeParams, callback ) {
-
-    for ( var url in meshUrlMap ) {
-
-        var meshEntry = meshUrlMap[url];
-        this.downloadMesh( path + url, url, meshEntry, decodeParams, callback );
-
-    }
-
-};
-
-THREE.UTF8v2Loader.prototype.createMeshCallback = function( materialBaseUrl, loadModelInfo, allDoneCallback ) {
-
-	var nCompletedUrls = 0;
-    var nExpectedUrls = 0;
-
-    var expectedMeshesPerUrl = {};
-    var decodedMeshesPerUrl = {};
-
-	var modelParts = {};
-
-	var meshUrlMap = loadModelInfo.urls;
-
-    for ( var url in meshUrlMap ) {
-
-        expectedMeshesPerUrl[ url ] = meshUrlMap[ url ].length;
-        decodedMeshesPerUrl[ url ] = 0;
-
-		nExpectedUrls ++;
-
-        modelParts[ url ] = new THREE.Object3D();
-
-    }
-
-    var model = new THREE.Object3D();
-
-    // Prepare materials first...
-
-    var materialCreator = new THREE.MTLLoader.MaterialCreator( materialBaseUrl, loadModelInfo.options );
-    materialCreator.setMaterials( loadModelInfo.materials );
-
-    materialCreator.preload();
-
-	// Create callback for creating mesh parts
-
-    var geometryCreator = new THREE.UTF8v2Loader.GeometryCreator();
-	var bufferGeometryCreator = new THREE.UTF8v2Loader.BufferGeometryCreator();
-
-	var meshCallback = function( name, idx, attribArray, indexArray, bboxen, meshParams ) {
-
-        // Got ourselves a new mesh
-
-        // name identifies this part of the model (url)
-        // idx is the mesh index of this mesh of the part
-        // attribArray defines the vertices
-        // indexArray defines the faces
-        // bboxen defines the bounding box
-        // meshParams contains the material info
-
-		var useBuffers = loadModelInfo.options.useBuffers !== undefined ? loadModelInfo.options.useBuffers : true;
-
-		if ( useBuffers ) {
-
-			var geometry = bufferGeometryCreator.create( attribArray, indexArray );
-
-		} else {
-
-			var geometry = geometryCreator.create( attribArray, indexArray );
-
-		}
-
-        var material = materialCreator.create( meshParams.material );
-
-		var mesh = new THREE.Mesh( geometry, material );
-        modelParts[ name ].add( mesh );
-
-        //model.add(new THREE.Mesh(geometry, material));
-
-        decodedMeshesPerUrl[ name ] ++;
-
-        if ( decodedMeshesPerUrl[ name ] === expectedMeshesPerUrl[ name ] ) {
-
-            nCompletedUrls ++;
-
-            model.add( modelParts[ name ] );
-
-            if ( nCompletedUrls === nExpectedUrls ) {
-
-                // ALL DONE!!!
-
-                allDoneCallback( model );
-
-            }
-
-        }
-
-    };
-
-	return meshCallback;
-
-};
-
-THREE.UTF8v2Loader.prototype.downloadModel = function ( geometryBase, materialBase, model, callback ) {
-
-    var meshCallback = this.createMeshCallback( materialBase, model, callback );
-    this.downloadMeshes( geometryBase, model.urls, model.decodeParams, meshCallback );
-
-};
-
-THREE.UTF8v2Loader.prototype.downloadModelJson = function ( jsonUrl, options, callback ) {
-
-    getJsonRequest( jsonUrl, function( loaded ) {
-
-        if ( ! loaded.decodeParams ) {
-
-            if ( options && options.decodeParams ) {
-
-                loaded.decodeParams = options.decodeParams;
-
-            } else {
-
-                loaded.decodeParams = DEFAULT_DECODE_PARAMS;
-
-            }
-
-        }
-
-        loaded.options = options;
-
-        var geometryBase = jsonUrl.substr( 0, jsonUrl.lastIndexOf( "/" ) + 1 );
-        var materialBase = geometryBase;
-
-        if ( options && options.geometryBase ) {
-
-            geometryBase = options.geometryBase;
-
-            if ( geometryBase.charAt( geometryBase.length - 1 ) !== "/" ) {
-
-                geometryBase = geometryBase + "/";
-
-            }
-
-        }
-
-        if ( options && options.materialBase ) {
-
-            materialBase = options.materialBase;
-
-            if ( materialBase.charAt( materialBase.length - 1 ) !== "/" ) {
-
-                materialBase = materialBase  + "/";
-
-            }
-
-        }
-
-        this.downloadModel( geometryBase, materialBase, loaded, callback );
-
-    }.bind( this ) );
-
-};
-
-// XMLHttpRequest stuff
-
-function getHttpRequest( url, onload, opt_onprogress ) {
-
-    var LISTENERS = {
-
-        load: function( e ) { onload( req, e ); },
-        progress: function( e ) { opt_onprogress( req, e ); }
-
-    };
-
-    var req = new XMLHttpRequest();
-    addListeners( req, LISTENERS );
-
-    req.open( 'GET', url, true );
-    req.send( null );
-}
-
-function getJsonRequest( url, onjson ) {
-
-    getHttpRequest( url,
-        function( e ) { onjson( JSON.parse( e.responseText ) ); },
-        function() {} );
-
-}
-
-function addListeners( dom, listeners ) {
-
-    // TODO: handle event capture, object binding.
-
-    for ( var key in listeners ) {
-
-        dom.addEventListener( key, listeners[ key ] );
-
-    }
-}

+ 0 - 0
examples/models/utf8_r104/James_Body_Lores.jpg → examples/models/utf8/James_Body_Lores.jpg


+ 0 - 0
examples/models/utf8_r104/James_EyeLashBotTran.png → examples/models/utf8/James_EyeLashBotTran.png


+ 0 - 0
examples/models/utf8_r104/James_EyeLashTopTran.png → examples/models/utf8/James_EyeLashTopTran.png


+ 0 - 0
examples/models/utf8_r104/James_Eye_Green.jpg → examples/models/utf8/James_Eye_Green.jpg


+ 0 - 0
examples/models/utf8_r104/James_Eye_Inner_Green.jpg → examples/models/utf8/James_Eye_Inner_Green.jpg


+ 0 - 0
examples/models/utf8_r104/James_Face_Color_Hair_Lores.jpg → examples/models/utf8/James_Face_Color_Hair_Lores.jpg


+ 0 - 0
examples/models/utf8_r104/James_Mouth_Gum_Lores.jpg → examples/models/utf8/James_Mouth_Gum_Lores.jpg


+ 0 - 0
examples/models/utf8_r104/James_Tongue_Lores.jpg → examples/models/utf8/James_Tongue_Lores.jpg


+ 0 - 0
examples/models/utf8_r104/MCasShoe1TEX_Lores.jpg → examples/models/utf8/MCasShoe1TEX_Lores.jpg


+ 0 - 0
examples/models/utf8_r104/MJeans1TEX_Lores.jpg → examples/models/utf8/MJeans1TEX_Lores.jpg


+ 0 - 0
examples/models/utf8_r104/MTshirt3TEX_Lores.jpg → examples/models/utf8/MTshirt3TEX_Lores.jpg


+ 0 - 0
examples/models/utf8_r104/Nail_Hand_01_Lores.jpg → examples/models/utf8/Nail_Hand_01_Lores.jpg


+ 0 - 0
examples/models/utf8_r104/ben.js → examples/models/utf8/ben.js


BIN
examples/models/utf8/ben.utf8


+ 107 - 0
examples/models/utf8/ben_dds.js

@@ -0,0 +1,107 @@
+{
+  "materials": {
+    "gums": { "map_Kd": "dds/James_Mouth_Gum_Lores.dds" },
+    "tongue": { "map_Kd": "dds/James_Tongue_Lores.dds" },
+    "teethbottom": { "Kd": [251, 248, 248] },
+    "teethtop": { "Kd": [251, 248, 248] },
+    "topeyelashes": { "Kd": [66, 52, 42], "map_Kd": "dds/James_EyeLashTopTran.dds", "d": 0.999 },
+    "bottomeyelashes": { "Kd": [66, 52, 42], "map_Kd": "dds/James_EyeLashBotTran.dds", "d": 0.999 },
+    "head": { "map_Kd": "dds/James_Face_Color_Hair_Lores.dds", "Ks": [25,25,25], "Ns": 70 },
+    "eyetrans": { "Kd": [0, 0, 0] },
+    "pupil": { "Kd": [1, 1, 1] },
+    "iris": { "map_Kd": "dds/James_Eye_Inner_Green.dds" },
+    "eyeball": { "map_Kd": "dds/James_Eye_Green.dds" },
+    "pants": { "map_Kd": "dds/MJeans1TEX_Lores.dds", "Ks": [30,30,30], "Ns": 20 },
+    "tshirt3": { "map_Kd": "dds/MTshirt3TEX_Lores.dds", "Ks": [30,30,30], "Ns": 20 },
+    "skinbody": { "map_Kd": "dds/James_Body_Lores.dds", "Ks": [25,25,25], "Ns": 70 },
+    "fingernails": { "map_Kd": "dds/Nail_Hand_01_Lores.dds" },
+    "soleshoe": { "map_Kd": "dds/MCasShoe1TEX_Lores.dds" },
+    "sole": { "map_Kd": "dds/MCasShoe1TEX_Lores.dds" },
+    "laces": { "map_Kd": "dds/MCasShoe1TEX_Lores.dds" },
+    "bow": { "map_Kd": "dds/MCasShoe1TEX_Lores.dds" }
+  },
+  "decodeParams": {
+    "decodeOffsets": [-2533,-149,-6225,0,0,-511,-511,-511],
+    "decodeScales": [0.000043,0.000043,0.000043,0.000978,0.000978,0.001957,0.001957,0.001957]
+  },
+  "urls": {
+    "ben.utf8": [
+      { "material": "bottomeyelashes",
+        "attribRange": [0, 130],
+        "codeRange": [1040, 388, 192]
+      },
+      { "material": "bow",
+        "attribRange": [1428, 1848],
+        "codeRange": [16212, 6457, 3224]
+      },
+      { "material": "eyeball",
+        "attribRange": [22669, 3834],
+        "codeRange": [53341, 14697, 7284]
+      },
+      { "material": "eyetrans",
+        "attribRange": [68038, 5248],
+        "codeRange": [110022, 20180, 9964]
+      },
+      { "material": "fingernails",
+        "attribRange": [130202, 1023],
+        "codeRange": [138386, 2669, 1228]
+      },
+      { "material": "gums",
+        "attribRange": [141055, 1446],
+        "codeRange": [152623, 5270, 2624]
+      },
+      { "material": "head",
+        "attribRange": [157893, 2219],
+        "codeRange": [175645, 8353, 4168]
+      },
+      { "material": "iris",
+        "attribRange": [183998, 902],
+        "codeRange": [191214, 3332, 1664]
+      },
+      { "material": "laces",
+        "attribRange": [194546, 1016],
+        "codeRange": [202674, 3590, 1792]
+      },
+      { "material": "pants",
+        "attribRange": [206264, 8200],
+        "codeRange": [271864, 30625, 15293]
+      },
+      { "material": "pupil",
+        "attribRange": [302489, 148],
+        "codeRange": [303673, 581, 288]
+      },
+      { "material": "skinbody",
+        "attribRange": [304254, 4990],
+        "codeRange": [344174, 15770, 7830]
+      },
+      { "material": "sole",
+        "attribRange": [359944, 2588],
+        "codeRange": [380648, 9345, 4668]
+      },
+      { "material": "soleshoe",
+        "attribRange": [389993, 3164],
+        "codeRange": [415305, 10721, 5352]
+      },
+      { "material": "teethbottom",
+        "attribRange": [426026, 1235],
+        "codeRange": [435906, 3513, 1656]
+      },
+      { "material": "teethtop",
+        "attribRange": [439419, 1666],
+        "codeRange": [452747, 3937, 1816]
+      },
+      { "material": "tongue",
+        "attribRange": [456684, 845],
+        "codeRange": [463444, 3162, 1578]
+      },
+      { "material": "topeyelashes",
+        "attribRange": [466606, 130],
+        "codeRange": [467646, 388, 192]
+      },
+      { "material": "tshirt3",
+        "attribRange": [468034, 4283],
+        "codeRange": [502298, 14470, 7216]
+      }
+    ]
+  }
+}

BIN
examples/models/utf8/dds/James_Body_Lores.dds


BIN
examples/models/utf8/dds/James_EyeLashBotTran.dds


BIN
examples/models/utf8/dds/James_EyeLashTopTran.dds


BIN
examples/models/utf8/dds/James_Eye_Green.dds


BIN
examples/models/utf8/dds/James_Eye_Inner_Green.dds


BIN
examples/models/utf8/dds/James_Face_Color_Hair_Lores.dds


BIN
examples/models/utf8/dds/James_Mouth_Gum_Lores.dds


BIN
examples/models/utf8/dds/James_Tongue_Lores.dds


BIN
examples/models/utf8/dds/MCasShoe1TEX_Lores.dds


BIN
examples/models/utf8/dds/MJeans1TEX_Lores.dds


BIN
examples/models/utf8/dds/MTshirt3TEX_Lores.dds


BIN
examples/models/utf8/dds/Nail_Hand_01_Lores.dds


+ 0 - 0
examples/models/utf8_r104/hand.jpg → examples/models/utf8/hand.jpg


+ 0 - 0
examples/models/utf8_r104/hand.js → examples/models/utf8/hand.js


BIN
examples/models/utf8/hand.utf8


BIN
examples/models/utf8_r104/ben.utf8


BIN
examples/models/utf8_r104/hand.utf8


+ 0 - 2
examples/webgl_loader_scene.html

@@ -152,7 +152,6 @@
 		<script src="js/loaders/ctm/lzma.js"></script>
 		<script src="js/loaders/ctm/ctm.js"></script>
 		<script src="js/loaders/ctm/CTMLoader.js"></script>
-		<script src="js/loaders/UTF8Loader.js"></script>
 
 		<script src="js/Detector.js"></script>
 		<script src="js/Stats.js"></script>
@@ -334,7 +333,6 @@
 				var loader = new THREE.SceneLoader();
 
 				loader.addGeometryHandler( "ctm", THREE.CTMLoader );
-				loader.addGeometryHandler( "utf8", THREE.UTF8Loader );
 
 				loader.callbackSync = callbackSync;
 				loader.callbackProgress = callbackProgress;

+ 81 - 30
examples/webgl_loader_utf8.html

@@ -33,7 +33,9 @@
 		</div>
 
 		<script src="../build/three.min.js"></script>
+
 		<script src="js/loaders/UTF8Loader.js"></script>
+		<script src="js/loaders/MTLLoader.js"></script>
 
 		<script src="js/Detector.js"></script>
 		<script src="js/Stats.js"></script>
@@ -85,16 +87,33 @@
 
 				// LIGHTS
 
-				var ambient = new THREE.AmbientLight( 0x221100 );
+				var ambient = new THREE.AmbientLight( 0x222222 );
 				scene.add( ambient );
 
-				var directionalLight = new THREE.DirectionalLight( 0xffeedd );
-				directionalLight.position.set( 0, 0, 1 ).normalize();
+				var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.1 );
+				directionalLight.position.set( 0, 20, 300 );
 				scene.add( directionalLight );
 
+				directionalLight.castShadow = true;
+				//directionalLight.shadowCameraVisible = true;
+
+				directionalLight.shadowMapWidth = 2048;
+				directionalLight.shadowMaHeight = 2048;
+
+				var d = 150;
+
+				directionalLight.shadowCameraLeft = -d * 1.2;
+				directionalLight.shadowCameraRight = d * 1.2;
+				directionalLight.shadowCameraTop = d;
+				directionalLight.shadowCameraBottom = -d;
+
+				directionalLight.shadowCameraNear = 200;
+				directionalLight.shadowCameraFar = 500;
+
+
 				// RENDERER
 
-				renderer = new THREE.WebGLRenderer();
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
 				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
 
 				renderer.setClearColor( scene.fog.color, 1 );
@@ -102,6 +121,14 @@
 				renderer.domElement.style.position = "relative";
 				container.appendChild( renderer.domElement );
 
+				//
+
+				renderer.gammaInput = true;
+				renderer.gammaOutput = true;
+				renderer.physicallyBasedShading = true;
+
+				renderer.shadowMapEnabled = true;
+
 				// STATS
 
 				stats = new Stats();
@@ -110,19 +137,63 @@
 				stats.domElement.style.zIndex = 100;
 				container.appendChild( stats.domElement );
 
+				var start = Date.now();
+
 				var loader = new THREE.UTF8Loader();
 
-				loader.load( "models/utf8/hand.utf8", function ( geometry ) {
+				loader.load( "models/utf8/hand.js", function ( object ) {
+
+					var end = Date.now();
+					console.log( "hand", end - start, "ms" );
+
+					var s = 350;
+					object.scale.set( s, s, s );
+					object.position.x = 125;
+					object.position.y = -125;
+					scene.add( object );
+
+					THREE.SceneUtils.traverseHierarchy( object, function( node ) {
+
+						node.castShadow = true;
+						node.receiveShadow = true;
+
+						if ( node.material && node.material.name === "skin" ) {
 
-					callbackModel( geometry, 400, 0xffffff, 125, FLOOR, 0 );
+							node.material.wrapAround = true;
+							node.material.wrapRGB.set( 0.6, 0.2, 0.1 );
 
-				}, { scale: 0.815141, offsetX: -0.371823, offsetY: -0.011920, offsetZ: -0.416061 } );
+						}
 
-				loader.load( "models/utf8/ben.utf8", function ( geometry ) {
+					} );
 
-					callbackModel( geometry, 400, 0xffaa00, -125, FLOOR, 0 );
+				}, { normalizeRGB: true } );
 
-				}, { scale: 0.707192, offsetX: -0.109362, offsetY: -0.006435, offsetZ: -0.268751 } );
+				loader.load( "models/utf8/ben_dds.js", function ( object ) {
+
+					var end = Date.now();
+					console.log( "ben", end - start, "ms" );
+
+					var s = 350;
+					object.scale.set( s, s, s );
+					object.position.x = -125;
+					object.position.y = -125;
+					scene.add( object );
+
+					THREE.SceneUtils.traverseHierarchy( object, function( node ) {
+
+						node.castShadow = true;
+						node.receiveShadow = true;
+
+						if ( node.material && ( node.material.name === "head" || node.material.name === "skinbody" ) ) {
+
+							node.material.wrapAround = true;
+							node.material.wrapRGB.set( 0.6, 0.2, 0.1 );
+
+						}
+
+					} );
+
+				}, { normalizeRGB: true } );
 
 				//
 
@@ -142,26 +213,6 @@
 
 			}
 
-			function callbackModel( geometry, s, color, x, y, z ) {
-
-				var material = new THREE.MeshLambertMaterial( {
-					color: color,
-					map: THREE.ImageUtils.loadTexture( "textures/ash_uvgrid01.jpg" ),
-					envMap: reflectionCube,
-					combine: THREE.MixOperation,
-					reflectivity: 0.3
-				} );
-				//material.shading =  THREE.FlatShading;
-
-				var mesh = new THREE.Mesh( geometry, material );
-
-				mesh.position.set( x, y, z );
-				mesh.scale.set( s, s, s );
-
-				scene.add( mesh );
-
-			}
-
 			function onDocumentMouseMove( event ) {
 
 				mouseX = ( event.clientX - windowHalfX );

+ 0 - 247
examples/webgl_loader_utf8_r104.html

@@ -1,247 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<title>three.js webgl - io - UTF8 loader</title>
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<style>
-			body {
-				font-family: Monospace;
-				background-color: #000;
-				color: #fff;
-				margin: 0px;
-				overflow: hidden;
-			}
-			#info {
-				color: #fff;
-				position: absolute;
-				top: 10px;
-				width: 100%;
-				text-align: center;
-				z-index: 100;
-				display:block;
-			}
-			#info a, .button { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
-		</style>
-	</head>
-
-	<body>
-		<div id="info">
-		<a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> -
-		<a href="http://code.google.com/p/webgl-loader/" target="_blank">UTF8 format</a> loader test -
-		models from <a href="http://www.sci.utah.edu/~wald/animrep/" target="_blank">The Utah 3D Animation Repository</a>
-		</div>
-
-		<script src="../build/three.min.js"></script>
-		<script src="js/loaders/UTF8v2Loader.js"></script>
-		<script src="js/loaders/MTLLoader.js"></script>
-
-		<script src="js/Detector.js"></script>
-		<script src="js/Stats.js"></script>
-
-		<script>
-
-			var SCREEN_WIDTH = window.innerWidth;
-			var SCREEN_HEIGHT = window.innerHeight;
-
-			var FLOOR = -150;
-
-			var container, stats;
-
-			var camera, scene, renderer;
-
-			var mesh, zmesh, geometry;
-
-			var mouseX = 0, mouseY = 0;
-
-			var windowHalfX = window.innerWidth / 2;
-			var windowHalfY = window.innerHeight / 2;
-
-			document.addEventListener('mousemove', onDocumentMouseMove, false);
-
-			init();
-			animate();
-
-
-			function init() {
-
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
-
-				camera = new THREE.PerspectiveCamera( 20, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 2000 );
-				camera.position.z = 800;
-
-				scene = new THREE.Scene();
-				scene.fog = new THREE.Fog( 0x000000, 800, 2000 );
-
-				var path = "textures/cube/SwedishRoyalCastle/";
-				var format = '.jpg';
-				var urls = [
-					path + 'px' + format, path + 'nx' + format,
-					path + 'py' + format, path + 'ny' + format,
-					path + 'pz' + format, path + 'nz' + format
-				];
-
-				reflectionCube = THREE.ImageUtils.loadTextureCube( urls );
-
-				// LIGHTS
-
-				var ambient = new THREE.AmbientLight( 0x222222 );
-				scene.add( ambient );
-
-				var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.1 );
-				directionalLight.position.set( 0, 20, 300 );
-				scene.add( directionalLight );
-
-				directionalLight.castShadow = true;
-				//directionalLight.shadowCameraVisible = true;
-
-				directionalLight.shadowMapWidth = 2048;
-				directionalLight.shadowMaHeight = 2048;
-
-				var d = 150;
-
-				directionalLight.shadowCameraLeft = -d * 1.2;
-				directionalLight.shadowCameraRight = d * 1.2;
-				directionalLight.shadowCameraTop = d;
-				directionalLight.shadowCameraBottom = -d;
-
-				directionalLight.shadowCameraNear = 200;
-				directionalLight.shadowCameraFar = 500;
-
-
-				// RENDERER
-
-				renderer = new THREE.WebGLRenderer( { antialias: true } );
-				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-
-				renderer.setClearColor( scene.fog.color, 1 );
-
-				renderer.domElement.style.position = "relative";
-				container.appendChild( renderer.domElement );
-
-				//
-
-				renderer.gammaInput = true;
-				renderer.gammaOutput = true;
-				renderer.physicallyBasedShading = true;
-
-				renderer.shadowMapEnabled = true;
-
-				// STATS
-
-				stats = new Stats();
-				stats.domElement.style.position = 'absolute';
-				stats.domElement.style.top = '0px';
-				stats.domElement.style.zIndex = 100;
-				container.appendChild( stats.domElement );
-
-				var start = Date.now();
-
-				var loader = new THREE.UTF8v2Loader();
-
-				loader.load( "models/utf8_r104/hand.js", function ( object ) {
-
-					var end = Date.now();
-					console.log( "hand", end - start, "ms" );
-
-					var s = 350;
-					object.scale.set( s, s, s );
-					object.position.x = 125;
-					object.position.y = -125;
-					scene.add( object );
-
-					THREE.SceneUtils.traverseHierarchy( object, function( node ) {
-
-						node.castShadow = true;
-						node.receiveShadow = true;
-
-						if ( node.material && node.material.name === "skin" ) {
-
-							node.material.wrapAround = true;
-							node.material.wrapRGB.set( 0.6, 0.2, 0.1 );
-
-						}
-
-					} );
-
-				}, { normalizeRGB: true } );
-
-				loader.load( "models/utf8_r104/ben.js", function ( object ) {
-
-					var end = Date.now();
-					console.log( "ben", end - start, "ms" );
-
-					var s = 350;
-					object.scale.set( s, s, s );
-					object.position.x = -125;
-					object.position.y = -125;
-					scene.add( object );
-
-					THREE.SceneUtils.traverseHierarchy( object, function( node ) {
-
-						node.castShadow = true;
-						node.receiveShadow = true;
-
-						if ( node.material && ( node.material.name === "head" || node.material.name === "skinbody" ) ) {
-
-							node.material.wrapAround = true;
-							node.material.wrapRGB.set( 0.6, 0.2, 0.1 );
-
-						}
-
-					} );
-
-				}, { normalizeRGB: true } );
-
-				//
-
-				window.addEventListener( 'resize', onWindowResize, false );
-
-			}
-
-			function onWindowResize() {
-
-				windowHalfX = window.innerWidth / 2;
-				windowHalfY = window.innerHeight / 2;
-
-				camera.aspect = window.innerWidth / window.innerHeight;
-				camera.updateProjectionMatrix();
-
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
-			}
-
-			function onDocumentMouseMove( event ) {
-
-				mouseX = ( event.clientX - windowHalfX );
-				mouseY = ( event.clientY - windowHalfY );
-
-			}
-
-			//
-
-			function animate() {
-
-				requestAnimationFrame( animate );
-
-				render();
-				stats.update();
-
-			}
-
-			function render() {
-
-				camera.position.x += ( mouseX - camera.position.x ) * .05;
-				camera.position.y += ( - mouseY - camera.position.y ) * .05;
-
-				camera.lookAt( scene.position );
-
-				renderer.render( scene, camera );
-
-			}
-
-		</script>
-
-	</body>
-</html>

+ 3 - 2
src/extras/ImageUtils.js

@@ -266,8 +266,9 @@ THREE.ImageUtils = {
 			dds.mipmaps.push( mipmap );
 
 			dataOffset += dataLength;
-			width *= 0.5;
-			height *= 0.5;
+
+			width = Math.max( width * 0.5, 1 );
+			height = Math.max( height * 0.5, 1 );
 
 		}
 

+ 0 - 3
utils/exporters/utf8-r104/build.bat

@@ -1,3 +0,0 @@
-g++ ./src/objcompress.cc -O2 -Wall -o objcompress
-g++ ./src/obj2utf8.cc -O2 -Wall -o obj2utf8
-g++ ./src/obj2utf8x.cc -O2 -Wall -o obj2utf8x

BIN
utils/exporters/utf8-r104/objcompress.exe


+ 0 - 37
utils/exporters/utf8/README

@@ -1,37 +0,0 @@
--------------------------
----- three.js README ----
--------------------------
-
-Slightly modified "mesh.h" so that compressor:
-
-- compiles with latest MinGW on Windows 64-bit
-  (had to replace %z size_t formatting option)
-
-- dumps to stderr scaling and offset parameters
-
-
-Also for convenience added pre-build executable for Windows.
-
-Latest version of compressor can be downloaded at webgl-loader repository:
-
-http://code.google.com/p/webgl-loader/
-
---------------------------------------------
----- Original README from webgl-loader -----
---------------------------------------------
-
-Usage: ./objcompress in.obj [out.utf8]
-
-        If 'out' is specified, then attempt to write out a compressed,
-        UTF-8 version to 'out.'
-
-        If not, write a JSON version to STDOUT.
-
-
-Building:
-
-Since there are no external dependences outside of the C/C++ standard
-libraries, you can pretty much build this however you please. I've
-included a cheeky way to do this on POSIX-like systems by including a
-build shell script at the top of the file itself. You can build by
-making the .cc file executable, and running it on the command line.

+ 3 - 1
utils/exporters/utf8/build.bat

@@ -1 +1,3 @@
-g++ objcompress.cc -O2 -Wall -o objcompress
+g++ ./src/objcompress.cc -O2 -Wall -o objcompress
+g++ ./src/obj2utf8.cc -O2 -Wall -o obj2utf8
+g++ ./src/obj2utf8x.cc -O2 -Wall -o obj2utf8x

+ 0 - 787
utils/exporters/utf8/mesh.h

@@ -1,787 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you
-// may not use this file except in compliance with the License. You
-// may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-// implied. See the License for the specific language governing
-// permissions and limitations under the License.
-
-#ifndef WEBGL_LOADER_MESH_H_
-#define WEBGL_LOADER_MESH_H_
-
-#include <float.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <map>
-#include <string>
-#include <utility>
-#include <vector>
-
-typedef unsigned short uint16;
-typedef short int16;
-
-typedef std::vector<float> AttribList;
-typedef std::vector<int> IndexList;
-typedef std::vector<uint16> QuantizedAttribList;
-
-struct DrawMesh {
-  // Interleaved vertex format:
-  //  3-D Position
-  //  3-D Normal
-  //  2-D TexCoord
-  // Note that these
-  AttribList interleaved_attribs;
-  // Indices are 0-indexed.
-  IndexList triangle_indices;
-};
-
-void DumpJsonFromQuantizedAttribs(const QuantizedAttribList& attribs) {
-  puts("var attribs = new Uint16Array([");
-  for (size_t i = 0; i < attribs.size(); i += 8) {
-    printf("%u,%hu,%hu,%hu,%hu,%hu,%hu,%hu,\n",
-           attribs[i + 0], attribs[i + 1], attribs[i + 2], attribs[i + 3],
-           attribs[i + 4], attribs[i + 5], attribs[i + 6], attribs[i + 7]);
-  }
-  puts("]);");
-}
-
-void DumpJsonFromInterleavedAttribs(const AttribList& attribs) {
-  puts("var attribs = new Float32Array([");
-  for (size_t i = 0; i < attribs.size(); i += 8) {
-    printf("%f,%f,%f,%f,%f,%f,%f,%f,\n",
-           attribs[i + 0], attribs[i + 1], attribs[i + 2], attribs[i + 3],
-           attribs[i + 4], attribs[i + 5], attribs[i + 6], attribs[i + 7]);
-  }
-  puts("]);");
-}
-
-void DumpJsonFromIndices(const IndexList& indices) {
-  puts("var indices = new Uint16Array([");
-  for (size_t i = 0; i < indices.size(); i += 3) {
-    printf("%d,%d,%d,\n", indices[i + 0], indices[i + 1], indices[i + 2]);
-  }
-  puts("]);");
-}
-
-// A short list of floats, useful for parsing a single vector
-// attribute.
-class ShortFloatList {
- public:
-  static const size_t kMaxNumFloats = 4;
-  ShortFloatList()
-      : size_(0) { }
-
-  // Parse up to kMaxNumFloats from C string.
-  size_t ParseLine(const char* line) {
-    for (size_ = 0; size_ != kMaxNumFloats; ++size_) {
-      char* endptr = NULL;
-      a_[size_] = strtof(line, &endptr);
-      if (endptr == NULL || line == endptr) break;
-      line = endptr;
-    }
-    return size_;
-  }
-
-  void AppendTo(AttribList* attribs) const {
-    attribs->insert(attribs->end(), a_, a_ + size_);
-  }
-
-  bool empty() const { return size_ == 0; }
-
-  size_t size() const { return size_; }
- private:
-  float a_[kMaxNumFloats];
-  size_t size_;
-};
-
-class IndexFlattener {
- public:
-  explicit IndexFlattener(size_t num_positions)
-      : count_(0),
-        table_(num_positions) {
-  }
-
-  int count() const { return count_; }
-
-  // Returns a pair of: < flattened index, newly inserted >.
-  std::pair<int, bool> GetFlattenedIndex(int position_index,
-                                         int texcoord_index,
-                                         int normal_index) {
-    // First, optimistically look up position_index in the table.
-    IndexType& index = table_[position_index];
-    if (index.position_or_flat == kIndexUnknown) {
-      // This is the first time we've seen this position in the table,
-      // so fill it. Since the table is indexed by position, we can
-      // use the position_or_flat_index field to store the flat index.
-      const int flat_index = count_++;
-      index.position_or_flat = flat_index;
-      index.texcoord = texcoord_index;
-      index.normal = normal_index;
-      return std::make_pair(flat_index, true);
-    } else if (index.position_or_flat == kIndexNotInTable) {
-      // There are multiple flattened indices at this position index,
-      // so resort to the map.
-      return GetFlattenedIndexFromMap(position_index,
-                                      texcoord_index,
-                                      normal_index);
-    } else if (index.texcoord == texcoord_index &&
-               index.normal == normal_index) {
-      // The other indices match, so we can use the value cached in
-      // the table.
-      return std::make_pair(index.position_or_flat, false);
-    }
-    // The other indices don't match, so we mark this table entry,
-    // and insert both the old and new indices into the map.
-    const IndexType old_index(position_index, index.texcoord, index.normal);
-    map_.insert(std::make_pair(old_index, index.position_or_flat));
-    index.position_or_flat = kIndexNotInTable;
-    const IndexType new_index(position_index, texcoord_index, normal_index);
-    const int flat_index = count_++;
-    map_.insert(std::make_pair(new_index, flat_index));
-    return std::make_pair(flat_index, true);
-  }
- private:
-  std::pair<int, bool> GetFlattenedIndexFromMap(int position_index,
-                                                int texcoord_index,
-                                                int normal_index) {
-    IndexType index(position_index, texcoord_index, normal_index);
-    MapType::iterator iter = map_.lower_bound(index);
-    if (iter == map_.end() || iter->first != index) {
-      const int flat_index = count_++;
-      map_.insert(iter, std::make_pair(index, flat_index));
-      return std::make_pair(flat_index, true);
-    } else {
-      return std::make_pair(iter->second, false);
-    }
-  }
-
-  static const int kIndexUnknown = -1;
-  static const int kIndexNotInTable = -2;
-
-  struct IndexType {
-    IndexType()
-        : position_or_flat(kIndexUnknown),
-          texcoord(kIndexUnknown),
-          normal(kIndexUnknown)
-    { }
-
-    IndexType(int position_index, int texcoord_index, int normal_index)
-        : position_or_flat(position_index),
-          texcoord(texcoord_index),
-          normal(normal_index)
-    { }
-
-    // I'm being tricky/lazy here. The table_ stores the flattened
-    // index in the first field, since it is indexed by position. The
-    // map_ stores position and uses this struct as a key to lookup the
-    // flattened index.
-    int position_or_flat;
-    int texcoord;
-    int normal;
-
-    // An ordering for std::map.
-    bool operator<(const IndexType& that) const {
-      if (position_or_flat == that.position_or_flat) {
-        if (texcoord == that.texcoord) {
-          return normal < that.normal;
-        } else {
-          return texcoord < that.texcoord;
-        }
-      } else {
-        return position_or_flat < that.position_or_flat;
-      }
-    }
-
-    bool operator==(const IndexType& that) const {
-      return position_or_flat == that.position_or_flat &&
-          texcoord == that.texcoord && normal == that.normal;
-    }
-
-    bool operator!=(const IndexType& that) const {
-      return !operator==(that);
-    }
-  };
-  typedef std::map<IndexType, int> MapType;
-
-  int count_;
-  std::vector<IndexType> table_;
-  MapType map_;
-};
-
-// TODO: consider splitting this into a low-level parser and a high-level
-// object.
-class WavefrontObjFile {
- public:
-  struct Group {
-    std::string name;
-    size_t start, end;
-  };
-
-  typedef std::vector<Group> GroupList;
-
-  explicit WavefrontObjFile(FILE* fp) {
-    ParseFile(fp);
-  };
-
-  const GroupList& groups() const { return groups_; }
-
-  // Populate draw_meshes.
-  void CreateDrawMeshes(std::vector<DrawMesh>* draw_meshes) {
-    draw_meshes->push_back(DrawMesh());
-    DrawMesh& draw_mesh = draw_meshes->back();
-    IndexFlattener flattener(positions_.size() / positionDim());
-    for (size_t i = 0; i < faces_.size(); i += 3) {
-      // .OBJ files use 1-based indexing.
-      const int position_index = faces_[i + 0] - 1;
-      const int texcoord_index = faces_[i + 1] - 1;
-      const int normal_index = faces_[i + 2] - 1;
-      const std::pair<int, bool> flattened = flattener.GetFlattenedIndex(
-          position_index, texcoord_index, normal_index);
-      draw_mesh.triangle_indices.push_back(flattened.first);
-      if (flattened.second) {
-        for (size_t i = 0; i < positionDim(); ++i) {
-          draw_mesh.interleaved_attribs.push_back(
-              positions_[positionDim() * position_index + i]);
-        }
-        for (size_t i = 0; i < texcoordDim(); ++i) {
-          draw_mesh.interleaved_attribs.push_back(
-              texcoords_[texcoordDim() * texcoord_index + i]);
-        }
-        for (size_t i = 0; i < normalDim(); ++i) {
-          draw_mesh.interleaved_attribs.push_back(
-              normals_[normalDim() * normal_index + i]);
-        }
-      }
-    }
-  }
-
-  /*
-  // %z formatting chokes MinGW compiler on Windows :/
-  // using instead unsigned long
-
-  void DumpDebug() const {
-    printf("positions size: %zu\ntexcoords size: %zu\nnormals size: %zu"
-           "\nfaces size: %zu\n", positions_.size(), texcoords_.size(),
-           normals_.size(), faces_.size());
-  }
-  */
-
-  void DumpDebug() const {
-    printf("positions size: %lu\ntexcoords size: %lu\nnormals size: %lu"
-           "\nfaces size: %lu\n", (unsigned long)positions_.size(), (unsigned long)texcoords_.size(),
-           (unsigned long)normals_.size(), (unsigned long)faces_.size());
-  }
-
- private:
-  void ParseFile(FILE* fp) {
-    // TODO: don't use a fixed-size buffer.
-    const size_t kLineBufferSize = 256;
-    char buffer[kLineBufferSize];
-    unsigned int line_num = 1;
-    while (fgets(buffer, kLineBufferSize, fp) != NULL) {
-      const char* stripped = buffer;
-      while (isspace(*stripped)) {
-        ++stripped;
-      }
-      ParseLine(stripped, line_num++);
-    }
-  }
-
-  void ParseLine(const char* line, unsigned int line_num) {
-    switch (*line) {
-      case 'v':
-        ParseAttrib(line + 1, line_num);
-        break;
-      case 'f':
-        ParseFace(line + 1, line_num);
-        break;
-      case 'g':
-        ParseGroup(line + 1, line_num);
-        break;
-      case '\0':
-      case '#':
-        break;  // Do nothing for comments or blank lines.
-      case 'p':
-        WarnLine("point unsupported", line_num);
-        break;
-      case 'l':
-        WarnLine("line unsupported", line_num);
-        break;
-      case 'u':
-        WarnLine("usemtl (?) unsupported", line_num);
-        break;
-      case 'm':
-        WarnLine("mtllib (?) unsupported", line_num);
-        break;
-      case 's':
-        WarnLine("s unsupported", line_num);
-        break;
-      default:
-        WarnLine("unknown keyword", line_num);
-        break;
-    }
-  }
-
-  void ParseAttrib(const char* line, unsigned int line_num) {
-    ShortFloatList floats;
-    floats.ParseLine(line + 1);
-    if (isspace(*line)) {
-      ParsePosition(floats, line_num);
-    } else if (*line == 't') {
-      ParseTexCoord(floats, line_num);
-    } else if (*line == 'n') {
-      ParseNormal(floats, line_num);
-    } else {
-      WarnLine("unknown attribute format", line_num);
-    }
-  }
-
-  void ParsePosition(const ShortFloatList& floats, unsigned int line_num) {
-    if (floats.size() != positionDim()) {
-      ErrorLine("bad position", line_num);
-    }
-    floats.AppendTo(&positions_);
-  }
-
-  void ParseTexCoord(const ShortFloatList& floats, unsigned int line_num) {
-    if (floats.size() != texcoordDim()) {
-      ErrorLine("bad texcoord", line_num);
-    }
-    floats.AppendTo(&texcoords_);
-  }
-
-  void ParseNormal(const ShortFloatList& floats, unsigned int line_num) {
-    if (floats.size() != normalDim()) {
-      ErrorLine("bad normal", line_num);
-    }
-    floats.AppendTo(&normals_);
-  }
-
-  // Parses faces and converts to triangle fans. This is not a
-  // particularly good tesselation in general case, but it is really
-  // simple, and is perfectly fine for triangles and quads.
-  void ParseFace(const char* line, unsigned int line_num) {
-    // Also handle face outlines as faces.
-    if (*line == 'o') ++line;
-
-    // TODO: instead of storing these indices as-is, it might make
-    // sense to flatten them right away. This can reduce memory
-    // consumption and improve access locality, especially since .OBJ
-    // face indices are so needlessly large.
-    int indices[9] = { 0 };
-    // The first index acts as the pivot for the triangle fan.
-    line = ParseIndices(line, line_num, indices + 0, indices + 1, indices + 2);
-    if (line == NULL) {
-      ErrorLine("bad first index", line_num);
-    }
-    line = ParseIndices(line, line_num, indices + 3, indices + 4, indices + 5);
-    if (line == NULL) {
-      ErrorLine("bad second index", line_num);
-    }
-    // After the first two indices, each index introduces a new
-    // triangle to the fan.
-    while ((line = ParseIndices(line, line_num,
-                                indices + 6, indices + 7, indices + 8))) {
-      faces_.insert(faces_.end(), indices, indices + 9);
-      // The most recent vertex is reused for the next triangle.
-      indices[3] = indices[6];
-      indices[4] = indices[7];
-      indices[5] = indices[8];
-      indices[6] = indices[7] = indices[8] = 0;
-    }
-  }
-
-  // Parse a single group of indices, separated by slashes ('/').
-  // TODO: convert negative indices (that is, relative to the end of
-  // the current vertex positions) to more conventional positive
-  // indices.
-  const char* ParseIndices(const char* line, unsigned int line_num,
-                           int* position_index, int* texcoord_index,
-                           int* normal_index) {
-    int bytes_consumed = 0;
-    int indices_parsed = sscanf(line, "%d/%d/%d%n",
-                                position_index, texcoord_index, normal_index,
-                                &bytes_consumed);
-    if (indices_parsed != 3) {
-      return NULL;
-    }
-
-    if (*position_index <= 0 || *texcoord_index <= 0 || *normal_index <= 0 ) {
-      ErrorLine("bad index format", line_num);
-    }
-
-    return line + bytes_consumed;
-  }
-
-  void ParseGroup(const char* line, unsigned int line_num) {
-    WarnLine("group unsupported", line_num);
-  }
-
-  void WarnLine(const char* why, unsigned int line_num) {
-    fprintf(stderr, "WARNING: %s at line %u\n", why, line_num);
-  }
-
-  void ErrorLine(const char* why, unsigned int line_num) {
-    fprintf(stderr, "ERROR: %s at line %u\n", why, line_num);
-    exit(-1);
-  }
-
-  static size_t positionDim() { return 3; }
-  static size_t texcoordDim() { return 2; }
-  static size_t normalDim() { return 3; }
-
-  AttribList positions_;
-  AttribList texcoords_;
-  AttribList normals_;
-  // Indices are 1-indexed, and per-attrib.
-  IndexList faces_;
-  GroupList groups_;
-};
-
-// Axis-aligned bounding box
-struct AABB {
-  float mins[3];
-  float maxes[3];
-};
-
-void DumpJsonFromAABB(const AABB& aabb) {
-  printf("var aabb = { mins: [%f, %f, %f], maxes: [%f, %f, %f] };\n",
-         aabb.mins[0], aabb.mins[1], aabb.mins[2],
-         aabb.maxes[0], aabb.maxes[1], aabb.maxes[2]);
-}
-
-float UniformScaleFromAABB(const AABB& aabb) {
-  const float x = aabb.maxes[0] - aabb.mins[0];
-  const float y = aabb.maxes[1] - aabb.mins[1];
-  const float z = aabb.maxes[2] - aabb.mins[2];
-  return (x > y)
-      ? ((x > z) ? x : z)
-      : ((y > z) ? y : z);
-}
-
-void AABBToCenter(const AABB& aabb, float center[3]) {
-  for (size_t i = 0; i < 3; ++i) {
-    center[i] = 0.5*(aabb.mins[i] + aabb.maxes[i]);
-  }
-}
-
-AABB AABBFromAttribs(const AttribList& interleaved_attribs) {
-  AABB aabb;
-  for (size_t i = 0; i < 3; ++i) {
-    aabb.mins[i] = FLT_MAX;
-    aabb.maxes[i] = -FLT_MAX;
-  }
-  for (size_t i = 0; i < interleaved_attribs.size(); i += 8) {
-    for (size_t j = 0; j < 3; ++j) {
-      const float attrib = interleaved_attribs[i + j];
-      if (aabb.mins[j] > attrib) {
-        aabb.mins[j] = attrib;
-      }
-      if (aabb.maxes[j] < attrib) {
-        aabb.maxes[j] = attrib;
-      }
-    }
-  }
-  return aabb;
-}
-
-uint16 Quantize(float f, float offset, float range, int bits) {
-  const float f_offset = f + offset;
-  // Losslessly multiply a float by 1 << bits;
-  const float f_scaled = ldexpf(f_offset, bits);
-  // static_cast rounds towards zero (i.e. truncates).
-  return static_cast<uint16>(f_scaled / range - 0.5f);
-}
-
-void AttribsToQuantizedAttribs(const AttribList& interleaved_attribs,
-                               QuantizedAttribList* quantized_attribs) {
-  const AABB aabb = AABBFromAttribs(interleaved_attribs);
-  const float scale = UniformScaleFromAABB(aabb);
-  quantized_attribs->resize(interleaved_attribs.size());
-  const float offsets[8] = { -aabb.mins[0], -aabb.mins[1], -aabb.mins[2],
-                             0.0f, 0.0f, 1.f, 1.f, 1.f };
-  const float scales[8] = { scale, scale, scale, 1.f, 1.f, 2.f, 2.f, 2.f };
-  const int bits[8] = { 14, 14, 14, 10, 10, 10, 10, 10 };
-  for (size_t i = 0; i < interleaved_attribs.size(); i += 8) {
-    for (size_t j = 0; j < 8; ++j) {
-      quantized_attribs->at(i + j) = Quantize(interleaved_attribs[i + j],
-                                              offsets[j], scales[j], bits[j]);
-    }
-  }
-
-  // dump for reconstruction of real dimensions in JavaScript
-  // (probably should be embedded as a part of the UTF8 file)
-  fprintf(stderr, "scale: %f, offsetX: %f, offsetY: %f, offsetZ: %f\n", scale, aabb.mins[0], aabb.mins[1], aabb.mins[2] );
-}
-
-// Based on:
-// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
-class VertexOptimizer {
- public:
-  // TODO: this could easily work with non-quantized attribute lists.
-  VertexOptimizer(const QuantizedAttribList& attribs, const IndexList& indices)
-      : attribs_(attribs),
-        indices_(indices),
-        per_vertex_data_(attribs_.size() / 8) {
-    // The cache has an extra slot allocated to simplify the logic in
-    // InsertIndexToCache.
-    for (unsigned int i = 0; i <= kCacheSize; ++i) {
-      cache_[i] = kUnknownIndex;
-    }
-
-    // Loop through the indices, incrementing active triangle counts.
-    for (size_t i = 0; i < indices_.size(); ++i) {
-      per_vertex_data_[indices_[i]].active_tris++;
-    }
-
-    // Compute initial vertex scores.
-    for (size_t i = 0; i < per_vertex_data_.size(); ++i) {
-      per_vertex_data_[i].UpdateScore();
-    }
-  }
-
-  void GetOptimizedMesh(QuantizedAttribList* attribs, IndexList* indices) {
-    attribs->resize(attribs_.size());
-    indices->resize(indices_.size());
-
-    uint16* attribs_out = &attribs->at(0);
-    int* indices_out = &indices->at(0);
-    int next_unused_index = 0;
-    // Consume indices_, one triangle at a time. When a triangle is consumed from
-    // the middle of indices_, the last one is copied in to replace it so that we
-    // can shrink indices_ from the end.
-    while (!indices_.empty()) {
-      const size_t best_triangle = FindBestTriangle();
-      const size_t last_triangle = indices_.size() - 3;
-      // Go through the indices of the best triangle.
-      for (size_t i = 0; i < 3; ++i) {
-        const int index = indices_[best_triangle + i];
-        // After consuming this vertex, copy the corresponding index
-        // from the last triangle into this slot.
-        indices_[best_triangle + i] = indices_[last_triangle + i];
-        per_vertex_data_[index].active_tris--;
-        InsertIndexToCache(index);
-        const int cached_output_index = per_vertex_data_[index].output_index;
-        // Have we seen this index before?
-        if (cached_output_index != kUnknownIndex) {
-          *indices_out++ = cached_output_index;
-          continue;
-        }
-        // The first time we see an index, not only do we increment
-        // next_index counter, but we must also copy the corresponding
-        // attributes.
-        per_vertex_data_[index].output_index = next_unused_index;
-        for (size_t j = 0; j < 8; ++j) {
-          *attribs_out++ = attribs_[8*index + j];
-        }
-        *indices_out++ = next_unused_index++;
-      }
-      // Remove the last triangle.
-      indices_.resize(last_triangle);
-    }
-  }
- private:
-  static const int kUnknownIndex = -1;
-  static const uint16 kCacheSize = 32;
-
-  struct VertexData {
-    VertexData()
-        : active_tris(0),
-          cache_tag(kCacheSize),
-          output_index(kUnknownIndex),
-          score(-1.f)
-    { }
-
-    void UpdateScore() {
-      if (active_tris <= 0) {
-        score = -1.f;
-        return;
-      }
-      // TODO: build initial score table.
-      if (cache_tag < 3) {
-        // The most recent triangle should has a fixed score to
-        // discourage generating nothing but really long strips. If we
-        // want strips, we should use a different optimizer.
-        const float kLastTriScore = 0.75f;
-        score = kLastTriScore;
-      } else if (cache_tag < kCacheSize) {
-        // Points for being recently used.
-        const float kScale = 1.f / (kCacheSize - 3);
-        const float kCacheDecayPower = 1.5f;
-        score = powf(1.f - kScale * (cache_tag - 3), kCacheDecayPower);
-      } else {
-        // Not in cache.
-        score = 0.f;
-      }
-
-      // Bonus points for having a low number of tris still to use the
-      // vert, so we get rid of lone verts quickly.
-      const float kValenceBoostScale = 2.0f;
-      const float kValenceBoostPower = 0.5f;
-      const float valence_boost = powf(active_tris, -kValenceBoostPower);  // rsqrt?
-      score += valence_boost * kValenceBoostScale;
-    };
-
-    int active_tris;
-    unsigned int cache_tag;  // == kCacheSize means not in cache.
-    int output_index;  // For output remapping.
-    float score;
-  };
-
-  // This also updates the vertex scores!
-  void InsertIndexToCache(int index) {
-    // Find how recently the vertex was used.
-    const unsigned int cache_tag = per_vertex_data_[index].cache_tag;
-
-    // Don't do anything if the vertex is already at the head of the
-    // LRU list.
-    if (cache_tag == 0) return;
-
-    // Loop through the cache, inserting the index at the front, and
-    // bubbling down to where the index was originally found. If the
-    // index was not originally in the cache, then it claims to be at
-    // the (kCacheSize + 1)th entry, and we use an extra slot to make
-    // that case simpler.
-    int to_insert = index;
-    for (unsigned int i = 0; i <= cache_tag; ++i) {
-      const int current_index = cache_[i];
-
-      // Update cross references between the entry of the cache and
-      // the per-vertex data.
-      cache_[i] = to_insert;
-      per_vertex_data_[to_insert].cache_tag = i;
-      per_vertex_data_[to_insert].UpdateScore();
-
-      // No need to continue if we find an empty entry.
-      if (current_index == kUnknownIndex) {
-        break;
-      }
-
-      to_insert = current_index;
-    }
-  }
-
-  size_t FindBestTriangle() {
-    float best_score = -FLT_MAX;
-    size_t best_triangle = 0;
-    // TODO: without a boundary structure, this performs a linear
-    // scan, which makes Tom Forsyth's linear algorithm run in
-    // quadratic time!
-    for (size_t i = 0; i < indices_.size(); i += 3) {
-      const float score =
-          per_vertex_data_[indices_[i + 0]].score +
-          per_vertex_data_[indices_[i + 1]].score +
-          per_vertex_data_[indices_[i + 2]].score;
-      if (score > best_score) {
-        best_score = score;
-        best_triangle = i;
-      }
-    }
-    return best_triangle;
-  }
-
-  const QuantizedAttribList& attribs_;
-  IndexList indices_;
-  std::vector<VertexData> per_vertex_data_;
-  int cache_[kCacheSize + 1];
-};
-
-uint16 ZigZag(int16 word) {
-  return (word >> 15) ^ (word << 1);
-}
-
-#define CHECK(PRED) if (!(PRED)) {                              \
-    fprintf(stderr, "%d: CHECK failed: " #PRED "\n", __LINE__); \
-    exit(-1); } else
-
-bool Uint16ToUtf8(uint16 word, std::vector<char>* utf8) {
-  const char kMoreBytesPrefix = static_cast<char>(0x80);
-  const uint16 kMoreBytesMask = 0x3F;
-  if (word < 0x80) {
-    utf8->push_back(static_cast<char>(word));
-  } else if (word < 0x800) {
-    const char kTwoBytePrefix = static_cast<char>(0xC0);
-    utf8->push_back(kTwoBytePrefix + static_cast<char>(word >> 6));
-    utf8->push_back(kMoreBytesPrefix +
-                    static_cast<char>(word & kMoreBytesMask));
-  } else if (word < 0xF800) {
-    const char kThreeBytePrefix = static_cast<char>(0xE0);
-    // We can only encode 65535 - 2048 values because of illegal UTF-8
-    // characters, such as surrogate pairs in [0xD800, 0xDFFF].
-    //TODO: what about other characters, like reversed-BOM 0xFFFE?
-    if (word >= 0xD800) {
-      // Shift the result to avoid the surrogate pair range.
-      word += 0x0800;
-    }
-    utf8->push_back(kThreeBytePrefix + static_cast<char>(word >> 12));
-    utf8->push_back(kMoreBytesPrefix +
-                    static_cast<char>((word >> 6) & kMoreBytesMask));
-    utf8->push_back(kMoreBytesPrefix +
-                    static_cast<char>(word & kMoreBytesMask));
-  } else {
-    return false;
-  }
-  return true;
-}
-
-void CompressIndicesToUtf8(const IndexList& list, std::vector<char>* utf8) {
-  // For indices, we don't do delta from the most recent index, but
-  // from the high water mark. The assumption is that the high water
-  // mark only ever moves by one at a time. Foruntately, the vertex
-  // optimizer does that for us, to optimize for per-transform vertex
-  // fetch order.
-  uint16 index_high_water_mark = 0;
-  for (size_t i = 0; i < list.size(); ++i) {
-    const int index = list[i];
-    CHECK(index >= 0);
-    CHECK(index <= index_high_water_mark);
-    CHECK(Uint16ToUtf8(index_high_water_mark - index, utf8));
-    if (index == index_high_water_mark) {
-      ++index_high_water_mark;
-    }
-  }
-}
-
-void CompressQuantizedAttribsToUtf8(const QuantizedAttribList& attribs,
-                                    std::vector<char>* utf8) {
-  for (size_t i = 0; i < 8; ++i) {
-    // Use a transposed representation, and delta compression.
-    uint16 prev = 0;
-    for (size_t j = i; j < attribs.size(); j += 8) {
-      const uint16 word = attribs[j];
-      const uint16 za = ZigZag(static_cast<int16>(word - prev));
-      prev = word;
-      CHECK(Uint16ToUtf8(za, utf8));
-    }
-  }
-}
-
-void CompressMeshToFile(const QuantizedAttribList& attribs,
-                        const IndexList& indices,
-                        const char* fn) {
-  CHECK((attribs.size() & 7) == 0);
-  const size_t num_verts = attribs.size() / 8;
-
-  fprintf(stderr, "num_verts: %lu", (unsigned long)num_verts);
-
-  CHECK(num_verts > 0);
-  CHECK(num_verts < 65536);
-  std::vector<char> utf8;
-  CHECK(Uint16ToUtf8(static_cast<uint16>(num_verts - 1), &utf8));
-  CompressQuantizedAttribsToUtf8(attribs, &utf8);
-  CompressIndicesToUtf8(indices, &utf8);
-
-  FILE* fp = fopen(fn, "wb");
-  fwrite(&utf8[0], 1, utf8.size(), fp);
-  fclose(fp);
-}
-
-#endif  // WEBGL_LOADER_MESH_H_

+ 0 - 0
utils/exporters/utf8-r104/obj2utf8.exe → utils/exporters/utf8/obj2utf8.exe


+ 0 - 0
utils/exporters/utf8-r104/obj2utf8x.exe → utils/exporters/utf8/obj2utf8x.exe


BIN
utils/exporters/utf8/objcompress


+ 0 - 61
utils/exporters/utf8/objcompress.cc

@@ -1,61 +0,0 @@
-#if 0  // A cute trick to making this .cc self-building from shell.
-g++ $0 -O2 -Wall -Werror -o `basename $0 .cc`;
-exit;
-#endif
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you
-// may not use this file except in compliance with the License. You
-// may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-// implied. See the License for the specific language governing
-// permissions and limitations under the License.
-
-#include "mesh.h"
-
-const bool kQuantize = true;
-const bool kOptimize = true;
-
-int main(int argc, char* argv[]) {
-  if (argc < 2 || argc > 3) {
-    fprintf(stderr, "Usage: %s in.obj [out.utf8]\n\n"
-            "\tIf 'out' is specified, then attempt to write out a compressed,\n"
-            "\tUTF-8 version to 'out.'\n\n"
-            "\tIf not, write a JSON version to STDOUT.\n\n",
-            argv[0]);
-    return -1;
-  }
-  FILE* fp = fopen(argv[1], "r");
-  WavefrontObjFile obj(fp);
-  fclose(fp);
-  std::vector<DrawMesh> meshes;
-  obj.CreateDrawMeshes(&meshes);
-  if (kQuantize) {
-    QuantizedAttribList attribs;
-    AttribsToQuantizedAttribs(meshes[0].interleaved_attribs, &attribs);
-    if (kOptimize) {
-      QuantizedAttribList optimized_attribs;
-      IndexList optimized_indices;
-      VertexOptimizer vertex_optimizer(attribs, meshes[0].triangle_indices);
-      vertex_optimizer.GetOptimizedMesh(&optimized_attribs, &optimized_indices);
-      if (argc == 3) {
-        CompressMeshToFile(optimized_attribs, optimized_indices, argv[2]);
-      } else {
-        DumpJsonFromQuantizedAttribs(optimized_attribs);
-        DumpJsonFromIndices(optimized_indices);
-      }
-      return 0;
-    } else {
-      DumpJsonFromQuantizedAttribs(attribs);
-    }
-  } else {
-    DumpJsonFromInterleavedAttribs(meshes[0].interleaved_attribs);
-  }
-  DumpJsonFromIndices(meshes[0].triangle_indices);
-  return 0;
-}

BIN
utils/exporters/utf8/objcompress.exe


+ 0 - 0
utils/exporters/utf8-r104/src/README → utils/exporters/utf8/src/README


+ 0 - 0
utils/exporters/utf8-r104/src/base.h → utils/exporters/utf8/src/base.h


+ 0 - 0
utils/exporters/utf8-r104/src/bounds.h → utils/exporters/utf8/src/bounds.h


+ 0 - 0
utils/exporters/utf8-r104/src/compress.h → utils/exporters/utf8/src/compress.h


+ 0 - 0
utils/exporters/utf8-r104/src/mesh.h → utils/exporters/utf8/src/mesh.h


+ 0 - 0
utils/exporters/utf8-r104/src/obj2utf8.cc → utils/exporters/utf8/src/obj2utf8.cc


+ 0 - 0
utils/exporters/utf8-r104/src/obj2utf8x.cc → utils/exporters/utf8/src/obj2utf8x.cc


+ 0 - 0
utils/exporters/utf8-r104/src/objcompress.cc → utils/exporters/utf8/src/objcompress.cc


+ 0 - 0
utils/exporters/utf8-r104/src/optimize.h → utils/exporters/utf8/src/optimize.h


+ 0 - 0
utils/exporters/utf8-r104/src/stream.h → utils/exporters/utf8/src/stream.h


+ 0 - 0
utils/exporters/utf8-r104/src/utf8.h → utils/exporters/utf8/src/utf8.h