@damienmortini/three 0.1.172 → 0.1.173

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,7 @@ import {
12
12
  FrontSide,
13
13
  Group,
14
14
  ImageBitmapLoader,
15
+ InstancedMesh,
15
16
  InterleavedBuffer,
16
17
  InterleavedBufferAttribute,
17
18
  Interpolant,
@@ -147,6 +148,12 @@ class GLTFLoader extends Loader {
147
148
 
148
149
  } );
149
150
 
151
+ this.register( function ( parser ) {
152
+
153
+ return new GLTFMeshGpuInstancing( parser );
154
+
155
+ } );
156
+
150
157
  }
151
158
 
152
159
  load( url, onLoad, onProgress, onError ) {
@@ -277,15 +284,15 @@ class GLTFLoader extends Loader {
277
284
 
278
285
  parse( data, path, onLoad, onError ) {
279
286
 
280
- let content;
287
+ let json;
281
288
  const extensions = {};
282
289
  const plugins = {};
283
290
 
284
291
  if ( typeof data === 'string' ) {
285
292
 
286
- content = data;
293
+ json = JSON.parse( data );
287
294
 
288
- } else {
295
+ } else if ( data instanceof ArrayBuffer ) {
289
296
 
290
297
  const magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) );
291
298
 
@@ -302,17 +309,19 @@ class GLTFLoader extends Loader {
302
309
 
303
310
  }
304
311
 
305
- content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content;
312
+ json = JSON.parse( extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content );
306
313
 
307
314
  } else {
308
315
 
309
- content = LoaderUtils.decodeText( new Uint8Array( data ) );
316
+ json = JSON.parse( LoaderUtils.decodeText( new Uint8Array( data ) ) );
310
317
 
311
318
  }
312
319
 
313
- }
320
+ } else {
321
+
322
+ json = data;
314
323
 
315
- const json = JSON.parse( content );
324
+ }
316
325
 
317
326
  if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) {
318
327
 
@@ -468,7 +477,8 @@ const EXTENSIONS = {
468
477
  KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization',
469
478
  KHR_MATERIALS_EMISSIVE_STRENGTH: 'KHR_materials_emissive_strength',
470
479
  EXT_TEXTURE_WEBP: 'EXT_texture_webp',
471
- EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression'
480
+ EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression',
481
+ EXT_MESH_GPU_INSTANCING: 'EXT_mesh_gpu_instancing'
472
482
  };
473
483
 
474
484
  /**
@@ -1384,6 +1394,160 @@ class GLTFMeshoptCompression {
1384
1394
 
1385
1395
  }
1386
1396
 
1397
+ /**
1398
+ * GPU Instancing Extension
1399
+ *
1400
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing
1401
+ *
1402
+ */
1403
+ class GLTFMeshGpuInstancing {
1404
+
1405
+ constructor( parser ) {
1406
+
1407
+ this.name = EXTENSIONS.EXT_MESH_GPU_INSTANCING;
1408
+ this.parser = parser;
1409
+
1410
+ }
1411
+
1412
+ createNodeMesh( nodeIndex ) {
1413
+
1414
+ const json = this.parser.json;
1415
+ const nodeDef = json.nodes[ nodeIndex ];
1416
+
1417
+ if ( ! nodeDef.extensions || ! nodeDef.extensions[ this.name ] ||
1418
+ nodeDef.mesh === undefined ) {
1419
+
1420
+ return null;
1421
+
1422
+ }
1423
+
1424
+ const meshDef = json.meshes[ nodeDef.mesh ];
1425
+
1426
+ // No Points or Lines + Instancing support yet
1427
+
1428
+ for ( const primitive of meshDef.primitives ) {
1429
+
1430
+ if ( primitive.mode !== WEBGL_CONSTANTS.TRIANGLES &&
1431
+ primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_STRIP &&
1432
+ primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_FAN &&
1433
+ primitive.mode !== undefined ) {
1434
+
1435
+ return null;
1436
+
1437
+ }
1438
+
1439
+ }
1440
+
1441
+ const extensionDef = nodeDef.extensions[ this.name ];
1442
+ const attributesDef = extensionDef.attributes;
1443
+
1444
+ // @TODO: Can we support InstancedMesh + SkinnedMesh?
1445
+
1446
+ const pending = [];
1447
+ const attributes = {};
1448
+
1449
+ for ( const key in attributesDef ) {
1450
+
1451
+ pending.push( this.parser.getDependency( 'accessor', attributesDef[ key ] ).then( accessor => {
1452
+
1453
+ attributes[ key ] = accessor;
1454
+ return attributes[ key ];
1455
+
1456
+ } ) );
1457
+
1458
+ }
1459
+
1460
+ if ( pending.length < 1 ) {
1461
+
1462
+ return null;
1463
+
1464
+ }
1465
+
1466
+ pending.push( this.parser.createNodeMesh( nodeIndex ) );
1467
+
1468
+ return Promise.all( pending ).then( results => {
1469
+
1470
+ const nodeObject = results.pop();
1471
+ const meshes = nodeObject.isGroup ? nodeObject.children : [ nodeObject ];
1472
+ const count = results[ 0 ].count; // All attribute counts should be same
1473
+ const instancedMeshes = [];
1474
+
1475
+ for ( const mesh of meshes ) {
1476
+
1477
+ // Temporal variables
1478
+ const m = new Matrix4();
1479
+ const p = new Vector3();
1480
+ const q = new Quaternion();
1481
+ const s = new Vector3( 1, 1, 1 );
1482
+
1483
+ const instancedMesh = new InstancedMesh( mesh.geometry, mesh.material, count );
1484
+
1485
+ for ( let i = 0; i < count; i ++ ) {
1486
+
1487
+ if ( attributes.TRANSLATION ) {
1488
+
1489
+ p.fromBufferAttribute( attributes.TRANSLATION, i );
1490
+
1491
+ }
1492
+
1493
+ if ( attributes.ROTATION ) {
1494
+
1495
+ q.fromBufferAttribute( attributes.ROTATION, i );
1496
+
1497
+ }
1498
+
1499
+ if ( attributes.SCALE ) {
1500
+
1501
+ s.fromBufferAttribute( attributes.SCALE, i );
1502
+
1503
+ }
1504
+
1505
+ instancedMesh.setMatrixAt( i, m.compose( p, q, s ) );
1506
+
1507
+ }
1508
+
1509
+ // Add instance attributes to the geometry, excluding TRS.
1510
+ for ( const attributeName in attributes ) {
1511
+
1512
+ if ( attributeName !== 'TRANSLATION' &&
1513
+ attributeName !== 'ROTATION' &&
1514
+ attributeName !== 'SCALE' ) {
1515
+
1516
+ mesh.geometry.setAttribute( attributeName, attributes[ attributeName ] );
1517
+
1518
+ }
1519
+
1520
+ }
1521
+
1522
+ // Just in case
1523
+ Object3D.prototype.copy.call( instancedMesh, mesh );
1524
+
1525
+ // https://github.com/mrdoob/three.js/issues/18334
1526
+ instancedMesh.frustumCulled = false;
1527
+ this.parser.assignFinalMaterial( instancedMesh );
1528
+
1529
+ instancedMeshes.push( instancedMesh );
1530
+
1531
+ }
1532
+
1533
+ if ( nodeObject.isGroup ) {
1534
+
1535
+ nodeObject.clear();
1536
+
1537
+ nodeObject.add( ... instancedMeshes );
1538
+
1539
+ return nodeObject;
1540
+
1541
+ }
1542
+
1543
+ return instancedMeshes[ 0 ];
1544
+
1545
+ } );
1546
+
1547
+ }
1548
+
1549
+ }
1550
+
1387
1551
  /* BINARY EXTENSION */
1388
1552
  const BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
1389
1553
  const BINARY_EXTENSION_HEADER_LENGTH = 12;
@@ -3026,7 +3190,7 @@ class GLTFParser {
3026
3190
 
3027
3191
  texture.flipY = false;
3028
3192
 
3029
- if ( textureDef.name ) texture.name = textureDef.name;
3193
+ texture.name = textureDef.name || sourceDef.name || '';
3030
3194
 
3031
3195
  const samplers = json.samplers || {};
3032
3196
  const sampler = samplers[ textureDef.sampler ] || {};
@@ -13,6 +13,7 @@
13
13
 
14
14
  import {
15
15
  CompressedTexture,
16
+ CompressedArrayTexture,
16
17
  Data3DTexture,
17
18
  DataTexture,
18
19
  FileLoader,
@@ -35,7 +36,7 @@ import {
35
36
  RGBAFormat,
36
37
  RGFormat,
37
38
  sRGBEncoding,
38
- UnsignedByteType
39
+ UnsignedByteType,
39
40
  } from '../../../../three/src/Three.js';
40
41
  import { WorkerPool } from '../utils/WorkerPool.js';
41
42
  import {
@@ -236,16 +237,21 @@ class KTX2Loader extends Loader {
236
237
 
237
238
  }
238
239
 
239
- _createTextureFrom( transcodeResult ) {
240
+ _createTextureFrom( transcodeResult, container ) {
240
241
 
241
242
  const { mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags } = transcodeResult;
242
243
 
243
244
  if ( type === 'error' ) return Promise.reject( error );
244
245
 
245
- const texture = new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
246
+ const texture = container.layerCount > 1
247
+ ? new CompressedArrayTexture( mipmaps, width, height, container.layerCount, format, UnsignedByteType )
248
+ : new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
249
+
250
+
246
251
  texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
247
252
  texture.magFilter = LinearFilter;
248
253
  texture.generateMipmaps = false;
254
+
249
255
  texture.needsUpdate = true;
250
256
  texture.encoding = dfdTransferFn === KHR_DF_TRANSFER_SRGB ? sRGBEncoding : LinearEncoding;
251
257
  texture.premultiplyAlpha = !! ( dfdFlags & KHR_DF_FLAG_ALPHA_PREMULTIPLIED );
@@ -257,9 +263,9 @@ class KTX2Loader extends Loader {
257
263
  /**
258
264
  * @param {ArrayBuffer} buffer
259
265
  * @param {object?} config
260
- * @return {Promise<CompressedTexture|DataTexture|Data3DTexture>}
266
+ * @return {Promise<CompressedTexture|CompressedArrayTexture|DataTexture|Data3DTexture>}
261
267
  */
262
- _createTexture( buffer, config = {} ) {
268
+ async _createTexture( buffer, config = {} ) {
263
269
 
264
270
  const container = read( new Uint8Array( buffer ) );
265
271
 
@@ -270,13 +276,12 @@ class KTX2Loader extends Loader {
270
276
  }
271
277
 
272
278
  //
273
-
274
279
  const taskConfig = config;
275
280
  const texturePending = this.init().then( () => {
276
281
 
277
282
  return this.workerPool.postMessage( { type: 'transcode', buffer, taskConfig: taskConfig }, [ buffer ] );
278
283
 
279
- } ).then( ( e ) => this._createTextureFrom( e.data ) );
284
+ } ).then( ( e ) => this._createTextureFrom( e.data, container ) );
280
285
 
281
286
  // Cache the task result.
282
287
  _taskCache.set( buffer, { promise: texturePending } );
@@ -437,6 +442,7 @@ KTX2Loader.BasisWorker = function () {
437
442
  const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
438
443
  const width = ktx2File.getWidth();
439
444
  const height = ktx2File.getHeight();
445
+ const layers = ktx2File.getLayers() || 1;
440
446
  const levels = ktx2File.getLevels();
441
447
  const hasAlpha = ktx2File.getHasAlpha();
442
448
  const dfdTransferFn = ktx2File.getDFDTransferFunc();
@@ -462,30 +468,39 @@ KTX2Loader.BasisWorker = function () {
462
468
 
463
469
  for ( let mip = 0; mip < levels; mip ++ ) {
464
470
 
465
- const levelInfo = ktx2File.getImageLevelInfo( mip, 0, 0 );
466
- const mipWidth = levelInfo.origWidth;
467
- const mipHeight = levelInfo.origHeight;
468
- const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, 0, 0, transcoderFormat ) );
469
-
470
- const status = ktx2File.transcodeImage(
471
- dst,
472
- mip,
473
- 0,
474
- 0,
475
- transcoderFormat,
476
- 0,
477
- - 1,
478
- - 1,
479
- );
471
+ const layerMips = [];
472
+
473
+ let mipWidth, mipHeight;
474
+
475
+ for ( let layer = 0; layer < layers; layer ++ ) {
476
+
477
+ const levelInfo = ktx2File.getImageLevelInfo( mip, layer, 0 );
478
+ mipWidth = levelInfo.origWidth;
479
+ mipHeight = levelInfo.origHeight;
480
+ const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, layer, 0, transcoderFormat ) );
481
+ const status = ktx2File.transcodeImage(
482
+ dst,
483
+ mip,
484
+ layer,
485
+ 0,
486
+ transcoderFormat,
487
+ 0,
488
+ - 1,
489
+ - 1,
490
+ );
491
+
492
+ if ( ! status ) {
480
493
 
481
- if ( ! status ) {
494
+ cleanup();
495
+ throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );
482
496
 
483
- cleanup();
484
- throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );
497
+ }
498
+
499
+ layerMips.push( dst );
485
500
 
486
501
  }
487
502
 
488
- mipmaps.push( { data: dst, width: mipWidth, height: mipHeight } );
503
+ mipmaps.push( { data: concat( layerMips ), width: mipWidth, height: mipHeight } );
489
504
 
490
505
  }
491
506
 
@@ -612,6 +627,33 @@ KTX2Loader.BasisWorker = function () {
612
627
 
613
628
  }
614
629
 
630
+ /** Concatenates N byte arrays. */
631
+ function concat( arrays ) {
632
+
633
+ let totalByteLength = 0;
634
+
635
+ for ( const array of arrays ) {
636
+
637
+ totalByteLength += array.byteLength;
638
+
639
+ }
640
+
641
+ const result = new Uint8Array( totalByteLength );
642
+
643
+ let byteOffset = 0;
644
+
645
+ for ( const array of arrays ) {
646
+
647
+ result.set( array, byteOffset );
648
+
649
+ byteOffset += array.byteLength;
650
+
651
+ }
652
+
653
+ return result;
654
+
655
+ }
656
+
615
657
  };
616
658
 
617
659
  //
@@ -673,8 +715,6 @@ async function createDataTexture( container ) {
673
715
 
674
716
  }
675
717
 
676
- //
677
-
678
718
  const level = container.levels[ 0 ];
679
719
 
680
720
  let levelData;
@@ -731,7 +771,6 @@ async function createDataTexture( container ) {
731
771
  view = levelData;
732
772
 
733
773
  }
734
-
735
774
  //
736
775
 
737
776
  const texture = pixelDepth === 0
@@ -192,11 +192,6 @@ function mergeBufferGeometries( geometries, useGroups = false ) {
192
192
 
193
193
  }
194
194
 
195
- // gather .userData
196
-
197
- mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || [];
198
- mergedGeometry.userData.mergedUserData.push( geometry.userData );
199
-
200
195
  if ( useGroups ) {
201
196
 
202
197
  let count;
@@ -371,6 +366,28 @@ function mergeBufferAttributes( attributes ) {
371
366
 
372
367
  }
373
368
 
369
+ /**
370
+ * @param {BufferAttribute}
371
+ * @return {BufferAttribute}
372
+ */
373
+ export function deepCloneAttribute( attribute ) {
374
+
375
+ if ( attribute.isInstancedInterleavedBufferAttribute || attribute.isInterleavedBufferAttribute ) {
376
+
377
+ return deinterleaveAttribute( attribute );
378
+
379
+ }
380
+
381
+ if ( attribute.isInstancedBufferAttribute ) {
382
+
383
+ return new InstancedBufferAttribute().copy( attribute );
384
+
385
+ }
386
+
387
+ return new BufferAttribute().copy( attribute );
388
+
389
+ }
390
+
374
391
  /**
375
392
  * @param {Array<BufferAttribute>} attributes
376
393
  * @return {Array<InterleavedBufferAttribute>}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damienmortini/three",
3
- "version": "0.1.172",
3
+ "version": "0.1.173",
4
4
  "description": "Three.js helpers",
5
5
  "scripts": {
6
6
  "install": "npm run copyexamples",
@@ -25,9 +25,9 @@
25
25
  "bugs": "https://github.com/damienmortini/lib/issues",
26
26
  "homepage": "https://github.com/damienmortini/lib/tree/main/packages/three",
27
27
  "dependencies": {
28
- "@damienmortini/core": "^0.2.134",
28
+ "@damienmortini/core": "^0.2.135",
29
29
  "fs-extra": "^10.1.0",
30
- "three": "0.145.0"
30
+ "three": "0.146.0"
31
31
  },
32
- "gitHead": "9df921714722d7ffbe00c7ae4f0cdf77f0cbc6a4"
32
+ "gitHead": "2eeafcaa9fe81bdec2dc81d847962be4a821f5eb"
33
33
  }