@babylonjs/serializers 7.37.1 → 7.37.2

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 (70) hide show
  1. package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.d.ts +6 -7
  2. package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.js +18 -12
  3. package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.js.map +1 -1
  4. package/glTF/2.0/Extensions/KHR_lights_punctual.d.ts +5 -6
  5. package/glTF/2.0/Extensions/KHR_lights_punctual.js +99 -109
  6. package/glTF/2.0/Extensions/KHR_lights_punctual.js.map +1 -1
  7. package/glTF/2.0/Extensions/KHR_materials_anisotropy.d.ts +2 -2
  8. package/glTF/2.0/Extensions/KHR_materials_anisotropy.js +6 -6
  9. package/glTF/2.0/Extensions/KHR_materials_anisotropy.js.map +1 -1
  10. package/glTF/2.0/Extensions/KHR_materials_clearcoat.d.ts +2 -2
  11. package/glTF/2.0/Extensions/KHR_materials_clearcoat.js +9 -9
  12. package/glTF/2.0/Extensions/KHR_materials_clearcoat.js.map +1 -1
  13. package/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.d.ts +2 -2
  14. package/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.js +7 -7
  15. package/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.js.map +1 -1
  16. package/glTF/2.0/Extensions/KHR_materials_dispersion.js +2 -2
  17. package/glTF/2.0/Extensions/KHR_materials_dispersion.js.map +1 -1
  18. package/glTF/2.0/Extensions/KHR_materials_emissive_strength.js +2 -2
  19. package/glTF/2.0/Extensions/KHR_materials_emissive_strength.js.map +1 -1
  20. package/glTF/2.0/Extensions/KHR_materials_ior.js +2 -2
  21. package/glTF/2.0/Extensions/KHR_materials_ior.js.map +1 -1
  22. package/glTF/2.0/Extensions/KHR_materials_iridescence.d.ts +2 -2
  23. package/glTF/2.0/Extensions/KHR_materials_iridescence.js +7 -7
  24. package/glTF/2.0/Extensions/KHR_materials_iridescence.js.map +1 -1
  25. package/glTF/2.0/Extensions/KHR_materials_sheen.d.ts +2 -2
  26. package/glTF/2.0/Extensions/KHR_materials_sheen.js +8 -8
  27. package/glTF/2.0/Extensions/KHR_materials_sheen.js.map +1 -1
  28. package/glTF/2.0/Extensions/KHR_materials_specular.d.ts +2 -2
  29. package/glTF/2.0/Extensions/KHR_materials_specular.js +7 -7
  30. package/glTF/2.0/Extensions/KHR_materials_specular.js.map +1 -1
  31. package/glTF/2.0/Extensions/KHR_materials_transmission.d.ts +2 -2
  32. package/glTF/2.0/Extensions/KHR_materials_transmission.js +6 -6
  33. package/glTF/2.0/Extensions/KHR_materials_transmission.js.map +1 -1
  34. package/glTF/2.0/Extensions/KHR_materials_unlit.js +2 -2
  35. package/glTF/2.0/Extensions/KHR_materials_unlit.js.map +1 -1
  36. package/glTF/2.0/Extensions/KHR_materials_volume.d.ts +2 -2
  37. package/glTF/2.0/Extensions/KHR_materials_volume.js +6 -6
  38. package/glTF/2.0/Extensions/KHR_materials_volume.js.map +1 -1
  39. package/glTF/2.0/Extensions/KHR_texture_transform.js +2 -2
  40. package/glTF/2.0/Extensions/KHR_texture_transform.js.map +1 -1
  41. package/glTF/2.0/Extensions/index.d.ts +9 -9
  42. package/glTF/2.0/Extensions/index.js +9 -9
  43. package/glTF/2.0/Extensions/index.js.map +1 -1
  44. package/glTF/2.0/dataWriter.d.ts +18 -0
  45. package/glTF/2.0/dataWriter.js +65 -0
  46. package/glTF/2.0/dataWriter.js.map +1 -0
  47. package/glTF/2.0/glTFAnimation.d.ts +4 -10
  48. package/glTF/2.0/glTFAnimation.js +87 -19
  49. package/glTF/2.0/glTFAnimation.js.map +1 -1
  50. package/glTF/2.0/glTFData.d.ts +5 -3
  51. package/glTF/2.0/glTFData.js +35 -36
  52. package/glTF/2.0/glTFData.js.map +1 -1
  53. package/glTF/2.0/glTFExporter.d.ts +62 -363
  54. package/glTF/2.0/glTFExporter.js +867 -1579
  55. package/glTF/2.0/glTFExporter.js.map +1 -1
  56. package/glTF/2.0/glTFExporterExtension.d.ts +8 -9
  57. package/glTF/2.0/glTFExporterExtension.js.map +1 -1
  58. package/glTF/2.0/glTFMaterialExporter.d.ts +26 -126
  59. package/glTF/2.0/glTFMaterialExporter.js +276 -438
  60. package/glTF/2.0/glTFMaterialExporter.js.map +1 -1
  61. package/glTF/2.0/glTFMorphTargetsUtilities.d.ts +14 -0
  62. package/glTF/2.0/glTFMorphTargetsUtilities.js +105 -0
  63. package/glTF/2.0/glTFMorphTargetsUtilities.js.map +1 -0
  64. package/glTF/2.0/glTFSerializer.d.ts +10 -13
  65. package/glTF/2.0/glTFSerializer.js +25 -44
  66. package/glTF/2.0/glTFSerializer.js.map +1 -1
  67. package/glTF/2.0/glTFUtilities.d.ts +75 -43
  68. package/glTF/2.0/glTFUtilities.js +350 -103
  69. package/glTF/2.0/glTFUtilities.js.map +1 -1
  70. package/package.json +3 -3
@@ -1,97 +1,133 @@
1
- import { Matrix, TmpVectors, Vector2, Vector3, Vector4, Quaternion } from "@babylonjs/core/Maths/math.vector.js";
2
- import { Color3, Color4 } from "@babylonjs/core/Maths/math.color.js";
1
+ import { TmpVectors, Quaternion, Matrix } from "@babylonjs/core/Maths/math.vector.js";
3
2
  import { Tools } from "@babylonjs/core/Misc/tools.js";
4
3
  import { VertexBuffer } from "@babylonjs/core/Buffers/buffer.js";
5
4
  import { TransformNode } from "@babylonjs/core/Meshes/transformNode.js";
6
5
  import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
7
- import { LinesMesh } from "@babylonjs/core/Meshes/linesMesh.js";
8
6
  import { InstancedMesh } from "@babylonjs/core/Meshes/instancedMesh.js";
9
7
  import { Material } from "@babylonjs/core/Materials/material.js";
10
8
  import { Engine } from "@babylonjs/core/Engines/engine.js";
11
- import { _GLTFMaterialExporter } from "./glTFMaterialExporter.js";
12
- import { _GLTFUtilities } from "./glTFUtilities.js";
9
+ import { EngineStore } from "@babylonjs/core/Engines/engineStore.js";
10
+ import { GLTFMaterialExporter } from "./glTFMaterialExporter.js";
13
11
  import { GLTFData } from "./glTFData.js";
14
- import { _GLTFAnimation } from "./glTFAnimation.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";
15
14
  import { Camera } from "@babylonjs/core/Cameras/camera.js";
16
- import { EngineStore } from "@babylonjs/core/Engines/engineStore.js";
17
15
  import { MultiMaterial } from "@babylonjs/core/Materials/multiMaterial.js";
18
- // Matrix that converts handedness on the X-axis.
19
- const convertHandednessMatrix = Matrix.Compose(new Vector3(-1, 1, 1), Quaternion.Identity(), Vector3.Zero());
20
- // 180 degrees rotation in Y.
21
- const rotation180Y = new Quaternion(0, 1, 0, 0);
22
- function isNoopNode(node, useRightHandedSystem) {
23
- if (!(node instanceof TransformNode)) {
24
- return false;
25
- }
26
- // Transform
27
- if (useRightHandedSystem) {
28
- const matrix = node.getWorldMatrix();
29
- if (!matrix.isIdentity()) {
30
- return false;
31
- }
32
- }
33
- else {
34
- const matrix = node.getWorldMatrix().multiplyToRef(convertHandednessMatrix, TmpVectors.Matrix[0]);
35
- if (!matrix.isIdentity()) {
36
- return false;
37
- }
38
- }
39
- // Geometry
40
- if ((node instanceof Mesh && node.geometry) || (node instanceof InstancedMesh && node.sourceMesh.geometry)) {
41
- return false;
42
- }
43
- return true;
44
- }
45
- function convertNodeHandedness(node) {
46
- const translation = Vector3.FromArrayToRef(node.translation || [0, 0, 0], 0, TmpVectors.Vector3[0]);
47
- const rotation = Quaternion.FromArrayToRef(node.rotation || [0, 0, 0, 1], 0, TmpVectors.Quaternion[0]);
48
- const scale = Vector3.FromArrayToRef(node.scale || [1, 1, 1], 0, TmpVectors.Vector3[1]);
49
- const matrix = Matrix.ComposeToRef(scale, rotation, translation, TmpVectors.Matrix[0]).multiplyToRef(convertHandednessMatrix, TmpVectors.Matrix[0]);
50
- matrix.decompose(scale, rotation, translation);
51
- if (translation.equalsToFloats(0, 0, 0)) {
52
- delete node.translation;
53
- }
54
- else {
55
- node.translation = translation.asArray();
56
- }
57
- if (Quaternion.IsIdentity(rotation)) {
58
- delete node.rotation;
59
- }
60
- else {
61
- node.rotation = rotation.asArray();
62
- }
63
- if (scale.equalsToFloats(1, 1, 1)) {
64
- delete node.scale;
65
- }
66
- else {
67
- node.scale = scale.asArray();
68
- }
69
- }
70
- function getBinaryWriterFunc(binaryWriter, attributeComponentKind) {
71
- switch (attributeComponentKind) {
72
- case 5121 /* AccessorComponentType.UNSIGNED_BYTE */: {
73
- return binaryWriter.setUInt8.bind(binaryWriter);
74
- }
75
- case 5123 /* AccessorComponentType.UNSIGNED_SHORT */: {
76
- return binaryWriter.setUInt16.bind(binaryWriter);
77
- }
78
- case 5125 /* AccessorComponentType.UNSIGNED_INT */: {
79
- return binaryWriter.setUInt32.bind(binaryWriter);
80
- }
81
- case 5126 /* AccessorComponentType.FLOAT */: {
82
- return binaryWriter.setFloat32.bind(binaryWriter);
83
- }
84
- default: {
85
- Tools.Warn("Unsupported Attribute Component kind: " + attributeComponentKind);
86
- return null;
87
- }
16
+ import { PBRMaterial } from "@babylonjs/core/Materials/PBR/pbrMaterial.js";
17
+ import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial.js";
18
+ import { Logger } from "@babylonjs/core/Misc/logger.js";
19
+ import { EnumerateFloatValues } from "@babylonjs/core/Buffers/bufferUtils.js";
20
+ import { _GLTFAnimation } from "./glTFAnimation.js";
21
+ import { BuildMorphTargetBuffers } from "./glTFMorphTargetsUtilities.js";
22
+ import { LinesMesh } from "@babylonjs/core/Meshes/linesMesh.js";
23
+ import { Color3, Color4 } from "@babylonjs/core/Maths/math.color.js";
24
+ class ExporterState {
25
+ constructor(convertToRightHanded, wasAddedByNoopNode) {
26
+ // Babylon indices array, start, count, offset, flip -> glTF accessor index
27
+ this._indicesAccessorMap = new Map();
28
+ // Babylon buffer -> glTF buffer view index
29
+ this._vertexBufferViewMap = new Map();
30
+ // Babylon vertex buffer, start, count -> glTF accessor index
31
+ this._vertexAccessorMap = new Map();
32
+ this._remappedBufferView = new Map();
33
+ this._meshMorphTargetMap = new Map();
34
+ this._vertexMapColorAlpha = new Map();
35
+ this._exportedNodes = new Set();
36
+ // Babylon mesh -> glTF mesh index
37
+ this._meshMap = new Map();
38
+ // Only used when convertToRightHanded is true.
39
+ this.convertedToRightHandedBuffers = new Map();
40
+ this.convertToRightHanded = convertToRightHanded;
41
+ this.wasAddedByNoopNode = wasAddedByNoopNode;
42
+ }
43
+ getIndicesAccessor(indices, start, count, offset, flip) {
44
+ return this._indicesAccessorMap.get(indices)?.get(start)?.get(count)?.get(offset)?.get(flip);
45
+ }
46
+ setIndicesAccessor(indices, start, count, offset, flip, accessorIndex) {
47
+ let map1 = this._indicesAccessorMap.get(indices);
48
+ if (!map1) {
49
+ map1 = new Map();
50
+ this._indicesAccessorMap.set(indices, map1);
51
+ }
52
+ let map2 = map1.get(start);
53
+ if (!map2) {
54
+ map2 = new Map();
55
+ map1.set(start, map2);
56
+ }
57
+ let map3 = map2.get(count);
58
+ if (!map3) {
59
+ map3 = new Map();
60
+ map2.set(count, map3);
61
+ }
62
+ let map4 = map3.get(offset);
63
+ if (!map4) {
64
+ map4 = new Map();
65
+ map3.set(offset, map4);
66
+ }
67
+ map4.set(flip, accessorIndex);
68
+ }
69
+ pushExportedNode(node) {
70
+ if (!this._exportedNodes.has(node)) {
71
+ this._exportedNodes.add(node);
72
+ }
73
+ }
74
+ getNodesSet() {
75
+ return this._exportedNodes;
76
+ }
77
+ getVertexBufferView(buffer) {
78
+ return this._vertexBufferViewMap.get(buffer);
79
+ }
80
+ setVertexBufferView(buffer, bufferViewIndex) {
81
+ this._vertexBufferViewMap.set(buffer, bufferViewIndex);
82
+ }
83
+ setRemappedBufferView(buffer, vertexBuffer, bufferViewIndex) {
84
+ this._remappedBufferView.set(buffer, new Map());
85
+ this._remappedBufferView.get(buffer).set(vertexBuffer, bufferViewIndex);
86
+ }
87
+ getRemappedBufferView(buffer, vertexBuffer) {
88
+ return this._remappedBufferView.get(buffer)?.get(vertexBuffer);
89
+ }
90
+ getVertexAccessor(vertexBuffer, start, count) {
91
+ return this._vertexAccessorMap.get(vertexBuffer)?.get(start)?.get(count);
92
+ }
93
+ setVertexAccessor(vertexBuffer, start, count, accessorIndex) {
94
+ let map1 = this._vertexAccessorMap.get(vertexBuffer);
95
+ if (!map1) {
96
+ map1 = new Map();
97
+ this._vertexAccessorMap.set(vertexBuffer, map1);
98
+ }
99
+ let map2 = map1.get(start);
100
+ if (!map2) {
101
+ map2 = new Map();
102
+ map1.set(start, map2);
103
+ }
104
+ map2.set(count, accessorIndex);
105
+ }
106
+ hasVertexColorAlpha(vertexBuffer) {
107
+ return this._vertexMapColorAlpha.get(vertexBuffer) || false;
108
+ }
109
+ setHasVertexColorAlpha(vertexBuffer, hasAlpha) {
110
+ return this._vertexMapColorAlpha.set(vertexBuffer, hasAlpha);
111
+ }
112
+ getMesh(mesh) {
113
+ return this._meshMap.get(mesh);
114
+ }
115
+ setMesh(mesh, meshIndex) {
116
+ this._meshMap.set(mesh, meshIndex);
117
+ }
118
+ bindMorphDataToMesh(mesh, morphData) {
119
+ const morphTargets = this._meshMorphTargetMap.get(mesh) || [];
120
+ this._meshMorphTargetMap.set(mesh, morphTargets);
121
+ if (morphTargets.indexOf(morphData) === -1) {
122
+ morphTargets.push(morphData);
123
+ }
124
+ }
125
+ getMorphTargetsFromMesh(mesh) {
126
+ return this._meshMorphTargetMap.get(mesh);
88
127
  }
89
128
  }
90
- /**
91
- * Converts Babylon Scene into glTF 2.0.
92
- * @internal
93
- */
94
- export class _Exporter {
129
+ /** @internal */
130
+ export class GLTFExporter {
95
131
  _applyExtension(node, extensions, index, actionAsync) {
96
132
  if (index >= extensions.length) {
97
133
  return Promise.resolve(node);
@@ -100,11 +136,11 @@ export class _Exporter {
100
136
  if (!currentPromise) {
101
137
  return this._applyExtension(node, extensions, index + 1, actionAsync);
102
138
  }
103
- return currentPromise.then((newNode) => this._applyExtension(newNode, extensions, index + 1, actionAsync));
139
+ return currentPromise.then((newNode) => (newNode ? this._applyExtension(newNode, extensions, index + 1, actionAsync) : null));
104
140
  }
105
141
  _applyExtensions(node, actionAsync) {
106
142
  const extensions = [];
107
- for (const name of _Exporter._ExtensionNames) {
143
+ for (const name of GLTFExporter._ExtensionNames) {
108
144
  extensions.push(this._extensions[name]);
109
145
  }
110
146
  return this._applyExtension(node, extensions, 0, actionAsync);
@@ -112,18 +148,18 @@ export class _Exporter {
112
148
  _extensionsPreExportTextureAsync(context, babylonTexture, mimeType) {
113
149
  return this._applyExtensions(babylonTexture, (extension, node) => extension.preExportTextureAsync && extension.preExportTextureAsync(context, node, mimeType));
114
150
  }
115
- _extensionsPostExportMeshPrimitiveAsync(context, meshPrimitive, babylonSubMesh, binaryWriter) {
116
- return this._applyExtensions(meshPrimitive, (extension, node) => extension.postExportMeshPrimitiveAsync && extension.postExportMeshPrimitiveAsync(context, node, babylonSubMesh, binaryWriter));
151
+ _extensionsPostExportMeshPrimitiveAsync(context, meshPrimitive, babylonSubMesh) {
152
+ return this._applyExtensions(meshPrimitive, (extension, node) => extension.postExportMeshPrimitiveAsync && extension.postExportMeshPrimitiveAsync(context, node, babylonSubMesh));
117
153
  }
118
- _extensionsPostExportNodeAsync(context, node, babylonNode, nodeMap, binaryWriter) {
119
- return this._applyExtensions(node, (extension, node) => extension.postExportNodeAsync && extension.postExportNodeAsync(context, node, babylonNode, nodeMap, binaryWriter));
154
+ _extensionsPostExportNodeAsync(context, node, babylonNode, nodeMap, convertToRightHanded) {
155
+ return this._applyExtensions(node, (extension, node) => extension.postExportNodeAsync && extension.postExportNodeAsync(context, node, babylonNode, nodeMap, convertToRightHanded, this._dataWriter));
120
156
  }
121
157
  _extensionsPostExportMaterialAsync(context, material, babylonMaterial) {
122
158
  return this._applyExtensions(material, (extension, node) => extension.postExportMaterialAsync && extension.postExportMaterialAsync(context, node, babylonMaterial));
123
159
  }
124
160
  _extensionsPostExportMaterialAdditionalTextures(context, material, babylonMaterial) {
125
161
  const output = [];
126
- for (const name of _Exporter._ExtensionNames) {
162
+ for (const name of GLTFExporter._ExtensionNames) {
127
163
  const extension = this._extensions[name];
128
164
  if (extension.postExportMaterialAdditionalTextures) {
129
165
  output.push(...extension.postExportMaterialAdditionalTextures(context, material, babylonMaterial));
@@ -132,7 +168,7 @@ export class _Exporter {
132
168
  return output;
133
169
  }
134
170
  _extensionsPostExportTextures(context, textureInfo, babylonTexture) {
135
- for (const name of _Exporter._ExtensionNames) {
171
+ for (const name of GLTFExporter._ExtensionNames) {
136
172
  const extension = this._extensions[name];
137
173
  if (extension.postExportTexture) {
138
174
  extension.postExportTexture(context, textureInfo, babylonTexture);
@@ -140,7 +176,7 @@ export class _Exporter {
140
176
  }
141
177
  }
142
178
  _forEachExtensions(action) {
143
- for (const name of _Exporter._ExtensionNames) {
179
+ for (const name of GLTFExporter._ExtensionNames) {
144
180
  const extension = this._extensions[name];
145
181
  if (extension.enabled) {
146
182
  action(extension);
@@ -149,583 +185,113 @@ export class _Exporter {
149
185
  }
150
186
  _extensionsOnExporting() {
151
187
  this._forEachExtensions((extension) => {
188
+ var _a, _b, _c;
152
189
  if (extension.wasUsed) {
153
- if (this._glTF.extensionsUsed == null) {
154
- this._glTF.extensionsUsed = [];
155
- }
190
+ (_a = this._glTF).extensionsUsed || (_a.extensionsUsed = []);
156
191
  if (this._glTF.extensionsUsed.indexOf(extension.name) === -1) {
157
192
  this._glTF.extensionsUsed.push(extension.name);
158
193
  }
159
194
  if (extension.required) {
160
- if (this._glTF.extensionsRequired == null) {
161
- this._glTF.extensionsRequired = [];
162
- }
195
+ (_b = this._glTF).extensionsRequired || (_b.extensionsRequired = []);
163
196
  if (this._glTF.extensionsRequired.indexOf(extension.name) === -1) {
164
197
  this._glTF.extensionsRequired.push(extension.name);
165
198
  }
166
199
  }
167
- if (this._glTF.extensions == null) {
168
- this._glTF.extensions = {};
169
- }
200
+ (_c = this._glTF).extensions || (_c.extensions = {});
170
201
  if (extension.onExporting) {
171
202
  extension.onExporting();
172
203
  }
173
204
  }
174
205
  });
175
206
  }
176
- /**
177
- * Load glTF serializer extensions
178
- */
179
207
  _loadExtensions() {
180
- for (const name of _Exporter._ExtensionNames) {
181
- const extension = _Exporter._ExtensionFactories[name](this);
208
+ for (const name of GLTFExporter._ExtensionNames) {
209
+ const extension = GLTFExporter._ExtensionFactories[name](this);
182
210
  this._extensions[name] = extension;
183
211
  }
184
212
  }
185
- /**
186
- * Creates a glTF Exporter instance, which can accept optional exporter options
187
- * @param babylonScene Babylon scene object
188
- * @param options Options to modify the behavior of the exporter
189
- */
190
- constructor(babylonScene, options) {
191
- this._extensions = {};
213
+ constructor(babylonScene = EngineStore.LastCreatedScene, options) {
192
214
  this._glTF = {
193
215
  asset: { generator: `Babylon.js v${Engine.Version}`, version: "2.0" },
194
216
  };
195
- babylonScene = babylonScene || EngineStore.LastCreatedScene;
196
- if (!babylonScene) {
197
- return;
198
- }
199
- this._babylonScene = babylonScene;
200
- this._bufferViews = [];
217
+ this._animations = [];
201
218
  this._accessors = [];
202
- this._meshes = [];
203
- this._scenes = [];
219
+ this._bufferViews = [];
204
220
  this._cameras = [];
205
- this._nodes = [];
206
221
  this._images = [];
207
222
  this._materials = [];
208
- this._materialMap = [];
209
- this._textures = [];
223
+ this._meshes = [];
224
+ this._nodes = [];
210
225
  this._samplers = [];
226
+ this._scenes = [];
211
227
  this._skins = [];
212
- this._animations = [];
228
+ this._textures = [];
213
229
  this._imageData = {};
214
230
  this._orderedImageData = [];
215
- this._options = options || {};
216
- this._animationSampleRate = this._options.animationSampleRate || 1 / 60;
217
- this._glTFMaterialExporter = new _GLTFMaterialExporter(this);
231
+ this._materialExporter = new GLTFMaterialExporter(this);
232
+ this._extensions = {};
233
+ this._dataWriter = new DataWriter(4);
234
+ this._shouldExportNodeMap = new Map();
235
+ // Babylon node -> glTF node index
236
+ this._nodeMap = new Map();
237
+ // Babylon material -> glTF material index
238
+ this._materialMap = new Map();
239
+ this._camerasMap = new Map();
240
+ this._nodesCameraMap = new Map();
241
+ this._skinMap = new Map();
242
+ this._nodesSkinMap = new Map();
243
+ // A material in this set requires UVs
244
+ this._materialNeedsUVsSet = new Set();
245
+ if (!babylonScene) {
246
+ throw new Error("No scene available to export");
247
+ }
248
+ this._babylonScene = babylonScene;
249
+ this._options = {
250
+ shouldExportNode: () => true,
251
+ shouldExportAnimation: () => true,
252
+ metadataSelector: (metadata) => metadata,
253
+ animationSampleRate: 1 / 60,
254
+ exportWithoutWaitingForScene: false,
255
+ exportUnusedUVs: false,
256
+ removeNoopRootNodes: true,
257
+ includeCoordinateSystemConversionNodes: false,
258
+ ...options,
259
+ };
218
260
  this._loadExtensions();
219
261
  }
220
262
  dispose() {
221
- for (const extensionKey in this._extensions) {
222
- const extension = this._extensions[extensionKey];
263
+ for (const key in this._extensions) {
264
+ const extension = this._extensions[key];
223
265
  extension.dispose();
224
266
  }
225
267
  }
226
268
  get options() {
227
269
  return this._options;
228
270
  }
229
- /**
230
- * Registers a glTF exporter extension
231
- * @param name Name of the extension to export
232
- * @param factory The factory function that creates the exporter extension
233
- */
234
271
  static RegisterExtension(name, factory) {
235
- if (_Exporter.UnregisterExtension(name)) {
272
+ if (GLTFExporter.UnregisterExtension(name)) {
236
273
  Tools.Warn(`Extension with the name ${name} already exists`);
237
274
  }
238
- _Exporter._ExtensionFactories[name] = factory;
239
- _Exporter._ExtensionNames.push(name);
275
+ GLTFExporter._ExtensionFactories[name] = factory;
276
+ GLTFExporter._ExtensionNames.push(name);
240
277
  }
241
- /**
242
- * Un-registers an exporter extension
243
- * @param name The name fo the exporter extension
244
- * @returns A boolean indicating whether the extension has been un-registered
245
- */
246
278
  static UnregisterExtension(name) {
247
- if (!_Exporter._ExtensionFactories[name]) {
279
+ if (!GLTFExporter._ExtensionFactories[name]) {
248
280
  return false;
249
281
  }
250
- delete _Exporter._ExtensionFactories[name];
251
- const index = _Exporter._ExtensionNames.indexOf(name);
282
+ delete GLTFExporter._ExtensionFactories[name];
283
+ const index = GLTFExporter._ExtensionNames.indexOf(name);
252
284
  if (index !== -1) {
253
- _Exporter._ExtensionNames.splice(index, 1);
285
+ GLTFExporter._ExtensionNames.splice(index, 1);
254
286
  }
255
287
  return true;
256
288
  }
257
- _reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter) {
258
- switch (primitiveMode) {
259
- case Material.TriangleFillMode: {
260
- if (!byteOffset) {
261
- byteOffset = 0;
262
- }
263
- for (let i = submesh.indexStart, length = submesh.indexStart + submesh.indexCount; i < length; i = i + 3) {
264
- const index = byteOffset + i * 4;
265
- // swap the second and third indices
266
- const secondIndex = binaryWriter.getUInt32(index + 4);
267
- const thirdIndex = binaryWriter.getUInt32(index + 8);
268
- binaryWriter.setUInt32(thirdIndex, index + 4);
269
- binaryWriter.setUInt32(secondIndex, index + 8);
270
- }
271
- break;
272
- }
273
- case Material.TriangleFanDrawMode: {
274
- for (let i = submesh.indexStart + submesh.indexCount - 1, start = submesh.indexStart; i >= start; --i) {
275
- binaryWriter.setUInt32(babylonIndices[i], byteOffset);
276
- byteOffset += 4;
277
- }
278
- break;
279
- }
280
- case Material.TriangleStripDrawMode: {
281
- if (submesh.indexCount >= 3) {
282
- binaryWriter.setUInt32(babylonIndices[submesh.indexStart + 2], byteOffset + 4);
283
- binaryWriter.setUInt32(babylonIndices[submesh.indexStart + 1], byteOffset + 8);
284
- }
285
- break;
286
- }
287
- }
288
- }
289
- /**
290
- * Reorders the vertex attribute data based on the primitive mode. This is necessary when indices are not available and the winding order is
291
- * clock-wise during export to glTF
292
- * @param submesh BabylonJS submesh
293
- * @param primitiveMode Primitive mode of the mesh
294
- * @param vertexBufferKind The type of vertex attribute
295
- * @param meshAttributeArray The vertex attribute data
296
- * @param byteOffset The offset to the binary data
297
- * @param binaryWriter The binary data for the glTF file
298
- */
299
- _reorderVertexAttributeDataBasedOnPrimitiveMode(submesh, primitiveMode, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
300
- switch (primitiveMode) {
301
- case Material.TriangleFillMode: {
302
- this._reorderTriangleFillMode(submesh, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
303
- break;
304
- }
305
- case Material.TriangleStripDrawMode: {
306
- this._reorderTriangleStripDrawMode(submesh, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
307
- break;
308
- }
309
- case Material.TriangleFanDrawMode: {
310
- this._reorderTriangleFanMode(submesh, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter);
311
- break;
312
- }
313
- }
314
- }
315
- /**
316
- * Reorders the vertex attributes in the correct triangle mode order . This is necessary when indices are not available and the winding order is
317
- * clock-wise during export to glTF
318
- * @param submesh BabylonJS submesh
319
- * @param vertexBufferKind The type of vertex attribute
320
- * @param meshAttributeArray The vertex attribute data
321
- * @param byteOffset The offset to the binary data
322
- * @param binaryWriter The binary data for the glTF file
323
- */
324
- _reorderTriangleFillMode(submesh, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
325
- const vertexBuffer = this._getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh());
326
- if (vertexBuffer) {
327
- const stride = vertexBuffer.byteStride / VertexBuffer.GetTypeByteLength(vertexBuffer.type);
328
- if (submesh.verticesCount % 3 !== 0) {
329
- Tools.Error("The submesh vertices for the triangle fill mode is not divisible by 3!");
330
- }
331
- else {
332
- const vertexData = [];
333
- let index = 0;
334
- switch (vertexBufferKind) {
335
- case VertexBuffer.PositionKind:
336
- case VertexBuffer.NormalKind: {
337
- for (let x = submesh.verticesStart; x < submesh.verticesStart + submesh.verticesCount; x = x + 3) {
338
- index = x * stride;
339
- vertexData.push(Vector3.FromArray(meshAttributeArray, index));
340
- vertexData.push(Vector3.FromArray(meshAttributeArray, index + 2 * stride));
341
- vertexData.push(Vector3.FromArray(meshAttributeArray, index + stride));
342
- }
343
- break;
344
- }
345
- case VertexBuffer.TangentKind: {
346
- for (let x = submesh.verticesStart; x < submesh.verticesStart + submesh.verticesCount; x = x + 3) {
347
- index = x * stride;
348
- vertexData.push(Vector4.FromArray(meshAttributeArray, index));
349
- vertexData.push(Vector4.FromArray(meshAttributeArray, index + 2 * stride));
350
- vertexData.push(Vector4.FromArray(meshAttributeArray, index + stride));
351
- }
352
- break;
353
- }
354
- case VertexBuffer.ColorKind: {
355
- const size = vertexBuffer.getSize();
356
- for (let x = submesh.verticesStart; x < submesh.verticesStart + submesh.verticesCount; x = x + size) {
357
- index = x * stride;
358
- if (size === 4) {
359
- vertexData.push(Vector4.FromArray(meshAttributeArray, index));
360
- vertexData.push(Vector4.FromArray(meshAttributeArray, index + 2 * stride));
361
- vertexData.push(Vector4.FromArray(meshAttributeArray, index + stride));
362
- }
363
- else {
364
- vertexData.push(Vector3.FromArray(meshAttributeArray, index));
365
- vertexData.push(Vector3.FromArray(meshAttributeArray, index + 2 * stride));
366
- vertexData.push(Vector3.FromArray(meshAttributeArray, index + stride));
367
- }
368
- }
369
- break;
370
- }
371
- case VertexBuffer.UVKind:
372
- case VertexBuffer.UV2Kind: {
373
- for (let x = submesh.verticesStart; x < submesh.verticesStart + submesh.verticesCount; x = x + 3) {
374
- index = x * stride;
375
- vertexData.push(Vector2.FromArray(meshAttributeArray, index));
376
- vertexData.push(Vector2.FromArray(meshAttributeArray, index + 2 * stride));
377
- vertexData.push(Vector2.FromArray(meshAttributeArray, index + stride));
378
- }
379
- break;
380
- }
381
- default: {
382
- Tools.Error(`Unsupported Vertex Buffer type: ${vertexBufferKind}`);
383
- }
384
- }
385
- this._writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, binaryWriter);
386
- }
387
- }
388
- else {
389
- Tools.Warn(`reorderTriangleFillMode: Vertex Buffer Kind ${vertexBufferKind} not present!`);
390
- }
391
- }
392
- /**
393
- * Reorders the vertex attributes in the correct triangle strip order. This is necessary when indices are not available and the winding order is
394
- * clock-wise during export to glTF
395
- * @param submesh BabylonJS submesh
396
- * @param vertexBufferKind The type of vertex attribute
397
- * @param meshAttributeArray The vertex attribute data
398
- * @param byteOffset The offset to the binary data
399
- * @param binaryWriter The binary data for the glTF file
400
- */
401
- _reorderTriangleStripDrawMode(submesh, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
402
- const vertexBuffer = this._getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh());
403
- if (vertexBuffer) {
404
- const stride = vertexBuffer.byteStride / VertexBuffer.GetTypeByteLength(vertexBuffer.type);
405
- const vertexData = [];
406
- let index = 0;
407
- switch (vertexBufferKind) {
408
- case VertexBuffer.PositionKind:
409
- case VertexBuffer.NormalKind: {
410
- index = submesh.verticesStart;
411
- vertexData.push(Vector3.FromArray(meshAttributeArray, index + 2 * stride));
412
- vertexData.push(Vector3.FromArray(meshAttributeArray, index + stride));
413
- break;
414
- }
415
- case VertexBuffer.TangentKind: {
416
- for (let x = submesh.verticesStart + submesh.verticesCount - 1; x >= submesh.verticesStart; --x) {
417
- index = x * stride;
418
- vertexData.push(Vector4.FromArray(meshAttributeArray, index));
419
- }
420
- break;
421
- }
422
- case VertexBuffer.ColorKind: {
423
- for (let x = submesh.verticesStart + submesh.verticesCount - 1; x >= submesh.verticesStart; --x) {
424
- index = x * stride;
425
- vertexBuffer.getSize() === 4
426
- ? vertexData.push(Vector4.FromArray(meshAttributeArray, index))
427
- : vertexData.push(Vector3.FromArray(meshAttributeArray, index));
428
- }
429
- break;
430
- }
431
- case VertexBuffer.UVKind:
432
- case VertexBuffer.UV2Kind: {
433
- for (let x = submesh.verticesStart + submesh.verticesCount - 1; x >= submesh.verticesStart; --x) {
434
- index = x * stride;
435
- vertexData.push(Vector2.FromArray(meshAttributeArray, index));
436
- }
437
- break;
438
- }
439
- default: {
440
- Tools.Error(`Unsupported Vertex Buffer type: ${vertexBufferKind}`);
441
- }
442
- }
443
- this._writeVertexAttributeData(vertexData, byteOffset + 12, vertexBufferKind, binaryWriter);
444
- }
445
- else {
446
- Tools.Warn(`reorderTriangleStripDrawMode: Vertex buffer kind ${vertexBufferKind} not present!`);
447
- }
448
- }
449
- /**
450
- * Reorders the vertex attributes in the correct triangle fan order. This is necessary when indices are not available and the winding order is
451
- * clock-wise during export to glTF
452
- * @param submesh BabylonJS submesh
453
- * @param vertexBufferKind The type of vertex attribute
454
- * @param meshAttributeArray The vertex attribute data
455
- * @param byteOffset The offset to the binary data
456
- * @param binaryWriter The binary data for the glTF file
457
- */
458
- _reorderTriangleFanMode(submesh, vertexBufferKind, meshAttributeArray, byteOffset, binaryWriter) {
459
- const vertexBuffer = this._getVertexBufferFromMesh(vertexBufferKind, submesh.getMesh());
460
- if (vertexBuffer) {
461
- const stride = vertexBuffer.byteStride / VertexBuffer.GetTypeByteLength(vertexBuffer.type);
462
- const vertexData = [];
463
- let index = 0;
464
- switch (vertexBufferKind) {
465
- case VertexBuffer.PositionKind:
466
- case VertexBuffer.NormalKind: {
467
- for (let x = submesh.verticesStart + submesh.verticesCount - 1; x >= submesh.verticesStart; --x) {
468
- index = x * stride;
469
- vertexData.push(Vector3.FromArray(meshAttributeArray, index));
470
- }
471
- break;
472
- }
473
- case VertexBuffer.TangentKind: {
474
- for (let x = submesh.verticesStart + submesh.verticesCount - 1; x >= submesh.verticesStart; --x) {
475
- index = x * stride;
476
- vertexData.push(Vector4.FromArray(meshAttributeArray, index));
477
- }
478
- break;
479
- }
480
- case VertexBuffer.ColorKind: {
481
- for (let x = submesh.verticesStart + submesh.verticesCount - 1; x >= submesh.verticesStart; --x) {
482
- index = x * stride;
483
- vertexData.push(Vector4.FromArray(meshAttributeArray, index));
484
- vertexBuffer.getSize() === 4
485
- ? vertexData.push(Vector4.FromArray(meshAttributeArray, index))
486
- : vertexData.push(Vector3.FromArray(meshAttributeArray, index));
487
- }
488
- break;
489
- }
490
- case VertexBuffer.UVKind:
491
- case VertexBuffer.UV2Kind: {
492
- for (let x = submesh.verticesStart + submesh.verticesCount - 1; x >= submesh.verticesStart; --x) {
493
- index = x * stride;
494
- vertexData.push(Vector2.FromArray(meshAttributeArray, index));
495
- }
496
- break;
497
- }
498
- default: {
499
- Tools.Error(`Unsupported Vertex Buffer type: ${vertexBufferKind}`);
500
- }
501
- }
502
- this._writeVertexAttributeData(vertexData, byteOffset, vertexBufferKind, binaryWriter);
503
- }
504
- else {
505
- Tools.Warn(`reorderTriangleFanMode: Vertex buffer kind ${vertexBufferKind} not present!`);
506
- }
507
- }
508
- /**
509
- * Writes the vertex attribute data to binary
510
- * @param vertices The vertices to write to the binary writer
511
- * @param byteOffset The offset into the binary writer to overwrite binary data
512
- * @param vertexAttributeKind The vertex attribute type
513
- * @param binaryWriter The writer containing the binary data
514
- */
515
- _writeVertexAttributeData(vertices, byteOffset, vertexAttributeKind, binaryWriter) {
516
- for (const vertex of vertices) {
517
- if (vertexAttributeKind === VertexBuffer.NormalKind) {
518
- vertex.normalize();
519
- }
520
- else if (vertexAttributeKind === VertexBuffer.TangentKind && vertex instanceof Vector4) {
521
- _GLTFUtilities._NormalizeTangentFromRef(vertex);
522
- }
523
- for (const component of vertex.asArray()) {
524
- binaryWriter.setFloat32(component, byteOffset);
525
- byteOffset += 4;
526
- }
527
- }
528
- }
529
- /**
530
- * Writes mesh attribute data to a data buffer
531
- * Returns the bytelength of the data
532
- * @param vertexBufferKind Indicates what kind of vertex data is being passed in
533
- * @param attributeComponentKind
534
- * @param meshAttributeArray Array containing the attribute data
535
- * @param stride Specifies the space between data
536
- * @param binaryWriter The buffer to write the binary data to
537
- * @param babylonTransformNode
538
- */
539
- _writeAttributeData(vertexBufferKind, attributeComponentKind, meshAttributeArray, stride, binaryWriter, babylonTransformNode) {
540
- let vertexAttributes = [];
541
- let index;
542
- switch (vertexBufferKind) {
543
- case VertexBuffer.PositionKind: {
544
- for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
545
- index = k * stride;
546
- const vertexData = Vector3.FromArray(meshAttributeArray, index);
547
- vertexAttributes.push(vertexData.asArray());
548
- }
549
- break;
550
- }
551
- case VertexBuffer.NormalKind: {
552
- for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
553
- index = k * stride;
554
- const vertexData = Vector3.FromArray(meshAttributeArray, index);
555
- vertexAttributes.push(vertexData.normalize().asArray());
556
- }
557
- break;
558
- }
559
- case VertexBuffer.TangentKind: {
560
- for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
561
- index = k * stride;
562
- const vertexData = Vector4.FromArray(meshAttributeArray, index);
563
- _GLTFUtilities._NormalizeTangentFromRef(vertexData);
564
- vertexAttributes.push(vertexData.asArray());
565
- }
566
- break;
567
- }
568
- case VertexBuffer.ColorKind: {
569
- const meshMaterial = babylonTransformNode.material;
570
- const convertToLinear = meshMaterial ? meshMaterial.getClassName() === "StandardMaterial" : true;
571
- const vertexData = stride === 3 ? new Color3() : new Color4();
572
- const useExactSrgbConversions = this._babylonScene.getEngine().useExactSrgbConversions;
573
- for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
574
- index = k * stride;
575
- if (stride === 3) {
576
- Color3.FromArrayToRef(meshAttributeArray, index, vertexData);
577
- if (convertToLinear) {
578
- vertexData.toLinearSpaceToRef(vertexData, useExactSrgbConversions);
579
- }
580
- }
581
- else {
582
- Color4.FromArrayToRef(meshAttributeArray, index, vertexData);
583
- if (convertToLinear) {
584
- vertexData.toLinearSpaceToRef(vertexData, useExactSrgbConversions);
585
- }
586
- }
587
- vertexAttributes.push(vertexData.asArray());
588
- }
589
- break;
590
- }
591
- case VertexBuffer.UVKind:
592
- case VertexBuffer.UV2Kind: {
593
- for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
594
- index = k * stride;
595
- const vertexData = Vector2.FromArray(meshAttributeArray, index);
596
- vertexAttributes.push(vertexData.asArray());
597
- }
598
- break;
599
- }
600
- case VertexBuffer.MatricesIndicesKind:
601
- case VertexBuffer.MatricesIndicesExtraKind: {
602
- for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
603
- index = k * stride;
604
- const vertexData = Vector4.FromArray(meshAttributeArray, index);
605
- vertexAttributes.push(vertexData.asArray());
606
- }
607
- break;
608
- }
609
- case VertexBuffer.MatricesWeightsKind:
610
- case VertexBuffer.MatricesWeightsExtraKind: {
611
- for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
612
- index = k * stride;
613
- const vertexData = Vector4.FromArray(meshAttributeArray, index);
614
- vertexAttributes.push(vertexData.asArray());
615
- }
616
- break;
617
- }
618
- default: {
619
- Tools.Warn("Unsupported Vertex Buffer Type: " + vertexBufferKind);
620
- vertexAttributes = [];
621
- }
622
- }
623
- const writeBinaryFunc = getBinaryWriterFunc(binaryWriter, attributeComponentKind);
624
- if (writeBinaryFunc) {
625
- for (const vertexAttribute of vertexAttributes) {
626
- for (const component of vertexAttribute) {
627
- writeBinaryFunc(component);
628
- }
629
- }
630
- }
631
- }
632
- _createMorphTargetBufferViewKind(vertexBufferKind, accessorType, attributeComponentKind, mesh, morphTarget, binaryWriter, byteStride) {
633
- let vertexCount;
634
- let minMax;
635
- const morphData = [];
636
- const difference = TmpVectors.Vector3[0];
637
- switch (vertexBufferKind) {
638
- case VertexBuffer.PositionKind: {
639
- const morphPositions = morphTarget.getPositions();
640
- if (!morphPositions) {
641
- return null;
642
- }
643
- const originalPositions = mesh.getVerticesData(VertexBuffer.PositionKind, undefined, undefined, true);
644
- const vertexStart = 0;
645
- const min = new Vector3(Infinity, Infinity, Infinity);
646
- const max = new Vector3(-Infinity, -Infinity, -Infinity);
647
- vertexCount = originalPositions.length / 3;
648
- for (let i = vertexStart; i < vertexCount; ++i) {
649
- const originalPosition = Vector3.FromArray(originalPositions, i * 3);
650
- const morphPosition = Vector3.FromArray(morphPositions, i * 3);
651
- morphPosition.subtractToRef(originalPosition, difference);
652
- min.copyFromFloats(Math.min(difference.x, min.x), Math.min(difference.y, min.y), Math.min(difference.z, min.z));
653
- max.copyFromFloats(Math.max(difference.x, max.x), Math.max(difference.y, max.y), Math.max(difference.z, max.z));
654
- morphData.push(difference.x, difference.y, difference.z);
655
- }
656
- minMax = { min, max };
657
- break;
658
- }
659
- case VertexBuffer.NormalKind: {
660
- const morphNormals = morphTarget.getNormals();
661
- if (!morphNormals) {
662
- return null;
663
- }
664
- const originalNormals = mesh.getVerticesData(VertexBuffer.NormalKind, undefined, undefined, true);
665
- const vertexStart = 0;
666
- vertexCount = originalNormals.length / 3;
667
- for (let i = vertexStart; i < vertexCount; ++i) {
668
- const originalNormal = Vector3.FromArray(originalNormals, i * 3).normalize();
669
- const morphNormal = Vector3.FromArray(morphNormals, i * 3).normalize();
670
- morphNormal.subtractToRef(originalNormal, difference);
671
- morphData.push(difference.x, difference.y, difference.z);
672
- }
673
- break;
674
- }
675
- case VertexBuffer.TangentKind: {
676
- const morphTangents = morphTarget.getTangents();
677
- if (!morphTangents) {
678
- return null;
679
- }
680
- // Handedness cannot be displaced, so morph target tangents omit the w component
681
- accessorType = "VEC3" /* AccessorType.VEC3 */;
682
- byteStride = 12; // 3 components (x/y/z) * 4 bytes (float32)
683
- const originalTangents = mesh.getVerticesData(VertexBuffer.TangentKind, undefined, undefined, true);
684
- const vertexStart = 0;
685
- vertexCount = originalTangents.length / 4;
686
- for (let i = vertexStart; i < vertexCount; ++i) {
687
- // Only read the x, y, z components and ignore w
688
- const originalTangent = Vector3.FromArray(originalTangents, i * 4);
689
- _GLTFUtilities._NormalizeTangentFromRef(originalTangent);
690
- // Morph target tangents omit the w component so it won't be present in the data
691
- const morphTangent = Vector3.FromArray(morphTangents, i * 3);
692
- _GLTFUtilities._NormalizeTangentFromRef(morphTangent);
693
- morphTangent.subtractToRef(originalTangent, difference);
694
- morphData.push(difference.x, difference.y, difference.z);
695
- }
696
- break;
697
- }
698
- default: {
699
- return null;
700
- }
701
- }
702
- const binaryWriterFunc = getBinaryWriterFunc(binaryWriter, attributeComponentKind);
703
- if (!binaryWriterFunc) {
704
- return null;
705
- }
706
- const typeByteLength = VertexBuffer.GetTypeByteLength(attributeComponentKind);
707
- const byteLength = morphData.length * typeByteLength;
708
- const bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, `${vertexBufferKind} - ${morphTarget.name} (Morph Target)`);
709
- this._bufferViews.push(bufferView);
710
- const bufferViewIndex = this._bufferViews.length - 1;
711
- for (const value of morphData) {
712
- binaryWriterFunc(value);
713
- }
714
- return { bufferViewIndex, vertexCount, accessorType, minMax };
715
- }
716
- /**
717
- * Generates glTF json data
718
- * @param shouldUseGlb Indicates whether the json should be written for a glb file
719
- * @param glTFPrefix Text to use when prefixing a glTF file
720
- * @param prettyPrint Indicates whether the json file should be pretty printed (true) or not (false)
721
- * @returns json data as string
722
- */
723
- _generateJSON(shouldUseGlb, glTFPrefix, prettyPrint) {
724
- const buffer = { byteLength: this._totalByteLength };
289
+ _generateJSON(shouldUseGlb, bufferByteLength, fileName, prettyPrint) {
290
+ const buffer = { byteLength: bufferByteLength };
725
291
  let imageName;
726
292
  let imageData;
727
293
  let bufferView;
728
- let byteOffset = this._totalByteLength;
294
+ let byteOffset = bufferByteLength;
729
295
  if (buffer.byteLength) {
730
296
  this._glTF.buffers = [buffer];
731
297
  }
@@ -773,17 +339,13 @@ export class _Exporter {
773
339
  if (image.uri) {
774
340
  imageData = this._imageData[image.uri];
775
341
  this._orderedImageData.push(imageData);
776
- imageName = image.uri.split(".")[0] + " image";
777
- bufferView = _GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.byteLength, undefined, imageName);
342
+ bufferView = CreateBufferView(0, byteOffset, imageData.data.byteLength, undefined);
778
343
  byteOffset += imageData.data.byteLength;
779
344
  this._bufferViews.push(bufferView);
780
345
  image.bufferView = this._bufferViews.length - 1;
781
346
  image.name = imageName;
782
347
  image.mimeType = imageData.mimeType;
783
348
  image.uri = undefined;
784
- if (!this._glTF.images) {
785
- this._glTF.images = [];
786
- }
787
349
  this._glTF.images.push(image);
788
350
  }
789
351
  });
@@ -792,50 +354,30 @@ export class _Exporter {
792
354
  }
793
355
  }
794
356
  if (!shouldUseGlb) {
795
- buffer.uri = glTFPrefix + ".bin";
357
+ buffer.uri = fileName + ".bin";
796
358
  }
797
- const jsonText = prettyPrint ? JSON.stringify(this._glTF, null, 2) : JSON.stringify(this._glTF);
798
- return jsonText;
359
+ return prettyPrint ? JSON.stringify(this._glTF, null, 2) : JSON.stringify(this._glTF);
799
360
  }
800
- /**
801
- * Generates data for .gltf and .bin files based on the glTF prefix string
802
- * @param glTFPrefix Text to use when prefixing a glTF file
803
- * @param dispose Dispose the exporter
804
- * @returns GLTFData with glTF file data
805
- */
806
- _generateGLTFAsync(glTFPrefix, dispose = true) {
807
- return this._generateBinaryAsync().then((binaryBuffer) => {
808
- this._extensionsOnExporting();
809
- const jsonText = this._generateJSON(false, glTFPrefix, true);
810
- const bin = new Blob([binaryBuffer], { type: "application/octet-stream" });
811
- const glTFFileName = glTFPrefix + ".gltf";
812
- const glTFBinFile = glTFPrefix + ".bin";
813
- const container = new GLTFData();
814
- container.glTFFiles[glTFFileName] = jsonText;
815
- container.glTFFiles[glTFBinFile] = bin;
816
- if (this._imageData) {
817
- for (const image in this._imageData) {
818
- container.glTFFiles[image] = new Blob([this._imageData[image].data], { type: this._imageData[image].mimeType });
819
- }
820
- }
821
- if (dispose) {
822
- this.dispose();
361
+ async generateGLTFAsync(glTFPrefix) {
362
+ const binaryBuffer = await this._generateBinaryAsync();
363
+ this._extensionsOnExporting();
364
+ const jsonText = this._generateJSON(false, binaryBuffer.byteLength, glTFPrefix, true);
365
+ const bin = new Blob([binaryBuffer], { type: "application/octet-stream" });
366
+ const glTFFileName = glTFPrefix + ".gltf";
367
+ const glTFBinFile = glTFPrefix + ".bin";
368
+ const container = new GLTFData();
369
+ container.files[glTFFileName] = jsonText;
370
+ container.files[glTFBinFile] = bin;
371
+ if (this._imageData) {
372
+ for (const image in this._imageData) {
373
+ container.files[image] = new Blob([this._imageData[image].data], { type: this._imageData[image].mimeType });
823
374
  }
824
- return container;
825
- });
375
+ }
376
+ return container;
826
377
  }
827
- /**
828
- * Creates a binary buffer for glTF
829
- * @returns array buffer for binary data
830
- */
831
- _generateBinaryAsync() {
832
- const binaryWriter = new _BinaryWriter(4);
833
- return this._createSceneAsync(binaryWriter).then(() => {
834
- if (this._localEngine) {
835
- this._localEngine.dispose();
836
- }
837
- return binaryWriter.getArrayBuffer();
838
- });
378
+ async _generateBinaryAsync() {
379
+ await this._exportSceneAsync();
380
+ return this._dataWriter.getOutputData();
839
381
  }
840
382
  /**
841
383
  * Pads the number to a multiple of 4
@@ -847,113 +389,102 @@ export class _Exporter {
847
389
  const padding = remainder === 0 ? remainder : 4 - remainder;
848
390
  return padding;
849
391
  }
850
- /**
851
- * @internal
852
- */
853
- _generateGLBAsync(glTFPrefix, dispose = true) {
854
- return this._generateBinaryAsync().then((binaryBuffer) => {
855
- this._extensionsOnExporting();
856
- const jsonText = this._generateJSON(true);
857
- const glbFileName = glTFPrefix + ".glb";
858
- const headerLength = 12;
859
- const chunkLengthPrefix = 8;
860
- let jsonLength = jsonText.length;
861
- let encodedJsonText;
862
- let imageByteLength = 0;
863
- // make use of TextEncoder when available
864
- if (typeof TextEncoder !== "undefined") {
865
- const encoder = new TextEncoder();
866
- encodedJsonText = encoder.encode(jsonText);
867
- jsonLength = encodedJsonText.length;
868
- }
869
- for (let i = 0; i < this._orderedImageData.length; ++i) {
870
- imageByteLength += this._orderedImageData[i].data.byteLength;
871
- }
872
- const jsonPadding = this._getPadding(jsonLength);
873
- const binPadding = this._getPadding(binaryBuffer.byteLength);
874
- const imagePadding = this._getPadding(imageByteLength);
875
- const byteLength = headerLength + 2 * chunkLengthPrefix + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding + imageByteLength + imagePadding;
876
- //header
877
- const headerBuffer = new ArrayBuffer(headerLength);
878
- const headerBufferView = new DataView(headerBuffer);
879
- headerBufferView.setUint32(0, 0x46546c67, true); //glTF
880
- headerBufferView.setUint32(4, 2, true); // version
881
- headerBufferView.setUint32(8, byteLength, true); // total bytes in file
882
- //json chunk
883
- const jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
884
- const jsonChunkBufferView = new DataView(jsonChunkBuffer);
885
- jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
886
- jsonChunkBufferView.setUint32(4, 0x4e4f534a, true);
887
- //json chunk bytes
888
- const jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
889
- // if TextEncoder was available, we can simply copy the encoded array
890
- if (encodedJsonText) {
891
- jsonData.set(encodedJsonText);
892
- }
893
- else {
894
- const blankCharCode = "_".charCodeAt(0);
895
- for (let i = 0; i < jsonLength; ++i) {
896
- const charCode = jsonText.charCodeAt(i);
897
- // if the character doesn't fit into a single UTF-16 code unit, just put a blank character
898
- if (charCode != jsonText.codePointAt(i)) {
899
- jsonData[i] = blankCharCode;
900
- }
901
- else {
902
- jsonData[i] = charCode;
903
- }
392
+ async generateGLBAsync(glTFPrefix) {
393
+ const binaryBuffer = await this._generateBinaryAsync();
394
+ this._extensionsOnExporting();
395
+ const jsonText = this._generateJSON(true, binaryBuffer.byteLength);
396
+ const glbFileName = glTFPrefix + ".glb";
397
+ const headerLength = 12;
398
+ const chunkLengthPrefix = 8;
399
+ let jsonLength = jsonText.length;
400
+ let encodedJsonText;
401
+ let imageByteLength = 0;
402
+ // make use of TextEncoder when available
403
+ if (typeof TextEncoder !== "undefined") {
404
+ const encoder = new TextEncoder();
405
+ encodedJsonText = encoder.encode(jsonText);
406
+ jsonLength = encodedJsonText.length;
407
+ }
408
+ for (let i = 0; i < this._orderedImageData.length; ++i) {
409
+ imageByteLength += this._orderedImageData[i].data.byteLength;
410
+ }
411
+ const jsonPadding = this._getPadding(jsonLength);
412
+ 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;
415
+ // header
416
+ const headerBuffer = new ArrayBuffer(headerLength);
417
+ const headerBufferView = new DataView(headerBuffer);
418
+ headerBufferView.setUint32(0, 0x46546c67, true); //glTF
419
+ headerBufferView.setUint32(4, 2, true); // version
420
+ headerBufferView.setUint32(8, byteLength, true); // total bytes in file
421
+ // json chunk
422
+ const jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
423
+ const jsonChunkBufferView = new DataView(jsonChunkBuffer);
424
+ jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
425
+ jsonChunkBufferView.setUint32(4, 0x4e4f534a, true);
426
+ // json chunk bytes
427
+ const jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
428
+ // if TextEncoder was available, we can simply copy the encoded array
429
+ if (encodedJsonText) {
430
+ jsonData.set(encodedJsonText);
431
+ }
432
+ else {
433
+ const blankCharCode = "_".charCodeAt(0);
434
+ for (let i = 0; i < jsonLength; ++i) {
435
+ const charCode = jsonText.charCodeAt(i);
436
+ // if the character doesn't fit into a single UTF-16 code unit, just put a blank character
437
+ if (charCode != jsonText.codePointAt(i)) {
438
+ jsonData[i] = blankCharCode;
439
+ }
440
+ else {
441
+ jsonData[i] = charCode;
904
442
  }
905
443
  }
906
- //json padding
907
- const jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
908
- for (let i = 0; i < jsonPadding; ++i) {
909
- jsonPaddingView[i] = 0x20;
910
- }
911
- //binary chunk
912
- const binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
913
- const binaryChunkBufferView = new DataView(binaryChunkBuffer);
914
- binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + imageByteLength + imagePadding, true);
915
- binaryChunkBufferView.setUint32(4, 0x004e4942, true);
916
- // binary padding
917
- const binPaddingBuffer = new ArrayBuffer(binPadding);
918
- const binPaddingView = new Uint8Array(binPaddingBuffer);
919
- for (let i = 0; i < binPadding; ++i) {
920
- binPaddingView[i] = 0;
921
- }
922
- const imagePaddingBuffer = new ArrayBuffer(imagePadding);
923
- const imagePaddingView = new Uint8Array(imagePaddingBuffer);
924
- for (let i = 0; i < imagePadding; ++i) {
925
- imagePaddingView[i] = 0;
926
- }
927
- const glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer];
928
- // binary data
929
- for (let i = 0; i < this._orderedImageData.length; ++i) {
930
- glbData.push(this._orderedImageData[i].data);
931
- }
932
- glbData.push(binPaddingBuffer);
933
- glbData.push(imagePaddingBuffer);
934
- const glbFile = new Blob(glbData, { type: "application/octet-stream" });
935
- const container = new GLTFData();
936
- container.glTFFiles[glbFileName] = glbFile;
937
- if (this._localEngine != null) {
938
- this._localEngine.dispose();
939
- }
940
- if (dispose) {
941
- this.dispose();
942
- }
943
- return container;
944
- });
945
- }
946
- /**
947
- * Sets the TRS for each node
948
- * @param node glTF Node for storing the transformation data
949
- * @param babylonTransformNode Babylon mesh used as the source for the transformation data
950
- */
951
- _setNodeTransformation(node, babylonTransformNode) {
444
+ }
445
+ // json padding
446
+ const jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
447
+ for (let i = 0; i < jsonPadding; ++i) {
448
+ jsonPaddingView[i] = 0x20;
449
+ }
450
+ // binary chunk
451
+ const binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
452
+ const binaryChunkBufferView = new DataView(binaryChunkBuffer);
453
+ binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + binPadding + imageByteLength + imagePadding, true);
454
+ binaryChunkBufferView.setUint32(4, 0x004e4942, true);
455
+ // binary padding
456
+ const binPaddingBuffer = new ArrayBuffer(binPadding);
457
+ const binPaddingView = new Uint8Array(binPaddingBuffer);
458
+ for (let i = 0; i < binPadding; ++i) {
459
+ binPaddingView[i] = 0;
460
+ }
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);
473
+ const glbFile = new Blob(glbData, { type: "application/octet-stream" });
474
+ const container = new GLTFData();
475
+ container.files[glbFileName] = glbFile;
476
+ return container;
477
+ }
478
+ _setNodeTransformation(node, babylonTransformNode, convertToRightHanded) {
952
479
  if (!babylonTransformNode.getPivotPoint().equalsToFloats(0, 0, 0)) {
953
480
  Tools.Warn("Pivot points are not supported in the glTF serializer");
954
481
  }
955
482
  if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
956
- node.translation = babylonTransformNode.position.asArray();
483
+ const translation = TmpVectors.Vector3[0].copyFrom(babylonTransformNode.position);
484
+ if (convertToRightHanded) {
485
+ ConvertToRightHandedPosition(translation);
486
+ }
487
+ node.translation = translation.asArray();
957
488
  }
958
489
  if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
959
490
  node.scale = babylonTransformNode.scaling.asArray();
@@ -963,939 +494,696 @@ export class _Exporter {
963
494
  rotationQuaternion.multiplyInPlace(babylonTransformNode.rotationQuaternion);
964
495
  }
965
496
  if (!Quaternion.IsIdentity(rotationQuaternion)) {
497
+ if (convertToRightHanded) {
498
+ ConvertToRightHandedRotation(rotationQuaternion);
499
+ }
966
500
  node.rotation = rotationQuaternion.normalize().asArray();
967
501
  }
968
502
  }
969
- _setCameraTransformation(node, babylonCamera) {
503
+ _setCameraTransformation(node, babylonCamera, convertToRightHanded, parent) {
970
504
  const translation = TmpVectors.Vector3[0];
971
505
  const rotation = TmpVectors.Quaternion[0];
972
- babylonCamera.getWorldMatrix().decompose(undefined, rotation, translation);
506
+ if (parent !== null) {
507
+ // Camera.getWorldMatrix returns global coordinates. GLTF node must use local coordinates. If camera has parent we need to use local translation/rotation.
508
+ const parentWorldMatrix = Matrix.Invert(parent.getWorldMatrix());
509
+ const cameraWorldMatrix = babylonCamera.getWorldMatrix();
510
+ const cameraLocal = cameraWorldMatrix.multiply(parentWorldMatrix);
511
+ cameraLocal.decompose(undefined, rotation, translation);
512
+ }
513
+ else {
514
+ babylonCamera.getWorldMatrix().decompose(undefined, rotation, translation);
515
+ }
973
516
  if (!translation.equalsToFloats(0, 0, 0)) {
974
517
  node.translation = translation.asArray();
975
518
  }
976
- // // Rotation by 180 as glTF has a different convention than Babylon.
977
- rotation.multiplyInPlace(rotation180Y);
978
519
  if (!Quaternion.IsIdentity(rotation)) {
979
520
  node.rotation = rotation.asArray();
980
521
  }
981
522
  }
982
- _getVertexBufferFromMesh(attributeKind, bufferMesh) {
983
- if (bufferMesh.isVerticesDataPresent(attributeKind, true)) {
984
- const vertexBuffer = bufferMesh.getVertexBuffer(attributeKind, true);
985
- if (vertexBuffer) {
986
- return vertexBuffer;
523
+ // Export babylon cameras to glTF cameras
524
+ _listAvailableCameras() {
525
+ for (const camera of this._babylonScene.cameras) {
526
+ const glTFCamera = {
527
+ type: camera.mode === Camera.PERSPECTIVE_CAMERA ? "perspective" /* CameraType.PERSPECTIVE */ : "orthographic" /* CameraType.ORTHOGRAPHIC */,
528
+ };
529
+ if (camera.name) {
530
+ glTFCamera.name = camera.name;
531
+ }
532
+ if (glTFCamera.type === "perspective" /* CameraType.PERSPECTIVE */) {
533
+ glTFCamera.perspective = {
534
+ aspectRatio: camera.getEngine().getAspectRatio(camera),
535
+ yfov: camera.fovMode === Camera.FOVMODE_VERTICAL_FIXED ? camera.fov : camera.fov * camera.getEngine().getAspectRatio(camera),
536
+ znear: camera.minZ,
537
+ zfar: camera.maxZ,
538
+ };
539
+ }
540
+ else if (glTFCamera.type === "orthographic" /* CameraType.ORTHOGRAPHIC */) {
541
+ const halfWidth = camera.orthoLeft && camera.orthoRight ? 0.5 * (camera.orthoRight - camera.orthoLeft) : camera.getEngine().getRenderWidth() * 0.5;
542
+ const halfHeight = camera.orthoBottom && camera.orthoTop ? 0.5 * (camera.orthoTop - camera.orthoBottom) : camera.getEngine().getRenderHeight() * 0.5;
543
+ glTFCamera.orthographic = {
544
+ xmag: halfWidth,
545
+ ymag: halfHeight,
546
+ znear: camera.minZ,
547
+ zfar: camera.maxZ,
548
+ };
987
549
  }
550
+ this._camerasMap.set(camera, glTFCamera);
988
551
  }
989
- return null;
990
552
  }
991
- /**
992
- * Creates a bufferview based on the vertices type for the Babylon mesh
993
- * @param kind Indicates the type of vertices data
994
- * @param attributeComponentKind Indicates the numerical type used to store the data
995
- * @param babylonTransformNode The Babylon mesh to get the vertices data from
996
- * @param binaryWriter The buffer to write the bufferview data to
997
- * @param byteStride
998
- */
999
- _createBufferViewKind(kind, attributeComponentKind, babylonTransformNode, binaryWriter, byteStride) {
1000
- const bufferMesh = babylonTransformNode instanceof Mesh
1001
- ? babylonTransformNode
1002
- : babylonTransformNode instanceof InstancedMesh
1003
- ? babylonTransformNode.sourceMesh
1004
- : null;
1005
- if (bufferMesh) {
1006
- const vertexBuffer = bufferMesh.getVertexBuffer(kind, true);
1007
- const vertexData = bufferMesh.getVerticesData(kind, undefined, undefined, true);
1008
- if (vertexBuffer && vertexData) {
1009
- const typeByteLength = VertexBuffer.GetTypeByteLength(attributeComponentKind);
1010
- const byteLength = vertexData.length * typeByteLength;
1011
- const bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
1012
- this._bufferViews.push(bufferView);
1013
- this._writeAttributeData(kind, attributeComponentKind, vertexData, byteStride / typeByteLength, binaryWriter, babylonTransformNode);
553
+ // Cleanup unused cameras and assign index to nodes.
554
+ _exportAndAssignCameras() {
555
+ const gltfCameras = Array.from(this._camerasMap.values());
556
+ for (const gltfCamera of gltfCameras) {
557
+ const usedNodes = this._nodesCameraMap.get(gltfCamera);
558
+ if (usedNodes !== undefined) {
559
+ this._cameras.push(gltfCamera);
560
+ for (const node of usedNodes) {
561
+ node.camera = this._cameras.length - 1;
562
+ }
1014
563
  }
1015
564
  }
1016
565
  }
1017
- /**
1018
- * The primitive mode of the Babylon mesh
1019
- * @param babylonMesh The BabylonJS mesh
1020
- * @returns Unsigned integer of the primitive mode or null
1021
- */
1022
- _getMeshPrimitiveMode(babylonMesh) {
1023
- if (babylonMesh instanceof LinesMesh) {
1024
- return Material.LineListDrawMode;
1025
- }
1026
- if (babylonMesh instanceof InstancedMesh || babylonMesh instanceof Mesh) {
1027
- const baseMesh = babylonMesh instanceof Mesh ? babylonMesh : babylonMesh.sourceMesh;
1028
- if (typeof baseMesh.overrideRenderingFillMode === "number") {
1029
- return baseMesh.overrideRenderingFillMode;
566
+ // Builds all skins in the skins array so nodes can reference it during node parsing.
567
+ _listAvailableSkeletons() {
568
+ for (const skeleton of this._babylonScene.skeletons) {
569
+ if (skeleton.bones.length <= 0) {
570
+ continue;
1030
571
  }
572
+ const skin = { joints: [] };
573
+ this._skinMap.set(skeleton, skin);
1031
574
  }
1032
- return babylonMesh.material ? babylonMesh.material.fillMode : Material.TriangleFillMode;
1033
575
  }
1034
- /**
1035
- * Sets the primitive mode of the glTF mesh primitive
1036
- * @param meshPrimitive glTF mesh primitive
1037
- * @param primitiveMode The primitive mode
1038
- */
1039
- _setPrimitiveMode(meshPrimitive, primitiveMode) {
1040
- switch (primitiveMode) {
1041
- case Material.TriangleFillMode: {
1042
- // glTF defaults to using Triangle Mode
1043
- break;
1044
- }
1045
- case Material.TriangleStripDrawMode: {
1046
- meshPrimitive.mode = 5 /* MeshPrimitiveMode.TRIANGLE_STRIP */;
1047
- break;
1048
- }
1049
- case Material.TriangleFanDrawMode: {
1050
- meshPrimitive.mode = 6 /* MeshPrimitiveMode.TRIANGLE_FAN */;
1051
- break;
1052
- }
1053
- case Material.PointListDrawMode: {
1054
- meshPrimitive.mode = 0 /* MeshPrimitiveMode.POINTS */;
1055
- break;
576
+ _exportAndAssignSkeletons() {
577
+ for (const skeleton of this._babylonScene.skeletons) {
578
+ if (skeleton.bones.length <= 0) {
579
+ continue;
1056
580
  }
1057
- case Material.PointFillMode: {
1058
- meshPrimitive.mode = 0 /* MeshPrimitiveMode.POINTS */;
1059
- break;
581
+ const skin = this._skinMap.get(skeleton);
582
+ if (skin == undefined) {
583
+ continue;
1060
584
  }
1061
- case Material.LineLoopDrawMode: {
1062
- meshPrimitive.mode = 2 /* MeshPrimitiveMode.LINE_LOOP */;
1063
- break;
585
+ const boneIndexMap = {};
586
+ const inverseBindMatrices = [];
587
+ let maxBoneIndex = -1;
588
+ for (let i = 0; i < skeleton.bones.length; ++i) {
589
+ const bone = skeleton.bones[i];
590
+ const boneIndex = bone.getIndex() ?? i;
591
+ if (boneIndex !== -1) {
592
+ boneIndexMap[boneIndex] = bone;
593
+ if (boneIndex > maxBoneIndex) {
594
+ maxBoneIndex = boneIndex;
595
+ }
596
+ }
1064
597
  }
1065
- case Material.LineListDrawMode: {
1066
- meshPrimitive.mode = 1 /* MeshPrimitiveMode.LINES */;
1067
- break;
598
+ // Set joints index to scene node.
599
+ for (let boneIndex = 0; boneIndex <= maxBoneIndex; ++boneIndex) {
600
+ const bone = boneIndexMap[boneIndex];
601
+ inverseBindMatrices.push(bone.getAbsoluteInverseBindMatrix());
602
+ const transformNode = bone.getTransformNode();
603
+ if (transformNode !== null) {
604
+ const nodeID = this._nodeMap.get(transformNode);
605
+ if (transformNode && nodeID !== null && nodeID !== undefined) {
606
+ skin.joints.push(nodeID);
607
+ }
608
+ else {
609
+ Tools.Warn("Exporting a bone without a linked transform node is currently unsupported");
610
+ }
611
+ }
612
+ else {
613
+ Tools.Warn("Exporting a bone without a linked transform node is currently unsupported");
614
+ }
1068
615
  }
1069
- case Material.LineStripDrawMode: {
1070
- meshPrimitive.mode = 3 /* MeshPrimitiveMode.LINE_STRIP */;
1071
- break;
616
+ // Nodes that use this skin.
617
+ const skinedNodes = this._nodesSkinMap.get(skin);
618
+ // Only create skeleton if it has at least one joint and is used by a mesh.
619
+ 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
+ });
634
+ });
635
+ this._skins.push(skin);
636
+ for (const skinedNode of skinedNodes) {
637
+ skinedNode.skin = this._skins.length - 1;
638
+ }
1072
639
  }
1073
640
  }
1074
641
  }
1075
- /**
1076
- * Sets the vertex attribute accessor based of the glTF mesh primitive
1077
- * @param meshPrimitive glTF mesh primitive
1078
- * @param attributeKind vertex attribute
1079
- */
1080
- _setAttributeKind(attributes, attributeKind) {
1081
- switch (attributeKind) {
1082
- case VertexBuffer.PositionKind: {
1083
- attributes.POSITION = this._accessors.length - 1;
1084
- break;
1085
- }
1086
- case VertexBuffer.NormalKind: {
1087
- attributes.NORMAL = this._accessors.length - 1;
1088
- break;
1089
- }
1090
- case VertexBuffer.ColorKind: {
1091
- attributes.COLOR_0 = this._accessors.length - 1;
1092
- break;
1093
- }
1094
- case VertexBuffer.TangentKind: {
1095
- attributes.TANGENT = this._accessors.length - 1;
1096
- break;
1097
- }
1098
- case VertexBuffer.UVKind: {
1099
- attributes.TEXCOORD_0 = this._accessors.length - 1;
1100
- break;
1101
- }
1102
- case VertexBuffer.UV2Kind: {
1103
- attributes.TEXCOORD_1 = this._accessors.length - 1;
1104
- break;
1105
- }
1106
- case VertexBuffer.MatricesIndicesKind: {
1107
- attributes.JOINTS_0 = this._accessors.length - 1;
1108
- break;
642
+ async _exportSceneAsync() {
643
+ const scene = { nodes: [] };
644
+ // Scene metadata
645
+ if (this._babylonScene.metadata) {
646
+ if (this._options.metadataSelector) {
647
+ scene.extras = this._options.metadataSelector(this._babylonScene.metadata);
1109
648
  }
1110
- case VertexBuffer.MatricesIndicesExtraKind: {
1111
- attributes.JOINTS_1 = this._accessors.length - 1;
1112
- break;
649
+ else if (this._babylonScene.metadata.gltf) {
650
+ scene.extras = this._babylonScene.metadata.gltf.extras;
1113
651
  }
1114
- case VertexBuffer.MatricesWeightsKind: {
1115
- attributes.WEIGHTS_0 = this._accessors.length - 1;
1116
- break;
652
+ }
653
+ // TODO:
654
+ // deal with this from the loader:
655
+ // babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
656
+ // babylonMaterial.invertNormalMapY = this._babylonScene.useRightHandedSystem;
657
+ const rootNodesRH = new Array();
658
+ const rootNodesLH = new Array();
659
+ const rootNoopNodesRH = new Array();
660
+ for (const rootNode of this._babylonScene.rootNodes) {
661
+ if (this._options.removeNoopRootNodes && !this._options.includeCoordinateSystemConversionNodes && IsNoopNode(rootNode, this._babylonScene.useRightHandedSystem)) {
662
+ rootNoopNodesRH.push(...rootNode.getChildren());
1117
663
  }
1118
- case VertexBuffer.MatricesWeightsExtraKind: {
1119
- attributes.WEIGHTS_1 = this._accessors.length - 1;
1120
- break;
664
+ else if (this._babylonScene.useRightHandedSystem) {
665
+ rootNodesRH.push(rootNode);
1121
666
  }
1122
- default: {
1123
- Tools.Warn("Unsupported Vertex Buffer Type: " + attributeKind);
667
+ else {
668
+ rootNodesLH.push(rootNode);
669
+ }
670
+ }
671
+ this._listAvailableCameras();
672
+ this._listAvailableSkeletons();
673
+ const stateLH = new ExporterState(true, false);
674
+ scene.nodes.push(...(await this._exportNodesAsync(rootNodesLH, stateLH)));
675
+ const stateRH = new ExporterState(false, false);
676
+ scene.nodes.push(...(await this._exportNodesAsync(rootNodesRH, stateRH)));
677
+ const noopRH = new ExporterState(false, true);
678
+ scene.nodes.push(...(await this._exportNodesAsync(rootNoopNodesRH, noopRH)));
679
+ if (scene.nodes.length) {
680
+ this._scenes.push(scene);
681
+ }
682
+ this._exportAndAssignCameras();
683
+ this._exportAndAssignSkeletons();
684
+ if (this._babylonScene.animationGroups.length) {
685
+ _GLTFAnimation._CreateNodeAndMorphAnimationFromAnimationGroups(this._babylonScene, this._animations, this._nodeMap, this._dataWriter, this._bufferViews, this._accessors, this._animationSampleRate, stateLH.getNodesSet());
686
+ }
687
+ }
688
+ _shouldExportNode(babylonNode) {
689
+ let result = this._shouldExportNodeMap.get(babylonNode);
690
+ if (result === undefined) {
691
+ result = this._options.shouldExportNode(babylonNode);
692
+ this._shouldExportNodeMap.set(babylonNode, result);
693
+ }
694
+ return result;
695
+ }
696
+ async _exportNodesAsync(babylonRootNodes, state) {
697
+ const nodes = new Array();
698
+ this._exportBuffers(babylonRootNodes, state);
699
+ for (const babylonNode of babylonRootNodes) {
700
+ if (this._shouldExportNode(babylonNode)) {
701
+ const nodeIndex = await this._exportNodeAsync(babylonNode, state);
702
+ if (nodeIndex !== null) {
703
+ nodes.push(nodeIndex);
704
+ }
1124
705
  }
1125
706
  }
707
+ return nodes;
1126
708
  }
1127
- /**
1128
- * Sets data for the primitive attributes of each submesh
1129
- * @param mesh glTF Mesh object to store the primitive attribute information
1130
- * @param babylonTransformNode Babylon mesh to get the primitive attribute data from
1131
- * @param binaryWriter Buffer to write the attribute data to
1132
- * @returns promise that resolves when done setting the primitive attributes
1133
- */
1134
- _setPrimitiveAttributesAsync(mesh, babylonTransformNode, binaryWriter) {
1135
- const promises = [];
1136
- let bufferMesh = null;
1137
- let bufferView;
1138
- let minMax;
1139
- if (babylonTransformNode instanceof Mesh) {
1140
- bufferMesh = babylonTransformNode;
1141
- }
1142
- else if (babylonTransformNode instanceof InstancedMesh) {
1143
- bufferMesh = babylonTransformNode.sourceMesh;
1144
- }
1145
- const attributeData = [
1146
- { kind: VertexBuffer.PositionKind, accessorType: "VEC3" /* AccessorType.VEC3 */, accessorComponentType: 5126 /* AccessorComponentType.FLOAT */, byteStride: 12 },
1147
- { kind: VertexBuffer.NormalKind, accessorType: "VEC3" /* AccessorType.VEC3 */, accessorComponentType: 5126 /* AccessorComponentType.FLOAT */, byteStride: 12 },
1148
- { kind: VertexBuffer.ColorKind, accessorType: "VEC4" /* AccessorType.VEC4 */, accessorComponentType: 5126 /* AccessorComponentType.FLOAT */, byteStride: 16 },
1149
- { kind: VertexBuffer.TangentKind, accessorType: "VEC4" /* AccessorType.VEC4 */, accessorComponentType: 5126 /* AccessorComponentType.FLOAT */, byteStride: 16 },
1150
- { kind: VertexBuffer.UVKind, accessorType: "VEC2" /* AccessorType.VEC2 */, accessorComponentType: 5126 /* AccessorComponentType.FLOAT */, byteStride: 8 },
1151
- { kind: VertexBuffer.UV2Kind, accessorType: "VEC2" /* AccessorType.VEC2 */, accessorComponentType: 5126 /* AccessorComponentType.FLOAT */, byteStride: 8 },
1152
- { kind: VertexBuffer.MatricesIndicesKind, accessorType: "VEC4" /* AccessorType.VEC4 */, accessorComponentType: 5123 /* AccessorComponentType.UNSIGNED_SHORT */, byteStride: 8 },
1153
- { kind: VertexBuffer.MatricesIndicesExtraKind, accessorType: "VEC4" /* AccessorType.VEC4 */, accessorComponentType: 5123 /* AccessorComponentType.UNSIGNED_SHORT */, byteStride: 8 },
1154
- { kind: VertexBuffer.MatricesWeightsKind, accessorType: "VEC4" /* AccessorType.VEC4 */, accessorComponentType: 5126 /* AccessorComponentType.FLOAT */, byteStride: 16 },
1155
- { kind: VertexBuffer.MatricesWeightsExtraKind, accessorType: "VEC4" /* AccessorType.VEC4 */, accessorComponentType: 5126 /* AccessorComponentType.FLOAT */, byteStride: 16 },
1156
- ];
1157
- if (bufferMesh) {
1158
- let indexBufferViewIndex = null;
1159
- const primitiveMode = this._getMeshPrimitiveMode(bufferMesh);
1160
- const vertexAttributeBufferViews = {};
1161
- const morphTargetManager = bufferMesh.morphTargetManager;
1162
- // For each BabylonMesh, create bufferviews for each 'kind'
1163
- for (const attribute of attributeData) {
1164
- const attributeKind = attribute.kind;
1165
- const attributeComponentKind = attribute.accessorComponentType;
1166
- if (bufferMesh.isVerticesDataPresent(attributeKind, true)) {
1167
- const vertexBuffer = this._getVertexBufferFromMesh(attributeKind, bufferMesh);
1168
- attribute.byteStride = vertexBuffer
1169
- ? vertexBuffer.getSize() * VertexBuffer.GetTypeByteLength(attribute.accessorComponentType)
1170
- : VertexBuffer.DeduceStride(attributeKind) * 4;
1171
- if (attribute.byteStride === 12) {
1172
- attribute.accessorType = "VEC3" /* AccessorType.VEC3 */;
709
+ _collectBuffers(babylonNode, bufferToVertexBuffersMap, vertexBufferToMeshesMap, morphTargetsToMeshesMap, state) {
710
+ if (!this._shouldExportNode(babylonNode)) {
711
+ return;
712
+ }
713
+ if (babylonNode instanceof Mesh && babylonNode.geometry) {
714
+ const vertexBuffers = babylonNode.geometry.getVertexBuffers();
715
+ if (vertexBuffers) {
716
+ for (const kind in vertexBuffers) {
717
+ const vertexBuffer = vertexBuffers[kind];
718
+ state.setHasVertexColorAlpha(vertexBuffer, babylonNode.hasVertexAlpha);
719
+ const buffer = vertexBuffer._buffer;
720
+ const vertexBufferArray = bufferToVertexBuffersMap.get(buffer) || [];
721
+ bufferToVertexBuffersMap.set(buffer, vertexBufferArray);
722
+ if (vertexBufferArray.indexOf(vertexBuffer) === -1) {
723
+ vertexBufferArray.push(vertexBuffer);
1173
724
  }
1174
- this._createBufferViewKind(attributeKind, attributeComponentKind, babylonTransformNode, binaryWriter, attribute.byteStride);
1175
- attribute.bufferViewIndex = this._bufferViews.length - 1;
1176
- vertexAttributeBufferViews[attributeKind] = attribute.bufferViewIndex;
1177
- // Write any morph target data to the buffer and create an associated buffer view
1178
- if (morphTargetManager) {
1179
- for (let i = 0; i < morphTargetManager.numTargets; ++i) {
1180
- const morphTarget = morphTargetManager.getTarget(i);
1181
- const morphTargetInfo = this._createMorphTargetBufferViewKind(attributeKind, attribute.accessorType, attributeComponentKind, bufferMesh, morphTarget, binaryWriter, attribute.byteStride);
1182
- // Store info about the morph target that will be needed later when creating per-submesh accessors
1183
- if (morphTargetInfo) {
1184
- if (!attribute.morphTargetInfo) {
1185
- attribute.morphTargetInfo = [];
1186
- }
1187
- attribute.morphTargetInfo[i] = morphTargetInfo;
1188
- }
1189
- }
725
+ const meshes = vertexBufferToMeshesMap.get(vertexBuffer) || [];
726
+ vertexBufferToMeshesMap.set(vertexBuffer, meshes);
727
+ if (meshes.indexOf(babylonNode) === -1) {
728
+ meshes.push(babylonNode);
1190
729
  }
1191
730
  }
1192
731
  }
1193
- if (bufferMesh.getTotalIndices()) {
1194
- const indices = bufferMesh.getIndices();
1195
- if (indices) {
1196
- const byteLength = indices.length * 4;
1197
- bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
1198
- this._bufferViews.push(bufferView);
1199
- indexBufferViewIndex = this._bufferViews.length - 1;
1200
- for (let k = 0, length = indices.length; k < length; ++k) {
1201
- binaryWriter.setUInt32(indices[k]);
732
+ const morphTargetManager = babylonNode.morphTargetManager;
733
+ if (morphTargetManager) {
734
+ for (let morphIndex = 0; morphIndex < morphTargetManager.numTargets; morphIndex++) {
735
+ const morphTarget = morphTargetManager.getTarget(morphIndex);
736
+ const meshes = morphTargetsToMeshesMap.get(morphTarget) || [];
737
+ morphTargetsToMeshesMap.set(morphTarget, meshes);
738
+ if (meshes.indexOf(babylonNode) === -1) {
739
+ meshes.push(babylonNode);
1202
740
  }
1203
741
  }
1204
742
  }
1205
- if (bufferMesh.subMeshes) {
1206
- // go through all mesh primitives (submeshes)
1207
- for (const submesh of bufferMesh.subMeshes) {
1208
- let babylonMaterial = submesh.getMaterial() || bufferMesh.getScene().defaultMaterial;
1209
- let materialIndex = null;
1210
- if (babylonMaterial) {
1211
- if (bufferMesh instanceof LinesMesh) {
1212
- // get the color from the lines mesh and set it in the material
1213
- const material = {
1214
- name: bufferMesh.name + " material",
1215
- };
1216
- if (!bufferMesh.color.equals(Color3.White()) || bufferMesh.alpha < 1) {
1217
- material.pbrMetallicRoughness = {
1218
- baseColorFactor: bufferMesh.color.asArray().concat([bufferMesh.alpha]),
1219
- };
1220
- }
1221
- this._materials.push(material);
1222
- materialIndex = this._materials.length - 1;
743
+ }
744
+ for (const babylonChildNode of babylonNode.getChildren()) {
745
+ this._collectBuffers(babylonChildNode, bufferToVertexBuffersMap, vertexBufferToMeshesMap, morphTargetsToMeshesMap, state);
746
+ }
747
+ }
748
+ _exportBuffers(babylonRootNodes, state) {
749
+ const bufferToVertexBuffersMap = new Map();
750
+ const vertexBufferToMeshesMap = new Map();
751
+ const morphTagetsMeshesMap = new Map();
752
+ for (const babylonNode of babylonRootNodes) {
753
+ this._collectBuffers(babylonNode, bufferToVertexBuffersMap, vertexBufferToMeshesMap, morphTagetsMeshesMap, state);
754
+ }
755
+ const buffers = Array.from(bufferToVertexBuffersMap.keys());
756
+ for (const buffer of buffers) {
757
+ const data = buffer.getData();
758
+ if (!data) {
759
+ throw new Error("Buffer data is not available");
760
+ }
761
+ const vertexBuffers = bufferToVertexBuffersMap.get(buffer);
762
+ if (!vertexBuffers) {
763
+ continue;
764
+ }
765
+ const byteStride = vertexBuffers[0].byteStride;
766
+ if (vertexBuffers.some((vertexBuffer) => vertexBuffer.byteStride !== byteStride)) {
767
+ throw new Error("Vertex buffers pointing to the same buffer must have the same byte stride");
768
+ }
769
+ const bytes = DataArrayToUint8Array(data).slice();
770
+ // Apply conversions to buffer data in-place.
771
+ for (const vertexBuffer of vertexBuffers) {
772
+ const { byteOffset, byteStride, type, normalized } = vertexBuffer;
773
+ const size = vertexBuffer.getSize();
774
+ const meshes = vertexBufferToMeshesMap.get(vertexBuffer);
775
+ const maxTotalVertices = meshes.reduce((max, current) => {
776
+ return current.getTotalVertices() > max ? current.getTotalVertices() : max;
777
+ }, -Number.MAX_VALUE); // To ensure nothing is missed when enumerating, but may not be necessary.
778
+ switch (vertexBuffer.getKind()) {
779
+ // Normalize normals and tangents.
780
+ case VertexBuffer.NormalKind:
781
+ case VertexBuffer.TangentKind: {
782
+ EnumerateFloatValues(bytes, byteOffset, byteStride, size, type, maxTotalVertices * size, normalized, (values) => {
783
+ const invLength = 1 / Math.sqrt(values[0] * values[0] + values[1] * values[1] + values[2] * values[2]);
784
+ values[0] *= invLength;
785
+ values[1] *= invLength;
786
+ values[2] *= invLength;
787
+ });
788
+ break;
789
+ }
790
+ // Convert StandardMaterial vertex colors from gamma to linear space.
791
+ case VertexBuffer.ColorKind: {
792
+ const stdMaterialCount = meshes.filter((mesh) => mesh.material instanceof StandardMaterial || mesh.material == null).length;
793
+ if (stdMaterialCount == 0) {
794
+ break; // Buffer not used by StandardMaterials, so no conversion needed.
1223
795
  }
1224
- else if (babylonMaterial instanceof MultiMaterial) {
1225
- const subMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
1226
- if (subMaterial) {
1227
- babylonMaterial = subMaterial;
1228
- materialIndex = this._materialMap[babylonMaterial.uniqueId];
1229
- }
796
+ // TODO: Implement this case.
797
+ if (stdMaterialCount != meshes.length) {
798
+ Logger.Warn("Not converting vertex color space, as buffer is shared by StandardMaterials and other material types. Results may look incorrect.");
799
+ break;
1230
800
  }
1231
- else {
1232
- materialIndex = this._materialMap[babylonMaterial.uniqueId];
801
+ if (type == VertexBuffer.UNSIGNED_BYTE) {
802
+ Logger.Warn("Converting uint8 vertex colors to linear space. Results may look incorrect.");
1233
803
  }
1234
- }
1235
- const glTFMaterial = materialIndex != null ? this._materials[materialIndex] : null;
1236
- const meshPrimitive = { attributes: {} };
1237
- this._setPrimitiveMode(meshPrimitive, primitiveMode);
1238
- for (const attribute of attributeData) {
1239
- const attributeKind = attribute.kind;
1240
- if ((attributeKind === VertexBuffer.UVKind || attributeKind === VertexBuffer.UV2Kind) && !this._options.exportUnusedUVs) {
1241
- if (!glTFMaterial || !this._glTFMaterialExporter._hasTexturesPresent(glTFMaterial)) {
1242
- continue;
804
+ const vertexData3 = new Color3();
805
+ const vertexData4 = new Color4();
806
+ const useExactSrgbConversions = this._babylonScene.getEngine().useExactSrgbConversions;
807
+ EnumerateFloatValues(bytes, byteOffset, byteStride, size, type, maxTotalVertices * size, normalized, (values) => {
808
+ // Using separate Color3 and Color4 objects to ensure the right functions are called.
809
+ if (values.length === 3) {
810
+ vertexData3.fromArray(values, 0);
811
+ vertexData3.toLinearSpaceToRef(vertexData3, useExactSrgbConversions);
812
+ vertexData3.toArray(values, 0);
1243
813
  }
1244
- }
1245
- const vertexData = bufferMesh.getVerticesData(attributeKind, undefined, undefined, true);
1246
- if (vertexData) {
1247
- const vertexBuffer = this._getVertexBufferFromMesh(attributeKind, bufferMesh);
1248
- if (vertexBuffer) {
1249
- const stride = vertexBuffer.getSize();
1250
- const bufferViewIndex = attribute.bufferViewIndex;
1251
- if (bufferViewIndex != undefined) {
1252
- // check to see if bufferviewindex has a numeric value assigned.
1253
- minMax = { min: null, max: null };
1254
- if (attributeKind == VertexBuffer.PositionKind) {
1255
- minMax = _GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride);
1256
- }
1257
- const accessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, attribute.accessorComponentType, vertexData.length / stride, 0, minMax.min, minMax.max);
1258
- this._accessors.push(accessor);
1259
- this._setAttributeKind(meshPrimitive.attributes, attributeKind);
1260
- }
814
+ else {
815
+ vertexData4.fromArray(values, 0);
816
+ vertexData4.toLinearSpaceToRef(vertexData4, useExactSrgbConversions);
817
+ vertexData4.toArray(values, 0);
1261
818
  }
1262
- }
1263
- }
1264
- if (indexBufferViewIndex) {
1265
- // Create accessor
1266
- const accessor = _GLTFUtilities._CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* AccessorType.SCALAR */, 5125 /* AccessorComponentType.UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
1267
- this._accessors.push(accessor);
1268
- meshPrimitive.indices = this._accessors.length - 1;
819
+ });
1269
820
  }
1270
- if (Object.keys(meshPrimitive.attributes).length > 0) {
1271
- const sideOrientation = babylonMaterial._getEffectiveOrientation(bufferMesh);
1272
- if (sideOrientation === (this._babylonScene.useRightHandedSystem ? Material.ClockWiseSideOrientation : Material.CounterClockWiseSideOrientation)) {
1273
- let byteOffset = indexBufferViewIndex != null ? this._bufferViews[indexBufferViewIndex].byteOffset : null;
1274
- if (byteOffset == null) {
1275
- byteOffset = 0;
1276
- }
1277
- let babylonIndices = null;
1278
- if (indexBufferViewIndex != null) {
1279
- babylonIndices = bufferMesh.getIndices();
1280
- }
1281
- if (babylonIndices) {
1282
- this._reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
1283
- }
1284
- else {
1285
- for (const attribute of attributeData) {
1286
- const vertexData = bufferMesh.getVerticesData(attribute.kind, undefined, undefined, true);
1287
- if (vertexData) {
1288
- const byteOffset = this._bufferViews[vertexAttributeBufferViews[attribute.kind]].byteOffset || 0;
1289
- this._reorderVertexAttributeDataBasedOnPrimitiveMode(submesh, primitiveMode, attribute.kind, vertexData, byteOffset, binaryWriter);
1290
- }
1291
- }
821
+ }
822
+ }
823
+ // Performs coordinate conversion if needed (only for position, normal and tanget).
824
+ if (state.convertToRightHanded) {
825
+ for (const vertexBuffer of vertexBuffers) {
826
+ switch (vertexBuffer.getKind()) {
827
+ case VertexBuffer.PositionKind:
828
+ case VertexBuffer.NormalKind:
829
+ case VertexBuffer.TangentKind: {
830
+ for (const mesh of vertexBufferToMeshesMap.get(vertexBuffer)) {
831
+ const { byteOffset, byteStride, type, normalized } = vertexBuffer;
832
+ const size = vertexBuffer.getSize();
833
+ EnumerateFloatValues(bytes, byteOffset, byteStride, size, type, mesh.getTotalVertices() * size, normalized, (values) => {
834
+ values[0] = -values[0];
835
+ });
1292
836
  }
1293
837
  }
1294
- if (materialIndex != null) {
1295
- meshPrimitive.material = materialIndex;
1296
- }
1297
838
  }
1298
- // If there are morph targets, write out targets and associated accessors
1299
- if (morphTargetManager) {
1300
- // By convention, morph target names are stored in the mesh extras.
1301
- if (!mesh.extras) {
1302
- mesh.extras = {};
1303
- }
1304
- mesh.extras.targetNames = [];
1305
- for (let i = 0; i < morphTargetManager.numTargets; ++i) {
1306
- const morphTarget = morphTargetManager.getTarget(i);
1307
- mesh.extras.targetNames.push(morphTarget.name);
1308
- for (const attribute of attributeData) {
1309
- const morphTargetInfo = attribute.morphTargetInfo?.[i];
1310
- if (morphTargetInfo) {
1311
- // Write the accessor
1312
- const byteOffset = 0;
1313
- const accessor = _GLTFUtilities._CreateAccessor(morphTargetInfo.bufferViewIndex, `${attribute.kind} - ${morphTarget.name} (Morph Target)`, morphTargetInfo.accessorType, attribute.accessorComponentType, morphTargetInfo.vertexCount, byteOffset, morphTargetInfo.minMax?.min?.asArray() ?? null, morphTargetInfo.minMax?.max?.asArray() ?? null);
1314
- this._accessors.push(accessor);
1315
- // Create a target that references the new accessor
1316
- if (!meshPrimitive.targets) {
1317
- meshPrimitive.targets = [];
1318
- }
1319
- if (!meshPrimitive.targets[i]) {
1320
- meshPrimitive.targets[i] = {};
1321
- }
1322
- this._setAttributeKind(meshPrimitive.targets[i], attribute.kind);
839
+ }
840
+ // Save converted bytes for min/max computation.
841
+ state.convertedToRightHandedBuffers.set(buffer, bytes);
842
+ }
843
+ const byteOffset = this._dataWriter.byteOffset;
844
+ this._dataWriter.writeUint8Array(bytes);
845
+ this._bufferViews.push(CreateBufferView(0, byteOffset, bytes.length, byteStride));
846
+ state.setVertexBufferView(buffer, this._bufferViews.length - 1);
847
+ const floatMatricesIndices = new Map();
848
+ // If buffers are of type MatricesWeightsKind and have float values, we need to create a new buffer instead.
849
+ for (const vertexBuffer of vertexBuffers) {
850
+ switch (vertexBuffer.getKind()) {
851
+ case VertexBuffer.MatricesIndicesKind:
852
+ case VertexBuffer.MatricesIndicesExtraKind: {
853
+ if (vertexBuffer.type == VertexBuffer.FLOAT) {
854
+ for (const mesh of vertexBufferToMeshesMap.get(vertexBuffer)) {
855
+ const floatData = vertexBuffer.getFloatData(mesh.getTotalVertices());
856
+ if (floatData !== null) {
857
+ floatMatricesIndices.set(vertexBuffer, floatData);
1323
858
  }
1324
859
  }
1325
860
  }
1326
861
  }
1327
- mesh.primitives.push(meshPrimitive);
1328
- this._extensionsPostExportMeshPrimitiveAsync("postExport", meshPrimitive, submesh, binaryWriter);
1329
- promises.push();
1330
862
  }
1331
863
  }
1332
- }
1333
- return Promise.all(promises).then(() => {
1334
- /* do nothing */
1335
- });
1336
- }
1337
- /**
1338
- * Creates a glTF scene based on the array of meshes
1339
- * Returns the total byte offset
1340
- * @param binaryWriter Buffer to write binary data to
1341
- * @returns a promise that resolves when done
1342
- */
1343
- _createSceneAsync(binaryWriter) {
1344
- const scene = { nodes: [] };
1345
- let glTFNodeIndex;
1346
- let glTFNode;
1347
- let directDescendents;
1348
- const nodes = [...this._babylonScene.transformNodes, ...this._babylonScene.meshes, ...this._babylonScene.lights, ...this._babylonScene.cameras];
1349
- const removedRootNodes = new Set();
1350
- // Scene metadata
1351
- if (this._babylonScene.metadata) {
1352
- if (this._options.metadataSelector) {
1353
- scene.extras = this._options.metadataSelector(this._babylonScene.metadata);
1354
- }
1355
- else if (this._babylonScene.metadata.gltf) {
1356
- scene.extras = this._babylonScene.metadata.gltf.extras;
864
+ if (floatMatricesIndices.size !== 0) {
865
+ Logger.Warn(`Joints conversion needed: some joints are stored as floats in Babylon but GLTF requires UNSIGNED BYTES. We will perform the conversion but this might lead to unused data in the buffer.`);
1357
866
  }
1358
- }
1359
- // Remove no-op root nodes
1360
- if ((this._options.removeNoopRootNodes ?? true) && !this._options.includeCoordinateSystemConversionNodes) {
1361
- for (const rootNode of this._babylonScene.rootNodes) {
1362
- if (isNoopNode(rootNode, this._babylonScene.useRightHandedSystem)) {
1363
- removedRootNodes.add(rootNode);
1364
- // Exclude the node from list of nodes to export
1365
- nodes.splice(nodes.indexOf(rootNode), 1);
867
+ const floatArrayVertexBuffers = Array.from(floatMatricesIndices.keys());
868
+ for (const vertexBuffer of floatArrayVertexBuffers) {
869
+ const array = floatMatricesIndices.get(vertexBuffer);
870
+ if (!array) {
871
+ continue;
872
+ }
873
+ const byteOffset = this._dataWriter.byteOffset;
874
+ if (FloatsNeed16BitInteger(array)) {
875
+ const newArray = new Uint16Array(array.length);
876
+ for (let index = 0; index < array.length; index++) {
877
+ newArray[index] = array[index];
878
+ }
879
+ this._dataWriter.writeUint16Array(newArray);
880
+ this._bufferViews.push(CreateBufferView(0, byteOffset, newArray.byteLength, 4 * 2));
881
+ }
882
+ else {
883
+ const newArray = new Uint8Array(array.length);
884
+ for (let index = 0; index < array.length; index++) {
885
+ newArray[index] = array[index];
886
+ }
887
+ this._dataWriter.writeUint8Array(newArray);
888
+ this._bufferViews.push(CreateBufferView(0, byteOffset, newArray.byteLength, 4));
1366
889
  }
890
+ state.setRemappedBufferView(buffer, vertexBuffer, this._bufferViews.length - 1);
1367
891
  }
1368
892
  }
1369
- // Export babylon cameras to glTFCamera
1370
- const cameraMap = new Map();
1371
- this._babylonScene.cameras.forEach((camera) => {
1372
- if (this._options.shouldExportNode && !this._options.shouldExportNode(camera)) {
1373
- return;
1374
- }
1375
- const glTFCamera = {
1376
- type: camera.mode === Camera.PERSPECTIVE_CAMERA ? "perspective" /* CameraType.PERSPECTIVE */ : "orthographic" /* CameraType.ORTHOGRAPHIC */,
1377
- };
1378
- if (camera.name) {
1379
- glTFCamera.name = camera.name;
893
+ const morphTargets = Array.from(morphTagetsMeshesMap.keys());
894
+ for (const morphTarget of morphTargets) {
895
+ const meshes = morphTagetsMeshesMap.get(morphTarget);
896
+ if (!meshes) {
897
+ continue;
1380
898
  }
1381
- if (glTFCamera.type === "perspective" /* CameraType.PERSPECTIVE */) {
1382
- glTFCamera.perspective = {
1383
- aspectRatio: camera.getEngine().getAspectRatio(camera),
1384
- yfov: camera.fovMode === Camera.FOVMODE_VERTICAL_FIXED ? camera.fov : camera.fov * camera.getEngine().getAspectRatio(camera),
1385
- znear: camera.minZ,
1386
- zfar: camera.maxZ,
1387
- };
899
+ const glTFMorphTarget = BuildMorphTargetBuffers(morphTarget, meshes[0], this._dataWriter, this._bufferViews, this._accessors, state.convertToRightHanded);
900
+ for (const mesh of meshes) {
901
+ state.bindMorphDataToMesh(mesh, glTFMorphTarget);
1388
902
  }
1389
- else if (glTFCamera.type === "orthographic" /* CameraType.ORTHOGRAPHIC */) {
1390
- const halfWidth = camera.orthoLeft && camera.orthoRight ? 0.5 * (camera.orthoRight - camera.orthoLeft) : camera.getEngine().getRenderWidth() * 0.5;
1391
- const halfHeight = camera.orthoBottom && camera.orthoTop ? 0.5 * (camera.orthoTop - camera.orthoBottom) : camera.getEngine().getRenderHeight() * 0.5;
1392
- glTFCamera.orthographic = {
1393
- xmag: halfWidth,
1394
- ymag: halfHeight,
1395
- znear: camera.minZ,
1396
- zfar: camera.maxZ,
1397
- };
1398
- }
1399
- cameraMap.set(camera, this._cameras.length);
1400
- this._cameras.push(glTFCamera);
1401
- });
1402
- const [exportNodes, exportMaterials] = this._getExportNodes(nodes);
1403
- return this._glTFMaterialExporter._convertMaterialsToGLTFAsync(exportMaterials, "image/png" /* ImageMimeType.PNG */, true).then(() => {
1404
- return this._createNodeMapAndAnimationsAsync(exportNodes, binaryWriter).then((nodeMap) => {
1405
- return this._createSkinsAsync(nodeMap, binaryWriter).then((skinMap) => {
1406
- this._nodeMap = nodeMap;
1407
- this._totalByteLength = binaryWriter.getByteOffset();
1408
- if (this._totalByteLength == undefined) {
1409
- throw new Error("undefined byte length!");
1410
- }
1411
- // Build Hierarchy with the node map.
1412
- for (const babylonNode of nodes) {
1413
- glTFNodeIndex = this._nodeMap[babylonNode.uniqueId];
1414
- if (glTFNodeIndex !== undefined) {
1415
- glTFNode = this._nodes[glTFNodeIndex];
1416
- if (babylonNode.metadata) {
1417
- if (this._options.metadataSelector) {
1418
- glTFNode.extras = this._options.metadataSelector(babylonNode.metadata);
1419
- }
1420
- else if (babylonNode.metadata.gltf) {
1421
- glTFNode.extras = babylonNode.metadata.gltf.extras;
1422
- }
1423
- }
1424
- if (babylonNode instanceof Camera) {
1425
- glTFNode.camera = cameraMap.get(babylonNode);
1426
- }
1427
- if (this._options.shouldExportNode && !this._options.shouldExportNode(babylonNode)) {
1428
- Tools.Log("Omitting " + babylonNode.name + " from scene.");
1429
- }
1430
- else {
1431
- if (!babylonNode.parent && !this._babylonScene.useRightHandedSystem) {
1432
- convertNodeHandedness(glTFNode);
1433
- }
1434
- if (!babylonNode.parent || removedRootNodes.has(babylonNode.parent)) {
1435
- scene.nodes.push(glTFNodeIndex);
1436
- }
1437
- }
1438
- if (babylonNode instanceof Mesh) {
1439
- if (babylonNode.skeleton) {
1440
- glTFNode.skin = skinMap[babylonNode.skeleton.uniqueId];
1441
- }
1442
- }
1443
- directDescendents = babylonNode.getDescendants(true);
1444
- if (!glTFNode.children && directDescendents && directDescendents.length) {
1445
- const children = [];
1446
- for (const descendent of directDescendents) {
1447
- if (this._nodeMap[descendent.uniqueId] != null) {
1448
- children.push(this._nodeMap[descendent.uniqueId]);
1449
- }
1450
- }
1451
- if (children.length) {
1452
- glTFNode.children = children;
1453
- }
1454
- }
1455
- }
1456
- }
1457
- if (scene.nodes.length) {
1458
- this._scenes.push(scene);
1459
- }
1460
- });
1461
- });
1462
- });
903
+ }
1463
904
  }
1464
905
  /**
1465
- * Getting the nodes and materials that would be exported.
1466
- * @param nodes Babylon transform nodes
1467
- * @returns Set of materials which would be exported.
906
+ * Processes a node to be exported to the glTF file
907
+ * @returns A promise that resolves with the node index when the processing is complete, or null if the node should not be exported
908
+ * @internal
1468
909
  */
1469
- _getExportNodes(nodes) {
1470
- const exportNodes = [];
1471
- const exportMaterials = new Set();
1472
- for (const babylonNode of nodes) {
1473
- if (!this._options.shouldExportNode || this._options.shouldExportNode(babylonNode)) {
1474
- exportNodes.push(babylonNode);
1475
- const babylonMesh = babylonNode;
910
+ async _exportNodeAsync(babylonNode, state) {
911
+ let nodeIndex = this._nodeMap.get(babylonNode);
912
+ if (nodeIndex !== undefined) {
913
+ return nodeIndex;
914
+ }
915
+ const node = {};
916
+ if (babylonNode.name) {
917
+ node.name = babylonNode.name;
918
+ }
919
+ if (babylonNode instanceof TransformNode) {
920
+ this._setNodeTransformation(node, babylonNode, state.convertToRightHanded);
921
+ if (babylonNode instanceof Mesh || babylonNode instanceof InstancedMesh) {
922
+ const babylonMesh = babylonNode instanceof Mesh ? babylonNode : babylonNode.sourceMesh;
1476
923
  if (babylonMesh.subMeshes && babylonMesh.subMeshes.length > 0) {
1477
- const material = babylonMesh.material || babylonMesh.getScene().defaultMaterial;
1478
- if (material instanceof MultiMaterial) {
1479
- for (const subMaterial of material.subMaterials) {
1480
- if (subMaterial) {
1481
- exportMaterials.add(subMaterial);
1482
- }
924
+ node.mesh = await this._exportMeshAsync(babylonMesh, state);
925
+ }
926
+ if (babylonNode.skeleton) {
927
+ const skin = this._skinMap.get(babylonNode.skeleton);
928
+ if (skin !== undefined) {
929
+ if (this._nodesSkinMap.get(skin) === undefined) {
930
+ this._nodesSkinMap.set(skin, []);
1483
931
  }
1484
- }
1485
- else {
1486
- exportMaterials.add(material);
932
+ this._nodesSkinMap.get(skin)?.push(node);
1487
933
  }
1488
934
  }
1489
935
  }
1490
- else {
1491
- `Excluding node ${babylonNode.name}`;
936
+ }
937
+ if (babylonNode instanceof Camera) {
938
+ const gltfCamera = this._camerasMap.get(babylonNode);
939
+ if (gltfCamera) {
940
+ if (this._nodesCameraMap.get(gltfCamera) === undefined) {
941
+ this._nodesCameraMap.set(gltfCamera, []);
942
+ }
943
+ const parentBabylonNode = babylonNode.parent;
944
+ this._setCameraTransformation(node, babylonNode, state.convertToRightHanded, parentBabylonNode);
945
+ // If a camera has a node that was added by the GLTF importer, we can just use the parent node transform as the "camera" transform.
946
+ if (parentBabylonNode && IsParentAddedByImporter(babylonNode, parentBabylonNode)) {
947
+ const parentNodeIndex = this._nodeMap.get(parentBabylonNode);
948
+ if (parentNodeIndex) {
949
+ const parentNode = this._nodes[parentNodeIndex];
950
+ this._nodesCameraMap.get(gltfCamera)?.push(parentNode);
951
+ return null; // Skip exporting this node
952
+ }
953
+ }
954
+ else {
955
+ if (state.convertToRightHanded) {
956
+ ConvertToRightHandedNode(node);
957
+ RotateNode180Y(node);
958
+ }
959
+ this._nodesCameraMap.get(gltfCamera)?.push(node);
960
+ }
1492
961
  }
1493
962
  }
1494
- return [exportNodes, exportMaterials];
1495
- }
1496
- /**
1497
- * Creates a mapping of Node unique id to node index and handles animations
1498
- * @param nodes Babylon transform nodes
1499
- * @param binaryWriter Buffer to write binary data to
1500
- * @returns Node mapping of unique id to index
1501
- */
1502
- _createNodeMapAndAnimationsAsync(nodes, binaryWriter) {
1503
- let promiseChain = Promise.resolve();
1504
- const nodeMap = {};
1505
- let nodeIndex;
963
+ // Apply extensions to the node. If this resolves to null, it means we should skip exporting this node (NOTE: This will also skip its children)
964
+ const processedNode = await this._extensionsPostExportNodeAsync("exportNodeAsync", node, babylonNode, this._nodeMap, state.convertToRightHanded);
965
+ if (!processedNode) {
966
+ Logger.Warn(`Not exporting node ${babylonNode.name}`);
967
+ return null;
968
+ }
969
+ nodeIndex = this._nodes.length;
970
+ this._nodes.push(node);
971
+ this._nodeMap.set(babylonNode, nodeIndex);
972
+ state.pushExportedNode(babylonNode);
973
+ // Process node's animations once the node has been added to nodeMap (TODO: This should be refactored)
1506
974
  const runtimeGLTFAnimation = {
1507
975
  name: "runtime animations",
1508
976
  channels: [],
1509
977
  samplers: [],
1510
978
  };
1511
979
  const idleGLTFAnimations = [];
1512
- for (const babylonNode of nodes) {
1513
- promiseChain = promiseChain.then(() => {
1514
- return this._createNodeAsync(babylonNode, binaryWriter).then((node) => {
1515
- const promise = this._extensionsPostExportNodeAsync("createNodeAsync", node, babylonNode, nodeMap, binaryWriter);
1516
- if (promise == null) {
1517
- Tools.Warn(`Not exporting node ${babylonNode.name}`);
1518
- return Promise.resolve();
1519
- }
1520
- else {
1521
- return promise.then((node) => {
1522
- if (!node) {
1523
- return;
1524
- }
1525
- this._nodes.push(node);
1526
- nodeIndex = this._nodes.length - 1;
1527
- nodeMap[babylonNode.uniqueId] = nodeIndex;
1528
- if (!this._babylonScene.animationGroups.length) {
1529
- _GLTFAnimation._CreateMorphTargetAnimationFromMorphTargetAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, this._animationSampleRate, this._options.shouldExportAnimation);
1530
- if (babylonNode.animations.length) {
1531
- _GLTFAnimation._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, nodeMap, this._nodes, binaryWriter, this._bufferViews, this._accessors, this._animationSampleRate, this._options.shouldExportAnimation);
1532
- }
1533
- }
1534
- });
1535
- }
1536
- });
1537
- });
1538
- }
1539
- return promiseChain.then(() => {
1540
- if (runtimeGLTFAnimation.channels.length && runtimeGLTFAnimation.samplers.length) {
1541
- this._animations.push(runtimeGLTFAnimation);
980
+ if (!this._babylonScene.animationGroups.length) {
981
+ _GLTFAnimation._CreateMorphTargetAnimationFromMorphTargetAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, this._nodeMap, this._nodes, this._dataWriter, this._bufferViews, this._accessors, this._animationSampleRate, state.convertToRightHanded, this._options.shouldExportAnimation);
982
+ if (babylonNode.animations.length) {
983
+ _GLTFAnimation._CreateNodeAnimationFromNodeAnimations(babylonNode, runtimeGLTFAnimation, idleGLTFAnimations, this._nodeMap, this._nodes, this._dataWriter, this._bufferViews, this._accessors, this._animationSampleRate, state.convertToRightHanded, this._options.shouldExportAnimation);
1542
984
  }
1543
- idleGLTFAnimations.forEach((idleGLTFAnimation) => {
1544
- if (idleGLTFAnimation.channels.length && idleGLTFAnimation.samplers.length) {
1545
- this._animations.push(idleGLTFAnimation);
1546
- }
1547
- });
1548
- if (this._babylonScene.animationGroups.length) {
1549
- _GLTFAnimation._CreateNodeAndMorphAnimationFromAnimationGroups(this._babylonScene, this._animations, nodeMap, binaryWriter, this._bufferViews, this._accessors, this._animationSampleRate, this._options.shouldExportAnimation);
985
+ }
986
+ if (runtimeGLTFAnimation.channels.length && runtimeGLTFAnimation.samplers.length) {
987
+ this._animations.push(runtimeGLTFAnimation);
988
+ }
989
+ idleGLTFAnimations.forEach((idleGLTFAnimation) => {
990
+ if (idleGLTFAnimation.channels.length && idleGLTFAnimation.samplers.length) {
991
+ this._animations.push(idleGLTFAnimation);
1550
992
  }
1551
- return nodeMap;
1552
993
  });
1553
- }
1554
- /**
1555
- * Creates a glTF node from a Babylon mesh
1556
- * @param babylonNode Source Babylon mesh
1557
- * @param binaryWriter Buffer for storing geometry data
1558
- * @returns glTF node
1559
- */
1560
- _createNodeAsync(babylonNode, binaryWriter) {
1561
- return Promise.resolve().then(() => {
1562
- // create node to hold translation/rotation/scale and the mesh
1563
- const node = {};
1564
- // create mesh
1565
- const mesh = { primitives: [] };
1566
- if (babylonNode.name) {
1567
- node.name = babylonNode.name;
1568
- }
1569
- if (babylonNode instanceof TransformNode) {
1570
- // Set transformation
1571
- this._setNodeTransformation(node, babylonNode);
1572
- if (babylonNode instanceof Mesh) {
1573
- const morphTargetManager = babylonNode.morphTargetManager;
1574
- if (morphTargetManager && morphTargetManager.numTargets > 0) {
1575
- mesh.weights = [];
1576
- for (let i = 0; i < morphTargetManager.numTargets; ++i) {
1577
- mesh.weights.push(morphTargetManager.getTarget(i).influence);
1578
- }
1579
- }
994
+ // Begin processing child nodes once parent has been added to the node list
995
+ for (const babylonChildNode of babylonNode.getChildren()) {
996
+ if (this._shouldExportNode(babylonChildNode)) {
997
+ const childNodeIndex = await this._exportNodeAsync(babylonChildNode, state);
998
+ if (childNodeIndex !== null) {
999
+ node.children || (node.children = []);
1000
+ node.children.push(childNodeIndex);
1580
1001
  }
1581
- return this._setPrimitiveAttributesAsync(mesh, babylonNode, binaryWriter).then(() => {
1582
- if (mesh.primitives.length) {
1583
- this._meshes.push(mesh);
1584
- node.mesh = this._meshes.length - 1;
1585
- }
1586
- return node;
1587
- });
1588
1002
  }
1589
- else if (babylonNode instanceof Camera) {
1590
- this._setCameraTransformation(node, babylonNode);
1591
- return node;
1003
+ }
1004
+ return nodeIndex;
1005
+ }
1006
+ _exportIndices(indices, start, count, offset, fillMode, sideOrientation, state, primitive) {
1007
+ const is32Bits = AreIndices32Bits(indices, count);
1008
+ let indicesToExport = indices;
1009
+ primitive.mode = GetPrimitiveMode(fillMode);
1010
+ // Flip if triangle winding order is not CCW as glTF is always CCW.
1011
+ const invertedMaterial = sideOrientation !== Material.CounterClockWiseSideOrientation;
1012
+ const flipWhenInvertedMaterial = !state.wasAddedByNoopNode && invertedMaterial;
1013
+ const flip = IsTriangleFillMode(fillMode) && flipWhenInvertedMaterial;
1014
+ if (flip) {
1015
+ if (fillMode === Material.TriangleStripDrawMode || fillMode === Material.TriangleFanDrawMode) {
1016
+ throw new Error("Triangle strip/fan fill mode is not implemented");
1017
+ }
1018
+ primitive.mode = GetPrimitiveMode(fillMode);
1019
+ const newIndices = is32Bits ? new Uint32Array(count) : new Uint16Array(count);
1020
+ if (indices) {
1021
+ for (let i = 0; i + 2 < count; i += 3) {
1022
+ newIndices[i] = indices[start + i] + offset;
1023
+ newIndices[i + 1] = indices[start + i + 2] + offset;
1024
+ newIndices[i + 2] = indices[start + i + 1] + offset;
1025
+ }
1592
1026
  }
1593
1027
  else {
1594
- return node;
1595
- }
1596
- });
1597
- }
1598
- /**
1599
- * Creates a glTF skin from a Babylon skeleton
1600
- * @param nodeMap Babylon transform nodes
1601
- * @param binaryWriter Buffer to write binary data to
1602
- * @returns Node mapping of unique id to index
1603
- */
1604
- _createSkinsAsync(nodeMap, binaryWriter) {
1605
- const promiseChain = Promise.resolve();
1606
- const skinMap = {};
1607
- for (const skeleton of this._babylonScene.skeletons) {
1608
- if (skeleton.bones.length <= 0) {
1609
- continue;
1610
- }
1611
- // create skin
1612
- const skin = { joints: [] };
1613
- const inverseBindMatrices = [];
1614
- const boneIndexMap = {};
1615
- let maxBoneIndex = -1;
1616
- for (let i = 0; i < skeleton.bones.length; ++i) {
1617
- const bone = skeleton.bones[i];
1618
- const boneIndex = bone.getIndex() ?? i;
1619
- if (boneIndex !== -1) {
1620
- boneIndexMap[boneIndex] = bone;
1621
- if (boneIndex > maxBoneIndex) {
1622
- maxBoneIndex = boneIndex;
1623
- }
1028
+ for (let i = 0; i + 2 < count; i += 3) {
1029
+ newIndices[i] = i;
1030
+ newIndices[i + 1] = i + 2;
1031
+ newIndices[i + 2] = i + 1;
1624
1032
  }
1625
1033
  }
1626
- for (let boneIndex = 0; boneIndex <= maxBoneIndex; ++boneIndex) {
1627
- const bone = boneIndexMap[boneIndex];
1628
- inverseBindMatrices.push(bone.getInvertedAbsoluteTransform());
1629
- const transformNode = bone.getTransformNode();
1630
- if (transformNode && nodeMap[transformNode.uniqueId] !== null && nodeMap[transformNode.uniqueId] !== undefined) {
1631
- skin.joints.push(nodeMap[transformNode.uniqueId]);
1632
- }
1633
- else {
1634
- Tools.Warn("Exporting a bone without a linked transform node is currently unsupported");
1635
- }
1034
+ indicesToExport = newIndices;
1035
+ }
1036
+ else if (indices && offset !== 0) {
1037
+ const newIndices = is32Bits ? new Uint32Array(count) : new Uint16Array(count);
1038
+ for (let i = 0; i < count; i++) {
1039
+ newIndices[i] = indices[start + i] + offset;
1636
1040
  }
1637
- if (skin.joints.length > 0) {
1638
- // create buffer view for inverse bind matrices
1639
- const byteStride = 64; // 4 x 4 matrix of 32 bit float
1640
- const byteLength = inverseBindMatrices.length * byteStride;
1641
- const bufferViewOffset = binaryWriter.getByteOffset();
1642
- const bufferView = _GLTFUtilities._CreateBufferView(0, bufferViewOffset, byteLength, undefined, "InverseBindMatrices" + " - " + skeleton.name);
1643
- this._bufferViews.push(bufferView);
1041
+ indicesToExport = newIndices;
1042
+ }
1043
+ if (indicesToExport) {
1044
+ let accessorIndex = state.getIndicesAccessor(indices, start, count, offset, flip);
1045
+ if (accessorIndex === undefined) {
1046
+ const bufferViewByteOffset = this._dataWriter.byteOffset;
1047
+ const bytes = IndicesArrayToUint8Array(indicesToExport, start, count, is32Bits);
1048
+ this._dataWriter.writeUint8Array(bytes);
1049
+ this._bufferViews.push(CreateBufferView(0, bufferViewByteOffset, bytes.length));
1644
1050
  const bufferViewIndex = this._bufferViews.length - 1;
1645
- const bindMatrixAccessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, "InverseBindMatrices" + " - " + skeleton.name, "MAT4" /* AccessorType.MAT4 */, 5126 /* AccessorComponentType.FLOAT */, inverseBindMatrices.length, null, null, null);
1646
- const inverseBindAccessorIndex = this._accessors.push(bindMatrixAccessor) - 1;
1647
- skin.inverseBindMatrices = inverseBindAccessorIndex;
1648
- this._skins.push(skin);
1649
- skinMap[skeleton.uniqueId] = this._skins.length - 1;
1650
- inverseBindMatrices.forEach((mat) => {
1651
- mat.m.forEach((cell) => {
1652
- binaryWriter.setFloat32(cell);
1653
- });
1654
- });
1051
+ const componentType = is32Bits ? 5125 /* AccessorComponentType.UNSIGNED_INT */ : 5123 /* AccessorComponentType.UNSIGNED_SHORT */;
1052
+ this._accessors.push(CreateAccessor(bufferViewIndex, "SCALAR" /* AccessorType.SCALAR */, componentType, count, 0));
1053
+ accessorIndex = this._accessors.length - 1;
1054
+ state.setIndicesAccessor(indices, start, count, offset, flip, accessorIndex);
1655
1055
  }
1056
+ primitive.indices = accessorIndex;
1656
1057
  }
1657
- return promiseChain.then(() => {
1658
- return skinMap;
1659
- });
1660
- }
1661
- }
1662
- _Exporter._ExtensionNames = new Array();
1663
- _Exporter._ExtensionFactories = {};
1664
- /**
1665
- * @internal
1666
- *
1667
- * Stores glTF binary data. If the array buffer byte length is exceeded, it doubles in size dynamically
1668
- */
1669
- export class _BinaryWriter {
1670
- /**
1671
- * Initialize binary writer with an initial byte length
1672
- * @param byteLength Initial byte length of the array buffer
1673
- */
1674
- constructor(byteLength) {
1675
- this._arrayBuffer = new ArrayBuffer(byteLength);
1676
- this._dataView = new DataView(this._arrayBuffer);
1677
- this._byteOffset = 0;
1678
- }
1679
- /**
1680
- * Resize the array buffer to the specified byte length
1681
- * @param byteLength The new byte length
1682
- * @returns The resized array buffer
1683
- */
1684
- _resizeBuffer(byteLength) {
1685
- const newBuffer = new ArrayBuffer(byteLength);
1686
- const copyOldBufferSize = Math.min(this._arrayBuffer.byteLength, byteLength);
1687
- const oldUint8Array = new Uint8Array(this._arrayBuffer, 0, copyOldBufferSize);
1688
- const newUint8Array = new Uint8Array(newBuffer);
1689
- newUint8Array.set(oldUint8Array, 0);
1690
- this._arrayBuffer = newBuffer;
1691
- this._dataView = new DataView(this._arrayBuffer);
1692
- return newBuffer;
1693
- }
1694
- /**
1695
- * Get an array buffer with the length of the byte offset
1696
- * @returns ArrayBuffer resized to the byte offset
1697
- */
1698
- getArrayBuffer() {
1699
- return this._resizeBuffer(this.getByteOffset());
1700
1058
  }
1701
- /**
1702
- * Get the byte offset of the array buffer
1703
- * @returns byte offset
1704
- */
1705
- getByteOffset() {
1706
- if (this._byteOffset == undefined) {
1707
- throw new Error("Byte offset is undefined!");
1708
- }
1709
- return this._byteOffset;
1710
- }
1711
- /**
1712
- * Stores an UInt8 in the array buffer
1713
- * @param entry
1714
- * @param byteOffset If defined, specifies where to set the value as an offset.
1715
- */
1716
- setUInt8(entry, byteOffset) {
1717
- if (byteOffset != null) {
1718
- if (byteOffset < this._byteOffset) {
1719
- this._dataView.setUint8(byteOffset, entry);
1720
- }
1721
- else {
1722
- Tools.Error("BinaryWriter: byteoffset is greater than the current binary buffer length!");
1723
- }
1059
+ _exportVertexBuffer(vertexBuffer, babylonMaterial, start, count, state, primitive) {
1060
+ const kind = vertexBuffer.getKind();
1061
+ if (!IsStandardVertexAttribute(kind)) {
1062
+ return;
1724
1063
  }
1725
- else {
1726
- if (this._byteOffset + 1 > this._arrayBuffer.byteLength) {
1727
- this._resizeBuffer(this._arrayBuffer.byteLength * 2);
1064
+ if (kind.startsWith("uv") && !this._options.exportUnusedUVs) {
1065
+ if (!babylonMaterial || !this._materialNeedsUVsSet.has(babylonMaterial)) {
1066
+ return;
1728
1067
  }
1729
- this._dataView.setUint8(this._byteOffset, entry);
1730
- this._byteOffset += 1;
1731
1068
  }
1732
- }
1733
- /**
1734
- * Stores an UInt16 in the array buffer
1735
- * @param entry
1736
- * @param byteOffset If defined, specifies where to set the value as an offset.
1737
- */
1738
- setUInt16(entry, byteOffset) {
1739
- if (byteOffset != null) {
1740
- if (byteOffset < this._byteOffset) {
1741
- this._dataView.setUint16(byteOffset, entry, true);
1069
+ let accessorIndex = state.getVertexAccessor(vertexBuffer, start, count);
1070
+ if (accessorIndex === undefined) {
1071
+ // Get min/max from converted or original data.
1072
+ const data = state.convertedToRightHandedBuffers.get(vertexBuffer._buffer) || vertexBuffer._buffer.getData();
1073
+ const minMax = kind === VertexBuffer.PositionKind ? GetMinMax(data, vertexBuffer, start, count) : null;
1074
+ if ((kind === VertexBuffer.MatricesIndicesKind || kind === VertexBuffer.MatricesIndicesExtraKind) && vertexBuffer.type === VertexBuffer.FLOAT) {
1075
+ const bufferViewIndex = state.getRemappedBufferView(vertexBuffer._buffer, vertexBuffer);
1076
+ if (bufferViewIndex !== undefined) {
1077
+ const byteOffset = vertexBuffer.byteOffset + start * vertexBuffer.byteStride;
1078
+ this._accessors.push(CreateAccessor(bufferViewIndex, GetAccessorType(kind, state.hasVertexColorAlpha(vertexBuffer)), VertexBuffer.UNSIGNED_BYTE, count, byteOffset, minMax));
1079
+ accessorIndex = this._accessors.length - 1;
1080
+ state.setVertexAccessor(vertexBuffer, start, count, accessorIndex);
1081
+ primitive.attributes[GetAttributeType(kind)] = accessorIndex;
1082
+ }
1742
1083
  }
1743
1084
  else {
1744
- Tools.Error("BinaryWriter: byteoffset is greater than the current binary buffer length!");
1085
+ const bufferViewIndex = state.getVertexBufferView(vertexBuffer._buffer);
1086
+ const byteOffset = vertexBuffer.byteOffset + start * vertexBuffer.byteStride;
1087
+ 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.
1088
+ ));
1089
+ accessorIndex = this._accessors.length - 1;
1090
+ state.setVertexAccessor(vertexBuffer, start, count, accessorIndex);
1091
+ primitive.attributes[GetAttributeType(kind)] = accessorIndex;
1745
1092
  }
1746
1093
  }
1747
1094
  else {
1748
- if (this._byteOffset + 2 > this._arrayBuffer.byteLength) {
1749
- this._resizeBuffer(this._arrayBuffer.byteLength * 2);
1750
- }
1751
- this._dataView.setUint16(this._byteOffset, entry, true);
1752
- this._byteOffset += 2;
1095
+ primitive.attributes[GetAttributeType(kind)] = accessorIndex;
1753
1096
  }
1754
1097
  }
1755
- /**
1756
- * Gets an UInt32 in the array buffer
1757
- * @param byteOffset If defined, specifies where to set the value as an offset.
1758
- * @returns entry
1759
- */
1760
- getUInt32(byteOffset) {
1761
- if (byteOffset < this._byteOffset) {
1762
- return this._dataView.getUint32(byteOffset, true);
1763
- }
1764
- else {
1765
- Tools.Error("BinaryWriter: byteoffset is greater than the current binary buffer length!");
1766
- throw new Error("BinaryWriter: byteoffset is greater than the current binary buffer length!");
1767
- }
1768
- }
1769
- getVector3Float32FromRef(vector3, byteOffset) {
1770
- if (byteOffset + 8 > this._byteOffset) {
1771
- Tools.Error(`BinaryWriter: byteoffset is greater than the current binary buffer length!`);
1772
- }
1773
- else {
1774
- vector3.x = this._dataView.getFloat32(byteOffset, true);
1775
- vector3.y = this._dataView.getFloat32(byteOffset + 4, true);
1776
- vector3.z = this._dataView.getFloat32(byteOffset + 8, true);
1777
- }
1778
- }
1779
- setVector3Float32FromRef(vector3, byteOffset) {
1780
- if (byteOffset + 8 > this._byteOffset) {
1781
- Tools.Error(`BinaryWriter: byteoffset is greater than the current binary buffer length!`);
1782
- }
1783
- else {
1784
- this._dataView.setFloat32(byteOffset, vector3.x, true);
1785
- this._dataView.setFloat32(byteOffset + 4, vector3.y, true);
1786
- this._dataView.setFloat32(byteOffset + 8, vector3.z, true);
1787
- }
1788
- }
1789
- getVector4Float32FromRef(vector4, byteOffset) {
1790
- if (byteOffset + 12 > this._byteOffset) {
1791
- Tools.Error(`BinaryWriter: byteoffset is greater than the current binary buffer length!`);
1792
- }
1793
- else {
1794
- vector4.x = this._dataView.getFloat32(byteOffset, true);
1795
- vector4.y = this._dataView.getFloat32(byteOffset + 4, true);
1796
- vector4.z = this._dataView.getFloat32(byteOffset + 8, true);
1797
- vector4.w = this._dataView.getFloat32(byteOffset + 12, true);
1798
- }
1799
- }
1800
- setVector4Float32FromRef(vector4, byteOffset) {
1801
- if (byteOffset + 12 > this._byteOffset) {
1802
- Tools.Error(`BinaryWriter: byteoffset is greater than the current binary buffer length!`);
1803
- }
1804
- else {
1805
- this._dataView.setFloat32(byteOffset, vector4.x, true);
1806
- this._dataView.setFloat32(byteOffset + 4, vector4.y, true);
1807
- this._dataView.setFloat32(byteOffset + 8, vector4.z, true);
1808
- this._dataView.setFloat32(byteOffset + 12, vector4.w, true);
1809
- }
1810
- }
1811
- /**
1812
- * Stores a Float32 in the array buffer
1813
- * @param entry
1814
- * @param byteOffset
1815
- */
1816
- setFloat32(entry, byteOffset) {
1817
- if (isNaN(entry)) {
1818
- Tools.Error("Invalid data being written!");
1819
- }
1820
- if (byteOffset != null) {
1821
- if (byteOffset < this._byteOffset) {
1822
- this._dataView.setFloat32(byteOffset, entry, true);
1098
+ async _exportMaterialAsync(babylonMaterial, vertexBuffers, subMesh, primitive) {
1099
+ let materialIndex = this._materialMap.get(babylonMaterial);
1100
+ if (materialIndex === undefined) {
1101
+ const hasUVs = vertexBuffers && Object.keys(vertexBuffers).some((kind) => kind.startsWith("uv"));
1102
+ babylonMaterial = babylonMaterial instanceof MultiMaterial ? babylonMaterial.subMaterials[subMesh.materialIndex] : babylonMaterial;
1103
+ if (babylonMaterial instanceof PBRMaterial) {
1104
+ materialIndex = await this._materialExporter.exportPBRMaterialAsync(babylonMaterial, "image/png" /* ImageMimeType.PNG */, hasUVs);
1823
1105
  }
1824
- else {
1825
- Tools.Error("BinaryWriter: byteoffset is greater than the current binary length!");
1826
- }
1827
- }
1828
- if (this._byteOffset + 4 > this._arrayBuffer.byteLength) {
1829
- this._resizeBuffer(this._arrayBuffer.byteLength * 2);
1830
- }
1831
- this._dataView.setFloat32(this._byteOffset, entry, true);
1832
- this._byteOffset += 4;
1833
- }
1834
- /**
1835
- * Stores an UInt32 in the array buffer
1836
- * @param entry
1837
- * @param byteOffset If defined, specifies where to set the value as an offset.
1838
- */
1839
- setUInt32(entry, byteOffset) {
1840
- if (byteOffset != null) {
1841
- if (byteOffset < this._byteOffset) {
1842
- this._dataView.setUint32(byteOffset, entry, true);
1106
+ else if (babylonMaterial instanceof StandardMaterial) {
1107
+ materialIndex = await this._materialExporter.exportStandardMaterialAsync(babylonMaterial, "image/png" /* ImageMimeType.PNG */, hasUVs);
1843
1108
  }
1844
1109
  else {
1845
- Tools.Error("BinaryWriter: byteoffset is greater than the current binary buffer length!");
1846
- }
1847
- }
1848
- else {
1849
- if (this._byteOffset + 4 > this._arrayBuffer.byteLength) {
1850
- this._resizeBuffer(this._arrayBuffer.byteLength * 2);
1110
+ Logger.Warn(`Unsupported material '${babylonMaterial.name}' with type ${babylonMaterial.getClassName()}`);
1111
+ return;
1851
1112
  }
1852
- this._dataView.setUint32(this._byteOffset, entry, true);
1853
- this._byteOffset += 4;
1113
+ this._materialMap.set(babylonMaterial, materialIndex);
1854
1114
  }
1115
+ primitive.material = materialIndex;
1855
1116
  }
1856
- /**
1857
- * Stores an Int16 in the array buffer
1858
- * @param entry
1859
- * @param byteOffset If defined, specifies where to set the value as an offset.
1860
- */
1861
- setInt16(entry, byteOffset) {
1862
- if (byteOffset != null) {
1863
- if (byteOffset < this._byteOffset) {
1864
- this._dataView.setInt16(byteOffset, entry, true);
1865
- }
1866
- else {
1867
- Tools.Error("BinaryWriter: byteoffset is greater than the current binary buffer length!");
1868
- }
1117
+ async _exportMeshAsync(babylonMesh, state) {
1118
+ let meshIndex = state.getMesh(babylonMesh);
1119
+ if (meshIndex !== undefined) {
1120
+ return meshIndex;
1869
1121
  }
1870
- else {
1871
- if (this._byteOffset + 2 > this._arrayBuffer.byteLength) {
1872
- this._resizeBuffer(this._arrayBuffer.byteLength * 2);
1122
+ const mesh = { primitives: [] };
1123
+ meshIndex = this._meshes.length;
1124
+ this._meshes.push(mesh);
1125
+ state.setMesh(babylonMesh, meshIndex);
1126
+ const indices = babylonMesh.isUnIndexed ? null : babylonMesh.getIndices();
1127
+ const vertexBuffers = babylonMesh.geometry?.getVertexBuffers();
1128
+ const morphTargets = state.getMorphTargetsFromMesh(babylonMesh);
1129
+ let isLinesMesh = false;
1130
+ if (babylonMesh instanceof LinesMesh) {
1131
+ isLinesMesh = true;
1132
+ }
1133
+ const subMeshes = babylonMesh.subMeshes;
1134
+ if (vertexBuffers && subMeshes && subMeshes.length > 0) {
1135
+ for (const subMesh of subMeshes) {
1136
+ const primitive = { attributes: {} };
1137
+ const babylonMaterial = subMesh.getMaterial() || this._babylonScene.defaultMaterial;
1138
+ // Special case for LinesMesh
1139
+ if (isLinesMesh) {
1140
+ const material = {
1141
+ name: babylonMaterial.name,
1142
+ };
1143
+ const babylonLinesMesh = babylonMesh;
1144
+ if (!babylonLinesMesh.color.equals(Color3.White()) || babylonLinesMesh.alpha < 1) {
1145
+ material.pbrMetallicRoughness = {
1146
+ baseColorFactor: [...babylonLinesMesh.color.asArray(), babylonLinesMesh.alpha],
1147
+ };
1148
+ }
1149
+ this._materials.push(material);
1150
+ primitive.material = this._materials.length - 1;
1151
+ }
1152
+ else {
1153
+ // Material
1154
+ await this._exportMaterialAsync(babylonMaterial, vertexBuffers, subMesh, primitive);
1155
+ }
1156
+ // Index buffer
1157
+ const fillMode = isLinesMesh ? Material.LineListDrawMode : (babylonMesh.overrideRenderingFillMode ?? babylonMaterial.fillMode);
1158
+ const sideOrientation = babylonMaterial._getEffectiveOrientation(babylonMesh);
1159
+ this._exportIndices(indices, subMesh.indexStart, subMesh.indexCount, -subMesh.verticesStart, fillMode, sideOrientation, state, primitive);
1160
+ // Vertex buffers
1161
+ for (const vertexBuffer of Object.values(vertexBuffers)) {
1162
+ this._exportVertexBuffer(vertexBuffer, babylonMaterial, subMesh.verticesStart, subMesh.verticesCount, state, primitive);
1163
+ }
1164
+ mesh.primitives.push(primitive);
1165
+ if (morphTargets) {
1166
+ primitive.targets = [];
1167
+ for (const gltfMorphTarget of morphTargets) {
1168
+ primitive.targets.push(gltfMorphTarget.attributes);
1169
+ }
1170
+ }
1873
1171
  }
1874
- this._dataView.setInt16(this._byteOffset, entry, true);
1875
- this._byteOffset += 2;
1876
1172
  }
1877
- }
1878
- /**
1879
- * Stores a byte in the array buffer
1880
- * @param entry
1881
- * @param byteOffset If defined, specifies where to set the value as an offset.
1882
- */
1883
- setByte(entry, byteOffset) {
1884
- if (byteOffset != null) {
1885
- if (byteOffset < this._byteOffset) {
1886
- this._dataView.setInt8(byteOffset, entry);
1887
- }
1888
- else {
1889
- Tools.Error("BinaryWriter: byteoffset is greater than the current binary buffer length!");
1173
+ if (morphTargets) {
1174
+ mesh.weights = [];
1175
+ if (!mesh.extras) {
1176
+ mesh.extras = {};
1890
1177
  }
1891
- }
1892
- else {
1893
- if (this._byteOffset + 1 > this._arrayBuffer.byteLength) {
1894
- this._resizeBuffer(this._arrayBuffer.byteLength * 2);
1178
+ mesh.extras.targetNames = [];
1179
+ for (const gltfMorphTarget of morphTargets) {
1180
+ mesh.weights.push(gltfMorphTarget.influence);
1181
+ mesh.extras.targetNames.push(gltfMorphTarget.name);
1895
1182
  }
1896
- this._dataView.setInt8(this._byteOffset, entry);
1897
- this._byteOffset++;
1898
1183
  }
1184
+ return meshIndex;
1899
1185
  }
1900
1186
  }
1187
+ GLTFExporter._ExtensionNames = new Array();
1188
+ GLTFExporter._ExtensionFactories = {};
1901
1189
  //# sourceMappingURL=glTFExporter.js.map