@combeenation/3d-viewer 18.4.0-beta1 → 18.5.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/texture-parameter-helper.js +4 -1
- package/dist/lib-cjs/internal/texture-parameter-helper.js.map +1 -1
- package/dist/lib-cjs/manager/camera-manager.js +2 -2
- package/dist/lib-cjs/manager/camera-manager.js.map +1 -1
- package/dist/lib-cjs/manager/dimension-line-manager.d.ts +2 -16
- package/dist/lib-cjs/manager/dimension-line-manager.js +4 -6
- package/dist/lib-cjs/manager/dimension-line-manager.js.map +1 -1
- package/dist/lib-cjs/manager/html-anchor-manager.d.ts +54 -13
- package/dist/lib-cjs/manager/html-anchor-manager.js +27 -15
- package/dist/lib-cjs/manager/html-anchor-manager.js.map +1 -1
- package/dist/lib-cjs/manager/material-manager.js +7 -3
- package/dist/lib-cjs/manager/material-manager.js.map +1 -1
- package/dist/lib-cjs/manager/texture-manager.d.ts +8 -0
- package/dist/lib-cjs/manager/texture-manager.js +24 -0
- package/dist/lib-cjs/manager/texture-manager.js.map +1 -1
- package/package.json +1 -1
- package/src/internal/texture-parameter-helper.ts +7 -2
- package/src/manager/camera-manager.ts +7 -2
- package/src/manager/dimension-line-manager.ts +7 -23
- package/src/manager/html-anchor-manager.ts +84 -26
- package/src/manager/material-manager.ts +10 -4
- package/src/manager/texture-manager.ts +23 -1
|
@@ -380,7 +380,12 @@ export class CameraManager {
|
|
|
380
380
|
// the idea is to create a dedicated canvas for these elements and merge the result with the main screenshot into
|
|
381
381
|
// a combined canvas
|
|
382
382
|
const screenshotHtmlCanvas = htmlAnchorKeys.length
|
|
383
|
-
? await this.viewer.htmlAnchorManager.createScreenshotCanvas(
|
|
383
|
+
? await this.viewer.htmlAnchorManager.createScreenshotCanvas(
|
|
384
|
+
screenshotSize,
|
|
385
|
+
screenshotCam,
|
|
386
|
+
htmlAnchorKeys,
|
|
387
|
+
excludeNodes
|
|
388
|
+
)
|
|
384
389
|
: undefined;
|
|
385
390
|
|
|
386
391
|
const screenshotCombinedCanvas = await createScreenshotCanvas(imageStr3d, screenshotSize, screenshotHtmlCanvas);
|
|
@@ -402,7 +407,7 @@ export class CameraManager {
|
|
|
402
407
|
if (fileName) {
|
|
403
408
|
// rebuild Babylon.js default behaviour: if a file name is given, download the screenshot instead of returning the
|
|
404
409
|
// data string
|
|
405
|
-
downloadFile(
|
|
410
|
+
downloadFile(imageStr, fileName);
|
|
406
411
|
|
|
407
412
|
return '';
|
|
408
413
|
} else {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Color3, LinesMesh, MeshBuilder, Tags, TransformNode, Vector3, Viewer } from '../index';
|
|
1
|
+
import { Color3, HtmlAnchorOptions, LinesMesh, MeshBuilder, Tags, TransformNode, Vector3, Viewer } from '../index';
|
|
2
2
|
import { merge } from 'lodash-es';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Options for dimension line creation
|
|
6
6
|
*/
|
|
7
|
-
export type DimensionLineOptions = {
|
|
7
|
+
export type DimensionLineOptions = Pick<HtmlAnchorOptions, 'occlusion' | 'scaling'> & {
|
|
8
8
|
/**
|
|
9
9
|
* Default: `Color3.Black()`
|
|
10
10
|
*/
|
|
@@ -27,20 +27,6 @@ export type DimensionLineOptions = {
|
|
|
27
27
|
* Default: no parent node set
|
|
28
28
|
*/
|
|
29
29
|
parentNode?: TransformNode;
|
|
30
|
-
/**
|
|
31
|
-
* `true`: html elements can be occluded by other meshes
|
|
32
|
-
* `false`: html elements will always be shown in front of the scene
|
|
33
|
-
*
|
|
34
|
-
* Default: `false`
|
|
35
|
-
*/
|
|
36
|
-
hideIfOccluded: boolean;
|
|
37
|
-
/**
|
|
38
|
-
* `true`: html element size is relative to camera distance, just like a "normal" 3d object
|
|
39
|
-
* `false`: html element size remains constant
|
|
40
|
-
*
|
|
41
|
-
* Default: `false`
|
|
42
|
-
*/
|
|
43
|
-
scaleWithCameraDistance: boolean;
|
|
44
30
|
/**
|
|
45
31
|
* Optional callback for label text creation.\
|
|
46
32
|
* Provides calculated length from `startPoint` and `endPoint` as parameter in meters.
|
|
@@ -64,8 +50,6 @@ export class DimensionLineManager {
|
|
|
64
50
|
protected _defaultLineOptions: DimensionLineOptions = {
|
|
65
51
|
lineColor: Color3.Black(),
|
|
66
52
|
closingLineHeight: 0.05,
|
|
67
|
-
hideIfOccluded: false,
|
|
68
|
-
scaleWithCameraDistance: false,
|
|
69
53
|
labelTextCb: (lengthM): string => {
|
|
70
54
|
const lengthRounded = Math.round(lengthM * 1000 * 100) / 100;
|
|
71
55
|
return `${lengthRounded} mm`;
|
|
@@ -123,8 +107,8 @@ export class DimensionLineManager {
|
|
|
123
107
|
closingLineHeight,
|
|
124
108
|
closingLineDirection: closingLineDirectionIn,
|
|
125
109
|
parentNode,
|
|
126
|
-
|
|
127
|
-
|
|
110
|
+
occlusion,
|
|
111
|
+
scaling,
|
|
128
112
|
labelTextCb,
|
|
129
113
|
labelCssClass,
|
|
130
114
|
} = resDefaultOptions;
|
|
@@ -151,7 +135,7 @@ export class DimensionLineManager {
|
|
|
151
135
|
linesMesh.position = lineCenter;
|
|
152
136
|
linesMesh.color = lineColor;
|
|
153
137
|
// rendering group id 1 reserved for html anchor meshes, which are required for occlusion checking
|
|
154
|
-
linesMesh.renderingGroupId = hideIfOccluded ? 0 : 2;
|
|
138
|
+
linesMesh.renderingGroupId = occlusion?.hideIfOccluded ? 0 : 2;
|
|
155
139
|
// tag can be used to exclude dimension lines from autofocus or gltf export
|
|
156
140
|
Tags.AddTagsTo(linesMesh, DimensionLineManager.DIMENSION_LINE_KEY);
|
|
157
141
|
if (parentNode) {
|
|
@@ -185,8 +169,8 @@ export class DimensionLineManager {
|
|
|
185
169
|
this.viewer.htmlAnchorManager.addHtmlAnchor(name, span, Vector3.Zero(), {
|
|
186
170
|
parentNode: linesMesh,
|
|
187
171
|
group: DimensionLineManager.DIMENSION_LINE_KEY,
|
|
188
|
-
|
|
189
|
-
|
|
172
|
+
occlusion,
|
|
173
|
+
scaling,
|
|
190
174
|
});
|
|
191
175
|
|
|
192
176
|
this._dimensionLineObjs[name] = { linesMesh, htmlLabelName };
|
|
@@ -4,18 +4,68 @@ import {
|
|
|
4
4
|
Camera,
|
|
5
5
|
DimensionLineManager,
|
|
6
6
|
MeshBuilder,
|
|
7
|
+
NodeDescription,
|
|
7
8
|
Ray,
|
|
8
9
|
ScreenshotSize,
|
|
9
10
|
StandardMaterial,
|
|
10
|
-
Tags,
|
|
11
11
|
TransformNode,
|
|
12
12
|
Vector3,
|
|
13
13
|
Viewer,
|
|
14
14
|
Viewport,
|
|
15
15
|
} from '..';
|
|
16
16
|
import { getInternalMetadataValue, setInternalMetadataValue } from '../internal/metadata-helper';
|
|
17
|
+
import { nodeMatchesAnyCriteria } from '../internal/node-helper';
|
|
17
18
|
import html2canvas from 'html2canvas';
|
|
18
19
|
|
|
20
|
+
export type HtmlAnchorOcclusionOptions = {
|
|
21
|
+
/**
|
|
22
|
+
* `true`: html elements can be occluded by other meshes
|
|
23
|
+
* `false`: html elements will always be shown in front of the scene
|
|
24
|
+
*
|
|
25
|
+
* Default: `false`
|
|
26
|
+
*/
|
|
27
|
+
hideIfOccluded: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Size of dummy mesh for occlusion check
|
|
30
|
+
* Smaller values result in a more precise occlusion check around the center of the anchor.
|
|
31
|
+
* Too small values can lead to flickering of the html element.
|
|
32
|
+
*
|
|
33
|
+
* Default: 0.01
|
|
34
|
+
*/
|
|
35
|
+
anchorMeshSize?: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type HtmlAnchorScalingOptions = {
|
|
39
|
+
/**
|
|
40
|
+
* `true`: html element size is relative to camera distance, just like a "normal" 3d object
|
|
41
|
+
* `false`: html element size remains constant and `referenceScale`, `minScale` and `maxScale` will have no effect
|
|
42
|
+
*
|
|
43
|
+
* Default: `false`
|
|
44
|
+
*/
|
|
45
|
+
scaleWithCameraDistance: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Basis factor for converting camera distance in Babylon.js units to pixels.
|
|
48
|
+
* Find a suitable value for your project by trial and error, whereas larger values will make the html anchor elements
|
|
49
|
+
* larger as well.
|
|
50
|
+
*
|
|
51
|
+
* Default: 1
|
|
52
|
+
*/
|
|
53
|
+
referenceScale?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Can be used to limit the calculated scale value so that a html anchor element is still readable/clickable if the
|
|
56
|
+
* camera is far zoomed out.
|
|
57
|
+
*
|
|
58
|
+
* Default: undefined (no limit)
|
|
59
|
+
*/
|
|
60
|
+
minScale?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Max limit cap when zooming the camera in to avoid html anchor elements getting too large.
|
|
63
|
+
*
|
|
64
|
+
* Default: undefined (no limit)
|
|
65
|
+
*/
|
|
66
|
+
maxScale?: number;
|
|
67
|
+
};
|
|
68
|
+
|
|
19
69
|
export type HtmlAnchorOptions = {
|
|
20
70
|
/**
|
|
21
71
|
* Associated anchor mesh will be created underneath this parent node.\
|
|
@@ -27,19 +77,13 @@ export type HtmlAnchorOptions = {
|
|
|
27
77
|
*/
|
|
28
78
|
group?: string;
|
|
29
79
|
/**
|
|
30
|
-
*
|
|
31
|
-
* `false`: html elements will always be shown in front of the scene
|
|
32
|
-
*
|
|
33
|
-
* Default: `false`
|
|
80
|
+
* Occlusion options, see {@link HtmlAnchorOcclusionOptions}
|
|
34
81
|
*/
|
|
35
|
-
|
|
82
|
+
occlusion?: HtmlAnchorOcclusionOptions;
|
|
36
83
|
/**
|
|
37
|
-
*
|
|
38
|
-
* `false`: html element size remains constant
|
|
39
|
-
*
|
|
40
|
-
* Default: `false`
|
|
84
|
+
* Scaling options, see {@link HtmlAnchorScalingOptions}
|
|
41
85
|
*/
|
|
42
|
-
|
|
86
|
+
scaling?: HtmlAnchorScalingOptions;
|
|
43
87
|
/**
|
|
44
88
|
* Activates/deactivates `pointer-events` CSS class of anchor element.\
|
|
45
89
|
* Set this to `true` if the html element should be interactive (e.g. button)
|
|
@@ -122,7 +166,7 @@ export class HtmlAnchorManager {
|
|
|
122
166
|
return;
|
|
123
167
|
}
|
|
124
168
|
|
|
125
|
-
const { parentNode, enablePointerEvents } = options ?? {};
|
|
169
|
+
const { parentNode, occlusion, enablePointerEvents } = options ?? {};
|
|
126
170
|
|
|
127
171
|
// create a parent for the input html element, which will receive the updated style and transform data
|
|
128
172
|
// in this way the original input element remains untouched
|
|
@@ -141,7 +185,9 @@ export class HtmlAnchorManager {
|
|
|
141
185
|
|
|
142
186
|
// NOTE: creates a sphere with fixed size, which could be problematic in scene with "strange" dimensions
|
|
143
187
|
// add a property for the sphere size if required
|
|
144
|
-
const anchorMesh = MeshBuilder.CreateSphere(`${HtmlAnchorManager._HTML_ANCHOR_KEY}_${name}`, {
|
|
188
|
+
const anchorMesh = MeshBuilder.CreateSphere(`${HtmlAnchorManager._HTML_ANCHOR_KEY}_${name}`, {
|
|
189
|
+
diameter: occlusion?.anchorMeshSize ?? 0.01,
|
|
190
|
+
});
|
|
145
191
|
anchorMesh.position = position;
|
|
146
192
|
anchorMesh.parent = parentNode ?? null;
|
|
147
193
|
// anchor mesh will be invisible, we only need it for positioning and occlusion check
|
|
@@ -203,7 +249,8 @@ export class HtmlAnchorManager {
|
|
|
203
249
|
public async createScreenshotCanvas(
|
|
204
250
|
size: ScreenshotSize,
|
|
205
251
|
camera: ArcRotateCamera,
|
|
206
|
-
htmlAnchorKeys: string[]
|
|
252
|
+
htmlAnchorKeys: string[],
|
|
253
|
+
excludeNodes?: NodeDescription[]
|
|
207
254
|
): Promise<HTMLCanvasElement> {
|
|
208
255
|
const canvasParentHtmlElement = this.viewer.canvas?.parentElement;
|
|
209
256
|
if (!canvasParentHtmlElement) {
|
|
@@ -242,7 +289,8 @@ export class HtmlAnchorManager {
|
|
|
242
289
|
size.canvasHeight,
|
|
243
290
|
baseScale,
|
|
244
291
|
true,
|
|
245
|
-
options
|
|
292
|
+
options,
|
|
293
|
+
excludeNodes
|
|
246
294
|
);
|
|
247
295
|
});
|
|
248
296
|
|
|
@@ -291,9 +339,12 @@ export class HtmlAnchorManager {
|
|
|
291
339
|
height: number,
|
|
292
340
|
baseScale: number,
|
|
293
341
|
useRayHitTestForOcclusionCheck: boolean,
|
|
294
|
-
options?: HtmlAnchorOptions
|
|
342
|
+
options?: HtmlAnchorOptions,
|
|
343
|
+
excludeNodes?: NodeDescription[]
|
|
295
344
|
): void {
|
|
296
|
-
const {
|
|
345
|
+
const { occlusion, scaling } = options ?? {};
|
|
346
|
+
const { hideIfOccluded } = occlusion ?? {};
|
|
347
|
+
const { scaleWithCameraDistance, referenceScale, minScale, maxScale } = scaling ?? {};
|
|
297
348
|
|
|
298
349
|
const viewMatrix = camera.getViewMatrix();
|
|
299
350
|
const projectionMatrix = camera.getProjectionMatrix();
|
|
@@ -307,20 +358,27 @@ export class HtmlAnchorManager {
|
|
|
307
358
|
new Viewport(0, 0, width, height)
|
|
308
359
|
);
|
|
309
360
|
|
|
361
|
+
const meshWorldPos = anchorMesh.getAbsolutePosition();
|
|
362
|
+
// calculate camera world position manually, as there is no help function like for meshes
|
|
363
|
+
const camWorldMatrix = camera.computeWorldMatrix();
|
|
364
|
+
const camWorldPos = camWorldMatrix.getTranslation();
|
|
365
|
+
const distance = Vector3.Distance(meshWorldPos, camWorldPos);
|
|
366
|
+
const camYWindow = distance * Math.tan(camera.fov / 2);
|
|
367
|
+
|
|
310
368
|
// base scale is used if width and height don't equal the viewer canvas, which is the case for screenshots
|
|
311
369
|
let scale = baseScale;
|
|
312
370
|
if (scaleWithCameraDistance) {
|
|
313
|
-
const meshWorldPos = anchorMesh.getAbsolutePosition();
|
|
314
|
-
// calculate camera world position manually, as there is no help function like for meshes
|
|
315
|
-
const camWorldMatrix = camera.computeWorldMatrix();
|
|
316
|
-
const camWorldPos = camWorldMatrix.getTranslation();
|
|
317
|
-
const distance = Vector3.Distance(meshWorldPos, camWorldPos);
|
|
318
|
-
const frustumSlopeY = Math.tan(camera.fov / 2);
|
|
319
371
|
// if the distance from camera to mesh gets larger, the html elements scaling will be decreased
|
|
320
372
|
// we also consider the cameras FOV, so scale 1 means, that the resulting vertical frustum of the camera
|
|
321
373
|
// distance equals 1
|
|
322
|
-
|
|
323
|
-
|
|
374
|
+
scale *= (referenceScale ?? 1) / camYWindow;
|
|
375
|
+
// apply scale caps
|
|
376
|
+
if (minScale !== undefined) {
|
|
377
|
+
scale = Math.max(scale, minScale);
|
|
378
|
+
}
|
|
379
|
+
if (maxScale !== undefined) {
|
|
380
|
+
scale = Math.min(scale, maxScale);
|
|
381
|
+
}
|
|
324
382
|
}
|
|
325
383
|
|
|
326
384
|
const elementXOffset = (parentHtmlElement.offsetWidth * scale) / 2;
|
|
@@ -350,7 +408,7 @@ export class HtmlAnchorManager {
|
|
|
350
408
|
const ray = new Ray(camera.position, camDirection);
|
|
351
409
|
const hit = this.viewer.scene.pickWithRay(
|
|
352
410
|
ray,
|
|
353
|
-
mesh => !
|
|
411
|
+
mesh => !nodeMatchesAnyCriteria(mesh, { isInList: excludeNodes, isDimensionLine: true }),
|
|
354
412
|
false
|
|
355
413
|
);
|
|
356
414
|
isOccluded = hit?.pickedMesh !== anchorMesh;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AbstractMesh,
|
|
3
|
-
BaseTexture,
|
|
4
3
|
Material,
|
|
5
4
|
StandardMaterial,
|
|
6
5
|
TagNamingStrategy,
|
|
6
|
+
TextureManager,
|
|
7
7
|
Viewer,
|
|
8
8
|
ViewerError,
|
|
9
9
|
ViewerErrorIds,
|
|
@@ -188,9 +188,15 @@ export class MaterialManager {
|
|
|
188
188
|
|
|
189
189
|
await this.viewer.parameterManager.applyParameterValuesToMaterial(material);
|
|
190
190
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
const texturesLoadProm = async (): Promise<void> => {
|
|
192
|
+
const errTextureName = await TextureManager.waitTexturesLoadedOrFailed(material.getActiveTextures());
|
|
193
|
+
if (errTextureName) {
|
|
194
|
+
console.warn(
|
|
195
|
+
`Couldn't load texture "${errTextureName}" of material "${material.id}", material still got created`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const matReadyProms = [texturesLoadProm()];
|
|
194
200
|
|
|
195
201
|
if (mesh) {
|
|
196
202
|
// this promise should only take some time on the first call of the corresponding shader (eg: PBRMaterial shader)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseTexture, Viewer } from '../index';
|
|
1
|
+
import { BaseTexture, Texture, Viewer } from '../index';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Manager for texture related tasks, like renaming textures that are based on Combeenation image assets.\
|
|
@@ -7,6 +7,28 @@ import { BaseTexture, Viewer } from '../index';
|
|
|
7
7
|
* @internal
|
|
8
8
|
*/
|
|
9
9
|
export class TextureManager {
|
|
10
|
+
/**
|
|
11
|
+
* Basic textures loading check `BaseTexture.WhenAllReady` doesn't consider loading errors, which easily leads to a
|
|
12
|
+
* deadlock. (see CB-10769)
|
|
13
|
+
* This implementation listens for loading errors in the associated textures and aborts the promise accordingly.
|
|
14
|
+
*
|
|
15
|
+
* @returns (display) name of the texture that failed to load, or "" if every texture loaded successfully
|
|
16
|
+
*/
|
|
17
|
+
public static async waitTexturesLoadedOrFailed(textures: BaseTexture[]): Promise<string> {
|
|
18
|
+
const texturesReadyProm = new Promise<string>(resolve => BaseTexture.WhenAllReady(textures, () => resolve('')));
|
|
19
|
+
const textureLoadErrorProm = new Promise<string>(resolve => {
|
|
20
|
+
const errorObserver = Texture.OnTextureLoadErrorObservable.add(errTexture => {
|
|
21
|
+
const activeTextureUniqueIds = textures.map(x => x.uniqueId);
|
|
22
|
+
if (activeTextureUniqueIds.includes(errTexture.uniqueId)) {
|
|
23
|
+
resolve(errTexture.displayName ?? errTexture.name);
|
|
24
|
+
errorObserver.remove();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return Promise.race([texturesReadyProm, textureLoadErrorProm]);
|
|
30
|
+
}
|
|
31
|
+
|
|
10
32
|
public constructor(protected viewer: Viewer) {
|
|
11
33
|
this.viewer.scene.onNewTextureAddedObservable.add(texture => this._onTextureAdded(texture));
|
|
12
34
|
}
|