LoaderSupport.js 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780
  1. /**
  2. * @author Kai Salmen / https://kaisalmen.de
  3. * Development repository: https://github.com/kaisalmen/WWOBJLoader
  4. */
  5. 'use strict';
  6. if ( THREE.LoaderSupport === undefined ) { THREE.LoaderSupport = {} }
  7. /**
  8. * Validation functions.
  9. * @class
  10. */
  11. THREE.LoaderSupport.Validator = {
  12. /**
  13. * If given input is null or undefined, false is returned otherwise true.
  14. *
  15. * @param input Can be anything
  16. * @returns {boolean}
  17. */
  18. isValid: function( input ) {
  19. return ( input !== null && input !== undefined );
  20. },
  21. /**
  22. * If given input is null or undefined, the defaultValue is returned otherwise the given input.
  23. *
  24. * @param input Can be anything
  25. * @param defaultValue Can be anything
  26. * @returns {*}
  27. */
  28. verifyInput: function( input, defaultValue ) {
  29. return ( input === null || input === undefined ) ? defaultValue : input;
  30. }
  31. };
  32. /**
  33. * Logging wrapper for console.
  34. * @class
  35. *
  36. * @param {boolean} enabled=true Tell if logger is enabled.
  37. * @param {boolean} debug=false Toggle debug logging.
  38. */
  39. THREE.LoaderSupport.ConsoleLogger = (function () {
  40. function ConsoleLogger( enabled, debug ) {
  41. this.enabled = enabled !== false;
  42. this.debug = debug === true;
  43. }
  44. /**
  45. * Enable or disable debug logging.
  46. * @memberOf THREE.LoaderSupport.ConsoleLogger
  47. *
  48. * @param {boolean} debug True or False
  49. */
  50. ConsoleLogger.prototype.setDebug = function ( debug ) {
  51. this.debug = debug === true;
  52. };
  53. /**
  54. * Returns if is enabled and debug.
  55. * @memberOf THREE.LoaderSupport.ConsoleLogger
  56. *
  57. * @returns {boolean}
  58. */
  59. ConsoleLogger.prototype.isDebug = function () {
  60. return this.isEnabled() && this.debug;
  61. };
  62. /**
  63. * Enable or disable info, debug and time logging.
  64. * @memberOf THREE.LoaderSupport.ConsoleLogger
  65. *
  66. * @param {boolean} enabled True or False
  67. */
  68. ConsoleLogger.prototype.setEnabled = function ( enabled ) {
  69. this.enabled = enabled === true;
  70. };
  71. /**
  72. * Returns if is enabled.
  73. * @memberOf THREE.LoaderSupport.ConsoleLogger
  74. *
  75. * @returns {boolean}
  76. */
  77. ConsoleLogger.prototype.isEnabled = function () {
  78. return this.enabled;
  79. };
  80. /**
  81. * Log a debug message if enabled and debug is set.
  82. * @memberOf THREE.LoaderSupport.ConsoleLogger
  83. *
  84. * @param {string} message Message to log
  85. * @param {string[]} additional Array of strings containing additional content to be logged
  86. *
  87. */
  88. ConsoleLogger.prototype.logDebug = function ( message, additional ) {
  89. if ( this.enabled && this.debug ) {
  90. this._createStatement( message, 'Additional content:', additional, function ( output ) { console.debug( output ) } );
  91. }
  92. };
  93. /**
  94. * Log an info message if enabled.
  95. * @memberOf THREE.LoaderSupport.ConsoleLogger
  96. *
  97. * @param {string} message Message to log
  98. * @param {string[]} additional Array of strings containing additional content to be logged
  99. */
  100. ConsoleLogger.prototype.logInfo = function ( message, additional ) {
  101. if ( this.enabled ) {
  102. this._createStatement( message, 'Additional content:', additional, function ( output ) { console.info( output ) } );
  103. }
  104. };
  105. /**
  106. * Log a warn message (always).
  107. * @memberOf THREE.LoaderSupport.ConsoleLogger
  108. *
  109. * @param {string} message Message to log
  110. * @param {string[]} additional Array of strings containing additional content to be logged
  111. */
  112. ConsoleLogger.prototype.logWarn = function ( message, additional ) {
  113. this._createStatement( message, 'Additional content:', additional, function ( output ) { console.warn( output ) } );
  114. };
  115. /**
  116. * Log an error message (always).
  117. * @memberOf THREE.LoaderSupport.ConsoleLogger
  118. *
  119. * @param {string} message Message to log
  120. * @param {string[]} additional Array of strings containing additional content to be logged
  121. */
  122. ConsoleLogger.prototype.logError = function ( message, additional ) {
  123. this._createStatement( message, 'Additional content:', additional, function ( output ) { console.error( output ) } );
  124. };
  125. /**
  126. * Start time measurement with provided id.
  127. * @memberOf THREE.LoaderSupport.ConsoleLogger
  128. *
  129. * @param {string} id Time identification
  130. */
  131. ConsoleLogger.prototype.logTimeStart = function ( id ) {
  132. if ( this.enabled ) console.time( id );
  133. };
  134. /**
  135. * Stop time measurement started with provided id.
  136. * @memberOf THREE.LoaderSupport.ConsoleLogger
  137. *
  138. * @param {string} id Time identification
  139. */
  140. ConsoleLogger.prototype.logTimeEnd = function ( id ) {
  141. if ( this.enabled ) console.timeEnd( id );
  142. };
  143. ConsoleLogger.prototype._createStatement = function ( message, addHeader, additional, logFunction ) {
  144. var output = message;
  145. if ( Array.isArray( additional ) ) {
  146. output += '\n' + addHeader + '\n' + additional.join( '\n' );
  147. }
  148. logFunction( output );
  149. };
  150. return ConsoleLogger;
  151. })();
  152. /**
  153. * Callbacks utilized by loaders and builder.
  154. * @class
  155. */
  156. THREE.LoaderSupport.Callbacks = (function () {
  157. var Validator = THREE.LoaderSupport.Validator;
  158. function Callbacks() {
  159. this.onProgress = null;
  160. this.onMeshAlter = null;
  161. this.onLoad = null;
  162. this.onLoadMaterials = null;
  163. }
  164. /**
  165. * Register callback function that is invoked by internal function "announceProgress" to print feedback.
  166. * @memberOf THREE.LoaderSupport.Callbacks
  167. *
  168. * @param {callback} callbackOnProgress Callback function for described functionality
  169. */
  170. Callbacks.prototype.setCallbackOnProgress = function ( callbackOnProgress ) {
  171. this.onProgress = Validator.verifyInput( callbackOnProgress, this.onProgress );
  172. };
  173. /**
  174. * Register callback function that is called every time a mesh was loaded.
  175. * Use {@link THREE.LoaderSupport.LoadedMeshUserOverride} for alteration instructions (geometry, material or disregard mesh).
  176. * @memberOf THREE.LoaderSupport.Callbacks
  177. *
  178. * @param {callback} callbackOnMeshAlter Callback function for described functionality
  179. */
  180. Callbacks.prototype.setCallbackOnMeshAlter = function ( callbackOnMeshAlter ) {
  181. this.onMeshAlter = Validator.verifyInput( callbackOnMeshAlter, this.onMeshAlter );
  182. };
  183. /**
  184. * Register callback function that is called once loading of the complete OBJ file is completed.
  185. * @memberOf THREE.LoaderSupport.Callbacks
  186. *
  187. * @param {callback} callbackOnLoad Callback function for described functionality
  188. */
  189. Callbacks.prototype.setCallbackOnLoad = function ( callbackOnLoad ) {
  190. this.onLoad = Validator.verifyInput( callbackOnLoad, this.onLoad );
  191. };
  192. /**
  193. * Register callback function that is called when materials have been loaded.
  194. * @memberOf THREE.LoaderSupport.Callbacks
  195. *
  196. * @param {callback} callbackOnLoadMaterials Callback function for described functionality
  197. */
  198. Callbacks.prototype.setCallbackOnLoadMaterials = function ( callbackOnLoadMaterials ) {
  199. this.onLoadMaterials = Validator.verifyInput( callbackOnLoadMaterials, this.onLoadMaterials );
  200. };
  201. return Callbacks;
  202. })();
  203. /**
  204. * Object to return by callback onMeshAlter. Used to disregard a certain mesh or to return one to many meshes.
  205. * @class
  206. *
  207. * @param {boolean} disregardMesh=false Tell implementation to completely disregard this mesh
  208. * @param {boolean} disregardMesh=false Tell implementation that mesh(es) have been altered or added
  209. */
  210. THREE.LoaderSupport.LoadedMeshUserOverride = (function () {
  211. function LoadedMeshUserOverride( disregardMesh, alteredMesh ) {
  212. this.disregardMesh = disregardMesh === true;
  213. this.alteredMesh = alteredMesh === true;
  214. this.meshes = [];
  215. }
  216. /**
  217. * Add a mesh created within callback.
  218. *
  219. * @memberOf THREE.OBJLoader2.LoadedMeshUserOverride
  220. *
  221. * @param {THREE.Mesh} mesh
  222. */
  223. LoadedMeshUserOverride.prototype.addMesh = function ( mesh ) {
  224. this.meshes.push( mesh );
  225. this.alteredMesh = true;
  226. };
  227. /**
  228. * Answers if mesh shall be disregarded completely.
  229. *
  230. * @returns {boolean}
  231. */
  232. LoadedMeshUserOverride.prototype.isDisregardMesh = function () {
  233. return this.disregardMesh;
  234. };
  235. /**
  236. * Answers if new mesh(es) were created.
  237. *
  238. * @returns {boolean}
  239. */
  240. LoadedMeshUserOverride.prototype.providesAlteredMeshes = function () {
  241. return this.alteredMesh;
  242. };
  243. return LoadedMeshUserOverride;
  244. })();
  245. /**
  246. * A resource description used by {@link THREE.LoaderSupport.PrepData} and others.
  247. * @class
  248. *
  249. * @param {string} url URL to the file
  250. * @param {string} extension The file extension (type)
  251. */
  252. THREE.LoaderSupport.ResourceDescriptor = (function () {
  253. var Validator = THREE.LoaderSupport.Validator;
  254. function ResourceDescriptor( url, extension ) {
  255. var urlParts = url.split( '/' );
  256. if ( urlParts.length < 2 ) {
  257. this.path = null;
  258. this.name = url;
  259. this.url = url;
  260. } else {
  261. this.path = Validator.verifyInput( urlParts.slice( 0, urlParts.length - 1).join( '/' ) + '/', null );
  262. this.name = Validator.verifyInput( urlParts[ urlParts.length - 1 ], null );
  263. this.url = url;
  264. }
  265. this.extension = Validator.verifyInput( extension, "default" );
  266. this.extension = this.extension.trim();
  267. this.content = null;
  268. }
  269. /**
  270. * Set the content of this resource (String)
  271. * @memberOf THREE.LoaderSupport.ResourceDescriptor
  272. *
  273. * @param {Object} content The file content as arraybuffer or text
  274. */
  275. ResourceDescriptor.prototype.setContent = function ( content ) {
  276. this.content = Validator.verifyInput( content, null );
  277. };
  278. return ResourceDescriptor;
  279. })();
  280. /**
  281. * Configuration instructions to be used by run method.
  282. * @class
  283. */
  284. THREE.LoaderSupport.PrepData = (function () {
  285. var Validator = THREE.LoaderSupport.Validator;
  286. function PrepData( modelName ) {
  287. this.modelName = Validator.verifyInput( modelName, '' );
  288. this.resources = [];
  289. this.streamMeshesTo = null;
  290. this.materialPerSmoothingGroup = false;
  291. this.useIndices = false;
  292. this.disregardNormals = false;
  293. this.callbacks = new THREE.LoaderSupport.Callbacks();
  294. this.crossOrigin;
  295. this.useAsync = false;
  296. }
  297. /**
  298. * Set the node where the loaded objects will be attached directly.
  299. * @memberOf THREE.LoaderSupport.PrepData
  300. *
  301. * @param {THREE.Object3D} streamMeshesTo Object already attached to scenegraph where new meshes will be attached to
  302. */
  303. PrepData.prototype.setStreamMeshesTo = function ( streamMeshesTo ) {
  304. this.streamMeshesTo = Validator.verifyInput( streamMeshesTo, null );
  305. };
  306. /**
  307. * Tells whether a material shall be created per smoothing group.
  308. * @memberOf THREE.LoaderSupport.PrepData
  309. *
  310. * @param {boolean} materialPerSmoothingGroup=false
  311. */
  312. PrepData.prototype.setMaterialPerSmoothingGroup = function ( materialPerSmoothingGroup ) {
  313. this.materialPerSmoothingGroup = materialPerSmoothingGroup === true;
  314. };
  315. /**
  316. * Tells whether indices should be used
  317. * @memberOf THREE.LoaderSupport.PrepData
  318. *
  319. * @param {boolean} useIndices=false
  320. */
  321. PrepData.prototype.setUseIndices = function ( useIndices ) {
  322. this.useIndices = useIndices === true;
  323. };
  324. /**
  325. * Tells whether normals should be completely disregarded and regenerated.
  326. * @memberOf THREE.LoaderSupport.PrepData
  327. *
  328. * @param {boolean} disregardNormals=false
  329. */
  330. PrepData.prototype.setDisregardNormals = function ( disregardNormals ) {
  331. this.disregardNormals = disregardNormals === true;
  332. };
  333. /**
  334. * Returns all callbacks as {@link THREE.LoaderSupport.Callbacks}
  335. * @memberOf THREE.LoaderSupport.PrepData
  336. *
  337. * @returns {THREE.LoaderSupport.Callbacks}
  338. */
  339. PrepData.prototype.getCallbacks = function () {
  340. return this.callbacks;
  341. };
  342. /**
  343. * Sets the CORS string to be used.
  344. * @memberOf THREE.LoaderSupport.PrepData
  345. *
  346. * @param {string} crossOrigin CORS value
  347. */
  348. PrepData.prototype.setCrossOrigin = function ( crossOrigin ) {
  349. this.crossOrigin = crossOrigin;
  350. };
  351. /**
  352. * Add a resource description.
  353. * @memberOf THREE.LoaderSupport.PrepData
  354. *
  355. * @param {THREE.LoaderSupport.ResourceDescriptor}
  356. */
  357. PrepData.prototype.addResource = function ( resource ) {
  358. this.resources.push( resource );
  359. };
  360. /**
  361. * If true uses async loading with worker, if false loads data synchronously.
  362. * @memberOf THREE.LoaderSupport.PrepData
  363. *
  364. * @param {boolean} useAsync
  365. */
  366. PrepData.prototype.setUseAsync = function ( useAsync ) {
  367. this.useAsync = useAsync === true;
  368. };
  369. /**
  370. * Clones this object and returns it afterwards.
  371. * @memberOf THREE.LoaderSupport.PrepData
  372. *
  373. * @returns {@link THREE.LoaderSupport.PrepData}
  374. */
  375. PrepData.prototype.clone = function () {
  376. var clone = new THREE.LoaderSupport.PrepData( this.modelName );
  377. clone.resources = this.resources;
  378. clone.streamMeshesTo = this.streamMeshesTo;
  379. clone.materialPerSmoothingGroup = this.materialPerSmoothingGroup;
  380. clone.useIndices = this.useIndices;
  381. clone.disregardNormals = this.disregardNormals;
  382. clone.callbacks = this.callbacks;
  383. clone.crossOrigin = this.crossOrigin;
  384. clone.useAsync = this.useAsync;
  385. return clone;
  386. };
  387. return PrepData;
  388. })();
  389. /**
  390. * Builds one or many THREE.Mesh from one raw set of Arraybuffers, materialGroup descriptions and further parameters.
  391. * Supports vertex, vertexColor, normal, uv and index buffers.
  392. * @class
  393. */
  394. THREE.LoaderSupport.Builder = (function () {
  395. var LOADER_BUILDER_VERSION = '1.1.1';
  396. var Validator = THREE.LoaderSupport.Validator;
  397. var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger;
  398. function Builder( logger ) {
  399. this.logger = Validator.verifyInput( logger, new ConsoleLogger() );
  400. this.logger.logInfo( 'Using THREE.LoaderSupport.Builder version: ' + LOADER_BUILDER_VERSION );
  401. this.callbacks = new THREE.LoaderSupport.Callbacks();
  402. this.materials = [];
  403. this._createDefaultMaterials();
  404. }
  405. Builder.prototype._createDefaultMaterials = function () {
  406. var defaultMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } );
  407. defaultMaterial.name = 'defaultMaterial';
  408. var defaultVertexColorMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } );
  409. defaultVertexColorMaterial.name = 'defaultVertexColorMaterial';
  410. defaultVertexColorMaterial.vertexColors = THREE.VertexColors;
  411. var defaultLineMaterial = new THREE.LineBasicMaterial();
  412. defaultLineMaterial.name = 'defaultLineMaterial';
  413. var defaultPointMaterial = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
  414. defaultPointMaterial.name = 'defaultPointMaterial';
  415. var runtimeMaterials = {};
  416. runtimeMaterials[ defaultMaterial.name ] = defaultMaterial;
  417. runtimeMaterials[ defaultVertexColorMaterial.name ] = defaultVertexColorMaterial;
  418. runtimeMaterials[ defaultLineMaterial.name ] = defaultLineMaterial;
  419. runtimeMaterials[ defaultPointMaterial.name ] = defaultPointMaterial;
  420. this.updateMaterials(
  421. {
  422. cmd: 'materialData',
  423. materials: {
  424. materialCloneInstructions: null,
  425. serializedMaterials: null,
  426. runtimeMaterials: runtimeMaterials
  427. }
  428. }
  429. );
  430. };
  431. /**
  432. * Set materials loaded by any supplier of an Array of {@link THREE.Material}.
  433. * @memberOf THREE.LoaderSupport.Builder
  434. *
  435. * @param {THREE.Material[]} materials Array of {@link THREE.Material}
  436. */
  437. Builder.prototype.setMaterials = function ( materials ) {
  438. var payload = {
  439. cmd: 'materialData',
  440. materials: {
  441. materialCloneInstructions: null,
  442. serializedMaterials: null,
  443. runtimeMaterials: Validator.isValid( this.callbacks.onLoadMaterials ) ? this.callbacks.onLoadMaterials( materials ) : materials
  444. }
  445. };
  446. this.updateMaterials( payload );
  447. };
  448. Builder.prototype._setCallbacks = function ( callbacks ) {
  449. if ( Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress );
  450. if ( Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter );
  451. if ( Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad );
  452. if ( Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials );
  453. };
  454. /**
  455. * Delegates processing of the payload (mesh building or material update) to the corresponding functions (BW-compatibility).
  456. * @memberOf THREE.LoaderSupport.Builder
  457. *
  458. * @param {Object} payload Raw Mesh or Material descriptions.
  459. * @returns {THREE.Mesh[]} mesh Array of {@link THREE.Mesh} or null in case of material update
  460. */
  461. Builder.prototype.processPayload = function ( payload ) {
  462. if ( payload.cmd === 'meshData' ) {
  463. return this.buildMeshes( payload );
  464. } else if ( payload.cmd === 'materialData' ) {
  465. this.updateMaterials( payload );
  466. return null;
  467. }
  468. };
  469. /**
  470. * Builds one or multiple meshes from the data described in the payload (buffers, params, material info).
  471. * @memberOf THREE.LoaderSupport.Builder
  472. *
  473. * @param {Object} meshPayload Raw mesh description (buffers, params, materials) used to build one to many meshes.
  474. * @returns {THREE.Mesh[]} mesh Array of {@link THREE.Mesh}
  475. */
  476. Builder.prototype.buildMeshes = function ( meshPayload ) {
  477. var meshName = meshPayload.params.meshName;
  478. var bufferGeometry = new THREE.BufferGeometry();
  479. bufferGeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.vertices ), 3 ) );
  480. if ( Validator.isValid( meshPayload.buffers.indices ) ) {
  481. bufferGeometry.setIndex( new THREE.BufferAttribute( new Uint32Array( meshPayload.buffers.indices ), 1 ));
  482. }
  483. var haveVertexColors = Validator.isValid( meshPayload.buffers.colors );
  484. if ( haveVertexColors ) {
  485. bufferGeometry.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.colors ), 3 ) );
  486. }
  487. if ( Validator.isValid( meshPayload.buffers.normals ) ) {
  488. bufferGeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.normals ), 3 ) );
  489. } else {
  490. bufferGeometry.computeVertexNormals();
  491. }
  492. if ( Validator.isValid( meshPayload.buffers.uvs ) ) {
  493. bufferGeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.uvs ), 2 ) );
  494. }
  495. var material, materialName, key;
  496. var materialNames = meshPayload.materials.materialNames;
  497. var createMultiMaterial = meshPayload.materials.multiMaterial;
  498. var multiMaterials = [];
  499. for ( key in materialNames ) {
  500. materialName = materialNames[ key ];
  501. material = this.materials[ materialName ];
  502. if ( createMultiMaterial ) multiMaterials.push( material );
  503. }
  504. if ( createMultiMaterial ) {
  505. material = multiMaterials;
  506. var materialGroups = meshPayload.materials.materialGroups;
  507. var materialGroup;
  508. for ( key in materialGroups ) {
  509. materialGroup = materialGroups[ key ];
  510. bufferGeometry.addGroup( materialGroup.start, materialGroup.count, materialGroup.index );
  511. }
  512. }
  513. var meshes = [];
  514. var mesh;
  515. var callbackOnMeshAlter = this.callbacks.onMeshAlter;
  516. var callbackOnMeshAlterResult;
  517. var useOrgMesh = true;
  518. var geometryType = Validator.verifyInput( meshPayload.geometryType, 0 );
  519. if ( Validator.isValid( callbackOnMeshAlter ) ) {
  520. callbackOnMeshAlterResult = callbackOnMeshAlter(
  521. {
  522. detail: {
  523. meshName: meshName,
  524. bufferGeometry: bufferGeometry,
  525. material: material,
  526. geometryType: geometryType
  527. }
  528. }
  529. );
  530. if ( Validator.isValid( callbackOnMeshAlterResult ) ) {
  531. if ( ! callbackOnMeshAlterResult.isDisregardMesh() && callbackOnMeshAlterResult.providesAlteredMeshes() ) {
  532. for ( var i in callbackOnMeshAlterResult.meshes ) {
  533. meshes.push( callbackOnMeshAlterResult.meshes[ i ] );
  534. }
  535. }
  536. useOrgMesh = false;
  537. }
  538. }
  539. if ( useOrgMesh ) {
  540. if ( meshPayload.computeBoundingSphere ) bufferGeometry.computeBoundingSphere();
  541. if ( geometryType === 0 ) {
  542. mesh = new THREE.Mesh( bufferGeometry, material );
  543. } else if ( geometryType === 1) {
  544. mesh = new THREE.LineSegments( bufferGeometry, material );
  545. } else {
  546. mesh = new THREE.Points( bufferGeometry, material );
  547. }
  548. mesh.name = meshName;
  549. meshes.push( mesh );
  550. }
  551. var progressMessage;
  552. if ( Validator.isValid( meshes ) && meshes.length > 0 ) {
  553. var meshNames = [];
  554. for ( var i in meshes ) {
  555. mesh = meshes[ i ];
  556. meshNames[ i ] = mesh.name;
  557. }
  558. progressMessage = 'Adding mesh(es) (' + meshNames.length + ': ' + meshNames + ') from input mesh: ' + meshName;
  559. progressMessage += ' (' + ( meshPayload.progress.numericalValue * 100 ).toFixed( 2 ) + '%)';
  560. } else {
  561. progressMessage = 'Not adding mesh: ' + meshName;
  562. progressMessage += ' (' + ( meshPayload.progress.numericalValue * 100 ).toFixed( 2 ) + '%)';
  563. }
  564. var callbackOnProgress = this.callbacks.onProgress;
  565. if ( Validator.isValid( callbackOnProgress ) ) {
  566. var event = new CustomEvent( 'BuilderEvent', {
  567. detail: {
  568. type: 'progress',
  569. modelName: meshPayload.params.meshName,
  570. text: progressMessage,
  571. numericalValue: meshPayload.progress.numericalValue
  572. }
  573. } );
  574. callbackOnProgress( event );
  575. }
  576. return meshes;
  577. };
  578. /**
  579. * Updates the materials with contained material objects (sync) or from alteration instructions (async).
  580. * @memberOf THREE.LoaderSupport.Builder
  581. *
  582. * @param {Object} materialPayload Material update instructions
  583. */
  584. Builder.prototype.updateMaterials = function ( materialPayload ) {
  585. var material, materialName;
  586. var materialCloneInstructions = materialPayload.materials.materialCloneInstructions;
  587. if ( Validator.isValid( materialCloneInstructions ) ) {
  588. var materialNameOrg = materialCloneInstructions.materialNameOrg;
  589. var materialOrg = this.materials[ materialNameOrg ];
  590. if ( Validator.isValid( materialNameOrg ) ) {
  591. material = materialOrg.clone();
  592. materialName = materialCloneInstructions.materialName;
  593. material.name = materialName;
  594. var materialProperties = materialCloneInstructions.materialProperties;
  595. for ( var key in materialProperties ) {
  596. if ( material.hasOwnProperty( key ) && materialProperties.hasOwnProperty( key ) ) material[ key ] = materialProperties[ key ];
  597. }
  598. this.materials[ materialName ] = material;
  599. } else {
  600. this.logger.logWarn( 'Requested material "' + materialNameOrg + '" is not available!' );
  601. }
  602. }
  603. var materials = materialPayload.materials.serializedMaterials;
  604. if ( Validator.isValid( materials ) && Object.keys( materials ).length > 0 ) {
  605. var loader = new THREE.MaterialLoader();
  606. var materialJson;
  607. for ( materialName in materials ) {
  608. materialJson = materials[ materialName ];
  609. if ( Validator.isValid( materialJson ) ) {
  610. material = loader.parse( materialJson );
  611. this.logger.logInfo( 'De-serialized material with name "' + materialName + '" will be added.' );
  612. this.materials[ materialName ] = material;
  613. }
  614. }
  615. }
  616. materials = materialPayload.materials.runtimeMaterials;
  617. if ( Validator.isValid( materials ) && Object.keys( materials ).length > 0 ) {
  618. for ( materialName in materials ) {
  619. material = materials[ materialName ];
  620. this.logger.logInfo( 'Material with name "' + materialName + '" will be added.' );
  621. this.materials[ materialName ] = material;
  622. }
  623. }
  624. };
  625. /**
  626. * Returns the mapping object of material name and corresponding jsonified material.
  627. *
  628. * @returns {Object} Map of Materials in JSON representation
  629. */
  630. Builder.prototype.getMaterialsJSON = function () {
  631. var materialsJSON = {};
  632. var material;
  633. for ( var materialName in this.materials ) {
  634. material = this.materials[ materialName ];
  635. materialsJSON[ materialName ] = material.toJSON();
  636. }
  637. return materialsJSON;
  638. };
  639. /**
  640. * Returns the mapping object of material name and corresponding material.
  641. *
  642. * @returns {Object} Map of {@link THREE.Material}
  643. */
  644. Builder.prototype.getMaterials = function () {
  645. return this.materials;
  646. };
  647. return Builder;
  648. })();
  649. /**
  650. * Base class to be used by Loaders that provide load, parse, parseAsync and run
  651. * @class
  652. *
  653. * @param {THREE.DefaultLoadingManager} [manager] The loadingManager for the loader to use. Default is {@link THREE.DefaultLoadingManager}
  654. * @param {THREE.LoaderSupport.ConsoleLogger} logger logger to be used
  655. */
  656. THREE.LoaderSupport.LoaderBase = (function () {
  657. var Validator = THREE.LoaderSupport.Validator;
  658. var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger;
  659. function LoaderBase( manager, logger ) {
  660. this.manager = Validator.verifyInput( manager, THREE.DefaultLoadingManager );
  661. this.logger = Validator.verifyInput( logger, new ConsoleLogger() );
  662. this.fileLoader = new THREE.FileLoader( this.manager );
  663. this.fileLoader.setResponseType( 'arraybuffer' );
  664. this.modelName = '';
  665. this.instanceNo = 0;
  666. this.path = '';
  667. this.useIndices = false;
  668. this.disregardNormals = false;
  669. this.loaderRootNode = new THREE.Group();
  670. this.builder = new THREE.LoaderSupport.Builder( this.logger );
  671. this.callbacks = new THREE.LoaderSupport.Callbacks();
  672. }
  673. LoaderBase.prototype._applyPrepData = function ( prepData ) {
  674. if ( Validator.isValid( prepData ) ) {
  675. this.setModelName( prepData.modelName );
  676. this.setStreamMeshesTo( prepData.streamMeshesTo );
  677. this.builder.setMaterials( prepData.materials );
  678. this.setUseIndices( prepData.useIndices );
  679. this.setDisregardNormals( prepData.disregardNormals );
  680. this._setCallbacks( prepData.getCallbacks() );
  681. }
  682. };
  683. LoaderBase.prototype._setCallbacks = function ( callbacks ) {
  684. if ( Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress );
  685. if ( Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter );
  686. if ( Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad );
  687. if ( Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials );
  688. this.builder._setCallbacks( this.callbacks );
  689. };
  690. /**
  691. * Provides access to console logging wrapper.
  692. *
  693. * @returns {THREE.LoaderSupport.ConsoleLogger}
  694. */
  695. LoaderBase.prototype.getLogger = function () {
  696. return this.logger;
  697. };
  698. /**
  699. * Set the name of the model.
  700. * @memberOf THREE.LoaderSupport.LoaderBase
  701. *
  702. * @param {string} modelName
  703. */
  704. LoaderBase.prototype.setModelName = function ( modelName ) {
  705. this.modelName = Validator.verifyInput( modelName, this.modelName );
  706. };
  707. /**
  708. * The URL of the base path.
  709. * @memberOf THREE.LoaderSupport.LoaderBase
  710. *
  711. * @param {string} path URL
  712. */
  713. LoaderBase.prototype.setPath = function ( path ) {
  714. this.path = Validator.verifyInput( path, this.path );
  715. };
  716. /**
  717. * Set the node where the loaded objects will be attached directly.
  718. * @memberOf THREE.LoaderSupport.LoaderBase
  719. *
  720. * @param {THREE.Object3D} streamMeshesTo Object already attached to scenegraph where new meshes will be attached to
  721. */
  722. LoaderBase.prototype.setStreamMeshesTo = function ( streamMeshesTo ) {
  723. this.loaderRootNode = Validator.verifyInput( streamMeshesTo, this.loaderRootNode );
  724. };
  725. /**
  726. * Set materials loaded by MTLLoader or any other supplier of an Array of {@link THREE.Material}.
  727. * @memberOf THREE.LoaderSupport.LoaderBase
  728. *
  729. * @param {THREE.Material[]} materials Array of {@link THREE.Material}
  730. */
  731. LoaderBase.prototype.setMaterials = function ( materials ) {
  732. this.builder.setMaterials( materials );
  733. };
  734. /**
  735. * Instructs loaders to create indexed {@link THREE.BufferGeometry}.
  736. * @memberOf THREE.LoaderSupport.LoaderBase
  737. *
  738. * @param {boolean} useIndices=false
  739. */
  740. LoaderBase.prototype.setUseIndices = function ( useIndices ) {
  741. this.useIndices = useIndices === true;
  742. };
  743. /**
  744. * Tells whether normals should be completely disregarded and regenerated.
  745. * @memberOf THREE.LoaderSupport.LoaderBase
  746. *
  747. * @param {boolean} disregardNormals=false
  748. */
  749. LoaderBase.prototype.setDisregardNormals = function ( disregardNormals ) {
  750. this.disregardNormals = disregardNormals === true;
  751. };
  752. /**
  753. * Announce feedback which is give to the registered callbacks.
  754. * @memberOf THREE.LoaderSupport.LoaderBase
  755. * @private
  756. *
  757. * @param {string} type The type of event
  758. * @param {string} text Textual description of the event
  759. * @param {number} numericalValue Numerical value describing the progress
  760. */
  761. LoaderBase.prototype.onProgress = function ( type, text, numericalValue ) {
  762. var content = Validator.isValid( text ) ? text: '';
  763. var event = {
  764. detail: {
  765. type: type,
  766. modelName: this.modelName,
  767. instanceNo: this.instanceNo,
  768. text: content,
  769. numericalValue: numericalValue
  770. }
  771. };
  772. if ( Validator.isValid( this.callbacks.onProgress ) ) this.callbacks.onProgress( event );
  773. this.logger.logDebug( content );
  774. };
  775. /**
  776. * Use this convenient method to load a file at the given URL. By default the fileLoader uses an ArrayBuffer.
  777. * @memberOf THREE.LoaderSupport.LoaderBase
  778. *
  779. * @param {string} url A string containing the path/URL of the file to be loaded.
  780. * @param {callback} onLoad A function to be called after loading is successfully completed. The function receives loaded Object3D as an argument.
  781. * @param {callback} [onProgress] A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains total and Integer bytes.
  782. * @param {callback} [onError] A function to be called if an error occurs during loading. The function receives the error as an argument.
  783. * @param {callback} [onMeshAlter] A function to be called after a new mesh raw data becomes available for alteration.
  784. * @param {boolean} [useAsync] If true, uses async loading with worker, if false loads data synchronously.
  785. */
  786. LoaderBase.prototype.load = function ( url, onLoad, onProgress, onError, onMeshAlter, useAsync ) {
  787. var scope = this;
  788. if ( ! Validator.isValid( onProgress ) ) {
  789. var numericalValueRef = 0;
  790. var numericalValue = 0;
  791. onProgress = function ( event ) {
  792. if ( ! event.lengthComputable ) return;
  793. numericalValue = event.loaded / event.total;
  794. if ( numericalValue > numericalValueRef ) {
  795. numericalValueRef = numericalValue;
  796. var output = 'Download of "' + url + '": ' + ( numericalValue * 100 ).toFixed( 2 ) + '%';
  797. scope.onProgress( 'progressLoad', output, numericalValue );
  798. }
  799. };
  800. }
  801. if ( ! Validator.isValid( onError ) ) {
  802. onError = function ( event ) {
  803. var output = 'Error occurred while downloading "' + url + '"';
  804. scope.logger.logError( output + ': ' + event );
  805. scope.onProgress( 'error', output, -1 );
  806. };
  807. }
  808. this.fileLoader.setPath( this.path );
  809. this.fileLoader.load( url, function ( content ) {
  810. if ( useAsync ) {
  811. scope.parseAsync( content, onLoad );
  812. } else {
  813. var callbacks = new THREE.LoaderSupport.Callbacks();
  814. callbacks.setCallbackOnMeshAlter( onMeshAlter );
  815. scope._setCallbacks( callbacks );
  816. onLoad(
  817. {
  818. detail: {
  819. loaderRootNode: scope.parse( content ),
  820. modelName: scope.modelName,
  821. instanceNo: scope.instanceNo
  822. }
  823. }
  824. );
  825. }
  826. }, onProgress, onError );
  827. };
  828. /**
  829. * Identify files or content of interest from an Array of {@link THREE.LoaderSupport.ResourceDescriptor}.
  830. *
  831. * @param {THREE.LoaderSupport.ResourceDescriptor[]} resources Array of {@link THREE.LoaderSupport.ResourceDescriptor}
  832. * @param Object fileDesc Object describing which resources are of interest (ext, type (string or UInt8Array) and ignore (boolean))
  833. * @returns {{}} Object with each "ext" and the corresponding {@link THREE.LoaderSupport.ResourceDescriptor}
  834. */
  835. LoaderBase.prototype.checkResourceDescriptorFiles = function ( resources, fileDesc ) {
  836. var resource, triple, i, found;
  837. var result = {};
  838. for ( var index in resources ) {
  839. resource = resources[ index ];
  840. found = false;
  841. if ( ! Validator.isValid( resource.name ) ) continue;
  842. if ( Validator.isValid( resource.content ) ) {
  843. for ( i = 0; i < fileDesc.length && !found; i++ ) {
  844. triple = fileDesc[ i ];
  845. if ( resource.extension.toLowerCase() === triple.ext.toLowerCase() ) {
  846. if ( triple.ignore ) {
  847. found = true;
  848. } else if ( triple.type === "Uint8Array" ) {
  849. // fast-fail on bad type
  850. if ( ! ( resource.content instanceof Uint8Array ) ) throw 'Provided content is not of type arraybuffer! Aborting...';
  851. result[ triple.ext ] = resource;
  852. found = true;
  853. } else if ( triple.type === "String" ) {
  854. if ( ! (typeof(resource.content) === 'string' || resource.content instanceof String) ) throw 'Provided content is not of type String! Aborting...';
  855. result[ triple.ext ] = resource;
  856. found = true;
  857. }
  858. }
  859. }
  860. if ( !found ) throw 'Unidentified resource "' + resource.name + '": ' + resource.url;
  861. } else {
  862. // fast-fail on bad type
  863. if ( ! ( typeof( resource.name ) === 'string' || resource.name instanceof String ) ) throw 'Provided file is not properly defined! Aborting...';
  864. for ( i = 0; i < fileDesc.length && !found; i++ ) {
  865. triple = fileDesc[ i ];
  866. if ( resource.extension.toLowerCase() === triple.ext.toLowerCase() ) {
  867. if ( ! triple.ignore ) result[ triple.ext ] = resource;
  868. found = true;
  869. }
  870. }
  871. if ( !found ) throw 'Unidentified resource "' + resource.name + '": ' + resource.url;
  872. }
  873. }
  874. return result;
  875. };
  876. return LoaderBase;
  877. })();
  878. /**
  879. * Default implementation of the WorkerRunner responsible for creation and configuration of the parser within the worker.
  880. *
  881. * @class
  882. */
  883. THREE.LoaderSupport.WorkerRunnerRefImpl = (function () {
  884. function WorkerRunnerRefImpl() {
  885. var scope = this;
  886. var scopedRunner = function( event ) {
  887. scope.processMessage( event.data );
  888. };
  889. self.addEventListener( 'message', scopedRunner, false );
  890. }
  891. /**
  892. * Applies values from parameter object via set functions or via direct assignment.
  893. * @memberOf THREE.LoaderSupport.WorkerRunnerRefImpl
  894. *
  895. * @param {Object} parser The parser instance
  896. * @param {Object} params The parameter object
  897. */
  898. WorkerRunnerRefImpl.prototype.applyProperties = function ( parser, params ) {
  899. var property, funcName, values;
  900. for ( property in params ) {
  901. funcName = 'set' + property.substring( 0, 1 ).toLocaleUpperCase() + property.substring( 1 );
  902. values = params[ property ];
  903. if ( typeof parser[ funcName ] === 'function' ) {
  904. parser[ funcName ]( values );
  905. } else if ( parser.hasOwnProperty( property ) ) {
  906. parser[ property ] = values;
  907. }
  908. }
  909. };
  910. /**
  911. * Configures the Parser implementation according the supplied configuration object.
  912. * @memberOf THREE.LoaderSupport.WorkerRunnerRefImpl
  913. *
  914. * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
  915. */
  916. WorkerRunnerRefImpl.prototype.processMessage = function ( payload ) {
  917. var logEnabled = payload.logger.enabled;
  918. var logDebug = payload.logger.enabled;
  919. if ( payload.cmd === 'run' ) {
  920. var callbacks = {
  921. callbackBuilder: function ( payload ) {
  922. self.postMessage( payload );
  923. },
  924. callbackProgress: function ( text ) {
  925. if ( logEnabled && logDebug ) console.debug( 'WorkerRunner: progress: ' + text );
  926. }
  927. };
  928. // Parser is expected to be named as such
  929. var parser = new Parser();
  930. if ( typeof parser[ 'setLogConfig' ] === 'function' ) parser.setLogConfig( logEnabled, logDebug );
  931. this.applyProperties( parser, payload.params );
  932. this.applyProperties( parser, payload.materials );
  933. this.applyProperties( parser, callbacks );
  934. parser.workerScope = self;
  935. parser.parse( payload.data.input, payload.data.options );
  936. if ( logEnabled ) console.log( 'WorkerRunner: Run complete!' );
  937. callbacks.callbackBuilder( {
  938. cmd: 'complete',
  939. msg: 'WorkerRunner completed run.'
  940. } );
  941. } else {
  942. console.error( 'WorkerRunner: Received unknown command: ' + payload.cmd );
  943. }
  944. };
  945. return WorkerRunnerRefImpl;
  946. })();
  947. /**
  948. * This class provides means to transform existing parser code into a web worker. It defines a simple communication protocol
  949. * which allows to configure the worker and receive raw mesh data during execution.
  950. * @class
  951. *
  952. * @param {THREE.LoaderSupport.ConsoleLogger} logger logger to be used
  953. */
  954. THREE.LoaderSupport.WorkerSupport = (function () {
  955. var WORKER_SUPPORT_VERSION = '2.1.2';
  956. var Validator = THREE.LoaderSupport.Validator;
  957. var LoaderWorker = (function () {
  958. function LoaderWorker( logger ) {
  959. this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() );
  960. this._reset();
  961. }
  962. LoaderWorker.prototype._reset = function () {
  963. this.worker = null;
  964. this.runnerImplName = null;
  965. this.callbacks = {
  966. builder: null,
  967. onLoad: null
  968. };
  969. this.terminateRequested = false;
  970. this.queuedMessage = null;
  971. this.started = false;
  972. };
  973. LoaderWorker.prototype.initWorker = function ( code, runnerImplName ) {
  974. this.runnerImplName = runnerImplName;
  975. var blob = new Blob( [ code ], { type: 'application/javascript' } );
  976. this.worker = new Worker( window.URL.createObjectURL( blob ) );
  977. this.worker.onmessage = this._receiveWorkerMessage;
  978. // set referemce to this, then processing in worker scope within "_receiveWorkerMessage" can access members
  979. this.worker.runtimeRef = this;
  980. // process stored queuedMessage
  981. this._postMessage();
  982. };
  983. /**
  984. * Executed in worker scope
  985. */
  986. LoaderWorker.prototype._receiveWorkerMessage = function ( e ) {
  987. var payload = e.data;
  988. switch ( payload.cmd ) {
  989. case 'meshData':
  990. case 'materialData':
  991. case 'imageData':
  992. this.runtimeRef.callbacks.builder( payload );
  993. break;
  994. case 'complete':
  995. this.runtimeRef.queuedMessage = null;
  996. this.started = false;
  997. this.runtimeRef.callbacks.onLoad( payload.msg );
  998. if ( this.runtimeRef.terminateRequested ) {
  999. this.runtimeRef.logger.logInfo( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run is complete. Terminating application on request!' );
  1000. this.runtimeRef._terminate();
  1001. }
  1002. break;
  1003. case 'error':
  1004. this.runtimeRef.logger.logError( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Reported error: ' + payload.msg );
  1005. this.runtimeRef.queuedMessage = null;
  1006. this.started = false;
  1007. this.runtimeRef.callbacks.onLoad( payload.msg );
  1008. if ( this.runtimeRef.terminateRequested ) {
  1009. this.runtimeRef.logger.logInfo( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run reported error. Terminating application on request!' );
  1010. this.runtimeRef._terminate();
  1011. }
  1012. break;
  1013. default:
  1014. this.runtimeRef.logger.logError( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Received unknown command: ' + payload.cmd );
  1015. break;
  1016. }
  1017. };
  1018. LoaderWorker.prototype.setCallbacks = function ( builder, onLoad ) {
  1019. this.callbacks.builder = Validator.verifyInput( builder, this.callbacks.builder );
  1020. this.callbacks.onLoad = Validator.verifyInput( onLoad, this.callbacks.onLoad );
  1021. };
  1022. LoaderWorker.prototype.run = function( payload ) {
  1023. if ( Validator.isValid( this.queuedMessage ) ) {
  1024. console.warn( 'Already processing message. Rejecting new run instruction' );
  1025. return;
  1026. } else {
  1027. this.queuedMessage = payload;
  1028. this.started = true;
  1029. }
  1030. if ( ! Validator.isValid( this.callbacks.builder ) ) throw 'Unable to run as no "builder" callback is set.';
  1031. if ( ! Validator.isValid( this.callbacks.onLoad ) ) throw 'Unable to run as no "onLoad" callback is set.';
  1032. if ( payload.cmd !== 'run' ) payload.cmd = 'run';
  1033. if ( Validator.isValid( payload.logger ) ) {
  1034. payload.logger.enabled = Validator.verifyInput( payload.logger.enabled, true );
  1035. payload.logger.debug = Validator.verifyInput( payload.logger.debug, false );
  1036. } else {
  1037. payload.logger = {
  1038. enabled: true,
  1039. debug: false
  1040. }
  1041. }
  1042. this._postMessage();
  1043. };
  1044. LoaderWorker.prototype._postMessage = function () {
  1045. if ( Validator.isValid( this.queuedMessage ) && Validator.isValid( this.worker ) ) {
  1046. if ( this.queuedMessage.data.input instanceof ArrayBuffer ) {
  1047. this.worker.postMessage( this.queuedMessage, [ this.queuedMessage.data.input ] );
  1048. } else {
  1049. this.worker.postMessage( this.queuedMessage );
  1050. }
  1051. }
  1052. };
  1053. LoaderWorker.prototype.setTerminateRequested = function ( terminateRequested ) {
  1054. this.terminateRequested = terminateRequested === true;
  1055. if ( this.terminateRequested && Validator.isValid( this.worker ) && ! Validator.isValid( this.queuedMessage ) && this.started ) {
  1056. this.logger.logInfo( 'Worker is terminated immediately as it is not running!' );
  1057. this._terminate();
  1058. }
  1059. };
  1060. LoaderWorker.prototype._terminate = function () {
  1061. this.worker.terminate();
  1062. this._reset();
  1063. };
  1064. return LoaderWorker;
  1065. })();
  1066. function WorkerSupport( logger ) {
  1067. this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() );
  1068. this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerSupport version: ' + WORKER_SUPPORT_VERSION );
  1069. // check worker support first
  1070. if ( window.Worker === undefined ) throw "This browser does not support web workers!";
  1071. if ( window.Blob === undefined ) throw "This browser does not support Blob!";
  1072. if ( typeof window.URL.createObjectURL !== 'function' ) throw "This browser does not support Object creation from URL!";
  1073. this.loaderWorker = new LoaderWorker( this.logger );
  1074. }
  1075. /**
  1076. * Validate the status of worker code and the derived worker.
  1077. * @memberOf THREE.LoaderSupport.WorkerSupport
  1078. *
  1079. * @param {Function} functionCodeBuilder Function that is invoked with funcBuildObject and funcBuildSingleton that allows stringification of objects and singletons.
  1080. * @param {String} parserName Name of the Parser object
  1081. * @param {String[]} libLocations URL of libraries that shall be added to worker code relative to libPath
  1082. * @param {String} libPath Base path used for loading libraries
  1083. * @param {THREE.LoaderSupport.WorkerRunnerRefImpl} runnerImpl The default worker parser wrapper implementation (communication and execution). An extended class could be passed here.
  1084. */
  1085. WorkerSupport.prototype.validate = function ( functionCodeBuilder, parserName, libLocations, libPath, runnerImpl ) {
  1086. if ( Validator.isValid( this.loaderWorker.worker ) ) return;
  1087. this.logger.logInfo( 'WorkerSupport: Building worker code...' );
  1088. this.logger.logTimeStart( 'buildWebWorkerCode' );
  1089. if ( Validator.isValid( runnerImpl ) ) {
  1090. this.logger.logInfo( 'WorkerSupport: Using "' + runnerImpl.name + '" as Runner class for worker.' );
  1091. } else {
  1092. runnerImpl = THREE.LoaderSupport.WorkerRunnerRefImpl;
  1093. this.logger.logInfo( 'WorkerSupport: Using DEFAULT "THREE.LoaderSupport.WorkerRunnerRefImpl" as Runner class for worker.' );
  1094. }
  1095. var userWorkerCode = functionCodeBuilder( buildObject, buildSingleton );
  1096. userWorkerCode += 'var Parser = '+ parserName + ';\n\n';
  1097. userWorkerCode += buildSingleton( runnerImpl.name, runnerImpl );
  1098. userWorkerCode += 'new ' + runnerImpl.name + '();\n\n';
  1099. var scope = this;
  1100. if ( Validator.isValid( libLocations ) && libLocations.length > 0 ) {
  1101. var libsContent = '';
  1102. var loadAllLibraries = function ( path, locations ) {
  1103. if ( locations.length === 0 ) {
  1104. scope.loaderWorker.initWorker( libsContent + userWorkerCode, runnerImpl.name );
  1105. scope.logger.logTimeEnd( 'buildWebWorkerCode' );
  1106. } else {
  1107. var loadedLib = function ( contentAsString ) {
  1108. libsContent += contentAsString;
  1109. loadAllLibraries( path, locations );
  1110. };
  1111. var fileLoader = new THREE.FileLoader();
  1112. fileLoader.setPath( path );
  1113. fileLoader.setResponseType( 'text' );
  1114. fileLoader.load( locations[ 0 ], loadedLib );
  1115. locations.shift();
  1116. }
  1117. };
  1118. loadAllLibraries( libPath, libLocations );
  1119. } else {
  1120. this.loaderWorker.initWorker( userWorkerCode, runnerImpl.name );
  1121. this.logger.logTimeEnd( 'buildWebWorkerCode' );
  1122. }
  1123. };
  1124. /**
  1125. * Specify functions that should be build when new raw mesh data becomes available and when the parser is finished.
  1126. * @memberOf THREE.LoaderSupport.WorkerSupport
  1127. *
  1128. * @param {Function} builder The builder function. Default is {@link THREE.LoaderSupport.Builder}.
  1129. * @param {Function} onLoad The function that is called when parsing is complete.
  1130. */
  1131. WorkerSupport.prototype.setCallbacks = function ( builder, onLoad ) {
  1132. this.loaderWorker.setCallbacks( builder, onLoad );
  1133. };
  1134. /**
  1135. * Runs the parser with the provided configuration.
  1136. * @memberOf THREE.LoaderSupport.WorkerSupport
  1137. *
  1138. * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
  1139. */
  1140. WorkerSupport.prototype.run = function ( payload ) {
  1141. this.loaderWorker.run( payload );
  1142. };
  1143. /**
  1144. * Request termination of worker once parser is finished.
  1145. * @memberOf THREE.LoaderSupport.WorkerSupport
  1146. *
  1147. * @param {boolean} terminateRequested True or false.
  1148. */
  1149. WorkerSupport.prototype.setTerminateRequested = function ( terminateRequested ) {
  1150. this.loaderWorker.setTerminateRequested( terminateRequested );
  1151. };
  1152. var buildObject = function ( fullName, object ) {
  1153. var objectString = fullName + ' = {\n';
  1154. var part;
  1155. for ( var name in object ) {
  1156. part = object[ name ];
  1157. if ( typeof( part ) === 'string' || part instanceof String ) {
  1158. part = part.replace( '\n', '\\n' );
  1159. part = part.replace( '\r', '\\r' );
  1160. objectString += '\t' + name + ': "' + part + '",\n';
  1161. } else if ( part instanceof Array ) {
  1162. objectString += '\t' + name + ': [' + part + '],\n';
  1163. } else if ( Number.isInteger( part ) ) {
  1164. objectString += '\t' + name + ': ' + part + ',\n';
  1165. } else if ( typeof part === 'function' ) {
  1166. objectString += '\t' + name + ': ' + part + ',\n';
  1167. }
  1168. }
  1169. objectString += '}\n\n';
  1170. return objectString;
  1171. };
  1172. var buildSingleton = function ( fullName, object, internalName ) {
  1173. var objectString = '';
  1174. var objectName = ( Validator.isValid( internalName ) ) ? internalName : object.name;
  1175. var funcString, objectPart, constructorString;
  1176. for ( var name in object.prototype ) {
  1177. objectPart = object.prototype[ name ];
  1178. if ( name === 'constructor' ) {
  1179. funcString = objectPart.toString();
  1180. funcString = funcString.replace( 'function', '' );
  1181. constructorString = '\tfunction ' + objectName + funcString + ';\n\n';
  1182. } else if ( typeof objectPart === 'function' ) {
  1183. funcString = objectPart.toString();
  1184. objectString += '\t' + objectName + '.prototype.' + name + ' = ' + funcString + ';\n\n';
  1185. }
  1186. }
  1187. objectString += '\treturn ' + objectName + ';\n';
  1188. objectString += '})();\n\n';
  1189. if ( ! Validator.isValid( constructorString ) ) {
  1190. constructorString = fullName + ' = (function () {\n\n';
  1191. constructorString += '\t' + object.prototype.constructor.toString() + '\n\n';
  1192. objectString = constructorString + objectString;
  1193. } else {
  1194. objectString = fullName + ' = (function () {\n\n' + constructorString + objectString;
  1195. }
  1196. return objectString;
  1197. };
  1198. return WorkerSupport;
  1199. })();
  1200. /**
  1201. * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16).
  1202. * Workflow:
  1203. * prepareWorkers
  1204. * enqueueForRun
  1205. * processQueue
  1206. * tearDown (to force stop)
  1207. *
  1208. * @class
  1209. *
  1210. * @param {string} classDef Class definition to be used for construction
  1211. * @param {THREE.LoaderSupport.ConsoleLogger} logger logger to be used
  1212. */
  1213. THREE.LoaderSupport.WorkerDirector = (function () {
  1214. var LOADER_WORKER_DIRECTOR_VERSION = '2.1.0';
  1215. var Validator = THREE.LoaderSupport.Validator;
  1216. var MAX_WEB_WORKER = 16;
  1217. var MAX_QUEUE_SIZE = 8192;
  1218. function WorkerDirector( classDef, logger ) {
  1219. this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() );
  1220. this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerDirector version: ' + LOADER_WORKER_DIRECTOR_VERSION );
  1221. this.maxQueueSize = MAX_QUEUE_SIZE ;
  1222. this.maxWebWorkers = MAX_WEB_WORKER;
  1223. this.crossOrigin = null;
  1224. if ( ! Validator.isValid( classDef ) ) throw 'Provided invalid classDef: ' + classDef;
  1225. this.workerDescription = {
  1226. classDef: classDef,
  1227. globalCallbacks: {},
  1228. workerSupports: {}
  1229. };
  1230. this.objectsCompleted = 0;
  1231. this.instructionQueue = [];
  1232. this.instructionQueuePointer = 0;
  1233. this.callbackOnFinishedProcessing = null;
  1234. }
  1235. /**
  1236. * Returns the maximum length of the instruction queue.
  1237. * @memberOf THREE.LoaderSupport.WorkerDirector
  1238. *
  1239. * @returns {number}
  1240. */
  1241. WorkerDirector.prototype.getMaxQueueSize = function () {
  1242. return this.maxQueueSize;
  1243. };
  1244. /**
  1245. * Returns the maximum number of workers.
  1246. * @memberOf THREE.LoaderSupport.WorkerDirector
  1247. *
  1248. * @returns {number}
  1249. */
  1250. WorkerDirector.prototype.getMaxWebWorkers = function () {
  1251. return this.maxWebWorkers;
  1252. };
  1253. /**
  1254. * Sets the CORS string to be used.
  1255. * @memberOf THREE.LoaderSupport.WorkerDirector
  1256. *
  1257. * @param {string} crossOrigin CORS value
  1258. */
  1259. WorkerDirector.prototype.setCrossOrigin = function ( crossOrigin ) {
  1260. this.crossOrigin = crossOrigin;
  1261. };
  1262. /**
  1263. * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers.
  1264. * @memberOf THREE.LoaderSupport.WorkerDirector
  1265. *
  1266. * @param {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks} globalCallbacks Register global callbacks used by all web workers
  1267. * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024)
  1268. * @param {number} maxWebWorkers Set the maximum amount of workers (1-16)
  1269. */
  1270. WorkerDirector.prototype.prepareWorkers = function ( globalCallbacks, maxQueueSize, maxWebWorkers ) {
  1271. if ( Validator.isValid( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks;
  1272. this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE );
  1273. this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER );
  1274. this.maxWebWorkers = Math.min( this.maxWebWorkers, this.maxQueueSize );
  1275. this.objectsCompleted = 0;
  1276. this.instructionQueue = [];
  1277. this.instructionQueuePointer = 0;
  1278. for ( var instanceNo = 0; instanceNo < this.maxWebWorkers; instanceNo++ ) {
  1279. this.workerDescription.workerSupports[ instanceNo ] = {
  1280. instanceNo: instanceNo,
  1281. inUse: false,
  1282. terminateRequested: false,
  1283. workerSupport: new THREE.LoaderSupport.WorkerSupport( this.logger ),
  1284. loader: null
  1285. };
  1286. }
  1287. };
  1288. /**
  1289. * Store run instructions in internal instructionQueue.
  1290. * @memberOf THREE.LoaderSupport.WorkerDirector
  1291. *
  1292. * @param {THREE.LoaderSupport.PrepData} prepData
  1293. */
  1294. WorkerDirector.prototype.enqueueForRun = function ( prepData ) {
  1295. if ( this.instructionQueue.length < this.maxQueueSize ) {
  1296. this.instructionQueue.push( prepData );
  1297. }
  1298. };
  1299. /**
  1300. * Returns if any workers are running.
  1301. *
  1302. * @memberOf THREE.LoaderSupport.WorkerDirector
  1303. * @returns {boolean}
  1304. */
  1305. WorkerDirector.prototype.isRunning = function () {
  1306. var wsKeys = Object.keys( this.workerDescription.workerSupports );
  1307. return ( ( this.instructionQueue.length > 0 && this.instructionQueuePointer < this.instructionQueue.length ) || wsKeys.length > 0 );
  1308. };
  1309. /**
  1310. * Process the instructionQueue until it is depleted.
  1311. * @memberOf THREE.LoaderSupport.WorkerDirector
  1312. */
  1313. WorkerDirector.prototype.processQueue = function () {
  1314. var prepData, supportDesc;
  1315. for ( var instanceNo in this.workerDescription.workerSupports ) {
  1316. supportDesc = this.workerDescription.workerSupports[ instanceNo ];
  1317. if ( ! supportDesc.inUse ) {
  1318. if ( this.instructionQueuePointer < this.instructionQueue.length ) {
  1319. prepData = this.instructionQueue[ this.instructionQueuePointer ];
  1320. this._kickWorkerRun( prepData, supportDesc );
  1321. this.instructionQueuePointer++;
  1322. } else {
  1323. this._deregister( supportDesc );
  1324. }
  1325. }
  1326. }
  1327. if ( ! this.isRunning() && this.callbackOnFinishedProcessing !== null ) {
  1328. this.callbackOnFinishedProcessing();
  1329. this.callbackOnFinishedProcessing = null;
  1330. }
  1331. };
  1332. WorkerDirector.prototype._kickWorkerRun = function( prepData, supportDesc ) {
  1333. supportDesc.inUse = true;
  1334. supportDesc.workerSupport.setTerminateRequested( supportDesc.terminateRequested );
  1335. this.logger.logInfo( '\nAssigning next item from queue to worker (queue length: ' + this.instructionQueue.length + ')\n\n' );
  1336. var scope = this;
  1337. var prepDataCallbacks = prepData.getCallbacks();
  1338. var globalCallbacks = this.workerDescription.globalCallbacks;
  1339. var wrapperOnLoad = function ( event ) {
  1340. if ( Validator.isValid( globalCallbacks.onLoad ) ) globalCallbacks.onLoad( event );
  1341. if ( Validator.isValid( prepDataCallbacks.onLoad ) ) prepDataCallbacks.onLoad( event );
  1342. scope.objectsCompleted++;
  1343. supportDesc.inUse = false;
  1344. scope.processQueue();
  1345. };
  1346. var wrapperOnProgress = function ( event ) {
  1347. if ( Validator.isValid( globalCallbacks.onProgress ) ) globalCallbacks.onProgress( event );
  1348. if ( Validator.isValid( prepDataCallbacks.onProgress ) ) prepDataCallbacks.onProgress( event );
  1349. };
  1350. var wrapperOnMeshAlter = function ( event ) {
  1351. if ( Validator.isValid( globalCallbacks.onMeshAlter ) ) globalCallbacks.onMeshAlter( event );
  1352. if ( Validator.isValid( prepDataCallbacks.onMeshAlter ) ) prepDataCallbacks.onMeshAlter( event );
  1353. };
  1354. supportDesc.loader = this._buildLoader( supportDesc.instanceNo );
  1355. var updatedCallbacks = new THREE.LoaderSupport.Callbacks();
  1356. updatedCallbacks.setCallbackOnLoad( wrapperOnLoad );
  1357. updatedCallbacks.setCallbackOnProgress( wrapperOnProgress );
  1358. updatedCallbacks.setCallbackOnMeshAlter( wrapperOnMeshAlter );
  1359. prepData.callbacks = updatedCallbacks;
  1360. supportDesc.loader.run( prepData, supportDesc.workerSupport );
  1361. };
  1362. WorkerDirector.prototype._buildLoader = function ( instanceNo ) {
  1363. var classDef = this.workerDescription.classDef;
  1364. var loader = Object.create( classDef.prototype );
  1365. this.workerDescription.classDef.call( loader, THREE.DefaultLoadingManager, this.logger );
  1366. // verify that all required functions are implemented
  1367. if ( ! loader.hasOwnProperty( 'instanceNo' ) ) throw classDef.name + ' has no property "instanceNo".';
  1368. loader.instanceNo = instanceNo;
  1369. if ( ! loader.hasOwnProperty( 'workerSupport' ) ) {
  1370. throw classDef.name + ' has no property "workerSupport".';
  1371. }
  1372. if ( typeof loader.run !== 'function' ) throw classDef.name + ' has no function "run".';
  1373. if ( ! loader.hasOwnProperty( 'callbacks' ) || ! Validator.isValid( loader.callbacks ) ) {
  1374. this.logger.logWarn( classDef.name + ' has an invalid property "callbacks". Will change to "THREE.LoaderSupport.Callbacks"' );
  1375. loader.callbacks = new THREE.LoaderSupport.Callbacks();
  1376. }
  1377. return loader;
  1378. };
  1379. WorkerDirector.prototype._deregister = function ( supportDesc ) {
  1380. if ( Validator.isValid( supportDesc ) ) {
  1381. supportDesc.workerSupport.setTerminateRequested( true );
  1382. this.logger.logInfo( 'Requested termination of worker #' + supportDesc.instanceNo + '.' );
  1383. var loaderCallbacks = supportDesc.loader.callbacks;
  1384. if ( Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } );
  1385. delete this.workerDescription.workerSupports[ supportDesc.instanceNo ];
  1386. }
  1387. };
  1388. /**
  1389. * Terminate all workers.
  1390. * @memberOf THREE.LoaderSupport.WorkerDirector
  1391. *
  1392. * @param {callback} callbackOnFinishedProcessing Function called once all workers finished processing.
  1393. */
  1394. WorkerDirector.prototype.tearDown = function ( callbackOnFinishedProcessing ) {
  1395. this.logger.logInfo( 'WorkerDirector received the deregister call. Terminating all workers!' );
  1396. this.instructionQueuePointer = this.instructionQueue.length;
  1397. this.callbackOnFinishedProcessing = Validator.verifyInput( callbackOnFinishedProcessing, null );
  1398. for ( var name in this.workerDescription.workerSupports ) {
  1399. this.workerDescription.workerSupports[ name ].terminateRequested = true;
  1400. }
  1401. };
  1402. return WorkerDirector;
  1403. })();