OBJLoader2Parallel.js 6.6 KB

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