@playcanvas/web-components 0.11.0 → 0.11.1
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 +1 -1
- package/dist/app.d.ts +18 -38
- package/dist/asset.d.ts +8 -7
- package/dist/async-element.d.ts +21 -5
- package/dist/components/button-component.d.ts +3 -7
- package/dist/components/camera-component.d.ts +3 -7
- package/dist/components/collision-component.d.ts +3 -7
- package/dist/components/component.d.ts +22 -4
- package/dist/components/element-component.d.ts +4 -8
- package/dist/components/gsplat-component.d.ts +2 -7
- package/dist/components/layoutchild-component.d.ts +2 -7
- package/dist/components/layoutgroup-component.d.ts +3 -7
- package/dist/components/light-component.d.ts +3 -7
- package/dist/components/listener-component.d.ts +1 -6
- package/dist/components/particlesystem-component.d.ts +2 -7
- package/dist/components/render-component.d.ts +2 -7
- package/dist/components/rigidbody-component.d.ts +3 -7
- package/dist/components/screen-component.d.ts +3 -7
- package/dist/components/script-component.d.ts +8 -20
- package/dist/components/script.d.ts +2 -22
- package/dist/components/scrollbar-component.d.ts +2 -7
- package/dist/components/scrollview-component.d.ts +3 -7
- package/dist/components/sound-component.d.ts +2 -7
- package/dist/components/sound-slot.d.ts +8 -6
- package/dist/custom-elements.json +5049 -10696
- package/dist/entity.d.ts +5 -11
- package/dist/index.d.ts +37 -0
- package/dist/material.d.ts +12 -12
- package/dist/model.d.ts +21 -5
- package/dist/module.d.ts +0 -6
- package/dist/parse.d.ts +2 -1
- package/dist/pwc.cjs +496 -136
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +496 -136
- package/dist/pwc.js.map +1 -1
- package/dist/pwc.min.js +1 -1
- package/dist/pwc.min.js.map +1 -1
- package/dist/pwc.min.mjs +1 -1
- package/dist/pwc.min.mjs.map +1 -1
- package/dist/pwc.mjs +496 -136
- package/dist/pwc.mjs.map +1 -1
- package/dist/scene.d.ts +4 -7
- package/dist/sky.d.ts +13 -5
- package/dist/vscode.html-custom-data.json +26 -26
- package/dist/web-types.json +545 -536
- package/package.json +8 -7
- package/src/app.ts +142 -70
- package/src/asset.ts +34 -28
- package/src/async-element.ts +34 -8
- package/src/components/button-component.ts +5 -9
- package/src/components/camera-component.ts +24 -10
- package/src/components/collision-component.ts +26 -15
- package/src/components/component.ts +58 -8
- package/src/components/element-component.ts +26 -30
- package/src/components/gsplat-component.ts +4 -9
- package/src/components/layoutchild-component.ts +4 -9
- package/src/components/layoutgroup-component.ts +14 -9
- package/src/components/light-component.ts +42 -12
- package/src/components/listener-component.ts +1 -7
- package/src/components/particlesystem-component.ts +7 -15
- package/src/components/render-component.ts +5 -10
- package/src/components/rigidbody-component.ts +23 -16
- package/src/components/screen-component.ts +5 -9
- package/src/components/script-component.ts +108 -46
- package/src/components/script.ts +38 -33
- package/src/components/scrollbar-component.ts +6 -16
- package/src/components/scrollview-component.ts +16 -11
- package/src/components/sound-component.ts +10 -15
- package/src/components/sound-slot.ts +30 -20
- package/src/entity.ts +42 -19
- package/src/index.ts +45 -1
- package/src/loading-bar.ts +8 -8
- package/src/material.ts +63 -37
- package/src/model.ts +69 -14
- package/src/module.ts +8 -7
- package/src/parse.ts +62 -17
- package/src/scene.ts +12 -9
- package/src/sky.ts +50 -9
package/src/material.ts
CHANGED
|
@@ -21,17 +21,26 @@ import {
|
|
|
21
21
|
SPECOCC_GLOSSDEPENDENT,
|
|
22
22
|
SPECOCC_NONE,
|
|
23
23
|
StandardMaterial,
|
|
24
|
-
Vec2
|
|
25
|
-
type EventHandle,
|
|
26
|
-
type Texture
|
|
24
|
+
Vec2
|
|
27
25
|
} from 'playcanvas';
|
|
26
|
+
import type { EventHandle, Texture } from 'playcanvas';
|
|
28
27
|
|
|
29
|
-
import { AppElement } from './app';
|
|
28
|
+
import type { AppElement } from './app';
|
|
30
29
|
import { AssetElement } from './asset';
|
|
31
30
|
import { parseBool, parseColor, parseEnum, parseNumber, parseVec2 } from './parse';
|
|
32
31
|
|
|
33
|
-
type BlendType =
|
|
34
|
-
|
|
32
|
+
type BlendType =
|
|
33
|
+
| 'none'
|
|
34
|
+
| 'normal'
|
|
35
|
+
| 'additive'
|
|
36
|
+
| 'additive-alpha'
|
|
37
|
+
| 'premultiplied'
|
|
38
|
+
| 'multiplicative'
|
|
39
|
+
| 'multiplicative-2x'
|
|
40
|
+
| 'screen'
|
|
41
|
+
| 'min'
|
|
42
|
+
| 'max'
|
|
43
|
+
| 'subtractive';
|
|
35
44
|
|
|
36
45
|
const blendTypes = new Map<BlendType, number>([
|
|
37
46
|
['none', BLEND_NONE],
|
|
@@ -100,8 +109,8 @@ const roughnessAliases = ['roughness', 'roughness-map'];
|
|
|
100
109
|
* The texture slots a `pc-material` can populate. Each is backed by a `pc-asset` id rather than a
|
|
101
110
|
* `Texture`, so the element can be authored before the asset has loaded.
|
|
102
111
|
*/
|
|
103
|
-
type TextureSlot =
|
|
104
|
-
'metalnessMap' | 'normalMap' | 'opacityMap';
|
|
112
|
+
type TextureSlot =
|
|
113
|
+
'aoMap' | 'diffuseMap' | 'emissiveMap' | 'glossMap' | 'heightMap' | 'metalnessMap' | 'normalMap' | 'opacityMap';
|
|
105
114
|
|
|
106
115
|
/**
|
|
107
116
|
* The MaterialElement interface provides properties and methods for manipulating
|
|
@@ -286,7 +295,7 @@ class MaterialElement extends HTMLElement {
|
|
|
286
295
|
|
|
287
296
|
private _useLighting = true;
|
|
288
297
|
|
|
289
|
-
// Diverges from the engine default of false - see the class docblock and
|
|
298
|
+
// Diverges from the engine default of false - see the class docblock and _createMaterial()
|
|
290
299
|
private _useMetalness = true;
|
|
291
300
|
|
|
292
301
|
private _useMetalnessSpecularColor = false;
|
|
@@ -306,10 +315,14 @@ class MaterialElement extends HTMLElement {
|
|
|
306
315
|
|
|
307
316
|
private _glossConflictWarned = false;
|
|
308
317
|
|
|
318
|
+
/**
|
|
319
|
+
* The material. `null` until the containing application has created it — an element present
|
|
320
|
+
* at startup has its material once the application is ready.
|
|
321
|
+
*/
|
|
309
322
|
material: StandardMaterial | null = null;
|
|
310
323
|
|
|
311
324
|
async connectedCallback() {
|
|
312
|
-
const appElement = this.parentElement?.closest('pc-app') as AppElement | null ?? null;
|
|
325
|
+
const appElement = (this.parentElement?.closest('pc-app') as AppElement | null) ?? null;
|
|
313
326
|
|
|
314
327
|
// Materials must be direct children of pc-app (matches the boot query ':scope > pc-material')
|
|
315
328
|
if (!appElement || this.parentElement !== appElement) {
|
|
@@ -326,11 +339,18 @@ class MaterialElement extends HTMLElement {
|
|
|
326
339
|
// elements inserted (or re-inserted) after the app is already running
|
|
327
340
|
if (!this.material) {
|
|
328
341
|
if (!appElement.app) return; // pc-app is re-connecting; its own boot will create this
|
|
329
|
-
this.
|
|
342
|
+
this._createMaterial();
|
|
330
343
|
}
|
|
331
344
|
}
|
|
332
345
|
|
|
333
|
-
|
|
346
|
+
/**
|
|
347
|
+
* Creates the material from the element's cached properties. Called by the containing
|
|
348
|
+
* `<pc-app>` element during its boot sweep, and on connection for elements inserted while
|
|
349
|
+
* the application is already running.
|
|
350
|
+
*
|
|
351
|
+
* @internal
|
|
352
|
+
*/
|
|
353
|
+
_createMaterial() {
|
|
334
354
|
const material = new StandardMaterial();
|
|
335
355
|
this.material = material;
|
|
336
356
|
|
|
@@ -464,10 +484,10 @@ class MaterialElement extends HTMLElement {
|
|
|
464
484
|
* warning latches and reports once per episode, clearing when the clash is resolved.
|
|
465
485
|
*/
|
|
466
486
|
private _warnGlossConflict() {
|
|
467
|
-
const quote = (names: string[]) => `'${names.join('
|
|
487
|
+
const quote = (names: string[]) => `'${names.join("', '")}'`;
|
|
468
488
|
|
|
469
|
-
const roughness = roughnessAliases.filter(name => this.hasAttribute(name));
|
|
470
|
-
const gloss = glossConflicts.filter(name => this.hasAttribute(name));
|
|
489
|
+
const roughness = roughnessAliases.filter((name) => this.hasAttribute(name));
|
|
490
|
+
const gloss = glossConflicts.filter((name) => this.hasAttribute(name));
|
|
471
491
|
|
|
472
492
|
if (roughness.length === 0 || gloss.length === 0) {
|
|
473
493
|
this._glossConflictWarned = false;
|
|
@@ -477,8 +497,10 @@ class MaterialElement extends HTMLElement {
|
|
|
477
497
|
if (this._glossConflictWarned) return;
|
|
478
498
|
this._glossConflictWarned = true;
|
|
479
499
|
|
|
480
|
-
console.warn(
|
|
481
|
-
'
|
|
500
|
+
console.warn(
|
|
501
|
+
`pc-material '${this.id}' sets both ${quote(roughness)} and ${quote(gloss)} - ` +
|
|
502
|
+
'the roughness-* attributes invert gloss, so the two families contradict each other. Use one or the other.'
|
|
503
|
+
);
|
|
482
504
|
}
|
|
483
505
|
|
|
484
506
|
/**
|
|
@@ -488,7 +510,7 @@ class MaterialElement extends HTMLElement {
|
|
|
488
510
|
* @param id - The id of the `pc-asset`, or an empty string to clear the slot.
|
|
489
511
|
* @param slot - The material property to write.
|
|
490
512
|
*/
|
|
491
|
-
|
|
513
|
+
private _setMap(id: string, slot: TextureSlot) {
|
|
492
514
|
// Drop any load still pending for this slot - its texture is no longer the one we want
|
|
493
515
|
this._mapHandles.get(slot)?.off();
|
|
494
516
|
this._mapHandles.delete(slot);
|
|
@@ -509,10 +531,13 @@ class MaterialElement extends HTMLElement {
|
|
|
509
531
|
return;
|
|
510
532
|
}
|
|
511
533
|
|
|
512
|
-
this._mapHandles.set(
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
534
|
+
this._mapHandles.set(
|
|
535
|
+
slot,
|
|
536
|
+
asset.once('load', () => {
|
|
537
|
+
this._mapHandles.delete(slot);
|
|
538
|
+
this._applyMap(slot, asset.resource as Texture);
|
|
539
|
+
})
|
|
540
|
+
);
|
|
516
541
|
}
|
|
517
542
|
|
|
518
543
|
/**
|
|
@@ -592,7 +617,7 @@ class MaterialElement extends HTMLElement {
|
|
|
592
617
|
*/
|
|
593
618
|
set aoMap(value: string) {
|
|
594
619
|
this._aoMap = value;
|
|
595
|
-
this.
|
|
620
|
+
this._setMap(value, 'aoMap');
|
|
596
621
|
}
|
|
597
622
|
|
|
598
623
|
/**
|
|
@@ -850,7 +875,7 @@ class MaterialElement extends HTMLElement {
|
|
|
850
875
|
*/
|
|
851
876
|
set diffuseMap(value: string) {
|
|
852
877
|
this._diffuseMap = value;
|
|
853
|
-
this.
|
|
878
|
+
this._setMap(value, 'diffuseMap');
|
|
854
879
|
}
|
|
855
880
|
|
|
856
881
|
/**
|
|
@@ -1007,7 +1032,7 @@ class MaterialElement extends HTMLElement {
|
|
|
1007
1032
|
*/
|
|
1008
1033
|
set emissiveMap(value: string) {
|
|
1009
1034
|
this._emissiveMap = value;
|
|
1010
|
-
this.
|
|
1035
|
+
this._setMap(value, 'emissiveMap');
|
|
1011
1036
|
}
|
|
1012
1037
|
|
|
1013
1038
|
/**
|
|
@@ -1205,7 +1230,7 @@ class MaterialElement extends HTMLElement {
|
|
|
1205
1230
|
*/
|
|
1206
1231
|
set glossMap(value: string) {
|
|
1207
1232
|
this._glossMap = value;
|
|
1208
|
-
this.
|
|
1233
|
+
this._setMap(value, 'glossMap');
|
|
1209
1234
|
}
|
|
1210
1235
|
|
|
1211
1236
|
/**
|
|
@@ -1322,7 +1347,7 @@ class MaterialElement extends HTMLElement {
|
|
|
1322
1347
|
*/
|
|
1323
1348
|
set heightMap(value: string) {
|
|
1324
1349
|
this._heightMap = value;
|
|
1325
|
-
this.
|
|
1350
|
+
this._setMap(value, 'heightMap');
|
|
1326
1351
|
}
|
|
1327
1352
|
|
|
1328
1353
|
/**
|
|
@@ -1479,7 +1504,7 @@ class MaterialElement extends HTMLElement {
|
|
|
1479
1504
|
*/
|
|
1480
1505
|
set metalnessMap(value: string) {
|
|
1481
1506
|
this._metalnessMap = value;
|
|
1482
|
-
this.
|
|
1507
|
+
this._setMap(value, 'metalnessMap');
|
|
1483
1508
|
}
|
|
1484
1509
|
|
|
1485
1510
|
/**
|
|
@@ -1596,7 +1621,7 @@ class MaterialElement extends HTMLElement {
|
|
|
1596
1621
|
*/
|
|
1597
1622
|
set normalMap(value: string) {
|
|
1598
1623
|
this._normalMap = value;
|
|
1599
|
-
this.
|
|
1624
|
+
this._setMap(value, 'normalMap');
|
|
1600
1625
|
}
|
|
1601
1626
|
|
|
1602
1627
|
/**
|
|
@@ -1694,7 +1719,7 @@ class MaterialElement extends HTMLElement {
|
|
|
1694
1719
|
set occludeDirect(value: boolean) {
|
|
1695
1720
|
this._occludeDirect = value;
|
|
1696
1721
|
if (this.material) {
|
|
1697
|
-
// @ts-ignore see
|
|
1722
|
+
// @ts-ignore see _createMaterial() - the engine mistypes occludeDirect as a number
|
|
1698
1723
|
this.material.occludeDirect = value;
|
|
1699
1724
|
this._scheduleUpdate();
|
|
1700
1725
|
}
|
|
@@ -1796,7 +1821,7 @@ class MaterialElement extends HTMLElement {
|
|
|
1796
1821
|
*/
|
|
1797
1822
|
set opacityMap(value: string) {
|
|
1798
1823
|
this._opacityMap = value;
|
|
1799
|
-
this.
|
|
1824
|
+
this._setMap(value, 'opacityMap');
|
|
1800
1825
|
}
|
|
1801
1826
|
|
|
1802
1827
|
/**
|
|
@@ -2150,6 +2175,13 @@ class MaterialElement extends HTMLElement {
|
|
|
2150
2175
|
return this._useTonemap;
|
|
2151
2176
|
}
|
|
2152
2177
|
|
|
2178
|
+
/**
|
|
2179
|
+
* Returns the {@link StandardMaterial} created by the `<pc-material>` element with the given
|
|
2180
|
+
* `id`, or `undefined` if there is no such element or its material has not been created yet.
|
|
2181
|
+
*
|
|
2182
|
+
* @param id - The `id` of the `<pc-material>` element.
|
|
2183
|
+
* @returns The material, or `undefined`.
|
|
2184
|
+
*/
|
|
2153
2185
|
static get(id: string) {
|
|
2154
2186
|
const materialElement = document.querySelector<MaterialElement>(`pc-material[id="${id}"]`);
|
|
2155
2187
|
return materialElement?.material;
|
|
@@ -2508,10 +2540,4 @@ class MaterialElement extends HTMLElement {
|
|
|
2508
2540
|
|
|
2509
2541
|
customElements.define('pc-material', MaterialElement);
|
|
2510
2542
|
|
|
2511
|
-
declare global {
|
|
2512
|
-
interface HTMLElementTagNameMap {
|
|
2513
|
-
'pc-material': MaterialElement;
|
|
2514
|
-
}
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
2543
|
export { MaterialElement };
|
package/src/model.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ContainerResource, Entity } from 'playcanvas';
|
|
1
|
+
import type { ContainerResource, Entity, EventHandle } from 'playcanvas';
|
|
2
2
|
|
|
3
3
|
import { AssetElement } from './asset';
|
|
4
4
|
import { AsyncElement } from './async-element';
|
|
@@ -10,39 +10,83 @@ import { AsyncElement } from './async-element';
|
|
|
10
10
|
* {@link HTMLElement} interface.
|
|
11
11
|
*/
|
|
12
12
|
class ModelElement extends AsyncElement {
|
|
13
|
-
private _asset
|
|
13
|
+
private _asset = '';
|
|
14
14
|
|
|
15
15
|
private _entity: Entity | null = null;
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Incremented on every new load and on disconnect, and captured by a load when it starts. A
|
|
19
|
+
* load that resumes from an await or a load callback abandons itself if the value has moved
|
|
20
|
+
* on, so a superseded load can neither instantiate a second entity nor parent one that has
|
|
21
|
+
* since been destroyed.
|
|
22
|
+
*/
|
|
23
|
+
private _loadGeneration = 0;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The pending asset-load subscription of the current load, if it is waiting for its asset.
|
|
27
|
+
* Held so that whatever supersedes the load can detach the handler from the asset, rather
|
|
28
|
+
* than leave it registered until the asset loads (or forever, if it never does).
|
|
29
|
+
*/
|
|
30
|
+
private _loadHandle: EventHandle | null = null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The root entity of the instantiated model. `null` until the container asset has loaded
|
|
34
|
+
* and been instantiated, and again once the element has been removed from the document.
|
|
35
|
+
* @returns The model's root entity, or `null`.
|
|
36
|
+
*/
|
|
37
|
+
get entity(): Entity | null {
|
|
38
|
+
return this._entity;
|
|
39
|
+
}
|
|
40
|
+
|
|
17
41
|
connectedCallback() {
|
|
18
42
|
this._loadModel();
|
|
19
43
|
this._onReady();
|
|
20
44
|
}
|
|
21
45
|
|
|
22
46
|
disconnectedCallback() {
|
|
47
|
+
this._loadGeneration++;
|
|
48
|
+
this._detachLoadHandler();
|
|
23
49
|
this._unloadModel();
|
|
50
|
+
this._resetReady();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private _detachLoadHandler() {
|
|
54
|
+
this._loadHandle?.off();
|
|
55
|
+
this._loadHandle = null;
|
|
24
56
|
}
|
|
25
57
|
|
|
26
58
|
private _instantiate(container: ContainerResource) {
|
|
27
|
-
|
|
59
|
+
const generation = this._loadGeneration;
|
|
60
|
+
|
|
61
|
+
const entity = container.instantiateRenderEntity();
|
|
62
|
+
this._entity = entity;
|
|
28
63
|
|
|
29
64
|
// @ts-ignore
|
|
30
65
|
if (container.animations.length > 0) {
|
|
31
|
-
|
|
66
|
+
entity.addComponent('anim');
|
|
32
67
|
// @ts-ignore
|
|
33
|
-
|
|
68
|
+
entity.anim.assignAnimation('animation', container.animations[0].resource);
|
|
34
69
|
}
|
|
35
70
|
|
|
71
|
+
// The parent's readiness re-arms when it is torn down, so these can resume in a later
|
|
72
|
+
// connection cycle. The entity is captured above and the generation re-checked, so a
|
|
73
|
+
// stale resume cannot parent an entity a newer cycle has already destroyed.
|
|
36
74
|
const parentEntityElement = this.closestEntity;
|
|
37
75
|
if (parentEntityElement) {
|
|
38
76
|
parentEntityElement.ready().then(() => {
|
|
39
|
-
|
|
77
|
+
if (generation !== this._loadGeneration) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
parentEntityElement.entity!.addChild(entity);
|
|
40
81
|
});
|
|
41
82
|
} else {
|
|
42
83
|
const appElement = this.closestApp;
|
|
43
84
|
if (appElement) {
|
|
44
85
|
appElement.ready().then(() => {
|
|
45
|
-
|
|
86
|
+
if (generation !== this._loadGeneration) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
appElement.app!.root.addChild(entity);
|
|
46
90
|
});
|
|
47
91
|
}
|
|
48
92
|
}
|
|
@@ -51,7 +95,17 @@ class ModelElement extends AsyncElement {
|
|
|
51
95
|
private async _loadModel() {
|
|
52
96
|
this._unloadModel();
|
|
53
97
|
|
|
98
|
+
// Supersede any load already in flight - only the newest load may instantiate
|
|
99
|
+
const generation = ++this._loadGeneration;
|
|
100
|
+
this._detachLoadHandler();
|
|
101
|
+
|
|
54
102
|
const appElement = await this.closestApp?.ready();
|
|
103
|
+
|
|
104
|
+
// The element may have been removed, or another load started, while we waited
|
|
105
|
+
if (generation !== this._loadGeneration) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
55
109
|
const app = appElement?.app;
|
|
56
110
|
|
|
57
111
|
const asset = AssetElement.get(this._asset);
|
|
@@ -62,7 +116,14 @@ class ModelElement extends AsyncElement {
|
|
|
62
116
|
if (asset.loaded) {
|
|
63
117
|
this._instantiate(asset.resource as ContainerResource);
|
|
64
118
|
} else {
|
|
65
|
-
|
|
119
|
+
// The generation is re-checked even though a superseded handler is detached: the
|
|
120
|
+
// detach relies on how the engine's event emitter treats removal, while the check
|
|
121
|
+
// holds on its own.
|
|
122
|
+
this._loadHandle = asset.once('load', () => {
|
|
123
|
+
this._loadHandle = null;
|
|
124
|
+
if (generation !== this._loadGeneration) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
66
127
|
this._instantiate(asset.resource as ContainerResource);
|
|
67
128
|
});
|
|
68
129
|
app!.assets.load(asset);
|
|
@@ -108,10 +169,4 @@ class ModelElement extends AsyncElement {
|
|
|
108
169
|
|
|
109
170
|
customElements.define('pc-model', ModelElement);
|
|
110
171
|
|
|
111
|
-
declare global {
|
|
112
|
-
interface HTMLElementTagNameMap {
|
|
113
|
-
'pc-model': ModelElement;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
172
|
export { ModelElement };
|
package/src/module.ts
CHANGED
|
@@ -43,17 +43,18 @@ class ModuleElement extends HTMLElement {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Returns the promise that settles when the module has loaded. Awaited by the containing
|
|
48
|
+
* `<pc-app>` element before it creates its graphics device.
|
|
49
|
+
*
|
|
50
|
+
* @returns The load promise.
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
_getLoadPromise(): Promise<void> {
|
|
47
54
|
return this.loadPromise;
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
customElements.define('pc-module', ModuleElement);
|
|
52
59
|
|
|
53
|
-
declare global {
|
|
54
|
-
interface HTMLElementTagNameMap {
|
|
55
|
-
'pc-module': ModuleElement;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
60
|
export { ModuleElement };
|
package/src/parse.ts
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* literal, and returns `null` instead of falling back to a default.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import type { Entity } from 'playcanvas';
|
|
20
|
+
import { Color, Quat, Vec2, Vec3, Vec4 } from 'playcanvas';
|
|
20
21
|
|
|
21
22
|
import { CSS_COLORS } from './colors';
|
|
22
23
|
|
|
@@ -31,7 +32,7 @@ import { CSS_COLORS } from './colors';
|
|
|
31
32
|
*/
|
|
32
33
|
export const parseComponents = (value: string, count: number): number[] | null => {
|
|
33
34
|
const components = value.trim().split(/\s+/).map(Number);
|
|
34
|
-
if (components.length !== count || components.some(component => !Number.isFinite(component))) {
|
|
35
|
+
if (components.length !== count || components.some((component) => !Number.isFinite(component))) {
|
|
35
36
|
return null;
|
|
36
37
|
}
|
|
37
38
|
return components;
|
|
@@ -77,7 +78,11 @@ export const parseBool = (value: string | null, defaultValue: boolean): boolean
|
|
|
77
78
|
* @param attribute - The attribute name, used in the warning message.
|
|
78
79
|
* @returns The parsed Color object.
|
|
79
80
|
*/
|
|
80
|
-
export const parseColor = <T extends Color | null>(
|
|
81
|
+
export const parseColor = <T extends Color | null>(
|
|
82
|
+
value: string | null,
|
|
83
|
+
defaultValue: T,
|
|
84
|
+
attribute: string
|
|
85
|
+
): Color | T => {
|
|
81
86
|
if (value === null) {
|
|
82
87
|
return cloneDefault(defaultValue);
|
|
83
88
|
}
|
|
@@ -92,7 +97,10 @@ export const parseColor = <T extends Color | null>(value: string | null, default
|
|
|
92
97
|
if (/^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(value)) {
|
|
93
98
|
let hex = value.slice(1);
|
|
94
99
|
if (hex.length === 3 || hex.length === 4) {
|
|
95
|
-
hex = hex
|
|
100
|
+
hex = hex
|
|
101
|
+
.split('')
|
|
102
|
+
.map((char) => char + char)
|
|
103
|
+
.join('');
|
|
96
104
|
}
|
|
97
105
|
return new Color().fromString(`#${hex}`);
|
|
98
106
|
}
|
|
@@ -103,7 +111,9 @@ export const parseColor = <T extends Color | null>(value: string | null, default
|
|
|
103
111
|
return new Color(components);
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
console.warn(
|
|
114
|
+
console.warn(
|
|
115
|
+
`Invalid value '${value}' for attribute '${attribute}'. Expected a CSS color name, a hex color or 3 or 4 space-separated numbers. Using '${defaultValue}'.`
|
|
116
|
+
);
|
|
107
117
|
return cloneDefault(defaultValue);
|
|
108
118
|
};
|
|
109
119
|
|
|
@@ -133,7 +143,9 @@ export const parseEnum = <T extends string>(
|
|
|
133
143
|
if (names.includes(value as T)) {
|
|
134
144
|
return value as T;
|
|
135
145
|
}
|
|
136
|
-
console.warn(
|
|
146
|
+
console.warn(
|
|
147
|
+
`Invalid value '${value}' for attribute '${attribute}'. Valid values: ${names.join(', ')}. Using '${defaultValue}'.`
|
|
148
|
+
);
|
|
137
149
|
return defaultValue;
|
|
138
150
|
};
|
|
139
151
|
|
|
@@ -147,13 +159,19 @@ export const parseEnum = <T extends string>(
|
|
|
147
159
|
* @param attribute - The attribute name, used in the warning message.
|
|
148
160
|
* @returns The parsed number.
|
|
149
161
|
*/
|
|
150
|
-
export const parseNumber = <T extends number | null>(
|
|
162
|
+
export const parseNumber = <T extends number | null>(
|
|
163
|
+
value: string | null,
|
|
164
|
+
defaultValue: T,
|
|
165
|
+
attribute: string
|
|
166
|
+
): number | T => {
|
|
151
167
|
if (value === null) {
|
|
152
168
|
return defaultValue;
|
|
153
169
|
}
|
|
154
170
|
const number = value.trim() === '' ? NaN : Number(value);
|
|
155
171
|
if (!Number.isFinite(number)) {
|
|
156
|
-
console.warn(
|
|
172
|
+
console.warn(
|
|
173
|
+
`Invalid value '${value}' for attribute '${attribute}'. Expected a finite number. Using '${defaultValue}'.`
|
|
174
|
+
);
|
|
157
175
|
return defaultValue;
|
|
158
176
|
}
|
|
159
177
|
return number;
|
|
@@ -170,13 +188,19 @@ export const parseNumber = <T extends number | null>(value: string | null, defau
|
|
|
170
188
|
* @param attribute - The attribute name, used in the warning message.
|
|
171
189
|
* @returns The parsed Quat object.
|
|
172
190
|
*/
|
|
173
|
-
export const parseQuat = <T extends Quat | null>(
|
|
191
|
+
export const parseQuat = <T extends Quat | null>(
|
|
192
|
+
value: string | null,
|
|
193
|
+
defaultValue: T,
|
|
194
|
+
attribute: string
|
|
195
|
+
): Quat | T => {
|
|
174
196
|
if (value === null) {
|
|
175
197
|
return cloneDefault(defaultValue);
|
|
176
198
|
}
|
|
177
199
|
const components = parseComponents(value, 3);
|
|
178
200
|
if (!components) {
|
|
179
|
-
console.warn(
|
|
201
|
+
console.warn(
|
|
202
|
+
`Invalid value '${value}' for attribute '${attribute}'. Expected 3 space-separated numbers. Using '${defaultValue}'.`
|
|
203
|
+
);
|
|
180
204
|
return cloneDefault(defaultValue);
|
|
181
205
|
}
|
|
182
206
|
return new Quat().setFromEulerAngles(components[0], components[1], components[2]);
|
|
@@ -200,7 +224,10 @@ export const parseTags = (value: string | null, defaultValue: string[] = []): st
|
|
|
200
224
|
// caller's default, or a later mutation would write back through it.
|
|
201
225
|
return [...defaultValue];
|
|
202
226
|
}
|
|
203
|
-
return value
|
|
227
|
+
return value
|
|
228
|
+
.split(',')
|
|
229
|
+
.map((tag) => tag.trim())
|
|
230
|
+
.filter((tag) => tag !== '');
|
|
204
231
|
};
|
|
205
232
|
|
|
206
233
|
/**
|
|
@@ -213,13 +240,19 @@ export const parseTags = (value: string | null, defaultValue: string[] = []): st
|
|
|
213
240
|
* @param attribute - The attribute name, used in the warning message.
|
|
214
241
|
* @returns The parsed Vec2 object.
|
|
215
242
|
*/
|
|
216
|
-
export const parseVec2 = <T extends Vec2 | null>(
|
|
243
|
+
export const parseVec2 = <T extends Vec2 | null>(
|
|
244
|
+
value: string | null,
|
|
245
|
+
defaultValue: T,
|
|
246
|
+
attribute: string
|
|
247
|
+
): Vec2 | T => {
|
|
217
248
|
if (value === null) {
|
|
218
249
|
return cloneDefault(defaultValue);
|
|
219
250
|
}
|
|
220
251
|
const components = parseComponents(value, 2);
|
|
221
252
|
if (!components) {
|
|
222
|
-
console.warn(
|
|
253
|
+
console.warn(
|
|
254
|
+
`Invalid value '${value}' for attribute '${attribute}'. Expected 2 space-separated numbers. Using '${defaultValue}'.`
|
|
255
|
+
);
|
|
223
256
|
return cloneDefault(defaultValue);
|
|
224
257
|
}
|
|
225
258
|
return new Vec2(components);
|
|
@@ -235,13 +268,19 @@ export const parseVec2 = <T extends Vec2 | null>(value: string | null, defaultVa
|
|
|
235
268
|
* @param attribute - The attribute name, used in the warning message.
|
|
236
269
|
* @returns The parsed Vec3 object.
|
|
237
270
|
*/
|
|
238
|
-
export const parseVec3 = <T extends Vec3 | null>(
|
|
271
|
+
export const parseVec3 = <T extends Vec3 | null>(
|
|
272
|
+
value: string | null,
|
|
273
|
+
defaultValue: T,
|
|
274
|
+
attribute: string
|
|
275
|
+
): Vec3 | T => {
|
|
239
276
|
if (value === null) {
|
|
240
277
|
return cloneDefault(defaultValue);
|
|
241
278
|
}
|
|
242
279
|
const components = parseComponents(value, 3);
|
|
243
280
|
if (!components) {
|
|
244
|
-
console.warn(
|
|
281
|
+
console.warn(
|
|
282
|
+
`Invalid value '${value}' for attribute '${attribute}'. Expected 3 space-separated numbers. Using '${defaultValue}'.`
|
|
283
|
+
);
|
|
245
284
|
return cloneDefault(defaultValue);
|
|
246
285
|
}
|
|
247
286
|
return new Vec3(components);
|
|
@@ -257,13 +296,19 @@ export const parseVec3 = <T extends Vec3 | null>(value: string | null, defaultVa
|
|
|
257
296
|
* @param attribute - The attribute name, used in the warning message.
|
|
258
297
|
* @returns The parsed Vec4 object.
|
|
259
298
|
*/
|
|
260
|
-
export const parseVec4 = <T extends Vec4 | null>(
|
|
299
|
+
export const parseVec4 = <T extends Vec4 | null>(
|
|
300
|
+
value: string | null,
|
|
301
|
+
defaultValue: T,
|
|
302
|
+
attribute: string
|
|
303
|
+
): Vec4 | T => {
|
|
261
304
|
if (value === null) {
|
|
262
305
|
return cloneDefault(defaultValue);
|
|
263
306
|
}
|
|
264
307
|
const components = parseComponents(value, 4);
|
|
265
308
|
if (!components) {
|
|
266
|
-
console.warn(
|
|
309
|
+
console.warn(
|
|
310
|
+
`Invalid value '${value}' for attribute '${attribute}'. Expected 4 space-separated numbers. Using '${defaultValue}'.`
|
|
311
|
+
);
|
|
267
312
|
return cloneDefault(defaultValue);
|
|
268
313
|
}
|
|
269
314
|
return new Vec4(components);
|
package/src/scene.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Scene } from 'playcanvas';
|
|
2
|
+
import { Color, Vec3 } from 'playcanvas';
|
|
2
3
|
|
|
3
4
|
import { AsyncElement } from './async-element';
|
|
4
5
|
import { parseColor, parseEnum, parseNumber, parseVec3 } from './parse';
|
|
@@ -77,12 +78,20 @@ class SceneElement extends AsyncElement {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
this._scene = app.scene;
|
|
80
|
-
this.
|
|
81
|
+
this._updateSceneSettings();
|
|
81
82
|
|
|
82
83
|
this._onReady();
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
disconnectedCallback() {
|
|
87
|
+
// The scene belongs to the application, and removing this element - or the <pc-app>
|
|
88
|
+
// above it, which disconnects first - parts the two. Re-arm readiness so a re-inserted
|
|
89
|
+
// element announces the scene it acquires then, not the one it lost here.
|
|
90
|
+
this._scene = null;
|
|
91
|
+
this._resetReady();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private _updateSceneSettings() {
|
|
86
95
|
if (this._scene) {
|
|
87
96
|
this._scene.fog.type = this._fog;
|
|
88
97
|
this._scene.fog.color = this._fogColor;
|
|
@@ -251,10 +260,4 @@ class SceneElement extends AsyncElement {
|
|
|
251
260
|
|
|
252
261
|
customElements.define('pc-scene', SceneElement);
|
|
253
262
|
|
|
254
|
-
declare global {
|
|
255
|
-
interface HTMLElementTagNameMap {
|
|
256
|
-
'pc-scene': SceneElement;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
263
|
export { SceneElement };
|