WWOBJLoader2.js 43 KB

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