WWOBJLoader2.js 44 KB

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