@babylonjs/serializers 7.43.0 → 7.44.1

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.
Files changed (37) hide show
  1. package/USDZ/usdzExporter.d.ts +3 -3
  2. package/USDZ/usdzExporter.js +3 -3
  3. package/USDZ/usdzExporter.js.map +1 -1
  4. package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.d.ts +3 -3
  5. package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.js +10 -42
  6. package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.js.map +1 -1
  7. package/glTF/2.0/Extensions/KHR_draco_mesh_compression.d.ts +32 -0
  8. package/glTF/2.0/Extensions/KHR_draco_mesh_compression.js +136 -0
  9. package/glTF/2.0/Extensions/KHR_draco_mesh_compression.js.map +1 -0
  10. package/glTF/2.0/Extensions/index.d.ts +1 -0
  11. package/glTF/2.0/Extensions/index.js +1 -0
  12. package/glTF/2.0/Extensions/index.js.map +1 -1
  13. package/glTF/2.0/bufferManager.d.ts +68 -0
  14. package/glTF/2.0/bufferManager.js +152 -0
  15. package/glTF/2.0/bufferManager.js.map +1 -0
  16. package/glTF/2.0/dataWriter.d.ts +5 -3
  17. package/glTF/2.0/dataWriter.js +30 -12
  18. package/glTF/2.0/dataWriter.js.map +1 -1
  19. package/glTF/2.0/glTFAnimation.d.ts +7 -7
  20. package/glTF/2.0/glTFAnimation.js +30 -51
  21. package/glTF/2.0/glTFAnimation.js.map +1 -1
  22. package/glTF/2.0/glTFExporter.d.ts +5 -4
  23. package/glTF/2.0/glTFExporter.js +112 -146
  24. package/glTF/2.0/glTFExporter.js.map +1 -1
  25. package/glTF/2.0/glTFExporterExtension.d.ts +14 -10
  26. package/glTF/2.0/glTFExporterExtension.js.map +1 -1
  27. package/glTF/2.0/glTFMaterialExporter.js +25 -15
  28. package/glTF/2.0/glTFMaterialExporter.js.map +1 -1
  29. package/glTF/2.0/glTFMorphTargetsUtilities.d.ts +2 -2
  30. package/glTF/2.0/glTFMorphTargetsUtilities.js +23 -25
  31. package/glTF/2.0/glTFMorphTargetsUtilities.js.map +1 -1
  32. package/glTF/2.0/glTFSerializer.d.ts +8 -0
  33. package/glTF/2.0/glTFSerializer.js.map +1 -1
  34. package/glTF/2.0/glTFUtilities.d.ts +11 -28
  35. package/glTF/2.0/glTFUtilities.js +16 -57
  36. package/glTF/2.0/glTFUtilities.js.map +1 -1
  37. package/package.json +3 -3
@@ -2,7 +2,6 @@ import type { IBufferView, IAccessor, INode, IScene, IMesh, IMaterial, ITexture,
2
2
  import { ImageMimeType } from "babylonjs-gltf2interface";
3
3
  import type { Nullable } from "@babylonjs/core/types.js";
4
4
  import type { Node } from "@babylonjs/core/node.js";
5
- import type { SubMesh } from "@babylonjs/core/Meshes/subMesh.js";
6
5
  import type { BaseTexture } from "@babylonjs/core/Materials/Textures/baseTexture.js";
7
6
  import type { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
8
7
  import { Material } from "@babylonjs/core/Materials/material.js";
@@ -11,6 +10,7 @@ import type { IGLTFExporterExtensionV2 } from "./glTFExporterExtension";
11
10
  import { GLTFMaterialExporter } from "./glTFMaterialExporter";
12
11
  import type { IExportOptions } from "./glTFSerializer";
13
12
  import { GLTFData } from "./glTFData";
13
+ import { BufferManager } from "./bufferManager";
14
14
  /** @internal */
15
15
  export declare class GLTFExporter {
16
16
  readonly _glTF: IGLTF;
@@ -33,15 +33,15 @@ export declare class GLTFExporter {
33
33
  mimeType: ImageMimeType;
34
34
  };
35
35
  };
36
- private readonly _orderedImageData;
37
36
  /**
38
37
  * Baked animation sample rate
39
38
  */
40
39
  private _animationSampleRate;
41
40
  private readonly _options;
41
+ _shouldUseGlb: boolean;
42
42
  readonly _materialExporter: GLTFMaterialExporter;
43
43
  private readonly _extensions;
44
- private readonly _dataWriter;
44
+ readonly _bufferManager: BufferManager;
45
45
  private readonly _shouldExportNodeMap;
46
46
  private readonly _nodeMap;
47
47
  readonly _materialMap: Map<Material, number>;
@@ -55,11 +55,12 @@ export declare class GLTFExporter {
55
55
  private _applyExtension;
56
56
  private _applyExtensions;
57
57
  _extensionsPreExportTextureAsync(context: string, babylonTexture: Texture, mimeType: ImageMimeType): Promise<Nullable<BaseTexture>>;
58
- _extensionsPostExportMeshPrimitiveAsync(context: string, meshPrimitive: IMeshPrimitive, babylonSubMesh: SubMesh): Promise<Nullable<IMeshPrimitive>>;
59
58
  _extensionsPostExportNodeAsync(context: string, node: INode, babylonNode: Node, nodeMap: Map<Node, number>, convertToRightHanded: boolean): Promise<Nullable<INode>>;
60
59
  _extensionsPostExportMaterialAsync(context: string, material: IMaterial, babylonMaterial: Material): Promise<Nullable<IMaterial>>;
61
60
  _extensionsPostExportMaterialAdditionalTextures(context: string, material: IMaterial, babylonMaterial: Material): BaseTexture[];
62
61
  _extensionsPostExportTextures(context: string, textureInfo: ITextureInfo, babylonTexture: BaseTexture): void;
62
+ _extensionsPostExportMeshPrimitive(primitive: IMeshPrimitive): void;
63
+ _extensionsPreGenerateBinaryAsync(): Promise<void>;
63
64
  private _forEachExtensions;
64
65
  private _extensionsOnExporting;
65
66
  private _loadExtensions;
@@ -9,23 +9,24 @@ import { Engine } from "@babylonjs/core/Engines/engine.js";
9
9
  import { EngineStore } from "@babylonjs/core/Engines/engineStore.js";
10
10
  import { GLTFMaterialExporter } from "./glTFMaterialExporter.js";
11
11
  import { GLTFData } from "./glTFData.js";
12
- import { AreIndices32Bits, ConvertToRightHandedPosition, ConvertToRightHandedRotation, CreateAccessor, CreateBufferView, DataArrayToUint8Array, GetAccessorType, GetAttributeType, GetMinMax, GetPrimitiveMode, IndicesArrayToUint8Array, IsNoopNode, IsTriangleFillMode, IsParentAddedByImporter, ConvertToRightHandedNode, RotateNode180Y, FloatsNeed16BitInteger, IsStandardVertexAttribute, } from "./glTFUtilities.js";
13
- import { DataWriter } from "./dataWriter.js";
12
+ import { ConvertToRightHandedPosition, ConvertToRightHandedRotation, DataArrayToUint8Array, GetAccessorType, GetAttributeType, GetMinMax, GetPrimitiveMode, IsNoopNode, IsTriangleFillMode, IsParentAddedByImporter, ConvertToRightHandedNode, RotateNode180Y, FloatsNeed16BitInteger, IsStandardVertexAttribute, IndicesArrayToTypedArray, } from "./glTFUtilities.js";
13
+ import { BufferManager } from "./bufferManager.js";
14
14
  import { Camera } from "@babylonjs/core/Cameras/camera.js";
15
15
  import { MultiMaterial } from "@babylonjs/core/Materials/multiMaterial.js";
16
16
  import { PBRMaterial } from "@babylonjs/core/Materials/PBR/pbrMaterial.js";
17
17
  import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial.js";
18
18
  import { Logger } from "@babylonjs/core/Misc/logger.js";
19
- import { EnumerateFloatValues } from "@babylonjs/core/Buffers/bufferUtils.js";
19
+ import { EnumerateFloatValues, AreIndices32Bits } from "@babylonjs/core/Buffers/bufferUtils.js";
20
20
  import { _GLTFAnimation } from "./glTFAnimation.js";
21
21
  import { BuildMorphTargetBuffers } from "./glTFMorphTargetsUtilities.js";
22
22
  import { LinesMesh } from "@babylonjs/core/Meshes/linesMesh.js";
23
+ import { GreasedLineBaseMesh } from "@babylonjs/core/Meshes/GreasedLine/greasedLineBaseMesh.js";
23
24
  import { Color3, Color4 } from "@babylonjs/core/Maths/math.color.js";
24
25
  class ExporterState {
25
26
  constructor(convertToRightHanded, wasAddedByNoopNode) {
26
27
  // Babylon indices array, start, count, offset, flip -> glTF accessor index
27
28
  this._indicesAccessorMap = new Map();
28
- // Babylon buffer -> glTF buffer view index
29
+ // Babylon buffer -> glTF buffer view
29
30
  this._vertexBufferViewMap = new Map();
30
31
  // Babylon vertex buffer, start, count -> glTF accessor index
31
32
  this._vertexAccessorMap = new Map();
@@ -77,12 +78,12 @@ class ExporterState {
77
78
  getVertexBufferView(buffer) {
78
79
  return this._vertexBufferViewMap.get(buffer);
79
80
  }
80
- setVertexBufferView(buffer, bufferViewIndex) {
81
- this._vertexBufferViewMap.set(buffer, bufferViewIndex);
81
+ setVertexBufferView(buffer, bufferView) {
82
+ this._vertexBufferViewMap.set(buffer, bufferView);
82
83
  }
83
- setRemappedBufferView(buffer, vertexBuffer, bufferViewIndex) {
84
+ setRemappedBufferView(buffer, vertexBuffer, bufferView) {
84
85
  this._remappedBufferView.set(buffer, new Map());
85
- this._remappedBufferView.get(buffer).set(vertexBuffer, bufferViewIndex);
86
+ this._remappedBufferView.get(buffer).set(vertexBuffer, bufferView);
86
87
  }
87
88
  getRemappedBufferView(buffer, vertexBuffer) {
88
89
  return this._remappedBufferView.get(buffer)?.get(vertexBuffer);
@@ -148,11 +149,8 @@ export class GLTFExporter {
148
149
  _extensionsPreExportTextureAsync(context, babylonTexture, mimeType) {
149
150
  return this._applyExtensions(babylonTexture, (extension, node) => extension.preExportTextureAsync && extension.preExportTextureAsync(context, node, mimeType));
150
151
  }
151
- _extensionsPostExportMeshPrimitiveAsync(context, meshPrimitive, babylonSubMesh) {
152
- return this._applyExtensions(meshPrimitive, (extension, node) => extension.postExportMeshPrimitiveAsync && extension.postExportMeshPrimitiveAsync(context, node, babylonSubMesh));
153
- }
154
152
  _extensionsPostExportNodeAsync(context, node, babylonNode, nodeMap, convertToRightHanded) {
155
- return this._applyExtensions(node, (extension, node) => extension.postExportNodeAsync && extension.postExportNodeAsync(context, node, babylonNode, nodeMap, convertToRightHanded, this._dataWriter));
153
+ return this._applyExtensions(node, (extension, node) => extension.postExportNodeAsync && extension.postExportNodeAsync(context, node, babylonNode, nodeMap, convertToRightHanded, this._bufferManager));
156
154
  }
157
155
  _extensionsPostExportMaterialAsync(context, material, babylonMaterial) {
158
156
  return this._applyExtensions(material, (extension, node) => extension.postExportMaterialAsync && extension.postExportMaterialAsync(context, node, babylonMaterial));
@@ -175,6 +173,22 @@ export class GLTFExporter {
175
173
  }
176
174
  }
177
175
  }
176
+ _extensionsPostExportMeshPrimitive(primitive) {
177
+ for (const name of GLTFExporter._ExtensionNames) {
178
+ const extension = this._extensions[name];
179
+ if (extension.postExportMeshPrimitive) {
180
+ extension.postExportMeshPrimitive(primitive, this._bufferManager, this._accessors);
181
+ }
182
+ }
183
+ }
184
+ async _extensionsPreGenerateBinaryAsync() {
185
+ for (const name of GLTFExporter._ExtensionNames) {
186
+ const extension = this._extensions[name];
187
+ if (extension.preGenerateBinaryAsync) {
188
+ await extension.preGenerateBinaryAsync(this._bufferManager);
189
+ }
190
+ }
191
+ }
178
192
  _forEachExtensions(action) {
179
193
  for (const name of GLTFExporter._ExtensionNames) {
180
194
  const extension = this._extensions[name];
@@ -227,10 +241,10 @@ export class GLTFExporter {
227
241
  this._skins = [];
228
242
  this._textures = [];
229
243
  this._imageData = {};
230
- this._orderedImageData = [];
244
+ this._shouldUseGlb = false;
231
245
  this._materialExporter = new GLTFMaterialExporter(this);
232
246
  this._extensions = {};
233
- this._dataWriter = new DataWriter(4);
247
+ this._bufferManager = new BufferManager();
234
248
  this._shouldExportNodeMap = new Map();
235
249
  // Babylon node -> glTF node index
236
250
  this._nodeMap = new Map();
@@ -255,6 +269,7 @@ export class GLTFExporter {
255
269
  exportUnusedUVs: false,
256
270
  removeNoopRootNodes: true,
257
271
  includeCoordinateSystemConversionNodes: false,
272
+ meshCompressionMethod: "None",
258
273
  ...options,
259
274
  };
260
275
  this._loadExtensions();
@@ -286,12 +301,8 @@ export class GLTFExporter {
286
301
  }
287
302
  return true;
288
303
  }
289
- _generateJSON(shouldUseGlb, bufferByteLength, fileName, prettyPrint) {
304
+ _generateJSON(bufferByteLength, fileName, prettyPrint) {
290
305
  const buffer = { byteLength: bufferByteLength };
291
- let imageName;
292
- let imageData;
293
- let bufferView;
294
- let byteOffset = bufferByteLength;
295
306
  if (buffer.byteLength) {
296
307
  this._glTF.buffers = [buffer];
297
308
  }
@@ -330,30 +341,9 @@ export class GLTFExporter {
330
341
  this._glTF.skins = this._skins;
331
342
  }
332
343
  if (this._images && this._images.length) {
333
- if (!shouldUseGlb) {
334
- this._glTF.images = this._images;
335
- }
336
- else {
337
- this._glTF.images = [];
338
- this._images.forEach((image) => {
339
- if (image.uri) {
340
- imageData = this._imageData[image.uri];
341
- this._orderedImageData.push(imageData);
342
- bufferView = CreateBufferView(0, byteOffset, imageData.data.byteLength, undefined);
343
- byteOffset += imageData.data.byteLength;
344
- this._bufferViews.push(bufferView);
345
- image.bufferView = this._bufferViews.length - 1;
346
- image.name = imageName;
347
- image.mimeType = imageData.mimeType;
348
- image.uri = undefined;
349
- this._glTF.images.push(image);
350
- }
351
- });
352
- // Replace uri with bufferview and mime type for glb
353
- buffer.byteLength = byteOffset;
354
- }
344
+ this._glTF.images = this._images;
355
345
  }
356
- if (!shouldUseGlb) {
346
+ if (!this._shouldUseGlb) {
357
347
  buffer.uri = fileName + ".bin";
358
348
  }
359
349
  return prettyPrint ? JSON.stringify(this._glTF, null, 2) : JSON.stringify(this._glTF);
@@ -361,7 +351,7 @@ export class GLTFExporter {
361
351
  async generateGLTFAsync(glTFPrefix) {
362
352
  const binaryBuffer = await this._generateBinaryAsync();
363
353
  this._extensionsOnExporting();
364
- const jsonText = this._generateJSON(false, binaryBuffer.byteLength, glTFPrefix, true);
354
+ const jsonText = this._generateJSON(binaryBuffer.byteLength, glTFPrefix, true);
365
355
  const bin = new Blob([binaryBuffer], { type: "application/octet-stream" });
366
356
  const glTFFileName = glTFPrefix + ".gltf";
367
357
  const glTFBinFile = glTFPrefix + ".bin";
@@ -377,7 +367,8 @@ export class GLTFExporter {
377
367
  }
378
368
  async _generateBinaryAsync() {
379
369
  await this._exportSceneAsync();
380
- return this._dataWriter.getOutputData();
370
+ await this._extensionsPreGenerateBinaryAsync();
371
+ return this._bufferManager.generateBinary(this._bufferViews);
381
372
  }
382
373
  /**
383
374
  * Pads the number to a multiple of 4
@@ -390,28 +381,24 @@ export class GLTFExporter {
390
381
  return padding;
391
382
  }
392
383
  async generateGLBAsync(glTFPrefix) {
384
+ this._shouldUseGlb = true;
393
385
  const binaryBuffer = await this._generateBinaryAsync();
394
386
  this._extensionsOnExporting();
395
- const jsonText = this._generateJSON(true, binaryBuffer.byteLength);
387
+ const jsonText = this._generateJSON(binaryBuffer.byteLength);
396
388
  const glbFileName = glTFPrefix + ".glb";
397
389
  const headerLength = 12;
398
390
  const chunkLengthPrefix = 8;
399
391
  let jsonLength = jsonText.length;
400
392
  let encodedJsonText;
401
- let imageByteLength = 0;
402
393
  // make use of TextEncoder when available
403
394
  if (typeof TextEncoder !== "undefined") {
404
395
  const encoder = new TextEncoder();
405
396
  encodedJsonText = encoder.encode(jsonText);
406
397
  jsonLength = encodedJsonText.length;
407
398
  }
408
- for (let i = 0; i < this._orderedImageData.length; ++i) {
409
- imageByteLength += this._orderedImageData[i].data.byteLength;
410
- }
411
399
  const jsonPadding = this._getPadding(jsonLength);
412
400
  const binPadding = this._getPadding(binaryBuffer.byteLength);
413
- const imagePadding = this._getPadding(imageByteLength);
414
- const byteLength = headerLength + 2 * chunkLengthPrefix + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding + imageByteLength + imagePadding;
401
+ const byteLength = headerLength + 2 * chunkLengthPrefix + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding;
415
402
  // header
416
403
  const headerBuffer = new ArrayBuffer(headerLength);
417
404
  const headerBufferView = new DataView(headerBuffer);
@@ -450,7 +437,7 @@ export class GLTFExporter {
450
437
  // binary chunk
451
438
  const binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
452
439
  const binaryChunkBufferView = new DataView(binaryChunkBuffer);
453
- binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + binPadding + imageByteLength + imagePadding, true);
440
+ binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + binPadding, true);
454
441
  binaryChunkBufferView.setUint32(4, 0x004e4942, true);
455
442
  // binary padding
456
443
  const binPaddingBuffer = new ArrayBuffer(binPadding);
@@ -458,18 +445,7 @@ export class GLTFExporter {
458
445
  for (let i = 0; i < binPadding; ++i) {
459
446
  binPaddingView[i] = 0;
460
447
  }
461
- const imagePaddingBuffer = new ArrayBuffer(imagePadding);
462
- const imagePaddingView = new Uint8Array(imagePaddingBuffer);
463
- for (let i = 0; i < imagePadding; ++i) {
464
- imagePaddingView[i] = 0;
465
- }
466
- const glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer];
467
- // binary data
468
- for (let i = 0; i < this._orderedImageData.length; ++i) {
469
- glbData.push(this._orderedImageData[i].data);
470
- }
471
- glbData.push(binPaddingBuffer);
472
- glbData.push(imagePaddingBuffer);
448
+ const glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer];
473
449
  const glbFile = new Blob(glbData, { type: "application/octet-stream" });
474
450
  const container = new GLTFData();
475
451
  container.files[glbFileName] = glbFile;
@@ -617,21 +593,16 @@ export class GLTFExporter {
617
593
  const skinedNodes = this._nodesSkinMap.get(skin);
618
594
  // Only create skeleton if it has at least one joint and is used by a mesh.
619
595
  if (skin.joints.length > 0 && skinedNodes !== undefined) {
620
- // create buffer view for inverse bind matrices
621
- const byteStride = 64; // 4 x 4 matrix of 32 bit float
622
- const byteLength = inverseBindMatrices.length * byteStride;
623
- const bufferViewOffset = this._dataWriter.byteOffset;
624
- const bufferView = CreateBufferView(0, bufferViewOffset, byteLength, undefined);
625
- this._bufferViews.push(bufferView);
626
- const bufferViewIndex = this._bufferViews.length - 1;
627
- const bindMatrixAccessor = CreateAccessor(bufferViewIndex, "MAT4" /* AccessorType.MAT4 */, 5126 /* AccessorComponentType.FLOAT */, inverseBindMatrices.length, null, null);
628
- const inverseBindAccessorIndex = this._accessors.push(bindMatrixAccessor) - 1;
629
- skin.inverseBindMatrices = inverseBindAccessorIndex;
630
- inverseBindMatrices.forEach((mat) => {
631
- mat.m.forEach((cell) => {
632
- this._dataWriter.writeFloat32(cell);
633
- });
596
+ // Put IBM data into TypedArraybuffer view
597
+ const byteLength = inverseBindMatrices.length * 64; // Always a 4 x 4 matrix of 32 bit float
598
+ const inverseBindMatricesData = new Float32Array(byteLength / 4);
599
+ inverseBindMatrices.forEach((mat, index) => {
600
+ inverseBindMatricesData.set(mat.m, index * 16);
634
601
  });
602
+ // Create buffer view and accessor
603
+ const bufferView = this._bufferManager.createBufferView(inverseBindMatricesData);
604
+ this._accessors.push(this._bufferManager.createAccessor(bufferView, "MAT4" /* AccessorType.MAT4 */, 5126 /* AccessorComponentType.FLOAT */, inverseBindMatrices.length));
605
+ skin.inverseBindMatrices = this._accessors.length - 1;
635
606
  this._skins.push(skin);
636
607
  for (const skinedNode of skinedNodes) {
637
608
  skinedNode.skin = this._skins.length - 1;
@@ -682,7 +653,7 @@ export class GLTFExporter {
682
653
  this._exportAndAssignCameras();
683
654
  this._exportAndAssignSkeletons();
684
655
  if (this._babylonScene.animationGroups.length) {
685
- _GLTFAnimation._CreateNodeAndMorphAnimationFromAnimationGroups(this._babylonScene, this._animations, this._nodeMap, this._dataWriter, this._bufferViews, this._accessors, this._animationSampleRate, stateLH.getNodesSet());
656
+ _GLTFAnimation._CreateNodeAndMorphAnimationFromAnimationGroups(this._babylonScene, this._animations, this._nodeMap, this._bufferManager, this._bufferViews, this._accessors, this._animationSampleRate, stateLH.getNodesSet());
686
657
  }
687
658
  }
688
659
  _shouldExportNode(babylonNode) {
@@ -706,6 +677,9 @@ export class GLTFExporter {
706
677
  const vertexBuffers = babylonNode.geometry.getVertexBuffers();
707
678
  if (vertexBuffers) {
708
679
  for (const kind in vertexBuffers) {
680
+ if (!IsStandardVertexAttribute(kind)) {
681
+ continue;
682
+ }
709
683
  const vertexBuffer = vertexBuffers[kind];
710
684
  state.setHasVertexColorAlpha(vertexBuffer, babylonNode.hasVertexAlpha);
711
685
  const buffer = vertexBuffer._buffer;
@@ -772,10 +746,13 @@ export class GLTFExporter {
772
746
  case VertexBuffer.NormalKind:
773
747
  case VertexBuffer.TangentKind: {
774
748
  EnumerateFloatValues(bytes, byteOffset, byteStride, size, type, maxTotalVertices * size, normalized, (values) => {
775
- const invLength = 1 / Math.sqrt(values[0] * values[0] + values[1] * values[1] + values[2] * values[2]);
776
- values[0] *= invLength;
777
- values[1] *= invLength;
778
- values[2] *= invLength;
749
+ const length = Math.sqrt(values[0] * values[0] + values[1] * values[1] + values[2] * values[2]);
750
+ if (length > 0) {
751
+ const invLength = 1 / length;
752
+ values[0] *= invLength;
753
+ values[1] *= invLength;
754
+ values[2] *= invLength;
755
+ }
779
756
  });
780
757
  break;
781
758
  }
@@ -832,12 +809,11 @@ export class GLTFExporter {
832
809
  // Save converted bytes for min/max computation.
833
810
  state.convertedToRightHandedBuffers.set(buffer, bytes);
834
811
  }
835
- const byteOffset = this._dataWriter.byteOffset;
836
- this._dataWriter.writeUint8Array(bytes);
837
- this._bufferViews.push(CreateBufferView(0, byteOffset, bytes.length, byteStride));
838
- state.setVertexBufferView(buffer, this._bufferViews.length - 1);
812
+ // Create buffer view, but defer accessor creation for later. Instead, track it via ExporterState.
813
+ const bufferView = this._bufferManager.createBufferView(bytes, byteStride);
814
+ state.setVertexBufferView(buffer, bufferView);
839
815
  const floatMatricesIndices = new Map();
840
- // If buffers are of type MatricesWeightsKind and have float values, we need to create a new buffer instead.
816
+ // If buffers are of type MatricesIndicesKind and have float values, we need to create a new buffer instead.
841
817
  for (const vertexBuffer of vertexBuffers) {
842
818
  switch (vertexBuffer.getKind()) {
843
819
  case VertexBuffer.MatricesIndicesKind:
@@ -862,24 +838,13 @@ export class GLTFExporter {
862
838
  if (!array) {
863
839
  continue;
864
840
  }
865
- const byteOffset = this._dataWriter.byteOffset;
866
- if (FloatsNeed16BitInteger(array)) {
867
- const newArray = new Uint16Array(array.length);
868
- for (let index = 0; index < array.length; index++) {
869
- newArray[index] = array[index];
870
- }
871
- this._dataWriter.writeUint16Array(newArray);
872
- this._bufferViews.push(CreateBufferView(0, byteOffset, newArray.byteLength, 4 * 2));
873
- }
874
- else {
875
- const newArray = new Uint8Array(array.length);
876
- for (let index = 0; index < array.length; index++) {
877
- newArray[index] = array[index];
878
- }
879
- this._dataWriter.writeUint8Array(newArray);
880
- this._bufferViews.push(CreateBufferView(0, byteOffset, newArray.byteLength, 4));
841
+ const is16Bit = FloatsNeed16BitInteger(array);
842
+ const newArray = new (is16Bit ? Uint16Array : Uint8Array)(array.length);
843
+ for (let index = 0; index < array.length; index++) {
844
+ newArray[index] = array[index];
881
845
  }
882
- state.setRemappedBufferView(buffer, vertexBuffer, this._bufferViews.length - 1);
846
+ const bufferView = this._bufferManager.createBufferView(newArray, 4 * (is16Bit ? 2 : 1));
847
+ state.setRemappedBufferView(buffer, vertexBuffer, bufferView);
883
848
  }
884
849
  }
885
850
  const morphTargets = Array.from(morphTagetsMeshesMap.keys());
@@ -888,7 +853,7 @@ export class GLTFExporter {
888
853
  if (!meshes) {
889
854
  continue;
890
855
  }
891
- const glTFMorphTarget = BuildMorphTargetBuffers(morphTarget, meshes[0], this._dataWriter, this._bufferViews, this._accessors, state.convertToRightHanded);
856
+ const glTFMorphTarget = BuildMorphTargetBuffers(morphTarget, meshes[0], this._bufferManager, this._bufferViews, this._accessors, state.convertToRightHanded);
892
857
  for (const mesh of meshes) {
893
858
  state.bindMorphDataToMesh(mesh, glTFMorphTarget);
894
859
  }
@@ -922,9 +887,9 @@ export class GLTFExporter {
922
887
  };
923
888
  const idleGLTFAnimations = [];
924
889
  if (!this._babylonScene.animationGroups.length) {
925
- _GLTFAnimation._CreateMorphTargetAnimationFromMorphTargetAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, this._nodeMap, this._nodes, this._dataWriter, this._bufferViews, this._accessors, this._animationSampleRate, state.convertToRightHanded, this._options.shouldExportAnimation);
890
+ _GLTFAnimation._CreateMorphTargetAnimationFromMorphTargetAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, this._nodeMap, this._nodes, this._bufferManager, this._bufferViews, this._accessors, this._animationSampleRate, state.convertToRightHanded, this._options.shouldExportAnimation);
926
891
  if (babylonNode.animations.length) {
927
- _GLTFAnimation._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, this._nodeMap, this._nodes, this._dataWriter, this._bufferViews, this._accessors, this._animationSampleRate, state.convertToRightHanded, this._options.shouldExportAnimation);
892
+ _GLTFAnimation._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, this._nodeMap, this._nodes, this._bufferManager, this._bufferViews, this._accessors, this._animationSampleRate, state.convertToRightHanded, this._options.shouldExportAnimation);
928
893
  }
929
894
  }
930
895
  if (runtimeGLTFAnimation.channels.length && runtimeGLTFAnimation.samplers.length) {
@@ -1047,13 +1012,10 @@ export class GLTFExporter {
1047
1012
  if (indicesToExport) {
1048
1013
  let accessorIndex = state.getIndicesAccessor(indices, start, count, offset, flip);
1049
1014
  if (accessorIndex === undefined) {
1050
- const bufferViewByteOffset = this._dataWriter.byteOffset;
1051
- const bytes = IndicesArrayToUint8Array(indicesToExport, start, count, is32Bits);
1052
- this._dataWriter.writeUint8Array(bytes);
1053
- this._bufferViews.push(CreateBufferView(0, bufferViewByteOffset, bytes.length));
1054
- const bufferViewIndex = this._bufferViews.length - 1;
1015
+ const bytes = IndicesArrayToTypedArray(indicesToExport, start, count, is32Bits);
1016
+ const bufferView = this._bufferManager.createBufferView(bytes);
1055
1017
  const componentType = is32Bits ? 5125 /* AccessorComponentType.UNSIGNED_INT */ : 5123 /* AccessorComponentType.UNSIGNED_SHORT */;
1056
- this._accessors.push(CreateAccessor(bufferViewIndex, "SCALAR" /* AccessorType.SCALAR */, componentType, count, 0));
1018
+ this._accessors.push(this._bufferManager.createAccessor(bufferView, "SCALAR" /* AccessorType.SCALAR */, componentType, count, 0));
1057
1019
  accessorIndex = this._accessors.length - 1;
1058
1020
  state.setIndicesAccessor(indices, start, count, offset, flip, accessorIndex);
1059
1021
  }
@@ -1074,30 +1036,19 @@ export class GLTFExporter {
1074
1036
  if (accessorIndex === undefined) {
1075
1037
  // Get min/max from converted or original data.
1076
1038
  const data = state.convertedToRightHandedBuffers.get(vertexBuffer._buffer) || vertexBuffer._buffer.getData();
1077
- const minMax = kind === VertexBuffer.PositionKind ? GetMinMax(data, vertexBuffer, start, count) : null;
1078
- if ((kind === VertexBuffer.MatricesIndicesKind || kind === VertexBuffer.MatricesIndicesExtraKind) && vertexBuffer.type === VertexBuffer.FLOAT) {
1079
- const bufferViewIndex = state.getRemappedBufferView(vertexBuffer._buffer, vertexBuffer);
1080
- if (bufferViewIndex !== undefined) {
1081
- const byteOffset = vertexBuffer.byteOffset + start * vertexBuffer.byteStride;
1082
- this._accessors.push(CreateAccessor(bufferViewIndex, GetAccessorType(kind, state.hasVertexColorAlpha(vertexBuffer)), VertexBuffer.UNSIGNED_BYTE, count, byteOffset, minMax));
1083
- accessorIndex = this._accessors.length - 1;
1084
- state.setVertexAccessor(vertexBuffer, start, count, accessorIndex);
1085
- primitive.attributes[GetAttributeType(kind)] = accessorIndex;
1086
- }
1087
- }
1088
- else {
1089
- const bufferViewIndex = state.getVertexBufferView(vertexBuffer._buffer);
1090
- const byteOffset = vertexBuffer.byteOffset + start * vertexBuffer.byteStride;
1091
- this._accessors.push(CreateAccessor(bufferViewIndex, GetAccessorType(kind, state.hasVertexColorAlpha(vertexBuffer)), vertexBuffer.type, count, byteOffset, minMax, vertexBuffer.normalized // TODO: Find other places where this is needed.
1092
- ));
1093
- accessorIndex = this._accessors.length - 1;
1094
- state.setVertexAccessor(vertexBuffer, start, count, accessorIndex);
1095
- primitive.attributes[GetAttributeType(kind)] = accessorIndex;
1096
- }
1097
- }
1098
- else {
1099
- primitive.attributes[GetAttributeType(kind)] = accessorIndex;
1100
- }
1039
+ const minMax = kind === VertexBuffer.PositionKind ? GetMinMax(data, vertexBuffer, start, count) : undefined;
1040
+ // For the remapped buffer views we created for float matrices indices, make sure to use their updated information.
1041
+ const isFloatMatricesIndices = (kind === VertexBuffer.MatricesIndicesKind || kind === VertexBuffer.MatricesIndicesExtraKind) && vertexBuffer.type === VertexBuffer.FLOAT;
1042
+ const vertexBufferType = isFloatMatricesIndices ? VertexBuffer.UNSIGNED_BYTE : vertexBuffer.type;
1043
+ const vertexBufferNormalized = isFloatMatricesIndices ? undefined : vertexBuffer.normalized;
1044
+ const bufferView = isFloatMatricesIndices ? state.getRemappedBufferView(vertexBuffer._buffer, vertexBuffer) : state.getVertexBufferView(vertexBuffer._buffer);
1045
+ const byteOffset = vertexBuffer.byteOffset + start * vertexBuffer.byteStride;
1046
+ this._accessors.push(this._bufferManager.createAccessor(bufferView, GetAccessorType(kind, state.hasVertexColorAlpha(vertexBuffer)), vertexBufferType, count, byteOffset, minMax, vertexBufferNormalized // TODO: Find other places where this is needed.
1047
+ ));
1048
+ accessorIndex = this._accessors.length - 1;
1049
+ state.setVertexAccessor(vertexBuffer, start, count, accessorIndex);
1050
+ }
1051
+ primitive.attributes[GetAttributeType(kind)] = accessorIndex;
1101
1052
  }
1102
1053
  async _exportMaterialAsync(babylonMaterial, vertexBuffers, subMesh, primitive) {
1103
1054
  let materialIndex = this._materialMap.get(babylonMaterial);
@@ -1130,17 +1081,31 @@ export class GLTFExporter {
1130
1081
  const indices = babylonMesh.isUnIndexed ? null : babylonMesh.getIndices();
1131
1082
  const vertexBuffers = babylonMesh.geometry?.getVertexBuffers();
1132
1083
  const morphTargets = state.getMorphTargetsFromMesh(babylonMesh);
1133
- let isLinesMesh = false;
1134
- if (babylonMesh instanceof LinesMesh) {
1135
- isLinesMesh = true;
1136
- }
1084
+ const isLinesMesh = babylonMesh instanceof LinesMesh;
1085
+ const isGreasedLineMesh = babylonMesh instanceof GreasedLineBaseMesh;
1137
1086
  const subMeshes = babylonMesh.subMeshes;
1138
1087
  if (vertexBuffers && subMeshes && subMeshes.length > 0) {
1139
1088
  for (const subMesh of subMeshes) {
1140
1089
  const primitive = { attributes: {} };
1141
1090
  const babylonMaterial = subMesh.getMaterial() || this._babylonScene.defaultMaterial;
1142
- // Special case for LinesMesh
1143
- if (isLinesMesh) {
1091
+ if (isGreasedLineMesh) {
1092
+ const material = {
1093
+ name: babylonMaterial.name,
1094
+ };
1095
+ const babylonLinesMesh = babylonMesh;
1096
+ const colorWhite = Color3.White();
1097
+ const alpha = babylonLinesMesh.material?.alpha ?? 1;
1098
+ const color = babylonLinesMesh.greasedLineMaterial?.color ?? colorWhite;
1099
+ if (!color.equals(colorWhite) || alpha < 1) {
1100
+ material.pbrMetallicRoughness = {
1101
+ baseColorFactor: [...color.asArray(), alpha],
1102
+ };
1103
+ }
1104
+ this._materials.push(material);
1105
+ primitive.material = this._materials.length - 1;
1106
+ }
1107
+ else if (isLinesMesh) {
1108
+ // Special case for LinesMesh
1144
1109
  const material = {
1145
1110
  name: babylonMaterial.name,
1146
1111
  };
@@ -1158,20 +1123,21 @@ export class GLTFExporter {
1158
1123
  await this._exportMaterialAsync(babylonMaterial, vertexBuffers, subMesh, primitive);
1159
1124
  }
1160
1125
  // Index buffer
1161
- const fillMode = isLinesMesh ? Material.LineListDrawMode : (babylonMesh.overrideRenderingFillMode ?? babylonMaterial.fillMode);
1126
+ const fillMode = isLinesMesh || isGreasedLineMesh ? Material.LineListDrawMode : (babylonMesh.overrideRenderingFillMode ?? babylonMaterial.fillMode);
1162
1127
  const sideOrientation = babylonMaterial._getEffectiveOrientation(babylonMesh);
1163
1128
  this._exportIndices(indices, subMesh.indexStart, subMesh.indexCount, -subMesh.verticesStart, fillMode, sideOrientation, state, primitive);
1164
1129
  // Vertex buffers
1165
1130
  for (const vertexBuffer of Object.values(vertexBuffers)) {
1166
1131
  this._exportVertexBuffer(vertexBuffer, babylonMaterial, subMesh.verticesStart, subMesh.verticesCount, state, primitive);
1167
1132
  }
1168
- mesh.primitives.push(primitive);
1169
1133
  if (morphTargets) {
1170
1134
  primitive.targets = [];
1171
1135
  for (const gltfMorphTarget of morphTargets) {
1172
1136
  primitive.targets.push(gltfMorphTarget.attributes);
1173
1137
  }
1174
1138
  }
1139
+ mesh.primitives.push(primitive);
1140
+ this._extensionsPostExportMeshPrimitive(primitive);
1175
1141
  }
1176
1142
  }
1177
1143
  if (morphTargets) {