/**
* @author mrdoob / http://mrdoob.com/
*/THREE.OBJLoader =(function(){// o object_name | g group_namevar object_pattern =/^[og]\s*(.+)?/;// mtllib file_referencevar material_library_pattern =/^mtllib /;// usemtl material_namevar material_use_pattern =/^usemtl /;functionParserState(){var state ={
objects:[],
object:{},
vertices:[],
normals:[],
colors:[],
uvs:[],
materialLibraries:[],
startObject:function( name, fromDeclaration ){// If the current object (initial from reset) is not from a g/o declaration in the parsed// file. We need to use it for the first parsed g/o to keep things in sync.if(this.object &&this.object.fromDeclaration ===false){this.object.name = name;this.object.fromDeclaration =( fromDeclaration !==false);return;}var previousMaterial =(this.object &&typeofthis.object.currentMaterial ==='function'?this.object.currentMaterial(): undefined );if(this.object &&typeofthis.object._finalize ==='function'){this.object._finalize(true);}this.object ={
name: name ||'',
fromDeclaration:( fromDeclaration !==false),
geometry:{
vertices:[],
normals:[],
colors:[],
uvs:[]},
materials:[],
smooth:true,
startMaterial:function( name, libraries ){var previous =this._finalize(false);// New usemtl declaration overwrites an inherited material, except if faces were declared// after the material, then it must be preserved for proper MultiMaterial continuation.if( previous &&( previous.inherited || previous.groupCount <=0)){this.materials.splice( previous.index,1);}var material ={
index:this.materials.length,
name: name ||'',
mtllib:( Array.isArray( libraries )&& libraries.length >0? libraries[ libraries.length -1]:''),
smooth:( previous !== undefined ? previous.smooth :this.smooth ),
groupStart:( previous !== undefined ? previous.groupEnd :0),
groupEnd:-1,
groupCount:-1,
inherited:false,
clone:function( index ){var cloned ={
index:(typeof index ==='number'? index :this.index ),
name:this.name,
mtllib:this.mtllib,
smooth:this.smooth,
groupStart:0,
groupEnd:-1,
groupCount:-1,
inherited:false};
cloned.clone =this.clone.bind( cloned );return cloned;}};this.materials.push( material );return material;},
currentMaterial:function(){if(this.materials.length >0){returnthis.materials[this.materials.length -1];}return undefined;},
_finalize:function( end ){var lastMultiMaterial =this.currentMaterial();if( lastMultiMaterial && lastMultiMaterial.groupEnd ===-1){
lastMultiMaterial.groupEnd =this.geometry.vertices.length /3;
lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
lastMultiMaterial.inherited =false;}// Ignore objects tail materials if no face declarations followed them before a new o/g started.if( end &&this.materials.length >1){for(var mi =this.materials.length -1; mi >=0; mi --){if(this.materials[ mi ].groupCount <=0){this.materials.splice( mi,1);}}}// Guarantee at least one empty material, this makes the creation later more straight forward.if( end &&this.materials.length ===0){this.materials.push({
name:'',
smooth:this.smooth
});}return lastMultiMaterial;}};// Inherit previous objects material.// Spec tells us that a declared material must be set to all objects until a new material is declared.// If a usemtl declaration is encountered while this new object is being parsed, it will// overwrite the inherited material. Exception being that there was already face declarations// to the inherited material, then it will be preserved for proper MultiMaterial continuation.if( previousMaterial && previousMaterial.name &&typeof previousMaterial.clone ==='function'){var declared = previousMaterial.clone(0);
declared.inherited =true;this.object.materials.push( declared );}this.objects.push(this.object );},
finalize:function(){if(this.object &&typeofthis.object._finalize ==='function'){this.object._finalize(true);}},
parseVertexIndex:function( value, len ){var index =parseInt( value,10);return( index >=0? index -1: index + len /3)*3;},
parseNormalIndex:function( value, len ){var index =parseInt( value,10);return( index >=0? index -1: index + len /3)*3;},
parseUVIndex:function( value, len ){var index =parseInt( value,10);return( index >=0? index -1: index + len /2)*2;},
addVertex:function( a, b, c ){var src =this.vertices;var dst =this.object.geometry.vertices;
dst.push( src[ a +0], src[ a +1], src[ a +2]);
dst.push( src[ b +0], src[ b +1], src[ b +2]);
dst.push( src[ c +0], src[ c +1], src[ c +2]);},
addVertexPoint:function( a ){var src =this.vertices;var dst =this.object.geometry.vertices;
dst.push( src[ a +0], src[ a +1], src[ a +2]);},
addVertexLine:function( a ){var src =this.vertices;var dst =this.object.geometry.vertices;
dst.push( src[ a +0], src[ a +1], src[ a +2]);},
addNormal:function( a, b, c ){var src =this.normals;var dst =this.object.geometry.normals;
dst.push( src[ a +0], src[ a +1], src[ a +2]);
dst.push( src[ b +0], src[ b +1], src[ b +2]);
dst.push( src[ c +0], src[ c +1], src[ c +2]);},
addColor:function( a, b, c ){var src =this.colors;var dst =this.object.geometry.colors;
dst.push( src[ a +0], src[ a +1], src[ a +2]);
dst.push( src[ b +0], src[ b +1], src[ b +2]);
dst.push( src[ c +0], src[ c +1], src[ c +2]);},
addUV:function( a, b, c ){var src =this.uvs;var dst =this.object.geometry.uvs;
dst.push( src[ a +0], src[ a +1]);
dst.push( src[ b +0], src[ b +1]);
dst.push( src[ c +0], src[ c +1]);},
addUVLine:function( a ){var src =this.uvs;var dst =this.object.geometry.uvs;
dst.push( src[ a +0], src[ a +1]);},
addFace:function( a, b, c, ua, ub, uc, na, nb, nc ){var vLen =this.vertices.length;var ia =this.parseVertexIndex( a, vLen );var ib =this.parseVertexIndex( b, vLen );var ic =this.parseVertexIndex( c, vLen );this.addVertex( ia, ib, ic );if( ua !== undefined && ua !==''){var uvLen =this.uvs.length;
ia =this.parseUVIndex( ua, uvLen );
ib =this.parseUVIndex( ub, uvLen );
ic =this.parseUVIndex( uc, uvLen );this.addUV( ia, ib, ic );}if( na !== undefined && na !==''){// Normals are many times the same. If so, skip function call and parseInt.var nLen =this.normals.length;
ia =this.parseNormalIndex( na, nLen );
ib = na === nb ? ia :this.parseNormalIndex( nb, nLen );
ic = na === nc ? ia :this.parseNormalIndex( nc, nLen );this.addNormal( ia, ib, ic );}if(this.colors.length >0){this.addColor( ia, ib, ic );}},
addPointGeometry:function( vertices ){this.object.geometry.type ='Points';var vLen =this.vertices.length;for(var vi =0, l = vertices.length; vi < l; vi ++){this.addVertexPoint(this.parseVertexIndex( vertices[ vi ], vLen ));}},
addLineGeometry:function( vertices, uvs ){this.object.geometry.type ='Line';var vLen =this.vertices.length;var uvLen =this.uvs.length;for(var vi =0, l = vertices.length; vi < l; vi ++){this.addVertexLine(this.parseVertexIndex( vertices[ vi ], vLen ));}for(var uvi =0, l = uvs.length; uvi < l; uvi ++){this.addUVLine(this.parseUVIndex( uvs[ uvi ], uvLen ));}}};
state.startObject('',false);return state;}//functionOBJLoader( manager ){this.manager =( manager !== undefined )? manager :THREE.DefaultLoadingManager;this.materials =null;}
OBJLoader.prototype ={
constructor: OBJLoader,
load:function( url, onLoad, onProgress, onError ){var scope =this;var loader =newTHREE.FileLoader( scope.manager );
loader.setPath(this.path );
loader.load( url,function( text ){onLoad( scope.parse( text ));}, onProgress, onError );},
setPath:function( value ){this.path = value;returnthis;},
setMaterials:function( materials ){this.materials = materials;returnthis;},
parse:function( text ){
console.time('OBJLoader');var state =newParserState();if( text.indexOf('\r\n')!==-1){// This is faster than String.split with regex that splits on both
text = text.replace(/\r\n/g,'\n');}if( text.indexOf('\\\n')!==-1){// join lines separated by a line continuation character (\)
text = text.replace(/\\\n/g,'');}var lines = text.split('\n');var line ='', lineFirstChar ='';var lineLength =0;var result =[];// Faster to just trim left side of the line. Use if available.var trimLeft =(typeof''.trimLeft ==='function');for(var i =0, l = lines.length; i < l; i ++){
line = lines[ i ];
line = trimLeft ? line.trimLeft(): line.trim();
lineLength = line.length;if( lineLength ===0)continue;
lineFirstChar = line.charAt(0);// @todo invoke passed in handler if anyif( lineFirstChar ==='#')continue;if( lineFirstChar ==='v'){var data = line.split(/\s+/);switch( data[0]){case'v':
state.vertices.push(parseFloat( data[1]),parseFloat( data[2]),parseFloat( data[3]));if( data.length ===8){
state.colors.push(parseFloat( data[4]),parseFloat( data[5]),parseFloat( data[6]));}break;case'vn':
state.normals.push(parseFloat( data[1]),parseFloat( data[2]),parseFloat( data[3]));break;case'vt':
state.uvs.push(parseFloat( data[1]),parseFloat( data[2]));break;}}elseif( lineFirstChar ==='f'){var lineData = line.substr(1).trim();var vertexData = lineData.split(/\s+/);var faceVertices =[];// Parse the face vertex data into an easy to work with formatfor(var j =0, jl = vertexData.length; j < jl; j ++){var vertex = vertexData[ j ];if( vertex.length >0){var vertexParts = vertex.split('/');
faceVertices.push( vertexParts );}}// Draw an edge between the first vertex and all subsequent vertices to form an n-gonvar v1 = faceVertices[0];for(var j =1, jl = faceVertices.length -1; j < jl; j ++){var v2 = faceVertices[ j ];var v3 = faceVertices[ j +1];
state.addFace(
v1[0], v2[0], v3[0],
v1[1], v2[1], v3[1],
v1[2], v2[2], v3[2]);}}elseif( lineFirstChar ==='l'){var lineParts = line.substring(1).trim().split(" ");var lineVertices =[], lineUVs =[];if( line.indexOf("/")===-1){
lineVertices = lineParts;}else{for(var li =0, llen = lineParts.length; li < llen; li ++){var parts = lineParts[ li ].split("/");if( parts[0]!=="") lineVertices.push( parts[0]);if( parts[1]!=="") lineUVs.push( parts[1]);}}
state.addLineGeometry( lineVertices, lineUVs );}elseif( lineFirstChar ==='p'){var lineData = line.substr(1).trim();var pointData = lineData.split(" ");
state.addPointGeometry( pointData );}elseif(( result = object_pattern.exec( line ))!==null){// o object_name// or// g group_name// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869// var name = result[ 0 ].substr( 1 ).trim();var name =(" "+ result[0].substr(1).trim()).substr(1);
state.startObject( name );}elseif( material_use_pattern.test( line )){// material
state.object.startMaterial( line.substring(7).trim(), state.materialLibraries );}elseif( material_library_pattern.test( line )){// mtl file
state.materialLibraries.push( line.substring(7).trim());}elseif( lineFirstChar ==='s'){
result = line.split(' ');// smooth shading// @todo Handle files that have varying smooth values for a set of faces inside one geometry,// but does not define a usemtl for each face set.// This should be detected and a dummy material created (later MultiMaterial and geometry groups).// This requires some care to not create extra material on each smooth value for "normal" obj files.// where explicit usemtl defines geometry groups.// Example asset: examples/models/obj/cerberus/Cerberus.obj/*
* http://paulbourke.net/dataformats/obj/
* or
* http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
*
* From chapter "Grouping" Syntax explanation "s group_number":
* "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
* Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
* surfaces, smoothing groups are either turned on or off; there is no difference between values greater
* than 0."
*/if( result.length >1){var value = result[1].trim().toLowerCase();
state.object.smooth =( value !=='0'&& value !=='off');}else{// ZBrush can produce "s" lines #11707
state.object.smooth =true;}var material = state.object.currentMaterial();if( material ) material.smooth = state.object.smooth;}else{// Handle null terminated files without exceptionif( line ==='\0')continue;thrownewError('THREE.OBJLoader: Unexpected line: "'+ line +'"');}}
state.finalize();var container =newTHREE.Group();
container.materialLibraries =[].concat( state.materialLibraries );for(var i =0, l = state.objects.length; i < l; i ++){var object = state.objects[ i ];var geometry = object.geometry;var materials = object.materials;var isLine =( geometry.type ==='Line');var isPoints =( geometry.type ==='Points');var hasVertexColors =false;// Skip o/g line declarations that did not follow with any facesif( geometry.vertices.length ===0)continue;var buffergeometry =newTHREE.BufferGeometry();
buffergeometry.addAttribute('position',newTHREE.Float32BufferAttribute( geometry.vertices,3));if( geometry.normals.length >0){
buffergeometry.addAttribute('normal',newTHREE.Float32BufferAttribute( geometry.normals,3));}else{
buffergeometry.computeVertexNormals();}if( geometry.colors.length >0){
hasVertexColors =true;
buffergeometry.addAttribute('color',newTHREE.Float32BufferAttribute( geometry.colors,3));}if( geometry.uvs.length >0){
buffergeometry.addAttribute('uv',newTHREE.Float32BufferAttribute( geometry.uvs,2));}// Create materialsvar createdMaterials =[];for(var mi =0, miLen = materials.length; mi < miLen; mi ++){var sourceMaterial = materials[ mi ];var material = undefined;if(this.materials !==null){
material =this.materials.create( sourceMaterial.name );// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.if( isLine && material &&!( material instanceofTHREE.LineBasicMaterial)){var materialLine =newTHREE.LineBasicMaterial();THREE.Material.prototype.copy.call( materialLine, material );
materialLine.color.copy( material.color );
materialLine.lights =false;
material = materialLine;}elseif( isPoints && material &&!( material instanceofTHREE.PointsMaterial)){var materialPoints =newTHREE.PointsMaterial({ size:10, sizeAttenuation:false});THREE.Material.prototype.copy.call( materialPoints, material );
materialPoints.color.copy( material.color );
materialPoints.map = material.map;
materialPoints.lights =false;
material = materialPoints;}}if(! material ){if( isLine ){
material =newTHREE.LineBasicMaterial();}elseif( isPoints ){
material =newTHREE.PointsMaterial({ size:1, sizeAttenuation:false});}else{
material =newTHREE.MeshPhongMaterial();}
material.name = sourceMaterial.name;}
material.flatShading = sourceMaterial.smooth ?false:true;
material.vertexColors = hasVertexColors ?THREE.VertexColors :THREE.NoColors;
createdMaterials.push( material );}// Create meshvar mesh;if( createdMaterials.length >1){for(var mi =0, miLen = materials.length; mi < miLen; mi ++){var sourceMaterial = materials[ mi ];
buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );}if( isLine ){
mesh =newTHREE.LineSegments( buffergeometry, createdMaterials );}elseif( isPoints ){
mesh =newTHREE.Points( buffergeometry, createdMaterials );}else{
mesh =newTHREE.Mesh( buffergeometry, createdMaterials );}}else{if( isLine ){
mesh =newTHREE.LineSegments( buffergeometry, createdMaterials[0]);}elseif( isPoints ){
mesh =newTHREE.Points( buffergeometry, createdMaterials[0]);}else{
mesh =newTHREE.Mesh( buffergeometry, createdMaterials[0]);}}
mesh.name = object.name;
container.add( mesh );}
console.timeEnd('OBJLoader');return container;}};return OBJLoader;})();