WWOBJLoader2.js 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407
  1. /**
  2. * @author Kai Salmen / https://kaisalmen.de
  3. * Development repository: https://github.com/kaisalmen/WWOBJLoader
  4. */
  5. 'use strict';
  6. if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
  7. /**
  8. * OBJ data will be loaded by dynamically created web worker.
  9. * First feed instructions with: prepareRun
  10. * Then: Execute with: run
  11. * @class
  12. */
  13. THREE.OBJLoader2.WWOBJLoader2 = (function () {
  14. var WWOBJLOADER2_VERSION = '1.2.1';
  15. var Validator = THREE.OBJLoader2.prototype._getValidator();
  16. function WWOBJLoader2() {
  17. this._init();
  18. }
  19. WWOBJLoader2.prototype._init = function () {
  20. console.log( "Using THREE.OBJLoader2.WWOBJLoader2 version: " + WWOBJLOADER2_VERSION );
  21. // check worker support first
  22. if ( window.Worker === undefined ) throw "This browser does not support web workers!";
  23. if ( window.Blob === undefined ) throw "This browser does not support Blob!";
  24. if ( ! typeof window.URL.createObjectURL === 'function' ) throw "This browser does not support Object creation from URL!";
  25. this.instanceNo = 0;
  26. this.worker = null;
  27. this.workerCode = null;
  28. this.debug = false;
  29. this.sceneGraphBaseNode = null;
  30. this.streamMeshes = true;
  31. this.meshStore = null;
  32. this.modelName = 'none';
  33. this.validated = false;
  34. this.running = false;
  35. this.requestTerminate = false;
  36. this.clearAllCallbacks();
  37. this.manager = THREE.DefaultLoadingManager;
  38. this.fileLoader = new THREE.FileLoader( this.manager );
  39. this.mtlLoader = null;
  40. this.crossOrigin = null;
  41. this.dataAvailable = false;
  42. this.objAsArrayBuffer = null;
  43. this.fileObj = null;
  44. this.pathObj = null;
  45. this.fileMtl = null;
  46. this.mtlAsString = null;
  47. this.texturePath = null;
  48. this.materials = [];
  49. this.counter = 0;
  50. };
  51. /**
  52. * Enable or disable debug logging.
  53. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  54. *
  55. * @param {boolean} enabled True or false
  56. */
  57. WWOBJLoader2.prototype.setDebug = function ( enabled ) {
  58. this.debug = enabled;
  59. };
  60. /**
  61. * Sets the CORS string to be used.
  62. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  63. *
  64. * @param {string} crossOrigin CORS value
  65. */
  66. WWOBJLoader2.prototype.setCrossOrigin = function ( crossOrigin ) {
  67. this.crossOrigin = crossOrigin;
  68. };
  69. /**
  70. * Register callback function that is invoked by internal function "_announceProgress" to print feedback.
  71. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  72. *
  73. * @param {callback} callbackProgress Callback function for described functionality
  74. */
  75. WWOBJLoader2.prototype.registerCallbackProgress = function ( callbackProgress ) {
  76. if ( Validator.isValid( callbackProgress ) ) this.callbacks.progress.push( callbackProgress );
  77. };
  78. /**
  79. * Register callback function that is called once loading of the complete model is completed.
  80. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  81. *
  82. * @param {callback} callbackCompletedLoading Callback function for described functionality
  83. */
  84. WWOBJLoader2.prototype.registerCallbackCompletedLoading = function ( callbackCompletedLoading ) {
  85. if ( Validator.isValid( callbackCompletedLoading ) ) this.callbacks.completedLoading.push( callbackCompletedLoading );
  86. };
  87. /**
  88. * Register callback function that is called once materials have been loaded. It allows to alter and return materials.
  89. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  90. *
  91. * @param {callback} callbackMaterialsLoaded Callback function for described functionality
  92. */
  93. WWOBJLoader2.prototype.registerCallbackMaterialsLoaded = function ( callbackMaterialsLoaded ) {
  94. if ( Validator.isValid( callbackMaterialsLoaded ) ) this.callbacks.materialsLoaded.push( callbackMaterialsLoaded );
  95. };
  96. /**
  97. * Register callback function that is called every time a mesh was loaded.
  98. * Use {@link THREE.OBJLoader2.WWOBJLoader2.LoadedMeshUserOverride} for alteration instructions (geometry, material or disregard mesh).
  99. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  100. *
  101. * @param {callback} callbackMeshLoaded Callback function for described functionality
  102. */
  103. WWOBJLoader2.prototype.registerCallbackMeshLoaded = function ( callbackMeshLoaded ) {
  104. if ( Validator.isValid( callbackMeshLoaded ) ) this.callbacks.meshLoaded.push( callbackMeshLoaded );
  105. };
  106. /**
  107. * Register callback function that is called to report an error that prevented loading.
  108. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  109. *
  110. * @param {callback} callbackErrorWhileLoading Callback function for described functionality
  111. */
  112. WWOBJLoader2.prototype.registerCallbackErrorWhileLoading = function ( callbackErrorWhileLoading ) {
  113. if ( Validator.isValid( callbackErrorWhileLoading ) ) this.callbacks.errorWhileLoading.push( callbackErrorWhileLoading );
  114. };
  115. /**
  116. * Clears all registered callbacks.
  117. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  118. */
  119. WWOBJLoader2.prototype.clearAllCallbacks = function () {
  120. this.callbacks = {
  121. progress: [],
  122. completedLoading: [],
  123. errorWhileLoading: [],
  124. materialsLoaded: [],
  125. meshLoaded: []
  126. };
  127. };
  128. /**
  129. * Call requestTerminate to terminate the web worker and free local resource after execution.
  130. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  131. *
  132. * @param {boolean} requestTerminate True or false
  133. */
  134. WWOBJLoader2.prototype.setRequestTerminate = function ( requestTerminate ) {
  135. this.requestTerminate = requestTerminate === true;
  136. };
  137. WWOBJLoader2.prototype._validate = function () {
  138. if ( this.validated ) return;
  139. if ( ! Validator.isValid( this.worker ) ) {
  140. this._buildWebWorkerCode();
  141. var blob = new Blob( [ this.workerCode ], { type: 'text/plain' } );
  142. this.worker = new Worker( window.URL.createObjectURL( blob ) );
  143. var scope = this;
  144. var scopeFunction = function ( e ) {
  145. scope._receiveWorkerMessage( e );
  146. };
  147. this.worker.addEventListener( 'message', scopeFunction, false );
  148. }
  149. this.sceneGraphBaseNode = null;
  150. this.streamMeshes = true;
  151. this.meshStore = [];
  152. this.modelName = 'none';
  153. this.validated = true;
  154. this.running = true;
  155. this.requestTerminate = false;
  156. this.fileLoader = Validator.verifyInput( this.fileLoader, new THREE.FileLoader( this.manager ) );
  157. this.mtlLoader = Validator.verifyInput( this.mtlLoader, new THREE.MTLLoader() );
  158. if ( Validator.isValid( this.crossOrigin ) ) this.mtlLoader.setCrossOrigin( this.crossOrigin );
  159. this.dataAvailable = false;
  160. this.fileObj = null;
  161. this.pathObj = null;
  162. this.fileMtl = null;
  163. this.texturePath = null;
  164. this.objAsArrayBuffer = null;
  165. this.mtlAsString = null;
  166. this.materials = [];
  167. var defaultMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } );
  168. defaultMaterial.name = 'defaultMaterial';
  169. this.materials[ defaultMaterial.name ] = defaultMaterial;
  170. this.counter = 0;
  171. };
  172. /**
  173. * Set all parameters for required for execution of "run".
  174. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  175. *
  176. * @param {Object} params Either {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
  177. */
  178. WWOBJLoader2.prototype.prepareRun = function ( params ) {
  179. this._validate();
  180. this.dataAvailable = params.dataAvailable;
  181. this.modelName = params.modelName;
  182. console.time( 'WWOBJLoader2' );
  183. if ( this.dataAvailable ) {
  184. // fast-fail on bad type
  185. if ( ! ( params.objAsArrayBuffer instanceof Uint8Array ) ) {
  186. throw 'Provided input is not of type arraybuffer! Aborting...';
  187. }
  188. this.worker.postMessage( {
  189. cmd: 'init',
  190. debug: this.debug
  191. } );
  192. this.objAsArrayBuffer = params.objAsArrayBuffer;
  193. this.mtlAsString = params.mtlAsString;
  194. } else {
  195. // fast-fail on bad type
  196. if ( ! ( typeof( params.fileObj ) === 'string' || params.fileObj instanceof String ) ) {
  197. throw 'Provided file is not properly defined! Aborting...';
  198. }
  199. this.worker.postMessage( {
  200. cmd: 'init',
  201. debug: this.debug
  202. } );
  203. this.fileObj = params.fileObj;
  204. this.pathObj = params.pathObj;
  205. this.fileMtl = params.fileMtl;
  206. }
  207. this.setRequestTerminate( params.requestTerminate );
  208. this.pathTexture = params.pathTexture;
  209. this.sceneGraphBaseNode = params.sceneGraphBaseNode;
  210. this.streamMeshes = params.streamMeshes;
  211. if ( ! this.streamMeshes ) this.meshStore = [];
  212. };
  213. /**
  214. * Run the loader according the preparation instruction provided in "prepareRun".
  215. * @memberOf THREE.OBJLoader2.WWOBJLoader2
  216. */
  217. WWOBJLoader2.prototype.run = function () {
  218. var scope = this;
  219. var processLoadedMaterials = function ( materialCreator ) {
  220. var materialCreatorMaterials = [];
  221. var materialNames = [];
  222. if ( Validator.isValid( materialCreator ) ) {
  223. materialCreator.preload();
  224. materialCreatorMaterials = materialCreator.materials;
  225. for ( var materialName in materialCreatorMaterials ) {
  226. if ( materialCreatorMaterials.hasOwnProperty( materialName ) ) {
  227. materialNames.push( materialName );
  228. scope.materials[ materialName ] = materialCreatorMaterials[ materialName ];
  229. }
  230. }
  231. }
  232. scope.worker.postMessage( {
  233. cmd: 'setMaterials',
  234. materialNames: materialNames
  235. } );
  236. var materialsFromCallback;
  237. var callbackMaterialsLoaded;
  238. for ( var index in scope.callbacks.materialsLoaded ) {
  239. callbackMaterialsLoaded = scope.callbacks.materialsLoaded[ index ];
  240. materialsFromCallback = callbackMaterialsLoaded( scope.materials );
  241. if ( Validator.isValid( materialsFromCallback ) ) scope.materials = materialsFromCallback;
  242. }
  243. if ( scope.dataAvailable && scope.objAsArrayBuffer ) {
  244. scope.worker.postMessage({
  245. cmd: 'run',
  246. objAsArrayBuffer: scope.objAsArrayBuffer
  247. }, [ scope.objAsArrayBuffer.buffer ] );
  248. } else {
  249. var refPercentComplete = 0;
  250. var percentComplete = 0;
  251. var output;
  252. var onLoad = function ( objAsArrayBuffer ) {
  253. scope._announceProgress( 'Running web worker!' );
  254. scope.objAsArrayBuffer = new Uint8Array( objAsArrayBuffer );
  255. scope.worker.postMessage( {
  256. cmd: 'run',
  257. objAsArrayBuffer: scope.objAsArrayBuffer
  258. }, [ scope.objAsArrayBuffer.buffer ] );
  259. };
  260. var onProgress = function ( event ) {
  261. if ( ! event.lengthComputable ) return;
  262. percentComplete = Math.round( event.loaded / event.total * 100 );
  263. if ( percentComplete > refPercentComplete ) {
  264. refPercentComplete = percentComplete;
  265. output = 'Download of "' + scope.fileObj + '": ' + percentComplete + '%';
  266. console.log( output );
  267. scope._announceProgress( output );
  268. }
  269. };
  270. var onError = function ( event ) {
  271. output = 'Error occurred while downloading "' + scope.fileObj + '"';
  272. console.error( output + ': ' + event );
  273. scope._announceProgress( output );
  274. scope._finalize( 'error' );
  275. };
  276. scope.fileLoader.setPath( scope.pathObj );
  277. scope.fileLoader.setResponseType( 'arraybuffer' );
  278. scope.fileLoader.load( scope.fileObj, onLoad, onProgress, onError );
  279. }
  280. console.timeEnd( 'Loading MTL textures' );
  281. };
  282. this.mtlLoader.setPath( this.pathTexture );
  283. if ( this.dataAvailable ) {
  284. processLoadedMaterials( Validator.isValid( this.mtlAsString ) ? this.mtlLoader.parse( this.mtlAsString ) : null );
  285. } else {
  286. if ( Validator.isValid( this.fileMtl ) ) {
  287. var onError = function ( event ) {
  288. output = 'Error occurred while downloading "' + scope.fileMtl + '"';
  289. console.error( output + ': ' + event );
  290. scope._announceProgress( output );
  291. scope._finalize( 'error' );
  292. };
  293. this.mtlLoader.load( this.fileMtl, processLoadedMaterials, undefined, onError );
  294. } else {
  295. processLoadedMaterials();
  296. }
  297. }
  298. };
  299. WWOBJLoader2.prototype._receiveWorkerMessage = function ( event ) {
  300. var payload = event.data;
  301. switch ( payload.cmd ) {
  302. case 'objData':
  303. this.counter++;
  304. var meshName = payload.meshName;
  305. var bufferGeometry = new THREE.BufferGeometry();
  306. bufferGeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( payload.vertices ), 3 ) );
  307. if ( Validator.isValid( payload.normals ) ) {
  308. bufferGeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( payload.normals ), 3 ) );
  309. } else {
  310. bufferGeometry.computeVertexNormals();
  311. }
  312. if ( Validator.isValid( payload.uvs ) ) {
  313. bufferGeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( payload.uvs ), 2 ) );
  314. }
  315. var materialDescriptions = payload.materialDescriptions;
  316. var materialDescription;
  317. var material;
  318. var materialName;
  319. var createMultiMaterial = payload.multiMaterial;
  320. var multiMaterials = [];
  321. var key;
  322. for ( key in materialDescriptions ) {
  323. materialDescription = materialDescriptions[ key ];
  324. material = this.materials[ materialDescription.name ];
  325. if ( materialDescription.default ) {
  326. material = this.materials[ 'defaultMaterial' ];
  327. } else if ( materialDescription.clone ) {
  328. materialName = material.name + '_flat';
  329. var materialClone = this.materials[ materialName ];
  330. if ( ! materialClone ) {
  331. materialClone = material.clone();
  332. materialClone.name = materialName;
  333. materialClone.shading = THREE.FlatShading;
  334. this.materials[ materialName ] = name;
  335. }
  336. } else if ( ! material ) {
  337. material = this.materials[ 'defaultMaterial' ];
  338. }
  339. if ( createMultiMaterial ) multiMaterials.push( material );
  340. }
  341. if ( createMultiMaterial ) {
  342. material = multiMaterials;
  343. var materialGroups = payload.materialGroups;
  344. var materialGroup;
  345. for ( key in materialGroups ) {
  346. materialGroup = materialGroups[ key ];
  347. bufferGeometry.addGroup( materialGroup.start, materialGroup.count, materialGroup.index );
  348. }
  349. }
  350. var callbackMeshLoaded;
  351. var callbackMeshLoadedResult;
  352. var disregardMesh = false;
  353. for ( var index in this.callbacks.meshLoaded ) {
  354. callbackMeshLoaded = this.callbacks.meshLoaded[ index ];
  355. callbackMeshLoadedResult = callbackMeshLoaded( meshName, bufferGeometry, material );
  356. if ( Validator.isValid( callbackMeshLoadedResult ) ) {
  357. if ( callbackMeshLoadedResult.disregardMesh ) {
  358. // if one callback disregards the mesh, then processing stops
  359. disregardMesh = true;
  360. break;
  361. }
  362. if ( callbackMeshLoadedResult.replaceBufferGeometry ) bufferGeometry = callbackMeshLoadedResult.bufferGeometry;
  363. if ( callbackMeshLoadedResult.replaceMaterial ) material = callbackMeshLoadedResult.material;
  364. }
  365. }
  366. if ( !disregardMesh ) {
  367. var mesh = new THREE.Mesh( bufferGeometry, material );
  368. mesh.name = meshName;
  369. if ( this.streamMeshes ) {
  370. this.sceneGraphBaseNode.add( mesh );
  371. } else {
  372. this.meshStore.push( mesh );
  373. }
  374. this._announceProgress( 'Adding mesh (' + this.counter + '):', meshName );
  375. } else {
  376. this._announceProgress( 'Removing mesh:', meshName );
  377. }
  378. break;
  379. case 'complete':
  380. if ( ! this.streamMeshes ) {
  381. for ( var meshStoreKey in this.meshStore ) {
  382. if ( this.meshStore.hasOwnProperty( meshStoreKey ) ) this.sceneGraphBaseNode.add( this.meshStore[ meshStoreKey ] );
  383. }
  384. }
  385. console.timeEnd( 'WWOBJLoader2' );
  386. if ( Validator.isValid( payload.msg ) ) {
  387. this._announceProgress( payload.msg );
  388. } else {
  389. this._announceProgress( '' );
  390. }
  391. this._finalize( 'complete' );
  392. break;
  393. case 'report_progress':
  394. this._announceProgress( '', payload.output );
  395. break;
  396. default:
  397. console.error( 'Received unknown command: ' + payload.cmd );
  398. break;
  399. }
  400. };
  401. WWOBJLoader2.prototype._terminate = function () {
  402. if ( Validator.isValid( this.worker ) ) {
  403. if ( this.running ) throw 'Unable to gracefully terminate worker as it is currently running!';
  404. this.worker.terminate();
  405. this.worker = null;
  406. this.workerCode = null;
  407. this._finalize( 'terminate' );
  408. }
  409. this.fileLoader = null;
  410. this.mtlLoader = null;
  411. };
  412. WWOBJLoader2.prototype._finalize = function ( reason, requestTerminate ) {
  413. this.running = false;
  414. var index;
  415. var callback;
  416. if ( reason === 'complete' ) {
  417. for ( index in this.callbacks.completedLoading ) {
  418. callback = this.callbacks.completedLoading[ index ];
  419. callback( this.modelName, this.instanceNo, this.requestTerminate );
  420. }
  421. } else if ( reason === 'error' ) {
  422. for ( index in this.callbacks.errorWhileLoading ) {
  423. callback = this.callbacks.errorWhileLoading[ index ];
  424. callback( this.modelName, this.instanceNo, this.requestTerminate );
  425. }
  426. }
  427. this.validated = false;
  428. this.setRequestTerminate( requestTerminate );
  429. if ( this.requestTerminate ) {
  430. this._terminate();
  431. }
  432. };
  433. WWOBJLoader2.prototype._announceProgress = function ( baseText, text ) {
  434. var output = Validator.isValid( baseText ) ? baseText: "";
  435. output = Validator.isValid( text ) ? output + " " + text : output;
  436. var callbackProgress;
  437. for ( var index in this.callbacks.progress ) {
  438. callbackProgress = this.callbacks.progress[ index ];
  439. callbackProgress( output );
  440. }
  441. if ( this.debug ) console.log( output );
  442. };
  443. WWOBJLoader2.prototype._buildWebWorkerCode = function ( existingWorkerCode ) {
  444. if ( Validator.isValid( existingWorkerCode ) ) this.workerCode = existingWorkerCode;
  445. if ( ! Validator.isValid( this.workerCode ) ) {
  446. console.time( 'buildWebWorkerCode' );
  447. var wwDef = (function () {
  448. function WWOBJLoader() {
  449. this.wwMeshCreator = new WWMeshCreator();
  450. this.parser = new Parser( this.wwMeshCreator );
  451. this.validated = false;
  452. this.cmdState = 'created';
  453. this.debug = false;
  454. }
  455. /**
  456. * Allows to set debug mode for the parser and the meshCreatorDebug
  457. *
  458. * @param parserDebug
  459. * @param meshCreatorDebug
  460. */
  461. WWOBJLoader.prototype.setDebug = function ( parserDebug, meshCreatorDebug ) {
  462. this.parser.setDebug( parserDebug );
  463. this.wwMeshCreator.setDebug( meshCreatorDebug );
  464. };
  465. /**
  466. * Validate status, then parse arrayBuffer, finalize and return objGroup
  467. *
  468. * @param arrayBuffer
  469. */
  470. WWOBJLoader.prototype.parse = function ( arrayBuffer ) {
  471. console.log( 'Parsing arrayBuffer...' );
  472. console.time( 'parseArrayBuffer' );
  473. this.validate();
  474. this.parser.parseArrayBuffer( arrayBuffer );
  475. var objGroup = this._finalize();
  476. console.timeEnd( 'parseArrayBuffer' );
  477. return objGroup;
  478. };
  479. WWOBJLoader.prototype.validate = function () {
  480. if ( this.validated ) return;
  481. this.parser.validate();
  482. this.wwMeshCreator.validate();
  483. this.validated = true;
  484. };
  485. WWOBJLoader.prototype._finalize = function () {
  486. console.log( 'Global output object count: ' + this.wwMeshCreator.globalObjectCount );
  487. this.parser.finalize();
  488. this.wwMeshCreator.finalize();
  489. this.validated = false;
  490. };
  491. WWOBJLoader.prototype.init = function ( payload ) {
  492. this.cmdState = 'init';
  493. this.setDebug( payload.debug, payload.debug );
  494. };
  495. WWOBJLoader.prototype.setMaterials = function ( payload ) {
  496. this.cmdState = 'setMaterials';
  497. this.wwMeshCreator.setMaterials( payload.materialNames );
  498. };
  499. WWOBJLoader.prototype.run = function ( payload ) {
  500. this.cmdState = 'run';
  501. this.parse( payload.objAsArrayBuffer );
  502. console.log( 'OBJ loading complete!' );
  503. this.cmdState = 'complete';
  504. self.postMessage( {
  505. cmd: this.cmdState,
  506. msg: null
  507. } );
  508. };
  509. return WWOBJLoader;
  510. })();
  511. var wwMeshCreatorDef = (function () {
  512. function WWMeshCreator() {
  513. this.materials = null;
  514. this.debug = false;
  515. this.globalObjectCount = 1;
  516. this.validated = false;
  517. }
  518. WWMeshCreator.prototype.setMaterials = function ( materials ) {
  519. this.materials = Validator.verifyInput( materials, this.materials );
  520. this.materials = Validator.verifyInput( this.materials, { materials: [] } );
  521. };
  522. WWMeshCreator.prototype.setDebug = function ( debug ) {
  523. if ( debug === true || debug === false ) this.debug = debug;
  524. };
  525. WWMeshCreator.prototype.validate = function () {
  526. if ( this.validated ) return;
  527. this.setMaterials( null );
  528. this.setDebug( null );
  529. this.globalObjectCount = 1;
  530. };
  531. WWMeshCreator.prototype.finalize = function () {
  532. this.materials = null;
  533. this.validated = false;
  534. };
  535. /**
  536. * RawObjectDescriptions are transformed to THREE.Mesh.
  537. * It is ensured that rawObjectDescriptions only contain objects with vertices (no need to check).
  538. *
  539. * @param rawObjectDescriptions
  540. * @param inputObjectCount
  541. * @param absoluteVertexCount
  542. * @param absoluteNormalCount
  543. * @param absoluteUvCount
  544. */
  545. WWMeshCreator.prototype.buildMesh = function ( rawObjectDescriptions, inputObjectCount, absoluteVertexCount, absoluteNormalCount, absoluteUvCount ) {
  546. if ( this.debug ) console.log( 'OBJLoader.buildMesh:\nInput object no.: ' + inputObjectCount );
  547. var vertexFa = new Float32Array( absoluteVertexCount );
  548. var normalFA = ( absoluteNormalCount > 0 ) ? new Float32Array( absoluteNormalCount ) : null;
  549. var uvFA = ( absoluteUvCount > 0 ) ? new Float32Array( absoluteUvCount ) : null;
  550. var rawObjectDescription;
  551. var materialDescription;
  552. var materialDescriptions = [];
  553. var createMultiMaterial = ( rawObjectDescriptions.length > 1 );
  554. var materialIndex = 0;
  555. var materialIndexMapping = [];
  556. var selectedMaterialIndex;
  557. var materialGroup;
  558. var materialGroups = [];
  559. var vertexBAOffset = 0;
  560. var vertexGroupOffset = 0;
  561. var vertexLength;
  562. var normalOffset = 0;
  563. var uvOffset = 0;
  564. for ( var oodIndex in rawObjectDescriptions ) {
  565. if ( ! rawObjectDescriptions.hasOwnProperty( oodIndex ) ) continue;
  566. rawObjectDescription = rawObjectDescriptions[ oodIndex ];
  567. materialDescription = { name: rawObjectDescription.materialName, flat: false, default: false };
  568. if ( this.materials[ materialDescription.name ] === null ) {
  569. materialDescription.default = true;
  570. console.warn( 'object_group "' + rawObjectDescription.objectName + '_' + rawObjectDescription.groupName + '" was defined without material! Assigning "defaultMaterial".' );
  571. }
  572. // Attach '_flat' to materialName in case flat shading is needed due to smoothingGroup 0
  573. if ( rawObjectDescription.smoothingGroup === 0 ) materialDescription.flat = true;
  574. vertexLength = rawObjectDescription.vertices.length;
  575. if ( createMultiMaterial ) {
  576. // re-use material if already used before. Reduces materials array size and eliminates duplicates
  577. selectedMaterialIndex = materialIndexMapping[ materialDescription.name ];
  578. if ( ! selectedMaterialIndex ) {
  579. selectedMaterialIndex = materialIndex;
  580. materialIndexMapping[ materialDescription.name ] = materialIndex;
  581. materialDescriptions.push( materialDescription );
  582. materialIndex++;
  583. }
  584. materialGroup = {
  585. start: vertexGroupOffset,
  586. count: vertexLength / 3,
  587. index: selectedMaterialIndex
  588. };
  589. materialGroups.push( materialGroup );
  590. vertexGroupOffset += vertexLength / 3;
  591. } else {
  592. materialDescriptions.push( materialDescription );
  593. }
  594. vertexFa.set( rawObjectDescription.vertices, vertexBAOffset );
  595. vertexBAOffset += vertexLength;
  596. if ( normalFA ) {
  597. normalFA.set( rawObjectDescription.normals, normalOffset );
  598. normalOffset += rawObjectDescription.normals.length;
  599. }
  600. if ( uvFA ) {
  601. uvFA.set( rawObjectDescription.uvs, uvOffset );
  602. uvOffset += rawObjectDescription.uvs.length;
  603. }
  604. if ( this.debug ) this.printReport( rawObjectDescription, selectedMaterialIndex );
  605. }
  606. self.postMessage( {
  607. cmd: 'objData',
  608. meshName: rawObjectDescription.objectName,
  609. multiMaterial: createMultiMaterial,
  610. materialDescriptions: materialDescriptions,
  611. materialGroups: materialGroups,
  612. vertices: vertexFa,
  613. normals: normalFA,
  614. uvs: uvFA
  615. }, [ vertexFa.buffer ], normalFA !== null ? [ normalFA.buffer ] : null, uvFA !== null ? [ uvFA.buffer ] : null );
  616. this.globalObjectCount++;
  617. };
  618. return WWMeshCreator;
  619. })();
  620. var wwObjLoaderRunnerDef = (function () {
  621. function WWOBJLoaderRunner() {
  622. self.addEventListener( 'message', this.runner, false );
  623. }
  624. WWOBJLoaderRunner.prototype.runner = function ( event ) {
  625. var payload = event.data;
  626. console.log( 'Command state before: ' + WWOBJLoaderRef.cmdState );
  627. switch ( payload.cmd ) {
  628. case 'init':
  629. WWOBJLoaderRef.init( payload );
  630. break;
  631. case 'setMaterials':
  632. WWOBJLoaderRef.setMaterials( payload );
  633. break;
  634. case 'run':
  635. WWOBJLoaderRef.run( payload );
  636. break;
  637. default:
  638. console.error( 'OBJLoader: Received unknown command: ' + payload.cmd );
  639. break;
  640. }
  641. console.log( 'Command state after: ' + WWOBJLoaderRef.cmdState );
  642. };
  643. return WWOBJLoaderRunner;
  644. })();
  645. var buildObject = function ( fullName, object ) {
  646. var objectString = fullName + ' = {\n';
  647. var part;
  648. for ( var name in object ) {
  649. part = object[ name ];
  650. if ( typeof( part ) === 'string' || part instanceof String ) {
  651. part = part.replace( '\n', '\\n' );
  652. part = part.replace( '\r', '\\r' );
  653. objectString += '\t' + name + ': "' + part + '",\n';
  654. } else if ( part instanceof Array ) {
  655. objectString += '\t' + name + ': [' + part + '],\n';
  656. } else if ( Number.isInteger( part ) ) {
  657. objectString += '\t' + name + ': ' + part + ',\n';
  658. } else if ( typeof part === 'function' ) {
  659. objectString += '\t' + name + ': ' + part + ',\n';
  660. }
  661. }
  662. objectString += '}\n\n';
  663. return objectString;
  664. };
  665. var buildSingelton = function ( fullName, internalName, object ) {
  666. var objectString = fullName + ' = (function () {\n\n';
  667. objectString += '\t' + object.prototype.constructor.toString() + '\n\n';
  668. var funcString;
  669. var objectPart;
  670. for ( var name in object.prototype ) {
  671. objectPart = object.prototype[ name ];
  672. if ( typeof objectPart === 'function' ) {
  673. funcString = objectPart.toString();
  674. objectString += '\t' + internalName + '.prototype.' + name + ' = ' + funcString + ';\n\n';
  675. }
  676. }
  677. objectString += '\treturn ' + internalName + ';\n';
  678. objectString += '})();\n\n';
  679. return objectString;
  680. };
  681. this.workerCode = '';
  682. this.workerCode += '/**\n';
  683. this.workerCode += ' * This code was constructed by WWOBJLoader2._buildWebWorkerCode\n';
  684. this.workerCode += ' */\n\n';
  685. // parser re-construction
  686. this.workerCode += THREE.OBJLoader2.prototype._buildWebWorkerCode( buildObject, buildSingelton );
  687. // web worker construction
  688. this.workerCode += buildSingelton( 'WWOBJLoader', 'WWOBJLoader', wwDef );
  689. this.workerCode += buildSingelton( 'WWMeshCreator', 'WWMeshCreator', wwMeshCreatorDef );
  690. this.workerCode += 'WWOBJLoaderRef = new WWOBJLoader();\n\n';
  691. this.workerCode += buildSingelton( 'WWOBJLoaderRunner', 'WWOBJLoaderRunner', wwObjLoaderRunnerDef );
  692. this.workerCode += 'new WWOBJLoaderRunner();\n\n';
  693. console.timeEnd( 'buildWebWorkerCode' );
  694. }
  695. return this.workerCode;
  696. };
  697. return WWOBJLoader2;
  698. })();
  699. /**
  700. * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ from given ArrayBuffer and MTL from given String.
  701. *
  702. * @param {string} modelName Overall name of the model
  703. * @param {Uint8Array} objAsArrayBuffer OBJ file content as ArrayBuffer
  704. * @param {string} pathTexture Path to texture files
  705. * @param {string} mtlAsString MTL file content as string
  706. *
  707. * @returns {{modelName: string, dataAvailable: boolean, objAsArrayBuffer: null, pathTexture: null, mtlAsString: null, sceneGraphBaseNode: null, streamMeshes: boolean, requestTerminate: boolean}}
  708. * @constructor
  709. */
  710. THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer = function ( modelName, objAsArrayBuffer, pathTexture, mtlAsString ) {
  711. var Validator = THREE.OBJLoader2.prototype._getValidator();
  712. return {
  713. /**
  714. * {@link THREE.Object3D} where meshes will be attached.
  715. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer
  716. *
  717. * @param {THREE.Object3D} sceneGraphBaseNode Scene graph object
  718. */
  719. setSceneGraphBaseNode: function ( sceneGraphBaseNode ) {
  720. this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, null );
  721. },
  722. /**
  723. * Singles meshes are directly integrated into scene when loaded or later.
  724. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer
  725. *
  726. * @param {boolean} streamMeshes=true Default is true
  727. */
  728. setStreamMeshes: function ( streamMeshes ) {
  729. this.streamMeshes = streamMeshes !== false;
  730. },
  731. /**
  732. * Request termination of web worker and free local resources after execution.
  733. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer
  734. *
  735. * @param {boolean} requestTerminate=false Default is false
  736. */
  737. setRequestTerminate: function ( requestTerminate ) {
  738. this.requestTerminate = requestTerminate === true;
  739. },
  740. /**
  741. * Returns all callbacks as {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks}
  742. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer
  743. *
  744. * @returns {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks}
  745. */
  746. getCallbacks: function () {
  747. return this.callbacks;
  748. },
  749. modelName: Validator.verifyInput( modelName, 'none' ),
  750. dataAvailable: true,
  751. objAsArrayBuffer: Validator.verifyInput( objAsArrayBuffer, null ),
  752. pathTexture: Validator.verifyInput( pathTexture, null ),
  753. mtlAsString: Validator.verifyInput( mtlAsString, null ),
  754. sceneGraphBaseNode: null,
  755. streamMeshes: true,
  756. requestTerminate: false,
  757. callbacks: new THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks()
  758. };
  759. };
  760. /**
  761. * Instruction to configure {@link THREE.OBJLoader2.WWOBJLoader2}.prepareRun to load OBJ and MTL from files.
  762. *
  763. * @param {string} modelName Overall name of the model
  764. * @param {string} pathObj Path to OBJ file
  765. * @param {string} fileObj OBJ file name
  766. * @param {string} pathTexture Path to texture files
  767. * @param {string} fileMtl MTL file name
  768. *
  769. * @returns {{modelName: string, dataAvailable: boolean, pathObj: null, fileObj: null, pathTexture: null, fileMtl: null, sceneGraphBaseNode: null, streamMeshes: boolean, requestTerminate: boolean}}
  770. * @constructor
  771. */
  772. THREE.OBJLoader2.WWOBJLoader2.PrepDataFile = function ( modelName, pathObj, fileObj, pathTexture, fileMtl ) {
  773. var Validator = THREE.OBJLoader2.prototype._getValidator();
  774. return {
  775. /**
  776. * {@link THREE.Object3D} where meshes will be attached.
  777. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataFile
  778. *
  779. * @param {THREE.Object3D} sceneGraphBaseNode Scene graph object
  780. */
  781. setSceneGraphBaseNode: function ( sceneGraphBaseNode ) {
  782. this.sceneGraphBaseNode = Validator.verifyInput( sceneGraphBaseNode, null );
  783. },
  784. /**
  785. * Singles meshes are directly integrated into scene when loaded or later.
  786. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataFile
  787. *
  788. * @param {boolean} streamMeshes=true Default is true
  789. */
  790. setStreamMeshes: function ( streamMeshes ) {
  791. this.streamMeshes = streamMeshes !== false;
  792. },
  793. /**
  794. * Request termination of web worker and free local resources after execution.
  795. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataFile
  796. *
  797. * @param {boolean} requestTerminate=false Default is false
  798. */
  799. setRequestTerminate: function ( requestTerminate ) {
  800. this.requestTerminate = requestTerminate === true;
  801. },
  802. /**
  803. * Returns all callbacks as {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks}
  804. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataFile
  805. *
  806. * @returns {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks}
  807. */
  808. getCallbacks: function () {
  809. return this.callbacks;
  810. },
  811. modelName: Validator.verifyInput( modelName, 'none' ),
  812. dataAvailable: false,
  813. pathObj: Validator.verifyInput( pathObj, null ),
  814. fileObj: Validator.verifyInput( fileObj, null ),
  815. pathTexture: Validator.verifyInput( pathTexture, null ),
  816. fileMtl: Validator.verifyInput( fileMtl, null ),
  817. sceneGraphBaseNode: null,
  818. streamMeshes: true,
  819. requestTerminate: false,
  820. callbacks: new THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks()
  821. };
  822. };
  823. /**
  824. * Callbacks utilized by functions working with {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
  825. *
  826. * @returns {{registerCallbackProgress: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackProgress, registerCallbackCompletedLoading: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackCompletedLoading, registerCallbackMaterialsLoaded: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackMaterialsLoaded, registerCallbackMeshLoaded: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackMeshLoaded, registerCallbackErrorWhileLoading: THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks.registerCallbackErrorWhileLoading, progress: null, completedLoading: null, errorWhileLoading: null, materialsLoaded: null, meshLoaded: null}}
  827. * @constructor
  828. */
  829. THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks = function () {
  830. var Validator = THREE.OBJLoader2.prototype._getValidator();
  831. return {
  832. /**
  833. * Register callback function that is invoked by internal function "_announceProgress" to print feedback.
  834. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
  835. *
  836. * @param {callback} callbackProgress Callback function for described functionality
  837. */
  838. registerCallbackProgress: function ( callbackProgress ) {
  839. if ( Validator.isValid( callbackProgress ) ) this.progress = callbackProgress;
  840. },
  841. /**
  842. * Register callback function that is called once loading of the complete model is completed.
  843. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
  844. *
  845. * @param {callback} callbackCompletedLoading Callback function for described functionality
  846. */
  847. registerCallbackCompletedLoading: function ( callbackCompletedLoading ) {
  848. if ( Validator.isValid( callbackCompletedLoading ) ) this.completedLoading = callbackCompletedLoading;
  849. },
  850. /**
  851. * Register callback function that is called once materials have been loaded. It allows to alter and return materials.
  852. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
  853. *
  854. * @param {callback} callbackMaterialsLoaded Callback function for described functionality
  855. */
  856. registerCallbackMaterialsLoaded: function ( callbackMaterialsLoaded ) {
  857. if ( Validator.isValid( callbackMaterialsLoaded ) ) this.materialsLoaded = callbackMaterialsLoaded;
  858. },
  859. /**
  860. * Register callback function that is called every time a mesh was loaded.
  861. * Use {@link THREE.OBJLoader2.WWOBJLoader2.LoadedMeshUserOverride} for alteration instructions (geometry, material or disregard mesh).
  862. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
  863. *
  864. * @param {callback} callbackMeshLoaded Callback function for described functionality
  865. */
  866. registerCallbackMeshLoaded: function ( callbackMeshLoaded ) {
  867. if ( Validator.isValid( callbackMeshLoaded ) ) this.meshLoaded = callbackMeshLoaded;
  868. },
  869. /**
  870. * Report if an error prevented loading.
  871. * @memberOf THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks
  872. *
  873. * @param {callback} callbackErrorWhileLoading Callback function for described functionality
  874. */
  875. registerCallbackErrorWhileLoading: function ( callbackErrorWhileLoading ) {
  876. if ( Validator.isValid( callbackErrorWhileLoading ) ) this.errorWhileLoading = callbackErrorWhileLoading;
  877. },
  878. progress: null,
  879. completedLoading: null,
  880. errorWhileLoading: null,
  881. materialsLoaded: null,
  882. meshLoaded: null
  883. };
  884. };
  885. /**
  886. * Object to return by {@link THREE.OBJLoader2.WWOBJLoader2}.callbacks.meshLoaded. Used to adjust bufferGeometry or material or prevent complete loading of mesh
  887. *
  888. * @param {boolean} disregardMesh=false Tell WWOBJLoader2 to completely disregard this mesh
  889. * @param {THREE.BufferGeometry} bufferGeometry The {@link THREE.BufferGeometry} to be used
  890. * @param {THREE.Material} material The {@link THREE.Material} to be used
  891. *
  892. * @returns {{ disregardMesh: boolean, replaceBufferGeometry: boolean, bufferGeometry: THREE.BufferGeometry, replaceMaterial: boolean, material: THREE.Material}}
  893. * @constructor
  894. */
  895. THREE.OBJLoader2.WWOBJLoader2.LoadedMeshUserOverride = function ( disregardMesh, bufferGeometry, material ) {
  896. var Validator = THREE.OBJLoader2.prototype._getValidator();
  897. return {
  898. disregardMesh: disregardMesh === true,
  899. replaceBufferGeometry: Validator.isValid( bufferGeometry ),
  900. bufferGeometry: Validator.verifyInput( bufferGeometry, null ),
  901. replaceMaterial: Validator.isValid( material ),
  902. material: Validator.verifyInput( material, null )
  903. };
  904. };
  905. /**
  906. * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16).
  907. * Workflow:
  908. * prepareWorkers
  909. * enqueueForRun
  910. * processQueue
  911. * deregister
  912. *
  913. * @class
  914. */
  915. THREE.OBJLoader2.WWOBJLoader2Director = (function () {
  916. var Validator = THREE.OBJLoader2.prototype._getValidator();
  917. var MAX_WEB_WORKER = 16;
  918. var MAX_QUEUE_SIZE = 1024;
  919. function WWOBJLoader2Director() {
  920. this.maxQueueSize = MAX_QUEUE_SIZE ;
  921. this.maxWebWorkers = MAX_WEB_WORKER;
  922. this.crossOrigin = null;
  923. this.workerDescription = {
  924. prototypeDef: THREE.OBJLoader2.WWOBJLoader2.prototype,
  925. globalCallbacks: {},
  926. webWorkers: [],
  927. codeBuffer: null
  928. };
  929. this.objectsCompleted = 0;
  930. this.instructionQueue = [];
  931. }
  932. /**
  933. * Returns the maximum length of the instruction queue.
  934. * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
  935. *
  936. * @returns {number}
  937. */
  938. WWOBJLoader2Director.prototype.getMaxQueueSize = function () {
  939. return this.maxQueueSize;
  940. };
  941. /**
  942. * Returns the maximum number of workers.
  943. * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
  944. *
  945. * @returns {number}
  946. */
  947. WWOBJLoader2Director.prototype.getMaxWebWorkers = function () {
  948. return this.maxWebWorkers;
  949. };
  950. /**
  951. * Sets the CORS string to be used.
  952. * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
  953. *
  954. * @param {string} crossOrigin CORS value
  955. */
  956. WWOBJLoader2Director.prototype.setCrossOrigin = function ( crossOrigin ) {
  957. this.crossOrigin = crossOrigin;
  958. };
  959. /**
  960. * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers.
  961. * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
  962. *
  963. * @param {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks} globalCallbacks Register global callbacks used by all web workers
  964. * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024)
  965. * @param {number} maxWebWorkers Set the maximum amount of workers (1-16)
  966. */
  967. WWOBJLoader2Director.prototype.prepareWorkers = function ( globalCallbacks, maxQueueSize, maxWebWorkers ) {
  968. if ( Validator.isValid( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks;
  969. this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE );
  970. this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER );
  971. this.objectsCompleted = 0;
  972. this.instructionQueue = [];
  973. var start = this.workerDescription.webWorkers.length;
  974. if ( start < this.maxWebWorkers ) {
  975. for ( i = start; i < this.maxWebWorkers; i ++ ) {
  976. webWorker = this._buildWebWorker();
  977. this.workerDescription.webWorkers[ i ] = webWorker;
  978. }
  979. } else {
  980. for ( var webWorker, i = start - 1; i >= this.maxWebWorkers; i-- ) {
  981. webWorker = this.workerDescription.webWorkers[ i ];
  982. webWorker.setRequestTerminate( true );
  983. this.workerDescription.webWorkers.pop();
  984. }
  985. }
  986. };
  987. /**
  988. * Store run instructions in internal instructionQueue.
  989. * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
  990. *
  991. * @param {Object} runParams Either {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataArrayBuffer} or {@link THREE.OBJLoader2.WWOBJLoader2.PrepDataFile}
  992. */
  993. WWOBJLoader2Director.prototype.enqueueForRun = function ( runParams ) {
  994. if ( this.instructionQueue.length < this.maxQueueSize ) {
  995. this.instructionQueue.push( runParams );
  996. }
  997. };
  998. /**
  999. * Process the instructionQueue until it is depleted.
  1000. * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
  1001. */
  1002. WWOBJLoader2Director.prototype.processQueue = function () {
  1003. if ( this.instructionQueue.length === 0 ) return;
  1004. var length = Math.min( this.maxWebWorkers, this.instructionQueue.length );
  1005. for ( var i = 0; i < length; i++ ) {
  1006. this._kickWebWorkerRun( this.workerDescription.webWorkers[ i ], this.instructionQueue[ 0 ] );
  1007. this.instructionQueue.shift();
  1008. }
  1009. };
  1010. WWOBJLoader2Director.prototype._kickWebWorkerRun = function( worker, runParams ) {
  1011. worker.clearAllCallbacks();
  1012. var key;
  1013. var globalCallbacks = this.workerDescription.globalCallbacks;
  1014. var workerCallbacks = worker.callbacks;
  1015. var selectedGlobalCallback;
  1016. for ( key in globalCallbacks ) {
  1017. if ( workerCallbacks.hasOwnProperty( key ) && globalCallbacks.hasOwnProperty( key ) ) {
  1018. selectedGlobalCallback = globalCallbacks[ key ];
  1019. if ( Validator.isValid( selectedGlobalCallback ) ) workerCallbacks[ key ].push( selectedGlobalCallback );
  1020. }
  1021. }
  1022. // register per object callbacks
  1023. var runCallbacks = runParams.callbacks;
  1024. if ( Validator.isValid( runCallbacks ) ) {
  1025. for ( key in runCallbacks ) {
  1026. if ( workerCallbacks.hasOwnProperty( key ) && runCallbacks.hasOwnProperty( key ) && Validator.isValid( runCallbacks[ key ] ) ) {
  1027. workerCallbacks[ key ].push( runCallbacks[ key ] );
  1028. }
  1029. }
  1030. }
  1031. var scope = this;
  1032. var directorCompletedLoading = function ( modelName, instanceNo, requestTerminate ) {
  1033. scope.objectsCompleted++;
  1034. if ( ! requestTerminate ) {
  1035. var worker = scope.workerDescription.webWorkers[ instanceNo ];
  1036. var runParams = scope.instructionQueue[ 0 ];
  1037. if ( Validator.isValid( runParams ) ) {
  1038. console.log( '\nAssigning next item from queue to worker (queue length: ' + scope.instructionQueue.length + ')\n\n' );
  1039. scope._kickWebWorkerRun( worker, runParams );
  1040. scope.instructionQueue.shift();
  1041. }
  1042. }
  1043. };
  1044. worker.registerCallbackCompletedLoading( directorCompletedLoading );
  1045. worker.prepareRun( runParams );
  1046. worker.run();
  1047. };
  1048. WWOBJLoader2Director.prototype._buildWebWorker = function () {
  1049. var webWorker = Object.create( this.workerDescription.prototypeDef );
  1050. webWorker._init();
  1051. if ( Validator.isValid( this.crossOrigin ) ) webWorker.setCrossOrigin( this.crossOrigin );
  1052. // Ensure code string is built once and then it is just passed on to every new instance
  1053. if ( Validator.isValid( this.workerDescription.codeBuffer ) ) {
  1054. webWorker._buildWebWorkerCode( this.workerDescription.codeBuffer );
  1055. } else {
  1056. this.workerDescription.codeBuffer = webWorker._buildWebWorkerCode();
  1057. }
  1058. webWorker.instanceNo = this.workerDescription.webWorkers.length;
  1059. this.workerDescription.webWorkers.push( webWorker );
  1060. return webWorker;
  1061. };
  1062. /**
  1063. * Terminate all workers.
  1064. * @memberOf THREE.OBJLoader2.WWOBJLoader2Director
  1065. */
  1066. WWOBJLoader2Director.prototype.deregister = function () {
  1067. console.log( 'WWOBJLoader2Director received the unregister call. Terminating all workers!' );
  1068. for ( var i = 0, webWorker, length = this.workerDescription.webWorkers.length; i < length; i++ ) {
  1069. webWorker = this.workerDescription.webWorkers[ i ];
  1070. webWorker.setRequestTerminate( true );
  1071. }
  1072. this.workerDescription.globalCallbacks = {};
  1073. this.workerDescription.webWorkers = [];
  1074. this.workerDescription.codeBuffer = null;
  1075. };
  1076. return WWOBJLoader2Director;
  1077. })();