WWOBJLoader2.js 46 KB

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