/** * Loader for CTM encoded models generated by OpenCTM tools: * http://openctm.sourceforge.net/ * * Uses js-openctm library by Juan Mellado * http://code.google.com/p/js-openctm/ * * @author alteredq / http://alteredqualia.com/ * * OpenCTM LICENSE: * * Copyright (c) 2009-2010 Marcus Geelnard * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. * */ /* global CTM */ THREE.CTMLoader = function () { this.workerPath = null; }; THREE.CTMLoader.prototype.constructor = THREE.CTMLoader; THREE.CTMLoader.prototype.setWorkerPath = function ( workerPath ) { this.workerPath = workerPath; }; // Load multiple CTM parts defined in JSON THREE.CTMLoader.prototype.loadParts = function ( url, callback, parameters ) { parameters = parameters || {}; var scope = this; var xhr = new XMLHttpRequest(); var basePath = parameters.basePath ? parameters.basePath : THREE.LoaderUtils.extractUrlBase( url ); xhr.onreadystatechange = function () { if ( xhr.readyState === 4 ) { if ( xhr.status === 200 || xhr.status === 0 ) { var jsonObject = JSON.parse( xhr.responseText ); var materials = [], geometries = [], counter = 0; function callbackFinal( geometry ) { counter += 1; geometries.push( geometry ); if ( counter === jsonObject.offsets.length ) { callback( geometries, materials ); } } // init materials for ( var i = 0; i < jsonObject.materials.length; i ++ ) { materials[ i ] = THREE.Loader.prototype.createMaterial( jsonObject.materials[ i ], basePath ); } // load joined CTM file var partUrl = basePath + jsonObject.data; var parametersPart = { useWorker: parameters.useWorker, worker: parameters.worker, offsets: jsonObject.offsets }; scope.load( partUrl, callbackFinal, parametersPart ); } } }; xhr.open( "GET", url, true ); xhr.setRequestHeader( "Content-Type", "text/plain" ); xhr.send( null ); }; // Load CTMLoader compressed models // - parameters // - url (required) // - callback (required) THREE.CTMLoader.prototype.load = function ( url, callback, parameters ) { parameters = parameters || {}; var scope = this; var offsets = parameters.offsets !== undefined ? parameters.offsets : [ 0 ]; var xhr = new XMLHttpRequest(), callbackProgress = null; var length = 0; xhr.onreadystatechange = function () { if ( xhr.readyState === 4 ) { if ( xhr.status === 200 || xhr.status === 0 ) { var binaryData = new Uint8Array( xhr.response ); var s = Date.now(); if ( parameters.useWorker ) { var worker = parameters.worker || new Worker( scope.workerPath ); worker.onmessage = function ( event ) { var files = event.data; for ( var i = 0; i < files.length; i ++ ) { var ctmFile = files[ i ]; var e1 = Date.now(); // console.log( "CTM data parse time [worker]: " + (e1-s) + " ms" ); scope._createGeometry( ctmFile, callback ); var e = Date.now(); console.log( "model load time [worker]: " + ( e - e1 ) + " ms, total: " + ( e - s ) ); } }; worker.postMessage( { "data": binaryData, "offsets": offsets }, [ binaryData.buffer ] ); } else { for ( var i = 0; i < offsets.length; i ++ ) { var stream = new CTM.Stream( binaryData ); stream.offset = offsets[ i ]; var ctmFile = new CTM.File( stream ); scope._createGeometry( ctmFile, callback ); } //var e = Date.now(); //console.log( "CTM data parse time [inline]: " + (e-s) + " ms" ); } } else { console.error( "Couldn't load [" + url + "] [" + xhr.status + "]" ); } } else if ( xhr.readyState === 3 ) { if ( callbackProgress ) { if ( length === 0 ) { length = xhr.getResponseHeader( "Content-Length" ); } callbackProgress( { total: length, loaded: xhr.responseText.length } ); } } else if ( xhr.readyState === 2 ) { length = xhr.getResponseHeader( "Content-Length" ); } }; xhr.open( "GET", url, true ); xhr.responseType = "arraybuffer"; xhr.send( null ); }; THREE.CTMLoader.prototype._createGeometry = function ( file, callback ) { var geometry = new THREE.BufferGeometry(); var indices = file.body.indices; var positions = file.body.vertices; var normals = file.body.normals; var uvs, colors; var uvMaps = file.body.uvMaps; if ( uvMaps !== undefined && uvMaps.length > 0 ) { uvs = uvMaps[ 0 ].uv; } var attrMaps = file.body.attrMaps; if ( attrMaps !== undefined && attrMaps.length > 0 && attrMaps[ 0 ].name === 'Color' ) { colors = attrMaps[ 0 ].attr; } geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); if ( normals !== undefined ) { geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); } if ( uvs !== undefined ) { geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); } if ( colors !== undefined ) { geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 4 ) ); } // compute vertex normals if not present in the CTM model if ( geometry.attributes.normal === undefined ) { geometry.computeVertexNormals(); } callback( geometry ); };