@combeenation/3d-viewer 18.2.0 → 18.3.0
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/dist/lib-cjs/buildinfo.json +1 -1
- package/dist/lib-cjs/commonjs.tsconfig.tsbuildinfo +1 -1
- package/dist/lib-cjs/index.d.ts +1 -0
- package/dist/lib-cjs/index.js +1 -0
- package/dist/lib-cjs/index.js.map +1 -1
- package/dist/lib-cjs/internal/export-helper.d.ts +34 -0
- package/dist/lib-cjs/internal/export-helper.js +306 -0
- package/dist/lib-cjs/internal/export-helper.js.map +1 -0
- package/dist/lib-cjs/internal/node-helper.js +2 -2
- package/dist/lib-cjs/internal/node-helper.js.map +1 -1
- package/dist/lib-cjs/manager/camera-manager.js +2 -6
- package/dist/lib-cjs/manager/camera-manager.js.map +1 -1
- package/dist/lib-cjs/manager/dxf-export-manager.d.ts +39 -0
- package/dist/lib-cjs/manager/dxf-export-manager.js +116 -0
- package/dist/lib-cjs/manager/dxf-export-manager.js.map +1 -0
- package/dist/lib-cjs/manager/gltf-export-manager.d.ts +19 -39
- package/dist/lib-cjs/manager/gltf-export-manager.js +38 -179
- package/dist/lib-cjs/manager/gltf-export-manager.js.map +1 -1
- package/dist/lib-cjs/viewer.d.ts +3 -1
- package/dist/lib-cjs/viewer.js +4 -0
- package/dist/lib-cjs/viewer.js.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +1 -0
- package/src/internal/export-helper.ts +378 -0
- package/src/internal/node-helper.ts +2 -2
- package/src/manager/camera-manager.ts +2 -6
- package/src/manager/dxf-export-manager.ts +123 -0
- package/src/manager/gltf-export-manager.ts +38 -231
- package/src/viewer.ts +6 -0
- package/dist/lib-cjs/internal/geometry-helper.d.ts +0 -14
- package/dist/lib-cjs/internal/geometry-helper.js +0 -114
- package/dist/lib-cjs/internal/geometry-helper.js.map +0 -1
- package/src/internal/geometry-helper.ts +0 -144
|
@@ -1,27 +1,25 @@
|
|
|
1
|
+
import { Animation, GLTF2Export, IExportOptions, Node, NodeDescription, Viewer } from '../index';
|
|
1
2
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
setInternalMetadataValue,
|
|
23
|
-
} from '../internal/metadata-helper';
|
|
24
|
-
import { nodeMatchesAnyCriteria } from '../internal/node-helper';
|
|
3
|
+
exportPostProcess,
|
|
4
|
+
exportPreProcess,
|
|
5
|
+
isExportableTransformNode,
|
|
6
|
+
normalizeFileName,
|
|
7
|
+
} from '../internal/export-helper';
|
|
8
|
+
import { getInternalMetadataValue } from '../internal/metadata-helper';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Interface TODOS
|
|
12
|
+
* ===============
|
|
13
|
+
* - create settings object instead of individual input parameters (optimizeForAR, excludeNodes, fileName)
|
|
14
|
+
* - see `DxfExportManager`
|
|
15
|
+
* - much more flexible when adding new parameters
|
|
16
|
+
* - rename functions to create/download, instead of suffixing "ToFile"
|
|
17
|
+
* - also see `DxfExportManager`
|
|
18
|
+
*
|
|
19
|
+
* Viewer control is directly accessing these functions, so introducing breaking changes here is really annoying, as we
|
|
20
|
+
* have to add another version switch in the source code of the viewer control.
|
|
21
|
+
* Maybe there is a chance to update the interface when re-creating the viewer control in the new cfgr editor.
|
|
22
|
+
*/
|
|
25
23
|
|
|
26
24
|
/**
|
|
27
25
|
* Manager for gltf export and augmented reality features
|
|
@@ -43,7 +41,7 @@ export class GltfExportManager {
|
|
|
43
41
|
return !!getInternalMetadataValue(node, 'exportNode');
|
|
44
42
|
} else {
|
|
45
43
|
// use the default export node check (enabled state, exclusion list, etc...)
|
|
46
|
-
return
|
|
44
|
+
return isExportableTransformNode(node, excludeNodes);
|
|
47
45
|
}
|
|
48
46
|
},
|
|
49
47
|
|
|
@@ -51,64 +49,6 @@ export class GltfExportManager {
|
|
|
51
49
|
};
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
/**
|
|
55
|
-
* Checks if a node should be available in the export
|
|
56
|
-
*/
|
|
57
|
-
protected static _shouldExportNode(node: Node, excludeNodes?: NodeDescription[]): boolean {
|
|
58
|
-
if (!(node instanceof TransformNode)) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// maybe add some other criterias like "hasInfiniteDistance" and "isGeneratedBackgroundMesh" here as well
|
|
63
|
-
const isExcluded = nodeMatchesAnyCriteria(node, {
|
|
64
|
-
isInList: excludeNodes,
|
|
65
|
-
isDisabled: true,
|
|
66
|
-
isHtmlAnchorMesh: true,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
return !isExcluded;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Creates a clone of the material which should be used for the export.
|
|
74
|
-
* This is mostly required for recreating textures with lower sizes.
|
|
75
|
-
* CAUTION: Material exchanging is not supported for materials that contain certain texture types:
|
|
76
|
-
* - Dynamic textures (Paintables): Cloning dynamic textures doesn't clone the canvas context
|
|
77
|
-
* => so the clone is just empty
|
|
78
|
-
* - Render target textures: Disposing the clone will leave the scene in a "not ready" state
|
|
79
|
-
* => this scenario is not fully analyzed yet, but it's not really worth the effort right now, since this kind of
|
|
80
|
-
* of texture is not really used ATM
|
|
81
|
-
*/
|
|
82
|
-
protected static _exchangeMaterial(material: Material): void {
|
|
83
|
-
const baseTextures = material.getActiveTextures();
|
|
84
|
-
const hasDynamicTextures = baseTextures.some(texture => texture instanceof DynamicTexture);
|
|
85
|
-
const hasRenderTargetTextures = baseTextures.some(texture => texture instanceof RenderTargetTexture);
|
|
86
|
-
if (hasDynamicTextures || hasRenderTargetTextures) {
|
|
87
|
-
const textureTypesString = [
|
|
88
|
-
hasDynamicTextures ? 'Dynamic Textures' : '',
|
|
89
|
-
hasRenderTargetTextures ? 'Render Target Textures' : '',
|
|
90
|
-
]
|
|
91
|
-
.filter(Boolean)
|
|
92
|
-
.join();
|
|
93
|
-
console.warn(
|
|
94
|
-
`Couldn't exchange material "${material.name}" in GLB export, as it contains unsupported texture type(s) (${textureTypesString}). The export will still work, but the textures of this material will keep their original size.`
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const newName = `${material.name}_clone`;
|
|
101
|
-
const clonedMaterial = material.clone(newName)!;
|
|
102
|
-
cloneInternalMetadata(material, clonedMaterial);
|
|
103
|
-
const clonedTextures = clonedMaterial.getActiveTextures();
|
|
104
|
-
|
|
105
|
-
// mark all exported textures, so that they will be deleted after the export
|
|
106
|
-
clonedTextures.forEach(texture => setInternalMetadataValue(texture, 'deleteAfterExport', true));
|
|
107
|
-
|
|
108
|
-
setInternalMetadataValue(material, 'exchangeMaterialWith', clonedMaterial.uniqueId);
|
|
109
|
-
setInternalMetadataValue(clonedMaterial, 'deleteAfterExport', true);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
52
|
/** @internal */
|
|
113
53
|
public constructor(protected viewer: Viewer) {
|
|
114
54
|
// store initial max texture size, so that we can restore it in the post processing
|
|
@@ -117,17 +57,17 @@ export class GltfExportManager {
|
|
|
117
57
|
|
|
118
58
|
/**
|
|
119
59
|
* Exports selected nodes to a file.
|
|
120
|
-
* @param
|
|
60
|
+
* @param fileName Optional name of the exported .GLB file.
|
|
121
61
|
* @param optimizeForAR Adjusts the exported GLB so that known issues get automatically fixed, this
|
|
122
62
|
* is mostly targeting Apples .usdz format.
|
|
123
63
|
* @param excludeNodes Optional list of nodes to be excluded from the export.
|
|
124
64
|
*/
|
|
125
65
|
public async exportGlb(
|
|
126
|
-
|
|
66
|
+
fileName?: string,
|
|
127
67
|
optimizeForAR: boolean = false,
|
|
128
68
|
excludeNodes?: NodeDescription[]
|
|
129
69
|
): Promise<File | undefined> {
|
|
130
|
-
await this.
|
|
70
|
+
if (optimizeForAR) await exportPreProcess(this.viewer, { excludeNodes, scaleDownTextures: true });
|
|
131
71
|
|
|
132
72
|
const glbData = await GLTF2Export.GLBAsync(
|
|
133
73
|
this.viewer.scene,
|
|
@@ -135,15 +75,13 @@ export class GltfExportManager {
|
|
|
135
75
|
GltfExportManager._gltfExportOptions(optimizeForAR, excludeNodes)
|
|
136
76
|
);
|
|
137
77
|
|
|
138
|
-
await this.
|
|
78
|
+
if (optimizeForAR) await exportPostProcess(this.viewer);
|
|
139
79
|
|
|
140
80
|
const resBlob = glbData.glTFFiles['dummy.glb'];
|
|
141
81
|
// check if result is valid, according to the typings this could also be a string
|
|
142
82
|
if (resBlob instanceof Blob) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
return new File([resBlob], filename);
|
|
83
|
+
const normalizedFileName = normalizeFileName(fileName, 'glb', 'glb-export');
|
|
84
|
+
return new File([resBlob], normalizedFileName);
|
|
147
85
|
} else {
|
|
148
86
|
// result was not a valid blob
|
|
149
87
|
return undefined;
|
|
@@ -158,175 +96,44 @@ export class GltfExportManager {
|
|
|
158
96
|
* @param excludeNodes Optional list of nodes to be excluded from the export.
|
|
159
97
|
*/
|
|
160
98
|
public async exportGltfToFile(
|
|
161
|
-
|
|
99
|
+
fileName: string,
|
|
162
100
|
optimizeForAR: boolean = false,
|
|
163
101
|
excludeNodes?: NodeDescription[]
|
|
164
102
|
): Promise<void> {
|
|
165
|
-
await this.
|
|
103
|
+
if (optimizeForAR) await exportPreProcess(this.viewer, { excludeNodes, scaleDownTextures: true });
|
|
166
104
|
|
|
167
105
|
const gltf = await GLTF2Export.GLTFAsync(
|
|
168
106
|
this.viewer.scene,
|
|
169
|
-
|
|
107
|
+
// no need to add .glb, as Babylon.js does that internally
|
|
108
|
+
fileName,
|
|
170
109
|
GltfExportManager._gltfExportOptions(optimizeForAR, excludeNodes)
|
|
171
110
|
);
|
|
172
111
|
gltf.downloadFiles();
|
|
173
112
|
|
|
174
|
-
|
|
113
|
+
if (optimizeForAR) await exportPostProcess(this.viewer);
|
|
175
114
|
}
|
|
176
115
|
|
|
177
116
|
/**
|
|
178
117
|
* Exports selected nodes to GLB. This results in one binary file.
|
|
179
|
-
* @param
|
|
118
|
+
* @param fileName Name of the main (text-based) .GLTF file referring to seperate texture files.
|
|
180
119
|
* @param optimizeForAR Adjusts the exported GLB so that known issues get automatically fixed, this
|
|
181
120
|
* is mostly targeting Apples .usdz format.
|
|
182
121
|
* @param excludeNodes Optional list of nodes to be excluded from the export.
|
|
183
122
|
*/
|
|
184
123
|
public async exportGlbToFile(
|
|
185
|
-
|
|
124
|
+
fileName: string,
|
|
186
125
|
optimizeForAR: boolean = false,
|
|
187
126
|
excludeNodes?: NodeDescription[]
|
|
188
127
|
): Promise<void> {
|
|
189
|
-
await this.
|
|
128
|
+
if (optimizeForAR) await exportPreProcess(this.viewer, { excludeNodes, scaleDownTextures: true });
|
|
190
129
|
|
|
191
130
|
const glb = await GLTF2Export.GLBAsync(
|
|
192
131
|
this.viewer.scene,
|
|
193
|
-
|
|
132
|
+
fileName,
|
|
194
133
|
GltfExportManager._gltfExportOptions(optimizeForAR, excludeNodes)
|
|
195
134
|
);
|
|
196
135
|
glb.downloadFiles();
|
|
197
136
|
|
|
198
|
-
await this.
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Prepares scene for GLB export.
|
|
203
|
-
* This is very important for AR exports, since we have to do a lot of conversions to satisfy Apples .usdz format.
|
|
204
|
-
*/
|
|
205
|
-
protected async _exportPreProcess(optimizeForAR: boolean, excludeNodes?: NodeDescription[]): Promise<void> {
|
|
206
|
-
if (!optimizeForAR) {
|
|
207
|
-
// actually nothing to do if AR optimization is not required
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// pause rendering, since we are altering the active scene, which should not be visible to the user
|
|
212
|
-
this.viewer.pauseRendering();
|
|
213
|
-
|
|
214
|
-
// exchange materials if required
|
|
215
|
-
// the AR export should never contain textures larger than 1024 px:
|
|
216
|
-
// - iOS devices will crash most likely when trying to access AR endpoints with such files
|
|
217
|
-
// - file size will be reduced, which leads to faster loading times
|
|
218
|
-
// - textures > 1024 px shouldn't make a difference on mobiles anyway
|
|
219
|
-
// we don't have to rescale anything if are already on a downscaled device, since the textures are already <= 1024
|
|
220
|
-
// also we have to be very cautios with copying textures on these devices, since we are potentially very limited
|
|
221
|
-
// with the available memory
|
|
222
|
-
const isScaledDownDevice = getIsScaledDownDevice(this.viewer.viewerSettings.limitTextureSize);
|
|
223
|
-
if (!isScaledDownDevice) {
|
|
224
|
-
// the idea is to re-create all textures with a smaller texture size
|
|
225
|
-
// we have to exchange all materials for this to work
|
|
226
|
-
this.viewer.engine.clearInternalTexturesCache();
|
|
227
|
-
this.viewer.engine.getCaps().maxTextureSize = 1024;
|
|
228
|
-
|
|
229
|
-
this.viewer.scene.materials
|
|
230
|
-
.filter(material => material instanceof PBRMaterial)
|
|
231
|
-
.forEach(material => GltfExportManager._exchangeMaterial(material as PBRMaterial));
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// create clones of each node (recursively), optionally exchange with cloned materials and mark these nodes for the
|
|
235
|
-
// export
|
|
236
|
-
this.viewer.scene.rootNodes
|
|
237
|
-
// .filter(rootNode => rootNode.name !== GltfExportManager._EXPORT_ROOT_NAME)
|
|
238
|
-
.forEach(rootNode => this._prepareNodeForExport(rootNode, null, excludeNodes));
|
|
239
|
-
|
|
240
|
-
// bake transformation of all meshes, so that no negative scalings are left
|
|
241
|
-
// it's important that this is done AFTER instanced meshes have been converted (_prepareNodeForExport)
|
|
242
|
-
this._getNodesMarkedForExport(true).forEach(mesh => bakeGeometryOfMesh(mesh as Mesh));
|
|
243
|
-
|
|
244
|
-
// reset transformation of all "TransformNodes", which couldn't be handled by the geometry baking algorithm
|
|
245
|
-
// it's important that this is done AFTER all geometries have been baked
|
|
246
|
-
this._getNodesMarkedForExport().forEach(node => resetTransformation(node as TransformNode));
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Cleans up scene after export
|
|
251
|
-
*/
|
|
252
|
-
protected async _exportPostProcess(optimizeForAR: boolean): Promise<void> {
|
|
253
|
-
if (!optimizeForAR) {
|
|
254
|
-
// nothing to do, since no changes have been made without AR optimizations
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// dispose all nodes, materials and textures that have only been created for the export
|
|
259
|
-
this.viewer.scene.rootNodes
|
|
260
|
-
.filter(rootNode => getInternalMetadataValue(rootNode, 'deleteAfterExport'))
|
|
261
|
-
.forEach(rootNode => rootNode.dispose());
|
|
262
|
-
|
|
263
|
-
this.viewer.scene.materials
|
|
264
|
-
.filter(material => getInternalMetadataValue(material, 'deleteAfterExport'))
|
|
265
|
-
.forEach(material => material.dispose(false, false));
|
|
266
|
-
// clean up temporary material exchange key for the rest of materials
|
|
267
|
-
this.viewer.scene.materials.forEach(material => clearInternalMetadataValue(material, 'exchangeMaterialWith'));
|
|
268
|
-
|
|
269
|
-
this.viewer.scene.textures
|
|
270
|
-
.filter(texture => getInternalMetadataValue(texture, 'deleteAfterExport'))
|
|
271
|
-
.forEach(texture => texture.dispose());
|
|
272
|
-
|
|
273
|
-
// reset engines max texture size and resume rendering
|
|
274
|
-
this.viewer.engine.getCaps().maxTextureSize = this._maxTextureSize;
|
|
275
|
-
this.viewer.resumeRendering();
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Creates a clone of the node which should be used for the export.
|
|
280
|
-
* Also switches to the cloned material if required.
|
|
281
|
-
*/
|
|
282
|
-
protected _prepareNodeForExport(
|
|
283
|
-
node: Node,
|
|
284
|
-
clonedParent: TransformNode | null,
|
|
285
|
-
excludeNodes?: NodeDescription[]
|
|
286
|
-
): void {
|
|
287
|
-
if (!GltfExportManager._shouldExportNode(node, excludeNodes)) {
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// from here on we only have TransformNodes
|
|
292
|
-
const transformNode = node as TransformNode;
|
|
293
|
-
|
|
294
|
-
// clone original node and create unique name (via uniqueId) for the export
|
|
295
|
-
const clonedNodeName = `${transformNode.name}_${transformNode.uniqueId}`;
|
|
296
|
-
const clonedNode =
|
|
297
|
-
transformNode instanceof InstancedMesh
|
|
298
|
-
? createMeshFromInstancedMesh(transformNode, clonedNodeName, clonedParent)
|
|
299
|
-
: transformNode.clone(clonedNodeName, clonedParent, true)!;
|
|
300
|
-
cloneInternalMetadata(transformNode, clonedNode);
|
|
301
|
-
|
|
302
|
-
// exchange material
|
|
303
|
-
if (clonedNode instanceof Mesh) {
|
|
304
|
-
const exchangeWithMaterial =
|
|
305
|
-
clonedNode.material && getInternalMetadataValue(clonedNode.material, 'exchangeMaterialWith');
|
|
306
|
-
if (exchangeWithMaterial) {
|
|
307
|
-
clonedNode.material = this.viewer.scene.getMaterialByUniqueID(exchangeWithMaterial as number);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// signalize that this is a cloned node
|
|
312
|
-
setInternalMetadataValue(clonedNode, 'exportNode', true);
|
|
313
|
-
setInternalMetadataValue(clonedNode, 'deleteAfterExport', true);
|
|
314
|
-
|
|
315
|
-
// handle children
|
|
316
|
-
const childs = transformNode.getChildTransformNodes(true);
|
|
317
|
-
childs.forEach(child => this._prepareNodeForExport(child, clonedNode, excludeNodes));
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Help function for receiving all nodes that are marked for the export
|
|
322
|
-
*/
|
|
323
|
-
protected _getNodesMarkedForExport(meshesOnly?: boolean): TransformNode[] {
|
|
324
|
-
const nodes: TransformNode[] = [...this.viewer.scene.meshes];
|
|
325
|
-
if (!meshesOnly) {
|
|
326
|
-
nodes.push(...this.viewer.scene.transformNodes);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const filteredNodes = nodes.filter(node => getInternalMetadataValue(node, 'exportNode'));
|
|
330
|
-
return filteredNodes;
|
|
137
|
+
if (optimizeForAR) await exportPostProcess(this.viewer);
|
|
331
138
|
}
|
|
332
139
|
}
|
package/src/viewer.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
DebugManager,
|
|
7
7
|
DefaultSceneSettings,
|
|
8
8
|
DimensionLineManager,
|
|
9
|
+
DxfExportManager,
|
|
9
10
|
Engine,
|
|
10
11
|
EngineOptions,
|
|
11
12
|
EventManager,
|
|
@@ -84,6 +85,7 @@ export class Viewer {
|
|
|
84
85
|
protected _cameraManager!: CameraManager;
|
|
85
86
|
protected _debugManager!: DebugManager;
|
|
86
87
|
protected _dimensionLineManager!: DimensionLineManager;
|
|
88
|
+
protected _dxfExportManager!: DxfExportManager;
|
|
87
89
|
protected _eventManager!: EventManager;
|
|
88
90
|
protected _gltfExportManager!: GltfExportManager;
|
|
89
91
|
protected _htmlAnchorManager!: HtmlAnchorManager;
|
|
@@ -127,6 +129,9 @@ export class Viewer {
|
|
|
127
129
|
get dimensionLineManager(): DimensionLineManager {
|
|
128
130
|
return this._dimensionLineManager;
|
|
129
131
|
}
|
|
132
|
+
get dxfExportManager(): DxfExportManager {
|
|
133
|
+
return this._dxfExportManager;
|
|
134
|
+
}
|
|
130
135
|
get eventManager(): EventManager {
|
|
131
136
|
return this._eventManager;
|
|
132
137
|
}
|
|
@@ -249,6 +254,7 @@ export class Viewer {
|
|
|
249
254
|
this._cameraManager = new CameraManager(this);
|
|
250
255
|
this._debugManager = new DebugManager(this);
|
|
251
256
|
this._dimensionLineManager = new DimensionLineManager(this);
|
|
257
|
+
this._dxfExportManager = new DxfExportManager(this);
|
|
252
258
|
this._eventManager = new EventManager(this);
|
|
253
259
|
this._gltfExportManager = new GltfExportManager(this);
|
|
254
260
|
this._htmlAnchorManager = new HtmlAnchorManager(this);
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { InstancedMesh, Mesh, TransformNode } from '../index';
|
|
2
|
-
/**
|
|
3
|
-
* Creates a "standard" mesh from an instanced mesh, by cloning the source mesh and applying transformation data
|
|
4
|
-
*/
|
|
5
|
-
export declare function createMeshFromInstancedMesh(instancedMesh: InstancedMesh, newName: string, clonedParent: TransformNode | null): Mesh;
|
|
6
|
-
/**
|
|
7
|
-
* Removes transformation data from the mesh and stores it in the geometry, which is called "baking".
|
|
8
|
-
* Also considers the geometry change from morph targets.
|
|
9
|
-
*/
|
|
10
|
-
export declare function bakeGeometryOfMesh(mesh: Mesh): void;
|
|
11
|
-
/**
|
|
12
|
-
* Resets transformation to initial state
|
|
13
|
-
*/
|
|
14
|
-
export declare function resetTransformation(node: TransformNode): void;
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resetTransformation = exports.bakeGeometryOfMesh = exports.createMeshFromInstancedMesh = void 0;
|
|
4
|
-
const index_1 = require("../index");
|
|
5
|
-
const metadata_helper_1 = require("./metadata-helper");
|
|
6
|
-
/**
|
|
7
|
-
* Creates a "standard" mesh from an instanced mesh, by cloning the source mesh and applying transformation data
|
|
8
|
-
*/
|
|
9
|
-
function createMeshFromInstancedMesh(instancedMesh, newName, clonedParent) {
|
|
10
|
-
// first create a clone of the source mesh
|
|
11
|
-
const newMesh = instancedMesh.sourceMesh.clone(newName, clonedParent, true);
|
|
12
|
-
(0, metadata_helper_1.cloneInternalMetadata)(instancedMesh.sourceMesh, newMesh);
|
|
13
|
-
// apply the transformation data, it's important to create clones of the transformations to not touch the original
|
|
14
|
-
// transformation when applying changes (eg: geometry baking)
|
|
15
|
-
newMesh.position = instancedMesh.position.clone();
|
|
16
|
-
newMesh.rotation = instancedMesh.rotation.clone();
|
|
17
|
-
newMesh.scaling = instancedMesh.scaling.clone();
|
|
18
|
-
// rotation quaternion is optional
|
|
19
|
-
if (instancedMesh.rotationQuaternion) {
|
|
20
|
-
newMesh.rotationQuaternion = instancedMesh.rotationQuaternion.clone();
|
|
21
|
-
}
|
|
22
|
-
// also sync the enabled state from the original instanced mesh
|
|
23
|
-
newMesh.setEnabled(instancedMesh.isEnabled(false));
|
|
24
|
-
return newMesh;
|
|
25
|
-
}
|
|
26
|
-
exports.createMeshFromInstancedMesh = createMeshFromInstancedMesh;
|
|
27
|
-
/**
|
|
28
|
-
* Removes transformation data from the mesh and stores it in the geometry, which is called "baking".
|
|
29
|
-
* Also considers the geometry change from morph targets.
|
|
30
|
-
*/
|
|
31
|
-
function bakeGeometryOfMesh(mesh) {
|
|
32
|
-
if (!mesh.geometry) {
|
|
33
|
-
// no geometry available, nothing to do
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
// geometries can be shared across multiple meshes, first make them unique to avoid side-effects
|
|
37
|
-
mesh.makeGeometryUnique();
|
|
38
|
-
// Babylon.js already provides a function for baking the current skeleton changes into the geometry
|
|
39
|
-
if (mesh.skeleton) {
|
|
40
|
-
mesh.applySkeleton(mesh.skeleton);
|
|
41
|
-
mesh.skeleton = null;
|
|
42
|
-
}
|
|
43
|
-
// NOTE: in difference to skeletons and transformations there is no baking function for morph targets (yet)
|
|
44
|
-
// however another approach could be to re-apply the position and normals data, as there are nice functions for it
|
|
45
|
-
// - `getPositionData(applySkeleton: boolean = false, applyMorph: boolean = false)`
|
|
46
|
-
// - `getNormalsData(applySkeleton: boolean = false, applyMorph: boolean = false)`
|
|
47
|
-
// you can decide if skeletons and morph targets can be added, which is exactly what we want
|
|
48
|
-
// I'm still hesitant to use it because "tangent" and "UV" kinds are not supported, whereas I'm not sure if it's
|
|
49
|
-
// required
|
|
50
|
-
// => try it out when there is enough time for detailed regression tests!
|
|
51
|
-
const morphTargetManager = mesh.morphTargetManager;
|
|
52
|
-
const geometry = mesh.geometry;
|
|
53
|
-
if (morphTargetManager === null || morphTargetManager === void 0 ? void 0 : morphTargetManager.numTargets) {
|
|
54
|
-
// apply morph target vertices data to mesh geometry
|
|
55
|
-
// mostly only the "PositionKind" is implemented
|
|
56
|
-
_bakeMorphTargetManagerIntoVertices(index_1.VertexBuffer.PositionKind, morphTargetManager, geometry);
|
|
57
|
-
_bakeMorphTargetManagerIntoVertices(index_1.VertexBuffer.NormalKind, morphTargetManager, geometry);
|
|
58
|
-
_bakeMorphTargetManagerIntoVertices(index_1.VertexBuffer.TangentKind, morphTargetManager, geometry);
|
|
59
|
-
_bakeMorphTargetManagerIntoVertices(index_1.VertexBuffer.UVKind, morphTargetManager, geometry);
|
|
60
|
-
// remove morph target manager with all it's morph targets
|
|
61
|
-
mesh.morphTargetManager = null;
|
|
62
|
-
}
|
|
63
|
-
// bake the transformation data in the mesh geometry, fortunately there is already a help function from Babylon.js
|
|
64
|
-
mesh.bakeCurrentTransformIntoVertices();
|
|
65
|
-
}
|
|
66
|
-
exports.bakeGeometryOfMesh = bakeGeometryOfMesh;
|
|
67
|
-
/**
|
|
68
|
-
* Resets transformation to initial state
|
|
69
|
-
*/
|
|
70
|
-
function resetTransformation(node) {
|
|
71
|
-
node.position = new index_1.Vector3(0, 0, 0);
|
|
72
|
-
node.rotation = new index_1.Vector3(0, 0, 0);
|
|
73
|
-
node.rotationQuaternion = null;
|
|
74
|
-
node.scaling = new index_1.Vector3(1, 1, 1);
|
|
75
|
-
}
|
|
76
|
-
exports.resetTransformation = resetTransformation;
|
|
77
|
-
/**
|
|
78
|
-
* @param kind morph targets can affect various vertices kinds, whereas "position" is the most common one
|
|
79
|
-
* still other kinds (like normals or tangents) can be affected as well and can be provided on this input
|
|
80
|
-
*/
|
|
81
|
-
function _bakeMorphTargetManagerIntoVertices(kind, morphTargetManager, geometry) {
|
|
82
|
-
const origVerticesData = geometry.getVerticesData(kind);
|
|
83
|
-
if (!origVerticesData) {
|
|
84
|
-
// no vertices data for this kind availabe on the mesh geometry
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
let verticesData = [...origVerticesData];
|
|
88
|
-
for (let i = 0; i < morphTargetManager.numTargets; i++) {
|
|
89
|
-
const target = morphTargetManager.getTarget(i);
|
|
90
|
-
const targetVerticesData = _getVerticesDataFromMorphTarget(kind, target);
|
|
91
|
-
if (targetVerticesData) {
|
|
92
|
-
// vertices data of this kind are implemented on the morph target
|
|
93
|
-
// add the influence of this morph target to the vertices data
|
|
94
|
-
// formula is taken from: https://doc.babylonjs.com/features/featuresDeepDive/mesh/morphTargets#basics
|
|
95
|
-
verticesData = verticesData.map((oldVal, idx) => oldVal + (targetVerticesData[idx] - origVerticesData[idx]) * target.influence);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// apply the updated vertices data
|
|
99
|
-
geometry.setVerticesData(kind, verticesData);
|
|
100
|
-
}
|
|
101
|
-
function _getVerticesDataFromMorphTarget(kind, morphTarget) {
|
|
102
|
-
switch (kind) {
|
|
103
|
-
case index_1.VertexBuffer.PositionKind:
|
|
104
|
-
return morphTarget.getPositions();
|
|
105
|
-
case index_1.VertexBuffer.NormalKind:
|
|
106
|
-
return morphTarget.getNormals();
|
|
107
|
-
case index_1.VertexBuffer.TangentKind:
|
|
108
|
-
return morphTarget.getTangents();
|
|
109
|
-
case index_1.VertexBuffer.UVKind:
|
|
110
|
-
return morphTarget.getUVs();
|
|
111
|
-
}
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
//# sourceMappingURL=geometry-helper.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"geometry-helper.js","sourceRoot":"","sources":["../../../src/internal/geometry-helper.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FloatArray,
|
|
3
|
-
Geometry,
|
|
4
|
-
InstancedMesh,
|
|
5
|
-
Mesh,
|
|
6
|
-
MorphTarget,
|
|
7
|
-
MorphTargetManager,
|
|
8
|
-
TransformNode,
|
|
9
|
-
Vector3,
|
|
10
|
-
VertexBuffer,
|
|
11
|
-
} from '../index';
|
|
12
|
-
import { cloneInternalMetadata } from './metadata-helper';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Creates a "standard" mesh from an instanced mesh, by cloning the source mesh and applying transformation data
|
|
16
|
-
*/
|
|
17
|
-
export function createMeshFromInstancedMesh(
|
|
18
|
-
instancedMesh: InstancedMesh,
|
|
19
|
-
newName: string,
|
|
20
|
-
clonedParent: TransformNode | null
|
|
21
|
-
): Mesh {
|
|
22
|
-
// first create a clone of the source mesh
|
|
23
|
-
const newMesh = instancedMesh.sourceMesh.clone(newName, clonedParent, true);
|
|
24
|
-
cloneInternalMetadata(instancedMesh.sourceMesh, newMesh);
|
|
25
|
-
// apply the transformation data, it's important to create clones of the transformations to not touch the original
|
|
26
|
-
// transformation when applying changes (eg: geometry baking)
|
|
27
|
-
newMesh.position = instancedMesh.position.clone();
|
|
28
|
-
newMesh.rotation = instancedMesh.rotation.clone();
|
|
29
|
-
newMesh.scaling = instancedMesh.scaling.clone();
|
|
30
|
-
|
|
31
|
-
// rotation quaternion is optional
|
|
32
|
-
if (instancedMesh.rotationQuaternion) {
|
|
33
|
-
newMesh.rotationQuaternion = instancedMesh.rotationQuaternion.clone();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// also sync the enabled state from the original instanced mesh
|
|
37
|
-
newMesh.setEnabled(instancedMesh.isEnabled(false));
|
|
38
|
-
|
|
39
|
-
return newMesh;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Removes transformation data from the mesh and stores it in the geometry, which is called "baking".
|
|
44
|
-
* Also considers the geometry change from morph targets.
|
|
45
|
-
*/
|
|
46
|
-
export function bakeGeometryOfMesh(mesh: Mesh): void {
|
|
47
|
-
if (!mesh.geometry) {
|
|
48
|
-
// no geometry available, nothing to do
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// geometries can be shared across multiple meshes, first make them unique to avoid side-effects
|
|
53
|
-
mesh.makeGeometryUnique();
|
|
54
|
-
|
|
55
|
-
// Babylon.js already provides a function for baking the current skeleton changes into the geometry
|
|
56
|
-
if (mesh.skeleton) {
|
|
57
|
-
mesh.applySkeleton(mesh.skeleton);
|
|
58
|
-
mesh.skeleton = null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// NOTE: in difference to skeletons and transformations there is no baking function for morph targets (yet)
|
|
62
|
-
// however another approach could be to re-apply the position and normals data, as there are nice functions for it
|
|
63
|
-
// - `getPositionData(applySkeleton: boolean = false, applyMorph: boolean = false)`
|
|
64
|
-
// - `getNormalsData(applySkeleton: boolean = false, applyMorph: boolean = false)`
|
|
65
|
-
// you can decide if skeletons and morph targets can be added, which is exactly what we want
|
|
66
|
-
// I'm still hesitant to use it because "tangent" and "UV" kinds are not supported, whereas I'm not sure if it's
|
|
67
|
-
// required
|
|
68
|
-
// => try it out when there is enough time for detailed regression tests!
|
|
69
|
-
const morphTargetManager = mesh.morphTargetManager;
|
|
70
|
-
const geometry = mesh.geometry;
|
|
71
|
-
|
|
72
|
-
if (morphTargetManager?.numTargets) {
|
|
73
|
-
// apply morph target vertices data to mesh geometry
|
|
74
|
-
// mostly only the "PositionKind" is implemented
|
|
75
|
-
_bakeMorphTargetManagerIntoVertices(VertexBuffer.PositionKind, morphTargetManager, geometry);
|
|
76
|
-
_bakeMorphTargetManagerIntoVertices(VertexBuffer.NormalKind, morphTargetManager, geometry);
|
|
77
|
-
_bakeMorphTargetManagerIntoVertices(VertexBuffer.TangentKind, morphTargetManager, geometry);
|
|
78
|
-
_bakeMorphTargetManagerIntoVertices(VertexBuffer.UVKind, morphTargetManager, geometry);
|
|
79
|
-
|
|
80
|
-
// remove morph target manager with all it's morph targets
|
|
81
|
-
mesh.morphTargetManager = null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// bake the transformation data in the mesh geometry, fortunately there is already a help function from Babylon.js
|
|
85
|
-
mesh.bakeCurrentTransformIntoVertices();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Resets transformation to initial state
|
|
90
|
-
*/
|
|
91
|
-
export function resetTransformation(node: TransformNode): void {
|
|
92
|
-
node.position = new Vector3(0, 0, 0);
|
|
93
|
-
node.rotation = new Vector3(0, 0, 0);
|
|
94
|
-
node.rotationQuaternion = null;
|
|
95
|
-
node.scaling = new Vector3(1, 1, 1);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* @param kind morph targets can affect various vertices kinds, whereas "position" is the most common one
|
|
100
|
-
* still other kinds (like normals or tangents) can be affected as well and can be provided on this input
|
|
101
|
-
*/
|
|
102
|
-
function _bakeMorphTargetManagerIntoVertices(
|
|
103
|
-
kind: string,
|
|
104
|
-
morphTargetManager: MorphTargetManager,
|
|
105
|
-
geometry: Geometry
|
|
106
|
-
): void {
|
|
107
|
-
const origVerticesData = geometry.getVerticesData(kind);
|
|
108
|
-
if (!origVerticesData) {
|
|
109
|
-
// no vertices data for this kind availabe on the mesh geometry
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
let verticesData = [...origVerticesData];
|
|
114
|
-
for (let i = 0; i < morphTargetManager.numTargets; i++) {
|
|
115
|
-
const target = morphTargetManager.getTarget(i);
|
|
116
|
-
const targetVerticesData = _getVerticesDataFromMorphTarget(kind, target);
|
|
117
|
-
if (targetVerticesData) {
|
|
118
|
-
// vertices data of this kind are implemented on the morph target
|
|
119
|
-
// add the influence of this morph target to the vertices data
|
|
120
|
-
// formula is taken from: https://doc.babylonjs.com/features/featuresDeepDive/mesh/morphTargets#basics
|
|
121
|
-
verticesData = verticesData.map(
|
|
122
|
-
(oldVal, idx) => oldVal + (targetVerticesData[idx] - origVerticesData[idx]) * target.influence
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// apply the updated vertices data
|
|
128
|
-
geometry.setVerticesData(kind, verticesData);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function _getVerticesDataFromMorphTarget(kind: string, morphTarget: MorphTarget): FloatArray | null {
|
|
132
|
-
switch (kind) {
|
|
133
|
-
case VertexBuffer.PositionKind:
|
|
134
|
-
return morphTarget.getPositions();
|
|
135
|
-
case VertexBuffer.NormalKind:
|
|
136
|
-
return morphTarget.getNormals();
|
|
137
|
-
case VertexBuffer.TangentKind:
|
|
138
|
-
return morphTarget.getTangents();
|
|
139
|
-
case VertexBuffer.UVKind:
|
|
140
|
-
return morphTarget.getUVs();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return null;
|
|
144
|
-
}
|