@combeenation/3d-viewer 4.0.0-beta3 → 4.0.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/README.md +3 -1
- package/dist/lib-cjs/api/classes/element.d.ts +14 -9
- package/dist/lib-cjs/api/classes/element.js +148 -87
- package/dist/lib-cjs/api/classes/element.js.map +1 -1
- package/dist/lib-cjs/api/classes/event.d.ts +15 -1
- package/dist/lib-cjs/api/classes/event.js +15 -1
- package/dist/lib-cjs/api/classes/event.js.map +1 -1
- package/dist/lib-cjs/api/classes/parameter.d.ts +101 -7
- package/dist/lib-cjs/api/classes/parameter.js +141 -21
- package/dist/lib-cjs/api/classes/parameter.js.map +1 -1
- package/dist/lib-cjs/api/classes/parameterObservable.js +11 -36
- package/dist/lib-cjs/api/classes/parameterObservable.js.map +1 -1
- package/dist/lib-cjs/api/classes/placementAnimation.d.ts +2 -2
- package/dist/lib-cjs/api/classes/placementAnimation.js +11 -0
- package/dist/lib-cjs/api/classes/placementAnimation.js.map +1 -1
- package/dist/lib-cjs/api/classes/variant.d.ts +48 -4
- package/dist/lib-cjs/api/classes/variant.js +320 -46
- package/dist/lib-cjs/api/classes/variant.js.map +1 -1
- package/dist/lib-cjs/api/classes/variantInstance.d.ts +5 -1
- package/dist/lib-cjs/api/classes/variantInstance.js +10 -0
- package/dist/lib-cjs/api/classes/variantInstance.js.map +1 -1
- package/dist/lib-cjs/api/classes/viewer.d.ts +6 -3
- package/dist/lib-cjs/api/classes/viewer.js +140 -59
- package/dist/lib-cjs/api/classes/viewer.js.map +1 -1
- package/dist/lib-cjs/api/internal/sceneSetup.d.ts +5 -1
- package/dist/lib-cjs/api/internal/sceneSetup.js +75 -71
- package/dist/lib-cjs/api/internal/sceneSetup.js.map +1 -1
- package/dist/lib-cjs/api/util/babylonHelper.d.ts +54 -4
- package/dist/lib-cjs/api/util/babylonHelper.js +160 -8
- package/dist/lib-cjs/api/util/babylonHelper.js.map +1 -1
- package/dist/lib-cjs/api/util/globalTypes.d.ts +62 -8
- package/dist/lib-cjs/api/util/resourceHelper.d.ts +13 -8
- package/dist/lib-cjs/api/util/resourceHelper.js +14 -14
- package/dist/lib-cjs/api/util/resourceHelper.js.map +1 -1
- package/dist/lib-cjs/index.d.ts +24 -22
- package/dist/lib-cjs/index.js +42 -38
- package/dist/lib-cjs/index.js.map +1 -1
- package/package.json +5 -5
- package/src/api/classes/element.ts +118 -91
- package/src/api/classes/event.ts +16 -1
- package/src/api/classes/parameter.ts +153 -22
- package/src/api/classes/parameterObservable.ts +9 -31
- package/src/api/classes/{elementParameterizable.ts → parameterizable.ts} +12 -1
- package/src/api/classes/placementAnimation.ts +10 -0
- package/src/api/classes/variant.ts +187 -40
- package/src/api/classes/variantInstance.ts +8 -1
- package/src/api/classes/variantParameterizable.ts +73 -0
- package/src/api/classes/viewer.ts +83 -17
- package/src/api/classes/viewerLight.ts +330 -0
- package/src/api/internal/sceneSetup.ts +99 -109
- package/src/api/util/babylonHelper.ts +173 -10
- package/src/api/util/globalTypes.ts +71 -10
- package/src/api/util/resourceHelper.ts +16 -16
- package/src/api/util/stringHelper.ts +26 -0
- package/src/dev.ts +3 -7
- package/src/index.ts +27 -23
- package/src/pagesconfig.json +4 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { merge } from 'lodash-es';
|
|
2
|
-
import {
|
|
3
|
-
import { uuidv4 } from '../util/resourceHelper';
|
|
2
|
+
import { uuidv4 } from '../util/stringHelper';
|
|
4
3
|
import { EventBroadcaster } from './eventBroadcaster';
|
|
5
4
|
|
|
6
5
|
export abstract class ParameterObservable extends EventBroadcaster {
|
|
@@ -75,37 +74,16 @@ export abstract class ParameterObservable extends EventBroadcaster {
|
|
|
75
74
|
return;
|
|
76
75
|
}
|
|
77
76
|
const declaration = parameterDeclaration[parameter];
|
|
78
|
-
const genericError = `"${value}" is not a valid value for parameter "${parameter}" of type ` +
|
|
77
|
+
const genericError = `"${value}" is not a valid value for parameter "${parameter}" of type. ` +
|
|
79
78
|
`"${declaration.type}" for ${this.constructor.name} "${this.id}".`;
|
|
79
|
+
if( declaration.parser ) {
|
|
80
|
+
try {
|
|
81
|
+
declaration.parser( value );
|
|
82
|
+
} catch( e ) {
|
|
83
|
+
throw Error( genericError + e.message );
|
|
84
|
+
}
|
|
85
|
+
}
|
|
80
86
|
switch( declaration.type ) {
|
|
81
|
-
case 'boolean':
|
|
82
|
-
try {
|
|
83
|
-
Parameter.parseBoolean( value );
|
|
84
|
-
} catch( e ) {
|
|
85
|
-
throw Error( genericError );
|
|
86
|
-
}
|
|
87
|
-
break;
|
|
88
|
-
case 'number':
|
|
89
|
-
try {
|
|
90
|
-
Parameter.parseNumber( value );
|
|
91
|
-
} catch( e ) {
|
|
92
|
-
throw Error( genericError );
|
|
93
|
-
}
|
|
94
|
-
break;
|
|
95
|
-
case 'color':
|
|
96
|
-
try {
|
|
97
|
-
Parameter.parseColor( value );
|
|
98
|
-
} catch( e ) {
|
|
99
|
-
throw Error( genericError + ` It must be hex with leading "#" or of format "(r,g,b)".` );
|
|
100
|
-
}
|
|
101
|
-
break;
|
|
102
|
-
case 'vector':
|
|
103
|
-
try {
|
|
104
|
-
Parameter.parseVector( value );
|
|
105
|
-
} catch( e ) {
|
|
106
|
-
throw Error( genericError + ` It must be of format "(x,y,z)".` );
|
|
107
|
-
}
|
|
108
|
-
break;
|
|
109
87
|
case 'select':
|
|
110
88
|
if( !declaration.options ) {
|
|
111
89
|
throw Error( `No options defined for parameter declaration "${parameter}"` +
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Parameter } from './parameter';
|
|
2
2
|
import { ParameterObservable } from './parameterObservable';
|
|
3
3
|
|
|
4
|
-
export abstract class
|
|
4
|
+
export abstract class Parameterizable extends ParameterObservable {
|
|
5
5
|
|
|
6
6
|
protected parameterDeclaration: ParameterDeclarations = Parameter.declarations;
|
|
7
7
|
|
|
@@ -55,6 +55,17 @@ export abstract class ElementParameterizable extends ParameterObservable {
|
|
|
55
55
|
return this.parameters[Parameter.ROTATION].toString();
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
get castShadow(): boolean | undefined {
|
|
59
|
+
if( !(Parameter.CAST_SHADOW in this.parameters) ) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
return Parameter.parseBoolean( this.parameters[Parameter.CAST_SHADOW] );
|
|
64
|
+
} catch( e ) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
public async show(): Promise<ParameterObservable> {
|
|
59
70
|
await this.commitParameter( Parameter.VISIBLE, true );
|
|
60
71
|
return this;
|
|
@@ -88,6 +88,11 @@ export class PlacementAnimation implements AnimationInterface {
|
|
|
88
88
|
position = Parameter.parseVector( position );
|
|
89
89
|
this._placementDefinition.position = position;
|
|
90
90
|
}
|
|
91
|
+
let shortestWay = true;
|
|
92
|
+
if( this._animationDefinition.shortestWay !== undefined ) {
|
|
93
|
+
shortestWay = Parameter.parseBoolean( this._animationDefinition.shortestWay );
|
|
94
|
+
this._animationDefinition.shortestWay = shortestWay;
|
|
95
|
+
}
|
|
91
96
|
if( this.mutable instanceof ArcRotateCamera ) {
|
|
92
97
|
// parse the target
|
|
93
98
|
if( isString( this._placementDefinition.target ) ) {
|
|
@@ -105,6 +110,11 @@ export class PlacementAnimation implements AnimationInterface {
|
|
|
105
110
|
} );
|
|
106
111
|
delete this._placementDefinition.position;
|
|
107
112
|
ghostCam.dispose();
|
|
113
|
+
} else if( shortestWay && this._placementDefinition.alpha ) {
|
|
114
|
+
// transform the target's alpha value into the same turn as the current camera position to avoid movements > 360 degrees
|
|
115
|
+
const alphaGap = this.mutable.alpha - this._placementDefinition.alpha;
|
|
116
|
+
const cntTurns = Math.round(alphaGap / (2 * Math.PI));
|
|
117
|
+
this._placementDefinition.alpha += (2 * Math.PI * cntTurns);
|
|
108
118
|
}
|
|
109
119
|
}
|
|
110
120
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AssetContainer } from '@babylonjs/core/assetContainer';
|
|
2
|
+
import { Light } from '@babylonjs/core/Lights/light';
|
|
2
3
|
import '@babylonjs/core/Loading/Plugins/babylonFileLoader';
|
|
3
4
|
import { SceneLoader } from '@babylonjs/core/Loading/sceneLoader';
|
|
4
5
|
import { Material } from '@babylonjs/core/Materials/material';
|
|
@@ -7,19 +8,18 @@ import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
|
|
|
7
8
|
import '@babylonjs/loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression';
|
|
8
9
|
import '@babylonjs/loaders/glTF/2.0/Extensions/KHR_texture_transform';
|
|
9
10
|
import '@babylonjs/loaders/glTF/2.0/glTFLoader';
|
|
10
|
-
import { cloneDeep, concat, isEmpty, isEqual, isString, merge } from 'lodash-es';
|
|
11
|
-
import {
|
|
12
|
-
deactivateTransformNode,
|
|
13
|
-
getDottedPathForTransformNode,
|
|
14
|
-
injectTransformNodeMetadata
|
|
15
|
-
} from '../util/babylonHelper';
|
|
11
|
+
import { cloneDeep, concat, get, isEmpty, isEqual, isString, merge, set } from 'lodash-es';
|
|
12
|
+
import { deactivateTransformNode, getDottedPathForNode, injectNodeMetadata } from '../util/babylonHelper';
|
|
16
13
|
import { loadJson, mergeMaps } from '../util/resourceHelper';
|
|
17
14
|
import { DottedPath } from './dottedPath';
|
|
18
15
|
import { Element } from './element';
|
|
19
|
-
import { ElementParameterizable } from './elementParameterizable';
|
|
20
16
|
import { Event } from './event';
|
|
21
17
|
import { Parameter } from './parameter';
|
|
18
|
+
import { Parameterizable } from './parameterizable';
|
|
19
|
+
import { ParameterObservable } from './parameterObservable';
|
|
20
|
+
import { VariantParameterizable } from './variantParameterizable';
|
|
22
21
|
import { Viewer } from './viewer';
|
|
22
|
+
import { ViewerLight } from './viewerLight';
|
|
23
23
|
|
|
24
24
|
import '@babylonjs/loaders/glTF/2.0/Extensions/KHR_texture_basisu';
|
|
25
25
|
import '@babylonjs/loaders/glTF/2.0/Extensions/KHR_materials_clearcoat';
|
|
@@ -35,12 +35,14 @@ import '@babylonjs/loaders/glTF/2.0/Extensions/KHR_materials_variants';
|
|
|
35
35
|
/**
|
|
36
36
|
* A concrete "Variant". Most of these are handled by either the {@link Viewer} or {@link VariantInstance}.
|
|
37
37
|
*/
|
|
38
|
-
export class Variant extends
|
|
38
|
+
export class Variant extends Parameterizable {
|
|
39
39
|
|
|
40
40
|
public assetContainer: AssetContainer;
|
|
41
41
|
|
|
42
42
|
public readonly elements: Element[] = [];
|
|
43
43
|
|
|
44
|
+
public readonly viewerLights: ViewerLight[] = [];
|
|
45
|
+
|
|
44
46
|
public structureJson: StructureJson;
|
|
45
47
|
|
|
46
48
|
protected _dottedNodes: Map<DottedPath, TransformNode> | undefined;
|
|
@@ -175,6 +177,13 @@ export class Variant extends ElementParameterizable {
|
|
|
175
177
|
return rootNodes;
|
|
176
178
|
}
|
|
177
179
|
|
|
180
|
+
/**
|
|
181
|
+
* The {@link ViewerLight}s of the {@link Variant}.
|
|
182
|
+
*/
|
|
183
|
+
get lights(): Light[] {
|
|
184
|
+
return this.assetContainer.lights;
|
|
185
|
+
}
|
|
186
|
+
|
|
178
187
|
/**
|
|
179
188
|
* All TransformNodes of the {@link Variant} mapped flat with a {@link DottedPath}.
|
|
180
189
|
*/
|
|
@@ -208,6 +217,17 @@ export class Variant extends ElementParameterizable {
|
|
|
208
217
|
return concat( elements, this.elements );
|
|
209
218
|
}
|
|
210
219
|
|
|
220
|
+
/**
|
|
221
|
+
* All {@link ViewerLight}s inherited from this {@link Variant}'s parents.
|
|
222
|
+
*/
|
|
223
|
+
get inheritedViewerLights(): ViewerLight[] {
|
|
224
|
+
let viewerLights: ViewerLight[] = [];
|
|
225
|
+
this.ancestors.forEach( ancestor => {
|
|
226
|
+
viewerLights = concat( viewerLights, ancestor.viewerLights );
|
|
227
|
+
} );
|
|
228
|
+
return concat( viewerLights, this.viewerLights );
|
|
229
|
+
}
|
|
230
|
+
|
|
211
231
|
/**
|
|
212
232
|
* All TransformNodes inherited from this {@link Variant}'s parents.
|
|
213
233
|
*/
|
|
@@ -230,6 +250,17 @@ export class Variant extends ElementParameterizable {
|
|
|
230
250
|
return dottedNodes;
|
|
231
251
|
}
|
|
232
252
|
|
|
253
|
+
/**
|
|
254
|
+
* All Lights inherited from this {@link Variant}'s parents.
|
|
255
|
+
*/
|
|
256
|
+
get inheritedLights(): Light[] {
|
|
257
|
+
let lights: Light[] = [];
|
|
258
|
+
this.ancestors.forEach( ancestor => {
|
|
259
|
+
lights = concat( lights, ancestor.lights );
|
|
260
|
+
} );
|
|
261
|
+
return concat( lights, this.lights );
|
|
262
|
+
}
|
|
263
|
+
|
|
233
264
|
/**
|
|
234
265
|
* The {@link ParameterDeclarations} inherited from this {@link Variant}'s parents.
|
|
235
266
|
*/
|
|
@@ -337,6 +368,33 @@ export class Variant extends ElementParameterizable {
|
|
|
337
368
|
return element;
|
|
338
369
|
}
|
|
339
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Gets the desired {@link ViewerLight} of the current {@link Variant} relative to its {@link DottedPath}.
|
|
373
|
+
* Uses the mechanism of {@link getDescendant} to resolve the appropriate variant in tree.
|
|
374
|
+
*/
|
|
375
|
+
public async getViewerLight( dottedPath: DottedPathArgument ): Promise<ViewerLight> {
|
|
376
|
+
const _dottedPath = DottedPath.create( dottedPath );
|
|
377
|
+
const viewerLightName = _dottedPath.popPart();
|
|
378
|
+
let variant: Variant = this;
|
|
379
|
+
if( _dottedPath.parts.length > 0 ) {
|
|
380
|
+
variant = await this.getDescendant( _dottedPath );
|
|
381
|
+
}
|
|
382
|
+
if( variant.inheritedViewerLights.length === 0 ) {
|
|
383
|
+
throw new Error( `No viewerLights for variant "${variant.id}" found. ` +
|
|
384
|
+
`Either none are defined or they are not initialized (are you operating on the appropriate living?).` );
|
|
385
|
+
}
|
|
386
|
+
let viewerLight;
|
|
387
|
+
variant.inheritedViewerLights.forEach( _viewerLight => {
|
|
388
|
+
if( _viewerLight.name === viewerLightName ) {
|
|
389
|
+
viewerLight = _viewerLight;
|
|
390
|
+
}
|
|
391
|
+
} );
|
|
392
|
+
if( !viewerLight ) {
|
|
393
|
+
throw new Error( `ViewerLight with name "${viewerLightName}" does not exist for variant "${variant.id}".` );
|
|
394
|
+
}
|
|
395
|
+
return viewerLight;
|
|
396
|
+
}
|
|
397
|
+
|
|
340
398
|
/**
|
|
341
399
|
* A proxy for directly getting a Node from an {@link Element} by its {@link DottedPath}s.
|
|
342
400
|
*/
|
|
@@ -383,7 +441,8 @@ export class Variant extends ElementParameterizable {
|
|
|
383
441
|
parent?._children.set( variant.name, variant );
|
|
384
442
|
variant.assetContainer = this.assetContainer;
|
|
385
443
|
variant.parameterObservers = cloneDeep( this.parameterObservers );
|
|
386
|
-
variant.createElements();
|
|
444
|
+
await variant.createElements();
|
|
445
|
+
await variant.createViewerLights();
|
|
387
446
|
variant.addParameterObservers();
|
|
388
447
|
await variant.bootstrapParameters( parameters );
|
|
389
448
|
this.broadcastEvent( Event.VARIANT_CREATED, variant );
|
|
@@ -489,31 +548,10 @@ export class Variant extends ElementParameterizable {
|
|
|
489
548
|
this.broadcastEvent(Event.VARIANT_PARAMETER_BAG_COMMITTED, this, oldParameters, newParameters);
|
|
490
549
|
|
|
491
550
|
// commit parameters to elements
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
if( DottedPath.create( parameter ).firstPart !== element.name ) {
|
|
497
|
-
continue;
|
|
498
|
-
}
|
|
499
|
-
// we got an element parameter
|
|
500
|
-
let newParameterValue = newParameters[parameter];
|
|
501
|
-
const elementParameter = parameter.replace( `${element.name}.`, '' );
|
|
502
|
-
// If the variant is explicitly hidden, we must not override the visibility with element parameters. We need
|
|
503
|
-
// an exception for visibility to avoid overloading already applied element parameters with element parameters
|
|
504
|
-
// defined in the variant spec ("dotted parameters").
|
|
505
|
-
// @see https://github.com/Combeenation/3d-viewer/issues/44
|
|
506
|
-
if( elementParameter === Parameter.VISIBLE && newParameters[Parameter.VISIBLE] === false ) {
|
|
507
|
-
newParameterValue = false;
|
|
508
|
-
}
|
|
509
|
-
elementParameters[elementParameter] = newParameterValue;
|
|
510
|
-
const search = new RegExp( `\\$\\{${elementParameter}\\}`, 'g' );
|
|
511
|
-
_elementDefinition = _elementDefinition.replace( search, newParameterValue.toString() );
|
|
512
|
-
}
|
|
513
|
-
this.structureJson.elements![this.name] = JSON.parse( _elementDefinition );
|
|
514
|
-
return element.commitParameters( elementParameters );
|
|
515
|
-
} );
|
|
516
|
-
await Promise.all( elementPromises );
|
|
551
|
+
await this.commitParametersToElements( newParameters );
|
|
552
|
+
|
|
553
|
+
// commit parameters to lights
|
|
554
|
+
await this.commitParametersToViewerLights( newParameters );
|
|
517
555
|
|
|
518
556
|
// propagate parameters to parent
|
|
519
557
|
if( this.parent ) {
|
|
@@ -562,7 +600,17 @@ export class Variant extends ElementParameterizable {
|
|
|
562
600
|
const nodes = this.assetContainer.getNodes().filter( n => n instanceof TransformNode ) as TransformNode[];
|
|
563
601
|
nodes.forEach( node => {
|
|
564
602
|
deactivateTransformNode( node, false );
|
|
565
|
-
|
|
603
|
+
injectNodeMetadata( node, { dottedPath: getDottedPathForNode( node ) }, false );
|
|
604
|
+
} );
|
|
605
|
+
this.assetContainer.lights.forEach( light => {
|
|
606
|
+
light.setEnabled( false );
|
|
607
|
+
injectNodeMetadata( light, { dottedPath: getDottedPathForNode( light ) }, false );
|
|
608
|
+
this.viewer.scene.addLight( light );
|
|
609
|
+
} );
|
|
610
|
+
this.assetContainer.cameras.forEach( camera => {
|
|
611
|
+
camera.setEnabled( false );
|
|
612
|
+
injectNodeMetadata( camera, { dottedPath: getDottedPathForNode( camera ) }, false );
|
|
613
|
+
this.viewer.scene.addCamera( camera );
|
|
566
614
|
} );
|
|
567
615
|
this.broadcastEvent( Event.ASSET_LOADING_END, this );
|
|
568
616
|
resolve( this );
|
|
@@ -573,15 +621,82 @@ export class Variant extends ElementParameterizable {
|
|
|
573
621
|
} );
|
|
574
622
|
}
|
|
575
623
|
|
|
624
|
+
/**
|
|
625
|
+
* Commits given parameters to all {@link Element}s.
|
|
626
|
+
*/
|
|
627
|
+
protected async commitParametersToElements( parameters: ParameterBag ) {
|
|
628
|
+
await Promise.all( this.elements.map(
|
|
629
|
+
element => this.commitParametersToVariantParameterizable(parameters, element, 'elements')
|
|
630
|
+
) );
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Commits given parameters to all {@link ViewerLight}s.
|
|
635
|
+
*/
|
|
636
|
+
protected async commitParametersToViewerLights( parameters: ParameterBag ) {
|
|
637
|
+
await Promise.all( this.viewerLights.map(
|
|
638
|
+
viewerLight => this.commitParametersToVariantParameterizable(parameters, viewerLight, 'lights')
|
|
639
|
+
) );
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Commits given parameters to a {@link VariantParameterizable} and updates the according definition with given
|
|
644
|
+
* key in the {@link StructureJson}. The `definitionKey` "elements" for example will update the definition in
|
|
645
|
+
* `this.structureJson.elements`.
|
|
646
|
+
*/
|
|
647
|
+
protected async commitParametersToVariantParameterizable( parameters: ParameterBag,
|
|
648
|
+
parameterizable: VariantParameterizable,
|
|
649
|
+
definitionKey: string ): Promise<ParameterObservable> {
|
|
650
|
+
const initialDefinition = get( this._structureJson, definitionKey )[parameterizable.name];
|
|
651
|
+
let initialDefinitionStr = JSON.stringify( initialDefinition );
|
|
652
|
+
const _parameters: ParameterBag = {};
|
|
653
|
+
for( const parameter in parameters ) {
|
|
654
|
+
const dpp = DottedPath.create( parameter );
|
|
655
|
+
if( dpp.shiftPart() !== parameterizable.name ) {
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
// we got a parameterizable ("element") parameter
|
|
659
|
+
let parameterValue = parameters[parameter];
|
|
660
|
+
const parameterizableParameter = dpp.path;
|
|
661
|
+
// If the variant is explicitly hidden, we must not override the visibility with element parameters. We need
|
|
662
|
+
// an exception for visibility to avoid overloading already applied element parameters with element parameters
|
|
663
|
+
// defined in the variant spec ("dotted parameters").
|
|
664
|
+
// @see https://github.com/Combeenation/3d-viewer/issues/44
|
|
665
|
+
if( parameterizableParameter === Parameter.VISIBLE && parameters[Parameter.VISIBLE] === false ) {
|
|
666
|
+
parameterValue = false;
|
|
667
|
+
}
|
|
668
|
+
_parameters[parameterizableParameter] = parameterValue;
|
|
669
|
+
const search = new RegExp( `\\$\\{${parameterizableParameter}\\}`, 'g' );
|
|
670
|
+
initialDefinitionStr = initialDefinitionStr.replace( search, parameterValue.toString() );
|
|
671
|
+
}
|
|
672
|
+
const definition = get( this.structureJson, definitionKey );
|
|
673
|
+
definition[this.name] = JSON.parse( initialDefinitionStr );
|
|
674
|
+
set( this.structureJson, definitionKey, definition );
|
|
675
|
+
return await parameterizable.commitParameters( _parameters );
|
|
676
|
+
}
|
|
677
|
+
|
|
576
678
|
/**
|
|
577
679
|
* Commits given {@link Parameter} to the {@link Variant}'s {@link Element}s.
|
|
578
680
|
*/
|
|
579
|
-
protected async commitParameterToElements( parameter: string, value: ParameterValue ) {
|
|
681
|
+
protected async commitParameterToElements( parameter: string, value: ParameterValue ): Promise<Variant> {
|
|
580
682
|
const promises = [];
|
|
581
683
|
for( const element of this.elements ) {
|
|
582
684
|
promises.push( element.commitParameter( parameter, value ) );
|
|
583
685
|
}
|
|
584
686
|
await Promise.all( promises );
|
|
687
|
+
return this;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Commits given {@link Parameter} to the {@link Variant}'s {@link Element}s.
|
|
692
|
+
*/
|
|
693
|
+
protected async commitParameterToViewerLights( parameter: string, value: ParameterValue ): Promise<Variant> {
|
|
694
|
+
const promises = [];
|
|
695
|
+
for( const viewerLight of this.viewerLights ) {
|
|
696
|
+
promises.push( viewerLight.commitParameter( parameter, value ) );
|
|
697
|
+
}
|
|
698
|
+
await Promise.all( promises );
|
|
699
|
+
return this;
|
|
585
700
|
}
|
|
586
701
|
|
|
587
702
|
/**
|
|
@@ -591,11 +706,13 @@ export class Variant extends ElementParameterizable {
|
|
|
591
706
|
this._parameterObservers.set( Parameter.VISIBLE, [
|
|
592
707
|
async ( variant: Variant, oldValue: ParameterValue, newValue: ParameterValue ) => {
|
|
593
708
|
await variant.commitParameterToElements( Parameter.VISIBLE, newValue );
|
|
709
|
+
await variant.commitParameterToViewerLights( Parameter.VISIBLE, newValue );
|
|
594
710
|
}
|
|
595
711
|
] );
|
|
596
712
|
this._parameterObservers.set( Parameter.SCALING, [
|
|
597
713
|
async ( variant: Variant, oldValue: ParameterValue, newValue: ParameterValue ) => {
|
|
598
714
|
await variant.commitParameterToElements( Parameter.SCALING, newValue );
|
|
715
|
+
await variant.commitParameterToViewerLights( Parameter.SCALING, newValue );
|
|
599
716
|
}
|
|
600
717
|
] );
|
|
601
718
|
this._parameterObservers.set( Parameter.MATERIAL, [
|
|
@@ -631,11 +748,28 @@ export class Variant extends ElementParameterizable {
|
|
|
631
748
|
this._parameterObservers.set( Parameter.POSITION, [
|
|
632
749
|
async ( variant: Variant, oldValue: ParameterValue, newValue: ParameterValue ) => {
|
|
633
750
|
await variant.commitParameterToElements( Parameter.POSITION, newValue );
|
|
751
|
+
await variant.commitParameterToViewerLights( Parameter.POSITION, newValue );
|
|
634
752
|
}
|
|
635
753
|
] );
|
|
636
754
|
this._parameterObservers.set( Parameter.ROTATION, [
|
|
637
755
|
async ( variant: Variant, oldValue: ParameterValue, newValue: ParameterValue ) => {
|
|
638
756
|
await variant.commitParameterToElements( Parameter.ROTATION, newValue );
|
|
757
|
+
await variant.commitParameterToViewerLights( Parameter.ROTATION, newValue );
|
|
758
|
+
}
|
|
759
|
+
] );
|
|
760
|
+
this._parameterObservers.set( Parameter.CAST_SHADOW, [
|
|
761
|
+
async ( variant: Variant, oldValue: ParameterValue, newValue: ParameterValue ) => {
|
|
762
|
+
await variant.commitParameterToElements( Parameter.CAST_SHADOW, newValue );
|
|
763
|
+
}
|
|
764
|
+
] );
|
|
765
|
+
this._parameterObservers.set( Parameter.CAST_SHADOW_FROM_LIGHTS, [
|
|
766
|
+
async ( variant: Variant, oldValue: ParameterValue, newValue: ParameterValue ) => {
|
|
767
|
+
await variant.commitParameterToElements( Parameter.CAST_SHADOW_FROM_LIGHTS, newValue );
|
|
768
|
+
}
|
|
769
|
+
] );
|
|
770
|
+
this._parameterObservers.set( Parameter.RECEIVE_SHADOWS, [
|
|
771
|
+
async ( variant: Variant, oldValue: ParameterValue, newValue: ParameterValue ) => {
|
|
772
|
+
await variant.commitParameterToElements( Parameter.RECEIVE_SHADOWS, newValue );
|
|
639
773
|
}
|
|
640
774
|
] );
|
|
641
775
|
return this;
|
|
@@ -644,20 +778,33 @@ export class Variant extends ElementParameterizable {
|
|
|
644
778
|
/**
|
|
645
779
|
* Creates {@link Element}s and clones nodes into them.
|
|
646
780
|
*/
|
|
647
|
-
protected createElements(): Variant {
|
|
648
|
-
for( const
|
|
649
|
-
this.elements.push(
|
|
781
|
+
protected async createElements(): Promise<Variant> {
|
|
782
|
+
for( const name in this.structureJson.elements || {} ) {
|
|
783
|
+
this.elements.push( await Element.create( this, name ) );
|
|
650
784
|
}
|
|
651
785
|
// inject node meta to all inherited elements
|
|
652
786
|
// we do this to inject the deepest and most concrete variant information to all cloned nodes in the tree
|
|
653
787
|
this.inheritedElements.forEach( element => {
|
|
654
788
|
element.nodes.forEach( node => {
|
|
655
|
-
|
|
789
|
+
injectNodeMetadata( node, { variant: this, variantParameterizable: element } );
|
|
656
790
|
} );
|
|
657
791
|
} );
|
|
658
792
|
return this;
|
|
659
793
|
}
|
|
660
794
|
|
|
795
|
+
/**
|
|
796
|
+
* Creates {@link ViewerLight}s.
|
|
797
|
+
*/
|
|
798
|
+
protected async createViewerLights(): Promise<Variant> {
|
|
799
|
+
for( const name in this.structureJson.lights || {} ) {
|
|
800
|
+
this.viewerLights.push( await ViewerLight.create( this, name ) );
|
|
801
|
+
}
|
|
802
|
+
this.inheritedViewerLights.forEach( viewerLight => {
|
|
803
|
+
injectNodeMetadata( viewerLight.light, { variant: this, variantParameterizable: viewerLight } );
|
|
804
|
+
} );
|
|
805
|
+
return this;
|
|
806
|
+
}
|
|
807
|
+
|
|
661
808
|
/**
|
|
662
809
|
* Bootstrapping for parameters. It sets the `parametersInitialized` to true for all ancestors.
|
|
663
810
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Mesh } from '@babylonjs/core/Meshes/mesh';
|
|
2
|
-
import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
|
|
3
2
|
import { Element } from './element';
|
|
3
|
+
import { ViewerLight } from './viewerLight';
|
|
4
4
|
import { EventBroadcaster } from './eventBroadcaster';
|
|
5
5
|
import { ParameterObservable } from './parameterObservable';
|
|
6
6
|
import { Variant } from './variant';
|
|
@@ -51,6 +51,13 @@ export class VariantInstance extends EventBroadcaster {
|
|
|
51
51
|
return this.variant.getElement( dottedPath );
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* A proxy for {@link Variant.getViewerLight}.
|
|
56
|
+
*/
|
|
57
|
+
public async getViewerLight( dottedPath: DottedPathArgument ): Promise<ViewerLight> {
|
|
58
|
+
return this.variant.getViewerLight( dottedPath );
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
/**
|
|
55
62
|
* A proxy for {@link Variant.getNode}.
|
|
56
63
|
*/
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { DottedPath } from './../classes/dottedPath';
|
|
2
|
+
import { Event } from './../classes/event';
|
|
3
|
+
import { Parameterizable } from './../classes/parameterizable';
|
|
4
|
+
import { Variant } from './../classes/variant';
|
|
5
|
+
import { mergeMaps } from './../util/resourceHelper';
|
|
6
|
+
import { camelToSnakeCase } from './../util/stringHelper';
|
|
7
|
+
import { cloneDeep, concat, get, merge } from 'lodash-es';
|
|
8
|
+
|
|
9
|
+
export abstract class VariantParameterizable extends Parameterizable {
|
|
10
|
+
|
|
11
|
+
protected readonly _parameterObservers: Map<string, ParameterObserver[]> = new Map();
|
|
12
|
+
|
|
13
|
+
protected constructor( public readonly variant: Variant,
|
|
14
|
+
public readonly name: string ) {
|
|
15
|
+
super();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Gets the inherited parameters from the {@link Variant} with cleaned {@link Parameterizable} prefix.
|
|
20
|
+
*/
|
|
21
|
+
get inheritedParameters(): ParameterBag {
|
|
22
|
+
const parameterBag: ParameterBag = {};
|
|
23
|
+
for( const parameter in this.variant.inheritedParameters ) {
|
|
24
|
+
const dpp = DottedPath.create( parameter );
|
|
25
|
+
if( dpp.firstPart !== this.name ) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
dpp.shiftPart();
|
|
29
|
+
parameterBag[dpp.path] = this.variant.inheritedParameters[parameter];
|
|
30
|
+
}
|
|
31
|
+
return parameterBag;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Places the given {@link ParameterBag} in the {@link VariantParameterizable}'s parameters, replaces all patterns in the
|
|
36
|
+
* {@link StructureJson} and broadcasts all {@link ParameterObserver}s.
|
|
37
|
+
*/
|
|
38
|
+
public async commitParameters( parameters?: ParameterBag ): Promise<VariantParameterizable> {
|
|
39
|
+
if( !parameters ) {
|
|
40
|
+
parameters = {};
|
|
41
|
+
}
|
|
42
|
+
const oldParameters = cloneDeep( this.parameters );
|
|
43
|
+
merge( this.parameters, parameters );
|
|
44
|
+
// handle parameter observers
|
|
45
|
+
let observerPromises: Promise<void | ParameterObserver>[] = [];
|
|
46
|
+
for( const parameter in this.parameters ) {
|
|
47
|
+
const oldParameterValue = oldParameters[parameter];
|
|
48
|
+
const newParameterValue = this.parameters[parameter];
|
|
49
|
+
this.variant.assertParameter( this.variant.inheritedParameterDeclaration, parameter, newParameterValue );
|
|
50
|
+
if( oldParameterValue === newParameterValue ) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// parameter changed
|
|
54
|
+
const parameterObservers = mergeMaps( this._parameterObservers, this.parameterObservers );
|
|
55
|
+
if( parameterObservers.has( parameter ) ) {
|
|
56
|
+
const observers = parameterObservers.get( parameter )!;
|
|
57
|
+
observerPromises = concat( observerPromises, observers.map( observer => {
|
|
58
|
+
const observerResult = observer( this, oldParameterValue, newParameterValue );
|
|
59
|
+
const observerPromise = Promise.resolve( observerResult );
|
|
60
|
+
observerPromise.then( () => {
|
|
61
|
+
const clsName = camelToSnakeCase( this.constructor.name ).toUpperCase();
|
|
62
|
+
this.broadcastEvent( get( Event, clsName + '_PARAMETER_COMMITTED' ),
|
|
63
|
+
this, parameter, oldParameterValue, newParameterValue );
|
|
64
|
+
} );
|
|
65
|
+
return observerPromise;
|
|
66
|
+
} ) );
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
await Promise.all( observerPromises );
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
}
|