@combeenation/3d-viewer 6.5.0 → 7.0.0-beta1

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 (69) hide show
  1. package/dist/lib-cjs/api/classes/element.d.ts +4 -0
  2. package/dist/lib-cjs/api/classes/element.js +59 -58
  3. package/dist/lib-cjs/api/classes/element.js.map +1 -1
  4. package/dist/lib-cjs/api/classes/event.d.ts +55 -1
  5. package/dist/lib-cjs/api/classes/event.js +55 -1
  6. package/dist/lib-cjs/api/classes/event.js.map +1 -1
  7. package/dist/lib-cjs/api/classes/fuzzyMap.d.ts +7 -0
  8. package/dist/lib-cjs/api/classes/fuzzyMap.js +22 -0
  9. package/dist/lib-cjs/api/classes/fuzzyMap.js.map +1 -0
  10. package/dist/lib-cjs/api/classes/parameter.d.ts +12 -0
  11. package/dist/lib-cjs/api/classes/parameter.js +86 -33
  12. package/dist/lib-cjs/api/classes/parameter.js.map +1 -1
  13. package/dist/lib-cjs/api/classes/parameterObservable.js +2 -27
  14. package/dist/lib-cjs/api/classes/parameterObservable.js.map +1 -1
  15. package/dist/lib-cjs/api/classes/variant.d.ts +23 -4
  16. package/dist/lib-cjs/api/classes/variant.js +45 -20
  17. package/dist/lib-cjs/api/classes/variant.js.map +1 -1
  18. package/dist/lib-cjs/api/classes/variantInstance.d.ts +11 -2
  19. package/dist/lib-cjs/api/classes/variantInstance.js +22 -2
  20. package/dist/lib-cjs/api/classes/variantInstance.js.map +1 -1
  21. package/dist/lib-cjs/api/classes/viewer.d.ts +13 -1
  22. package/dist/lib-cjs/api/classes/viewer.js +57 -7
  23. package/dist/lib-cjs/api/classes/viewer.js.map +1 -1
  24. package/dist/lib-cjs/api/classes/viewerLight.js.map +1 -1
  25. package/dist/lib-cjs/api/manager/animationManager.d.ts +3 -3
  26. package/dist/lib-cjs/api/manager/animationManager.js.map +1 -1
  27. package/dist/lib-cjs/api/manager/sceneManager.js.map +1 -1
  28. package/dist/lib-cjs/api/manager/tagManager.d.ts +108 -0
  29. package/dist/lib-cjs/api/manager/tagManager.js +420 -0
  30. package/dist/lib-cjs/api/manager/tagManager.js.map +1 -0
  31. package/dist/lib-cjs/api/manager/variantInstanceManager.d.ts +8 -3
  32. package/dist/lib-cjs/api/manager/variantInstanceManager.js +27 -8
  33. package/dist/lib-cjs/api/manager/variantInstanceManager.js.map +1 -1
  34. package/dist/lib-cjs/api/store/specStorage.d.ts +8 -0
  35. package/dist/lib-cjs/api/store/specStorage.js +15 -0
  36. package/dist/lib-cjs/api/store/specStorage.js.map +1 -1
  37. package/dist/lib-cjs/api/util/babylonHelper.d.ts +36 -7
  38. package/dist/lib-cjs/api/util/babylonHelper.js +92 -15
  39. package/dist/lib-cjs/api/util/babylonHelper.js.map +1 -1
  40. package/dist/lib-cjs/api/util/globalTypes.d.ts +38 -1
  41. package/dist/lib-cjs/api/util/sceneLoaderHelper.d.ts +1 -0
  42. package/dist/lib-cjs/api/util/sceneLoaderHelper.js +20 -2
  43. package/dist/lib-cjs/api/util/sceneLoaderHelper.js.map +1 -1
  44. package/dist/lib-cjs/buildinfo.json +1 -1
  45. package/dist/lib-cjs/commonjs.tsconfig.tsbuildinfo +1 -1
  46. package/dist/lib-cjs/index.d.ts +2 -1
  47. package/dist/lib-cjs/index.js +3 -1
  48. package/dist/lib-cjs/index.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/api/classes/element.ts +96 -82
  51. package/src/api/classes/event.ts +68 -1
  52. package/src/api/classes/fuzzyMap.ts +21 -0
  53. package/src/api/classes/parameter.ts +91 -34
  54. package/src/api/classes/parameterObservable.ts +2 -29
  55. package/src/api/classes/variant.ts +82 -33
  56. package/src/api/classes/variantInstance.ts +27 -1
  57. package/src/api/classes/viewer.ts +63 -12
  58. package/src/api/classes/viewerLight.ts +10 -10
  59. package/src/api/manager/animationManager.ts +2 -2
  60. package/src/api/manager/sceneManager.ts +5 -1
  61. package/src/api/manager/tagManager.ts +451 -0
  62. package/src/api/manager/variantInstanceManager.ts +35 -8
  63. package/src/api/store/specStorage.ts +17 -0
  64. package/src/api/util/babylonHelper.ts +99 -21
  65. package/src/api/util/globalTypes.ts +56 -1
  66. package/src/api/util/sceneLoaderHelper.ts +21 -2
  67. package/src/dev.ts +3 -1
  68. package/src/index.ts +2 -0
  69. package/src/types.d.ts +7 -0
@@ -1,6 +1,6 @@
1
1
  import { Color3 } from '@babylonjs/core/Maths/math.color';
2
2
  import { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import { get, isEmpty, isNumber, isString } from 'lodash-es';
3
+ import { capitalize, get, isEmpty, isNumber, isString } from 'lodash-es';
4
4
 
5
5
  /**
6
6
  * This `Parameter` class defines all parameters that can be consumed and committed by a {@link ParameterObservable}.
@@ -320,35 +320,46 @@ export class Parameter {
320
320
  * The {@link ParameterDeclarations} for all parameters in this class.
321
321
  */
322
322
  public static get declarations(): ParameterDeclarations {
323
- const declarations: ParameterDeclarations = {};
324
- declarations[Parameter.VISIBLE] = { type: 'boolean', parser: Parameter.parseBoolean };
325
- declarations[Parameter.SCALING] = { type: 'number', parser: Parameter.parseNumber };
326
- declarations[Parameter.POSITION] = { type: 'vector', parser: Parameter.parseVector };
327
- declarations[Parameter.ROTATION] = { type: 'vector', parser: Parameter.parseVector };
328
- declarations[Parameter.HIGHLIGHTED] = { type: 'boolean', parser: Parameter.parseBoolean };
329
- declarations[Parameter.MATERIAL] = { type: 'string' };
330
- declarations[Parameter.MATERIAL_COLOR] = { type: 'color', parser: Parameter.parseColor };
331
- declarations[Parameter.MATERIAL_METALLNESS] = { type: 'number', parser: Parameter.parseNumber };
332
- declarations[Parameter.MATERIAL_ROUGHNESS] = { type: 'number', parser: Parameter.parseNumber };
333
- declarations[Parameter.HIGHLIGHT_ENABLED] = { type: 'boolean', parser: Parameter.parseBoolean };
334
- declarations[Parameter.HIGHLIGHT_COLOR] = { type: 'color', parser: Parameter.parseColor };
335
- declarations[Parameter.HIGHLIGHTED] = { type: 'boolean', parser: Parameter.parseBoolean };
336
- declarations[Parameter.CAST_SHADOW] = { type: 'boolean', parser: Parameter.parseBoolean };
337
- declarations[Parameter.CAST_SHADOW_FROM_LIGHTS] = { type: 'csl', parser: Parameter.parseCommaSeparatedList };
338
- declarations[Parameter.RECEIVE_SHADOWS] = { type: 'boolean', parser: Parameter.parseBoolean };
339
- declarations[Parameter.INTENSITY] = { type: 'number', parser: Parameter.parseNumber };
340
- declarations[Parameter.DIRECTION] = { type: 'vector', parser: Parameter.parseVector };
341
- declarations[Parameter.ANGLE] = { type: 'number', parser: Parameter.parseNumber };
342
- declarations[Parameter.EXPONENT] = { type: 'number', parser: Parameter.parseNumber };
343
- declarations[Parameter.DIFFUSE] = { type: 'color', parser: Parameter.parseColor };
344
- declarations[Parameter.SPECULAR] = { type: 'color', parser: Parameter.parseColor };
345
- declarations[Parameter.ENVIRONMENT] = { type: 'string' };
346
- declarations[Parameter.ENVIRONMENT_COLOR] = { type: 'color', parser: Parameter.parseColor };
347
- declarations[Parameter.ENVIRONMENT_ROTATION] = { type: 'number', parser: Parameter.parseNumber };
348
- declarations[Parameter.ENVIRONMENT_INTENSITY] = { type: 'number', parser: Parameter.parseNumber };
349
- declarations[Parameter.ENVIRONMENT_BACKGROUND] = { type: 'string' };
350
- declarations[Parameter.ENVIRONMENT_USEDEFAULT] = { type: 'boolean', parser: Parameter.parseBoolean };
351
- return declarations;
323
+ return {
324
+ [Parameter.VISIBLE]: { type: 'boolean', parser: Parameter.parseBoolean },
325
+ [Parameter.SCALING]: { type: 'number', parser: Parameter.parseNumber },
326
+ [Parameter.POSITION]: { type: 'vector', parser: Parameter.parseVector },
327
+ [Parameter.ROTATION]: { type: 'vector', parser: Parameter.parseVector },
328
+ [Parameter.HIGHLIGHTED]: { type: 'boolean', parser: Parameter.parseBoolean },
329
+ [Parameter.MATERIAL]: { type: 'string' },
330
+ [Parameter.MATERIAL_COLOR]: { type: 'color', parser: Parameter.parseColor },
331
+ [Parameter.MATERIAL_METALLNESS]: { type: 'number', parser: Parameter.parseNumber },
332
+ [Parameter.MATERIAL_ROUGHNESS]: { type: 'number', parser: Parameter.parseNumber },
333
+ [Parameter.HIGHLIGHT_ENABLED]: { type: 'boolean', parser: Parameter.parseBoolean },
334
+ [Parameter.HIGHLIGHT_COLOR]: { type: 'color', parser: Parameter.parseColor },
335
+ [Parameter.HIGHLIGHTED]: { type: 'boolean', parser: Parameter.parseBoolean },
336
+ [Parameter.CAST_SHADOW]: { type: 'boolean', parser: Parameter.parseBoolean },
337
+ [Parameter.CAST_SHADOW_FROM_LIGHTS]: { type: 'csl', parser: Parameter.parseCommaSeparatedList },
338
+ [Parameter.RECEIVE_SHADOWS]: { type: 'boolean', parser: Parameter.parseBoolean },
339
+ [Parameter.INTENSITY]: { type: 'number', parser: Parameter.parseNumber },
340
+ [Parameter.DIRECTION]: { type: 'vector', parser: Parameter.parseVector },
341
+ [Parameter.ANGLE]: { type: 'number', parser: Parameter.parseNumber },
342
+ [Parameter.EXPONENT]: { type: 'number', parser: Parameter.parseNumber },
343
+ [Parameter.DIFFUSE]: { type: 'color', parser: Parameter.parseColor },
344
+ [Parameter.SPECULAR]: { type: 'color', parser: Parameter.parseColor },
345
+ [Parameter.ENVIRONMENT]: { type: 'string' },
346
+ [Parameter.ENVIRONMENT_COLOR]: { type: 'color', parser: Parameter.parseColor },
347
+ [Parameter.ENVIRONMENT_ROTATION]: { type: 'number', parser: Parameter.parseNumber },
348
+ [Parameter.ENVIRONMENT_INTENSITY]: { type: 'number', parser: Parameter.parseNumber },
349
+ [Parameter.ENVIRONMENT_BACKGROUND]: { type: 'string' },
350
+ [Parameter.ENVIRONMENT_USEDEFAULT]: { type: 'boolean', parser: Parameter.parseBoolean },
351
+ };
352
+ }
353
+
354
+ /**
355
+ * The default {@link ParameterValue}s returned as {@link ParameterBag} for a set of parameters in this class.
356
+ */
357
+ public static get defaultValues(): ParameterBag {
358
+ return {
359
+ [Parameter.SCALING]: '(1, 1, 1)',
360
+ [Parameter.POSITION]: '(0, 0, 0)',
361
+ [Parameter.ROTATION]: '(0, 0, 0)',
362
+ };
352
363
  }
353
364
 
354
365
  /**
@@ -358,6 +369,13 @@ export class Parameter {
358
369
  return Parameter.declarations[parameter];
359
370
  }
360
371
 
372
+ /**
373
+ * Gets the default {@link ParameterValue} for a given {@link Parameter} declared in this class.
374
+ */
375
+ public static getDefaultValue(parameter: string): ParameterValue {
376
+ return Parameter.defaultValues[parameter];
377
+ }
378
+
361
379
  /**
362
380
  * All parameters defined in this class.
363
381
  */
@@ -372,6 +390,36 @@ export class Parameter {
372
390
  return all;
373
391
  }
374
392
 
393
+ /**
394
+ * Asserts whether given parameter is declared and valid.
395
+ */
396
+ public static assertParameter(parameterDeclaration: ParameterDeclarations, parameter: string, value: ParameterValue) {
397
+ if (!(parameter in parameterDeclaration)) {
398
+ // This is a valid case since we are not able to check parameters that are not declared.
399
+ // We just ignore parameters that are not declared.
400
+ return;
401
+ }
402
+ const declaration = parameterDeclaration[parameter];
403
+ const genericError = `"${value}" is not a valid value for parameter "${parameter}" of type "${declaration.type}".`;
404
+ if (declaration.parser) {
405
+ try {
406
+ declaration.parser(value);
407
+ } catch (e: any) {
408
+ throw Error(`${genericError} ${e.message}`);
409
+ }
410
+ }
411
+ switch (declaration.type) {
412
+ case 'select':
413
+ if (!declaration.options) {
414
+ throw Error(`No options defined for parameter declaration "${parameter}" of type "${declaration.type}".`);
415
+ }
416
+ if (declaration.options.indexOf(value) === -1) {
417
+ throw Error(genericError + ` Valid values are: "${declaration.options.join('", "')}".`);
418
+ }
419
+ break;
420
+ }
421
+ }
422
+
375
423
  /**
376
424
  * Parses given {@link ParameterBag} with given {@link ParameterDeclarations}.
377
425
  */
@@ -453,16 +501,25 @@ export class Parameter {
453
501
  * Parses a string of format `'#rrggbb'` or `'(r,g,b)'` to a `Color3`.
454
502
  */
455
503
  public static parseColor(value: ParameterValue): Color3 {
456
- let cleanedValue = value.toString().split(' ').join('');
504
+ const cleanedValue = value.toString().split(' ').join('');
457
505
  if (cleanedValue.startsWith('#')) {
458
506
  return Color3.FromHexString(value.toString());
459
507
  }
460
508
  if (cleanedValue.startsWith('(') && cleanedValue.endsWith(')')) {
461
- cleanedValue = cleanedValue.substring(1, cleanedValue.length - 1);
462
- const [r, g, b] = cleanedValue.split(',').map(value => parseFloat(value));
509
+ const rgb = cleanedValue.substring(1, cleanedValue.length - 1);
510
+ const [r, g, b] = rgb.split(',').map(value => parseFloat(value));
463
511
  return Color3.FromInts(r, g, b);
464
512
  }
465
- throw new Error(`Unable to parse "${value}" to a Color: expected "#rrggbb" or "(r,g,b)".`);
513
+ const humanReadable = capitalize(cleanedValue);
514
+ // disable lint for this rule as there is no smoother option for checking static class functions (eg Color.Red())
515
+ // eslint-disable-next-line no-prototype-builtins
516
+ if (Color3.hasOwnProperty(humanReadable)) {
517
+ // @ts-ignore same as above
518
+ return Color3[humanReadable]();
519
+ }
520
+ throw new Error(
521
+ `Unable to parse "${value}" to a Color: expected "#rrggbb", "(r,g,b)" or any human readable property implemented in Color3.`
522
+ );
466
523
  }
467
524
 
468
525
  /**
@@ -1,5 +1,6 @@
1
1
  import { uuidv4 } from '../util/stringHelper';
2
2
  import { EventBroadcaster } from './eventBroadcaster';
3
+ import { Parameter } from './parameter';
3
4
  import { merge } from 'lodash-es';
4
5
 
5
6
  export abstract class ParameterObservable extends EventBroadcaster {
@@ -67,34 +68,6 @@ export abstract class ParameterObservable extends EventBroadcaster {
67
68
  * Asserts whether given parameter is declared and valid.
68
69
  */
69
70
  public assertParameter(parameterDeclaration: ParameterDeclarations, parameter: string, value: ParameterValue) {
70
- if (!(parameter in parameterDeclaration)) {
71
- // This is a valid case since we are not able to check parameters that are not declared.
72
- // We just ignore parameters that are not declared.
73
- return;
74
- }
75
- const declaration = parameterDeclaration[parameter];
76
- const genericError =
77
- `"${value}" is not a valid value for parameter "${parameter}" of type. ` +
78
- `"${declaration.type}" for ${this.constructor.name} "${this.id}".`;
79
- if (declaration.parser) {
80
- try {
81
- declaration.parser(value);
82
- } catch (e: any) {
83
- throw Error(genericError + e.message);
84
- }
85
- }
86
- switch (declaration.type) {
87
- case 'select':
88
- if (!declaration.options) {
89
- throw Error(
90
- `No options defined for parameter declaration "${parameter}"` +
91
- `of type "${declaration.type}" for ${this.constructor.name} "${this.id}".`
92
- );
93
- }
94
- if (declaration.options.indexOf(value) === -1) {
95
- throw Error(genericError + ` Valid values are: "${declaration.options.join('", "')}".`);
96
- }
97
- break;
98
- }
71
+ Parameter.assertParameter(parameterDeclaration, parameter, value);
99
72
  }
100
73
  }
@@ -1,4 +1,10 @@
1
- import { deactivateTransformNode, getDottedPathForNode, injectNodeMetadata } from '../util/babylonHelper';
1
+ import {
2
+ deactivateTransformNode,
3
+ getDottedPathForNode,
4
+ injectNodeMetadata,
5
+ intersectingNodeNames,
6
+ reportDuplicateNodeNames,
7
+ } from '../util/babylonHelper';
2
8
  import { loadJson, mergeMaps } from '../util/resourceHelper';
3
9
  import { DottedPath } from './dottedPath';
4
10
  import { Element } from './element';
@@ -58,6 +64,7 @@ export class Variant extends Parameterizable {
58
64
  * Constructor.
59
65
  */
60
66
  protected constructor(
67
+ protected readonly _variantInstance: VariantInstance | null,
61
68
  public readonly name: string,
62
69
  protected readonly _structureJson: StructureJson,
63
70
  public readonly viewer: Viewer,
@@ -73,18 +80,26 @@ export class Variant extends Parameterizable {
73
80
  this.structureJson = cloneDeep(_structureJson);
74
81
  }
75
82
 
83
+ /**
84
+ * Creates the root {@link Variant}.
85
+ */
86
+ public static async createRoot(structureJson: StructureJson, viewer: Viewer): Promise<Variant> {
87
+ return Variant.create(null, '_', structureJson, viewer);
88
+ }
89
+
76
90
  /**
77
91
  * Creates a {@link Variant} based on given parameters.
78
92
  *
79
93
  * @throws Error if "gltf" property is provided without a filename
80
94
  */
81
95
  public static async create(
96
+ variantInstance: VariantInstance | null,
82
97
  name: string,
83
98
  structureJson: StructureJson,
84
99
  viewer: Viewer,
85
100
  parent?: Variant
86
101
  ): Promise<Variant> {
87
- const variant = new Variant(name, structureJson, viewer, parent);
102
+ const variant = new Variant(variantInstance, name, structureJson, viewer, parent);
88
103
  await variant.loadAssets();
89
104
  return variant;
90
105
  }
@@ -120,6 +135,15 @@ export class Variant extends Parameterizable {
120
135
  return DottedPath.createFromParts(parentIds).addPart(this.name);
121
136
  }
122
137
 
138
+ /**
139
+ * Gets the {@link VariantInstance} this variant was created for. There are variants without an instance (the "ghost"
140
+ * ones used for bootstrapping instances). The usage of {@link Variant}s without an instance is an absolute edge-case
141
+ * when deeply using the viewer api and working abroad best practices.
142
+ */
143
+ get variantInstance(): VariantInstance | null {
144
+ return this._variantInstance;
145
+ }
146
+
123
147
  /**
124
148
  * The id representing a {@link DottedPath}.
125
149
  */
@@ -205,6 +229,20 @@ export class Variant extends Parameterizable {
205
229
  return this.assetContainer.materials;
206
230
  }
207
231
 
232
+ /**
233
+ * The cloned TransformNodes of all {@link Element}s created for this {@link Variant}.
234
+ */
235
+ get elementNodes(): TransformNode[] {
236
+ return this.elements.reduce((a, c) => a.concat(c.nodes), [] as TransformNode[]);
237
+ }
238
+
239
+ /**
240
+ * The cloned TransformNodes of all {@link Element}s created for this {@link Variant} flattened.
241
+ */
242
+ get elementNodesFlat(): TransformNode[] {
243
+ return this.elements.reduce((a, c) => [...a, ...c.nodesFlat], [] as TransformNode[]);
244
+ }
245
+
208
246
  /**
209
247
  * All {@link Element}s from this {@link Variant}'s parents.
210
248
  */
@@ -325,9 +363,21 @@ export class Variant extends Parameterizable {
325
363
  }
326
364
  if (this.structureJson.variants[name].file) {
327
365
  const file = this.structureJson.variants[name].file as string;
328
- variant = await Variant.create(name, await loadJson<StructureJson>(file), this.viewer, this);
366
+ variant = await Variant.create(
367
+ this.variantInstance,
368
+ name,
369
+ await loadJson<StructureJson>(file),
370
+ this.viewer,
371
+ this
372
+ );
329
373
  } else {
330
- variant = await Variant.create(name, this.structureJson.variants[name], this.viewer, this);
374
+ variant = await Variant.create(
375
+ this.variantInstance,
376
+ name,
377
+ this.structureJson.variants[name],
378
+ this.viewer,
379
+ this
380
+ );
331
381
  }
332
382
  this._children.set(name, variant);
333
383
  }
@@ -426,9 +476,9 @@ export class Variant extends Parameterizable {
426
476
  * @emit {@link Event.VARIANT_CREATED}
427
477
  * @ignore
428
478
  */
429
- public async createLiving(parameters?: ParameterBag): Promise<Variant> {
430
- const parent = await this.parent?.createLiving();
431
- const variant = new Variant(this.name, this._structureJson, this.viewer, parent);
479
+ public async createLiving(variantInstance: VariantInstance, parameters?: ParameterBag): Promise<Variant> {
480
+ const parent = await this.parent?.createLiving(variantInstance);
481
+ const variant = new Variant(variantInstance, this.name, this._structureJson, this.viewer, parent);
432
482
  parent?._children.set(variant.name, variant);
433
483
  variant.assetContainer = this.assetContainer;
434
484
  variant.parameterObservers = cloneDeep(this.parameterObservers);
@@ -436,7 +486,7 @@ export class Variant extends Parameterizable {
436
486
  await variant.createViewerLights();
437
487
  variant.addParameterObservers();
438
488
  await variant.bootstrapParameters(parameters);
439
- this.broadcastEvent(Event.VARIANT_CREATED, variant);
489
+ this.viewer.broadcastEvent(Event.VARIANT_CREATED, variant);
440
490
  return variant;
441
491
  }
442
492
 
@@ -589,8 +639,7 @@ export class Variant extends Parameterizable {
589
639
  */
590
640
  protected async loadAssets(): Promise<Variant> {
591
641
  this.broadcastEvent(Event.ASSET_LOADING_START, this);
592
-
593
- const promisifiedLoader = new Promise<Variant>((resolve, reject) => {
642
+ return new Promise<Variant>(resolve => {
594
643
  if (!this.structureJson) {
595
644
  this.broadcastEvent(Event.ASSET_LOADING_END, this);
596
645
  return resolve(this);
@@ -607,18 +656,21 @@ export class Variant extends Parameterizable {
607
656
  container => {
608
657
  this.assetContainer = container;
609
658
  const nodes = this.assetContainer.getNodes().filter(n => n instanceof TransformNode) as TransformNode[];
659
+ reportDuplicateNodeNames(
660
+ intersectingNodeNames(nodes, this.viewer.scene.getNodes(), n => n instanceof TransformNode)
661
+ );
610
662
  nodes.forEach(node => {
611
663
  deactivateTransformNode(node, false);
612
- injectNodeMetadata(node, { dottedPath: getDottedPathForNode(node) }, false);
664
+ injectNodeMetadata(node, { dottedPath: getDottedPathForNode(node), originalName: node.name }, false);
613
665
  });
614
666
  this.assetContainer.lights.forEach(light => {
615
667
  light.setEnabled(false);
616
- injectNodeMetadata(light, { dottedPath: getDottedPathForNode(light) }, false);
668
+ injectNodeMetadata(light, { dottedPath: getDottedPathForNode(light), originalName: light.name }, false);
617
669
  this.viewer.scene.addLight(light);
618
670
  });
619
671
  this.assetContainer.cameras.forEach(camera => {
620
672
  camera.setEnabled(false);
621
- injectNodeMetadata(camera, { dottedPath: getDottedPathForNode(camera) }, false);
673
+ injectNodeMetadata(camera, { dottedPath: getDottedPathForNode(camera), originalName: camera.name }, false);
622
674
  this.viewer.scene.addCamera(camera);
623
675
  });
624
676
  this.assetContainer.materials.forEach(material => this.viewer.scene.materials.push(material));
@@ -634,7 +686,6 @@ export class Variant extends Parameterizable {
634
686
  }
635
687
  );
636
688
  });
637
- return await promisifiedLoader;
638
689
  }
639
690
 
640
691
  /**
@@ -758,71 +809,71 @@ export class Variant extends Parameterizable {
758
809
  */
759
810
  protected addParameterObservers(): Variant {
760
811
  this._parameterObservers.set(Parameter.VISIBLE, [
761
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
812
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
762
813
  await variant.commitParameterToElements(Parameter.VISIBLE, newValue);
763
814
  await variant.commitParameterToViewerLights(Parameter.VISIBLE, newValue);
764
815
  },
765
816
  ]);
766
817
  this._parameterObservers.set(Parameter.SCALING, [
767
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
818
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
768
819
  await variant.commitParameterToElements(Parameter.SCALING, newValue);
769
820
  await variant.commitParameterToViewerLights(Parameter.SCALING, newValue);
770
821
  },
771
822
  ]);
772
823
  this._parameterObservers.set(Parameter.MATERIAL, [
773
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
824
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
774
825
  await variant.commitParameterToElements(Parameter.MATERIAL, newValue);
775
826
  },
776
827
  ]);
777
828
  this._parameterObservers.set(Parameter.MATERIAL_COLOR, [
778
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
829
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
779
830
  await variant.commitParameterToElements(Parameter.MATERIAL_COLOR, newValue);
780
831
  },
781
832
  ]);
782
833
  this._parameterObservers.set(Parameter.MATERIAL_METALLNESS, [
783
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
834
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
784
835
  await variant.commitParameterToElements(Parameter.MATERIAL_METALLNESS, newValue);
785
836
  },
786
837
  ]);
787
838
  this._parameterObservers.set(Parameter.MATERIAL_ROUGHNESS, [
788
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
839
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
789
840
  await variant.commitParameterToElements(Parameter.MATERIAL_ROUGHNESS, newValue);
790
841
  },
791
842
  ]);
792
843
  this._parameterObservers.set(Parameter.HIGHLIGHT_COLOR, [
793
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
844
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
794
845
  await variant.commitParameterToElements(Parameter.HIGHLIGHT_COLOR, newValue);
795
846
  },
796
847
  ]);
797
848
  this._parameterObservers.set(Parameter.HIGHLIGHTED, [
798
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
849
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
799
850
  await variant.commitParameterToElements(Parameter.HIGHLIGHTED, newValue);
800
851
  },
801
852
  ]);
802
853
  this._parameterObservers.set(Parameter.POSITION, [
803
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
854
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
804
855
  await variant.commitParameterToElements(Parameter.POSITION, newValue);
805
856
  await variant.commitParameterToViewerLights(Parameter.POSITION, newValue);
806
857
  },
807
858
  ]);
808
859
  this._parameterObservers.set(Parameter.ROTATION, [
809
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
860
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
810
861
  await variant.commitParameterToElements(Parameter.ROTATION, newValue);
811
862
  await variant.commitParameterToViewerLights(Parameter.ROTATION, newValue);
812
863
  },
813
864
  ]);
814
865
  this._parameterObservers.set(Parameter.CAST_SHADOW, [
815
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
866
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
816
867
  await variant.commitParameterToElements(Parameter.CAST_SHADOW, newValue);
817
868
  },
818
869
  ]);
819
870
  this._parameterObservers.set(Parameter.CAST_SHADOW_FROM_LIGHTS, [
820
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
871
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
821
872
  await variant.commitParameterToElements(Parameter.CAST_SHADOW_FROM_LIGHTS, newValue);
822
873
  },
823
874
  ]);
824
875
  this._parameterObservers.set(Parameter.RECEIVE_SHADOWS, [
825
- async (variant: Variant, oldValue: ParameterValue, newValue: ParameterValue) => {
876
+ async (variant: Variant, oldValue: Undefinable<ParameterValue>, newValue: ParameterValue) => {
826
877
  await variant.commitParameterToElements(Parameter.RECEIVE_SHADOWS, newValue);
827
878
  },
828
879
  ]);
@@ -832,18 +883,16 @@ export class Variant extends Parameterizable {
832
883
  /**
833
884
  * Creates {@link Element}s and clones nodes into them.
834
885
  */
835
- protected async createElements(): Promise<Variant> {
886
+ protected async createElements(forInstance?: string): Promise<Variant> {
836
887
  this.createElementDefinitionsIfNotExist();
837
888
  for (const name in this.structureJson.elements || {}) {
838
889
  this.elements.push(await Element.create(this, name));
839
890
  }
840
891
  // inject node meta to all inherited elements
841
892
  // we do this to inject the deepest and most concrete variant information to all cloned nodes in the tree
842
- this.inheritedElements.forEach(element => {
843
- element.nodes.forEach(node => {
844
- injectNodeMetadata(node, { variant: this, variantParameterizable: element });
845
- });
846
- });
893
+ this.inheritedElements.forEach(element =>
894
+ element.nodes.forEach(node => injectNodeMetadata(node, { variant: this, variantParameterizable: element }))
895
+ );
847
896
  return this;
848
897
  }
849
898
 
@@ -9,13 +9,29 @@ import { Mesh } from '@babylonjs/core/Meshes/mesh';
9
9
  * Class VariantInstance.
10
10
  */
11
11
  export class VariantInstance extends EventBroadcaster {
12
+ protected _variant: Variant | null = null;
13
+
12
14
  /**
13
15
  * Constructor
14
16
  */
15
- public constructor(public variant: Variant, public name: string) {
17
+ protected constructor(public name: string) {
16
18
  super();
17
19
  }
18
20
 
21
+ /**
22
+ * Creates a living clone of the given source {@link Variant} and instances a {@link VariantInstance} with given name
23
+ * and {@link ParameterBag}.
24
+ */
25
+ public static async createLiving(
26
+ sourceVariant: Variant,
27
+ name: string,
28
+ parameters?: ParameterBag
29
+ ): Promise<VariantInstance> {
30
+ const variantInstance = new VariantInstance(name);
31
+ variantInstance._variant = await sourceVariant.createLiving(variantInstance, parameters);
32
+ return variantInstance;
33
+ }
34
+
19
35
  /**
20
36
  * WORK IN PROGRESS
21
37
  */
@@ -47,6 +63,16 @@ public translate(
47
63
  }
48
64
  */
49
65
 
66
+ /**
67
+ * Gets the {@link Variant} attached to the {@link VariantInstance}.
68
+ */
69
+ get variant(): Variant {
70
+ if (!this._variant) {
71
+ throw new Error(`Variant has not been properly set for instance.`);
72
+ }
73
+ return this._variant;
74
+ }
75
+
50
76
  /**
51
77
  * A proxy for {@link Variant.getElement}.
52
78
  */