@combeenation/3d-viewer 18.1.0 → 18.2.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/internal/cloning-helper.js.map +1 -1
- package/dist/lib-cjs/internal/geometry-helper.d.ts +1 -8
- package/dist/lib-cjs/internal/geometry-helper.js +1 -32
- package/dist/lib-cjs/internal/geometry-helper.js.map +1 -1
- package/dist/lib-cjs/internal/metadata-helper.d.ts +2 -1
- package/dist/lib-cjs/internal/metadata-helper.js.map +1 -1
- package/dist/lib-cjs/internal/node-helper.d.ts +15 -0
- package/dist/lib-cjs/internal/node-helper.js +51 -0
- package/dist/lib-cjs/internal/node-helper.js.map +1 -0
- package/dist/lib-cjs/manager/camera-manager.d.ts +5 -5
- package/dist/lib-cjs/manager/camera-manager.js +5 -7
- package/dist/lib-cjs/manager/camera-manager.js.map +1 -1
- package/dist/lib-cjs/manager/dimension-line-manager.d.ts +6 -0
- package/dist/lib-cjs/manager/dimension-line-manager.js +8 -0
- package/dist/lib-cjs/manager/dimension-line-manager.js.map +1 -1
- package/dist/lib-cjs/manager/gltf-export-manager.d.ts +11 -11
- package/dist/lib-cjs/manager/gltf-export-manager.js +29 -30
- package/dist/lib-cjs/manager/gltf-export-manager.js.map +1 -1
- package/dist/lib-cjs/manager/html-anchor-manager.d.ts +6 -6
- package/dist/lib-cjs/manager/html-anchor-manager.js +10 -8
- package/dist/lib-cjs/manager/html-anchor-manager.js.map +1 -1
- package/dist/lib-cjs/manager/parameter-manager.js +13 -4
- package/dist/lib-cjs/manager/parameter-manager.js.map +1 -1
- package/dist/lib-cjs/manager/scene-manager.d.ts +9 -9
- package/dist/lib-cjs/manager/scene-manager.js +32 -29
- package/dist/lib-cjs/manager/scene-manager.js.map +1 -1
- package/dist/lib-cjs/viewer.d.ts +4 -4
- package/dist/lib-cjs/viewer.js.map +1 -1
- package/package.json +1 -1
- package/src/internal/cloning-helper.ts +1 -1
- package/src/internal/geometry-helper.ts +0 -37
- package/src/internal/metadata-helper.ts +6 -1
- package/src/internal/node-helper.ts +73 -0
- package/src/manager/camera-manager.ts +10 -12
- package/src/manager/dimension-line-manager.ts +9 -0
- package/src/manager/gltf-export-manager.ts +31 -31
- package/src/manager/html-anchor-manager.ts +11 -9
- package/src/manager/parameter-manager.ts +13 -4
- package/src/manager/scene-manager.ts +40 -40
- package/src/viewer.ts +5 -4
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { AbstractMesh, DimensionLineManager, HtmlAnchorManager, ParameterSubject, Tags, TransformNode } from '..';
|
|
2
|
+
|
|
3
|
+
type NodeCheckCriteria = {
|
|
4
|
+
isInList?: (TransformNode | ParameterSubject)[];
|
|
5
|
+
isDisabled?: boolean;
|
|
6
|
+
hasInvalidBoundingInfo?: boolean;
|
|
7
|
+
hasInfiniteDistance?: boolean;
|
|
8
|
+
isGeneratedBackgroundMesh?: boolean;
|
|
9
|
+
isHtmlAnchorMesh?: boolean;
|
|
10
|
+
isDimensionLine?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const _DEFAULT_BACKGROUND_PARENT_NAME = 'BackgroundHelper';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Checks if transform node matches any of the given criterias.
|
|
17
|
+
*/
|
|
18
|
+
export function nodeMatchesAnyCriteria(nodeToCheck: TransformNode, criteria: NodeCheckCriteria): boolean {
|
|
19
|
+
const {
|
|
20
|
+
isInList,
|
|
21
|
+
isDisabled,
|
|
22
|
+
hasInvalidBoundingInfo,
|
|
23
|
+
hasInfiniteDistance,
|
|
24
|
+
isGeneratedBackgroundMesh,
|
|
25
|
+
isHtmlAnchorMesh,
|
|
26
|
+
isDimensionLine,
|
|
27
|
+
} = criteria ?? {};
|
|
28
|
+
|
|
29
|
+
const matchesIsInList = (isInList ?? []).some(geometryToExclude => {
|
|
30
|
+
if (geometryToExclude instanceof TransformNode) {
|
|
31
|
+
// CAUTION: node has to be the same instance
|
|
32
|
+
return geometryToExclude === nodeToCheck;
|
|
33
|
+
}
|
|
34
|
+
if (geometryToExclude.tagName || geometryToExclude.nodeName) {
|
|
35
|
+
const nameMatches = !!geometryToExclude.nodeName && geometryToExclude.nodeName === nodeToCheck.name;
|
|
36
|
+
const tagMatches = !!geometryToExclude.tagName && Tags.MatchesQuery(nodeToCheck, geometryToExclude.tagName);
|
|
37
|
+
return nameMatches || tagMatches;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const isMesh = nodeToCheck instanceof AbstractMesh;
|
|
43
|
+
const matchesIsDisabled = !!isDisabled && !nodeToCheck.isEnabled(false);
|
|
44
|
+
const matchesHasInvalidBoundingInfo =
|
|
45
|
+
!!hasInvalidBoundingInfo && isMesh && nodeToCheck.getBoundingInfo().boundingSphere.radius === 0;
|
|
46
|
+
const matchesHasInfiniteDistance = !!hasInfiniteDistance && nodeToCheck.infiniteDistance;
|
|
47
|
+
// this targets generated ground and skybox meshes from `scene.createDefaultEnvironment`, as they are located
|
|
48
|
+
// underneath `BackgroundHelper`
|
|
49
|
+
const matchesIsGeneratedBackgroundMesh =
|
|
50
|
+
!!isGeneratedBackgroundMesh && nodeToCheck.name === _DEFAULT_BACKGROUND_PARENT_NAME;
|
|
51
|
+
const matchesIsHtmlAnchorMesh = !!isHtmlAnchorMesh && HtmlAnchorManager.isHtmlAnchorMesh(nodeToCheck);
|
|
52
|
+
const matchesIsDimensionLine = !!isDimensionLine && DimensionLineManager.isDimensionLineMesh(nodeToCheck);
|
|
53
|
+
|
|
54
|
+
let matchesAnyCriteria =
|
|
55
|
+
matchesIsInList ||
|
|
56
|
+
matchesIsDisabled ||
|
|
57
|
+
matchesHasInvalidBoundingInfo ||
|
|
58
|
+
matchesHasInfiniteDistance ||
|
|
59
|
+
matchesIsGeneratedBackgroundMesh ||
|
|
60
|
+
matchesIsHtmlAnchorMesh ||
|
|
61
|
+
matchesIsDimensionLine;
|
|
62
|
+
// consider parent as well, BUT ONLY for list and disabled state, as the other criterias have nothing to do with a
|
|
63
|
+
// child-parent relation
|
|
64
|
+
if (!matchesAnyCriteria && nodeToCheck.parent instanceof TransformNode) {
|
|
65
|
+
matchesAnyCriteria = nodeMatchesAnyCriteria(nodeToCheck.parent, {
|
|
66
|
+
isInList,
|
|
67
|
+
isDisabled,
|
|
68
|
+
isGeneratedBackgroundMesh,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return matchesAnyCriteria;
|
|
73
|
+
}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
AnimationGroup,
|
|
4
4
|
ArcRotateCamera,
|
|
5
5
|
BoundingSphere,
|
|
6
|
-
|
|
6
|
+
NodeDescription,
|
|
7
7
|
ScreenshotTools,
|
|
8
8
|
Vector3,
|
|
9
9
|
Viewer,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ViewerErrorIds,
|
|
12
12
|
ViewerEvent,
|
|
13
13
|
} from '../index';
|
|
14
|
-
import {
|
|
14
|
+
import { nodeMatchesAnyCriteria } from '../internal/node-helper';
|
|
15
15
|
import { createScreenshotCanvas, trimCanvas } from '../internal/screenshot-helper';
|
|
16
16
|
|
|
17
17
|
export type CameraPosition = {
|
|
@@ -31,8 +31,8 @@ export type AutofocusSettings = {
|
|
|
31
31
|
alpha?: number;
|
|
32
32
|
/** Desired vertical camera angle, this won't be overwritten by the autofocus function */
|
|
33
33
|
beta?: number;
|
|
34
|
-
/** Optional list of
|
|
35
|
-
|
|
34
|
+
/** Optional list of nodes to be excluded from camera distance calculation for autofocusing */
|
|
35
|
+
excludeNodes?: NodeDescription[];
|
|
36
36
|
durationMs?: number;
|
|
37
37
|
};
|
|
38
38
|
|
|
@@ -62,8 +62,8 @@ export type ScreenshotSettings = {
|
|
|
62
62
|
/** Can be used to automatically calculate camera `radius` and `target`, if these 2 settings are not defined
|
|
63
63
|
* explicitely */
|
|
64
64
|
autofocusScene?: boolean;
|
|
65
|
-
/** Optional list of
|
|
66
|
-
|
|
65
|
+
/** Optional list of nodes to be excluded from screenshot */
|
|
66
|
+
excludeNodes?: NodeDescription[];
|
|
67
67
|
/**
|
|
68
68
|
* Optional list of html anchor groups (see {@link HtmlAnchorManager}), that should be excluded from the screenshot.\
|
|
69
69
|
* Excludes ALL html anchor groups if set to `true`.
|
|
@@ -160,7 +160,7 @@ export class CameraManager {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
// get bounding box of all visible meshes, this is the base for the autofocus algorithm
|
|
163
|
-
const boundingInfo = this.viewer.sceneManager.calculateBoundingInfo(settings?.
|
|
163
|
+
const boundingInfo = this.viewer.sceneManager.calculateBoundingInfo(settings?.excludeNodes);
|
|
164
164
|
// optionally show bounding sphere for debugging purpose
|
|
165
165
|
this.viewer.eventManager.fireEvent(ViewerEvent.AutofocusStart, boundingInfo.boundingSphere);
|
|
166
166
|
|
|
@@ -306,7 +306,7 @@ export class CameraManager {
|
|
|
306
306
|
radius,
|
|
307
307
|
target,
|
|
308
308
|
autofocusScene,
|
|
309
|
-
|
|
309
|
+
excludeNodes,
|
|
310
310
|
excludeHtmlAnchorGroups,
|
|
311
311
|
cropTransparentArea,
|
|
312
312
|
fileName,
|
|
@@ -321,7 +321,7 @@ export class CameraManager {
|
|
|
321
321
|
const screenshotCam = this.viewer.scene.activeCamera?.clone(
|
|
322
322
|
CameraManager._SCREENSHOT_CAMERA_NAME
|
|
323
323
|
) as ArcRotateCamera;
|
|
324
|
-
const boundingInfo = autofocusScene ? this.viewer.sceneManager.calculateBoundingInfo(
|
|
324
|
+
const boundingInfo = autofocusScene ? this.viewer.sceneManager.calculateBoundingInfo(excludeNodes) : undefined;
|
|
325
325
|
|
|
326
326
|
if (alpha !== undefined) {
|
|
327
327
|
screenshotCam.alpha = alpha;
|
|
@@ -363,10 +363,8 @@ export class CameraManager {
|
|
|
363
363
|
useLayerMask,
|
|
364
364
|
quality,
|
|
365
365
|
texture => {
|
|
366
|
-
// NOTE: this doesn't work ATM, see https://github.com/BabylonJS/Babylon.js/pull/16081#issuecomment-2652861176
|
|
367
|
-
// we'll have to wait for a fix or solve the visibility in a different way (e.g. camera layer mask)
|
|
368
366
|
texture.renderList = this.viewer.scene.meshes.filter(
|
|
369
|
-
mesh => !
|
|
367
|
+
mesh => !nodeMatchesAnyCriteria(mesh, { isInList: excludeNodes })
|
|
370
368
|
);
|
|
371
369
|
}
|
|
372
370
|
);
|
|
@@ -82,6 +82,15 @@ export class DimensionLineManager {
|
|
|
82
82
|
};
|
|
83
83
|
} = {};
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Check if input node is a dimension line mesh, created by this manager.\
|
|
87
|
+
* Dimension lines have a dedicated tag set, which is checked here.
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
public static isDimensionLineMesh(node: TransformNode): boolean {
|
|
91
|
+
return Tags.MatchesQuery(node, DimensionLineManager.DIMENSION_LINE_KEY);
|
|
92
|
+
}
|
|
93
|
+
|
|
85
94
|
/** @internal */
|
|
86
95
|
public constructor(protected viewer: Viewer) {}
|
|
87
96
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Animation,
|
|
3
3
|
DynamicTexture,
|
|
4
|
-
ExcludedGeometryList,
|
|
5
4
|
GLTF2Export,
|
|
6
5
|
IExportOptions,
|
|
7
6
|
InstancedMesh,
|
|
8
7
|
Material,
|
|
9
8
|
Mesh,
|
|
10
9
|
Node,
|
|
10
|
+
NodeDescription,
|
|
11
11
|
PBRMaterial,
|
|
12
12
|
RenderTargetTexture,
|
|
13
13
|
TransformNode,
|
|
@@ -15,13 +15,13 @@ import {
|
|
|
15
15
|
} from '../index';
|
|
16
16
|
import { getIsScaledDownDevice } from '../internal/device-helper';
|
|
17
17
|
import { bakeGeometryOfMesh, createMeshFromInstancedMesh, resetTransformation } from '../internal/geometry-helper';
|
|
18
|
-
import { isNodeExcluded } from '../internal/geometry-helper';
|
|
19
18
|
import {
|
|
20
19
|
clearInternalMetadataValue,
|
|
21
20
|
cloneInternalMetadata,
|
|
22
21
|
getInternalMetadataValue,
|
|
23
22
|
setInternalMetadataValue,
|
|
24
23
|
} from '../internal/metadata-helper';
|
|
24
|
+
import { nodeMatchesAnyCriteria } from '../internal/node-helper';
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Manager for gltf export and augmented reality features
|
|
@@ -35,15 +35,15 @@ export class GltfExportManager {
|
|
|
35
35
|
* stay `true` in order to make the AR export work.
|
|
36
36
|
* We could theoretically allow it if AR optimization is not desired, but this may confuse the user.
|
|
37
37
|
*/
|
|
38
|
-
protected static _gltfExportOptions(optimizeForAR: boolean,
|
|
38
|
+
protected static _gltfExportOptions(optimizeForAR: boolean, excludeNodes?: NodeDescription[]): IExportOptions {
|
|
39
39
|
return {
|
|
40
40
|
shouldExportNode: function (node: Node): boolean {
|
|
41
41
|
if (optimizeForAR) {
|
|
42
42
|
// we explicitely marked nodes, that should be exported in AR mode
|
|
43
|
-
return getInternalMetadataValue(node, 'exportNode');
|
|
43
|
+
return !!getInternalMetadataValue(node, 'exportNode');
|
|
44
44
|
} else {
|
|
45
45
|
// use the default export node check (enabled state, exclusion list, etc...)
|
|
46
|
-
return GltfExportManager._shouldExportNode(node,
|
|
46
|
+
return GltfExportManager._shouldExportNode(node, excludeNodes);
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
|
|
@@ -54,19 +54,19 @@ export class GltfExportManager {
|
|
|
54
54
|
/**
|
|
55
55
|
* Checks if a node should be available in the export
|
|
56
56
|
*/
|
|
57
|
-
protected static _shouldExportNode(node: Node,
|
|
57
|
+
protected static _shouldExportNode(node: Node, excludeNodes?: NodeDescription[]): boolean {
|
|
58
58
|
if (!(node instanceof TransformNode)) {
|
|
59
59
|
return false;
|
|
60
60
|
}
|
|
61
|
-
// TODO WTT: think of adding "BackgroundHelper" and nodes with "infiniteDistance" here as well, at least in AR mode
|
|
62
|
-
if (!node.isEnabled()) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
if (excluded && isNodeExcluded(node, excluded)) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
61
|
|
|
69
|
-
|
|
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
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -120,19 +120,19 @@ export class GltfExportManager {
|
|
|
120
120
|
* @param filename Optional name of the exported .GLB file.
|
|
121
121
|
* @param optimizeForAR Adjusts the exported GLB so that known issues get automatically fixed, this
|
|
122
122
|
* is mostly targeting Apples .usdz format.
|
|
123
|
-
* @param
|
|
123
|
+
* @param excludeNodes Optional list of nodes to be excluded from the export.
|
|
124
124
|
*/
|
|
125
125
|
public async exportGlb(
|
|
126
126
|
filename = 'glb-export.glb',
|
|
127
127
|
optimizeForAR: boolean = false,
|
|
128
|
-
|
|
128
|
+
excludeNodes?: NodeDescription[]
|
|
129
129
|
): Promise<File | undefined> {
|
|
130
|
-
await this._exportPreProcess(optimizeForAR,
|
|
130
|
+
await this._exportPreProcess(optimizeForAR, excludeNodes);
|
|
131
131
|
|
|
132
132
|
const glbData = await GLTF2Export.GLBAsync(
|
|
133
133
|
this.viewer.scene,
|
|
134
134
|
'dummy',
|
|
135
|
-
GltfExportManager._gltfExportOptions(optimizeForAR,
|
|
135
|
+
GltfExportManager._gltfExportOptions(optimizeForAR, excludeNodes)
|
|
136
136
|
);
|
|
137
137
|
|
|
138
138
|
await this._exportPostProcess(optimizeForAR);
|
|
@@ -155,19 +155,19 @@ export class GltfExportManager {
|
|
|
155
155
|
* @param filename Name of the main (text-based) .GLTF file referring to separate texture files.
|
|
156
156
|
* @param optimizeForAR Adjusts the exported GLB so that known issues get automatically fixed, this
|
|
157
157
|
* is mostly targeting Apples .usdz format.
|
|
158
|
-
* @param
|
|
158
|
+
* @param excludeNodes Optional list of nodes to be excluded from the export.
|
|
159
159
|
*/
|
|
160
160
|
public async exportGltfToFile(
|
|
161
161
|
filename: string,
|
|
162
162
|
optimizeForAR: boolean = false,
|
|
163
|
-
|
|
163
|
+
excludeNodes?: NodeDescription[]
|
|
164
164
|
): Promise<void> {
|
|
165
|
-
await this._exportPreProcess(optimizeForAR,
|
|
165
|
+
await this._exportPreProcess(optimizeForAR, excludeNodes);
|
|
166
166
|
|
|
167
167
|
const gltf = await GLTF2Export.GLTFAsync(
|
|
168
168
|
this.viewer.scene,
|
|
169
169
|
filename,
|
|
170
|
-
GltfExportManager._gltfExportOptions(optimizeForAR,
|
|
170
|
+
GltfExportManager._gltfExportOptions(optimizeForAR, excludeNodes)
|
|
171
171
|
);
|
|
172
172
|
gltf.downloadFiles();
|
|
173
173
|
|
|
@@ -179,19 +179,19 @@ export class GltfExportManager {
|
|
|
179
179
|
* @param filename Name of the main (text-based) .GLTF file referring to seperate texture files.
|
|
180
180
|
* @param optimizeForAR Adjusts the exported GLB so that known issues get automatically fixed, this
|
|
181
181
|
* is mostly targeting Apples .usdz format.
|
|
182
|
-
* @param
|
|
182
|
+
* @param excludeNodes Optional list of nodes to be excluded from the export.
|
|
183
183
|
*/
|
|
184
184
|
public async exportGlbToFile(
|
|
185
185
|
filename: string,
|
|
186
186
|
optimizeForAR: boolean = false,
|
|
187
|
-
|
|
187
|
+
excludeNodes?: NodeDescription[]
|
|
188
188
|
): Promise<void> {
|
|
189
|
-
await this._exportPreProcess(optimizeForAR,
|
|
189
|
+
await this._exportPreProcess(optimizeForAR, excludeNodes);
|
|
190
190
|
|
|
191
191
|
const glb = await GLTF2Export.GLBAsync(
|
|
192
192
|
this.viewer.scene,
|
|
193
193
|
filename,
|
|
194
|
-
GltfExportManager._gltfExportOptions(optimizeForAR,
|
|
194
|
+
GltfExportManager._gltfExportOptions(optimizeForAR, excludeNodes)
|
|
195
195
|
);
|
|
196
196
|
glb.downloadFiles();
|
|
197
197
|
|
|
@@ -202,7 +202,7 @@ export class GltfExportManager {
|
|
|
202
202
|
* Prepares scene for GLB export.
|
|
203
203
|
* This is very important for AR exports, since we have to do a lot of conversions to satisfy Apples .usdz format.
|
|
204
204
|
*/
|
|
205
|
-
protected async _exportPreProcess(optimizeForAR: boolean,
|
|
205
|
+
protected async _exportPreProcess(optimizeForAR: boolean, excludeNodes?: NodeDescription[]): Promise<void> {
|
|
206
206
|
if (!optimizeForAR) {
|
|
207
207
|
// actually nothing to do if AR optimization is not required
|
|
208
208
|
return;
|
|
@@ -235,7 +235,7 @@ export class GltfExportManager {
|
|
|
235
235
|
// export
|
|
236
236
|
this.viewer.scene.rootNodes
|
|
237
237
|
// .filter(rootNode => rootNode.name !== GltfExportManager._EXPORT_ROOT_NAME)
|
|
238
|
-
.forEach(rootNode => this._prepareNodeForExport(rootNode, null,
|
|
238
|
+
.forEach(rootNode => this._prepareNodeForExport(rootNode, null, excludeNodes));
|
|
239
239
|
|
|
240
240
|
// bake transformation of all meshes, so that no negative scalings are left
|
|
241
241
|
// it's important that this is done AFTER instanced meshes have been converted (_prepareNodeForExport)
|
|
@@ -282,9 +282,9 @@ export class GltfExportManager {
|
|
|
282
282
|
protected _prepareNodeForExport(
|
|
283
283
|
node: Node,
|
|
284
284
|
clonedParent: TransformNode | null,
|
|
285
|
-
|
|
285
|
+
excludeNodes?: NodeDescription[]
|
|
286
286
|
): void {
|
|
287
|
-
if (!GltfExportManager._shouldExportNode(node,
|
|
287
|
+
if (!GltfExportManager._shouldExportNode(node, excludeNodes)) {
|
|
288
288
|
return;
|
|
289
289
|
}
|
|
290
290
|
|
|
@@ -314,7 +314,7 @@ export class GltfExportManager {
|
|
|
314
314
|
|
|
315
315
|
// handle children
|
|
316
316
|
const childs = transformNode.getChildTransformNodes(true);
|
|
317
|
-
childs.forEach(child => this._prepareNodeForExport(child, clonedNode,
|
|
317
|
+
childs.forEach(child => this._prepareNodeForExport(child, clonedNode, excludeNodes));
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
/**
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
Viewer,
|
|
14
14
|
Viewport,
|
|
15
15
|
} from '..';
|
|
16
|
+
import { getInternalMetadataValue, setInternalMetadataValue } from '../internal/metadata-helper';
|
|
16
17
|
import html2canvas from 'html2canvas';
|
|
17
18
|
|
|
18
19
|
export type HtmlAnchorOptions = {
|
|
@@ -70,6 +71,15 @@ export class HtmlAnchorManager {
|
|
|
70
71
|
|
|
71
72
|
protected _anchorMeshMaterial: StandardMaterial | null = null;
|
|
72
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Check if input node is a html anchor mesh, created by this manager.\
|
|
76
|
+
* This check is done via internal metadata check.
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
public static isHtmlAnchorMesh(node: TransformNode): boolean {
|
|
80
|
+
return !!getInternalMetadataValue(node, 'isHtmlAnchorMesh');
|
|
81
|
+
}
|
|
82
|
+
|
|
73
83
|
/** @internal */
|
|
74
84
|
public constructor(protected viewer: Viewer) {
|
|
75
85
|
const canvas = viewer.canvas;
|
|
@@ -141,6 +151,7 @@ export class HtmlAnchorManager {
|
|
|
141
151
|
// it's important that the occlusion check mesh is rendered after all "normal" meshes, which should be taken into
|
|
142
152
|
// account for the occlusion check
|
|
143
153
|
anchorMesh.renderingGroupId = 1;
|
|
154
|
+
setInternalMetadataValue(anchorMesh, 'isHtmlAnchorMesh', true);
|
|
144
155
|
|
|
145
156
|
this._htmlAnchors[name] = { parentHtmlElement, anchorMesh, options };
|
|
146
157
|
}
|
|
@@ -266,15 +277,6 @@ export class HtmlAnchorManager {
|
|
|
266
277
|
return anchorKeys;
|
|
267
278
|
}
|
|
268
279
|
|
|
269
|
-
/**
|
|
270
|
-
* Check if input mesh is a html anchor mesh, created by this manager.\
|
|
271
|
-
* This check is done via the material, as all html anchor meshes have the generic `_anchorMeshMaterial` set.
|
|
272
|
-
* @internal
|
|
273
|
-
*/
|
|
274
|
-
public isHtmlAnchorMesh(mesh: AbstractMesh): boolean {
|
|
275
|
-
return !!this._anchorMeshMaterial && mesh.material === this._anchorMeshMaterial;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
280
|
protected _updateHtmlAnchor(
|
|
279
281
|
parentHtmlElement: HTMLDivElement,
|
|
280
282
|
anchorMesh: AbstractMesh,
|
|
@@ -501,13 +501,17 @@ export class ParameterManager {
|
|
|
501
501
|
const setMaterialProms = activatedNodes.map(async an => {
|
|
502
502
|
const anVisibleNested = this.getNestedVisibilityParameterValueOfNode(an);
|
|
503
503
|
const anDeferredMaterial = getInternalMetadataValue(an, 'deferredMaterial');
|
|
504
|
-
// skip applying material if it's already set via the parameter
|
|
505
504
|
const anMaterialParamValue = this._getParameterValueOfNode(an, BuiltInParameter.Material) as
|
|
506
505
|
| string
|
|
507
506
|
| undefined;
|
|
508
|
-
|
|
507
|
+
// get latest requested material, either from (new) parameter value or from deferred material
|
|
508
|
+
const anRequestedMaterial = anMaterialParamValue ?? anDeferredMaterial;
|
|
509
|
+
// material observer might have been executed before visibility observer
|
|
510
|
+
// => skip the material application in this case, as if it would be done twice
|
|
511
|
+
const anMaterialToBeSet = getInternalMetadataValue(an, 'materialToBeSet');
|
|
512
|
+
if (anVisibleNested && anRequestedMaterial && !anMaterialToBeSet) {
|
|
509
513
|
await this.viewer.materialManager.setMaterialOnMesh(
|
|
510
|
-
|
|
514
|
+
anRequestedMaterial,
|
|
511
515
|
// this cast is fine, as deferred material can only be set on meshes
|
|
512
516
|
an as AbstractMesh
|
|
513
517
|
);
|
|
@@ -535,7 +539,12 @@ export class ParameterManager {
|
|
|
535
539
|
// only set material if the mesh is visible, or gets visible by the parameter update
|
|
536
540
|
const visible = this.getNestedVisibilityParameterValueOfNode(node);
|
|
537
541
|
if (visible) {
|
|
538
|
-
|
|
542
|
+
// visibility observer might have been executed before material observer
|
|
543
|
+
// => skip the material application in this case, as if it would be done twice
|
|
544
|
+
const anMaterialToBeSet = getInternalMetadataValue(node, 'materialToBeSet');
|
|
545
|
+
if (!anMaterialToBeSet) {
|
|
546
|
+
await this.viewer.materialManager.setMaterialOnMesh(material, node);
|
|
547
|
+
}
|
|
539
548
|
} else {
|
|
540
549
|
setInternalMetadataValue(node, 'deferredMaterial', material);
|
|
541
550
|
}
|
|
@@ -2,13 +2,12 @@ import {
|
|
|
2
2
|
AbstractMesh,
|
|
3
3
|
ArcRotateCamera,
|
|
4
4
|
AssetContainer,
|
|
5
|
-
BackgroundMaterial,
|
|
6
5
|
BoundingInfo,
|
|
7
6
|
CameraManager,
|
|
8
7
|
Color4,
|
|
9
8
|
CubeTexture,
|
|
10
|
-
ExcludedGeometryList,
|
|
11
9
|
IShadowGenerator,
|
|
10
|
+
NodeDescription,
|
|
12
11
|
TransformNode,
|
|
13
12
|
Vector3,
|
|
14
13
|
Viewer,
|
|
@@ -16,7 +15,7 @@ import {
|
|
|
16
15
|
ViewerErrorIds,
|
|
17
16
|
} from '../index';
|
|
18
17
|
import { BaseAsset, loadAsset, prepareAssetForScene } from '../internal/asset-helper';
|
|
19
|
-
import {
|
|
18
|
+
import { nodeMatchesAnyCriteria } from '../internal/node-helper';
|
|
20
19
|
import { merge } from 'lodash-es';
|
|
21
20
|
|
|
22
21
|
/**
|
|
@@ -63,9 +62,9 @@ export type UpdateGroundSettings = {
|
|
|
63
62
|
*/
|
|
64
63
|
factor?: number;
|
|
65
64
|
/**
|
|
66
|
-
* Optional list of
|
|
65
|
+
* Optional list of nodes to be excluded from scene size calculation
|
|
67
66
|
*/
|
|
68
|
-
|
|
67
|
+
excludeNodes?: NodeDescription[];
|
|
69
68
|
};
|
|
70
69
|
|
|
71
70
|
export type UpdateShadowsSettings = {
|
|
@@ -75,13 +74,13 @@ export type UpdateShadowsSettings = {
|
|
|
75
74
|
*/
|
|
76
75
|
shadowGenerators?: IShadowGenerator[];
|
|
77
76
|
/**
|
|
78
|
-
* Optional list of
|
|
77
|
+
* Optional list of nodes which should not be defined as shadow caster
|
|
79
78
|
*/
|
|
80
|
-
|
|
79
|
+
excludeCasterNodes?: NodeDescription[];
|
|
81
80
|
/**
|
|
82
|
-
* Optional list of
|
|
81
|
+
* Optional list of nodes which should not be defined as shadow receiver
|
|
83
82
|
*/
|
|
84
|
-
|
|
83
|
+
excludeReceiverNodes?: NodeDescription[];
|
|
85
84
|
};
|
|
86
85
|
|
|
87
86
|
/**
|
|
@@ -277,36 +276,27 @@ export class SceneManager {
|
|
|
277
276
|
* Calculates size of the current scene.\
|
|
278
277
|
* Only takes meshes into considerations that:
|
|
279
278
|
* - are visible
|
|
280
|
-
* - are not excluded by "
|
|
279
|
+
* - are not excluded by "excludeNodes" parameter
|
|
281
280
|
* - do not have a material of type "BackgroundMaterial"
|
|
282
281
|
* - do not have an infinite distance (like sky boxes)
|
|
283
282
|
*/
|
|
284
|
-
public calculateBoundingInfo(
|
|
283
|
+
public calculateBoundingInfo(excludeNodes?: NodeDescription[]): BoundingInfo {
|
|
285
284
|
// CB-6062: workaround for BoundingBox not respecting render loop
|
|
286
285
|
this.viewer.scene.render();
|
|
287
286
|
|
|
288
287
|
const { max, min } = this.viewer.scene.meshes
|
|
289
288
|
.filter(mesh => {
|
|
290
|
-
const isEnabled = mesh.isEnabled();
|
|
291
|
-
// ignore meshes with invalid bounding infos
|
|
292
|
-
const hasValidBBoxInfo = mesh.getBoundingInfo().boundingSphere.radius > 0;
|
|
293
|
-
// ignore meshes with infinite distance, typically these are sky boxes
|
|
294
|
-
const hasInfiniteDistance = mesh.infiniteDistance;
|
|
295
|
-
// ignore meshes with "BackgroundMaterial" - usually a ground or skybox
|
|
296
|
-
const hasBackgroundMaterial = mesh.material instanceof BackgroundMaterial;
|
|
297
|
-
// ignore dummy meshes for html anchor occlusion check
|
|
298
|
-
const isHtmlAnchorMesh = this.viewer.htmlAnchorManager.isHtmlAnchorMesh(mesh);
|
|
299
289
|
// ignore excluded meshes
|
|
300
|
-
const isExcluded =
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
290
|
+
const isExcluded = nodeMatchesAnyCriteria(mesh, {
|
|
291
|
+
isInList: excludeNodes,
|
|
292
|
+
isDisabled: true,
|
|
293
|
+
hasInvalidBoundingInfo: true,
|
|
294
|
+
hasInfiniteDistance: true,
|
|
295
|
+
isGeneratedBackgroundMesh: true,
|
|
296
|
+
isHtmlAnchorMesh: true,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
return !isExcluded;
|
|
310
300
|
})
|
|
311
301
|
.reduce(
|
|
312
302
|
(accBBoxMinMax, curMesh, idx) => {
|
|
@@ -331,7 +321,7 @@ export class SceneManager {
|
|
|
331
321
|
* plane mesh can be used as well.
|
|
332
322
|
*/
|
|
333
323
|
public updateGround(settings?: UpdateGroundSettings): void {
|
|
334
|
-
const bboxInfo = this.calculateBoundingInfo(settings?.
|
|
324
|
+
const bboxInfo = this.calculateBoundingInfo(settings?.excludeNodes);
|
|
335
325
|
|
|
336
326
|
const groundMesh = settings?.groundMesh ?? this.viewer.scene.getMeshByName(SceneManager._DEFAULT_GROUND_PLANE_NAME);
|
|
337
327
|
if (!groundMesh) {
|
|
@@ -343,7 +333,10 @@ export class SceneManager {
|
|
|
343
333
|
const factor =
|
|
344
334
|
bboxInfo.boundingSphere.radius * 2 * (settings?.factor ?? SceneManager._DEFAULT_GROUND_SIZING_FACTOR);
|
|
345
335
|
const position = bboxInfo.boundingSphere.centerWorld;
|
|
346
|
-
|
|
336
|
+
// scale in all dimensions, in this case it doesn't matter in which coordinate space (XY, XZ) the ground has been
|
|
337
|
+
// built
|
|
338
|
+
// also the flat dimension of the plane is independent of the scaling, it will always remain a plane
|
|
339
|
+
groundMesh.scaling = new Vector3(factor, factor, factor);
|
|
347
340
|
groundMesh.position = new Vector3(position.x, 0, position.z);
|
|
348
341
|
}
|
|
349
342
|
|
|
@@ -358,16 +351,23 @@ export class SceneManager {
|
|
|
358
351
|
|
|
359
352
|
const shadowCasterMeshes: AbstractMesh[] = [];
|
|
360
353
|
this.viewer.scene.meshes.forEach(mesh => {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
354
|
+
mesh.receiveShadows = !nodeMatchesAnyCriteria(mesh, {
|
|
355
|
+
isInList: settings?.excludeReceiverNodes,
|
|
356
|
+
isHtmlAnchorMesh: true,
|
|
357
|
+
isDimensionLine: true,
|
|
358
|
+
});
|
|
365
359
|
|
|
366
|
-
//
|
|
360
|
+
// exclude "helper meshes" (e.g. skybox, ground, dimension lines, html anchors) here to reduce the size of the
|
|
367
361
|
// poisson filters auto shadow plane calculation, which will result in better shadow resolution
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
362
|
+
const excludeAsShadowCaster = nodeMatchesAnyCriteria(mesh, {
|
|
363
|
+
isInList: settings?.excludeCasterNodes,
|
|
364
|
+
hasInvalidBoundingInfo: true,
|
|
365
|
+
hasInfiniteDistance: true,
|
|
366
|
+
isGeneratedBackgroundMesh: true,
|
|
367
|
+
isHtmlAnchorMesh: true,
|
|
368
|
+
isDimensionLine: true,
|
|
369
|
+
});
|
|
370
|
+
if (!excludeAsShadowCaster) {
|
|
371
371
|
shadowCasterMeshes.push(mesh);
|
|
372
372
|
}
|
|
373
373
|
});
|
package/src/viewer.ts
CHANGED
|
@@ -13,11 +13,12 @@ import {
|
|
|
13
13
|
HtmlAnchorManager,
|
|
14
14
|
MaterialManager,
|
|
15
15
|
ModelManager,
|
|
16
|
+
NodeParameterSubject,
|
|
16
17
|
NullEngine,
|
|
17
18
|
ParameterManager,
|
|
18
|
-
ParameterSubject,
|
|
19
19
|
Scene,
|
|
20
20
|
SceneManager,
|
|
21
|
+
TagParameterSubject,
|
|
21
22
|
TextureManager,
|
|
22
23
|
TransformNode,
|
|
23
24
|
} from './index';
|
|
@@ -26,10 +27,10 @@ import { getIsScaledDownDevice } from './internal/device-helper';
|
|
|
26
27
|
import { cloneDeep, merge } from 'lodash-es';
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
|
-
* Use this to
|
|
30
|
+
* Use this type to select nodes directly or via node or tag name.\
|
|
31
|
+
* This pattern is used for excluding nodes from autofocus, GLB export, etc.
|
|
30
32
|
*/
|
|
31
|
-
export type
|
|
32
|
-
export type ExcludedGeometryList = ExcludedGeometry[];
|
|
33
|
+
export type NodeDescription = TransformNode | TagParameterSubject | NodeParameterSubject;
|
|
33
34
|
|
|
34
35
|
export type LimitTextureSizeConfig = {
|
|
35
36
|
size: 512 | 1024;
|