OBJLoader2Parallel.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /**
  2. * @author Kai Salmen / https://kaisalmen.de
  3. * Development repository: https://github.com/kaisalmen/WWOBJLoader
  4. */
  5. // Imports only related to wrapper
  6. import {
  7. Object3D
  8. } from "../../../build/three.module.js";
  9. import {
  10. CodeBuilderInstructions,
  11. WorkerExecutionSupport
  12. } from "./obj2/worker/main/WorkerExecutionSupport.js";
  13. import { CodeSerializer } from "./obj2/utils/CodeSerializer.js";
  14. import { OBJLoader2 } from "./OBJLoader2.js";
  15. // Imports only related to worker (when standard workers (modules aren't supported) are used)
  16. import { OBJLoader2Parser } from "./obj2/OBJLoader2Parser.js";
  17. import {
  18. WorkerRunner,
  19. DefaultWorkerPayloadHandler,
  20. ObjectManipulator
  21. } from "./obj2/worker/parallel/WorkerRunner.js";
  22. /**
  23. * Creates a new OBJLoader2Parallel. Use it to load OBJ data from files or to parse OBJ data from arraybuffer.
  24. * It extends {@link OBJLoader2} with the capability to run the parser in a web worker.
  25. *
  26. * @param [LoadingManager] manager The loadingManager for the loader to use. Default is {@link LoadingManager}
  27. * @constructor
  28. */
  29. const OBJLoader2Parallel = function ( manager ) {
  30. OBJLoader2.call( this, manager );
  31. this.preferJsmWorker = false;
  32. this.jsmWorkerUrl = null;
  33. this.executeParallel = true;
  34. this.workerExecutionSupport = new WorkerExecutionSupport();
  35. };
  36. OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION = '3.2.0';
  37. console.info( 'Using OBJLoader2Parallel version: ' + OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION );
  38. OBJLoader2Parallel.DEFAULT_JSM_WORKER_PATH = './jsm/loaders/obj2/worker/parallel/OBJLoader2JsmWorker.js';
  39. OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototype ), {
  40. constructor: OBJLoader2Parallel,
  41. /**
  42. * Execution of parse in parallel via Worker is default, but normal {OBJLoader2} parsing can be enforced via false here.
  43. *
  44. * @param {boolean} executeParallel True or False
  45. * @return {OBJLoader2Parallel}
  46. */
  47. setExecuteParallel: function ( executeParallel ) {
  48. this.executeParallel = executeParallel === true;
  49. return this;
  50. },
  51. /**
  52. * Set whether jsm modules in workers should be used. This requires browser support which is currently only experimental.
  53. * @param {boolean} preferJsmWorker True or False
  54. * @param {URL} jsmWorkerUrl Provide complete jsm worker URL otherwise relative path to this module may not be correct
  55. * @return {OBJLoader2Parallel}
  56. */
  57. setJsmWorker: function ( preferJsmWorker, jsmWorkerUrl ) {
  58. this.preferJsmWorker = preferJsmWorker === true;
  59. if ( jsmWorkerUrl === undefined || jsmWorkerUrl === null ) {
  60. throw "The url to the jsm worker is not valid. Aborting...";
  61. }
  62. this.jsmWorkerUrl = jsmWorkerUrl;
  63. return this;
  64. },
  65. /**
  66. * Allow to get hold of {@link WorkerExecutionSupport} for configuration purposes.
  67. * @return {WorkerExecutionSupport}
  68. */
  69. getWorkerExecutionSupport: function () {
  70. return this.workerExecutionSupport;
  71. },
  72. /**
  73. * Provide instructions on what is to be contained in the worker.
  74. * @return {CodeBuilderInstructions}
  75. */
  76. buildWorkerCode: function () {
  77. let codeBuilderInstructions = new CodeBuilderInstructions( true, true, this.preferJsmWorker );
  78. if ( codeBuilderInstructions.isSupportsJsmWorker() ) {
  79. codeBuilderInstructions.setJsmWorkerUrl( this.jsmWorkerUrl );
  80. }
  81. if ( codeBuilderInstructions.isSupportsStandardWorker() ) {
  82. let objectManipulator = new ObjectManipulator();
  83. let defaultWorkerPayloadHandler = new DefaultWorkerPayloadHandler( this.parser );
  84. let workerRunner = new WorkerRunner( {} );
  85. codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( OBJLoader2Parser, this.parser ) );
  86. codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( ObjectManipulator, objectManipulator ) );
  87. codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( DefaultWorkerPayloadHandler, defaultWorkerPayloadHandler ) );
  88. codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( WorkerRunner, workerRunner ) );
  89. let startCode = 'new ' + workerRunner.constructor.name + '( new ' + defaultWorkerPayloadHandler.constructor.name + '( new ' + this.parser.constructor.name + '() ) );';
  90. codeBuilderInstructions.addStartCode( startCode );
  91. }
  92. return codeBuilderInstructions;
  93. },
  94. /**
  95. * See {@link OBJLoader2.load}
  96. */
  97. load: function ( content, onLoad, onFileLoadProgress, onError, onMeshAlter ) {
  98. let scope = this;
  99. function interceptOnLoad( object3d, message ) {
  100. if ( object3d.name === 'OBJLoader2ParallelDummy' ) {
  101. if ( scope.parser.logging.enabled && scope.parser.logging.debug ) {
  102. console.debug( 'Received dummy answer from OBJLoader2Parallel#parse' );
  103. }
  104. } else {
  105. onLoad( object3d, message );
  106. }
  107. }
  108. OBJLoader2.prototype.load.call( this, content, interceptOnLoad, onFileLoadProgress, onError, onMeshAlter );
  109. },
  110. /**
  111. * See {@link OBJLoader2.parse}
  112. * The callback onLoad needs to be set to be able to receive the content if used in parallel mode.
  113. * Fallback is possible via {@link OBJLoader2Parallel#setExecuteParallel}.
  114. */
  115. parse: function ( content ) {
  116. if ( this.executeParallel ) {
  117. if ( this.parser.callbacks.onLoad === this.parser._onLoad ) {
  118. throw "No callback other than the default callback was provided! Aborting!";
  119. }
  120. // check if worker has been initialize before. If yes, skip init
  121. if ( ! this.workerExecutionSupport.isWorkerLoaded( this.preferJsmWorker ) ) {
  122. this.workerExecutionSupport.buildWorker( this.buildWorkerCode() );
  123. let scope = this;
  124. let scopedOnAssetAvailable = function ( payload ) {
  125. scope._onAssetAvailable( payload );
  126. };
  127. function scopedOnLoad( message ) {
  128. scope.parser.callbacks.onLoad( scope.baseObject3d, message );
  129. }
  130. this.workerExecutionSupport.updateCallbacks( scopedOnAssetAvailable, scopedOnLoad );
  131. }
  132. // Create default materials beforehand, but do not override previously set materials (e.g. during init)
  133. this.materialHandler.createDefaultMaterials( false );
  134. this.workerExecutionSupport.executeParallel(
  135. {
  136. params: {
  137. modelName: this.modelName,
  138. instanceNo: this.instanceNo,
  139. useIndices: this.parser.useIndices,
  140. disregardNormals: this.parser.disregardNormals,
  141. materialPerSmoothingGroup: this.parser.materialPerSmoothingGroup,
  142. useOAsMesh: this.parser.useOAsMesh,
  143. materials: this.materialHandler.getMaterialsJSON()
  144. },
  145. data: {
  146. input: content,
  147. options: null
  148. },
  149. logging: {
  150. enabled: this.parser.logging.enabled,
  151. debug: this.parser.logging.debug
  152. }
  153. } );
  154. let dummy = new Object3D();
  155. dummy.name = 'OBJLoader2ParallelDummy';
  156. return dummy;
  157. } else {
  158. return OBJLoader2.prototype.parse.call( this, content );
  159. }
  160. },
  161. } );
  162. export { OBJLoader2Parallel };