OBJMTLLoader.js 13 KB

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