@combeenation/3d-viewer 17.1.0 → 18.0.0-beta2
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 +8 -0
- package/dist/lib-cjs/index.js +8 -0
- package/dist/lib-cjs/index.js.map +1 -1
- package/dist/lib-cjs/internal/asset-helper.d.ts +32 -0
- package/dist/lib-cjs/internal/asset-helper.js +105 -0
- package/dist/lib-cjs/internal/asset-helper.js.map +1 -0
- package/dist/lib-cjs/internal/cbn-custom-babylon-loader-plugin.d.ts +18 -0
- package/dist/lib-cjs/internal/cbn-custom-babylon-loader-plugin.js +22 -3
- package/dist/lib-cjs/internal/cbn-custom-babylon-loader-plugin.js.map +1 -1
- package/dist/lib-cjs/internal/cloning-helper.js +1 -1
- package/dist/lib-cjs/internal/cloning-helper.js.map +1 -1
- package/dist/lib-cjs/internal/texture-parameter-helper.js +26 -7
- package/dist/lib-cjs/internal/texture-parameter-helper.js.map +1 -1
- package/dist/lib-cjs/manager/camera-manager.d.ts +23 -2
- package/dist/lib-cjs/manager/camera-manager.js +91 -24
- package/dist/lib-cjs/manager/camera-manager.js.map +1 -1
- package/dist/lib-cjs/manager/debug-manager.d.ts +1 -1
- package/dist/lib-cjs/manager/debug-manager.js +3 -3
- package/dist/lib-cjs/manager/debug-manager.js.map +1 -1
- package/dist/lib-cjs/manager/dimension-line-manager.d.ts +126 -0
- package/dist/lib-cjs/manager/dimension-line-manager.js +138 -0
- package/dist/lib-cjs/manager/dimension-line-manager.js.map +1 -0
- package/dist/lib-cjs/manager/html-anchor-manager.d.ts +93 -0
- package/dist/lib-cjs/manager/html-anchor-manager.js +228 -0
- package/dist/lib-cjs/manager/html-anchor-manager.js.map +1 -0
- package/dist/lib-cjs/manager/model-manager.d.ts +11 -34
- package/dist/lib-cjs/manager/model-manager.js +47 -107
- package/dist/lib-cjs/manager/model-manager.js.map +1 -1
- package/dist/lib-cjs/manager/parameter-manager.d.ts +17 -12
- package/dist/lib-cjs/manager/parameter-manager.js +78 -69
- package/dist/lib-cjs/manager/parameter-manager.js.map +1 -1
- package/dist/lib-cjs/manager/scene-manager.d.ts +111 -5
- package/dist/lib-cjs/manager/scene-manager.js +276 -10
- package/dist/lib-cjs/manager/scene-manager.js.map +1 -1
- package/dist/lib-cjs/viewer-error.d.ts +1 -0
- package/dist/lib-cjs/viewer-error.js +1 -0
- package/dist/lib-cjs/viewer-error.js.map +1 -1
- package/dist/lib-cjs/viewer.d.ts +9 -14
- package/dist/lib-cjs/viewer.js +16 -38
- package/dist/lib-cjs/viewer.js.map +1 -1
- package/package.json +22 -12
- package/src/index.ts +8 -0
- package/src/internal/asset-helper.ts +115 -0
- package/src/internal/cbn-custom-babylon-loader-plugin.ts +30 -3
- package/src/internal/cloning-helper.ts +1 -1
- package/src/internal/texture-parameter-helper.ts +25 -8
- package/src/manager/camera-manager.ts +153 -39
- package/src/manager/debug-manager.ts +3 -3
- package/src/manager/dimension-line-manager.ts +255 -0
- package/src/manager/html-anchor-manager.ts +332 -0
- package/src/manager/model-manager.ts +55 -138
- package/src/manager/parameter-manager.ts +94 -75
- package/src/manager/scene-manager.ts +375 -10
- package/src/viewer-error.ts +1 -0
- package/src/viewer.ts +30 -56
- package/src/dev.ts +0 -47
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AssetContainer,
|
|
3
|
-
BuiltInParameter,
|
|
4
3
|
DecalConfiguration,
|
|
5
4
|
MaterialManager,
|
|
6
5
|
MeshBuilder,
|
|
7
|
-
ParameterManager,
|
|
8
|
-
SceneLoader,
|
|
9
6
|
TransformNode,
|
|
10
7
|
Viewer,
|
|
11
8
|
ViewerError,
|
|
12
9
|
ViewerErrorIds,
|
|
13
10
|
} from '../index';
|
|
11
|
+
import { BaseAsset, loadAsset, prepareAssetForScene } from '../internal/asset-helper';
|
|
14
12
|
import { cloneModelAssetContainer } from '../internal/cloning-helper';
|
|
15
|
-
import { getInternalMetadataValue } from '../internal/metadata-helper';
|
|
16
13
|
import { isArray } from 'lodash-es';
|
|
17
14
|
|
|
18
|
-
/**
|
|
19
|
-
* Contains cbn custom data, like decals.
|
|
20
|
-
* This is just a temporary type, as the `loadAssetContainer` function only returns an asset container, which can be
|
|
21
|
-
* altered by our file loader plugin.
|
|
22
|
-
* After loading the model, `cbnData` is cropped and a pure asset container is available for further processing.
|
|
23
|
-
*
|
|
24
|
-
* @internal
|
|
25
|
-
*/
|
|
26
|
-
export class ExtendedAssetContainer extends AssetContainer {
|
|
27
|
-
cbnData?: CbnBabylonFileData;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type CbnBabylonFileData = { decals?: ParsedDecalConfiguration[] };
|
|
31
15
|
export type ParsedDecalConfiguration = DecalConfiguration & { materialId?: string; tags?: string };
|
|
32
16
|
|
|
33
17
|
export type ModelAssetDefinition = {
|
|
@@ -62,20 +46,14 @@ export type ModelCloneOptions = {
|
|
|
62
46
|
tagNamingStrategy?: TagNamingStrategy;
|
|
63
47
|
};
|
|
64
48
|
|
|
65
|
-
type
|
|
66
|
-
|
|
67
|
-
url: string;
|
|
68
|
-
state: ModelAssetState;
|
|
69
|
-
assetContainer: AssetContainer;
|
|
70
|
-
cbnBabylonFileData?: CbnBabylonFileData;
|
|
49
|
+
type ModelAsset = BaseAsset & {
|
|
50
|
+
decals?: ParsedDecalConfiguration[];
|
|
71
51
|
isClone: boolean;
|
|
72
52
|
// only set for "instantiated" clones
|
|
73
53
|
sourceModelName?: string;
|
|
74
54
|
visibilityCallId?: number;
|
|
75
55
|
};
|
|
76
56
|
|
|
77
|
-
type ModelAssetState = 'notLoaded' | 'loading' | 'loaded' | 'inScene';
|
|
78
|
-
|
|
79
57
|
/**
|
|
80
58
|
* Manager for handling 3d models.\
|
|
81
59
|
* Responsible for loading models and handling their visibility.\
|
|
@@ -84,12 +62,11 @@ type ModelAssetState = 'notLoaded' | 'loading' | 'loaded' | 'inScene';
|
|
|
84
62
|
export class ModelManager {
|
|
85
63
|
/**
|
|
86
64
|
* CAUTION: this has to be in sync with the Combeenation backend!
|
|
87
|
-
* @internal
|
|
88
65
|
*/
|
|
89
66
|
public static readonly CBN_FALLBACK_MODEL_ASSET_NAME = '$fallback';
|
|
90
67
|
|
|
91
|
-
protected _modelAssets: { [name: string]:
|
|
92
|
-
protected _fallbackModelAsset:
|
|
68
|
+
protected _modelAssets: { [name: string]: ModelAsset } = {};
|
|
69
|
+
protected _fallbackModelAsset: ModelAsset = {
|
|
93
70
|
name: ModelManager.CBN_FALLBACK_MODEL_ASSET_NAME,
|
|
94
71
|
url: '',
|
|
95
72
|
state: 'loaded',
|
|
@@ -113,6 +90,7 @@ export class ModelManager {
|
|
|
113
90
|
return inputStr.split('/').join('.');
|
|
114
91
|
}
|
|
115
92
|
|
|
93
|
+
/** @internal */
|
|
116
94
|
public constructor(protected viewer: Viewer) {}
|
|
117
95
|
|
|
118
96
|
/**
|
|
@@ -144,11 +122,11 @@ export class ModelManager {
|
|
|
144
122
|
for (const { name, url } of modelAssets) {
|
|
145
123
|
const existingModel = this._modelAssets[name];
|
|
146
124
|
if (existingModel) {
|
|
147
|
-
console.warn(`Model ${name} is already registered`);
|
|
125
|
+
console.warn(`Model "${name}" is already registered`);
|
|
148
126
|
return;
|
|
149
127
|
}
|
|
150
128
|
|
|
151
|
-
const model:
|
|
129
|
+
const model: ModelAsset = { name, url, state: 'notLoaded', assetContainer: new AssetContainer(), isClone: false };
|
|
152
130
|
this._modelAssets[name] = model;
|
|
153
131
|
}
|
|
154
132
|
}
|
|
@@ -170,7 +148,7 @@ export class ModelManager {
|
|
|
170
148
|
}
|
|
171
149
|
|
|
172
150
|
if (model.state !== 'notLoaded') {
|
|
173
|
-
console.warn(`Model ${name} is already loaded or currently loading`);
|
|
151
|
+
console.warn(`Model "${name}" is already loaded or currently loading`);
|
|
174
152
|
return;
|
|
175
153
|
}
|
|
176
154
|
|
|
@@ -187,14 +165,14 @@ export class ModelManager {
|
|
|
187
165
|
public async setModelVisibility(
|
|
188
166
|
modelVisibility: ModelVisibilityEntry | ModelVisibilityEntry[]
|
|
189
167
|
): Promise<ModelVisibilityEntry[]> {
|
|
190
|
-
const
|
|
191
|
-
const
|
|
168
|
+
const modelsToShow: ModelAsset[] = [];
|
|
169
|
+
const modelsToHide: ModelAsset[] = [];
|
|
192
170
|
|
|
193
171
|
if (!isArray(modelVisibility)) {
|
|
194
172
|
modelVisibility = [modelVisibility];
|
|
195
173
|
}
|
|
196
174
|
|
|
197
|
-
|
|
175
|
+
const loadModelProms = modelVisibility.map(async entry => {
|
|
198
176
|
const model = this._getModel(entry.name);
|
|
199
177
|
if (!model) {
|
|
200
178
|
throw new ViewerError({
|
|
@@ -206,8 +184,10 @@ export class ModelManager {
|
|
|
206
184
|
// there can be multiple "setModelVisibility" calls while the model is loading
|
|
207
185
|
// loading can be awaited, but it has to be ensured, that the last call of "setModelVisibility" has priority
|
|
208
186
|
// therefore we store the id of the call in the model
|
|
209
|
-
|
|
210
|
-
model.visibilityCallId
|
|
187
|
+
model.visibilityCallId = (model.visibilityCallId ?? 0) + 1;
|
|
188
|
+
const curVisibilityCallId = model.visibilityCallId;
|
|
189
|
+
let showModel = false;
|
|
190
|
+
let hideModel = false;
|
|
211
191
|
|
|
212
192
|
if (entry.visible) {
|
|
213
193
|
if (model.state === 'notLoaded') {
|
|
@@ -215,45 +195,51 @@ export class ModelManager {
|
|
|
215
195
|
|
|
216
196
|
// check if this is still the latest visibility call
|
|
217
197
|
if (model.visibilityCallId === curVisibilityCallId) {
|
|
218
|
-
|
|
198
|
+
showModel = true;
|
|
219
199
|
}
|
|
220
200
|
} else if (model.state === 'loading') {
|
|
221
201
|
await this._loadModelPromises[model.name];
|
|
222
202
|
|
|
223
203
|
if (model.visibilityCallId === curVisibilityCallId) {
|
|
224
|
-
|
|
204
|
+
showModel = true;
|
|
225
205
|
}
|
|
226
206
|
} else if (model.state === 'loaded') {
|
|
227
|
-
|
|
207
|
+
showModel = true;
|
|
228
208
|
}
|
|
229
209
|
} else {
|
|
230
210
|
if (model.state === 'loading') {
|
|
231
211
|
await this._loadModelPromises[model.name];
|
|
232
212
|
|
|
233
213
|
if (model.visibilityCallId === curVisibilityCallId) {
|
|
234
|
-
|
|
214
|
+
hideModel = true;
|
|
235
215
|
}
|
|
236
216
|
} else if (model.state === 'inScene') {
|
|
237
|
-
|
|
217
|
+
hideModel = true;
|
|
238
218
|
}
|
|
239
219
|
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
for (const showModel of showModels) {
|
|
243
|
-
await this._prepareModelForScene(showModel);
|
|
244
|
-
}
|
|
245
220
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
221
|
+
if (showModel) {
|
|
222
|
+
await this._prepareModelForScene(model);
|
|
223
|
+
modelsToShow.push(model);
|
|
224
|
+
} else if (hideModel) {
|
|
225
|
+
modelsToHide.push(model);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
// model loading and preparation (e.g. create materials) is done simultaniously for all models to save time
|
|
229
|
+
// model (`ModelManager._loadModelPromises`) and material queues (`MaterialManager._createMaterialPromises`) are
|
|
230
|
+
// responsible for correct loading state handling of these instances
|
|
231
|
+
await Promise.all(loadModelProms);
|
|
253
232
|
|
|
254
233
|
const returnVal: ModelVisibilityEntry[] = [];
|
|
255
|
-
|
|
256
|
-
|
|
234
|
+
modelsToShow.forEach(model => {
|
|
235
|
+
// show model is not async, as preparation has already been done
|
|
236
|
+
this._showModel(model, true);
|
|
237
|
+
returnVal.push({ name: model.name, visible: true });
|
|
238
|
+
});
|
|
239
|
+
modelsToHide.forEach(model => {
|
|
240
|
+
this._hideModel(model);
|
|
241
|
+
returnVal.push({ name: model.name, visible: false });
|
|
242
|
+
});
|
|
257
243
|
|
|
258
244
|
return returnVal;
|
|
259
245
|
}
|
|
@@ -294,7 +280,7 @@ export class ModelManager {
|
|
|
294
280
|
await this._loadModelPromises[sourceModel.name];
|
|
295
281
|
}
|
|
296
282
|
|
|
297
|
-
const clonedModel:
|
|
283
|
+
const clonedModel: ModelAsset = {
|
|
298
284
|
name: newModelName,
|
|
299
285
|
url: sourceModel.url,
|
|
300
286
|
state: 'loaded',
|
|
@@ -400,14 +386,14 @@ export class ModelManager {
|
|
|
400
386
|
await this._prepareModelForScene(model);
|
|
401
387
|
}
|
|
402
388
|
|
|
403
|
-
return model.
|
|
389
|
+
return model.decals ?? [];
|
|
404
390
|
}
|
|
405
391
|
|
|
406
392
|
/**
|
|
407
393
|
* Get model by name
|
|
408
394
|
*/
|
|
409
|
-
protected _getModel(name: string):
|
|
410
|
-
if (name ===
|
|
395
|
+
protected _getModel(name: string): ModelAsset | undefined {
|
|
396
|
+
if (name === ModelManager.CBN_FALLBACK_MODEL_ASSET_NAME) {
|
|
411
397
|
return this._fallbackModelAsset;
|
|
412
398
|
}
|
|
413
399
|
|
|
@@ -420,67 +406,16 @@ export class ModelManager {
|
|
|
420
406
|
/**
|
|
421
407
|
* Load model into scene, but don't show it immediately
|
|
422
408
|
*/
|
|
423
|
-
protected async _loadModel(model:
|
|
409
|
+
protected async _loadModel(model: ModelAsset): Promise<void> {
|
|
424
410
|
const loadModelPromise = async (): Promise<void> => {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
// CB-9240: Babylon.js doesn't recognize gzipped babylon files (`.babylon.gz`) as such, leading to a warning
|
|
430
|
-
// message, therefore we overwrite the plugin extension actively for such files
|
|
431
|
-
let pluginExtension;
|
|
432
|
-
try {
|
|
433
|
-
// URL constructor can throw for "valid" URL, which happened to be the case for the test asset environment where
|
|
434
|
-
// the urls are relative (starting with ".")
|
|
435
|
-
const urlObj = new URL(model.url);
|
|
436
|
-
if (urlObj.pathname.endsWith('.babylon.gz')) {
|
|
437
|
-
pluginExtension = '.babylon';
|
|
438
|
-
}
|
|
439
|
-
} catch (e) {}
|
|
440
|
-
|
|
441
|
-
let assetContainer;
|
|
442
|
-
try {
|
|
443
|
-
const fullContainer = (await SceneLoader.LoadAssetContainerAsync(
|
|
444
|
-
'',
|
|
445
|
-
model.url,
|
|
446
|
-
this.viewer.scene,
|
|
447
|
-
undefined,
|
|
448
|
-
pluginExtension
|
|
449
|
-
)) as ExtendedAssetContainer;
|
|
450
|
-
|
|
451
|
-
// crop and store custom cbn data from .babylon file
|
|
452
|
-
model.cbnBabylonFileData = fullContainer.cbnData;
|
|
453
|
-
delete fullContainer.cbnData;
|
|
454
|
-
|
|
455
|
-
// from here it's a basic asset container again
|
|
456
|
-
assetContainer = fullContainer as AssetContainer;
|
|
457
|
-
} catch (e) {
|
|
458
|
-
throw new ViewerError({
|
|
459
|
-
id: ViewerErrorIds.AssetLoadingFailed,
|
|
460
|
-
message: (e as Error).message,
|
|
461
|
-
});
|
|
462
|
-
}
|
|
411
|
+
const { cbnData } = await loadAsset(model, this.viewer);
|
|
412
|
+
|
|
413
|
+
model.decals = cbnData?.decals;
|
|
463
414
|
|
|
464
415
|
// remove all lights and cameras from the asset, as this data should be handled globally in the scene instead of
|
|
465
416
|
// being in a model context
|
|
466
|
-
[...assetContainer.lights].forEach(light => light.dispose());
|
|
467
|
-
[...assetContainer.cameras].forEach(camera => camera.dispose());
|
|
468
|
-
|
|
469
|
-
// materials should be a "global" thing and not assigned to an asset container
|
|
470
|
-
// this is not relevant in most of the cases, since materials are cropped from babylon models on the CBN server
|
|
471
|
-
// anyway
|
|
472
|
-
assetContainer.materials.forEach(material => {
|
|
473
|
-
this.viewer.scene.addMaterial(material);
|
|
474
|
-
material._parentContainer = null;
|
|
475
|
-
});
|
|
476
|
-
assetContainer.materials = [];
|
|
477
|
-
|
|
478
|
-
// environment texture and intensity has been overwritten by load asset container function
|
|
479
|
-
this.viewer.scene.environmentTexture = curEnvTexture;
|
|
480
|
-
this.viewer.scene.environmentIntensity = curEnvIntensity;
|
|
481
|
-
|
|
482
|
-
model.assetContainer = assetContainer;
|
|
483
|
-
model.state = 'loaded';
|
|
417
|
+
[...model.assetContainer.lights].forEach(light => light.dispose());
|
|
418
|
+
[...model.assetContainer.cameras].forEach(camera => camera.dispose());
|
|
484
419
|
|
|
485
420
|
delete this._loadModelPromises[model.name];
|
|
486
421
|
};
|
|
@@ -494,7 +429,7 @@ export class ModelManager {
|
|
|
494
429
|
* This is typically done before the model is added to the scene to avoid changes on the visible model, which ensures
|
|
495
430
|
* a smooth model switch behaviour.
|
|
496
431
|
*/
|
|
497
|
-
protected async _prepareModelForScene(model:
|
|
432
|
+
protected async _prepareModelForScene(model: ModelAsset): Promise<void> {
|
|
498
433
|
if (model.sourceModelName) {
|
|
499
434
|
// source model has to be prepared for scene as well in "instantiate" mode, because materials would not be
|
|
500
435
|
// loaded otherwise
|
|
@@ -504,10 +439,7 @@ export class ModelManager {
|
|
|
504
439
|
await this._prepareModelForScene(sourceModel);
|
|
505
440
|
}
|
|
506
441
|
|
|
507
|
-
await this.viewer
|
|
508
|
-
// parameter manager did his job, now apply the deferred materials to all meshes that are not explicitely hidden by
|
|
509
|
-
// the parameter manager
|
|
510
|
-
await this._applyDeferredMaterialsForAllVisibleMeshes(model);
|
|
442
|
+
await prepareAssetForScene(model, this.viewer);
|
|
511
443
|
}
|
|
512
444
|
|
|
513
445
|
/**
|
|
@@ -517,7 +449,7 @@ export class ModelManager {
|
|
|
517
449
|
* @param skipPreparation optionally skip applying parameter values to model before showing it
|
|
518
450
|
* (see {@link _prepareModelForScene})
|
|
519
451
|
*/
|
|
520
|
-
protected async _showModel(model:
|
|
452
|
+
protected async _showModel(model: ModelAsset, skipPreparation?: boolean): Promise<void> {
|
|
521
453
|
if (!skipPreparation) {
|
|
522
454
|
await this._prepareModelForScene(model);
|
|
523
455
|
}
|
|
@@ -529,23 +461,8 @@ export class ModelManager {
|
|
|
529
461
|
/**
|
|
530
462
|
* Remove assets of asset container from scene
|
|
531
463
|
*/
|
|
532
|
-
protected _hideModel(model:
|
|
464
|
+
protected _hideModel(model: ModelAsset): void {
|
|
533
465
|
model.assetContainer.removeFromScene();
|
|
534
466
|
model.state = 'loaded';
|
|
535
467
|
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Creates and assigns each "deferred" material to the corresponding mesh, if the mesh is visible.
|
|
539
|
-
* Model should be ready to use immediately after this function has done it's job.
|
|
540
|
-
*/
|
|
541
|
-
protected async _applyDeferredMaterialsForAllVisibleMeshes(model: Model): Promise<void> {
|
|
542
|
-
for (const mesh of model.assetContainer.meshes) {
|
|
543
|
-
const deferredMaterial = getInternalMetadataValue(mesh, 'deferredMaterial');
|
|
544
|
-
const rawVisibleValue = this.viewer.parameterManager.getParameterValueOfNode(mesh, BuiltInParameter.Visible);
|
|
545
|
-
const visible = rawVisibleValue === undefined || ParameterManager.parseBoolean(rawVisibleValue) === true;
|
|
546
|
-
if (deferredMaterial && visible) {
|
|
547
|
-
await this.viewer.materialManager.setMaterialOnMesh(deferredMaterial as string, mesh);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
468
|
}
|
|
@@ -97,7 +97,7 @@ export type ParameterObserver = (payload: ParameterObserverPayload) => Promise<v
|
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
99
|
* Payload of parameter observer.\
|
|
100
|
-
* Contains current data of parameter entry, which can be
|
|
100
|
+
* Contains current data of parameter entry, which can be useful for implementing the dedicated observer
|
|
101
101
|
*/
|
|
102
102
|
export type ParameterObserverPayload = {
|
|
103
103
|
subject: ParameterSubject;
|
|
@@ -258,6 +258,7 @@ export class ParameterManager {
|
|
|
258
258
|
protected _parameterEntries: ParameterEntry[] = [];
|
|
259
259
|
protected _parameterObserver: { [parameterName: ParameterName]: ParameterObserver } = {};
|
|
260
260
|
|
|
261
|
+
/** @internal */
|
|
261
262
|
public constructor(protected viewer: Viewer) {
|
|
262
263
|
this._addBuiltInParameterObservers();
|
|
263
264
|
}
|
|
@@ -459,49 +460,28 @@ export class ParameterManager {
|
|
|
459
460
|
}
|
|
460
461
|
|
|
461
462
|
/**
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
* @internal
|
|
466
|
-
*/
|
|
467
|
-
public getParameterValueOfNode(node: TransformNode, parameterName: string): ParameterValue | undefined {
|
|
468
|
-
const nodeParamValue = this.getParameterValue({ nodeName: node.name }, parameterName);
|
|
469
|
-
if (nodeParamValue !== undefined) {
|
|
470
|
-
return nodeParamValue;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const tags = getTagsAsStrArr(node);
|
|
474
|
-
const tagParamValue = tags.reduce<ParameterValue | undefined>((accValue, curTag) => {
|
|
475
|
-
// NOTE: it is possible that values are available for multiple tags
|
|
476
|
-
// in this case the resulting parameter value is quite "random" as the last tag in the tag string of the node has
|
|
477
|
-
// priority
|
|
478
|
-
const tagParamValue = this.getParameterValue({ tagName: curTag }, parameterName);
|
|
479
|
-
return accValue ?? tagParamValue;
|
|
480
|
-
}, undefined);
|
|
481
|
-
|
|
482
|
-
return tagParamValue;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* @returns Desired parameter value of a certain material.
|
|
487
|
-
* Tags are considered as well but have lower priority than material parameters, as these are more specific.
|
|
488
|
-
* Unused ATM, but added for consistency as counter part for {@link getParameterValueOfNode}
|
|
463
|
+
* Retrieves visibility state of node, considering pending parameter values and node hierarchy.\
|
|
464
|
+
* `node.isEnabled()` alone is insufficient here, as visibility observers for the desired node or one of it's parents
|
|
465
|
+
* may be called in the same cycle, overwriting the visibility state again.
|
|
489
466
|
*
|
|
490
467
|
* @internal
|
|
491
468
|
*/
|
|
492
|
-
public
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
469
|
+
public getNestedVisibilityParameterValueOfNode(node: TransformNode): boolean {
|
|
470
|
+
let curNode: TransformNode | null = node;
|
|
471
|
+
let visibleByParameter: boolean | undefined = undefined;
|
|
472
|
+
while (curNode && visibleByParameter !== false) {
|
|
473
|
+
const curNodeVisibleByParameter = this._getParameterValueOfNode(curNode, BuiltInParameter.Visible);
|
|
474
|
+
if (curNodeVisibleByParameter !== undefined) {
|
|
475
|
+
visibleByParameter = curNodeVisibleByParameter as boolean;
|
|
476
|
+
}
|
|
477
|
+
curNode = curNode.parent as TransformNode;
|
|
496
478
|
}
|
|
497
479
|
|
|
498
|
-
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
return accValue ?? tagParamValue;
|
|
502
|
-
}, undefined);
|
|
480
|
+
// check state of nested visibility parameter value and fall back to basic node enabled state
|
|
481
|
+
const visible =
|
|
482
|
+
visibleByParameter !== undefined ? ParameterManager.parseBoolean(visibleByParameter) : node.isEnabled();
|
|
503
483
|
|
|
504
|
-
return
|
|
484
|
+
return visible;
|
|
505
485
|
}
|
|
506
486
|
|
|
507
487
|
/**
|
|
@@ -511,33 +491,40 @@ export class ParameterManager {
|
|
|
511
491
|
this.setParameterObserver(BuiltInParameter.Visible, async ({ nodes, newValue }) => {
|
|
512
492
|
const visible = ParameterManager.parseBoolean(newValue);
|
|
513
493
|
|
|
514
|
-
|
|
515
|
-
|
|
494
|
+
const observerProms = nodes.map(async node => {
|
|
495
|
+
const visibleNested = this.getNestedVisibilityParameterValueOfNode(node);
|
|
496
|
+
if (visibleNested) {
|
|
516
497
|
// if a mesh gets visible by this operation we have to activate the assigned material which is stored in the
|
|
517
498
|
// internal metadata
|
|
518
499
|
// => consider child meshes as well (CB-10143)
|
|
519
500
|
const activatedNodes = [node, ...node.getChildMeshes(false)];
|
|
520
|
-
|
|
521
|
-
const
|
|
522
|
-
|
|
501
|
+
const setMaterialProms = activatedNodes.map(async an => {
|
|
502
|
+
const anVisibleNested = this.getNestedVisibilityParameterValueOfNode(an);
|
|
503
|
+
const anDeferredMaterial = getInternalMetadataValue(an, 'deferredMaterial');
|
|
504
|
+
// skip applying material if it's already set via the parameter
|
|
505
|
+
const anMaterialParamValue = this._getParameterValueOfNode(an, BuiltInParameter.Material) as
|
|
506
|
+
| string
|
|
507
|
+
| undefined;
|
|
508
|
+
if (anVisibleNested && anDeferredMaterial && !anMaterialParamValue) {
|
|
523
509
|
await this.viewer.materialManager.setMaterialOnMesh(
|
|
524
|
-
|
|
510
|
+
anDeferredMaterial,
|
|
525
511
|
// this cast is fine, as deferred material can only be set on meshes
|
|
526
|
-
|
|
512
|
+
an as AbstractMesh
|
|
527
513
|
);
|
|
528
514
|
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
node.setEnabled(true);
|
|
532
|
-
} else {
|
|
533
|
-
node.setEnabled(false);
|
|
515
|
+
});
|
|
516
|
+
await Promise.all(setMaterialProms);
|
|
534
517
|
}
|
|
535
|
-
|
|
518
|
+
|
|
519
|
+
// set enabled state anyway
|
|
520
|
+
node.setEnabled(visible);
|
|
521
|
+
});
|
|
522
|
+
await Promise.all(observerProms);
|
|
536
523
|
});
|
|
537
524
|
this.setParameterObserver(BuiltInParameter.Material, async ({ nodes, newValue }) => {
|
|
538
525
|
const material = ParameterManager.parseString(newValue);
|
|
539
526
|
|
|
540
|
-
|
|
527
|
+
const observerProms = nodes.map(async node => {
|
|
541
528
|
if (!(node instanceof AbstractMesh)) {
|
|
542
529
|
throw new ViewerError({
|
|
543
530
|
id: ViewerErrorIds.InvalidParameterSubject,
|
|
@@ -545,28 +532,15 @@ export class ParameterManager {
|
|
|
545
532
|
});
|
|
546
533
|
}
|
|
547
534
|
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
// we have to go through all parents as well, because a parent may have been set to false, which also disables
|
|
551
|
-
// this child node
|
|
552
|
-
let curNode: TransformNode | null = node;
|
|
553
|
-
let visibleByParameter: boolean | undefined = undefined;
|
|
554
|
-
while (curNode && visibleByParameter !== false) {
|
|
555
|
-
const curNodeVisibleByParameter = this.getParameterValueOfNode(curNode, BuiltInParameter.Visible);
|
|
556
|
-
if (curNodeVisibleByParameter !== undefined) {
|
|
557
|
-
visibleByParameter = curNodeVisibleByParameter as boolean;
|
|
558
|
-
}
|
|
559
|
-
curNode = curNode.parent as TransformNode;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const visible =
|
|
563
|
-
visibleByParameter !== undefined ? ParameterManager.parseBoolean(visibleByParameter) : node.isEnabled();
|
|
535
|
+
// only set material if the mesh is visible, or gets visible by the parameter update
|
|
536
|
+
const visible = this.getNestedVisibilityParameterValueOfNode(node);
|
|
564
537
|
if (visible) {
|
|
565
538
|
await this.viewer.materialManager.setMaterialOnMesh(material, node);
|
|
566
539
|
} else {
|
|
567
540
|
setInternalMetadataValue(node, 'deferredMaterial', material);
|
|
568
541
|
}
|
|
569
|
-
}
|
|
542
|
+
});
|
|
543
|
+
await Promise.all(observerProms);
|
|
570
544
|
});
|
|
571
545
|
this.setParameterObserver(BuiltInParameter.Position, async ({ nodes, newValue }) => {
|
|
572
546
|
const position = ParameterManager.parseVector(newValue);
|
|
@@ -710,12 +684,15 @@ export class ParameterManager {
|
|
|
710
684
|
const tagParamEntries = parameterEntries.filter(entry => isTagParameterSubject(entry.subject));
|
|
711
685
|
const nonTagParamEntries = parameterEntries.filter(entry => !isTagParameterSubject(entry.subject));
|
|
712
686
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
687
|
+
const tagParamProms = tagParamEntries.map(entry =>
|
|
688
|
+
this._applyParameterValue(entry.subject, entry.parameterName, assetContainer)
|
|
689
|
+
);
|
|
690
|
+
await Promise.all(tagParamProms);
|
|
691
|
+
|
|
692
|
+
const nonTagParamProms = nonTagParamEntries.map(entry =>
|
|
693
|
+
this._applyParameterValue(entry.subject, entry.parameterName, assetContainer)
|
|
694
|
+
);
|
|
695
|
+
await Promise.all(nonTagParamProms);
|
|
719
696
|
}
|
|
720
697
|
|
|
721
698
|
/**
|
|
@@ -811,4 +788,46 @@ export class ParameterManager {
|
|
|
811
788
|
|
|
812
789
|
return entries;
|
|
813
790
|
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* @returns Desired parameter value of a certain node.\
|
|
794
|
+
* Tags are considered as well but have lower priority than node parameters, as these are more specific.
|
|
795
|
+
*/
|
|
796
|
+
protected _getParameterValueOfNode(node: TransformNode, parameterName: string): ParameterValue | undefined {
|
|
797
|
+
const nodeParamValue = this.getParameterValue({ nodeName: node.name }, parameterName);
|
|
798
|
+
if (nodeParamValue !== undefined) {
|
|
799
|
+
return nodeParamValue;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const tags = getTagsAsStrArr(node);
|
|
803
|
+
const tagParamValue = tags.reduce<ParameterValue | undefined>((accValue, curTag) => {
|
|
804
|
+
// NOTE: it is possible that values are available for multiple tags
|
|
805
|
+
// in this case the resulting parameter value is quite "random" as the last tag in the tag string of the node has
|
|
806
|
+
// priority
|
|
807
|
+
const tagParamValue = this.getParameterValue({ tagName: curTag }, parameterName);
|
|
808
|
+
return accValue ?? tagParamValue;
|
|
809
|
+
}, undefined);
|
|
810
|
+
|
|
811
|
+
return tagParamValue;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* @returns Desired parameter value of a certain material.\
|
|
816
|
+
* Tags are considered as well but have lower priority than material parameters, as these are more specific.\
|
|
817
|
+
* Unused ATM, but added for consistency as counter part for {@link _getParameterValueOfNode}
|
|
818
|
+
*/
|
|
819
|
+
protected _getParameterValueOfMaterial(material: Material, parameterName: string): ParameterValue | undefined {
|
|
820
|
+
const materialParamValue = this.getParameterValue({ materialName: material.name }, parameterName);
|
|
821
|
+
if (materialParamValue !== undefined) {
|
|
822
|
+
return materialParamValue;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const tags = getTagsAsStrArr(material);
|
|
826
|
+
const tagParamValue = tags.reduce<ParameterValue | undefined>((accValue, curTag) => {
|
|
827
|
+
const tagParamValue = this.getParameterValue({ tagName: curTag }, parameterName);
|
|
828
|
+
return accValue ?? tagParamValue;
|
|
829
|
+
}, undefined);
|
|
830
|
+
|
|
831
|
+
return tagParamValue;
|
|
832
|
+
}
|
|
814
833
|
}
|