OBJMTLLoader.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /**
  2. * Loads a Wavefront .obj file with materials
  3. *
  4. * @author mrdoob / http://mrdoob.com/
  5. * @author angelxuanchang
  6. */
  7. THREE.OBJMTLLoader = function () {};
  8. THREE.OBJMTLLoader.prototype = {
  9. constructor: THREE.OBJMTLLoader,
  10. /**
  11. * Load a Wavefront OBJ file with materials (MTL file)
  12. *
  13. * Loading progress is indicated by the following events:
  14. * "load" event (successful loading): type = 'load', content = THREE.Object3D
  15. * "error" event (error loading): type = 'load', message
  16. * "progress" event (progress loading): type = 'progress', loaded, total
  17. *
  18. * If the MTL file cannot be loaded, then a MeshLambertMaterial is used as a default
  19. * @param url - Location of OBJ file to load
  20. * @param mtlfileurl - MTL file to load (optional, if not specified, attempts to use MTL specified in OBJ file)
  21. * @param options - Options on how to interpret the material (see THREE.MTLLoader.MaterialCreator )
  22. */
  23. load: function ( url, mtlfileurl, options ) {
  24. var scope = this;
  25. var xhr = new XMLHttpRequest();
  26. var mtlDone; // Is the MTL done (true if no MTL, error loading MTL, or MTL actually loaded)
  27. var obj3d; // Loaded model (from obj file)
  28. var materialsCreator; // Material creator is created when MTL file is loaded
  29. // Loader for MTL
  30. var mtlLoader = new THREE.MTLLoader( url.substr( 0, url.lastIndexOf( "/" ) + 1 ), options );
  31. mtlLoader.addEventListener( 'load', waitReady );
  32. mtlLoader.addEventListener( 'error', waitReady );
  33. // Try to load mtlfile
  34. if ( mtlfileurl ) {
  35. mtlLoader.load( mtlfileurl );
  36. mtlDone = false;
  37. } else {
  38. mtlDone = true;
  39. }
  40. function waitReady( event ) {
  41. if ( event.type === 'load' ) {
  42. if ( event.content instanceof THREE.MTLLoader.MaterialCreator ) {
  43. // MTL file is loaded
  44. mtlDone = true;
  45. materialsCreator = event.content;
  46. materialsCreator.preload();
  47. } else {
  48. // OBJ file is loaded
  49. if ( event.target.status === 200 || event.target.status === 0 ) {
  50. var objContent = event.target.responseText;
  51. if ( mtlfileurl ) {
  52. // Parse with passed in MTL file
  53. obj3d = scope.parse( objContent );
  54. } else {
  55. // No passed in MTL file, look for mtlfile in obj file
  56. obj3d = scope.parse( objContent, function( mtlfile ) {
  57. mtlDone = false;
  58. mtlLoader.load( mtlLoader.baseUrl + mtlfile );
  59. } );
  60. }
  61. } else {
  62. // Error loading OBJ file....
  63. scope.dispatchEvent( {
  64. type: 'error',
  65. message: 'Couldn\'t load URL [' + url + ']',
  66. response: event.target.responseText } );
  67. }
  68. }
  69. } else if ( event.type === 'error' ) {
  70. // MTL failed to load -- oh well, we will just not have material ...
  71. mtlDone = true;
  72. }
  73. if ( mtlDone && obj3d ) {
  74. // MTL file is loaded and OBJ file is loaded
  75. // Apply materials to model
  76. if ( materialsCreator ) {
  77. obj3d.traverse( function( object ) {
  78. if ( object instanceof THREE.Mesh ) {
  79. if ( object.material.name ) {
  80. var material = materialsCreator.create( object.material.name );
  81. if ( material ) {
  82. object.material = material;
  83. }
  84. }
  85. }
  86. } );
  87. }
  88. // Notify listeners
  89. scope.dispatchEvent( { type: 'load', content: obj3d } );
  90. }
  91. }
  92. xhr.addEventListener( 'load', waitReady, false );
  93. xhr.addEventListener( 'progress', function ( event ) {
  94. scope.dispatchEvent( { type: 'progress', loaded: event.loaded, total: event.total } );
  95. }, false );
  96. xhr.addEventListener( 'error', function () {
  97. scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );
  98. }, false );
  99. xhr.open( 'GET', url, true );
  100. xhr.send( null );
  101. },
  102. /**
  103. * Parses loaded .obj file
  104. * @param data - content of .obj file
  105. * @param mtllibCallback - callback to handle mtllib declaration (optional)
  106. * @return {THREE.Object3D} - Object3D (with default material)
  107. */
  108. parse: function ( data, mtllibCallback ) {
  109. // fixes
  110. data = data.replace( /\ \\\r\n/g, '' ); // rhino adds ' \\r\n' some times.
  111. //
  112. function vector( x, y, z ) {
  113. return new THREE.Vector3( x, y, z );
  114. }
  115. function uv( u, v ) {
  116. return new THREE.Vector2( u, v );
  117. }
  118. function face3( a, b, c, normals ) {
  119. return new THREE.Face3( a, b, c, normals );
  120. }
  121. function face4( a, b, c, d, normals ) {
  122. return new THREE.Face4( a, b, c, d, normals );
  123. }
  124. function meshN( meshName, materialName ) {
  125. if ( geometry.vertices.length > 0 ) {
  126. geometry.mergeVertices();
  127. geometry.computeCentroids();
  128. geometry.computeFaceNormals();
  129. geometry.computeBoundingSphere();
  130. object.add( mesh );
  131. geometry = new THREE.Geometry();
  132. mesh = new THREE.Mesh( geometry, material );
  133. verticesCount = 0;
  134. }
  135. if ( meshName !== undefined ) mesh.name = meshName;
  136. if ( materialName !== undefined ) {
  137. material = new THREE.MeshLambertMaterial();
  138. material.name = materialName;
  139. mesh.material = material;
  140. }
  141. }
  142. var group = new THREE.Object3D();
  143. var object = group;
  144. var geometry = new THREE.Geometry();
  145. var material = new THREE.MeshLambertMaterial();
  146. var mesh = new THREE.Mesh( geometry, material );
  147. var vertices = [];
  148. var verticesCount = 0;
  149. var normals = [];
  150. var uvs = [];
  151. // v float float float
  152. var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
  153. // vn float float float
  154. var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
  155. // vt float float
  156. var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/
  157. // f vertex vertex vertex ...
  158. var face_pattern1 = /f( +\d+)( +\d+)( +\d+)( +\d+)?/
  159. // f vertex/uv vertex/uv vertex/uv ...
  160. var face_pattern2 = /f( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))?/;
  161. // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
  162. var face_pattern3 = /f( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))?/;
  163. // f vertex//normal vertex//normal vertex//normal ...
  164. var face_pattern4 = /f( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))?/;
  165. //
  166. var lines = data.split( "\n" );
  167. for ( var i = 0; i < lines.length; i ++ ) {
  168. var line = lines[ i ];
  169. line = line.trim();
  170. // temporary variable storing pattern matching result
  171. var result;
  172. if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
  173. continue;
  174. } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) {
  175. // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
  176. vertices.push( vector(
  177. parseFloat( result[ 1 ] ),
  178. parseFloat( result[ 2 ] ),
  179. parseFloat( result[ 3 ] )
  180. ) );
  181. } else if ( ( result = normal_pattern.exec( line ) ) !== null ) {
  182. // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
  183. normals.push( vector(
  184. parseFloat( result[ 1 ] ),
  185. parseFloat( result[ 2 ] ),
  186. parseFloat( result[ 3 ] )
  187. ) );
  188. } else if ( ( result = uv_pattern.exec( line ) ) !== null ) {
  189. // ["vt 0.1 0.2", "0.1", "0.2"]
  190. uvs.push( uv(
  191. parseFloat( result[ 1 ] ),
  192. parseFloat( result[ 2 ] )
  193. ) );
  194. } else if ( ( result = face_pattern1.exec( line ) ) !== null ) {
  195. // ["f 1 2 3", "1", "2", "3", undefined]
  196. if ( result[ 4 ] === undefined ) {
  197. geometry.vertices.push(
  198. vertices[ parseInt( result[ 1 ] ) - 1 ],
  199. vertices[ parseInt( result[ 2 ] ) - 1 ],
  200. vertices[ parseInt( result[ 3 ] ) - 1 ]
  201. );
  202. geometry.faces.push( face3(
  203. verticesCount ++,
  204. verticesCount ++,
  205. verticesCount ++
  206. ) );
  207. } else {
  208. geometry.vertices.push(
  209. vertices[ parseInt( result[ 1 ] ) - 1 ],
  210. vertices[ parseInt( result[ 2 ] ) - 1 ],
  211. vertices[ parseInt( result[ 3 ] ) - 1 ],
  212. vertices[ parseInt( result[ 4 ] ) - 1 ]
  213. );
  214. geometry.faces.push( face4(
  215. verticesCount ++,
  216. verticesCount ++,
  217. verticesCount ++,
  218. verticesCount ++
  219. ) );
  220. }
  221. } else if ( ( result = face_pattern2.exec( line ) ) !== null ) {
  222. // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined]
  223. if ( result[ 10 ] === undefined ) {
  224. geometry.vertices.push(
  225. vertices[ parseInt( result[ 2 ] ) - 1 ],
  226. vertices[ parseInt( result[ 5 ] ) - 1 ],
  227. vertices[ parseInt( result[ 8 ] ) - 1 ]
  228. );
  229. geometry.faces.push( face3(
  230. verticesCount ++,
  231. verticesCount ++,
  232. verticesCount ++
  233. ) );
  234. geometry.faceVertexUvs[ 0 ].push( [
  235. uvs[ parseInt( result[ 3 ] ) - 1 ],
  236. uvs[ parseInt( result[ 6 ] ) - 1 ],
  237. uvs[ parseInt( result[ 9 ] ) - 1 ]
  238. ] );
  239. } else {
  240. geometry.vertices.push(
  241. vertices[ parseInt( result[ 2 ] ) - 1 ],
  242. vertices[ parseInt( result[ 5 ] ) - 1 ],
  243. vertices[ parseInt( result[ 8 ] ) - 1 ],
  244. vertices[ parseInt( result[ 11 ] ) - 1 ]
  245. );
  246. geometry.faces.push( face4(
  247. verticesCount ++,
  248. verticesCount ++,
  249. verticesCount ++,
  250. verticesCount ++
  251. ) );
  252. geometry.faceVertexUvs[ 0 ].push( [
  253. uvs[ parseInt( result[ 3 ] ) - 1 ],
  254. uvs[ parseInt( result[ 6 ] ) - 1 ],
  255. uvs[ parseInt( result[ 9 ] ) - 1 ],
  256. uvs[ parseInt( result[ 12 ] ) - 1 ]
  257. ] );
  258. }
  259. } else if ( ( result = face_pattern3.exec( line ) ) !== null ) {
  260. // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined]
  261. if ( result[ 13 ] === undefined ) {
  262. geometry.vertices.push(
  263. vertices[ parseInt( result[ 2 ] ) - 1 ],
  264. vertices[ parseInt( result[ 6 ] ) - 1 ],
  265. vertices[ parseInt( result[ 10 ] ) - 1 ]
  266. );
  267. geometry.faces.push( face3(
  268. verticesCount ++,
  269. verticesCount ++,
  270. verticesCount ++,
  271. [
  272. normals[ parseInt( result[ 4 ] ) - 1 ],
  273. normals[ parseInt( result[ 8 ] ) - 1 ],
  274. normals[ parseInt( result[ 12 ] ) - 1 ]
  275. ]
  276. ) );
  277. geometry.faceVertexUvs[ 0 ].push( [
  278. uvs[ parseInt( result[ 3 ] ) - 1 ],
  279. uvs[ parseInt( result[ 7 ] ) - 1 ],
  280. uvs[ parseInt( result[ 11 ] ) - 1 ]
  281. ] );
  282. } else {
  283. geometry.vertices.push(
  284. vertices[ parseInt( result[ 2 ] ) - 1 ],
  285. vertices[ parseInt( result[ 6 ] ) - 1 ],
  286. vertices[ parseInt( result[ 10 ] ) - 1 ],
  287. vertices[ parseInt( result[ 14 ] ) - 1 ]
  288. );
  289. geometry.faces.push( face4(
  290. verticesCount ++,
  291. verticesCount ++,
  292. verticesCount ++,
  293. verticesCount ++,
  294. [
  295. normals[ parseInt( result[ 4 ] ) - 1 ],
  296. normals[ parseInt( result[ 8 ] ) - 1 ],
  297. normals[ parseInt( result[ 12 ] ) - 1 ],
  298. normals[ parseInt( result[ 16 ] ) - 1 ]
  299. ]
  300. ) );
  301. geometry.faceVertexUvs[ 0 ].push( [
  302. uvs[ parseInt( result[ 3 ] ) - 1 ],
  303. uvs[ parseInt( result[ 7 ] ) - 1 ],
  304. uvs[ parseInt( result[ 11 ] ) - 1 ],
  305. uvs[ parseInt( result[ 15 ] ) - 1 ]
  306. ] );
  307. }
  308. } else if ( ( result = face_pattern4.exec( line ) ) !== null ) {
  309. // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined]
  310. if ( result[ 10 ] === undefined ) {
  311. geometry.vertices.push(
  312. vertices[ parseInt( result[ 2 ] ) - 1 ],
  313. vertices[ parseInt( result[ 5 ] ) - 1 ],
  314. vertices[ parseInt( result[ 8 ] ) - 1 ]
  315. );
  316. geometry.faces.push( face3(
  317. verticesCount ++,
  318. verticesCount ++,
  319. verticesCount ++,
  320. [
  321. normals[ parseInt( result[ 3 ] ) - 1 ],
  322. normals[ parseInt( result[ 6 ] ) - 1 ],
  323. normals[ parseInt( result[ 9 ] ) - 1 ]
  324. ]
  325. ) );
  326. } else {
  327. geometry.vertices.push(
  328. vertices[ parseInt( result[ 2 ] ) - 1 ],
  329. vertices[ parseInt( result[ 5 ] ) - 1 ],
  330. vertices[ parseInt( result[ 8 ] ) - 1 ],
  331. vertices[ parseInt( result[ 11 ] ) - 1 ]
  332. );
  333. geometry.faces.push( face4(
  334. verticesCount ++,
  335. verticesCount ++,
  336. verticesCount ++,
  337. verticesCount ++,
  338. [
  339. normals[ parseInt( result[ 3 ] ) - 1 ],
  340. normals[ parseInt( result[ 6 ] ) - 1 ],
  341. normals[ parseInt( result[ 9 ] ) - 1 ],
  342. normals[ parseInt( result[ 12 ] ) - 1 ]
  343. ]
  344. ) );
  345. }
  346. } else if ( /^o /.test( line ) ) {
  347. // object
  348. object = new THREE.Object3D();
  349. object.name = line.substring( 2 ).trim();
  350. group.add( object );
  351. } else if ( /^g /.test( line ) ) {
  352. // group
  353. meshN( line.substring( 2 ).trim(), undefined );
  354. } else if ( /^usemtl /.test( line ) ) {
  355. // material
  356. meshN( undefined, line.substring( 7 ).trim() );
  357. } else if ( /^mtllib /.test( line ) ) {
  358. // mtl file
  359. if ( mtllibCallback ) {
  360. var mtlfile = line.substring( 7 );
  361. mtlfile = mtlfile.trim();
  362. mtllibCallback( mtlfile );
  363. }
  364. } else if ( /^s /.test( line ) ) {
  365. // Smooth shading
  366. } else {
  367. console.log( "THREE.OBJMTLLoader: Unhandled line " + line );
  368. }
  369. }
  370. //Add last object
  371. meshN(undefined, undefined);
  372. return group;
  373. }
  374. };
  375. THREE.EventDispatcher.prototype.apply( THREE.OBJMTLLoader.prototype );