/** * @author alteredq / http://alteredqualia.com/ */ THREE.BinaryLoader = function ( showStatus ) { THREE.Loader.call( this, showStatus ); }; THREE.BinaryLoader.prototype = Object.create( THREE.Loader.prototype ); THREE.BinaryLoader.prototype.constructor = THREE.BinaryLoader; // Load models generated by slim OBJ converter with BINARY option (converter_obj_three_slim.py -t binary) // - binary models consist of two files: JS and BIN // - parameters // - url (required) // - callback (required) // - texturePath (optional: if not specified, textures will be assumed to be in the same folder as JS model file) // - binaryPath (optional: if not specified, binary file will be assumed to be in the same folder as JS model file) THREE.BinaryLoader.prototype.load = function ( url, callback, texturePath, binaryPath ) { // todo: unify load API to for easier SceneLoader use texturePath = texturePath || this.extractUrlBase( url ); binaryPath = binaryPath || this.extractUrlBase( url ); var callbackProgress = this.showProgress ? THREE.Loader.prototype.updateProgress : undefined; this.onLoadStart(); // #1 load JS part via web worker this.loadAjaxJSON( this, url, callback, texturePath, binaryPath, callbackProgress ); }; THREE.BinaryLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, binaryPath, callbackProgress ) { var xhr = new XMLHttpRequest(); texturePath = texturePath && ( typeof texturePath === "string" ) ? texturePath : this.extractUrlBase( url ); binaryPath = binaryPath && ( typeof binaryPath === "string" ) ? binaryPath : this.extractUrlBase( url ); xhr.onreadystatechange = function () { if ( xhr.readyState == 4 ) { if ( xhr.status == 200 || xhr.status == 0 ) { var json = JSON.parse( xhr.responseText ); context.loadAjaxBuffers( json, callback, binaryPath, texturePath, callbackProgress ); } else { console.error( "THREE.BinaryLoader: Couldn't load [" + url + "] [" + xhr.status + "]" ); } } }; xhr.open( "GET", url, true ); xhr.send( null ); }; THREE.BinaryLoader.prototype.loadAjaxBuffers = function ( json, callback, binaryPath, texturePath, callbackProgress ) { var scope = this; var xhr = new XMLHttpRequest(), url = binaryPath + json.buffers; xhr.addEventListener( 'load', function ( event ) { var buffer = xhr.response; if ( buffer === undefined ) { // IEWEBGL needs this buffer = ( new Uint8Array( xhr.responseBody ) ).buffer; } if ( buffer.byteLength == 0 ) { // iOS and other XMLHttpRequest level 1 var buffer = new ArrayBuffer( xhr.responseText.length ); var bufView = new Uint8Array( buffer ); for ( var i = 0, l = xhr.responseText.length; i < l; i ++ ) { bufView[ i ] = xhr.responseText.charCodeAt( i ) & 0xff; } } scope.createBinModel( buffer, callback, texturePath, json.materials ); }, false ); if ( callbackProgress !== undefined ) { xhr.addEventListener( 'progress', function ( event ) { if ( event.lengthComputable ) { callbackProgress( event ); } }, false ); } xhr.addEventListener( 'error', function ( event ) { console.error( "THREE.BinaryLoader: Couldn't load [" + url + "] [" + xhr.status + "]" ); }, false ); xhr.open( "GET", url, true ); xhr.responseType = "arraybuffer"; if ( xhr.overrideMimeType ) xhr.overrideMimeType( "text/plain; charset=x-user-defined" ); xhr.send( null ); }; // Binary AJAX parser THREE.BinaryLoader.prototype.createBinModel = function ( data, callback, texturePath, jsonMaterials ) { var Model = function ( texturePath ) { var scope = this, currentOffset = 0, md, normals = [], uvs = [], start_tri_flat, start_tri_smooth, start_tri_flat_uv, start_tri_smooth_uv, start_quad_flat, start_quad_smooth, start_quad_flat_uv, start_quad_smooth_uv, tri_size, quad_size, len_tri_flat, len_tri_smooth, len_tri_flat_uv, len_tri_smooth_uv, len_quad_flat, len_quad_smooth, len_quad_flat_uv, len_quad_smooth_uv; THREE.Geometry.call( this ); md = parseMetaData( data, currentOffset ); currentOffset += md.header_bytes; /* md.vertex_index_bytes = Uint32Array.BYTES_PER_ELEMENT; md.material_index_bytes = Uint16Array.BYTES_PER_ELEMENT; md.normal_index_bytes = Uint32Array.BYTES_PER_ELEMENT; md.uv_index_bytes = Uint32Array.BYTES_PER_ELEMENT; */ // buffers sizes tri_size = md.vertex_index_bytes * 3 + md.material_index_bytes; quad_size = md.vertex_index_bytes * 4 + md.material_index_bytes; len_tri_flat = md.ntri_flat * ( tri_size ); len_tri_smooth = md.ntri_smooth * ( tri_size + md.normal_index_bytes * 3 ); len_tri_flat_uv = md.ntri_flat_uv * ( tri_size + md.uv_index_bytes * 3 ); len_tri_smooth_uv = md.ntri_smooth_uv * ( tri_size + md.normal_index_bytes * 3 + md.uv_index_bytes * 3 ); len_quad_flat = md.nquad_flat * ( quad_size ); len_quad_smooth = md.nquad_smooth * ( quad_size + md.normal_index_bytes * 4 ); len_quad_flat_uv = md.nquad_flat_uv * ( quad_size + md.uv_index_bytes * 4 ); len_quad_smooth_uv = md.nquad_smooth_uv * ( quad_size + md.normal_index_bytes * 4 + md.uv_index_bytes * 4 ); // read buffers currentOffset += init_vertices( currentOffset ); currentOffset += init_normals( currentOffset ); currentOffset += handlePadding( md.nnormals * 3 ); currentOffset += init_uvs( currentOffset ); start_tri_flat = currentOffset; start_tri_smooth = start_tri_flat + len_tri_flat + handlePadding( md.ntri_flat * 2 ); start_tri_flat_uv = start_tri_smooth + len_tri_smooth + handlePadding( md.ntri_smooth * 2 ); start_tri_smooth_uv = start_tri_flat_uv + len_tri_flat_uv + handlePadding( md.ntri_flat_uv * 2 ); start_quad_flat = start_tri_smooth_uv + len_tri_smooth_uv + handlePadding( md.ntri_smooth_uv * 2 ); start_quad_smooth = start_quad_flat + len_quad_flat + handlePadding( md.nquad_flat * 2 ); start_quad_flat_uv = start_quad_smooth + len_quad_smooth + handlePadding( md.nquad_smooth * 2 ); start_quad_smooth_uv = start_quad_flat_uv + len_quad_flat_uv + handlePadding( md.nquad_flat_uv * 2 ); // have to first process faces with uvs // so that face and uv indices match init_triangles_flat_uv( start_tri_flat_uv ); init_triangles_smooth_uv( start_tri_smooth_uv ); init_quads_flat_uv( start_quad_flat_uv ); init_quads_smooth_uv( start_quad_smooth_uv ); // now we can process untextured faces init_triangles_flat( start_tri_flat ); init_triangles_smooth( start_tri_smooth ); init_quads_flat( start_quad_flat ); init_quads_smooth( start_quad_smooth ); this.computeFaceNormals(); function handlePadding( n ) { return ( n % 4 ) ? ( 4 - n % 4 ) : 0; }; function parseMetaData( data, offset ) { var metaData = { 'signature' :parseString( data, offset, 12 ), 'header_bytes' :parseUChar8( data, offset + 12 ), 'vertex_coordinate_bytes' :parseUChar8( data, offset + 13 ), 'normal_coordinate_bytes' :parseUChar8( data, offset + 14 ), 'uv_coordinate_bytes' :parseUChar8( data, offset + 15 ), 'vertex_index_bytes' :parseUChar8( data, offset + 16 ), 'normal_index_bytes' :parseUChar8( data, offset + 17 ), 'uv_index_bytes' :parseUChar8( data, offset + 18 ), 'material_index_bytes' :parseUChar8( data, offset + 19 ), 'nvertices' :parseUInt32( data, offset + 20 ), 'nnormals' :parseUInt32( data, offset + 20 + 4 * 1 ), 'nuvs' :parseUInt32( data, offset + 20 + 4 * 2 ), 'ntri_flat' :parseUInt32( data, offset + 20 + 4 * 3 ), 'ntri_smooth' :parseUInt32( data, offset + 20 + 4 * 4 ), 'ntri_flat_uv' :parseUInt32( data, offset + 20 + 4 * 5 ), 'ntri_smooth_uv' :parseUInt32( data, offset + 20 + 4 * 6 ), 'nquad_flat' :parseUInt32( data, offset + 20 + 4 * 7 ), 'nquad_smooth' :parseUInt32( data, offset + 20 + 4 * 8 ), 'nquad_flat_uv' :parseUInt32( data, offset + 20 + 4 * 9 ), 'nquad_smooth_uv' :parseUInt32( data, offset + 20 + 4 * 10 ) }; /* console.log( "signature: " + metaData.signature ); console.log( "header_bytes: " + metaData.header_bytes ); console.log( "vertex_coordinate_bytes: " + metaData.vertex_coordinate_bytes ); console.log( "normal_coordinate_bytes: " + metaData.normal_coordinate_bytes ); console.log( "uv_coordinate_bytes: " + metaData.uv_coordinate_bytes ); console.log( "vertex_index_bytes: " + metaData.vertex_index_bytes ); console.log( "normal_index_bytes: " + metaData.normal_index_bytes ); console.log( "uv_index_bytes: " + metaData.uv_index_bytes ); console.log( "material_index_bytes: " + metaData.material_index_bytes ); console.log( "nvertices: " + metaData.nvertices ); console.log( "nnormals: " + metaData.nnormals ); console.log( "nuvs: " + metaData.nuvs ); console.log( "ntri_flat: " + metaData.ntri_flat ); console.log( "ntri_smooth: " + metaData.ntri_smooth ); console.log( "ntri_flat_uv: " + metaData.ntri_flat_uv ); console.log( "ntri_smooth_uv: " + metaData.ntri_smooth_uv ); console.log( "nquad_flat: " + metaData.nquad_flat ); console.log( "nquad_smooth: " + metaData.nquad_smooth ); console.log( "nquad_flat_uv: " + metaData.nquad_flat_uv ); console.log( "nquad_smooth_uv: " + metaData.nquad_smooth_uv ); var total = metaData.header_bytes + metaData.nvertices * metaData.vertex_coordinate_bytes * 3 + metaData.nnormals * metaData.normal_coordinate_bytes * 3 + metaData.nuvs * metaData.uv_coordinate_bytes * 2 + metaData.ntri_flat * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes ) + metaData.ntri_smooth * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 ) + metaData.ntri_flat_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.uv_index_bytes*3 ) + metaData.ntri_smooth_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 + metaData.uv_index_bytes*3 ) + metaData.nquad_flat * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes ) + metaData.nquad_smooth * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 ) + metaData.nquad_flat_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.uv_index_bytes*4 ) + metaData.nquad_smooth_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 + metaData.uv_index_bytes*4 ); console.log( "total bytes: " + total ); */ return metaData; }; function parseString( data, offset, length ) { var charArray = new Uint8Array( data, offset, length ); var text = ""; for ( var i = 0; i < length; i ++ ) { text += String.fromCharCode( charArray[ offset + i ] ); } return text; }; function parseUChar8( data, offset ) { var charArray = new Uint8Array( data, offset, 1 ); return charArray[ 0 ]; }; function parseUInt32( data, offset ) { var intArray = new Uint32Array( data, offset, 1 ); return intArray[ 0 ]; }; function init_vertices( start ) { var nElements = md.nvertices; var coordArray = new Float32Array( data, start, nElements * 3 ); var i, x, y, z; for ( i = 0; i < nElements; i ++ ) { x = coordArray[ i * 3 ]; y = coordArray[ i * 3 + 1 ]; z = coordArray[ i * 3 + 2 ]; scope.vertices.push( new THREE.Vector3( x, y, z ) ); } return nElements * 3 * Float32Array.BYTES_PER_ELEMENT; }; function init_normals( start ) { var nElements = md.nnormals; if ( nElements ) { var normalArray = new Int8Array( data, start, nElements * 3 ); var i, x, y, z; for ( i = 0; i < nElements; i ++ ) { x = normalArray[ i * 3 ]; y = normalArray[ i * 3 + 1 ]; z = normalArray[ i * 3 + 2 ]; normals.push( x / 127, y / 127, z / 127 ); } } return nElements * 3 * Int8Array.BYTES_PER_ELEMENT; }; function init_uvs( start ) { var nElements = md.nuvs; if ( nElements ) { var uvArray = new Float32Array( data, start, nElements * 2 ); var i, u, v; for ( i = 0; i < nElements; i ++ ) { u = uvArray[ i * 2 ]; v = uvArray[ i * 2 + 1 ]; uvs.push( u, v ); } } return nElements * 2 * Float32Array.BYTES_PER_ELEMENT; }; function init_uvs3( nElements, offset ) { var i, uva, uvb, uvc, u1, u2, u3, v1, v2, v3; var uvIndexBuffer = new Uint32Array( data, offset, 3 * nElements ); for ( i = 0; i < nElements; i ++ ) { uva = uvIndexBuffer[ i * 3 ]; uvb = uvIndexBuffer[ i * 3 + 1 ]; uvc = uvIndexBuffer[ i * 3 + 2 ]; u1 = uvs[ uva * 2 ]; v1 = uvs[ uva * 2 + 1 ]; u2 = uvs[ uvb * 2 ]; v2 = uvs[ uvb * 2 + 1 ]; u3 = uvs[ uvc * 2 ]; v3 = uvs[ uvc * 2 + 1 ]; scope.faceVertexUvs[ 0 ].push( [ new THREE.Vector2( u1, v1 ), new THREE.Vector2( u2, v2 ), new THREE.Vector2( u3, v3 ) ] ); } }; function init_uvs4( nElements, offset ) { var i, uva, uvb, uvc, uvd, u1, u2, u3, u4, v1, v2, v3, v4; var uvIndexBuffer = new Uint32Array( data, offset, 4 * nElements ); for ( i = 0; i < nElements; i ++ ) { uva = uvIndexBuffer[ i * 4 ]; uvb = uvIndexBuffer[ i * 4 + 1 ]; uvc = uvIndexBuffer[ i * 4 + 2 ]; uvd = uvIndexBuffer[ i * 4 + 3 ]; u1 = uvs[ uva * 2 ]; v1 = uvs[ uva * 2 + 1 ]; u2 = uvs[ uvb * 2 ]; v2 = uvs[ uvb * 2 + 1 ]; u3 = uvs[ uvc * 2 ]; v3 = uvs[ uvc * 2 + 1 ]; u4 = uvs[ uvd * 2 ]; v4 = uvs[ uvd * 2 + 1 ]; scope.faceVertexUvs[ 0 ].push( [ new THREE.Vector2( u1, v1 ), new THREE.Vector2( u2, v2 ), new THREE.Vector2( u4, v4 ) ] ); scope.faceVertexUvs[ 0 ].push( [ new THREE.Vector2( u2, v2 ), new THREE.Vector2( u3, v3 ), new THREE.Vector2( u4, v4 ) ] ); } }; function init_faces3_flat( nElements, offsetVertices, offsetMaterials ) { var i, a, b, c, m; var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 3 * nElements ); var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); for ( i = 0; i < nElements; i ++ ) { a = vertexIndexBuffer[ i * 3 ]; b = vertexIndexBuffer[ i * 3 + 1 ]; c = vertexIndexBuffer[ i * 3 + 2 ]; m = materialIndexBuffer[ i ]; scope.faces.push( new THREE.Face3( a, b, c, null, null, m ) ); } }; function init_faces4_flat( nElements, offsetVertices, offsetMaterials ) { var i, a, b, c, d, m; var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 4 * nElements ); var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); for ( i = 0; i < nElements; i ++ ) { a = vertexIndexBuffer[ i * 4 ]; b = vertexIndexBuffer[ i * 4 + 1 ]; c = vertexIndexBuffer[ i * 4 + 2 ]; d = vertexIndexBuffer[ i * 4 + 3 ]; m = materialIndexBuffer[ i ]; scope.faces.push( new THREE.Face3( a, b, d, null, null, m ) ); scope.faces.push( new THREE.Face3( b, c, d, null, null, m ) ); } }; function init_faces3_smooth( nElements, offsetVertices, offsetNormals, offsetMaterials ) { var i, a, b, c, m; var na, nb, nc; var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 3 * nElements ); var normalIndexBuffer = new Uint32Array( data, offsetNormals, 3 * nElements ); var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); for ( i = 0; i < nElements; i ++ ) { a = vertexIndexBuffer[ i * 3 ]; b = vertexIndexBuffer[ i * 3 + 1 ]; c = vertexIndexBuffer[ i * 3 + 2 ]; na = normalIndexBuffer[ i * 3 ]; nb = normalIndexBuffer[ i * 3 + 1 ]; nc = normalIndexBuffer[ i * 3 + 2 ]; m = materialIndexBuffer[ i ]; var nax = normals[ na * 3 ], nay = normals[ na * 3 + 1 ], naz = normals[ na * 3 + 2 ], nbx = normals[ nb * 3 ], nby = normals[ nb * 3 + 1 ], nbz = normals[ nb * 3 + 2 ], ncx = normals[ nc * 3 ], ncy = normals[ nc * 3 + 1 ], ncz = normals[ nc * 3 + 2 ]; scope.faces.push( new THREE.Face3( a, b, c, [ new THREE.Vector3( nax, nay, naz ), new THREE.Vector3( nbx, nby, nbz ), new THREE.Vector3( ncx, ncy, ncz ) ], null, m ) ); } }; function init_faces4_smooth( nElements, offsetVertices, offsetNormals, offsetMaterials ) { var i, a, b, c, d, m; var na, nb, nc, nd; var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 4 * nElements ); var normalIndexBuffer = new Uint32Array( data, offsetNormals, 4 * nElements ); var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); for ( i = 0; i < nElements; i ++ ) { a = vertexIndexBuffer[ i * 4 ]; b = vertexIndexBuffer[ i * 4 + 1 ]; c = vertexIndexBuffer[ i * 4 + 2 ]; d = vertexIndexBuffer[ i * 4 + 3 ]; na = normalIndexBuffer[ i * 4 ]; nb = normalIndexBuffer[ i * 4 + 1 ]; nc = normalIndexBuffer[ i * 4 + 2 ]; nd = normalIndexBuffer[ i * 4 + 3 ]; m = materialIndexBuffer[ i ]; var nax = normals[ na * 3 ], nay = normals[ na * 3 + 1 ], naz = normals[ na * 3 + 2 ], nbx = normals[ nb * 3 ], nby = normals[ nb * 3 + 1 ], nbz = normals[ nb * 3 + 2 ], ncx = normals[ nc * 3 ], ncy = normals[ nc * 3 + 1 ], ncz = normals[ nc * 3 + 2 ], ndx = normals[ nd * 3 ], ndy = normals[ nd * 3 + 1 ], ndz = normals[ nd * 3 + 2 ]; scope.faces.push( new THREE.Face3( a, b, d, [ new THREE.Vector3( nax, nay, naz ), new THREE.Vector3( nbx, nby, nbz ), new THREE.Vector3( ndx, ndy, ndz ) ], null, m ) ); scope.faces.push( new THREE.Face3( b, c, d, [ new THREE.Vector3( nbx, nby, nbz ), new THREE.Vector3( ncx, ncy, ncz ), new THREE.Vector3( ndx, ndy, ndz ) ], null, m ) ); } }; function init_triangles_flat( start ) { var nElements = md.ntri_flat; if ( nElements ) { var offsetMaterials = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; init_faces3_flat( nElements, start, offsetMaterials ); } }; function init_triangles_flat_uv( start ) { var nElements = md.ntri_flat_uv; if ( nElements ) { var offsetUvs = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; init_faces3_flat( nElements, start, offsetMaterials ); init_uvs3( nElements, offsetUvs ); } }; function init_triangles_smooth( start ) { var nElements = md.ntri_smooth; if ( nElements ) { var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; var offsetMaterials = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; init_faces3_smooth( nElements, start, offsetNormals, offsetMaterials ); } }; function init_triangles_smooth_uv( start ) { var nElements = md.ntri_smooth_uv; if ( nElements ) { var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; var offsetUvs = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; init_faces3_smooth( nElements, start, offsetNormals, offsetMaterials ); init_uvs3( nElements, offsetUvs ); } }; function init_quads_flat( start ) { var nElements = md.nquad_flat; if ( nElements ) { var offsetMaterials = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; init_faces4_flat( nElements, start, offsetMaterials ); } }; function init_quads_flat_uv( start ) { var nElements = md.nquad_flat_uv; if ( nElements ) { var offsetUvs = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; init_faces4_flat( nElements, start, offsetMaterials ); init_uvs4( nElements, offsetUvs ); } }; function init_quads_smooth( start ) { var nElements = md.nquad_smooth; if ( nElements ) { var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; var offsetMaterials = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; init_faces4_smooth( nElements, start, offsetNormals, offsetMaterials ); } }; function init_quads_smooth_uv( start ) { var nElements = md.nquad_smooth_uv; if ( nElements ) { var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; var offsetUvs = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; init_faces4_smooth( nElements, start, offsetNormals, offsetMaterials ); init_uvs4( nElements, offsetUvs ); } }; }; Model.prototype = Object.create( THREE.Geometry.prototype ); Model.prototype.constructor = Model; var geometry = new Model( texturePath ); var materials = this.initMaterials( jsonMaterials, texturePath ); if ( this.needsTangents( materials ) ) geometry.computeTangents(); callback( geometry, materials ); };