@combeenation/3d-viewer 9.0.0 → 9.0.2-alpha1
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/api/classes/parameter.d.ts +33 -20
- package/dist/lib-cjs/api/classes/parameter.js +44 -29
- package/dist/lib-cjs/api/classes/parameter.js.map +1 -1
- package/dist/lib-cjs/api/manager/tagManager.js +17 -4
- package/dist/lib-cjs/api/manager/tagManager.js.map +1 -1
- package/dist/lib-cjs/api/util/babylonHelper.js +36 -1
- package/dist/lib-cjs/api/util/babylonHelper.js.map +1 -1
- package/dist/lib-cjs/api/util/globalTypes.d.ts +1 -1
- package/dist/lib-cjs/api/util/resourceHelper.js +4 -1
- package/dist/lib-cjs/api/util/resourceHelper.js.map +1 -1
- package/dist/lib-cjs/buildinfo.json +1 -1
- package/dist/lib-cjs/commonjs.tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/api/classes/parameter.ts +47 -31
- package/src/api/manager/tagManager.ts +16 -4
- package/src/api/util/babylonHelper.ts +44 -1
- package/src/api/util/globalTypes.ts +1 -1
- package/src/api/util/resourceHelper.ts +4 -1
- package/src/dev.ts +1 -1
package/package.json
CHANGED
|
@@ -160,11 +160,18 @@ export class Parameter {
|
|
|
160
160
|
public static readonly METALLIC = 'metallic';
|
|
161
161
|
|
|
162
162
|
/**
|
|
163
|
-
*
|
|
163
|
+
* Paintables can be used to let the client/browser draw graphics on a 3d model by mutating the texture of a
|
|
164
|
+
* material.\
|
|
165
|
+
* For detailed information have a look at the [paintables](./../pages/documentation/Paintables.html) page.
|
|
166
|
+
*
|
|
167
|
+
* Also see {@link parsePaintable} for detailed information on which values you can pass to the `paintable`
|
|
168
|
+
* parameter.
|
|
164
169
|
*
|
|
165
170
|
* Scopes:
|
|
166
171
|
* * {@link Material} via {@link TagManager}
|
|
167
172
|
*
|
|
173
|
+
* @parser {@link parsePaintable}
|
|
174
|
+
*
|
|
168
175
|
*/
|
|
169
176
|
public static readonly PAINTABLE = 'paintable';
|
|
170
177
|
|
|
@@ -276,7 +283,7 @@ export class Parameter {
|
|
|
276
283
|
*
|
|
277
284
|
* Usually the URL to an `*.env` file.
|
|
278
285
|
*
|
|
279
|
-
* See [environment page](./../pages/
|
|
286
|
+
* See [environment page](./../pages/documentation/Environment.html) for details on how to use this.
|
|
280
287
|
*
|
|
281
288
|
* Scopes:
|
|
282
289
|
* * {@link SceneManager}
|
|
@@ -288,7 +295,7 @@ export class Parameter {
|
|
|
288
295
|
/**
|
|
289
296
|
* Sets [BABYLON.Scene.clearColor](https://doc.babylonjs.com/typedoc/classes/BABYLON.Scene#clearColor)
|
|
290
297
|
*
|
|
291
|
-
* See [environment page](./../pages/
|
|
298
|
+
* See [environment page](./../pages/documentation/Environment.html) for details on how to use this.
|
|
292
299
|
*
|
|
293
300
|
* Scopes:
|
|
294
301
|
* * {@link SceneManager}
|
|
@@ -301,7 +308,7 @@ export class Parameter {
|
|
|
301
308
|
* Rotates connected textures, i.e. IBL, reflection, background, ...
|
|
302
309
|
* Rotation angle in degree.
|
|
303
310
|
*
|
|
304
|
-
* See [environment page](./../pages/
|
|
311
|
+
* See [environment page](./../pages/documentation/Environment.html) for details on how to use this.
|
|
305
312
|
*
|
|
306
313
|
* Scopes:
|
|
307
314
|
* * {@link SceneManager}
|
|
@@ -313,7 +320,7 @@ export class Parameter {
|
|
|
313
320
|
/**
|
|
314
321
|
* Intensity of IBL. Defaults to 1, usually between 0 & 2.
|
|
315
322
|
*
|
|
316
|
-
* See [environment page](./../pages/
|
|
323
|
+
* See [environment page](./../pages/documentation/Environment.html) for details on how to use this.
|
|
317
324
|
*
|
|
318
325
|
* Scopes:
|
|
319
326
|
* * {@link SceneManager}
|
|
@@ -329,7 +336,7 @@ export class Parameter {
|
|
|
329
336
|
* Recommended format is `*.jpg`. HDR files are not supported as background but can be converted to `*.jpg` using
|
|
330
337
|
* tools like Photoshop, Gimp or various free online converters.
|
|
331
338
|
*
|
|
332
|
-
* See [environment page](./../pages/
|
|
339
|
+
* See [environment page](./../pages/documentation/Environment.html) for details on how to use this.
|
|
333
340
|
*
|
|
334
341
|
* Scopes:
|
|
335
342
|
* * {@link SceneManager}
|
|
@@ -347,7 +354,7 @@ export class Parameter {
|
|
|
347
354
|
*
|
|
348
355
|
* The value of this parameter is ignored if {@link ENVIRONMENT_BACKGROUND} is also set.
|
|
349
356
|
*
|
|
350
|
-
* See [environment page](./../pages/
|
|
357
|
+
* See [environment page](./../pages/documentation/Environment.html) for details on how to use this.
|
|
351
358
|
*
|
|
352
359
|
* Scopes:
|
|
353
360
|
* * {@link SceneManager}
|
|
@@ -372,6 +379,10 @@ export class Parameter {
|
|
|
372
379
|
[Parameter.MATERIAL_ROUGHNESS]: { type: 'number', parser: Parameter.parseNumber },
|
|
373
380
|
[Parameter.HIGHLIGHT_ENABLED]: { type: 'boolean', parser: Parameter.parseBoolean },
|
|
374
381
|
[Parameter.HIGHLIGHT_COLOR]: { type: 'color', parser: Parameter.parseColor },
|
|
382
|
+
[Parameter.COLOR]: { type: 'color', parser: Parameter.parseColor },
|
|
383
|
+
[Parameter.METALLIC]: { type: 'number', parser: Parameter.parseNumber },
|
|
384
|
+
[Parameter.ROUGHNESS]: { type: 'number', parser: Parameter.parseNumber },
|
|
385
|
+
[Parameter.PAINTABLE]: { type: 'paintable', parser: Parameter.parsePaintable },
|
|
375
386
|
[Parameter.HIGHLIGHTED]: { type: 'boolean', parser: Parameter.parseBoolean },
|
|
376
387
|
[Parameter.CAST_SHADOW]: { type: 'boolean', parser: Parameter.parseBoolean },
|
|
377
388
|
[Parameter.CAST_SHADOW_FROM_LIGHTS]: { type: 'csl', parser: Parameter.parseCommaSeparatedList },
|
|
@@ -402,13 +413,6 @@ export class Parameter {
|
|
|
402
413
|
};
|
|
403
414
|
}
|
|
404
415
|
|
|
405
|
-
/**
|
|
406
|
-
* Gets the {@link ParameterDeclaration} for a given {@link Parameter} declared in this class.
|
|
407
|
-
*/
|
|
408
|
-
public static getDeclaration(parameter: string): ParameterDeclaration {
|
|
409
|
-
return Parameter.declarations[parameter];
|
|
410
|
-
}
|
|
411
|
-
|
|
412
416
|
/**
|
|
413
417
|
* Gets the default {@link ParameterValue} for a given {@link Parameter} declared in this class.
|
|
414
418
|
*/
|
|
@@ -440,14 +444,14 @@ export class Parameter {
|
|
|
440
444
|
return;
|
|
441
445
|
}
|
|
442
446
|
const declaration = parameterDeclaration[parameter];
|
|
443
|
-
const
|
|
447
|
+
const errorPrefix = `Invalid value for parameter "${parameter}" of type "${declaration.type}" given:`;
|
|
444
448
|
if (declaration.parser) {
|
|
445
449
|
try {
|
|
446
450
|
declaration.parser(value);
|
|
447
451
|
} catch (e: any) {
|
|
448
452
|
throw new ViewerError({
|
|
449
453
|
id: ViewerErrorIds.InvalidParameterValue,
|
|
450
|
-
message: `${
|
|
454
|
+
message: `${errorPrefix}\n${e.message}.\nGiven value: ${value}`,
|
|
451
455
|
});
|
|
452
456
|
}
|
|
453
457
|
}
|
|
@@ -460,7 +464,9 @@ export class Parameter {
|
|
|
460
464
|
});
|
|
461
465
|
}
|
|
462
466
|
if (declaration.options.indexOf(value) === -1) {
|
|
463
|
-
throw Error(
|
|
467
|
+
throw Error(
|
|
468
|
+
`${errorPrefix}\nValid values are: "${declaration.options.join('", "')}".\nGiven value: ${value}`
|
|
469
|
+
);
|
|
464
470
|
}
|
|
465
471
|
break;
|
|
466
472
|
}
|
|
@@ -589,18 +595,28 @@ export class Parameter {
|
|
|
589
595
|
}
|
|
590
596
|
|
|
591
597
|
/**
|
|
592
|
-
* Parses string defintion of a paintable into a paintable object
|
|
598
|
+
* Parses string defintion of a paintable into a paintable object.\
|
|
593
599
|
* Here are some examples:
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
|
|
601
|
-
|
|
600
|
+
* ```ts
|
|
601
|
+
* // Default definition as JSON object:
|
|
602
|
+
* '{ "src": "https://path.to/image.jpg", "uScale": 0.5 }'
|
|
603
|
+
*
|
|
604
|
+
* // Short hand definition, only contains source string:
|
|
605
|
+
* 'https://path.to/image.jpg'
|
|
606
|
+
*
|
|
607
|
+
* // Full content, paintable texture is flipped in both directions
|
|
608
|
+
* '{ "src": "https://path.to/image.jpg", "uScale": -1, "vScale": -1, "uOffset": 0, "vOffset": 0 }'
|
|
609
|
+
*
|
|
610
|
+
* // SVG content can be used directly:
|
|
611
|
+
* '<svg>...</svg>'
|
|
612
|
+
*
|
|
613
|
+
* // SVG in src property works as well:
|
|
614
|
+
* '{ "src": "<svg>...</svg>", "uScale": 0.5 }'
|
|
615
|
+
* ```
|
|
616
|
+
*/
|
|
617
|
+
public static parsePaintable(value: ParameterValue): PaintableValue {
|
|
602
618
|
if (!isString(value)) {
|
|
603
|
-
throw new Error(`
|
|
619
|
+
throw new Error(`Not a string.`);
|
|
604
620
|
}
|
|
605
621
|
|
|
606
622
|
const paintableValue: PaintableValue = { src: '' };
|
|
@@ -614,18 +630,18 @@ export class Parameter {
|
|
|
614
630
|
|
|
615
631
|
if (value.startsWith('{')) {
|
|
616
632
|
// seems like the user tried to use a JSON string, as the input starts with {
|
|
617
|
-
throw new Error(`
|
|
633
|
+
throw new Error(`Not a valid JSON string.`);
|
|
618
634
|
}
|
|
619
635
|
}
|
|
620
636
|
|
|
621
637
|
if (valObj) {
|
|
622
638
|
// input string is JSON, src attribute is required
|
|
623
639
|
if (!valObj.src) {
|
|
624
|
-
throw new Error(`
|
|
640
|
+
throw new Error(`Property "src" is missing.`);
|
|
625
641
|
}
|
|
626
642
|
|
|
627
643
|
if (!isString(valObj.src)) {
|
|
628
|
-
throw new Error(`
|
|
644
|
+
throw new Error(`Property "src" is not a string.`);
|
|
629
645
|
}
|
|
630
646
|
|
|
631
647
|
// split src and options
|
|
@@ -649,7 +665,7 @@ export class Parameter {
|
|
|
649
665
|
);
|
|
650
666
|
|
|
651
667
|
if (invalidKeys.length) {
|
|
652
|
-
console.warn('
|
|
668
|
+
console.warn('parsePaintable: invalid paintable options provided: ' + invalidKeys.toString());
|
|
653
669
|
}
|
|
654
670
|
|
|
655
671
|
paintableValue.options = validOptions;
|
|
@@ -2,6 +2,7 @@ import { Event, emitter } from '../classes/event';
|
|
|
2
2
|
import { FuzzyMap } from '../classes/fuzzyMap';
|
|
3
3
|
import { Parameter } from '../classes/parameter';
|
|
4
4
|
import { Viewer } from '../classes/viewer';
|
|
5
|
+
import { ViewerError, ViewerErrorIds } from '../classes/viewerError';
|
|
5
6
|
import {
|
|
6
7
|
assertMeshCapability,
|
|
7
8
|
drawPaintableOnMaterial,
|
|
@@ -427,7 +428,7 @@ export class TagManager {
|
|
|
427
428
|
return true;
|
|
428
429
|
});
|
|
429
430
|
this.parameterObservers.set(Parameter.PAINTABLE, async payload => {
|
|
430
|
-
const paintableValue: PaintableValue = Parameter.
|
|
431
|
+
const paintableValue: PaintableValue = Parameter.parsePaintable(payload.newValue);
|
|
431
432
|
|
|
432
433
|
// check if value is svg or image source, do the conversion accordingly
|
|
433
434
|
const srcIsSvg = isSvg(paintableValue.src);
|
|
@@ -437,9 +438,20 @@ export class TagManager {
|
|
|
437
438
|
throw new Error(`Setting paintable value failed: "${paintableValue.src}" is no valid SVG string.`);
|
|
438
439
|
}
|
|
439
440
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
441
|
+
let imageSource: CanvasImageSource;
|
|
442
|
+
try {
|
|
443
|
+
imageSource = srcIsSvg
|
|
444
|
+
? await createImageFromSvg(paintableValue.src)
|
|
445
|
+
: await createImageFromImgSrc(paintableValue.src);
|
|
446
|
+
} catch {
|
|
447
|
+
// SVG might be invalid, even if it passes `isSvg` check
|
|
448
|
+
// in this case the image can't be created and will throw an error, which should be handled by the viewer and
|
|
449
|
+
// Combeenation viewer control
|
|
450
|
+
throw new ViewerError({
|
|
451
|
+
id: ViewerErrorIds.InvalidParameterValue,
|
|
452
|
+
message: `Invalid value for parameter "paintable" given:\nImage can't be created from source string.\nGiven value: ${paintableValue.src}`,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
443
455
|
|
|
444
456
|
// apply image source on desired material(s)
|
|
445
457
|
for (const material of payload.materials) {
|
|
@@ -37,6 +37,10 @@ const backgroundDomeName = 'BackgroundDome_ViewerGenerated';
|
|
|
37
37
|
const envHelperMetadataName = 'viewerEnvHelper';
|
|
38
38
|
const materialWaitingToBeSetMetadataName = 'materialWaitingToBeSet';
|
|
39
39
|
|
|
40
|
+
// Holds pending `createMaterialFromCbnAssets` promises for dedicated material ids
|
|
41
|
+
// In this case we can make sure that a single material is not requested multiple times if used in various nodes
|
|
42
|
+
const getMaterialPromises: Map<string, Promise<Material | null>> = new Map();
|
|
43
|
+
|
|
40
44
|
/**
|
|
41
45
|
* @param node
|
|
42
46
|
* @return Node
|
|
@@ -435,15 +439,54 @@ const setMaterial = function (node: TransformNode, materialId: string, deep: boo
|
|
|
435
439
|
}
|
|
436
440
|
};
|
|
437
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Removes material from promise map once it has been added to the scene, because we can be sure that the material will
|
|
444
|
+
* be taken from the scene directly, the next time it is requested
|
|
445
|
+
*/
|
|
446
|
+
const onNewMaterialAddedCb = (material: Material) => {
|
|
447
|
+
if (getMaterialPromises.has(material.id)) {
|
|
448
|
+
getMaterialPromises.delete(material.id);
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Responsible for adding the `onNewMaterialAddedCb` only once
|
|
454
|
+
*/
|
|
455
|
+
const registerOnNewMaterialAddedCbOnce = (scene: Scene) => {
|
|
456
|
+
const observerAlreadyAssigned = scene.onNewMaterialAddedObservable.observers.find(
|
|
457
|
+
observer => observer.callback === onNewMaterialAddedCb
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
if (!observerAlreadyAssigned) {
|
|
461
|
+
scene.onNewMaterialAddedObservable.add(onNewMaterialAddedCb);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
438
465
|
/**
|
|
439
466
|
* Gets the Material either from the given {@link Variant}, the given scene or tries to create it from an Combeenation
|
|
440
467
|
* material asset when inside a Combeenation configurator.
|
|
441
468
|
*/
|
|
442
469
|
const getOrCreateMaterial = async function (scene: Scene, materialId: string, variant?: Variant): Promise<Material> {
|
|
470
|
+
registerOnNewMaterialAddedCbOnce(scene);
|
|
471
|
+
|
|
443
472
|
let chosenMaterial: Material | undefined | null;
|
|
444
473
|
chosenMaterial = variant?.inheritedMaterials.find(mat => mat.id === materialId);
|
|
445
474
|
chosenMaterial = chosenMaterial || scene.materials.find(mat => mat.id === materialId);
|
|
446
|
-
|
|
475
|
+
|
|
476
|
+
if (!chosenMaterial) {
|
|
477
|
+
// material not available yet, request it from Combeenation assets
|
|
478
|
+
if (getMaterialPromises.has(materialId)) {
|
|
479
|
+
// request is already pending, just wait for the result
|
|
480
|
+
chosenMaterial = await getMaterialPromises.get(materialId);
|
|
481
|
+
} else {
|
|
482
|
+
// request not pending, call the dedicated function
|
|
483
|
+
const getMaterialProm = createMaterialFromCbnAssets(materialId, scene);
|
|
484
|
+
// store the promise in a global map, so that subsequent requests can reference it
|
|
485
|
+
getMaterialPromises.set(materialId, getMaterialProm);
|
|
486
|
+
chosenMaterial = await getMaterialProm;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
447
490
|
if (chosenMaterial) {
|
|
448
491
|
return chosenMaterial as Material;
|
|
449
492
|
}
|
|
@@ -394,7 +394,7 @@ type ParameterDeclarations = {
|
|
|
394
394
|
};
|
|
395
395
|
|
|
396
396
|
type ParameterDeclaration = {
|
|
397
|
-
type: 'string' | 'boolean' | 'number' | 'color' | 'select' | 'vector' | 'csl';
|
|
397
|
+
type: 'string' | 'boolean' | 'number' | 'color' | 'select' | 'vector' | 'csl' | 'paintable';
|
|
398
398
|
parser?: any;
|
|
399
399
|
options?: ParameterValue[];
|
|
400
400
|
};
|
|
@@ -131,10 +131,13 @@ const createImageFromSvg = async function (svgSrc: string): Promise<HTMLImageEle
|
|
|
131
131
|
const createImageFromImgSrc = async function (imgSrc: string): Promise<HTMLImageElement> {
|
|
132
132
|
const img = new Image();
|
|
133
133
|
|
|
134
|
-
await new Promise(resolve => {
|
|
134
|
+
await new Promise((resolve, reject) => {
|
|
135
135
|
img.onload = () => {
|
|
136
136
|
setTimeout(resolve, 0);
|
|
137
137
|
};
|
|
138
|
+
img.onerror = () => {
|
|
139
|
+
reject();
|
|
140
|
+
};
|
|
138
141
|
img.crossOrigin = 'anonymous';
|
|
139
142
|
img.src = imgSrc;
|
|
140
143
|
});
|
package/src/dev.ts
CHANGED
|
@@ -25,7 +25,7 @@ window.Cbn = {
|
|
|
25
25
|
Assets: {
|
|
26
26
|
async getMaterial(materialId: string) {
|
|
27
27
|
//! this creates a new function on the object that uses the imported function of the same name..
|
|
28
|
-
const material = getMaterial(materialId);
|
|
28
|
+
const material = await getMaterial(materialId);
|
|
29
29
|
if (material) return material;
|
|
30
30
|
|
|
31
31
|
// Fallback to random mock material
|