@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.
- package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.d.ts +6 -7
- package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.js +18 -12
- package/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_lights_punctual.d.ts +5 -6
- package/glTF/2.0/Extensions/KHR_lights_punctual.js +99 -109
- package/glTF/2.0/Extensions/KHR_lights_punctual.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_anisotropy.d.ts +2 -2
- package/glTF/2.0/Extensions/KHR_materials_anisotropy.js +6 -6
- package/glTF/2.0/Extensions/KHR_materials_anisotropy.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_clearcoat.d.ts +2 -2
- package/glTF/2.0/Extensions/KHR_materials_clearcoat.js +9 -9
- package/glTF/2.0/Extensions/KHR_materials_clearcoat.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.d.ts +2 -2
- package/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.js +7 -7
- package/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_dispersion.js +2 -2
- package/glTF/2.0/Extensions/KHR_materials_dispersion.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_emissive_strength.js +2 -2
- package/glTF/2.0/Extensions/KHR_materials_emissive_strength.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_ior.js +2 -2
- package/glTF/2.0/Extensions/KHR_materials_ior.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_iridescence.d.ts +2 -2
- package/glTF/2.0/Extensions/KHR_materials_iridescence.js +7 -7
- package/glTF/2.0/Extensions/KHR_materials_iridescence.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_sheen.d.ts +2 -2
- package/glTF/2.0/Extensions/KHR_materials_sheen.js +8 -8
- package/glTF/2.0/Extensions/KHR_materials_sheen.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_specular.d.ts +2 -2
- package/glTF/2.0/Extensions/KHR_materials_specular.js +7 -7
- package/glTF/2.0/Extensions/KHR_materials_specular.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_transmission.d.ts +2 -2
- package/glTF/2.0/Extensions/KHR_materials_transmission.js +6 -6
- package/glTF/2.0/Extensions/KHR_materials_transmission.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_unlit.js +2 -2
- package/glTF/2.0/Extensions/KHR_materials_unlit.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_materials_volume.d.ts +2 -2
- package/glTF/2.0/Extensions/KHR_materials_volume.js +6 -6
- package/glTF/2.0/Extensions/KHR_materials_volume.js.map +1 -1
- package/glTF/2.0/Extensions/KHR_texture_transform.js +2 -2
- package/glTF/2.0/Extensions/KHR_texture_transform.js.map +1 -1
- package/glTF/2.0/Extensions/index.d.ts +9 -9
- package/glTF/2.0/Extensions/index.js +9 -9
- package/glTF/2.0/Extensions/index.js.map +1 -1
- package/glTF/2.0/dataWriter.d.ts +18 -0
- package/glTF/2.0/dataWriter.js +65 -0
- package/glTF/2.0/dataWriter.js.map +1 -0
- package/glTF/2.0/glTFAnimation.d.ts +4 -10
- package/glTF/2.0/glTFAnimation.js +87 -19
- package/glTF/2.0/glTFAnimation.js.map +1 -1
- package/glTF/2.0/glTFData.d.ts +5 -3
- package/glTF/2.0/glTFData.js +35 -36
- package/glTF/2.0/glTFData.js.map +1 -1
- package/glTF/2.0/glTFExporter.d.ts +62 -363
- package/glTF/2.0/glTFExporter.js +867 -1579
- package/glTF/2.0/glTFExporter.js.map +1 -1
- package/glTF/2.0/glTFExporterExtension.d.ts +8 -9
- package/glTF/2.0/glTFExporterExtension.js.map +1 -1
- package/glTF/2.0/glTFMaterialExporter.d.ts +26 -126
- package/glTF/2.0/glTFMaterialExporter.js +276 -438
- package/glTF/2.0/glTFMaterialExporter.js.map +1 -1
- package/glTF/2.0/glTFMorphTargetsUtilities.d.ts +14 -0
- package/glTF/2.0/glTFMorphTargetsUtilities.js +105 -0
- package/glTF/2.0/glTFMorphTargetsUtilities.js.map +1 -0
- package/glTF/2.0/glTFSerializer.d.ts +10 -13
- package/glTF/2.0/glTFSerializer.js +25 -44
- package/glTF/2.0/glTFSerializer.js.map +1 -1
- package/glTF/2.0/glTFUtilities.d.ts +75 -43
- package/glTF/2.0/glTFUtilities.js +350 -103
- package/glTF/2.0/glTFUtilities.js.map +1 -1
- package/package.json +3 -3
package/glTF/2.0/glTFExporter.js
CHANGED
@@ -1,97 +1,133 @@
|
|
1
|
-
import {
|
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 {
|
12
|
-
import {
|
9
|
+
import { EngineStore } from "@babylonjs/core/Engines/engineStore.js";
|
10
|
+
import { GLTFMaterialExporter } from "./glTFMaterialExporter.js";
|
13
11
|
import { GLTFData } from "./glTFData.js";
|
14
|
-
import {
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
}
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
}
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
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
|
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
|
116
|
-
return this._applyExtensions(meshPrimitive, (extension, node) => extension.postExportMeshPrimitiveAsync && extension.postExportMeshPrimitiveAsync(context, node, babylonSubMesh
|
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,
|
119
|
-
return this._applyExtensions(node, (extension, node) => extension.postExportNodeAsync && extension.postExportNodeAsync(context, node, babylonNode, nodeMap,
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
181
|
-
const extension =
|
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
|
-
|
196
|
-
if (!babylonScene) {
|
197
|
-
return;
|
198
|
-
}
|
199
|
-
this._babylonScene = babylonScene;
|
200
|
-
this._bufferViews = [];
|
217
|
+
this._animations = [];
|
201
218
|
this._accessors = [];
|
202
|
-
this.
|
203
|
-
this._scenes = [];
|
219
|
+
this._bufferViews = [];
|
204
220
|
this._cameras = [];
|
205
|
-
this._nodes = [];
|
206
221
|
this._images = [];
|
207
222
|
this._materials = [];
|
208
|
-
this.
|
209
|
-
this.
|
223
|
+
this._meshes = [];
|
224
|
+
this._nodes = [];
|
210
225
|
this._samplers = [];
|
226
|
+
this._scenes = [];
|
211
227
|
this._skins = [];
|
212
|
-
this.
|
228
|
+
this._textures = [];
|
213
229
|
this._imageData = {};
|
214
230
|
this._orderedImageData = [];
|
215
|
-
this.
|
216
|
-
this.
|
217
|
-
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
|
222
|
-
const extension = this._extensions[
|
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 (
|
272
|
+
if (GLTFExporter.UnregisterExtension(name)) {
|
236
273
|
Tools.Warn(`Extension with the name ${name} already exists`);
|
237
274
|
}
|
238
|
-
|
239
|
-
|
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 (!
|
279
|
+
if (!GLTFExporter._ExtensionFactories[name]) {
|
248
280
|
return false;
|
249
281
|
}
|
250
|
-
delete
|
251
|
-
const index =
|
282
|
+
delete GLTFExporter._ExtensionFactories[name];
|
283
|
+
const index = GLTFExporter._ExtensionNames.indexOf(name);
|
252
284
|
if (index !== -1) {
|
253
|
-
|
285
|
+
GLTFExporter._ExtensionNames.splice(index, 1);
|
254
286
|
}
|
255
287
|
return true;
|
256
288
|
}
|
257
|
-
|
258
|
-
|
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 =
|
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
|
-
|
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 =
|
357
|
+
buffer.uri = fileName + ".bin";
|
796
358
|
}
|
797
|
-
|
798
|
-
return jsonText;
|
359
|
+
return prettyPrint ? JSON.stringify(this._glTF, null, 2) : JSON.stringify(this._glTF);
|
799
360
|
}
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
const
|
812
|
-
|
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
|
-
|
825
|
-
|
375
|
+
}
|
376
|
+
return container;
|
826
377
|
}
|
827
|
-
|
828
|
-
|
829
|
-
|
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
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
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
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
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
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
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
|
-
|
1019
|
-
|
1020
|
-
|
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
|
-
|
1036
|
-
|
1037
|
-
|
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
|
-
|
1058
|
-
|
1059
|
-
|
581
|
+
const skin = this._skinMap.get(skeleton);
|
582
|
+
if (skin == undefined) {
|
583
|
+
continue;
|
1060
584
|
}
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
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
|
-
|
1066
|
-
|
1067
|
-
|
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
|
-
|
1070
|
-
|
1071
|
-
|
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
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
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
|
-
|
1111
|
-
|
1112
|
-
break;
|
649
|
+
else if (this._babylonScene.metadata.gltf) {
|
650
|
+
scene.extras = this._babylonScene.metadata.gltf.extras;
|
1113
651
|
}
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
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
|
-
|
1119
|
-
|
1120
|
-
break;
|
664
|
+
else if (this._babylonScene.useRightHandedSystem) {
|
665
|
+
rootNodesRH.push(rootNode);
|
1121
666
|
}
|
1122
|
-
|
1123
|
-
|
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
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
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
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
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
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
const
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
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
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
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
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
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
|
-
|
1232
|
-
|
801
|
+
if (type == VertexBuffer.UNSIGNED_BYTE) {
|
802
|
+
Logger.Warn("Converting uint8 vertex colors to linear space. Results may look incorrect.");
|
1233
803
|
}
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
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
|
-
|
1246
|
-
|
1247
|
-
|
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
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
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
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
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
|
-
|
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
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
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
|
-
|
1370
|
-
const
|
1371
|
-
|
1372
|
-
if (
|
1373
|
-
|
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
|
-
|
1382
|
-
|
1383
|
-
|
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
|
-
|
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
|
-
*
|
1466
|
-
* @
|
1467
|
-
* @
|
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
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
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
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
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
|
-
|
1491
|
-
|
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
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
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
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
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
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
if (
|
1549
|
-
|
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
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
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
|
-
|
1590
|
-
|
1591
|
-
|
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
|
-
|
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
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
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
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
const
|
1643
|
-
|
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
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
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
|
-
|
1703
|
-
|
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
|
-
|
1726
|
-
if (
|
1727
|
-
|
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
|
-
|
1735
|
-
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1757
|
-
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
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
|
-
|
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
|
-
|
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.
|
1853
|
-
this._byteOffset += 4;
|
1113
|
+
this._materialMap.set(babylonMaterial, materialIndex);
|
1854
1114
|
}
|
1115
|
+
primitive.material = materialIndex;
|
1855
1116
|
}
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
|
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
|
-
|
1871
|
-
|
1872
|
-
|
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
|
-
|
1880
|
-
|
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
|
-
|
1893
|
-
|
1894
|
-
|
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
|