webgl_loader_obj2_meshspray.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js webgl - Mesh Spray</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  7. <link type="text/css" rel="stylesheet" href="main.css">
  8. <style>
  9. #glFullscreen {
  10. width: 100%;
  11. height: 100vh;
  12. min-width: 640px;
  13. min-height: 360px;
  14. position: relative;
  15. overflow: hidden;
  16. z-index: 0;
  17. }
  18. #example {
  19. width: 100%;
  20. height: 100%;
  21. top: 0;
  22. left: 0;
  23. background-color: #000000;
  24. }
  25. #feedback {
  26. color: darkorange;
  27. }
  28. #dat {
  29. user-select: none;
  30. position: absolute;
  31. left: 0;
  32. top: 0;
  33. z-Index: 200;
  34. }
  35. </style>
  36. </head>
  37. <body>
  38. <div id="glFullscreen">
  39. <canvas id="example"></canvas>
  40. </div>
  41. <div id="dat">
  42. </div>
  43. <div id="info">
  44. <a href="http://threejs.org" target="_blank">three.js</a> - Mesh Spray
  45. <div id="feedback"></div>
  46. </div>
  47. <script src="../build/three.js"></script>
  48. <script src="js/controls/TrackballControls.js"></script>
  49. <script src="js/libs/dat.gui.min.js"></script>
  50. <script src="js/loaders/LoaderSupport.js"></script>
  51. <script>
  52. 'use strict';
  53. var MeshSpray = {};
  54. MeshSpray.Loader = function ( manager ) {
  55. this.manager = THREE.LoaderSupport.Validator.verifyInput( manager, THREE.DefaultLoadingManager );
  56. this.logging = {
  57. enabled: true,
  58. debug: false
  59. };
  60. this.instanceNo = 0;
  61. this.loaderRootNode = new THREE.Group();
  62. this.meshBuilder = new THREE.LoaderSupport.MeshBuilder();
  63. this.callbacks = new THREE.LoaderSupport.Callbacks();
  64. this.workerSupport = null;
  65. };
  66. MeshSpray.Loader.prototype = {
  67. constructor: MeshSpray.Loader,
  68. setLogging: function ( enabled, debug ) {
  69. this.logging.enabled = enabled === true;
  70. this.logging.debug = debug === true;
  71. this.meshBuilder.setLogging( this.logging.enabled, this.logging.debug );
  72. },
  73. setStreamMeshesTo: function ( streamMeshesTo ) {
  74. this.loaderRootNode = THREE.LoaderSupport.Validator.verifyInput( streamMeshesTo, this.loaderRootNode );
  75. },
  76. setForceWorkerDataCopy: function ( forceWorkerDataCopy ) {
  77. // nothing to do here
  78. },
  79. run: function ( prepData, workerSupportExternal ) {
  80. if ( THREE.LoaderSupport.Validator.isValid( workerSupportExternal ) ) {
  81. this.workerSupport = workerSupportExternal;
  82. this.logging.enabled = this.workerSupport.logging.enabled;
  83. this.logging.debug = this.workerSupport.logging.debug;
  84. } else {
  85. this.workerSupport = THREE.LoaderSupport.Validator.verifyInput( this.workerSupport, new THREE.LoaderSupport.WorkerSupport() );
  86. }
  87. if ( this.logging.enabled ) console.time( 'MeshSpray' + this.instanceNo );
  88. this._applyPrepData( prepData );
  89. this.meshBuilder.init();
  90. var scope = this;
  91. var scopeBuilderFunc = function ( payload ) {
  92. var meshes = scope.meshBuilder.processPayload( payload );
  93. var mesh;
  94. for ( var i in meshes ) {
  95. mesh = meshes[ i ];
  96. scope.loaderRootNode.add( mesh );
  97. }
  98. };
  99. var scopeFuncComplete = function ( message ) {
  100. var callback = scope.callbacks.onLoad;
  101. if ( THREE.LoaderSupport.Validator.isValid( callback ) ) callback(
  102. {
  103. detail: {
  104. loaderRootNode: scope.loaderRootNode,
  105. modelName: scope.modelName,
  106. instanceNo: scope.instanceNo
  107. }
  108. }
  109. );
  110. if ( scope.logging.enabled ) console.timeEnd( 'MeshSpray' + scope.instanceNo );
  111. };
  112. var buildCode = function ( codeSerializer ) {
  113. var workerCode = '';
  114. workerCode += '/**\n';
  115. workerCode += ' * This code was constructed by MeshSpray buildCode.\n';
  116. workerCode += ' */\n\n';
  117. workerCode += 'THREE.LoaderSupport = {};\n\n';
  118. workerCode += 'MeshSpray = {};\n\n';
  119. workerCode += codeSerializer.serializeObject( 'THREE.LoaderSupport.Validator', THREE.LoaderSupport.Validator );
  120. workerCode += codeSerializer.serializeClass( 'MeshSpray.Parser', MeshSpray.Parser );
  121. return workerCode;
  122. };
  123. var libs2Load = [ 'build/three.min.js' ];
  124. this.workerSupport.validate( buildCode, 'MeshSpray.Parser', libs2Load, '../../' );
  125. this.workerSupport.setCallbacks( scopeBuilderFunc, scopeFuncComplete );
  126. this.workerSupport.run(
  127. {
  128. params: {
  129. dimension: prepData.dimension,
  130. quantity: prepData.quantity,
  131. globalObjectCount: prepData.globalObjectCount
  132. },
  133. materials: {
  134. serializedMaterials: this.meshBuilder.getMaterialsJSON()
  135. },
  136. logging: {
  137. enabled: this.logging.enabled,
  138. debug: this.logging.debug
  139. },
  140. data: {
  141. input: null,
  142. options: null
  143. }
  144. }
  145. );
  146. },
  147. _applyPrepData: function ( prepData ) {
  148. if ( THREE.LoaderSupport.Validator.isValid( prepData ) ) {
  149. this.setLogging( prepData.logging.enabled, prepData.logging.debug );
  150. this.setStreamMeshesTo( prepData.streamMeshesTo );
  151. this.meshBuilder.setMaterials( prepData.materials );
  152. this._setCallbacks( prepData.getCallbacks() );
  153. }
  154. },
  155. _setCallbacks: function ( callbacks ) {
  156. if ( THREE.LoaderSupport.Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress );
  157. if ( THREE.LoaderSupport.Validator.isValid( callbacks.onReportError ) ) this.callbacks.setCallbackOnReportError( callbacks.onReportError );
  158. if ( THREE.LoaderSupport.Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter );
  159. if ( THREE.LoaderSupport.Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad );
  160. if ( THREE.LoaderSupport.Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials );
  161. this.meshBuilder._setCallbacks( this.callbacks );
  162. }
  163. };
  164. MeshSpray.Parser = function () {
  165. this.sizeFactor = 0.5;
  166. this.localOffsetFactor = 1.0;
  167. this.globalObjectCount = 0;
  168. this.debug = false;
  169. this.dimension = 200;
  170. this.quantity = 1;
  171. this.callbacks = {
  172. onAssetAvailable: null
  173. };
  174. this.serializedMaterials = null;
  175. this.logging = {
  176. enabled: true,
  177. debug: false
  178. };
  179. };
  180. MeshSpray.Parser.prototype = {
  181. constructor: MeshSpray.Parser,
  182. setCallbackOnAssetAvailable: function ( onAssetAvailable ) {
  183. if ( onAssetAvailable !== null && onAssetAvailable !== undefined ) {
  184. this.callbacks.onAssetAvailable = onAssetAvailable;
  185. }
  186. },
  187. setLogging: function ( enabled, debug ) {
  188. this.logging.enabled = enabled === true;
  189. this.logging.debug = debug === true;
  190. },
  191. parse: function () {
  192. var baseTriangle = [ 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, -1.0, 1.0 ];
  193. var vertices = [];
  194. var colors = [];
  195. var normals = [];
  196. var uvs = [];
  197. var dimensionHalf = this.dimension / 2;
  198. var fixedOffsetX;
  199. var fixedOffsetY;
  200. var fixedOffsetZ;
  201. var s, t;
  202. // complete triangle
  203. var sizeVaring = this.sizeFactor * Math.random();
  204. // local coords offset
  205. var localOffsetFactor = this.localOffsetFactor;
  206. for ( var i = 0; i < this.quantity; i++ ) {
  207. sizeVaring = this.sizeFactor * Math.random();
  208. s = 2 * Math.PI * Math.random();
  209. t = Math.PI * Math.random();
  210. fixedOffsetX = dimensionHalf * Math.random() * Math.cos( s ) * Math.sin( t );
  211. fixedOffsetY = dimensionHalf * Math.random() * Math.sin( s ) * Math.sin( t );
  212. fixedOffsetZ = dimensionHalf * Math.random() * Math.cos( t );
  213. for ( var j = 0; j < baseTriangle.length; j += 3 ) {
  214. vertices.push( baseTriangle[ j ] * sizeVaring + localOffsetFactor * Math.random() + fixedOffsetX );
  215. vertices.push( baseTriangle[ j + 1 ] * sizeVaring + localOffsetFactor * Math.random() + fixedOffsetY );
  216. vertices.push( baseTriangle[ j + 2 ] * sizeVaring + localOffsetFactor * Math.random() + fixedOffsetZ );
  217. colors.push( Math.random() );
  218. colors.push( Math.random() );
  219. colors.push( Math.random() );
  220. }
  221. }
  222. var absoluteVertexCount = vertices.length;
  223. var absoluteColorCount = colors.length;
  224. var absoluteNormalCount = 0;
  225. var absoluteUvCount = 0;
  226. var vertexFA = new Float32Array( absoluteVertexCount );
  227. var colorFA = ( absoluteColorCount > 0 ) ? new Float32Array( absoluteColorCount ) : null;
  228. var normalFA = ( absoluteNormalCount > 0 ) ? new Float32Array( absoluteNormalCount ) : null;
  229. var uvFA = ( absoluteUvCount > 0 ) ? new Float32Array( absoluteUvCount ) : null;
  230. vertexFA.set( vertices, 0 );
  231. if ( colorFA ) {
  232. colorFA.set( colors, 0 );
  233. }
  234. if ( normalFA ) {
  235. normalFA.set( normals, 0 );
  236. }
  237. if ( uvFA ) {
  238. uvFA.set( uvs, 0 );
  239. }
  240. /*
  241. * This demonstrates the usage of embedded three.js in the worker blob and
  242. * the serialization of materials back to the Builder outside the worker.
  243. *
  244. * This is not the most effective way, but outlining possibilities
  245. */
  246. var materialName = 'defaultVertexColorMaterial_double';
  247. var defaultVertexColorMaterialJson = this.serializedMaterials[ 'defaultVertexColorMaterial' ];
  248. var loader = new THREE.MaterialLoader();
  249. var defaultVertexColorMaterialDouble = loader.parse( defaultVertexColorMaterialJson );
  250. defaultVertexColorMaterialDouble.name = materialName;
  251. defaultVertexColorMaterialDouble.side = THREE.DoubleSide;
  252. var newSerializedMaterials = {};
  253. newSerializedMaterials[ materialName ] = defaultVertexColorMaterialDouble.toJSON();
  254. var payload = {
  255. cmd: 'materialData',
  256. materials: {
  257. serializedMaterials: newSerializedMaterials
  258. }
  259. };
  260. this.callbacks.onAssetAvailable( payload );
  261. this.globalObjectCount++;
  262. this.callbacks.onAssetAvailable(
  263. {
  264. cmd: 'meshData',
  265. progress: {
  266. numericalValue: 1.0
  267. },
  268. params: {
  269. meshName: 'Gen' + this.globalObjectCount
  270. },
  271. materials: {
  272. multiMaterial: false,
  273. materialNames: [ materialName ],
  274. materialGroups: []
  275. },
  276. buffers: {
  277. vertices: vertexFA,
  278. colors: colorFA,
  279. normals: normalFA,
  280. uvs: uvFA
  281. }
  282. },
  283. [ vertexFA.buffer ],
  284. colorFA !== null ? [ colorFA.buffer ] : null,
  285. normalFA !== null ? [ normalFA.buffer ] : null,
  286. uvFA !== null ? [ uvFA.buffer ] : null
  287. );
  288. if ( this.logging.enabled ) console.info( 'Global output object count: ' + this.globalObjectCount );
  289. },
  290. setSerializedMaterials: function ( serializedMaterials ) {
  291. if ( THREE.LoaderSupport.Validator.isValid( serializedMaterials ) ) {
  292. this.serializedMaterials = serializedMaterials;
  293. }
  294. }
  295. };
  296. var MeshSprayApp = function ( elementToBindTo ) {
  297. this.renderer = null;
  298. this.canvas = elementToBindTo;
  299. this.aspectRatio = 1;
  300. this.recalcAspectRatio();
  301. this.scene = null;
  302. this.cameraDefaults = {
  303. posCamera: new THREE.Vector3( 500.0, 500.0, 1000.0 ),
  304. posCameraTarget: new THREE.Vector3( 0, 0, 0 ),
  305. near: 0.1,
  306. far: 10000,
  307. fov: 45
  308. };
  309. this.camera = null;
  310. this.cameraTarget = this.cameraDefaults.posCameraTarget;
  311. this.controls = null;
  312. this.cube = null;
  313. this.pivot = null;
  314. };
  315. MeshSprayApp.prototype = {
  316. constructor: MeshSprayApp,
  317. initGL: function () {
  318. this.renderer = new THREE.WebGLRenderer( {
  319. canvas: this.canvas,
  320. antialias: true,
  321. autoClear: true
  322. } );
  323. this.renderer.setClearColor( 0x050505 );
  324. this.scene = new THREE.Scene();
  325. this.camera = new THREE.PerspectiveCamera( this.cameraDefaults.fov, this.aspectRatio, this.cameraDefaults.near, this.cameraDefaults.far );
  326. this.resetCamera();
  327. this.controls = new THREE.TrackballControls( this.camera, this.renderer.domElement );
  328. var ambientLight = new THREE.AmbientLight( 0x404040 );
  329. var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
  330. var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
  331. directionalLight1.position.set( -100, -50, 100 );
  332. directionalLight2.position.set( 100, 50, -100 );
  333. this.scene.add( directionalLight1 );
  334. this.scene.add( directionalLight2 );
  335. this.scene.add( ambientLight );
  336. var helper = new THREE.GridHelper( 1200, 60, 0xFF4444, 0x404040 );
  337. this.scene.add( helper );
  338. var geometry = new THREE.BoxBufferGeometry( 10, 10, 10 );
  339. var material = new THREE.MeshNormalMaterial();
  340. this.cube = new THREE.Mesh( geometry, material );
  341. this.cube.position.set( 0, 0, 0 );
  342. this.scene.add( this.cube );
  343. this.pivot = new THREE.Object3D();
  344. this.pivot.name = 'Pivot';
  345. this.scene.add( this.pivot );
  346. },
  347. initContent: function () {
  348. var maxQueueSize = 1024;
  349. var maxWebWorkers = 4;
  350. var radius = 640;
  351. var workerDirector = new THREE.LoaderSupport.WorkerDirector( MeshSpray.Loader );
  352. workerDirector.setLogging( false, false );
  353. workerDirector.setCrossOrigin( 'anonymous' );
  354. var callbackOnLoad = function ( event ) {
  355. console.info( 'Worker #' + event.detail.instanceNo + ': Completed loading. (#' + workerDirector.objectsCompleted + ')' );
  356. };
  357. var reportProgress = function( event ) {
  358. document.getElementById( 'feedback' ).innerHTML = event.detail.text;
  359. console.info( event.detail.text );
  360. };
  361. var callbackMeshAlter = function ( event ) {
  362. var override = new THREE.LoaderSupport.LoadedMeshUserOverride( false, true );
  363. event.detail.side = THREE.DoubleSide;
  364. var mesh = new THREE.Mesh( event.detail.bufferGeometry, event.detail.material );
  365. mesh.name = event.detail.meshName;
  366. override.addMesh( mesh );
  367. return override;
  368. };
  369. var callbacks = new THREE.LoaderSupport.Callbacks();
  370. callbacks.setCallbackOnMeshAlter( callbackMeshAlter );
  371. callbacks.setCallbackOnLoad( callbackOnLoad );
  372. callbacks.setCallbackOnProgress( reportProgress );
  373. workerDirector.prepareWorkers( callbacks, maxQueueSize, maxWebWorkers );
  374. var prepData;
  375. var pivot;
  376. var s, t, r, x, y, z;
  377. var globalObjectCount = 0;
  378. for ( var i = 0; i < maxQueueSize; i++ ) {
  379. prepData = new THREE.LoaderSupport.PrepData( 'Triangles_' + i );
  380. pivot = new THREE.Object3D();
  381. s = 2 * Math.PI * Math.random();
  382. t = Math.PI * Math.random();
  383. r = radius * Math.random();
  384. x = r * Math.cos( s ) * Math.sin( t );
  385. y = r * Math.sin( s ) * Math.sin( t );
  386. z = r * Math.cos( t );
  387. pivot.position.set( x, y, z );
  388. this.scene.add( pivot );
  389. prepData.streamMeshesTo = pivot;
  390. prepData.setLogging( false, false );
  391. prepData.quantity = 8192;
  392. prepData.dimension = Math.max( Math.random() * 500, 100 );
  393. prepData.globalObjectCount = globalObjectCount++;
  394. workerDirector.enqueueForRun( prepData );
  395. }
  396. workerDirector.processQueue();
  397. },
  398. resizeDisplayGL: function () {
  399. this.controls.handleResize();
  400. this.recalcAspectRatio();
  401. this.renderer.setSize( this.canvas.offsetWidth, this.canvas.offsetHeight, false );
  402. this.updateCamera();
  403. },
  404. recalcAspectRatio: function () {
  405. this.aspectRatio = ( this.canvas.offsetHeight === 0 ) ? 1 : this.canvas.offsetWidth / this.canvas.offsetHeight;
  406. },
  407. resetCamera: function () {
  408. this.camera.position.copy( this.cameraDefaults.posCamera );
  409. this.cameraTarget.copy( this.cameraDefaults.posCameraTarget );
  410. this.updateCamera();
  411. },
  412. updateCamera: function () {
  413. this.camera.aspect = this.aspectRatio;
  414. this.camera.lookAt( this.cameraTarget );
  415. this.camera.updateProjectionMatrix();
  416. },
  417. render: function () {
  418. if ( ! this.renderer.autoClear ) this.renderer.clear();
  419. this.controls.update();
  420. this.cube.rotation.x += 0.05;
  421. this.cube.rotation.y += 0.05;
  422. this.renderer.render( this.scene, this.camera );
  423. }
  424. };
  425. var app = new MeshSprayApp( document.getElementById( 'example' ) );
  426. // init three.js example application
  427. var resizeWindow = function () {
  428. app.resizeDisplayGL();
  429. };
  430. var render = function () {
  431. requestAnimationFrame( render );
  432. app.render();
  433. };
  434. window.addEventListener( 'resize', resizeWindow, false );
  435. console.log( 'Starting initialisation phase...' );
  436. app.initGL();
  437. app.resizeDisplayGL();
  438. app.initContent();
  439. render();
  440. </script>
  441. </body>
  442. </html>