@combeenation/3d-viewer 19.2.0-beta1 → 20.0.0-alpha2

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.
Files changed (66) hide show
  1. package/dist/lib-cjs/buildinfo.json +1 -1
  2. package/dist/lib-cjs/commonjs.tsconfig.tsbuildinfo +1 -1
  3. package/dist/lib-cjs/index.d.ts +1 -1
  4. package/dist/lib-cjs/index.js +1 -1
  5. package/dist/lib-cjs/index.js.map +1 -1
  6. package/dist/lib-cjs/internal/animation-helper.d.ts +2 -0
  7. package/dist/lib-cjs/internal/animation-helper.js +16 -0
  8. package/dist/lib-cjs/internal/animation-helper.js.map +1 -0
  9. package/dist/lib-cjs/internal/cloning-helper.js +4 -2
  10. package/dist/lib-cjs/internal/cloning-helper.js.map +1 -1
  11. package/dist/lib-cjs/internal/export-helper.js +1 -1
  12. package/dist/lib-cjs/internal/export-helper.js.map +1 -1
  13. package/dist/lib-cjs/internal/math-helper.d.ts +9 -0
  14. package/dist/lib-cjs/internal/math-helper.js +43 -0
  15. package/dist/lib-cjs/internal/math-helper.js.map +1 -0
  16. package/dist/lib-cjs/internal/{paintable-helper.d.ts → parameter/paintable-parameter-helper.d.ts} +3 -3
  17. package/dist/lib-cjs/internal/{paintable-helper.js → parameter/paintable-parameter-helper.js} +26 -26
  18. package/dist/lib-cjs/internal/parameter/paintable-parameter-helper.js.map +1 -0
  19. package/dist/lib-cjs/internal/parameter/parameter-helper.d.ts +2 -0
  20. package/dist/lib-cjs/internal/parameter/parameter-helper.js +214 -0
  21. package/dist/lib-cjs/internal/parameter/parameter-helper.js.map +1 -0
  22. package/dist/lib-cjs/internal/{texture-parameter-helper.d.ts → parameter/texture-parameter-helper.d.ts} +1 -2
  23. package/dist/lib-cjs/internal/{texture-parameter-helper.js → parameter/texture-parameter-helper.js} +15 -16
  24. package/dist/lib-cjs/internal/parameter/texture-parameter-helper.js.map +1 -0
  25. package/dist/lib-cjs/manager/camera-manager.js +2 -9
  26. package/dist/lib-cjs/manager/camera-manager.js.map +1 -1
  27. package/dist/lib-cjs/manager/debug-manager.d.ts +5 -1
  28. package/dist/lib-cjs/manager/debug-manager.js +21 -5
  29. package/dist/lib-cjs/manager/debug-manager.js.map +1 -1
  30. package/dist/lib-cjs/manager/model-manager.d.ts +35 -4
  31. package/dist/lib-cjs/manager/model-manager.js +50 -8
  32. package/dist/lib-cjs/manager/model-manager.js.map +1 -1
  33. package/dist/lib-cjs/manager/parameter-manager.d.ts +37 -20
  34. package/dist/lib-cjs/manager/parameter-manager.js +109 -204
  35. package/dist/lib-cjs/manager/parameter-manager.js.map +1 -1
  36. package/dist/lib-cjs/manager/scene-manager.d.ts +1 -0
  37. package/dist/lib-cjs/manager/scene-manager.js +2 -1
  38. package/dist/lib-cjs/manager/scene-manager.js.map +1 -1
  39. package/dist/lib-cjs/utils/viewer-utils.d.ts +35 -0
  40. package/dist/lib-cjs/utils/viewer-utils.js +112 -0
  41. package/dist/lib-cjs/utils/viewer-utils.js.map +1 -0
  42. package/dist/lib-cjs/viewer.d.ts +3 -1
  43. package/dist/lib-cjs/viewer.js +4 -0
  44. package/dist/lib-cjs/viewer.js.map +1 -1
  45. package/package.json +13 -12
  46. package/src/index.ts +1 -1
  47. package/src/internal/animation-helper.ts +18 -0
  48. package/src/internal/cloning-helper.ts +4 -2
  49. package/src/internal/export-helper.ts +1 -2
  50. package/src/internal/math-helper.ts +46 -0
  51. package/src/internal/{paintable-helper.ts → parameter/paintable-parameter-helper.ts} +35 -35
  52. package/src/internal/parameter/parameter-helper.ts +263 -0
  53. package/src/internal/{texture-parameter-helper.ts → parameter/texture-parameter-helper.ts} +13 -3
  54. package/src/manager/camera-manager.ts +2 -10
  55. package/src/manager/debug-manager.ts +35 -6
  56. package/src/manager/model-manager.ts +81 -13
  57. package/src/manager/parameter-manager.ts +138 -232
  58. package/src/manager/scene-manager.ts +3 -1
  59. package/src/utils/viewer-utils.ts +149 -0
  60. package/src/viewer.ts +6 -0
  61. package/dist/lib-cjs/internal/paintable-helper.js.map +0 -1
  62. package/dist/lib-cjs/internal/texture-parameter-helper.js.map +0 -1
  63. package/dist/lib-cjs/utils/node-utils.d.ts +0 -33
  64. package/dist/lib-cjs/utils/node-utils.js +0 -130
  65. package/dist/lib-cjs/utils/node-utils.js.map +0 -1
  66. package/src/utils/node-utils.ts +0 -168
@@ -1,10 +1,8 @@
1
1
  import {
2
- AbstractMesh,
3
2
  Color3,
4
3
  IAssetContainer,
5
4
  Material,
6
5
  PBRMaterial,
7
- StandardMaterial,
8
6
  Tags,
9
7
  TransformNode,
10
8
  Vector3,
@@ -12,16 +10,16 @@ import {
12
10
  ViewerError,
13
11
  ViewerErrorIds,
14
12
  } from '../index';
15
- import { getInternalMetadataValue, setInternalMetadataValue } from '../internal/metadata-helper';
16
- import { paintableParameterObserver } from '../internal/paintable-helper';
17
- import { getTagsAsStrArr } from '../internal/tags-helper';
13
+ import { createPaintableParameter } from '../internal/parameter/paintable-parameter-helper';
14
+ import { createBuiltInParameter } from '../internal/parameter/parameter-helper';
18
15
  import {
19
16
  BuiltInTextureParameter,
20
17
  BuiltInTextureParameterKeys,
21
18
  ParameterTextureChannelsKeys,
22
19
  createBuiltInTextureParameter,
23
- } from '../internal/texture-parameter-helper';
24
- import { capitalize, isNumber, isString } from 'lodash-es';
20
+ } from '../internal/parameter/texture-parameter-helper';
21
+ import { getTagsAsStrArr } from '../internal/tags-helper';
22
+ import { capitalize, isArray, isNumber, isObject, isString } from 'lodash-es';
25
23
 
26
24
  /**
27
25
  * Parameters with a built in observer implementation
@@ -51,10 +49,16 @@ export const LegacyParameter = {
51
49
  Paintable: 'paintable',
52
50
  };
53
51
 
52
+ export type ParameterName = string;
53
+
54
54
  export type MorphTargetParameterValue = { name: string; value: number };
55
+ const isMorphTargetParameterValue = (value: InputParameterValue): value is MorphTargetParameterValue =>
56
+ isObject(value) && 'name' in value && isString(value.name) && 'value' in value && isNumber(value.value);
57
+ const isMorphTargetsParameterValue = (value: ParameterValue): value is MorphTargetParameterValue[] =>
58
+ isArray(value) && value.every(mT => isMorphTargetParameterValue(mT));
55
59
 
56
- export type ParameterName = string;
57
- export type ParameterValue = string | number | boolean | Vector3 | Color3 | MorphTargetParameterValue;
60
+ export type ParameterValue = string | number | boolean | Vector3 | Color3 | MorphTargetParameterValue[];
61
+ export type InputParameterValue = Exclude<ParameterValue, MorphTargetParameterValue[]> | MorphTargetParameterValue;
58
62
 
59
63
  export type TagParameterSubject = { tagName: string; nodeName?: never; materialName?: never };
60
64
  export type NodeParameterSubject = { tagName?: never; nodeName: string; materialName?: never };
@@ -87,9 +91,14 @@ const isMaterialParameterSubject = (subject: ParameterSubject): subject is Mater
87
91
  */
88
92
  export type ParameterValues = (ParameterSubject & {
89
93
  parameterName: ParameterName;
90
- value: ParameterValue;
94
+ value: InputParameterValue;
95
+ options?: ParameterOptions;
91
96
  })[];
92
97
 
98
+ export type ParameterOptions = {
99
+ animationTimeMs?: number;
100
+ };
101
+
93
102
  /**
94
103
  * Definition of callback function for parameter change
95
104
  */
@@ -105,6 +114,7 @@ export type ParameterObserverPayload = {
105
114
  materials: Material[];
106
115
  newValue: ParameterValue;
107
116
  oldValue: ParameterValue | undefined;
117
+ options?: ParameterOptions;
108
118
  };
109
119
 
110
120
  // internal type which holds the data of a certain parameter entry
@@ -113,9 +123,12 @@ type ParameterEntry = {
113
123
  parameterName: ParameterName;
114
124
  value: ParameterValue;
115
125
  oldValue: ParameterValue | undefined;
126
+ options?: ParameterOptions;
116
127
  };
117
128
 
118
129
  export class ParameterManager {
130
+ public static readonly PARAMETER_ANIMATION_NAME = '$parameterAnimation';
131
+
119
132
  /**
120
133
  * Parses and converts input to a boolean value, valid values are:
121
134
  * - true / false
@@ -230,7 +243,7 @@ export class ParameterManager {
230
243
  });
231
244
  }
232
245
 
233
- public static parseMorphTarget(value: ParameterValue): MorphTargetParameterValue {
246
+ public static parseMorphTargets(value: ParameterValue): MorphTargetParameterValue[] {
234
247
  let objValue;
235
248
  if (isString(value)) {
236
249
  try {
@@ -245,8 +258,8 @@ export class ParameterManager {
245
258
  objValue = value;
246
259
  }
247
260
 
248
- if (isString(objValue.name) && isNumber(objValue.value)) {
249
- return { name: objValue.name, value: objValue.value };
261
+ if (isMorphTargetsParameterValue(objValue)) {
262
+ return objValue.map(mT => ({ name: mT.name, value: mT.value }));
250
263
  } else {
251
264
  throw new ViewerError({
252
265
  id: ViewerErrorIds.InvalidParameterValue,
@@ -271,9 +284,11 @@ export class ParameterManager {
271
284
  public async setNodeParameterValue(
272
285
  nodeName: string,
273
286
  parameterName: ParameterName,
274
- value: ParameterValue
287
+ value: InputParameterValue,
288
+ options?: ParameterOptions
275
289
  ): Promise<boolean> {
276
- const valueChanged = this._addParameterValue({ nodeName }, parameterName, value);
290
+ const internalValue = this._prepareInputValue(value);
291
+ const valueChanged = this._addParameterValue({ nodeName }, parameterName, internalValue, options);
277
292
  if (valueChanged) {
278
293
  await this._applyParameterValue({ nodeName }, parameterName);
279
294
  }
@@ -289,9 +304,11 @@ export class ParameterManager {
289
304
  public async setMaterialParameterValue(
290
305
  materialName: string,
291
306
  parameterName: ParameterName,
292
- value: ParameterValue
307
+ value: InputParameterValue,
308
+ options?: ParameterOptions
293
309
  ): Promise<boolean> {
294
- const valueChanged = this._addParameterValue({ materialName }, parameterName, value);
310
+ const internalValue = this._prepareInputValue(value);
311
+ const valueChanged = this._addParameterValue({ materialName }, parameterName, internalValue, options);
295
312
  if (valueChanged) {
296
313
  await this._applyParameterValue({ materialName }, parameterName);
297
314
  }
@@ -309,9 +326,11 @@ export class ParameterManager {
309
326
  public async setTagParameterValue(
310
327
  tagName: string,
311
328
  parameterName: ParameterName,
312
- value: ParameterValue
329
+ value: InputParameterValue,
330
+ options?: ParameterOptions
313
331
  ): Promise<boolean> {
314
- const valueChanged = this._addParameterValue({ tagName }, parameterName, value);
332
+ const internalValue = this._prepareInputValue(value);
333
+ const valueChanged = this._addParameterValue({ tagName }, parameterName, internalValue, options);
315
334
  if (valueChanged) {
316
335
  await this._applyParameterValue({ tagName }, parameterName);
317
336
  }
@@ -333,19 +352,30 @@ export class ParameterManager {
333
352
  : isMaterialParameterSubject(valueEntry)
334
353
  ? { materialName: valueEntry.materialName }
335
354
  : { tagName: valueEntry.tagName };
336
-
337
- return { subject, parameterName: valueEntry.parameterName, value: valueEntry.value, oldValue: undefined };
355
+ const value = this._prepareInputValue(valueEntry.value);
356
+
357
+ return {
358
+ subject,
359
+ parameterName: valueEntry.parameterName,
360
+ value,
361
+ oldValue: undefined,
362
+ options: valueEntry.options,
363
+ };
338
364
  });
339
365
 
340
- const changedParameterEntries = parameterEntries.filter(paramEntry =>
341
- this._addParameterValue(paramEntry.subject, paramEntry.parameterName, paramEntry.value)
366
+ const changedParameterEntries = parameterEntries.filter(({ subject, parameterName, value, options }) =>
367
+ this._addParameterValue(subject, parameterName, value, options)
342
368
  );
343
369
 
344
370
  await this._applyParameterValues(changedParameterEntries);
345
371
 
346
372
  // convert back to original typing
347
373
  const changedParameterValues: ParameterValues = changedParameterEntries.map(paramEntry => {
348
- return { ...paramEntry.subject, parameterName: paramEntry.parameterName, value: paramEntry.value };
374
+ const value = isMorphTargetsParameterValue(paramEntry.value)
375
+ ? // we can be sure that only one morph target was given on the input
376
+ paramEntry.value[0]
377
+ : paramEntry.value;
378
+ return { ...paramEntry.subject, parameterName: paramEntry.parameterName, value };
349
379
  });
350
380
 
351
381
  return changedParameterValues;
@@ -459,6 +489,52 @@ export class ParameterManager {
459
489
  await this._applyParameterValues(textureParameterEntriesToApply);
460
490
  }
461
491
 
492
+ /**
493
+ * @returns Desired parameter value of a certain node.\
494
+ * Tags are considered as well but have lower priority than node parameters, as these are more specific.
495
+ *
496
+ * @internal
497
+ */
498
+ public getParameterValueOfNode(node: TransformNode, parameterName: string): ParameterValue | undefined {
499
+ const nodeParamValue = this.getParameterValue({ nodeName: node.name }, parameterName);
500
+ if (nodeParamValue !== undefined) {
501
+ return nodeParamValue;
502
+ }
503
+
504
+ const tags = getTagsAsStrArr(node);
505
+ const tagParamValue = tags.reduce<ParameterValue | undefined>((accValue, curTag) => {
506
+ // NOTE: it is possible that values are available for multiple tags
507
+ // in this case the resulting parameter value is quite "random" as the last tag in the tag string of the node has
508
+ // priority
509
+ const tagParamValue = this.getParameterValue({ tagName: curTag }, parameterName);
510
+ return accValue ?? tagParamValue;
511
+ }, undefined);
512
+
513
+ return tagParamValue;
514
+ }
515
+
516
+ /**
517
+ * @returns Desired parameter value of a certain material.\
518
+ * Tags are considered as well but have lower priority than material parameters, as these are more specific.\
519
+ * Unused ATM, but added for consistency as counter part for {@link _getParameterValueOfNode}
520
+ *
521
+ * @internal
522
+ */
523
+ public getParameterValueOfMaterial(material: Material, parameterName: string): ParameterValue | undefined {
524
+ const materialParamValue = this.getParameterValue({ materialName: material.name }, parameterName);
525
+ if (materialParamValue !== undefined) {
526
+ return materialParamValue;
527
+ }
528
+
529
+ const tags = getTagsAsStrArr(material);
530
+ const tagParamValue = tags.reduce<ParameterValue | undefined>((accValue, curTag) => {
531
+ const tagParamValue = this.getParameterValue({ tagName: curTag }, parameterName);
532
+ return accValue ?? tagParamValue;
533
+ }, undefined);
534
+
535
+ return tagParamValue;
536
+ }
537
+
462
538
  /**
463
539
  * Retrieves visibility state of node, considering pending parameter values and node hierarchy.\
464
540
  * `node.isEnabled()` alone is insufficient here, as visibility observers for the desired node or one of it's parents
@@ -470,7 +546,7 @@ export class ParameterManager {
470
546
  let curNode: TransformNode | null = node;
471
547
  let visibleByParameter: boolean | undefined = undefined;
472
548
  while (curNode && visibleByParameter !== false) {
473
- const curNodeVisibleByParameter = this._getParameterValueOfNode(curNode, BuiltInParameter.Visible);
549
+ const curNodeVisibleByParameter = this.getParameterValueOfNode(curNode, BuiltInParameter.Visible);
474
550
  if (curNodeVisibleByParameter !== undefined) {
475
551
  visibleByParameter = curNodeVisibleByParameter as boolean;
476
552
  }
@@ -485,172 +561,31 @@ export class ParameterManager {
485
561
  }
486
562
 
487
563
  /**
488
- * Parameter observer implementation of default parameters
564
+ * Converts the input parameter value type to the actual internally used parameter type.\
565
+ * ATM this is required for making morph target parameters of a specific node mergeable.
489
566
  */
490
- protected _addBuiltInParameterObservers(): void {
491
- this.setParameterObserver(BuiltInParameter.Visible, async ({ nodes, newValue }) => {
492
- const visible = ParameterManager.parseBoolean(newValue);
493
-
494
- const observerProms = nodes.map(async node => {
495
- const visibleNested = this.getNestedVisibilityParameterValueOfNode(node);
496
- if (visibleNested) {
497
- // if a mesh gets visible by this operation we have to activate the assigned material which is stored in the
498
- // internal metadata
499
- // => consider child meshes as well (CB-10143)
500
- const activatedNodes = [node, ...node.getChildMeshes(false)];
501
- const setMaterialProms = activatedNodes.map(async an => {
502
- const anVisibleNested = this.getNestedVisibilityParameterValueOfNode(an);
503
- const anDeferredMaterial = getInternalMetadataValue(an, 'deferredMaterial');
504
- const anMaterialParamValue = this._getParameterValueOfNode(an, BuiltInParameter.Material) as
505
- | string
506
- | undefined;
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) {
513
- await this.viewer.materialManager.setMaterialOnMesh(
514
- anRequestedMaterial,
515
- // this cast is fine, as deferred material can only be set on meshes
516
- an as AbstractMesh
517
- );
518
- }
519
- });
520
- await Promise.all(setMaterialProms);
521
- }
522
-
523
- // set enabled state anyway
524
- node.setEnabled(visible);
525
- });
526
- await Promise.all(observerProms);
527
- });
528
- this.setParameterObserver(BuiltInParameter.Material, async ({ nodes, newValue }) => {
529
- const material = ParameterManager.parseString(newValue);
530
-
531
- const observerProms = nodes.map(async node => {
532
- if (!(node instanceof AbstractMesh)) {
533
- throw new ViewerError({
534
- id: ViewerErrorIds.InvalidParameterSubject,
535
- message: `Material can't be set, as the target node "${node.name}" is not a mesh`,
536
- });
537
- }
538
-
539
- // only set material if the mesh is visible, or gets visible by the parameter update
540
- const visible = this.getNestedVisibilityParameterValueOfNode(node);
541
- if (visible) {
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
- }
548
- } else {
549
- setInternalMetadataValue(node, 'deferredMaterial', material);
567
+ protected _prepareInputValue(inputValue: InputParameterValue): ParameterValue {
568
+ if (isMorphTargetParameterValue(inputValue)) {
569
+ return [inputValue];
570
+ } else if (isString(inputValue)) {
571
+ try {
572
+ const objValue = JSON.parse(inputValue);
573
+ if (isMorphTargetParameterValue(objValue)) {
574
+ return [objValue];
550
575
  }
551
- });
552
- await Promise.all(observerProms);
553
- });
554
- this.setParameterObserver(BuiltInParameter.Position, async ({ nodes, newValue }) => {
555
- const position = ParameterManager.parseVector(newValue);
556
-
557
- for (const node of nodes) {
558
- node.position = position;
559
- }
560
- });
561
- this.setParameterObserver(BuiltInParameter.Rotation, async ({ nodes, newValue }) => {
562
- const rotation = ParameterManager.parseRotation(newValue);
563
-
564
- for (const node of nodes) {
565
- node.rotation = rotation;
566
- }
567
- });
568
- this.setParameterObserver(BuiltInParameter.Scaling, async ({ nodes, newValue }) => {
569
- const scaling = ParameterManager.parseVector(newValue);
576
+ } catch (e) {}
577
+ }
570
578
 
571
- for (const node of nodes) {
572
- node.scaling = scaling;
573
- }
574
- });
575
- this.setParameterObserver(BuiltInParameter.Color, async ({ materials, newValue }) => {
576
- const color = ParameterManager.parseColor(newValue);
577
-
578
- for (const material of materials) {
579
- const materialCls = material.getClassName();
580
- switch (materialCls) {
581
- case 'PBRMaterial':
582
- (material as PBRMaterial).albedoColor = color.toLinearSpace();
583
- break;
584
- case 'StandardMaterial':
585
- (material as StandardMaterial).diffuseColor = color;
586
- break;
587
- default:
588
- throw new ViewerError({
589
- id: ViewerErrorIds.InvalidParameterSubject,
590
- message: `Setting color for material of instance "${materialCls}" not implemented`,
591
- });
592
- }
593
- }
594
- });
595
- this.setParameterObserver(BuiltInParameter.Roughness, async ({ materials, newValue }) => {
596
- const roughness = ParameterManager.parseNumber(newValue);
597
-
598
- for (const material of materials) {
599
- const materialCls = material.getClassName();
600
- switch (materialCls) {
601
- case 'PBRMaterial':
602
- (material as PBRMaterial).roughness = roughness;
603
- break;
604
- case 'StandardMaterial':
605
- (material as StandardMaterial).roughness = roughness;
606
- break;
607
- default:
608
- throw new ViewerError({
609
- id: ViewerErrorIds.InvalidParameterSubject,
610
- message: `Setting rougness for material of instance "${materialCls}" not implemented`,
611
- });
612
- }
613
- }
614
- });
615
- this.setParameterObserver(BuiltInParameter.Metallic, async ({ materials, newValue }) => {
616
- const metallic = ParameterManager.parseNumber(newValue);
617
-
618
- for (const material of materials) {
619
- const materialCls = material.getClassName();
620
- switch (materialCls) {
621
- case 'PBRMaterial':
622
- (material as PBRMaterial).metallic = metallic;
623
- break;
624
- default:
625
- throw new ViewerError({
626
- id: ViewerErrorIds.InvalidParameterSubject,
627
- message: `Setting metallic for material of instance "${materialCls}" not implemented`,
628
- });
629
- }
630
- }
631
- });
632
- this.setParameterObserver(BuiltInParameter.Influence, async ({ nodes, newValue }) => {
633
- const morphTargetValue = ParameterManager.parseMorphTarget(newValue);
634
-
635
- for (const node of nodes) {
636
- const target = node instanceof AbstractMesh && node.morphTargetManager?.getTargetByName(morphTargetValue.name);
637
-
638
- if (target) {
639
- target.influence = morphTargetValue.value;
640
- } else {
641
- throw new ViewerError({
642
- id: ViewerErrorIds.InvalidParameterSubject,
643
- message: `Morph target couldn't be found, node: ${node.name} / morph target: ${morphTargetValue.name}`,
644
- });
645
- }
646
- }
647
- });
648
- this.setParameterObserver(LegacyParameter.Paintable, async ({ newValue, materials }) => {
649
- paintableParameterObserver(newValue, materials, this.viewer.scene);
650
- });
579
+ return inputValue;
580
+ }
651
581
 
652
- // texture parameter use a more generic approach (`channel`.`parameter`) and are therefore coded in a dedicated file
582
+ /**
583
+ * Parameter observer implementation of default parameters
584
+ */
585
+ protected _addBuiltInParameterObservers(): void {
586
+ createBuiltInParameter(this, this.viewer.materialManager);
653
587
  createBuiltInTextureParameter(this, this.viewer.scene);
588
+ createPaintableParameter(this, this.viewer.scene);
654
589
  }
655
590
 
656
591
  /**
@@ -661,19 +596,31 @@ export class ParameterManager {
661
596
  protected _addParameterValue(
662
597
  subject: ParameterSubject,
663
598
  parameterName: ParameterName,
664
- value: ParameterValue
599
+ value: ParameterValue,
600
+ options?: ParameterOptions
665
601
  ): boolean {
666
602
  const existingEntry = this._getEntry(subject, parameterName);
667
603
 
668
- if (existingEntry?.value === value) {
604
+ let mergedValue = value;
605
+ if (existingEntry && isMorphTargetsParameterValue(existingEntry.value) && isMorphTargetsParameterValue(value)) {
606
+ // morph target values have to be merged instead of completely replaced
607
+ const map = new Map(existingEntry.value.map(e => [e.name, e]));
608
+ for (const item of value) {
609
+ map.set(item.name, item);
610
+ }
611
+ mergedValue = Array.from(map.values());
612
+ }
613
+
614
+ if (JSON.stringify(existingEntry?.value) === JSON.stringify(mergedValue)) {
669
615
  return false;
670
616
  }
671
617
 
672
618
  if (existingEntry) {
673
619
  existingEntry.oldValue = existingEntry.value;
674
- existingEntry.value = value;
620
+ existingEntry.value = mergedValue;
621
+ existingEntry.options = options;
675
622
  } else {
676
- this._parameterEntries.push({ subject, parameterName, value, oldValue: undefined });
623
+ this._parameterEntries.push({ subject, parameterName, value: mergedValue, oldValue: undefined, options });
677
624
  }
678
625
 
679
626
  return true;
@@ -730,6 +677,7 @@ export class ParameterManager {
730
677
  oldValue: parameterEntry.oldValue,
731
678
  nodes,
732
679
  materials,
680
+ options: parameterEntry.options,
733
681
  });
734
682
  }
735
683
 
@@ -797,46 +745,4 @@ export class ParameterManager {
797
745
 
798
746
  return entries;
799
747
  }
800
-
801
- /**
802
- * @returns Desired parameter value of a certain node.\
803
- * Tags are considered as well but have lower priority than node parameters, as these are more specific.
804
- */
805
- protected _getParameterValueOfNode(node: TransformNode, parameterName: string): ParameterValue | undefined {
806
- const nodeParamValue = this.getParameterValue({ nodeName: node.name }, parameterName);
807
- if (nodeParamValue !== undefined) {
808
- return nodeParamValue;
809
- }
810
-
811
- const tags = getTagsAsStrArr(node);
812
- const tagParamValue = tags.reduce<ParameterValue | undefined>((accValue, curTag) => {
813
- // NOTE: it is possible that values are available for multiple tags
814
- // in this case the resulting parameter value is quite "random" as the last tag in the tag string of the node has
815
- // priority
816
- const tagParamValue = this.getParameterValue({ tagName: curTag }, parameterName);
817
- return accValue ?? tagParamValue;
818
- }, undefined);
819
-
820
- return tagParamValue;
821
- }
822
-
823
- /**
824
- * @returns Desired parameter value of a certain material.\
825
- * Tags are considered as well but have lower priority than material parameters, as these are more specific.\
826
- * Unused ATM, but added for consistency as counter part for {@link _getParameterValueOfNode}
827
- */
828
- protected _getParameterValueOfMaterial(material: Material, parameterName: string): ParameterValue | undefined {
829
- const materialParamValue = this.getParameterValue({ materialName: material.name }, parameterName);
830
- if (materialParamValue !== undefined) {
831
- return materialParamValue;
832
- }
833
-
834
- const tags = getTagsAsStrArr(material);
835
- const tagParamValue = tags.reduce<ParameterValue | undefined>((accValue, curTag) => {
836
- const tagParamValue = this.getParameterValue({ tagName: curTag }, parameterName);
837
- return accValue ?? tagParamValue;
838
- }, undefined);
839
-
840
- return tagParamValue;
841
- }
842
748
  }
@@ -104,6 +104,8 @@ type SceneAsset = BaseAsset & {
104
104
  * This contains creating or loading scenes with certain settings for lighting, cameras and appearance in genereal.
105
105
  */
106
106
  export class SceneManager {
107
+ protected static _DEFAULT_SCENE_ASSET_ENVIRONMENT_URL =
108
+ 'https://cbnpeuwstservice.blob.core.windows.net/viewer3d/static/studio.env';
107
109
  protected static _DEFAULT_SCENE_ASSET_NAME = '$defaultScene';
108
110
  protected static _DEFAULT_GROUND_PLANE_NAME = 'BackgroundPlane';
109
111
  protected static _DEFAULT_GROUND_SIZING_FACTOR = 5;
@@ -125,7 +127,7 @@ export class SceneManager {
125
127
 
126
128
  environment: {
127
129
  create: true,
128
- url: 'https://assets.babylonjs.com/environments/environmentSpecular.env',
130
+ url: SceneManager._DEFAULT_SCENE_ASSET_ENVIRONMENT_URL,
129
131
  intensity: 1,
130
132
  },
131
133
  };