@playcanvas/web-components 0.9.0 → 0.10.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.
Files changed (72) hide show
  1. package/README.md +18 -0
  2. package/dist/app.d.ts +3 -2
  3. package/dist/asset.d.ts +16 -1
  4. package/dist/async-element.d.ts +3 -0
  5. package/dist/components/button-component.d.ts +1 -1
  6. package/dist/components/camera-component.d.ts +1 -1
  7. package/dist/components/collision-component.d.ts +1 -1
  8. package/dist/components/component.d.ts +1 -1
  9. package/dist/components/element-component.d.ts +1 -1
  10. package/dist/components/gsplat-component.d.ts +1 -1
  11. package/dist/components/layoutchild-component.d.ts +1 -1
  12. package/dist/components/layoutgroup-component.d.ts +1 -1
  13. package/dist/components/light-component.d.ts +1 -1
  14. package/dist/components/particlesystem-component.d.ts +1 -1
  15. package/dist/components/render-component.d.ts +1 -1
  16. package/dist/components/rigidbody-component.d.ts +1 -1
  17. package/dist/components/screen-component.d.ts +1 -1
  18. package/dist/components/script.d.ts +8 -1
  19. package/dist/components/scrollbar-component.d.ts +1 -1
  20. package/dist/components/scrollview-component.d.ts +1 -1
  21. package/dist/components/sound-component.d.ts +1 -1
  22. package/dist/components/sound-slot.d.ts +9 -1
  23. package/dist/custom-elements.json +16231 -0
  24. package/dist/entity.d.ts +19 -1
  25. package/dist/material.d.ts +966 -4
  26. package/dist/model.d.ts +1 -1
  27. package/dist/module.d.ts +10 -0
  28. package/dist/{utils.d.ts → parse.d.ts} +63 -33
  29. package/dist/pwc.cjs +2677 -586
  30. package/dist/pwc.cjs.map +1 -1
  31. package/dist/pwc.js +2677 -586
  32. package/dist/pwc.js.map +1 -1
  33. package/dist/pwc.min.js +1 -1
  34. package/dist/pwc.min.js.map +1 -1
  35. package/dist/pwc.min.mjs +2 -0
  36. package/dist/pwc.min.mjs.map +1 -0
  37. package/dist/pwc.mjs +2678 -587
  38. package/dist/pwc.mjs.map +1 -1
  39. package/dist/scene.d.ts +1 -1
  40. package/dist/sky.d.ts +1 -1
  41. package/dist/vscode.html-custom-data.json +1795 -0
  42. package/dist/web-types.json +3592 -0
  43. package/package.json +29 -11
  44. package/src/app.ts +6 -5
  45. package/src/asset.ts +19 -2
  46. package/src/async-element.ts +3 -0
  47. package/src/components/button-component.ts +6 -6
  48. package/src/components/camera-component.ts +2 -2
  49. package/src/components/collision-component.ts +2 -2
  50. package/src/components/component.ts +2 -2
  51. package/src/components/element-component.ts +6 -6
  52. package/src/components/gsplat-component.ts +3 -3
  53. package/src/components/layoutchild-component.ts +2 -2
  54. package/src/components/layoutgroup-component.ts +2 -2
  55. package/src/components/light-component.ts +2 -2
  56. package/src/components/particlesystem-component.ts +2 -2
  57. package/src/components/render-component.ts +10 -5
  58. package/src/components/rigidbody-component.ts +2 -2
  59. package/src/components/screen-component.ts +2 -2
  60. package/src/components/script-component.ts +4 -4
  61. package/src/components/script.ts +9 -2
  62. package/src/components/scrollbar-component.ts +3 -3
  63. package/src/components/scrollview-component.ts +6 -6
  64. package/src/components/sound-component.ts +2 -2
  65. package/src/components/sound-slot.ts +31 -9
  66. package/src/entity.ts +25 -7
  67. package/src/material.ts +2396 -59
  68. package/src/model.ts +2 -2
  69. package/src/module.ts +10 -0
  70. package/src/{utils.ts → parse.ts} +104 -65
  71. package/src/scene.ts +2 -2
  72. package/src/sky.ts +3 -3
package/dist/pwc.cjs CHANGED
@@ -4,8 +4,13 @@ var playcanvas = require('playcanvas');
4
4
 
5
5
  /**
6
6
  * Base class for all PlayCanvas Web Components that initialize asynchronously.
7
+ *
8
+ * @fires {CustomEvent} ready - Fired once the element is fully initialized. Bubbles and is
9
+ * composed.
7
10
  */
8
11
  class AsyncElement extends HTMLElement {
12
+ _readyPromise;
13
+ _readyResolve;
9
14
  /** @ignore */
10
15
  constructor() {
11
16
  super();
@@ -14,12 +19,10 @@ class AsyncElement extends HTMLElement {
14
19
  });
15
20
  }
16
21
  get closestApp() {
17
- var _a;
18
- return (_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.closest('pc-app');
22
+ return this.parentElement?.closest('pc-app');
19
23
  }
20
24
  get closestEntity() {
21
- var _a;
22
- return (_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.closest('pc-entity');
25
+ return this.parentElement?.closest('pc-entity');
23
26
  }
24
27
  /**
25
28
  * Called when the element is fully initialized and ready. Subclasses should call this when
@@ -50,7 +53,7 @@ async function whenReady(target) {
50
53
  try {
51
54
  element = document.querySelector(target);
52
55
  }
53
- catch (_a) {
56
+ catch {
54
57
  throw new Error(`whenReady: '${target}' is not a valid CSS selector`);
55
58
  }
56
59
  if (!element) {
@@ -73,8 +76,19 @@ async function whenReady(target) {
73
76
  * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-module/ | `<pc-module>`} elements.
74
77
  * The ModuleElement interface also inherits the properties and methods of the
75
78
  * {@link HTMLElement} interface.
79
+ *
80
+ * Note that these attributes are read once when the element is created, so changing them later
81
+ * has no effect.
82
+ *
83
+ * @attribute {string} name - The name of the WebAssembly module to configure, e.g. `Basis` or
84
+ * `Ammo`.
85
+ * @attribute {string} glue - The URL of the module's glue script.
86
+ * @attribute {string} wasm - The URL of the module's WebAssembly binary.
87
+ * @attribute {string} fallback - The URL of the module's asm.js fallback script, used when
88
+ * WebAssembly is unavailable.
76
89
  */
77
90
  class ModuleElement extends HTMLElement {
91
+ loadPromise;
78
92
  /** @ignore */
79
93
  constructor() {
80
94
  super();
@@ -254,20 +268,22 @@ const CSS_COLORS = {
254
268
  };
255
269
 
256
270
  /**
257
- * Parse a boolean attribute value. The same rules apply to every boolean attribute:
271
+ * Converts HTML attribute values into the values the engine expects. Every element's
272
+ * `attributeChangedCallback` funnels through this module.
258
273
  *
259
- * - Attribute absent (or removed): the supplied default is used.
260
- * - Attribute set to the string 'false': `false`.
261
- * - Attribute present with any other value, including the empty string of a bare boolean
262
- * attribute (e.g. `<pc-light cast-shadows>`): `true`.
274
+ * The parsers share one contract:
263
275
  *
264
- * @param value - The attribute value to parse (`null` when the attribute is absent).
265
- * @param defaultValue - The value to use when the attribute is absent or removed.
266
- * @returns The parsed boolean.
276
+ * - A `null` value means the attribute is absent or was removed, and yields the supplied default.
277
+ * - A malformed value yields the same default and logs exactly one `console.warn` naming the
278
+ * attribute, so misuse is reported rather than thrown — nothing here throws or rejects.
279
+ * - A math-type default is cloned on the way out, which is what makes it safe to pass the engine's
280
+ * shared frozen constants (`Vec3.ZERO`, `Color.WHITE`) as defaults.
281
+ * - `parseBool` and `parseTags` take no attribute name, because every value is valid for them and
282
+ * so they never warn.
283
+ *
284
+ * `getEntity` is the exception: it resolves a reference to a live entity rather than parsing a
285
+ * literal, and returns `null` instead of falling back to a default.
267
286
  */
268
- const parseBool = (value, defaultValue) => {
269
- return value === null ? defaultValue : value !== 'false';
270
- };
271
287
  /**
272
288
  * Splits an attribute value into exactly `count` numeric components. Returns `null` when the
273
289
  * value does not consist of exactly `count` whitespace-separated finite numbers.
@@ -295,6 +311,21 @@ const parseComponents = (value, count) => {
295
311
  const cloneDefault = (value) => {
296
312
  return (value === null ? null : value.clone());
297
313
  };
314
+ /**
315
+ * Parse a boolean attribute value. The same rules apply to every boolean attribute:
316
+ *
317
+ * - Attribute absent (or removed): the supplied default is used.
318
+ * - Attribute set to the string 'false': `false`.
319
+ * - Attribute present with any other value, including the empty string of a bare boolean
320
+ * attribute (e.g. `<pc-light cast-shadows>`): `true`.
321
+ *
322
+ * @param value - The attribute value to parse (`null` when the attribute is absent).
323
+ * @param defaultValue - The value to use when the attribute is absent or removed.
324
+ * @returns The parsed boolean.
325
+ */
326
+ const parseBool = (value, defaultValue) => {
327
+ return value === null ? defaultValue : value !== 'false';
328
+ };
298
329
  /**
299
330
  * Parse a color attribute value. The expected format is a CSS color name (e.g. 'rebeccapurple'),
300
331
  * a hex color (e.g. '#ff0000' or '#f00'), or 3 or 4 space-separated numbers in the range 0 to 1
@@ -308,7 +339,6 @@ const cloneDefault = (value) => {
308
339
  * @returns The parsed Color object.
309
340
  */
310
341
  const parseColor = (value, defaultValue, attribute) => {
311
- var _a;
312
342
  if (value === null) {
313
343
  return cloneDefault(defaultValue);
314
344
  }
@@ -326,13 +356,56 @@ const parseColor = (value, defaultValue, attribute) => {
326
356
  return new playcanvas.Color().fromString(`#${hex}`);
327
357
  }
328
358
  // 3 or 4 space-separated components (e.g. '1 0.5 0.5')
329
- const components = (_a = parseComponents(value, 4)) !== null && _a !== void 0 ? _a : parseComponents(value, 3);
359
+ const components = parseComponents(value, 4) ?? parseComponents(value, 3);
330
360
  if (components) {
331
361
  return new playcanvas.Color(components);
332
362
  }
333
363
  console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected a CSS color name, a hex color or 3 or 4 space-separated numbers. Using '${defaultValue}'.`);
334
364
  return cloneDefault(defaultValue);
335
365
  };
366
+ /**
367
+ * Resolves an enum attribute value against its set of valid names. Returns the value when it is
368
+ * one of the valid names. Returns `defaultValue` when the attribute is absent (`null`), or when
369
+ * the value is invalid — the latter also logs a warning listing the valid names.
370
+ *
371
+ * @param value - The attribute value to parse (`null` when the attribute is absent).
372
+ * @param valid - The valid names: an array, or a map whose keys are the valid names.
373
+ * @param defaultValue - The value to use when the attribute is absent or invalid.
374
+ * @param attribute - The attribute name, used in the warning message.
375
+ * @returns The resolved enum name.
376
+ */
377
+ const parseEnum = (value, valid, defaultValue, attribute) => {
378
+ if (value === null) {
379
+ return defaultValue;
380
+ }
381
+ const names = Array.isArray(valid) ? valid : [...valid.keys()];
382
+ if (names.includes(value)) {
383
+ return value;
384
+ }
385
+ console.warn(`Invalid value '${value}' for attribute '${attribute}'. Valid values: ${names.join(', ')}. Using '${defaultValue}'.`);
386
+ return defaultValue;
387
+ };
388
+ /**
389
+ * Parses a number attribute value. Returns the parsed number when the value is a finite number.
390
+ * Returns `defaultValue` when the attribute is absent (`null`), or when the value is not a
391
+ * finite number — the latter also logs a warning.
392
+ *
393
+ * @param value - The attribute value to parse (`null` when the attribute is absent).
394
+ * @param defaultValue - The value to use when the attribute is absent or invalid.
395
+ * @param attribute - The attribute name, used in the warning message.
396
+ * @returns The parsed number.
397
+ */
398
+ const parseNumber = (value, defaultValue, attribute) => {
399
+ if (value === null) {
400
+ return defaultValue;
401
+ }
402
+ const number = value.trim() === '' ? NaN : Number(value);
403
+ if (!Number.isFinite(number)) {
404
+ console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected a finite number. Using '${defaultValue}'.`);
405
+ return defaultValue;
406
+ }
407
+ return number;
408
+ };
336
409
  /**
337
410
  * Parse an Euler-angles attribute value into a quaternion. The expected format is 3
338
411
  * space-separated angles in degrees (e.g. '0 90 0'). Returns `defaultValue` (cloned, when it is
@@ -355,6 +428,26 @@ const parseQuat = (value, defaultValue, attribute) => {
355
428
  }
356
429
  return new playcanvas.Quat().setFromEulerAngles(components[0], components[1], components[2]);
357
430
  };
431
+ /**
432
+ * Parse a tags attribute value. The expected format is a comma-separated list of tag names
433
+ * (e.g. 'enemy, flying'). Surrounding whitespace is trimmed from each name and empty names are
434
+ * discarded, so a trailing comma or a doubled separator does not produce a blank tag. Returns a
435
+ * copy of `defaultValue` when the attribute is absent or removed (`null`).
436
+ *
437
+ * Every value is valid, so this never warns.
438
+ *
439
+ * @param value - The attribute value to parse (`null` when the attribute is absent).
440
+ * @param defaultValue - The value to use when the attribute is absent or removed.
441
+ * @returns The parsed tag names.
442
+ */
443
+ const parseTags = (value, defaultValue = []) => {
444
+ if (value === null) {
445
+ // Copied for the same reason cloneDefault exists: a parsed result must never alias the
446
+ // caller's default, or a later mutation would write back through it.
447
+ return [...defaultValue];
448
+ }
449
+ return value.split(',').map(tag => tag.trim()).filter(tag => tag !== '');
450
+ };
358
451
  /**
359
452
  * Parse a Vec2 attribute value. The expected format is 2 space-separated numbers (e.g. '1 2').
360
453
  * Returns `defaultValue` (cloned, when it is a vector) when the attribute is absent (`null`),
@@ -418,49 +511,6 @@ const parseVec4 = (value, defaultValue, attribute) => {
418
511
  }
419
512
  return new playcanvas.Vec4(components);
420
513
  };
421
- /**
422
- * Resolves an enum attribute value against its set of valid names. Returns the value when it is
423
- * one of the valid names. Returns `defaultValue` when the attribute is absent (`null`), or when
424
- * the value is invalid — the latter also logs a warning listing the valid names.
425
- *
426
- * @param value - The attribute value to parse (`null` when the attribute is absent).
427
- * @param valid - The valid names: an array, or a map whose keys are the valid names.
428
- * @param defaultValue - The value to use when the attribute is absent or invalid.
429
- * @param attribute - The attribute name, used in the warning message.
430
- * @returns The resolved enum name.
431
- */
432
- const parseEnum = (value, valid, defaultValue, attribute) => {
433
- if (value === null) {
434
- return defaultValue;
435
- }
436
- const names = Array.isArray(valid) ? valid : [...valid.keys()];
437
- if (names.includes(value)) {
438
- return value;
439
- }
440
- console.warn(`Invalid value '${value}' for attribute '${attribute}'. Valid values: ${names.join(', ')}. Using '${defaultValue}'.`);
441
- return defaultValue;
442
- };
443
- /**
444
- * Parses a number attribute value. Returns the parsed number when the value is a finite number.
445
- * Returns `defaultValue` when the attribute is absent (`null`), or when the value is not a
446
- * finite number — the latter also logs a warning.
447
- *
448
- * @param value - The attribute value to parse (`null` when the attribute is absent).
449
- * @param defaultValue - The value to use when the attribute is absent or invalid.
450
- * @param attribute - The attribute name, used in the warning message.
451
- * @returns The parsed number.
452
- */
453
- const parseNumber = (value, defaultValue, attribute) => {
454
- if (value === null) {
455
- return defaultValue;
456
- }
457
- const number = value.trim() === '' ? NaN : Number(value);
458
- if (!Number.isFinite(number)) {
459
- console.warn(`Invalid value '${value}' for attribute '${attribute}'. Expected a finite number. Using '${defaultValue}'.`);
460
- return defaultValue;
461
- }
462
- return number;
463
- };
464
514
  /**
465
515
  * Resolves a reference string to the {@link Entity} backing a `<pc-entity>` element. The reference
466
516
  * can be a CSS selector (e.g. `#my-id`, `pc-entity[name="Foo"]`), a bare element id, or a bare
@@ -470,7 +520,6 @@ const parseNumber = (value, defaultValue, attribute) => {
470
520
  * @returns The resolved entity, or `null`.
471
521
  */
472
522
  const getEntity = (ref) => {
473
- var _a, _b;
474
523
  if (!ref) {
475
524
  return null;
476
525
  }
@@ -480,13 +529,13 @@ const getEntity = (ref) => {
480
529
  try {
481
530
  element = document.querySelector(ref);
482
531
  }
483
- catch (_c) {
532
+ catch {
484
533
  element = null;
485
534
  }
486
535
  if (!element) {
487
- element = (_a = document.getElementById(ref)) !== null && _a !== void 0 ? _a : document.querySelector(`pc-entity[name="${ref}"]`);
536
+ element = document.getElementById(ref) ?? document.querySelector(`pc-entity[name="${ref}"]`);
488
537
  }
489
- return (_b = element === null || element === void 0 ? void 0 : element.entity) !== null && _b !== void 0 ? _b : null;
538
+ return element?.entity ?? null;
490
539
  };
491
540
 
492
541
  /**
@@ -496,6 +545,32 @@ const getEntity = (ref) => {
496
545
  * {@link HTMLElement} interface.
497
546
  */
498
547
  class AppElement extends AsyncElement {
548
+ /**
549
+ * The canvas element.
550
+ */
551
+ _canvas = null;
552
+ _alpha = true;
553
+ _backend = 'webgpu';
554
+ _antialias = true;
555
+ _depth = true;
556
+ _stencil = true;
557
+ _highResolution = true;
558
+ _hierarchyReady = false;
559
+ _picker = null;
560
+ _hasPointerListeners = {
561
+ pointerenter: false,
562
+ pointerleave: false,
563
+ pointerdown: false,
564
+ pointerup: false,
565
+ pointermove: false
566
+ };
567
+ _hoveredEntity = null;
568
+ _pointerHandlers = {
569
+ pointermove: null,
570
+ pointerdown: null,
571
+ pointerup: null
572
+ };
573
+ _app = null;
499
574
  /**
500
575
  * The PlayCanvas application instance. Available once the element is ready — await
501
576
  * {@link whenReady} or the element's `ready()` promise before accessing it.
@@ -511,32 +586,6 @@ class AppElement extends AsyncElement {
511
586
  */
512
587
  constructor() {
513
588
  super();
514
- /**
515
- * The canvas element.
516
- */
517
- this._canvas = null;
518
- this._alpha = true;
519
- this._backend = 'webgl2';
520
- this._antialias = true;
521
- this._depth = true;
522
- this._stencil = true;
523
- this._highResolution = true;
524
- this._hierarchyReady = false;
525
- this._picker = null;
526
- this._hasPointerListeners = {
527
- pointerenter: false,
528
- pointerleave: false,
529
- pointerdown: false,
530
- pointerup: false,
531
- pointermove: false
532
- };
533
- this._hoveredEntity = null;
534
- this._pointerHandlers = {
535
- pointermove: null,
536
- pointerdown: null,
537
- pointerup: null
538
- };
539
- this._app = null;
540
589
  // Bind methods to maintain 'this' context
541
590
  this._onWindowResize = this._onWindowResize.bind(this);
542
591
  }
@@ -876,7 +925,8 @@ class AppElement extends AsyncElement {
876
925
  return this._antialias;
877
926
  }
878
927
  /**
879
- * Sets the graphics backend.
928
+ * Sets the graphics backend. Defaults to 'webgpu', which falls back to 'webgl2' if WebGPU
929
+ * is not supported by the browser.
880
930
  * @param value - The graphics backend ('webgpu', 'webgl2', or 'null').
881
931
  */
882
932
  set backend(value) {
@@ -955,7 +1005,7 @@ class AppElement extends AsyncElement {
955
1005
  this.antialias = parseBool(newValue, true);
956
1006
  break;
957
1007
  case 'backend':
958
- this.backend = parseEnum(newValue, ['webgpu', 'webgl2', 'null'], 'webgl2', name);
1008
+ this.backend = parseEnum(newValue, ['webgpu', 'webgl2', 'null'], 'webgpu', name);
959
1009
  break;
960
1010
  case 'depth':
961
1011
  this.depth = parseBool(newValue, true);
@@ -976,48 +1026,63 @@ customElements.define('pc-app', AppElement);
976
1026
  * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-entity/ | `<pc-entity>`} elements.
977
1027
  * The EntityElement interface also inherits the properties and methods of the
978
1028
  * {@link HTMLElement} interface.
1029
+ *
1030
+ * The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
1031
+ * intersects this entity's geometry. They are only generated while the entity has a listener for
1032
+ * them, registered either with {@link addEventListener} or with the matching inline `onpointer*`
1033
+ * attribute.
1034
+ *
1035
+ * @attribute {string} onpointerenter - Script to run when the pointer moves onto the entity.
1036
+ * @attribute {string} onpointerleave - Script to run when the pointer moves off the entity.
1037
+ * @attribute {string} onpointermove - Script to run when the pointer moves over the entity.
1038
+ * @attribute {string} onpointerdown - Script to run when a pointer button is pressed over the
1039
+ * entity.
1040
+ * @attribute {string} onpointerup - Script to run when a pointer button is released over the
1041
+ * entity.
1042
+ * @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the entity.
1043
+ * @fires {PointerEvent} pointerleave - Fired when the pointer moves off the entity.
1044
+ * @fires {PointerEvent} pointermove - Fired when the pointer moves over the entity.
1045
+ * @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the entity.
1046
+ * @fires {PointerEvent} pointerup - Fired when a pointer button is released over the entity.
979
1047
  */
980
1048
  class EntityElement extends AsyncElement {
981
- constructor() {
982
- super(...arguments);
983
- /**
984
- * Whether the entity is enabled.
985
- */
986
- this._enabled = true;
987
- /**
988
- * The name of the entity.
989
- */
990
- this._name = 'Untitled';
991
- /**
992
- * The position of the entity.
993
- */
994
- this._position = new playcanvas.Vec3();
995
- /**
996
- * The rotation of the entity.
997
- */
998
- this._rotation = new playcanvas.Vec3();
999
- /**
1000
- * The scale of the entity.
1001
- */
1002
- this._scale = new playcanvas.Vec3(1, 1, 1);
1003
- /**
1004
- * The tags of the entity.
1005
- */
1006
- this._tags = [];
1007
- /**
1008
- * The pointer event listeners for the entity.
1009
- */
1010
- this._listeners = {};
1011
- /**
1012
- * The event types for which an inline `onpointer*` attribute is currently present.
1013
- */
1014
- this._inlineHandlerTypes = new Set();
1015
- /**
1016
- * Whether the hierarchy has been built for this entity.
1017
- */
1018
- this._built = false;
1019
- this._entity = null;
1020
- }
1049
+ /**
1050
+ * Whether the entity is enabled.
1051
+ */
1052
+ _enabled = true;
1053
+ /**
1054
+ * The name of the entity.
1055
+ */
1056
+ _name = 'Untitled';
1057
+ /**
1058
+ * The position of the entity.
1059
+ */
1060
+ _position = new playcanvas.Vec3();
1061
+ /**
1062
+ * The rotation of the entity.
1063
+ */
1064
+ _rotation = new playcanvas.Vec3();
1065
+ /**
1066
+ * The scale of the entity.
1067
+ */
1068
+ _scale = new playcanvas.Vec3(1, 1, 1);
1069
+ /**
1070
+ * The tags of the entity.
1071
+ */
1072
+ _tags = [];
1073
+ /**
1074
+ * The pointer event listeners for the entity.
1075
+ */
1076
+ _listeners = {};
1077
+ /**
1078
+ * The event types for which an inline `onpointer*` attribute is currently present.
1079
+ */
1080
+ _inlineHandlerTypes = new Set();
1081
+ /**
1082
+ * Whether the hierarchy has been built for this entity.
1083
+ */
1084
+ _built = false;
1085
+ _entity = null;
1021
1086
  /**
1022
1087
  * The PlayCanvas entity instance. Available once the element is ready — await
1023
1088
  * {@link whenReady} or the element's `ready()` promise before accessing it.
@@ -1040,9 +1105,9 @@ class EntityElement extends AsyncElement {
1040
1105
  entity.setLocalPosition(parseVec3(this.getAttribute('position'), playcanvas.Vec3.ZERO, 'position'));
1041
1106
  entity.setLocalEulerAngles(parseVec3(this.getAttribute('rotation'), playcanvas.Vec3.ZERO, 'rotation'));
1042
1107
  entity.setLocalScale(parseVec3(this.getAttribute('scale'), playcanvas.Vec3.ONE, 'scale'));
1043
- const tags = this.getAttribute('tags');
1044
- if (tags) {
1045
- entity.tags.add(tags.split(',').map(tag => tag.trim()));
1108
+ const tags = parseTags(this.getAttribute('tags'));
1109
+ if (tags.length > 0) {
1110
+ entity.tags.add(tags);
1046
1111
  }
1047
1112
  }
1048
1113
  buildHierarchy(app) {
@@ -1050,7 +1115,7 @@ class EntityElement extends AsyncElement {
1050
1115
  return;
1051
1116
  this._built = true;
1052
1117
  const closestEntity = this.closestEntity;
1053
- if (closestEntity === null || closestEntity === void 0 ? void 0 : closestEntity.entity) {
1118
+ if (closestEntity?.entity) {
1054
1119
  closestEntity.entity.addChild(this.entity);
1055
1120
  }
1056
1121
  else {
@@ -1239,7 +1304,7 @@ class EntityElement extends AsyncElement {
1239
1304
  this.enabled = parseBool(newValue, true);
1240
1305
  break;
1241
1306
  case 'name':
1242
- this.name = newValue;
1307
+ this.name = newValue ?? 'Untitled';
1243
1308
  break;
1244
1309
  case 'position':
1245
1310
  this.position = parseVec3(newValue, playcanvas.Vec3.ZERO, name);
@@ -1251,7 +1316,7 @@ class EntityElement extends AsyncElement {
1251
1316
  this.scale = parseVec3(newValue, playcanvas.Vec3.ONE, name);
1252
1317
  break;
1253
1318
  case 'tags':
1254
- this.tags = newValue.split(',').map(tag => tag.trim());
1319
+ this.tags = parseTags(newValue);
1255
1320
  break;
1256
1321
  case 'onpointerenter':
1257
1322
  case 'onpointerleave':
@@ -1282,8 +1347,7 @@ class EntityElement extends AsyncElement {
1282
1347
  }
1283
1348
  }
1284
1349
  hasListeners(type) {
1285
- var _a;
1286
- return Boolean((_a = this._listeners[type]) === null || _a === void 0 ? void 0 : _a.length) || this._inlineHandlerTypes.has(type);
1350
+ return Boolean(this._listeners[type]?.length) || this._inlineHandlerTypes.has(type);
1287
1351
  }
1288
1352
  }
1289
1353
  customElements.define('pc-entity', EntityElement);
@@ -1475,11 +1539,13 @@ const extToType = new Map([
1475
1539
  ['frag', 'shader'],
1476
1540
  ['glb', 'container'],
1477
1541
  ['glsl', 'shader'],
1542
+ ['gltf', 'container'],
1478
1543
  ['hdr', 'texture'],
1479
1544
  ['html', 'html'],
1480
1545
  ['jpg', 'texture'],
1481
1546
  ['js', 'script'],
1482
1547
  ['json', 'json'],
1548
+ ['ktx2', 'texture'],
1483
1549
  ['mp3', 'audio'],
1484
1550
  ['mjs', 'script'],
1485
1551
  ['ply', 'gsplat'],
@@ -1522,25 +1588,36 @@ const processBufferView = (gltfBuffer, buffers, continuation) => {
1522
1588
  * while the application is running are created and registered on insertion, and begin loading
1523
1589
  * immediately unless `lazy`. A `pc-asset` must be a direct child of `pc-app` — elements placed
1524
1590
  * elsewhere, or with an unsupported asset type, never become ready.
1591
+ *
1592
+ * Apart from `lazy`, these attributes are read once when the asset is created, so changing them
1593
+ * later has no effect.
1594
+ *
1595
+ * @attribute {string} id - The identifier used to reference the asset from other elements.
1596
+ * @attribute {string} src - The URL of the asset to load.
1597
+ * @attribute {string} type - The asset type. Inferred from the `src` file extension when omitted.
1598
+ * @attribute {string} data - Additional asset data, as a JSON object.
1599
+ * @attribute {string} atlas - For a `sprite` asset, the `id` of the texture atlas asset it uses.
1600
+ * The atlas must be declared before the sprite.
1601
+ * @attribute {string} frame-keys - For a `sprite` asset, the atlas frame keys it uses, separated
1602
+ * by spaces or commas.
1603
+ * @attribute {number} pixels-per-unit - For a `sprite` asset, the number of pixels per world unit.
1604
+ * @attribute {'simple' | 'sliced' | 'tiled'} render-mode - For a `sprite` asset, how the sprite is
1605
+ * rendered when resized.
1525
1606
  */
1526
1607
  class AssetElement extends AsyncElement {
1527
- constructor() {
1528
- super(...arguments);
1529
- this._lazy = false;
1530
- /**
1531
- * The asset that is loaded. Available once the element is ready — await
1532
- * {@link whenReady} or the element's `ready()` promise before accessing it.
1533
- */
1534
- this.asset = null;
1535
- }
1608
+ _lazy = false;
1609
+ /**
1610
+ * The asset that is loaded. Available once the element is ready — await
1611
+ * {@link whenReady} or the element's `ready()` promise before accessing it.
1612
+ */
1613
+ asset = null;
1536
1614
  async connectedCallback() {
1537
- var _a;
1538
1615
  const appElement = this.closestApp;
1539
1616
  if (!appElement)
1540
1617
  return;
1541
1618
  // Assets must be direct children of pc-app (matches the boot query ':scope > pc-asset')
1542
1619
  if (this.parentElement !== appElement) {
1543
- console.warn(`pc-asset '${(_a = this.getAttribute('id')) !== null && _a !== void 0 ? _a : this.getAttribute('src')}' must be a direct child of pc-app - asset not created`);
1620
+ console.warn(`pc-asset '${this.getAttribute('id') ?? this.getAttribute('src')}' must be a direct child of pc-app - asset not created`);
1544
1621
  return;
1545
1622
  }
1546
1623
  await appElement.ready();
@@ -1570,14 +1647,13 @@ class AssetElement extends AsyncElement {
1570
1647
  this.destroyAsset();
1571
1648
  }
1572
1649
  createAsset() {
1573
- var _a;
1574
1650
  const id = this.getAttribute('id') || '';
1575
1651
  const src = this.getAttribute('src') || '';
1576
1652
  let type = this.getAttribute('type');
1577
1653
  // If no type is specified, try to infer it from the file extension.
1578
1654
  if (!type) {
1579
1655
  const ext = src.split('.').pop();
1580
- type = (_a = extToType.get(ext || '')) !== null && _a !== void 0 ? _a : null;
1656
+ type = extToType.get(ext || '') ?? null;
1581
1657
  }
1582
1658
  if (!type) {
1583
1659
  console.warn(`Unsupported asset type: ${src}`);
@@ -1613,7 +1689,6 @@ class AssetElement extends AsyncElement {
1613
1689
  * @returns The asset data, or `undefined`.
1614
1690
  */
1615
1691
  _buildData(type) {
1616
- var _a, _b, _c, _d;
1617
1692
  let data;
1618
1693
  const dataAttr = this.getAttribute('data');
1619
1694
  if (dataAttr) {
@@ -1625,10 +1700,10 @@ class AssetElement extends AsyncElement {
1625
1700
  }
1626
1701
  }
1627
1702
  if (type === 'sprite') {
1628
- data = data !== null && data !== void 0 ? data : {};
1703
+ data = data ?? {};
1629
1704
  // Resolve the referenced texture atlas to its (numeric) asset id. The atlas must be
1630
1705
  // declared before the sprite so its asset already exists in the registry.
1631
- const atlas = (_a = this.getAttribute('atlas')) !== null && _a !== void 0 ? _a : data.textureAtlasAsset;
1706
+ const atlas = this.getAttribute('atlas') ?? data.textureAtlasAsset;
1632
1707
  if (typeof atlas === 'string') {
1633
1708
  const atlasAsset = AssetElement.get(atlas);
1634
1709
  if (atlasAsset) {
@@ -1651,17 +1726,16 @@ class AssetElement extends AsyncElement {
1651
1726
  data.renderMode = renderModes.get(parseEnum(renderMode, renderModes, 'simple', 'render-mode'));
1652
1727
  }
1653
1728
  // Apply engine defaults for any values not supplied.
1654
- data.renderMode = (_b = data.renderMode) !== null && _b !== void 0 ? _b : playcanvas.SPRITE_RENDERMODE_SIMPLE;
1655
- data.pixelsPerUnit = (_c = data.pixelsPerUnit) !== null && _c !== void 0 ? _c : 1;
1656
- data.frameKeys = (_d = data.frameKeys) !== null && _d !== void 0 ? _d : [];
1729
+ data.renderMode = data.renderMode ?? playcanvas.SPRITE_RENDERMODE_SIMPLE;
1730
+ data.pixelsPerUnit = data.pixelsPerUnit ?? 1;
1731
+ data.frameKeys = data.frameKeys ?? [];
1657
1732
  }
1658
1733
  return data;
1659
1734
  }
1660
1735
  destroyAsset() {
1661
- var _a;
1662
1736
  if (this.asset) {
1663
1737
  // Deregister first so unload() can still notify the registry
1664
- (_a = this.asset.registry) === null || _a === void 0 ? void 0 : _a.remove(this.asset);
1738
+ this.asset.registry?.remove(this.asset);
1665
1739
  this.asset.unload();
1666
1740
  this.asset = null;
1667
1741
  }
@@ -1685,7 +1759,7 @@ class AssetElement extends AsyncElement {
1685
1759
  }
1686
1760
  static get(id) {
1687
1761
  const assetElement = document.querySelector(`pc-asset[id="${id}"]`);
1688
- return assetElement === null || assetElement === void 0 ? void 0 : assetElement.asset;
1762
+ return assetElement?.asset;
1689
1763
  }
1690
1764
  static get observedAttributes() {
1691
1765
  return ['lazy'];
@@ -1704,6 +1778,10 @@ customElements.define('pc-asset', AssetElement);
1704
1778
  * @category Components
1705
1779
  */
1706
1780
  class ComponentElement extends AsyncElement {
1781
+ _componentName;
1782
+ _enabled = true;
1783
+ _component = null;
1784
+ _appElement = null;
1707
1785
  /**
1708
1786
  * Creates a new ComponentElement instance.
1709
1787
  *
@@ -1712,9 +1790,6 @@ class ComponentElement extends AsyncElement {
1712
1790
  */
1713
1791
  constructor(componentName) {
1714
1792
  super();
1715
- this._enabled = true;
1716
- this._component = null;
1717
- this._appElement = null;
1718
1793
  this._componentName = componentName;
1719
1794
  }
1720
1795
  // Method to be overridden by subclasses to provide initial component data
@@ -1737,19 +1812,17 @@ class ComponentElement extends AsyncElement {
1737
1812
  }
1738
1813
  initComponent() { }
1739
1814
  async connectedCallback() {
1740
- var _a, _b;
1741
- this._appElement = (_a = this.closestApp) !== null && _a !== void 0 ? _a : null;
1742
- await ((_b = this._appElement) === null || _b === void 0 ? void 0 : _b.ready());
1815
+ this._appElement = this.closestApp ?? null;
1816
+ await this._appElement?.ready();
1743
1817
  await this.addComponent();
1744
1818
  this.initComponent();
1745
1819
  this._onReady();
1746
1820
  }
1747
1821
  disconnectedCallback() {
1748
- var _a, _b;
1749
1822
  // Remove the component when the element is disconnected. Skip this when the owning
1750
1823
  // application has already been destroyed — removing a <pc-app> disconnects it before
1751
1824
  // its children, taking the component systems with it.
1752
- if (((_a = this._appElement) === null || _a === void 0 ? void 0 : _a.app) && ((_b = this._component) === null || _b === void 0 ? void 0 : _b.entity)) {
1825
+ if (this._appElement?.app && this._component?.entity) {
1753
1826
  this._component.entity.removeComponent(this._componentName);
1754
1827
  }
1755
1828
  this._component = null;
@@ -1828,26 +1901,25 @@ const transitionModes = new Map([
1828
1901
  * @category Components
1829
1902
  */
1830
1903
  class ButtonComponentElement extends ComponentElement {
1904
+ _active = true;
1905
+ _image = '';
1906
+ _hitPadding = new playcanvas.Vec4(0, 0, 0, 0);
1907
+ _transitionMode = 'tint';
1908
+ _hoverTint = new playcanvas.Color(1, 1, 1, 1);
1909
+ _pressedTint = new playcanvas.Color(1, 1, 1, 1);
1910
+ _inactiveTint = new playcanvas.Color(1, 1, 1, 1);
1911
+ _fadeDuration = 0;
1912
+ _hoverSpriteAsset = '';
1913
+ _hoverSpriteFrame = 0;
1914
+ _pressedSpriteAsset = '';
1915
+ _pressedSpriteFrame = 0;
1916
+ _inactiveSpriteAsset = '';
1917
+ _inactiveSpriteFrame = 0;
1831
1918
  /** @ignore */
1832
1919
  constructor() {
1833
1920
  super('button');
1834
- this._active = true;
1835
- this._image = '';
1836
- this._hitPadding = new playcanvas.Vec4(0, 0, 0, 0);
1837
- this._transitionMode = 'tint';
1838
- this._hoverTint = new playcanvas.Color(1, 1, 1, 1);
1839
- this._pressedTint = new playcanvas.Color(1, 1, 1, 1);
1840
- this._inactiveTint = new playcanvas.Color(1, 1, 1, 1);
1841
- this._fadeDuration = 0;
1842
- this._hoverSpriteAsset = '';
1843
- this._hoverSpriteFrame = 0;
1844
- this._pressedSpriteAsset = '';
1845
- this._pressedSpriteFrame = 0;
1846
- this._inactiveSpriteAsset = '';
1847
- this._inactiveSpriteFrame = 0;
1848
1921
  }
1849
1922
  getInitialComponentData() {
1850
- var _a;
1851
1923
  const data = {
1852
1924
  active: this._active,
1853
1925
  hitPadding: this._hitPadding,
@@ -1862,7 +1934,7 @@ class ButtonComponentElement extends ComponentElement {
1862
1934
  };
1863
1935
  // The image entity defaults to the button's own entity (which carries the image element)
1864
1936
  // when no explicit reference is provided.
1865
- const imageEntity = this._image ? getEntity(this._image) : (_a = this.closestEntity) === null || _a === void 0 ? void 0 : _a.entity;
1937
+ const imageEntity = this._image ? getEntity(this._image) : this.closestEntity?.entity;
1866
1938
  if (imageEntity) {
1867
1939
  data.imageEntity = imageEntity;
1868
1940
  }
@@ -1946,10 +2018,9 @@ class ButtonComponentElement extends ComponentElement {
1946
2018
  * @param value - The transition mode.
1947
2019
  */
1948
2020
  set transitionMode(value) {
1949
- var _a;
1950
2021
  this._transitionMode = value;
1951
2022
  if (this.component) {
1952
- this.component.transitionMode = (_a = transitionModes.get(value)) !== null && _a !== void 0 ? _a : playcanvas.BUTTON_TRANSITION_MODE_TINT;
2023
+ this.component.transitionMode = transitionModes.get(value) ?? playcanvas.BUTTON_TRANSITION_MODE_TINT;
1953
2024
  }
1954
2025
  }
1955
2026
  /**
@@ -2164,7 +2235,7 @@ class ButtonComponentElement extends ComponentElement {
2164
2235
  this.active = parseBool(newValue, true);
2165
2236
  break;
2166
2237
  case 'image':
2167
- this.image = newValue;
2238
+ this.image = newValue ?? '';
2168
2239
  break;
2169
2240
  case 'hit-padding':
2170
2241
  this.hitPadding = parseVec4(newValue, playcanvas.Vec4.ZERO, name);
@@ -2185,19 +2256,19 @@ class ButtonComponentElement extends ComponentElement {
2185
2256
  this.fadeDuration = parseNumber(newValue, 0, name);
2186
2257
  break;
2187
2258
  case 'hover-sprite-asset':
2188
- this.hoverSpriteAsset = newValue;
2259
+ this.hoverSpriteAsset = newValue ?? '';
2189
2260
  break;
2190
2261
  case 'hover-sprite-frame':
2191
2262
  this.hoverSpriteFrame = parseNumber(newValue, 0, name);
2192
2263
  break;
2193
2264
  case 'pressed-sprite-asset':
2194
- this.pressedSpriteAsset = newValue;
2265
+ this.pressedSpriteAsset = newValue ?? '';
2195
2266
  break;
2196
2267
  case 'pressed-sprite-frame':
2197
2268
  this.pressedSpriteFrame = parseNumber(newValue, 0, name);
2198
2269
  break;
2199
2270
  case 'inactive-sprite-asset':
2200
- this.inactiveSpriteAsset = newValue;
2271
+ this.inactiveSpriteAsset = newValue ?? '';
2201
2272
  break;
2202
2273
  case 'inactive-sprite-frame':
2203
2274
  this.inactiveSpriteFrame = parseNumber(newValue, 0, name);
@@ -2225,27 +2296,27 @@ const tonemaps = new Map([
2225
2296
  * @category Components
2226
2297
  */
2227
2298
  class CameraComponentElement extends ComponentElement {
2299
+ _clearColor = new playcanvas.Color(0.75, 0.75, 0.75, 1);
2300
+ _clearColorBuffer = true;
2301
+ _clearDepthBuffer = true;
2302
+ _clearStencilBuffer = false;
2303
+ _cullFaces = true;
2304
+ _farClip = 1000;
2305
+ _flipFaces = false;
2306
+ _fov = 45;
2307
+ _frustumCulling = true;
2308
+ _gamma = 'srgb';
2309
+ _horizontalFov = false;
2310
+ _nearClip = 0.1;
2311
+ _orthographic = false;
2312
+ _orthoHeight = 10;
2313
+ _priority = 0;
2314
+ _rect = new playcanvas.Vec4(0, 0, 1, 1);
2315
+ _scissorRect = new playcanvas.Vec4(0, 0, 1, 1);
2316
+ _tonemap = 'none';
2228
2317
  /** @ignore */
2229
2318
  constructor() {
2230
2319
  super('camera');
2231
- this._clearColor = new playcanvas.Color(0.75, 0.75, 0.75, 1);
2232
- this._clearColorBuffer = true;
2233
- this._clearDepthBuffer = true;
2234
- this._clearStencilBuffer = false;
2235
- this._cullFaces = true;
2236
- this._farClip = 1000;
2237
- this._flipFaces = false;
2238
- this._fov = 45;
2239
- this._frustumCulling = true;
2240
- this._gamma = 'srgb';
2241
- this._horizontalFov = false;
2242
- this._nearClip = 0.1;
2243
- this._orthographic = false;
2244
- this._orthoHeight = 10;
2245
- this._priority = 0;
2246
- this._rect = new playcanvas.Vec4(0, 0, 1, 1);
2247
- this._scissorRect = new playcanvas.Vec4(0, 0, 1, 1);
2248
- this._tonemap = 'none';
2249
2320
  }
2250
2321
  getInitialComponentData() {
2251
2322
  return {
@@ -2270,8 +2341,7 @@ class CameraComponentElement extends ComponentElement {
2270
2341
  };
2271
2342
  }
2272
2343
  get xrAvailable() {
2273
- var _a;
2274
- const xrManager = (_a = this.component) === null || _a === void 0 ? void 0 : _a.system.app.xr;
2344
+ const xrManager = this.component?.system.app.xr;
2275
2345
  return xrManager && xrManager.supported && xrManager.isAvailable(playcanvas.XRTYPE_VR);
2276
2346
  }
2277
2347
  /**
@@ -2599,10 +2669,9 @@ class CameraComponentElement extends ComponentElement {
2599
2669
  * @param value - The tone mapping.
2600
2670
  */
2601
2671
  set tonemap(value) {
2602
- var _a;
2603
2672
  this._tonemap = value;
2604
2673
  if (this.component) {
2605
- this.component.toneMapping = (_a = tonemaps.get(value)) !== null && _a !== void 0 ? _a : playcanvas.TONEMAP_NONE;
2674
+ this.component.toneMapping = tonemaps.get(value) ?? playcanvas.TONEMAP_NONE;
2606
2675
  }
2607
2676
  }
2608
2677
  /**
@@ -2706,17 +2775,17 @@ customElements.define('pc-camera', CameraComponentElement);
2706
2775
  * @category Components
2707
2776
  */
2708
2777
  class CollisionComponentElement extends ComponentElement {
2778
+ _angularOffset = new playcanvas.Quat();
2779
+ _axis = 1;
2780
+ _convexHull = false;
2781
+ _halfExtents = new playcanvas.Vec3(0.5, 0.5, 0.5);
2782
+ _height = 2;
2783
+ _linearOffset = new playcanvas.Vec3();
2784
+ _radius = 0.5;
2785
+ _type = 'box';
2709
2786
  /** @ignore */
2710
2787
  constructor() {
2711
2788
  super('collision');
2712
- this._angularOffset = new playcanvas.Quat();
2713
- this._axis = 1;
2714
- this._convexHull = false;
2715
- this._halfExtents = new playcanvas.Vec3(0.5, 0.5, 0.5);
2716
- this._height = 2;
2717
- this._linearOffset = new playcanvas.Vec3();
2718
- this._radius = 0.5;
2719
- this._type = 'box';
2720
2789
  }
2721
2790
  getInitialComponentData() {
2722
2791
  return {
@@ -2853,52 +2922,51 @@ customElements.define('pc-collision', CollisionComponentElement);
2853
2922
  * @category Components
2854
2923
  */
2855
2924
  class ElementComponentElement extends ComponentElement {
2925
+ _anchor = new playcanvas.Vec4(0.5, 0.5, 0.5, 0.5);
2926
+ _autoWidth = true;
2927
+ _autoHeight = true;
2928
+ _autoFitWidth = false;
2929
+ _autoFitHeight = false;
2930
+ _color = new playcanvas.Color(1, 1, 1, 1);
2931
+ _enableMarkup = false;
2932
+ _fontAsset = '';
2933
+ _fontSize = 32;
2934
+ _maxFontSize = 32;
2935
+ _minFontSize = 8;
2936
+ _height = 0;
2937
+ _lineHeight = 32;
2938
+ _margin = null;
2939
+ _mask = false;
2940
+ _opacity = 1;
2941
+ _pivot = new playcanvas.Vec2(0.5, 0.5);
2942
+ _pixelsPerUnit = null;
2943
+ _spriteAsset = '';
2944
+ _spriteFrame = 0;
2945
+ _text = '';
2946
+ _textureAsset = '';
2947
+ _type = 'group';
2948
+ _useInput = false;
2949
+ _width = 0;
2950
+ _wrapLines = false;
2856
2951
  /** @ignore */
2857
2952
  constructor() {
2858
2953
  super('element');
2859
- this._anchor = new playcanvas.Vec4(0.5, 0.5, 0.5, 0.5);
2860
- this._autoWidth = true;
2861
- this._autoHeight = true;
2862
- this._autoFitWidth = false;
2863
- this._autoFitHeight = false;
2864
- this._color = new playcanvas.Color(1, 1, 1, 1);
2865
- this._enableMarkup = false;
2866
- this._fontAsset = '';
2867
- this._fontSize = 32;
2868
- this._maxFontSize = 32;
2869
- this._minFontSize = 8;
2870
- this._height = 0;
2871
- this._lineHeight = 32;
2872
- this._margin = null;
2873
- this._mask = false;
2874
- this._opacity = 1;
2875
- this._pivot = new playcanvas.Vec2(0.5, 0.5);
2876
- this._pixelsPerUnit = null;
2877
- this._spriteAsset = '';
2878
- this._spriteFrame = 0;
2879
- this._text = '';
2880
- this._textureAsset = '';
2881
- this._type = 'group';
2882
- this._useInput = false;
2883
- this._width = 0;
2884
- this._wrapLines = false;
2885
2954
  }
2886
2955
  initComponent() {
2887
- var _a, _b;
2888
2956
  const component = this.component;
2889
2957
  if (!component) {
2890
2958
  return;
2891
2959
  }
2892
2960
  // Text elements render through their own material; enable fog on it so 3D text respects
2893
2961
  // scene fog. Image/group elements have no text material, so guard the access.
2894
- if ((_a = component._text) === null || _a === void 0 ? void 0 : _a._material) {
2962
+ if (component._text?._material) {
2895
2963
  component._text._material.useFog = true;
2896
2964
  }
2897
2965
  // The engine establishes element masking in ElementComponent._onInsert, which fires when an
2898
2966
  // entity is inserted into the hierarchy. Web-components inserts the entity first and adds
2899
2967
  // the element component afterwards, so that pass is missed. Re-dirty the mask state here so
2900
2968
  // masks (e.g. a scroll view viewport) correctly clip this element and any added at runtime.
2901
- (_b = component._dirtifyMask) === null || _b === void 0 ? void 0 : _b.call(component);
2969
+ component._dirtifyMask?.();
2902
2970
  }
2903
2971
  getInitialComponentData() {
2904
2972
  const data = {
@@ -3461,7 +3529,7 @@ class ElementComponentElement extends ComponentElement {
3461
3529
  this.enableMarkup = parseBool(newValue, false);
3462
3530
  break;
3463
3531
  case 'font-asset':
3464
- this.fontAsset = newValue;
3532
+ this.fontAsset = newValue ?? '';
3465
3533
  break;
3466
3534
  case 'font-size':
3467
3535
  this.fontSize = parseNumber(newValue, 32, name);
@@ -3494,16 +3562,16 @@ class ElementComponentElement extends ComponentElement {
3494
3562
  this.pixelsPerUnit = parseNumber(newValue, null, name);
3495
3563
  break;
3496
3564
  case 'sprite-asset':
3497
- this.spriteAsset = newValue;
3565
+ this.spriteAsset = newValue ?? '';
3498
3566
  break;
3499
3567
  case 'sprite-frame':
3500
3568
  this.spriteFrame = parseNumber(newValue, 0, name);
3501
3569
  break;
3502
3570
  case 'text':
3503
- this.text = newValue;
3571
+ this.text = newValue ?? '';
3504
3572
  break;
3505
3573
  case 'texture-asset':
3506
- this.textureAsset = newValue;
3574
+ this.textureAsset = newValue ?? '';
3507
3575
  break;
3508
3576
  case 'type':
3509
3577
  this.type = parseEnum(newValue, ['group', 'image', 'text'], 'group', name);
@@ -3531,16 +3599,16 @@ customElements.define('pc-element', ElementComponentElement);
3531
3599
  * @category Components
3532
3600
  */
3533
3601
  class LayoutChildComponentElement extends ComponentElement {
3602
+ _minWidth = 0;
3603
+ _minHeight = 0;
3604
+ _maxWidth = null;
3605
+ _maxHeight = null;
3606
+ _fitWidthProportion = 0;
3607
+ _fitHeightProportion = 0;
3608
+ _excludeFromLayout = false;
3534
3609
  /** @ignore */
3535
3610
  constructor() {
3536
3611
  super('layoutchild');
3537
- this._minWidth = 0;
3538
- this._minHeight = 0;
3539
- this._maxWidth = null;
3540
- this._maxHeight = null;
3541
- this._fitWidthProportion = 0;
3542
- this._fitHeightProportion = 0;
3543
- this._excludeFromLayout = false;
3544
3612
  }
3545
3613
  getInitialComponentData() {
3546
3614
  return {
@@ -3741,18 +3809,18 @@ const fittings = new Map([
3741
3809
  * @category Components
3742
3810
  */
3743
3811
  class LayoutGroupComponentElement extends ComponentElement {
3812
+ _orientation = 'horizontal';
3813
+ _reverseX = false;
3814
+ _reverseY = false;
3815
+ _alignment = new playcanvas.Vec2(0, 1);
3816
+ _padding = new playcanvas.Vec4(0, 0, 0, 0);
3817
+ _spacing = new playcanvas.Vec2(0, 0);
3818
+ _widthFitting = 'none';
3819
+ _heightFitting = 'none';
3820
+ _wrap = false;
3744
3821
  /** @ignore */
3745
3822
  constructor() {
3746
3823
  super('layoutgroup');
3747
- this._orientation = 'horizontal';
3748
- this._reverseX = false;
3749
- this._reverseY = false;
3750
- this._alignment = new playcanvas.Vec2(0, 1);
3751
- this._padding = new playcanvas.Vec4(0, 0, 0, 0);
3752
- this._spacing = new playcanvas.Vec2(0, 0);
3753
- this._widthFitting = 'none';
3754
- this._heightFitting = 'none';
3755
- this._wrap = false;
3756
3824
  }
3757
3825
  getInitialComponentData() {
3758
3826
  return {
@@ -3780,10 +3848,9 @@ class LayoutGroupComponentElement extends ComponentElement {
3780
3848
  * @param value - The orientation.
3781
3849
  */
3782
3850
  set orientation(value) {
3783
- var _a;
3784
3851
  this._orientation = value;
3785
3852
  if (this.component) {
3786
- this.component.orientation = (_a = orientations$1.get(value)) !== null && _a !== void 0 ? _a : playcanvas.ORIENTATION_HORIZONTAL;
3853
+ this.component.orientation = orientations$1.get(value) ?? playcanvas.ORIENTATION_HORIZONTAL;
3787
3854
  }
3788
3855
  }
3789
3856
  /**
@@ -3884,10 +3951,9 @@ class LayoutGroupComponentElement extends ComponentElement {
3884
3951
  * @param value - The width fitting mode.
3885
3952
  */
3886
3953
  set widthFitting(value) {
3887
- var _a;
3888
3954
  this._widthFitting = value;
3889
3955
  if (this.component) {
3890
- this.component.widthFitting = (_a = fittings.get(value)) !== null && _a !== void 0 ? _a : playcanvas.FITTING_NONE;
3956
+ this.component.widthFitting = fittings.get(value) ?? playcanvas.FITTING_NONE;
3891
3957
  }
3892
3958
  }
3893
3959
  /**
@@ -3903,10 +3969,9 @@ class LayoutGroupComponentElement extends ComponentElement {
3903
3969
  * @param value - The height fitting mode.
3904
3970
  */
3905
3971
  set heightFitting(value) {
3906
- var _a;
3907
3972
  this._heightFitting = value;
3908
3973
  if (this.component) {
3909
- this.component.heightFitting = (_a = fittings.get(value)) !== null && _a !== void 0 ? _a : playcanvas.FITTING_NONE;
3974
+ this.component.heightFitting = fittings.get(value) ?? playcanvas.FITTING_NONE;
3910
3975
  }
3911
3976
  }
3912
3977
  /**
@@ -4002,28 +4067,28 @@ const shadowTypes = new Map([
4002
4067
  * @category Components
4003
4068
  */
4004
4069
  class LightComponentElement extends ComponentElement {
4070
+ _castShadows = false;
4071
+ _color = new playcanvas.Color(1, 1, 1);
4072
+ _innerConeAngle = 40;
4073
+ _intensity = 1;
4074
+ _normalOffsetBias = 0.05;
4075
+ _outerConeAngle = 45;
4076
+ _range = 10;
4077
+ _shadowBias = 0.2;
4078
+ _shadowDistance = 16;
4079
+ _shadowIntensity = 1;
4080
+ _shadowResolution = 1024;
4081
+ _shadowType = 'pcf3-32f';
4082
+ _type = 'directional';
4083
+ _vsmBias = 0.01;
4084
+ _vsmBlurSize = 11;
4085
+ _penumbraSize = 1;
4086
+ _penumbraFalloff = 1;
4087
+ _shadowSamples = 16;
4088
+ _shadowBlockerSamples = 16;
4005
4089
  /** @ignore */
4006
4090
  constructor() {
4007
4091
  super('light');
4008
- this._castShadows = false;
4009
- this._color = new playcanvas.Color(1, 1, 1);
4010
- this._innerConeAngle = 40;
4011
- this._intensity = 1;
4012
- this._normalOffsetBias = 0.05;
4013
- this._outerConeAngle = 45;
4014
- this._range = 10;
4015
- this._shadowBias = 0.2;
4016
- this._shadowDistance = 16;
4017
- this._shadowIntensity = 1;
4018
- this._shadowResolution = 1024;
4019
- this._shadowType = 'pcf3-32f';
4020
- this._type = 'directional';
4021
- this._vsmBias = 0.01;
4022
- this._vsmBlurSize = 11;
4023
- this._penumbraSize = 1;
4024
- this._penumbraFalloff = 1;
4025
- this._shadowSamples = 16;
4026
- this._shadowBlockerSamples = 16;
4027
4092
  }
4028
4093
  getInitialComponentData() {
4029
4094
  return {
@@ -4257,10 +4322,9 @@ class LightComponentElement extends ComponentElement {
4257
4322
  * - `pcss-32f` - Percentage-closer soft shadow with 32-bit depth.
4258
4323
  */
4259
4324
  set shadowType(value) {
4260
- var _a;
4261
4325
  this._shadowType = value;
4262
4326
  if (this.component) {
4263
- this.component.shadowType = (_a = shadowTypes.get(value)) !== null && _a !== void 0 ? _a : playcanvas.SHADOW_PCF3_32F;
4327
+ this.component.shadowType = shadowTypes.get(value) ?? playcanvas.SHADOW_PCF3_32F;
4264
4328
  }
4265
4329
  }
4266
4330
  /**
@@ -4488,20 +4552,19 @@ customElements.define('pc-light', LightComponentElement);
4488
4552
  * @category Components
4489
4553
  */
4490
4554
  class ParticleSystemComponentElement extends ComponentElement {
4555
+ _asset = '';
4491
4556
  /** @ignore */
4492
4557
  constructor() {
4493
4558
  super('particlesystem');
4494
- this._asset = '';
4495
4559
  }
4496
4560
  getInitialComponentData() {
4497
- var _a;
4498
4561
  const asset = AssetElement.get(this._asset);
4499
4562
  if (!asset) {
4500
4563
  return {};
4501
4564
  }
4502
4565
  if (asset.resource.colorMapAsset) {
4503
4566
  const id = asset.resource.colorMapAsset;
4504
- const colorMapAsset = (_a = AssetElement.get(id)) === null || _a === void 0 ? void 0 : _a.id;
4567
+ const colorMapAsset = AssetElement.get(id)?.id;
4505
4568
  if (colorMapAsset) {
4506
4569
  asset.resource.colorMapAsset = colorMapAsset;
4507
4570
  }
@@ -4527,9 +4590,8 @@ class ParticleSystemComponentElement extends ComponentElement {
4527
4590
  }
4528
4591
  }
4529
4592
  async _loadAsset() {
4530
- var _a;
4531
- const appElement = await ((_a = this.closestApp) === null || _a === void 0 ? void 0 : _a.ready());
4532
- const app = appElement === null || appElement === void 0 ? void 0 : appElement.app;
4593
+ const appElement = await this.closestApp?.ready();
4594
+ const app = appElement?.app;
4533
4595
  const asset = AssetElement.get(this._asset);
4534
4596
  if (!asset) {
4535
4597
  return;
@@ -4604,13 +4666,53 @@ class ParticleSystemComponentElement extends ComponentElement {
4604
4666
  super.attributeChangedCallback(name, _oldValue, newValue);
4605
4667
  switch (name) {
4606
4668
  case 'asset':
4607
- this.asset = newValue;
4669
+ this.asset = newValue ?? '';
4608
4670
  break;
4609
4671
  }
4610
4672
  }
4611
4673
  }
4612
4674
  customElements.define('pc-particles', ParticleSystemComponentElement);
4613
4675
 
4676
+ const blendTypes = new Map([
4677
+ ['none', playcanvas.BLEND_NONE],
4678
+ ['normal', playcanvas.BLEND_NORMAL],
4679
+ ['additive', playcanvas.BLEND_ADDITIVE],
4680
+ ['additive-alpha', playcanvas.BLEND_ADDITIVEALPHA],
4681
+ ['premultiplied', playcanvas.BLEND_PREMULTIPLIED],
4682
+ ['multiplicative', playcanvas.BLEND_MULTIPLICATIVE],
4683
+ ['multiplicative-2x', playcanvas.BLEND_MULTIPLICATIVE2X],
4684
+ ['screen', playcanvas.BLEND_SCREEN],
4685
+ ['min', playcanvas.BLEND_MIN],
4686
+ ['max', playcanvas.BLEND_MAX],
4687
+ ['subtractive', playcanvas.BLEND_SUBTRACTIVE]
4688
+ ]);
4689
+ const cullModes = new Map([
4690
+ ['none', playcanvas.CULLFACE_NONE],
4691
+ ['back', playcanvas.CULLFACE_BACK],
4692
+ ['front', playcanvas.CULLFACE_FRONT],
4693
+ ['front-and-back', playcanvas.CULLFACE_FRONTANDBACK]
4694
+ ]);
4695
+ const fresnelModels = new Map([
4696
+ ['none', playcanvas.FRESNEL_NONE],
4697
+ ['schlick', playcanvas.FRESNEL_SCHLICK]
4698
+ ]);
4699
+ const occludeSpeculars = new Map([
4700
+ ['none', playcanvas.SPECOCC_NONE],
4701
+ ['ao', playcanvas.SPECOCC_AO],
4702
+ ['gloss-dependent', playcanvas.SPECOCC_GLOSSDEPENDENT]
4703
+ ]);
4704
+ const opacityDithers = ['none', 'bayer8', 'bluenoise', 'ignnoise'];
4705
+ const colorChannels = ['r', 'g', 'b', 'a', 'rgb'];
4706
+ const scalarChannels = ['r', 'g', 'b', 'a'];
4707
+ /**
4708
+ * The attributes that contradict a `roughness-*` attribute: each one carries the opposite
4709
+ * interpretation of a value the aliases also write. The `gloss-map-*` modifiers are deliberately
4710
+ * absent - they only configure the shared slot (tiling, offset, channel and so on) and carry no
4711
+ * interpretation of their own, so they are the supported way to configure a `roughness-map`.
4712
+ */
4713
+ const glossConflicts = ['gloss', 'gloss-invert', 'gloss-map'];
4714
+ /** The aliases those attributes contradict. */
4715
+ const roughnessAliases = ['roughness', 'roughness-map'];
4614
4716
  /**
4615
4717
  * The MaterialElement interface provides properties and methods for manipulating
4616
4718
  * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-material/ | `<pc-material>`} elements.
@@ -4620,20 +4722,114 @@ customElements.define('pc-particles', ParticleSystemComponentElement);
4620
4722
  * A `pc-material` must be a direct child of `pc-app` — elements placed elsewhere log a warning
4621
4723
  * and never create a material. Elements inserted while the application is already running are
4622
4724
  * created on insertion.
4725
+ *
4726
+ * The element is metal/rough by default: unlike a bare `StandardMaterial` it enables the metalness
4727
+ * workflow, which is what the `metalness-*` attributes assume and what glTF means by PBR. The
4728
+ * `roughness` and `roughness-map` attributes are aliases for `gloss` and `gloss-map` that
4729
+ * additionally invert the gloss channel; do not mix the two families on one element.
4730
+ *
4731
+ * The two aliases are documented here rather than on an accessor, because they resolve to the
4732
+ * `gloss` properties and would otherwise inherit gloss's description - which reads inverted.
4733
+ *
4734
+ * @attribute {number} roughness - The roughness of the material, from 0 (shiny) to 1 (rough). An
4735
+ * alias for `gloss` that also inverts it, so do not combine it with the `gloss` attributes.
4736
+ * @attribute {string} roughness-map - The id of the `pc-asset` to use as the roughness map. An
4737
+ * alias for `gloss-map` that also inverts the gloss channel, so do not combine it with the `gloss`
4738
+ * attributes.
4623
4739
  */
4624
4740
  class MaterialElement extends HTMLElement {
4625
- constructor() {
4626
- super(...arguments);
4627
- this._diffuse = new playcanvas.Color(1, 1, 1);
4628
- this._diffuseMap = '';
4629
- this._metalnessMap = '';
4630
- this._normalMap = '';
4631
- this._roughnessMap = '';
4632
- this.material = null;
4633
- }
4741
+ _alphaTest = 0;
4742
+ _alphaToCoverage = false;
4743
+ _aoIntensity = 1;
4744
+ _aoMap = '';
4745
+ _aoMapChannel = 'g';
4746
+ _aoMapOffset = new playcanvas.Vec2(0, 0);
4747
+ _aoMapRotation = 0;
4748
+ _aoMapTiling = new playcanvas.Vec2(1, 1);
4749
+ _aoMapUv = 0;
4750
+ _blendType = 'none';
4751
+ _bumpiness = 1;
4752
+ _cull = 'back';
4753
+ _depthBias = 0;
4754
+ _depthTest = true;
4755
+ _depthWrite = true;
4756
+ _diffuse = new playcanvas.Color(1, 1, 1);
4757
+ _diffuseMap = '';
4758
+ _diffuseMapChannel = 'rgb';
4759
+ _diffuseMapOffset = new playcanvas.Vec2(0, 0);
4760
+ _diffuseMapRotation = 0;
4761
+ _diffuseMapTiling = new playcanvas.Vec2(1, 1);
4762
+ _diffuseMapUv = 0;
4763
+ _emissive = new playcanvas.Color(0, 0, 0);
4764
+ _emissiveIntensity = 1;
4765
+ _emissiveMap = '';
4766
+ _emissiveMapChannel = 'rgb';
4767
+ _emissiveMapOffset = new playcanvas.Vec2(0, 0);
4768
+ _emissiveMapRotation = 0;
4769
+ _emissiveMapTiling = new playcanvas.Vec2(1, 1);
4770
+ _emissiveMapUv = 0;
4771
+ _enableGGXSpecular = false;
4772
+ _fresnelModel = 'schlick';
4773
+ _gloss = 0.25;
4774
+ _glossInvert = false;
4775
+ _glossMap = '';
4776
+ _glossMapChannel = 'g';
4777
+ _glossMapOffset = new playcanvas.Vec2(0, 0);
4778
+ _glossMapRotation = 0;
4779
+ _glossMapTiling = new playcanvas.Vec2(1, 1);
4780
+ _glossMapUv = 0;
4781
+ _heightMap = '';
4782
+ _heightMapChannel = 'g';
4783
+ _heightMapFactor = 1;
4784
+ _heightMapOffset = new playcanvas.Vec2(0, 0);
4785
+ _heightMapRotation = 0;
4786
+ _heightMapTiling = new playcanvas.Vec2(1, 1);
4787
+ _heightMapUv = 0;
4788
+ _metalness = 1;
4789
+ _metalnessMap = '';
4790
+ _metalnessMapChannel = 'g';
4791
+ _metalnessMapOffset = new playcanvas.Vec2(0, 0);
4792
+ _metalnessMapRotation = 0;
4793
+ _metalnessMapTiling = new playcanvas.Vec2(1, 1);
4794
+ _metalnessMapUv = 0;
4795
+ _normalMap = '';
4796
+ _normalMapOffset = new playcanvas.Vec2(0, 0);
4797
+ _normalMapRotation = 0;
4798
+ _normalMapTiling = new playcanvas.Vec2(1, 1);
4799
+ _normalMapUv = 0;
4800
+ _occludeDirect = false;
4801
+ _occludeSpecular = 'ao';
4802
+ _opacity = 1;
4803
+ _opacityDither = 'none';
4804
+ _opacityFadesSpecular = true;
4805
+ _opacityMap = '';
4806
+ _opacityMapChannel = 'a';
4807
+ _opacityMapOffset = new playcanvas.Vec2(0, 0);
4808
+ _opacityMapRotation = 0;
4809
+ _opacityMapTiling = new playcanvas.Vec2(1, 1);
4810
+ _opacityMapUv = 0;
4811
+ _slopeDepthBias = 0;
4812
+ _specular = new playcanvas.Color(0, 0, 0);
4813
+ _specularityFactor = 1;
4814
+ _twoSidedLighting = false;
4815
+ _useFog = true;
4816
+ _useLighting = true;
4817
+ // Diverges from the engine default of false - see the class docblock and createMaterial()
4818
+ _useMetalness = true;
4819
+ _useMetalnessSpecularColor = false;
4820
+ _useSkybox = true;
4821
+ _useTonemap = true;
4822
+ /**
4823
+ * Pending `load` handlers, one per texture slot. A slot's handler is torn down when the slot is
4824
+ * reassigned or the element disconnects, so a late-arriving asset can never write a texture the
4825
+ * element no longer wants.
4826
+ */
4827
+ _mapHandles = new Map();
4828
+ _updateScheduled = false;
4829
+ _glossConflictWarned = false;
4830
+ material = null;
4634
4831
  async connectedCallback() {
4635
- var _a, _b;
4636
- const appElement = (_b = (_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.closest('pc-app')) !== null && _b !== void 0 ? _b : null;
4832
+ const appElement = this.parentElement?.closest('pc-app') ?? null;
4637
4833
  // Materials must be direct children of pc-app (matches the boot query ':scope > pc-material')
4638
4834
  if (!appElement || this.parentElement !== appElement) {
4639
4835
  console.warn(`pc-material '${this.id}' must be a direct child of pc-app - material not created`);
@@ -4652,100 +4848,1995 @@ class MaterialElement extends HTMLElement {
4652
4848
  }
4653
4849
  }
4654
4850
  createMaterial() {
4655
- this.material = new playcanvas.StandardMaterial();
4656
- this.material.glossInvert = false;
4657
- this.material.useMetalness = false;
4658
- this.material.diffuse = this._diffuse;
4851
+ const material = new playcanvas.StandardMaterial();
4852
+ this.material = material;
4853
+ material.alphaTest = this._alphaTest;
4854
+ material.alphaToCoverage = this._alphaToCoverage;
4855
+ material.aoIntensity = this._aoIntensity;
4856
+ material.aoMapChannel = this._aoMapChannel;
4857
+ material.aoMapOffset = this._aoMapOffset;
4858
+ material.aoMapRotation = this._aoMapRotation;
4859
+ material.aoMapTiling = this._aoMapTiling;
4860
+ material.aoMapUv = this._aoMapUv;
4861
+ material.blendType = blendTypes.get(this._blendType) ?? playcanvas.BLEND_NONE;
4862
+ material.bumpiness = this._bumpiness;
4863
+ material.cull = cullModes.get(this._cull) ?? playcanvas.CULLFACE_BACK;
4864
+ material.depthBias = this._depthBias;
4865
+ material.depthTest = this._depthTest;
4866
+ material.depthWrite = this._depthWrite;
4867
+ material.diffuse = this._diffuse;
4868
+ material.diffuseMapChannel = this._diffuseMapChannel;
4869
+ material.diffuseMapOffset = this._diffuseMapOffset;
4870
+ material.diffuseMapRotation = this._diffuseMapRotation;
4871
+ material.diffuseMapTiling = this._diffuseMapTiling;
4872
+ material.diffuseMapUv = this._diffuseMapUv;
4873
+ material.emissive = this._emissive;
4874
+ material.emissiveIntensity = this._emissiveIntensity;
4875
+ material.emissiveMapChannel = this._emissiveMapChannel;
4876
+ material.emissiveMapOffset = this._emissiveMapOffset;
4877
+ material.emissiveMapRotation = this._emissiveMapRotation;
4878
+ material.emissiveMapTiling = this._emissiveMapTiling;
4879
+ material.emissiveMapUv = this._emissiveMapUv;
4880
+ material.enableGGXSpecular = this._enableGGXSpecular;
4881
+ material.fresnelModel = fresnelModels.get(this._fresnelModel) ?? playcanvas.FRESNEL_SCHLICK;
4882
+ material.gloss = this._gloss;
4883
+ material.glossInvert = this._glossInvert;
4884
+ material.glossMapChannel = this._glossMapChannel;
4885
+ material.glossMapOffset = this._glossMapOffset;
4886
+ material.glossMapRotation = this._glossMapRotation;
4887
+ material.glossMapTiling = this._glossMapTiling;
4888
+ material.glossMapUv = this._glossMapUv;
4889
+ material.heightMapChannel = this._heightMapChannel;
4890
+ material.heightMapFactor = this._heightMapFactor;
4891
+ material.heightMapOffset = this._heightMapOffset;
4892
+ material.heightMapRotation = this._heightMapRotation;
4893
+ material.heightMapTiling = this._heightMapTiling;
4894
+ material.heightMapUv = this._heightMapUv;
4895
+ material.metalness = this._metalness;
4896
+ material.metalnessMapChannel = this._metalnessMapChannel;
4897
+ material.metalnessMapOffset = this._metalnessMapOffset;
4898
+ material.metalnessMapRotation = this._metalnessMapRotation;
4899
+ material.metalnessMapTiling = this._metalnessMapTiling;
4900
+ material.metalnessMapUv = this._metalnessMapUv;
4901
+ material.normalMapOffset = this._normalMapOffset;
4902
+ material.normalMapRotation = this._normalMapRotation;
4903
+ material.normalMapTiling = this._normalMapTiling;
4904
+ material.normalMapUv = this._normalMapUv;
4905
+ // @ts-ignore the engine's generated .d.ts types occludeDirect as a number, but its own
4906
+ // JSDoc documents it as a boolean and its runtime default is `false`
4907
+ material.occludeDirect = this._occludeDirect;
4908
+ material.occludeSpecular = occludeSpeculars.get(this._occludeSpecular) ?? playcanvas.SPECOCC_AO;
4909
+ material.opacity = this._opacity;
4910
+ material.opacityDither = this._opacityDither;
4911
+ material.opacityFadesSpecular = this._opacityFadesSpecular;
4912
+ material.opacityMapChannel = this._opacityMapChannel;
4913
+ material.opacityMapOffset = this._opacityMapOffset;
4914
+ material.opacityMapRotation = this._opacityMapRotation;
4915
+ material.opacityMapTiling = this._opacityMapTiling;
4916
+ material.opacityMapUv = this._opacityMapUv;
4917
+ material.slopeDepthBias = this._slopeDepthBias;
4918
+ material.specular = this._specular;
4919
+ material.specularityFactor = this._specularityFactor;
4920
+ material.twoSidedLighting = this._twoSidedLighting;
4921
+ material.useFog = this._useFog;
4922
+ material.useLighting = this._useLighting;
4923
+ // The engine defaults to the older specular/gloss workflow, in which metalnessMap is never
4924
+ // sampled at all - useMetalness drives the LIT_METALNESS define. This element defaults the
4925
+ // other way, so that `metalness-map` does what its name says.
4926
+ material.useMetalness = this._useMetalness;
4927
+ material.useMetalnessSpecularColor = this._useMetalnessSpecularColor;
4928
+ material.useSkybox = this._useSkybox;
4929
+ material.useTonemap = this._useTonemap;
4930
+ // Texture slots resolve a pc-asset id, which may not have loaded yet
4931
+ this.aoMap = this._aoMap;
4659
4932
  this.diffuseMap = this._diffuseMap;
4933
+ this.emissiveMap = this._emissiveMap;
4934
+ this.glossMap = this._glossMap;
4935
+ this.heightMap = this._heightMap;
4660
4936
  this.metalnessMap = this._metalnessMap;
4661
4937
  this.normalMap = this._normalMap;
4662
- this.roughnessMap = this._roughnessMap;
4663
- this.material.update();
4938
+ this.opacityMap = this._opacityMap;
4939
+ material.update();
4664
4940
  }
4665
4941
  disconnectedCallback() {
4942
+ for (const handle of this._mapHandles.values()) {
4943
+ handle.off();
4944
+ }
4945
+ this._mapHandles.clear();
4666
4946
  if (this.material) {
4667
4947
  this.material.destroy();
4668
4948
  this.material = null;
4669
4949
  }
4670
4950
  }
4671
- setMap(map, property) {
4672
- if (this.material) {
4673
- const asset = AssetElement.get(map);
4674
- if (asset) {
4675
- if (asset.loaded) {
4676
- this.material[property] = asset.resource;
4677
- this.material[property].anisotropy = 4;
4678
- }
4679
- else {
4680
- asset.once('load', () => {
4681
- this.material[property] = asset.resource;
4682
- this.material[property].anisotropy = 4;
4683
- this.material.update();
4684
- });
4685
- }
4686
- }
4951
+ /**
4952
+ * Coalesces `material.update()` across a burst of attribute or property writes, so that setting
4953
+ * a dozen attributes in one parse costs one update rather than a dozen.
4954
+ */
4955
+ _scheduleUpdate() {
4956
+ if (this._updateScheduled)
4957
+ return;
4958
+ this._updateScheduled = true;
4959
+ queueMicrotask(() => {
4960
+ this._updateScheduled = false;
4961
+ this.material?.update();
4962
+ });
4963
+ }
4964
+ /**
4965
+ * Warns when a `roughness-*` attribute is combined with one that carries the opposite
4966
+ * interpretation of the same value. They write the same engine properties but disagree about
4967
+ * whether the channel is inverted, so the result would depend on attribute order rather than
4968
+ * on intent.
4969
+ *
4970
+ * Called from both families rather than only from the roughness branches, because the two
4971
+ * orderings are equally wrong and only one of them would otherwise be caught. The conflict is
4972
+ * a property of the element rather than of any one write - and an upgrading element already
4973
+ * has all of its attributes, so every branch would otherwise report the same clash - so the
4974
+ * warning latches and reports once per episode, clearing when the clash is resolved.
4975
+ */
4976
+ _warnGlossConflict() {
4977
+ const quote = (names) => `'${names.join('\', \'')}'`;
4978
+ const roughness = roughnessAliases.filter(name => this.hasAttribute(name));
4979
+ const gloss = glossConflicts.filter(name => this.hasAttribute(name));
4980
+ if (roughness.length === 0 || gloss.length === 0) {
4981
+ this._glossConflictWarned = false;
4982
+ return;
4687
4983
  }
4984
+ if (this._glossConflictWarned)
4985
+ return;
4986
+ this._glossConflictWarned = true;
4987
+ console.warn(`pc-material '${this.id}' sets both ${quote(roughness)} and ${quote(gloss)} - ` +
4988
+ 'the roughness-* attributes invert gloss, so the two families contradict each other. Use one or the other.');
4688
4989
  }
4689
- set diffuse(value) {
4690
- this._diffuse = value;
4691
- if (this.material) {
4692
- this.material.diffuse = value;
4990
+ /**
4991
+ * Points a texture slot at the resource of a `pc-asset`, waiting for the asset to load when it
4992
+ * has not already. An empty id clears the slot.
4993
+ *
4994
+ * @param id - The id of the `pc-asset`, or an empty string to clear the slot.
4995
+ * @param slot - The material property to write.
4996
+ */
4997
+ setMap(id, slot) {
4998
+ // Drop any load still pending for this slot - its texture is no longer the one we want
4999
+ this._mapHandles.get(slot)?.off();
5000
+ this._mapHandles.delete(slot);
5001
+ if (!this.material)
5002
+ return;
5003
+ if (!id) {
5004
+ this.material[slot] = null;
5005
+ this._scheduleUpdate();
5006
+ return;
4693
5007
  }
5008
+ const asset = AssetElement.get(id);
5009
+ if (!asset)
5010
+ return;
5011
+ if (asset.loaded) {
5012
+ this._applyMap(slot, asset.resource);
5013
+ return;
5014
+ }
5015
+ this._mapHandles.set(slot, asset.once('load', () => {
5016
+ this._mapHandles.delete(slot);
5017
+ this._applyMap(slot, asset.resource);
5018
+ }));
4694
5019
  }
4695
- get diffuse() {
4696
- return this._diffuse;
5020
+ /**
5021
+ * @param slot - The material property to write.
5022
+ * @param texture - The loaded texture.
5023
+ */
5024
+ _applyMap(slot, texture) {
5025
+ if (!this.material)
5026
+ return;
5027
+ this.material[slot] = texture;
5028
+ texture.anisotropy = 4;
5029
+ this._scheduleUpdate();
4697
5030
  }
4698
- set diffuseMap(value) {
4699
- this._diffuseMap = value;
4700
- this.setMap(value, 'diffuseMap');
5031
+ /**
5032
+ * Sets the alpha test reference value. Fragments with an opacity below this value are discarded.
5033
+ * @param value - The alpha test reference value.
5034
+ */
5035
+ set alphaTest(value) {
5036
+ this._alphaTest = value;
5037
+ if (this.material) {
5038
+ this.material.alphaTest = value;
5039
+ this._scheduleUpdate();
5040
+ }
4701
5041
  }
4702
- get diffuseMap() {
4703
- return this._diffuseMap;
5042
+ /**
5043
+ * Gets the alpha test reference value.
5044
+ * @returns The alpha test reference value.
5045
+ */
5046
+ get alphaTest() {
5047
+ return this._alphaTest;
4704
5048
  }
4705
- set metalnessMap(value) {
4706
- this._metalnessMap = value;
4707
- this.setMap(value, 'metalnessMap');
5049
+ /**
5050
+ * Sets whether to use alpha to coverage, which resolves transparency using multisampling.
5051
+ * @param value - The alpha to coverage flag.
5052
+ */
5053
+ set alphaToCoverage(value) {
5054
+ this._alphaToCoverage = value;
5055
+ if (this.material) {
5056
+ this.material.alphaToCoverage = value;
5057
+ this._scheduleUpdate();
5058
+ }
4708
5059
  }
4709
- get metalnessMap() {
4710
- return this._metalnessMap;
5060
+ /**
5061
+ * Gets whether to use alpha to coverage.
5062
+ * @returns The alpha to coverage flag.
5063
+ */
5064
+ get alphaToCoverage() {
5065
+ return this._alphaToCoverage;
4711
5066
  }
4712
- set normalMap(value) {
4713
- this._normalMap = value;
4714
- this.setMap(value, 'normalMap');
5067
+ /**
5068
+ * Sets the strength of the ambient occlusion map, from 0 to 1.
5069
+ * @param value - The ambient occlusion intensity.
5070
+ */
5071
+ set aoIntensity(value) {
5072
+ this._aoIntensity = value;
5073
+ if (this.material) {
5074
+ this.material.aoIntensity = value;
5075
+ this._scheduleUpdate();
5076
+ }
4715
5077
  }
4716
- get normalMap() {
4717
- return this._normalMap;
5078
+ /**
5079
+ * Gets the strength of the ambient occlusion map.
5080
+ * @returns The ambient occlusion intensity.
5081
+ */
5082
+ get aoIntensity() {
5083
+ return this._aoIntensity;
4718
5084
  }
4719
- set roughnessMap(value) {
4720
- this._roughnessMap = value;
4721
- this.setMap(value, 'glossMap');
5085
+ /**
5086
+ * Sets the id of the `pc-asset` to use as the ambient occlusion map.
5087
+ * @param value - The asset id.
5088
+ */
5089
+ set aoMap(value) {
5090
+ this._aoMap = value;
5091
+ this.setMap(value, 'aoMap');
4722
5092
  }
4723
- get roughnessMap() {
4724
- return this._roughnessMap;
5093
+ /**
5094
+ * Gets the id of the `pc-asset` used as the ambient occlusion map.
5095
+ * @returns The asset id.
5096
+ */
5097
+ get aoMap() {
5098
+ return this._aoMap;
5099
+ }
5100
+ /**
5101
+ * Sets the color channel of the ambient occlusion map to sample.
5102
+ * @param value - The channel.
5103
+ */
5104
+ set aoMapChannel(value) {
5105
+ this._aoMapChannel = value;
5106
+ if (this.material) {
5107
+ this.material.aoMapChannel = value;
5108
+ this._scheduleUpdate();
5109
+ }
5110
+ }
5111
+ /**
5112
+ * Gets the color channel of the ambient occlusion map to sample.
5113
+ * @returns The channel.
5114
+ */
5115
+ get aoMapChannel() {
5116
+ return this._aoMapChannel;
5117
+ }
5118
+ /**
5119
+ * Sets the 2D offset of the ambient occlusion map.
5120
+ * @param value - The offset.
5121
+ */
5122
+ set aoMapOffset(value) {
5123
+ this._aoMapOffset = value;
5124
+ if (this.material) {
5125
+ this.material.aoMapOffset = value;
5126
+ this._scheduleUpdate();
5127
+ }
5128
+ }
5129
+ /**
5130
+ * Gets the 2D offset of the ambient occlusion map.
5131
+ * @returns The offset.
5132
+ */
5133
+ get aoMapOffset() {
5134
+ return this._aoMapOffset;
5135
+ }
5136
+ /**
5137
+ * Sets the 2D rotation of the ambient occlusion map, in degrees.
5138
+ * @param value - The rotation.
5139
+ */
5140
+ set aoMapRotation(value) {
5141
+ this._aoMapRotation = value;
5142
+ if (this.material) {
5143
+ this.material.aoMapRotation = value;
5144
+ this._scheduleUpdate();
5145
+ }
5146
+ }
5147
+ /**
5148
+ * Gets the 2D rotation of the ambient occlusion map.
5149
+ * @returns The rotation.
5150
+ */
5151
+ get aoMapRotation() {
5152
+ return this._aoMapRotation;
5153
+ }
5154
+ /**
5155
+ * Sets the 2D tiling of the ambient occlusion map.
5156
+ * @param value - The tiling.
5157
+ */
5158
+ set aoMapTiling(value) {
5159
+ this._aoMapTiling = value;
5160
+ if (this.material) {
5161
+ this.material.aoMapTiling = value;
5162
+ this._scheduleUpdate();
5163
+ }
5164
+ }
5165
+ /**
5166
+ * Gets the 2D tiling of the ambient occlusion map.
5167
+ * @returns The tiling.
5168
+ */
5169
+ get aoMapTiling() {
5170
+ return this._aoMapTiling;
5171
+ }
5172
+ /**
5173
+ * Sets the UV channel the ambient occlusion map samples.
5174
+ * @param value - The UV channel.
5175
+ */
5176
+ set aoMapUv(value) {
5177
+ this._aoMapUv = value;
5178
+ if (this.material) {
5179
+ this.material.aoMapUv = value;
5180
+ this._scheduleUpdate();
5181
+ }
5182
+ }
5183
+ /**
5184
+ * Gets the UV channel the ambient occlusion map samples.
5185
+ * @returns The UV channel.
5186
+ */
5187
+ get aoMapUv() {
5188
+ return this._aoMapUv;
5189
+ }
5190
+ /**
5191
+ * Sets how the material is blended with the scene behind it.
5192
+ * @param value - The blend type.
5193
+ */
5194
+ set blendType(value) {
5195
+ this._blendType = value;
5196
+ if (this.material) {
5197
+ this.material.blendType = blendTypes.get(value) ?? playcanvas.BLEND_NONE;
5198
+ this._scheduleUpdate();
5199
+ }
5200
+ }
5201
+ /**
5202
+ * Gets how the material is blended with the scene behind it.
5203
+ * @returns The blend type.
5204
+ */
5205
+ get blendType() {
5206
+ return this._blendType;
5207
+ }
5208
+ /**
5209
+ * Sets the strength of the normal map, where 0 is flat and 1 is the map's full effect.
5210
+ * @param value - The bumpiness.
5211
+ */
5212
+ set bumpiness(value) {
5213
+ this._bumpiness = value;
5214
+ if (this.material) {
5215
+ this.material.bumpiness = value;
5216
+ this._scheduleUpdate();
5217
+ }
5218
+ }
5219
+ /**
5220
+ * Gets the strength of the normal map.
5221
+ * @returns The bumpiness.
5222
+ */
5223
+ get bumpiness() {
5224
+ return this._bumpiness;
5225
+ }
5226
+ /**
5227
+ * Sets which faces of a mesh are culled.
5228
+ * @param value - The cull mode.
5229
+ */
5230
+ set cull(value) {
5231
+ this._cull = value;
5232
+ if (this.material) {
5233
+ this.material.cull = cullModes.get(value) ?? playcanvas.CULLFACE_BACK;
5234
+ this._scheduleUpdate();
5235
+ }
5236
+ }
5237
+ /**
5238
+ * Gets which faces of a mesh are culled.
5239
+ * @returns The cull mode.
5240
+ */
5241
+ get cull() {
5242
+ return this._cull;
5243
+ }
5244
+ /**
5245
+ * Sets the offset applied to the depth of a fragment, used to resolve z-fighting.
5246
+ * @param value - The depth bias.
5247
+ */
5248
+ set depthBias(value) {
5249
+ this._depthBias = value;
5250
+ if (this.material) {
5251
+ this.material.depthBias = value;
5252
+ this._scheduleUpdate();
5253
+ }
5254
+ }
5255
+ /**
5256
+ * Gets the offset applied to the depth of a fragment.
5257
+ * @returns The depth bias.
5258
+ */
5259
+ get depthBias() {
5260
+ return this._depthBias;
5261
+ }
5262
+ /**
5263
+ * Sets whether fragments are tested against the depth buffer.
5264
+ * @param value - The depth test flag.
5265
+ */
5266
+ set depthTest(value) {
5267
+ this._depthTest = value;
5268
+ if (this.material) {
5269
+ this.material.depthTest = value;
5270
+ this._scheduleUpdate();
5271
+ }
5272
+ }
5273
+ /**
5274
+ * Gets whether fragments are tested against the depth buffer.
5275
+ * @returns The depth test flag.
5276
+ */
5277
+ get depthTest() {
5278
+ return this._depthTest;
5279
+ }
5280
+ /**
5281
+ * Sets whether fragments write to the depth buffer.
5282
+ * @param value - The depth write flag.
5283
+ */
5284
+ set depthWrite(value) {
5285
+ this._depthWrite = value;
5286
+ if (this.material) {
5287
+ this.material.depthWrite = value;
5288
+ this._scheduleUpdate();
5289
+ }
5290
+ }
5291
+ /**
5292
+ * Gets whether fragments write to the depth buffer.
5293
+ * @returns The depth write flag.
5294
+ */
5295
+ get depthWrite() {
5296
+ return this._depthWrite;
5297
+ }
5298
+ /**
5299
+ * Sets the diffuse color of the material. With the metalness workflow this doubles as the
5300
+ * specular color where the surface is metallic.
5301
+ * @param value - The diffuse color.
5302
+ */
5303
+ set diffuse(value) {
5304
+ this._diffuse = value;
5305
+ if (this.material) {
5306
+ this.material.diffuse = value;
5307
+ this._scheduleUpdate();
5308
+ }
5309
+ }
5310
+ /**
5311
+ * Gets the diffuse color of the material.
5312
+ * @returns The diffuse color.
5313
+ */
5314
+ get diffuse() {
5315
+ return this._diffuse;
5316
+ }
5317
+ /**
5318
+ * Sets the id of the `pc-asset` to use as the diffuse map.
5319
+ * @param value - The asset id.
5320
+ */
5321
+ set diffuseMap(value) {
5322
+ this._diffuseMap = value;
5323
+ this.setMap(value, 'diffuseMap');
5324
+ }
5325
+ /**
5326
+ * Gets the id of the `pc-asset` used as the diffuse map.
5327
+ * @returns The asset id.
5328
+ */
5329
+ get diffuseMap() {
5330
+ return this._diffuseMap;
5331
+ }
5332
+ /**
5333
+ * Sets the color channels of the diffuse map to sample.
5334
+ * @param value - The channels.
5335
+ */
5336
+ set diffuseMapChannel(value) {
5337
+ this._diffuseMapChannel = value;
5338
+ if (this.material) {
5339
+ this.material.diffuseMapChannel = value;
5340
+ this._scheduleUpdate();
5341
+ }
5342
+ }
5343
+ /**
5344
+ * Gets the color channels of the diffuse map to sample.
5345
+ * @returns The channels.
5346
+ */
5347
+ get diffuseMapChannel() {
5348
+ return this._diffuseMapChannel;
5349
+ }
5350
+ /**
5351
+ * Sets the 2D offset of the diffuse map.
5352
+ * @param value - The offset.
5353
+ */
5354
+ set diffuseMapOffset(value) {
5355
+ this._diffuseMapOffset = value;
5356
+ if (this.material) {
5357
+ this.material.diffuseMapOffset = value;
5358
+ this._scheduleUpdate();
5359
+ }
5360
+ }
5361
+ /**
5362
+ * Gets the 2D offset of the diffuse map.
5363
+ * @returns The offset.
5364
+ */
5365
+ get diffuseMapOffset() {
5366
+ return this._diffuseMapOffset;
5367
+ }
5368
+ /**
5369
+ * Sets the 2D rotation of the diffuse map, in degrees.
5370
+ * @param value - The rotation.
5371
+ */
5372
+ set diffuseMapRotation(value) {
5373
+ this._diffuseMapRotation = value;
5374
+ if (this.material) {
5375
+ this.material.diffuseMapRotation = value;
5376
+ this._scheduleUpdate();
5377
+ }
5378
+ }
5379
+ /**
5380
+ * Gets the 2D rotation of the diffuse map.
5381
+ * @returns The rotation.
5382
+ */
5383
+ get diffuseMapRotation() {
5384
+ return this._diffuseMapRotation;
5385
+ }
5386
+ /**
5387
+ * Sets the 2D tiling of the diffuse map.
5388
+ * @param value - The tiling.
5389
+ */
5390
+ set diffuseMapTiling(value) {
5391
+ this._diffuseMapTiling = value;
5392
+ if (this.material) {
5393
+ this.material.diffuseMapTiling = value;
5394
+ this._scheduleUpdate();
5395
+ }
5396
+ }
5397
+ /**
5398
+ * Gets the 2D tiling of the diffuse map.
5399
+ * @returns The tiling.
5400
+ */
5401
+ get diffuseMapTiling() {
5402
+ return this._diffuseMapTiling;
5403
+ }
5404
+ /**
5405
+ * Sets the UV channel the diffuse map samples.
5406
+ * @param value - The UV channel.
5407
+ */
5408
+ set diffuseMapUv(value) {
5409
+ this._diffuseMapUv = value;
5410
+ if (this.material) {
5411
+ this.material.diffuseMapUv = value;
5412
+ this._scheduleUpdate();
5413
+ }
5414
+ }
5415
+ /**
5416
+ * Gets the UV channel the diffuse map samples.
5417
+ * @returns The UV channel.
5418
+ */
5419
+ get diffuseMapUv() {
5420
+ return this._diffuseMapUv;
5421
+ }
5422
+ /**
5423
+ * Sets the emissive color of the material, which is added to the lit result.
5424
+ * @param value - The emissive color.
5425
+ */
5426
+ set emissive(value) {
5427
+ this._emissive = value;
5428
+ if (this.material) {
5429
+ this.material.emissive = value;
5430
+ this._scheduleUpdate();
5431
+ }
5432
+ }
5433
+ /**
5434
+ * Gets the emissive color of the material.
5435
+ * @returns The emissive color.
5436
+ */
5437
+ get emissive() {
5438
+ return this._emissive;
5439
+ }
5440
+ /**
5441
+ * Sets the multiplier applied to the emissive color and map.
5442
+ * @param value - The emissive intensity.
5443
+ */
5444
+ set emissiveIntensity(value) {
5445
+ this._emissiveIntensity = value;
5446
+ if (this.material) {
5447
+ this.material.emissiveIntensity = value;
5448
+ this._scheduleUpdate();
5449
+ }
5450
+ }
5451
+ /**
5452
+ * Gets the multiplier applied to the emissive color and map.
5453
+ * @returns The emissive intensity.
5454
+ */
5455
+ get emissiveIntensity() {
5456
+ return this._emissiveIntensity;
5457
+ }
5458
+ /**
5459
+ * Sets the id of the `pc-asset` to use as the emissive map.
5460
+ * @param value - The asset id.
5461
+ */
5462
+ set emissiveMap(value) {
5463
+ this._emissiveMap = value;
5464
+ this.setMap(value, 'emissiveMap');
5465
+ }
5466
+ /**
5467
+ * Gets the id of the `pc-asset` used as the emissive map.
5468
+ * @returns The asset id.
5469
+ */
5470
+ get emissiveMap() {
5471
+ return this._emissiveMap;
5472
+ }
5473
+ /**
5474
+ * Sets the color channels of the emissive map to sample.
5475
+ * @param value - The channels.
5476
+ */
5477
+ set emissiveMapChannel(value) {
5478
+ this._emissiveMapChannel = value;
5479
+ if (this.material) {
5480
+ this.material.emissiveMapChannel = value;
5481
+ this._scheduleUpdate();
5482
+ }
5483
+ }
5484
+ /**
5485
+ * Gets the color channels of the emissive map to sample.
5486
+ * @returns The channels.
5487
+ */
5488
+ get emissiveMapChannel() {
5489
+ return this._emissiveMapChannel;
5490
+ }
5491
+ /**
5492
+ * Sets the 2D offset of the emissive map.
5493
+ * @param value - The offset.
5494
+ */
5495
+ set emissiveMapOffset(value) {
5496
+ this._emissiveMapOffset = value;
5497
+ if (this.material) {
5498
+ this.material.emissiveMapOffset = value;
5499
+ this._scheduleUpdate();
5500
+ }
5501
+ }
5502
+ /**
5503
+ * Gets the 2D offset of the emissive map.
5504
+ * @returns The offset.
5505
+ */
5506
+ get emissiveMapOffset() {
5507
+ return this._emissiveMapOffset;
5508
+ }
5509
+ /**
5510
+ * Sets the 2D rotation of the emissive map, in degrees.
5511
+ * @param value - The rotation.
5512
+ */
5513
+ set emissiveMapRotation(value) {
5514
+ this._emissiveMapRotation = value;
5515
+ if (this.material) {
5516
+ this.material.emissiveMapRotation = value;
5517
+ this._scheduleUpdate();
5518
+ }
5519
+ }
5520
+ /**
5521
+ * Gets the 2D rotation of the emissive map.
5522
+ * @returns The rotation.
5523
+ */
5524
+ get emissiveMapRotation() {
5525
+ return this._emissiveMapRotation;
5526
+ }
5527
+ /**
5528
+ * Sets the 2D tiling of the emissive map.
5529
+ * @param value - The tiling.
5530
+ */
5531
+ set emissiveMapTiling(value) {
5532
+ this._emissiveMapTiling = value;
5533
+ if (this.material) {
5534
+ this.material.emissiveMapTiling = value;
5535
+ this._scheduleUpdate();
5536
+ }
5537
+ }
5538
+ /**
5539
+ * Gets the 2D tiling of the emissive map.
5540
+ * @returns The tiling.
5541
+ */
5542
+ get emissiveMapTiling() {
5543
+ return this._emissiveMapTiling;
5544
+ }
5545
+ /**
5546
+ * Sets the UV channel the emissive map samples.
5547
+ * @param value - The UV channel.
5548
+ */
5549
+ set emissiveMapUv(value) {
5550
+ this._emissiveMapUv = value;
5551
+ if (this.material) {
5552
+ this.material.emissiveMapUv = value;
5553
+ this._scheduleUpdate();
5554
+ }
5555
+ }
5556
+ /**
5557
+ * Gets the UV channel the emissive map samples.
5558
+ * @returns The UV channel.
5559
+ */
5560
+ get emissiveMapUv() {
5561
+ return this._emissiveMapUv;
5562
+ }
5563
+ /**
5564
+ * Sets whether to use the GGX specular model, which supports anisotropy.
5565
+ * @param value - The GGX specular flag.
5566
+ */
5567
+ set enableGGXSpecular(value) {
5568
+ this._enableGGXSpecular = value;
5569
+ if (this.material) {
5570
+ this.material.enableGGXSpecular = value;
5571
+ this._scheduleUpdate();
5572
+ }
5573
+ }
5574
+ /**
5575
+ * Gets whether to use the GGX specular model.
5576
+ * @returns The GGX specular flag.
5577
+ */
5578
+ get enableGGXSpecular() {
5579
+ return this._enableGGXSpecular;
5580
+ }
5581
+ /**
5582
+ * Sets the Fresnel model used for specular reflections at grazing angles.
5583
+ * @param value - The Fresnel model.
5584
+ */
5585
+ set fresnelModel(value) {
5586
+ this._fresnelModel = value;
5587
+ if (this.material) {
5588
+ this.material.fresnelModel = fresnelModels.get(value) ?? playcanvas.FRESNEL_SCHLICK;
5589
+ this._scheduleUpdate();
5590
+ }
5591
+ }
5592
+ /**
5593
+ * Gets the Fresnel model used for specular reflections at grazing angles.
5594
+ * @returns The Fresnel model.
5595
+ */
5596
+ get fresnelModel() {
5597
+ return this._fresnelModel;
5598
+ }
5599
+ /**
5600
+ * Sets the glossiness of the material, from 0 (rough) to 1 (shiny). See also `roughness`.
5601
+ * @param value - The gloss.
5602
+ */
5603
+ set gloss(value) {
5604
+ this._gloss = value;
5605
+ if (this.material) {
5606
+ this.material.gloss = value;
5607
+ this._scheduleUpdate();
5608
+ }
5609
+ }
5610
+ /**
5611
+ * Gets the glossiness of the material.
5612
+ * @returns The gloss.
5613
+ */
5614
+ get gloss() {
5615
+ return this._gloss;
5616
+ }
5617
+ /**
5618
+ * Sets whether the gloss value and map are inverted, which makes the material treat them as
5619
+ * roughness. Setting `roughness` or `roughness-map` enables this automatically.
5620
+ * @param value - The gloss invert flag.
5621
+ */
5622
+ set glossInvert(value) {
5623
+ this._glossInvert = value;
5624
+ if (this.material) {
5625
+ this.material.glossInvert = value;
5626
+ this._scheduleUpdate();
5627
+ }
5628
+ }
5629
+ /**
5630
+ * Gets whether the gloss value and map are inverted.
5631
+ * @returns The gloss invert flag.
5632
+ */
5633
+ get glossInvert() {
5634
+ return this._glossInvert;
5635
+ }
5636
+ /**
5637
+ * Sets the id of the `pc-asset` to use as the gloss map. See also `roughnessMap`.
5638
+ * @param value - The asset id.
5639
+ */
5640
+ set glossMap(value) {
5641
+ this._glossMap = value;
5642
+ this.setMap(value, 'glossMap');
5643
+ }
5644
+ /**
5645
+ * Gets the id of the `pc-asset` used as the gloss map.
5646
+ * @returns The asset id.
5647
+ */
5648
+ get glossMap() {
5649
+ return this._glossMap;
5650
+ }
5651
+ /**
5652
+ * Sets the color channel of the gloss map to sample.
5653
+ * @param value - The channel.
5654
+ */
5655
+ set glossMapChannel(value) {
5656
+ this._glossMapChannel = value;
5657
+ if (this.material) {
5658
+ this.material.glossMapChannel = value;
5659
+ this._scheduleUpdate();
5660
+ }
5661
+ }
5662
+ /**
5663
+ * Gets the color channel of the gloss map to sample.
5664
+ * @returns The channel.
5665
+ */
5666
+ get glossMapChannel() {
5667
+ return this._glossMapChannel;
5668
+ }
5669
+ /**
5670
+ * Sets the 2D offset of the gloss map.
5671
+ * @param value - The offset.
5672
+ */
5673
+ set glossMapOffset(value) {
5674
+ this._glossMapOffset = value;
5675
+ if (this.material) {
5676
+ this.material.glossMapOffset = value;
5677
+ this._scheduleUpdate();
5678
+ }
5679
+ }
5680
+ /**
5681
+ * Gets the 2D offset of the gloss map.
5682
+ * @returns The offset.
5683
+ */
5684
+ get glossMapOffset() {
5685
+ return this._glossMapOffset;
5686
+ }
5687
+ /**
5688
+ * Sets the 2D rotation of the gloss map, in degrees.
5689
+ * @param value - The rotation.
5690
+ */
5691
+ set glossMapRotation(value) {
5692
+ this._glossMapRotation = value;
5693
+ if (this.material) {
5694
+ this.material.glossMapRotation = value;
5695
+ this._scheduleUpdate();
5696
+ }
5697
+ }
5698
+ /**
5699
+ * Gets the 2D rotation of the gloss map.
5700
+ * @returns The rotation.
5701
+ */
5702
+ get glossMapRotation() {
5703
+ return this._glossMapRotation;
5704
+ }
5705
+ /**
5706
+ * Sets the 2D tiling of the gloss map.
5707
+ * @param value - The tiling.
5708
+ */
5709
+ set glossMapTiling(value) {
5710
+ this._glossMapTiling = value;
5711
+ if (this.material) {
5712
+ this.material.glossMapTiling = value;
5713
+ this._scheduleUpdate();
5714
+ }
5715
+ }
5716
+ /**
5717
+ * Gets the 2D tiling of the gloss map.
5718
+ * @returns The tiling.
5719
+ */
5720
+ get glossMapTiling() {
5721
+ return this._glossMapTiling;
5722
+ }
5723
+ /**
5724
+ * Sets the UV channel the gloss map samples.
5725
+ * @param value - The UV channel.
5726
+ */
5727
+ set glossMapUv(value) {
5728
+ this._glossMapUv = value;
5729
+ if (this.material) {
5730
+ this.material.glossMapUv = value;
5731
+ this._scheduleUpdate();
5732
+ }
5733
+ }
5734
+ /**
5735
+ * Gets the UV channel the gloss map samples.
5736
+ * @returns The UV channel.
5737
+ */
5738
+ get glossMapUv() {
5739
+ return this._glossMapUv;
5740
+ }
5741
+ /**
5742
+ * Sets the id of the `pc-asset` to use as the height map, which drives parallax mapping.
5743
+ * @param value - The asset id.
5744
+ */
5745
+ set heightMap(value) {
5746
+ this._heightMap = value;
5747
+ this.setMap(value, 'heightMap');
5748
+ }
5749
+ /**
5750
+ * Gets the id of the `pc-asset` used as the height map.
5751
+ * @returns The asset id.
5752
+ */
5753
+ get heightMap() {
5754
+ return this._heightMap;
5755
+ }
5756
+ /**
5757
+ * Sets the color channel of the height map to sample.
5758
+ * @param value - The channel.
5759
+ */
5760
+ set heightMapChannel(value) {
5761
+ this._heightMapChannel = value;
5762
+ if (this.material) {
5763
+ this.material.heightMapChannel = value;
5764
+ this._scheduleUpdate();
5765
+ }
5766
+ }
5767
+ /**
5768
+ * Gets the color channel of the height map to sample.
5769
+ * @returns The channel.
5770
+ */
5771
+ get heightMapChannel() {
5772
+ return this._heightMapChannel;
5773
+ }
5774
+ /**
5775
+ * Sets the strength of the parallax effect driven by the height map.
5776
+ * @param value - The height map factor.
5777
+ */
5778
+ set heightMapFactor(value) {
5779
+ this._heightMapFactor = value;
5780
+ if (this.material) {
5781
+ this.material.heightMapFactor = value;
5782
+ this._scheduleUpdate();
5783
+ }
5784
+ }
5785
+ /**
5786
+ * Gets the strength of the parallax effect driven by the height map.
5787
+ * @returns The height map factor.
5788
+ */
5789
+ get heightMapFactor() {
5790
+ return this._heightMapFactor;
5791
+ }
5792
+ /**
5793
+ * Sets the 2D offset of the height map.
5794
+ * @param value - The offset.
5795
+ */
5796
+ set heightMapOffset(value) {
5797
+ this._heightMapOffset = value;
5798
+ if (this.material) {
5799
+ this.material.heightMapOffset = value;
5800
+ this._scheduleUpdate();
5801
+ }
5802
+ }
5803
+ /**
5804
+ * Gets the 2D offset of the height map.
5805
+ * @returns The offset.
5806
+ */
5807
+ get heightMapOffset() {
5808
+ return this._heightMapOffset;
5809
+ }
5810
+ /**
5811
+ * Sets the 2D rotation of the height map, in degrees.
5812
+ * @param value - The rotation.
5813
+ */
5814
+ set heightMapRotation(value) {
5815
+ this._heightMapRotation = value;
5816
+ if (this.material) {
5817
+ this.material.heightMapRotation = value;
5818
+ this._scheduleUpdate();
5819
+ }
5820
+ }
5821
+ /**
5822
+ * Gets the 2D rotation of the height map.
5823
+ * @returns The rotation.
5824
+ */
5825
+ get heightMapRotation() {
5826
+ return this._heightMapRotation;
5827
+ }
5828
+ /**
5829
+ * Sets the 2D tiling of the height map.
5830
+ * @param value - The tiling.
5831
+ */
5832
+ set heightMapTiling(value) {
5833
+ this._heightMapTiling = value;
5834
+ if (this.material) {
5835
+ this.material.heightMapTiling = value;
5836
+ this._scheduleUpdate();
5837
+ }
5838
+ }
5839
+ /**
5840
+ * Gets the 2D tiling of the height map.
5841
+ * @returns The tiling.
5842
+ */
5843
+ get heightMapTiling() {
5844
+ return this._heightMapTiling;
5845
+ }
5846
+ /**
5847
+ * Sets the UV channel the height map samples.
5848
+ * @param value - The UV channel.
5849
+ */
5850
+ set heightMapUv(value) {
5851
+ this._heightMapUv = value;
5852
+ if (this.material) {
5853
+ this.material.heightMapUv = value;
5854
+ this._scheduleUpdate();
5855
+ }
5856
+ }
5857
+ /**
5858
+ * Gets the UV channel the height map samples.
5859
+ * @returns The UV channel.
5860
+ */
5861
+ get heightMapUv() {
5862
+ return this._heightMapUv;
5863
+ }
5864
+ /**
5865
+ * Sets how metallic the surface is, from 0 (dielectric) to 1 (metal).
5866
+ * @param value - The metalness.
5867
+ */
5868
+ set metalness(value) {
5869
+ this._metalness = value;
5870
+ if (this.material) {
5871
+ this.material.metalness = value;
5872
+ this._scheduleUpdate();
5873
+ }
5874
+ }
5875
+ /**
5876
+ * Gets how metallic the surface is.
5877
+ * @returns The metalness.
5878
+ */
5879
+ get metalness() {
5880
+ return this._metalness;
5881
+ }
5882
+ /**
5883
+ * Sets the id of the `pc-asset` to use as the metalness map.
5884
+ * @param value - The asset id.
5885
+ */
5886
+ set metalnessMap(value) {
5887
+ this._metalnessMap = value;
5888
+ this.setMap(value, 'metalnessMap');
5889
+ }
5890
+ /**
5891
+ * Gets the id of the `pc-asset` used as the metalness map.
5892
+ * @returns The asset id.
5893
+ */
5894
+ get metalnessMap() {
5895
+ return this._metalnessMap;
5896
+ }
5897
+ /**
5898
+ * Sets the color channel of the metalness map to sample.
5899
+ * @param value - The channel.
5900
+ */
5901
+ set metalnessMapChannel(value) {
5902
+ this._metalnessMapChannel = value;
5903
+ if (this.material) {
5904
+ this.material.metalnessMapChannel = value;
5905
+ this._scheduleUpdate();
5906
+ }
5907
+ }
5908
+ /**
5909
+ * Gets the color channel of the metalness map to sample.
5910
+ * @returns The channel.
5911
+ */
5912
+ get metalnessMapChannel() {
5913
+ return this._metalnessMapChannel;
5914
+ }
5915
+ /**
5916
+ * Sets the 2D offset of the metalness map.
5917
+ * @param value - The offset.
5918
+ */
5919
+ set metalnessMapOffset(value) {
5920
+ this._metalnessMapOffset = value;
5921
+ if (this.material) {
5922
+ this.material.metalnessMapOffset = value;
5923
+ this._scheduleUpdate();
5924
+ }
5925
+ }
5926
+ /**
5927
+ * Gets the 2D offset of the metalness map.
5928
+ * @returns The offset.
5929
+ */
5930
+ get metalnessMapOffset() {
5931
+ return this._metalnessMapOffset;
5932
+ }
5933
+ /**
5934
+ * Sets the 2D rotation of the metalness map, in degrees.
5935
+ * @param value - The rotation.
5936
+ */
5937
+ set metalnessMapRotation(value) {
5938
+ this._metalnessMapRotation = value;
5939
+ if (this.material) {
5940
+ this.material.metalnessMapRotation = value;
5941
+ this._scheduleUpdate();
5942
+ }
5943
+ }
5944
+ /**
5945
+ * Gets the 2D rotation of the metalness map.
5946
+ * @returns The rotation.
5947
+ */
5948
+ get metalnessMapRotation() {
5949
+ return this._metalnessMapRotation;
5950
+ }
5951
+ /**
5952
+ * Sets the 2D tiling of the metalness map.
5953
+ * @param value - The tiling.
5954
+ */
5955
+ set metalnessMapTiling(value) {
5956
+ this._metalnessMapTiling = value;
5957
+ if (this.material) {
5958
+ this.material.metalnessMapTiling = value;
5959
+ this._scheduleUpdate();
5960
+ }
5961
+ }
5962
+ /**
5963
+ * Gets the 2D tiling of the metalness map.
5964
+ * @returns The tiling.
5965
+ */
5966
+ get metalnessMapTiling() {
5967
+ return this._metalnessMapTiling;
5968
+ }
5969
+ /**
5970
+ * Sets the UV channel the metalness map samples.
5971
+ * @param value - The UV channel.
5972
+ */
5973
+ set metalnessMapUv(value) {
5974
+ this._metalnessMapUv = value;
5975
+ if (this.material) {
5976
+ this.material.metalnessMapUv = value;
5977
+ this._scheduleUpdate();
5978
+ }
5979
+ }
5980
+ /**
5981
+ * Gets the UV channel the metalness map samples.
5982
+ * @returns The UV channel.
5983
+ */
5984
+ get metalnessMapUv() {
5985
+ return this._metalnessMapUv;
5986
+ }
5987
+ /**
5988
+ * Sets the id of the `pc-asset` to use as the normal map.
5989
+ * @param value - The asset id.
5990
+ */
5991
+ set normalMap(value) {
5992
+ this._normalMap = value;
5993
+ this.setMap(value, 'normalMap');
5994
+ }
5995
+ /**
5996
+ * Gets the id of the `pc-asset` used as the normal map.
5997
+ * @returns The asset id.
5998
+ */
5999
+ get normalMap() {
6000
+ return this._normalMap;
6001
+ }
6002
+ /**
6003
+ * Sets the 2D offset of the normal map.
6004
+ * @param value - The offset.
6005
+ */
6006
+ set normalMapOffset(value) {
6007
+ this._normalMapOffset = value;
6008
+ if (this.material) {
6009
+ this.material.normalMapOffset = value;
6010
+ this._scheduleUpdate();
6011
+ }
6012
+ }
6013
+ /**
6014
+ * Gets the 2D offset of the normal map.
6015
+ * @returns The offset.
6016
+ */
6017
+ get normalMapOffset() {
6018
+ return this._normalMapOffset;
6019
+ }
6020
+ /**
6021
+ * Sets the 2D rotation of the normal map, in degrees.
6022
+ * @param value - The rotation.
6023
+ */
6024
+ set normalMapRotation(value) {
6025
+ this._normalMapRotation = value;
6026
+ if (this.material) {
6027
+ this.material.normalMapRotation = value;
6028
+ this._scheduleUpdate();
6029
+ }
6030
+ }
6031
+ /**
6032
+ * Gets the 2D rotation of the normal map.
6033
+ * @returns The rotation.
6034
+ */
6035
+ get normalMapRotation() {
6036
+ return this._normalMapRotation;
6037
+ }
6038
+ /**
6039
+ * Sets the 2D tiling of the normal map.
6040
+ * @param value - The tiling.
6041
+ */
6042
+ set normalMapTiling(value) {
6043
+ this._normalMapTiling = value;
6044
+ if (this.material) {
6045
+ this.material.normalMapTiling = value;
6046
+ this._scheduleUpdate();
6047
+ }
6048
+ }
6049
+ /**
6050
+ * Gets the 2D tiling of the normal map.
6051
+ * @returns The tiling.
6052
+ */
6053
+ get normalMapTiling() {
6054
+ return this._normalMapTiling;
6055
+ }
6056
+ /**
6057
+ * Sets the UV channel the normal map samples.
6058
+ * @param value - The UV channel.
6059
+ */
6060
+ set normalMapUv(value) {
6061
+ this._normalMapUv = value;
6062
+ if (this.material) {
6063
+ this.material.normalMapUv = value;
6064
+ this._scheduleUpdate();
6065
+ }
6066
+ }
6067
+ /**
6068
+ * Gets the UV channel the normal map samples.
6069
+ * @returns The UV channel.
6070
+ */
6071
+ get normalMapUv() {
6072
+ return this._normalMapUv;
6073
+ }
6074
+ /**
6075
+ * Sets whether ambient occlusion also attenuates direct lighting.
6076
+ * @param value - The occlude direct flag.
6077
+ */
6078
+ set occludeDirect(value) {
6079
+ this._occludeDirect = value;
6080
+ if (this.material) {
6081
+ // @ts-ignore see createMaterial() - the engine mistypes occludeDirect as a number
6082
+ this.material.occludeDirect = value;
6083
+ this._scheduleUpdate();
6084
+ }
6085
+ }
6086
+ /**
6087
+ * Gets whether ambient occlusion also attenuates direct lighting.
6088
+ * @returns The occlude direct flag.
6089
+ */
6090
+ get occludeDirect() {
6091
+ return this._occludeDirect;
6092
+ }
6093
+ /**
6094
+ * Sets how specular reflections are occluded.
6095
+ * @param value - The specular occlusion mode.
6096
+ */
6097
+ set occludeSpecular(value) {
6098
+ this._occludeSpecular = value;
6099
+ if (this.material) {
6100
+ this.material.occludeSpecular = occludeSpeculars.get(value) ?? playcanvas.SPECOCC_AO;
6101
+ this._scheduleUpdate();
6102
+ }
6103
+ }
6104
+ /**
6105
+ * Gets how specular reflections are occluded.
6106
+ * @returns The specular occlusion mode.
6107
+ */
6108
+ get occludeSpecular() {
6109
+ return this._occludeSpecular;
6110
+ }
6111
+ /**
6112
+ * Sets the opacity of the material, from 0 (transparent) to 1 (opaque), which requires a
6113
+ * `blend-type` other than `none` to have any visible effect.
6114
+ * @param value - The opacity.
6115
+ */
6116
+ set opacity(value) {
6117
+ this._opacity = value;
6118
+ if (this.material) {
6119
+ this.material.opacity = value;
6120
+ this._scheduleUpdate();
6121
+ }
6122
+ }
6123
+ /**
6124
+ * Gets the opacity of the material, which requires a `blend-type` other than `none` to have
6125
+ * any visible effect.
6126
+ * @returns The opacity.
6127
+ */
6128
+ get opacity() {
6129
+ return this._opacity;
6130
+ }
6131
+ /**
6132
+ * Sets the dithering used to render opacity, which approximates transparency without blending.
6133
+ * @param value - The dither mode.
6134
+ */
6135
+ set opacityDither(value) {
6136
+ this._opacityDither = value;
6137
+ if (this.material) {
6138
+ this.material.opacityDither = value;
6139
+ this._scheduleUpdate();
6140
+ }
6141
+ }
6142
+ /**
6143
+ * Gets the dithering used to render opacity.
6144
+ * @returns The dither mode.
6145
+ */
6146
+ get opacityDither() {
6147
+ return this._opacityDither;
6148
+ }
6149
+ /**
6150
+ * Sets whether specular highlights fade out as the material becomes transparent.
6151
+ * @param value - The opacity fades specular flag.
6152
+ */
6153
+ set opacityFadesSpecular(value) {
6154
+ this._opacityFadesSpecular = value;
6155
+ if (this.material) {
6156
+ this.material.opacityFadesSpecular = value;
6157
+ this._scheduleUpdate();
6158
+ }
6159
+ }
6160
+ /**
6161
+ * Gets whether specular highlights fade out as the material becomes transparent.
6162
+ * @returns The opacity fades specular flag.
6163
+ */
6164
+ get opacityFadesSpecular() {
6165
+ return this._opacityFadesSpecular;
6166
+ }
6167
+ /**
6168
+ * Sets the id of the `pc-asset` to use as the opacity map.
6169
+ * @param value - The asset id.
6170
+ */
6171
+ set opacityMap(value) {
6172
+ this._opacityMap = value;
6173
+ this.setMap(value, 'opacityMap');
6174
+ }
6175
+ /**
6176
+ * Gets the id of the `pc-asset` used as the opacity map.
6177
+ * @returns The asset id.
6178
+ */
6179
+ get opacityMap() {
6180
+ return this._opacityMap;
6181
+ }
6182
+ /**
6183
+ * Sets the color channel of the opacity map to sample.
6184
+ * @param value - The channel.
6185
+ */
6186
+ set opacityMapChannel(value) {
6187
+ this._opacityMapChannel = value;
6188
+ if (this.material) {
6189
+ this.material.opacityMapChannel = value;
6190
+ this._scheduleUpdate();
6191
+ }
6192
+ }
6193
+ /**
6194
+ * Gets the color channel of the opacity map to sample.
6195
+ * @returns The channel.
6196
+ */
6197
+ get opacityMapChannel() {
6198
+ return this._opacityMapChannel;
6199
+ }
6200
+ /**
6201
+ * Sets the 2D offset of the opacity map.
6202
+ * @param value - The offset.
6203
+ */
6204
+ set opacityMapOffset(value) {
6205
+ this._opacityMapOffset = value;
6206
+ if (this.material) {
6207
+ this.material.opacityMapOffset = value;
6208
+ this._scheduleUpdate();
6209
+ }
6210
+ }
6211
+ /**
6212
+ * Gets the 2D offset of the opacity map.
6213
+ * @returns The offset.
6214
+ */
6215
+ get opacityMapOffset() {
6216
+ return this._opacityMapOffset;
6217
+ }
6218
+ /**
6219
+ * Sets the 2D rotation of the opacity map, in degrees.
6220
+ * @param value - The rotation.
6221
+ */
6222
+ set opacityMapRotation(value) {
6223
+ this._opacityMapRotation = value;
6224
+ if (this.material) {
6225
+ this.material.opacityMapRotation = value;
6226
+ this._scheduleUpdate();
6227
+ }
6228
+ }
6229
+ /**
6230
+ * Gets the 2D rotation of the opacity map.
6231
+ * @returns The rotation.
6232
+ */
6233
+ get opacityMapRotation() {
6234
+ return this._opacityMapRotation;
6235
+ }
6236
+ /**
6237
+ * Sets the 2D tiling of the opacity map.
6238
+ * @param value - The tiling.
6239
+ */
6240
+ set opacityMapTiling(value) {
6241
+ this._opacityMapTiling = value;
6242
+ if (this.material) {
6243
+ this.material.opacityMapTiling = value;
6244
+ this._scheduleUpdate();
6245
+ }
6246
+ }
6247
+ /**
6248
+ * Gets the 2D tiling of the opacity map.
6249
+ * @returns The tiling.
6250
+ */
6251
+ get opacityMapTiling() {
6252
+ return this._opacityMapTiling;
6253
+ }
6254
+ /**
6255
+ * Sets the UV channel the opacity map samples.
6256
+ * @param value - The UV channel.
6257
+ */
6258
+ set opacityMapUv(value) {
6259
+ this._opacityMapUv = value;
6260
+ if (this.material) {
6261
+ this.material.opacityMapUv = value;
6262
+ this._scheduleUpdate();
6263
+ }
6264
+ }
6265
+ /**
6266
+ * Gets the UV channel the opacity map samples.
6267
+ * @returns The UV channel.
6268
+ */
6269
+ get opacityMapUv() {
6270
+ return this._opacityMapUv;
6271
+ }
6272
+ /**
6273
+ * Sets the roughness of the material, from 0 (shiny) to 1 (rough). This is an alias for `gloss`
6274
+ * that also inverts the gloss channel, so do not combine it with the `gloss` attributes.
6275
+ * @param value - The roughness.
6276
+ */
6277
+ set roughness(value) {
6278
+ this.gloss = value;
6279
+ this.glossInvert = true;
6280
+ }
6281
+ /**
6282
+ * Gets the roughness of the material.
6283
+ * @returns The roughness.
6284
+ */
6285
+ get roughness() {
6286
+ return this._gloss;
6287
+ }
6288
+ /**
6289
+ * Sets the id of the `pc-asset` to use as the roughness map. This is an alias for `glossMap`
6290
+ * that also inverts the gloss channel, so do not combine it with the `gloss` attributes.
6291
+ * @param value - The asset id.
6292
+ */
6293
+ set roughnessMap(value) {
6294
+ this.glossMap = value;
6295
+ this.glossInvert = true;
6296
+ }
6297
+ /**
6298
+ * Gets the id of the `pc-asset` used as the roughness map.
6299
+ * @returns The asset id.
6300
+ */
6301
+ get roughnessMap() {
6302
+ return this._glossMap;
6303
+ }
6304
+ /**
6305
+ * Sets the depth offset applied in proportion to a surface's slope, used to resolve z-fighting.
6306
+ * @param value - The slope depth bias.
6307
+ */
6308
+ set slopeDepthBias(value) {
6309
+ this._slopeDepthBias = value;
6310
+ if (this.material) {
6311
+ this.material.slopeDepthBias = value;
6312
+ this._scheduleUpdate();
6313
+ }
6314
+ }
6315
+ /**
6316
+ * Gets the depth offset applied in proportion to a surface's slope.
6317
+ * @returns The slope depth bias.
6318
+ */
6319
+ get slopeDepthBias() {
6320
+ return this._slopeDepthBias;
6321
+ }
6322
+ /**
6323
+ * Sets the specular color of the material, which applies only when the metalness workflow is
6324
+ * disabled or `use-metalness-specular-color` is enabled.
6325
+ * @param value - The specular color.
6326
+ */
6327
+ set specular(value) {
6328
+ this._specular = value;
6329
+ if (this.material) {
6330
+ this.material.specular = value;
6331
+ this._scheduleUpdate();
6332
+ }
6333
+ }
6334
+ /**
6335
+ * Gets the specular color of the material, which applies only when the metalness workflow is
6336
+ * disabled or `use-metalness-specular-color` is enabled.
6337
+ * @returns The specular color.
6338
+ */
6339
+ get specular() {
6340
+ return this._specular;
6341
+ }
6342
+ /**
6343
+ * Sets the strength of specular reflections at direct angles, from 0 to 1, which applies only
6344
+ * when `use-metalness-specular-color` is enabled.
6345
+ * @param value - The specularity factor.
6346
+ */
6347
+ set specularityFactor(value) {
6348
+ this._specularityFactor = value;
6349
+ if (this.material) {
6350
+ this.material.specularityFactor = value;
6351
+ this._scheduleUpdate();
6352
+ }
6353
+ }
6354
+ /**
6355
+ * Gets the strength of specular reflections at direct angles, which applies only when
6356
+ * `use-metalness-specular-color` is enabled.
6357
+ * @returns The specularity factor.
6358
+ */
6359
+ get specularityFactor() {
6360
+ return this._specularityFactor;
6361
+ }
6362
+ /**
6363
+ * Sets whether back faces are lit as though their normals were flipped.
6364
+ * @param value - The two sided lighting flag.
6365
+ */
6366
+ set twoSidedLighting(value) {
6367
+ this._twoSidedLighting = value;
6368
+ if (this.material) {
6369
+ this.material.twoSidedLighting = value;
6370
+ this._scheduleUpdate();
6371
+ }
6372
+ }
6373
+ /**
6374
+ * Gets whether back faces are lit as though their normals were flipped.
6375
+ * @returns The two sided lighting flag.
6376
+ */
6377
+ get twoSidedLighting() {
6378
+ return this._twoSidedLighting;
6379
+ }
6380
+ /**
6381
+ * Sets whether the material is affected by scene fog.
6382
+ * @param value - The use fog flag.
6383
+ */
6384
+ set useFog(value) {
6385
+ this._useFog = value;
6386
+ if (this.material) {
6387
+ this.material.useFog = value;
6388
+ this._scheduleUpdate();
6389
+ }
6390
+ }
6391
+ /**
6392
+ * Gets whether the material is affected by scene fog.
6393
+ * @returns The use fog flag.
6394
+ */
6395
+ get useFog() {
6396
+ return this._useFog;
6397
+ }
6398
+ /**
6399
+ * Sets whether the material is affected by scene lights. When disabled the material renders
6400
+ * unlit, using the diffuse color and map alone.
6401
+ * @param value - The use lighting flag.
6402
+ */
6403
+ set useLighting(value) {
6404
+ this._useLighting = value;
6405
+ if (this.material) {
6406
+ this.material.useLighting = value;
6407
+ this._scheduleUpdate();
6408
+ }
6409
+ }
6410
+ /**
6411
+ * Gets whether the material is affected by scene lights.
6412
+ * @returns The use lighting flag.
6413
+ */
6414
+ get useLighting() {
6415
+ return this._useLighting;
6416
+ }
6417
+ /**
6418
+ * Sets whether to use the metalness workflow rather than the older specular workflow. Unlike a
6419
+ * bare `StandardMaterial` this defaults to `true`, because the `metalness-*` attributes have no
6420
+ * effect without it.
6421
+ * @param value - The use metalness flag.
6422
+ */
6423
+ set useMetalness(value) {
6424
+ this._useMetalness = value;
6425
+ if (this.material) {
6426
+ this.material.useMetalness = value;
6427
+ this._scheduleUpdate();
6428
+ }
6429
+ }
6430
+ /**
6431
+ * Gets whether to use the metalness workflow.
6432
+ * @returns The use metalness flag.
6433
+ */
6434
+ get useMetalness() {
6435
+ return this._useMetalness;
6436
+ }
6437
+ /**
6438
+ * Sets whether the specular color tints reflections while the metalness workflow is in use.
6439
+ * @param value - The use metalness specular color flag.
6440
+ */
6441
+ set useMetalnessSpecularColor(value) {
6442
+ this._useMetalnessSpecularColor = value;
6443
+ if (this.material) {
6444
+ this.material.useMetalnessSpecularColor = value;
6445
+ this._scheduleUpdate();
6446
+ }
6447
+ }
6448
+ /**
6449
+ * Gets whether the specular color tints reflections while the metalness workflow is in use.
6450
+ * @returns The use metalness specular color flag.
6451
+ */
6452
+ get useMetalnessSpecularColor() {
6453
+ return this._useMetalnessSpecularColor;
6454
+ }
6455
+ /**
6456
+ * Sets whether the material is lit by the scene's skybox.
6457
+ * @param value - The use skybox flag.
6458
+ */
6459
+ set useSkybox(value) {
6460
+ this._useSkybox = value;
6461
+ if (this.material) {
6462
+ this.material.useSkybox = value;
6463
+ this._scheduleUpdate();
6464
+ }
6465
+ }
6466
+ /**
6467
+ * Gets whether the material is lit by the scene's skybox.
6468
+ * @returns The use skybox flag.
6469
+ */
6470
+ get useSkybox() {
6471
+ return this._useSkybox;
6472
+ }
6473
+ /**
6474
+ * Sets whether the camera's tone mapping is applied to the material.
6475
+ * @param value - The use tonemap flag.
6476
+ */
6477
+ set useTonemap(value) {
6478
+ this._useTonemap = value;
6479
+ if (this.material) {
6480
+ this.material.useTonemap = value;
6481
+ this._scheduleUpdate();
6482
+ }
6483
+ }
6484
+ /**
6485
+ * Gets whether the camera's tone mapping is applied to the material.
6486
+ * @returns The use tonemap flag.
6487
+ */
6488
+ get useTonemap() {
6489
+ return this._useTonemap;
4725
6490
  }
4726
6491
  static get(id) {
4727
6492
  const materialElement = document.querySelector(`pc-material[id="${id}"]`);
4728
- return materialElement === null || materialElement === void 0 ? void 0 : materialElement.material;
6493
+ return materialElement?.material;
4729
6494
  }
4730
6495
  static get observedAttributes() {
4731
- return ['diffuse', 'diffuse-map', 'metalness-map', 'normal-map', 'roughness-map'];
6496
+ return [
6497
+ 'alpha-test',
6498
+ 'alpha-to-coverage',
6499
+ 'ao-intensity',
6500
+ 'ao-map',
6501
+ 'ao-map-channel',
6502
+ 'ao-map-offset',
6503
+ 'ao-map-rotation',
6504
+ 'ao-map-tiling',
6505
+ 'ao-map-uv',
6506
+ 'blend-type',
6507
+ 'bumpiness',
6508
+ 'cull',
6509
+ 'depth-bias',
6510
+ 'depth-test',
6511
+ 'depth-write',
6512
+ 'diffuse',
6513
+ 'diffuse-map',
6514
+ 'diffuse-map-channel',
6515
+ 'diffuse-map-offset',
6516
+ 'diffuse-map-rotation',
6517
+ 'diffuse-map-tiling',
6518
+ 'diffuse-map-uv',
6519
+ 'emissive',
6520
+ 'emissive-intensity',
6521
+ 'emissive-map',
6522
+ 'emissive-map-channel',
6523
+ 'emissive-map-offset',
6524
+ 'emissive-map-rotation',
6525
+ 'emissive-map-tiling',
6526
+ 'emissive-map-uv',
6527
+ 'enable-ggx-specular',
6528
+ 'fresnel-model',
6529
+ 'gloss',
6530
+ 'gloss-invert',
6531
+ 'gloss-map',
6532
+ 'gloss-map-channel',
6533
+ 'gloss-map-offset',
6534
+ 'gloss-map-rotation',
6535
+ 'gloss-map-tiling',
6536
+ 'gloss-map-uv',
6537
+ 'height-map',
6538
+ 'height-map-channel',
6539
+ 'height-map-factor',
6540
+ 'height-map-offset',
6541
+ 'height-map-rotation',
6542
+ 'height-map-tiling',
6543
+ 'height-map-uv',
6544
+ 'metalness',
6545
+ 'metalness-map',
6546
+ 'metalness-map-channel',
6547
+ 'metalness-map-offset',
6548
+ 'metalness-map-rotation',
6549
+ 'metalness-map-tiling',
6550
+ 'metalness-map-uv',
6551
+ 'normal-map',
6552
+ 'normal-map-offset',
6553
+ 'normal-map-rotation',
6554
+ 'normal-map-tiling',
6555
+ 'normal-map-uv',
6556
+ 'occlude-direct',
6557
+ 'occlude-specular',
6558
+ 'opacity',
6559
+ 'opacity-dither',
6560
+ 'opacity-fades-specular',
6561
+ 'opacity-map',
6562
+ 'opacity-map-channel',
6563
+ 'opacity-map-offset',
6564
+ 'opacity-map-rotation',
6565
+ 'opacity-map-tiling',
6566
+ 'opacity-map-uv',
6567
+ 'roughness',
6568
+ 'roughness-map',
6569
+ 'slope-depth-bias',
6570
+ 'specular',
6571
+ 'specularity-factor',
6572
+ 'two-sided-lighting',
6573
+ 'use-fog',
6574
+ 'use-lighting',
6575
+ 'use-metalness',
6576
+ 'use-metalness-specular-color',
6577
+ 'use-skybox',
6578
+ 'use-tonemap'
6579
+ ];
4732
6580
  }
6581
+ // newValue is null when an attribute is removed, which several branches below rely on. The
6582
+ // other elements still declare it as `string`; widening those surfaces 21 real removal bugs of
6583
+ // the #309 shape, which is its own change rather than a signature tweak.
4733
6584
  attributeChangedCallback(name, _oldValue, newValue) {
4734
6585
  switch (name) {
6586
+ case 'alpha-test':
6587
+ this.alphaTest = parseNumber(newValue, 0, name);
6588
+ break;
6589
+ case 'alpha-to-coverage':
6590
+ this.alphaToCoverage = parseBool(newValue, false);
6591
+ break;
6592
+ case 'ao-intensity':
6593
+ this.aoIntensity = parseNumber(newValue, 1, name);
6594
+ break;
6595
+ case 'ao-map':
6596
+ this.aoMap = newValue ?? '';
6597
+ break;
6598
+ case 'ao-map-channel':
6599
+ this.aoMapChannel = parseEnum(newValue, scalarChannels, 'g', name);
6600
+ break;
6601
+ case 'ao-map-offset':
6602
+ this.aoMapOffset = parseVec2(newValue, new playcanvas.Vec2(0, 0), name);
6603
+ break;
6604
+ case 'ao-map-rotation':
6605
+ this.aoMapRotation = parseNumber(newValue, 0, name);
6606
+ break;
6607
+ case 'ao-map-tiling':
6608
+ this.aoMapTiling = parseVec2(newValue, new playcanvas.Vec2(1, 1), name);
6609
+ break;
6610
+ case 'ao-map-uv':
6611
+ this.aoMapUv = parseNumber(newValue, 0, name);
6612
+ break;
6613
+ case 'blend-type':
6614
+ this.blendType = parseEnum(newValue, blendTypes, 'none', name);
6615
+ break;
6616
+ case 'bumpiness':
6617
+ this.bumpiness = parseNumber(newValue, 1, name);
6618
+ break;
6619
+ case 'cull':
6620
+ this.cull = parseEnum(newValue, cullModes, 'back', name);
6621
+ break;
6622
+ case 'depth-bias':
6623
+ this.depthBias = parseNumber(newValue, 0, name);
6624
+ break;
6625
+ case 'depth-test':
6626
+ this.depthTest = parseBool(newValue, true);
6627
+ break;
6628
+ case 'depth-write':
6629
+ this.depthWrite = parseBool(newValue, true);
6630
+ break;
4735
6631
  case 'diffuse':
4736
- this.diffuse = parseColor(newValue, playcanvas.Color.WHITE, name);
6632
+ this.diffuse = parseColor(newValue, new playcanvas.Color(1, 1, 1), name);
4737
6633
  break;
4738
6634
  case 'diffuse-map':
4739
- this.diffuseMap = newValue;
6635
+ this.diffuseMap = newValue ?? '';
6636
+ break;
6637
+ case 'diffuse-map-channel':
6638
+ this.diffuseMapChannel = parseEnum(newValue, colorChannels, 'rgb', name);
6639
+ break;
6640
+ case 'diffuse-map-offset':
6641
+ this.diffuseMapOffset = parseVec2(newValue, new playcanvas.Vec2(0, 0), name);
6642
+ break;
6643
+ case 'diffuse-map-rotation':
6644
+ this.diffuseMapRotation = parseNumber(newValue, 0, name);
6645
+ break;
6646
+ case 'diffuse-map-tiling':
6647
+ this.diffuseMapTiling = parseVec2(newValue, new playcanvas.Vec2(1, 1), name);
6648
+ break;
6649
+ case 'diffuse-map-uv':
6650
+ this.diffuseMapUv = parseNumber(newValue, 0, name);
6651
+ break;
6652
+ case 'emissive':
6653
+ this.emissive = parseColor(newValue, new playcanvas.Color(0, 0, 0), name);
6654
+ break;
6655
+ case 'emissive-intensity':
6656
+ this.emissiveIntensity = parseNumber(newValue, 1, name);
6657
+ break;
6658
+ case 'emissive-map':
6659
+ this.emissiveMap = newValue ?? '';
6660
+ break;
6661
+ case 'emissive-map-channel':
6662
+ this.emissiveMapChannel = parseEnum(newValue, colorChannels, 'rgb', name);
6663
+ break;
6664
+ case 'emissive-map-offset':
6665
+ this.emissiveMapOffset = parseVec2(newValue, new playcanvas.Vec2(0, 0), name);
6666
+ break;
6667
+ case 'emissive-map-rotation':
6668
+ this.emissiveMapRotation = parseNumber(newValue, 0, name);
6669
+ break;
6670
+ case 'emissive-map-tiling':
6671
+ this.emissiveMapTiling = parseVec2(newValue, new playcanvas.Vec2(1, 1), name);
6672
+ break;
6673
+ case 'emissive-map-uv':
6674
+ this.emissiveMapUv = parseNumber(newValue, 0, name);
6675
+ break;
6676
+ case 'enable-ggx-specular':
6677
+ this.enableGGXSpecular = parseBool(newValue, false);
6678
+ break;
6679
+ case 'fresnel-model':
6680
+ this.fresnelModel = parseEnum(newValue, fresnelModels, 'schlick', name);
6681
+ break;
6682
+ case 'gloss':
6683
+ this.gloss = parseNumber(newValue, 0.25, name);
6684
+ this._warnGlossConflict();
6685
+ break;
6686
+ case 'gloss-invert':
6687
+ this.glossInvert = parseBool(newValue, false);
6688
+ this._warnGlossConflict();
6689
+ break;
6690
+ case 'gloss-map':
6691
+ this.glossMap = newValue ?? '';
6692
+ this._warnGlossConflict();
6693
+ break;
6694
+ case 'gloss-map-channel':
6695
+ this.glossMapChannel = parseEnum(newValue, scalarChannels, 'g', name);
6696
+ break;
6697
+ case 'gloss-map-offset':
6698
+ this.glossMapOffset = parseVec2(newValue, new playcanvas.Vec2(0, 0), name);
6699
+ break;
6700
+ case 'gloss-map-rotation':
6701
+ this.glossMapRotation = parseNumber(newValue, 0, name);
6702
+ break;
6703
+ case 'gloss-map-tiling':
6704
+ this.glossMapTiling = parseVec2(newValue, new playcanvas.Vec2(1, 1), name);
6705
+ break;
6706
+ case 'gloss-map-uv':
6707
+ this.glossMapUv = parseNumber(newValue, 0, name);
6708
+ break;
6709
+ case 'height-map':
6710
+ this.heightMap = newValue ?? '';
6711
+ break;
6712
+ case 'height-map-channel':
6713
+ this.heightMapChannel = parseEnum(newValue, scalarChannels, 'g', name);
6714
+ break;
6715
+ case 'height-map-factor':
6716
+ this.heightMapFactor = parseNumber(newValue, 1, name);
6717
+ break;
6718
+ case 'height-map-offset':
6719
+ this.heightMapOffset = parseVec2(newValue, new playcanvas.Vec2(0, 0), name);
6720
+ break;
6721
+ case 'height-map-rotation':
6722
+ this.heightMapRotation = parseNumber(newValue, 0, name);
6723
+ break;
6724
+ case 'height-map-tiling':
6725
+ this.heightMapTiling = parseVec2(newValue, new playcanvas.Vec2(1, 1), name);
6726
+ break;
6727
+ case 'height-map-uv':
6728
+ this.heightMapUv = parseNumber(newValue, 0, name);
6729
+ break;
6730
+ case 'metalness':
6731
+ this.metalness = parseNumber(newValue, 1, name);
4740
6732
  break;
4741
6733
  case 'metalness-map':
4742
- this.metalnessMap = newValue;
6734
+ this.metalnessMap = newValue ?? '';
6735
+ break;
6736
+ case 'metalness-map-channel':
6737
+ this.metalnessMapChannel = parseEnum(newValue, scalarChannels, 'g', name);
6738
+ break;
6739
+ case 'metalness-map-offset':
6740
+ this.metalnessMapOffset = parseVec2(newValue, new playcanvas.Vec2(0, 0), name);
6741
+ break;
6742
+ case 'metalness-map-rotation':
6743
+ this.metalnessMapRotation = parseNumber(newValue, 0, name);
6744
+ break;
6745
+ case 'metalness-map-tiling':
6746
+ this.metalnessMapTiling = parseVec2(newValue, new playcanvas.Vec2(1, 1), name);
6747
+ break;
6748
+ case 'metalness-map-uv':
6749
+ this.metalnessMapUv = parseNumber(newValue, 0, name);
4743
6750
  break;
4744
6751
  case 'normal-map':
4745
- this.normalMap = newValue;
6752
+ this.normalMap = newValue ?? '';
6753
+ break;
6754
+ case 'normal-map-offset':
6755
+ this.normalMapOffset = parseVec2(newValue, new playcanvas.Vec2(0, 0), name);
6756
+ break;
6757
+ case 'normal-map-rotation':
6758
+ this.normalMapRotation = parseNumber(newValue, 0, name);
6759
+ break;
6760
+ case 'normal-map-tiling':
6761
+ this.normalMapTiling = parseVec2(newValue, new playcanvas.Vec2(1, 1), name);
6762
+ break;
6763
+ case 'normal-map-uv':
6764
+ this.normalMapUv = parseNumber(newValue, 0, name);
6765
+ break;
6766
+ case 'occlude-direct':
6767
+ this.occludeDirect = parseBool(newValue, false);
6768
+ break;
6769
+ case 'occlude-specular':
6770
+ this.occludeSpecular = parseEnum(newValue, occludeSpeculars, 'ao', name);
6771
+ break;
6772
+ case 'opacity':
6773
+ this.opacity = parseNumber(newValue, 1, name);
6774
+ break;
6775
+ case 'opacity-dither':
6776
+ this.opacityDither = parseEnum(newValue, opacityDithers, 'none', name);
6777
+ break;
6778
+ case 'opacity-fades-specular':
6779
+ this.opacityFadesSpecular = parseBool(newValue, true);
6780
+ break;
6781
+ case 'opacity-map':
6782
+ this.opacityMap = newValue ?? '';
6783
+ break;
6784
+ case 'opacity-map-channel':
6785
+ this.opacityMapChannel = parseEnum(newValue, scalarChannels, 'a', name);
6786
+ break;
6787
+ case 'opacity-map-offset':
6788
+ this.opacityMapOffset = parseVec2(newValue, new playcanvas.Vec2(0, 0), name);
6789
+ break;
6790
+ case 'opacity-map-rotation':
6791
+ this.opacityMapRotation = parseNumber(newValue, 0, name);
6792
+ break;
6793
+ case 'opacity-map-tiling':
6794
+ this.opacityMapTiling = parseVec2(newValue, new playcanvas.Vec2(1, 1), name);
6795
+ break;
6796
+ case 'opacity-map-uv':
6797
+ this.opacityMapUv = parseNumber(newValue, 0, name);
6798
+ break;
6799
+ case 'roughness':
6800
+ // Aliases gloss, and inverts it so the value reads as roughness. Removing the
6801
+ // attribute restores the engine's uninverted interpretation.
6802
+ this.gloss = parseNumber(newValue, 0.25, name);
6803
+ this.glossInvert = newValue !== null;
6804
+ this._warnGlossConflict();
4746
6805
  break;
4747
6806
  case 'roughness-map':
4748
- this.roughnessMap = newValue;
6807
+ this.glossMap = newValue ?? '';
6808
+ this.glossInvert = newValue !== null;
6809
+ this._warnGlossConflict();
6810
+ break;
6811
+ case 'slope-depth-bias':
6812
+ this.slopeDepthBias = parseNumber(newValue, 0, name);
6813
+ break;
6814
+ case 'specular':
6815
+ this.specular = parseColor(newValue, new playcanvas.Color(0, 0, 0), name);
6816
+ break;
6817
+ case 'specularity-factor':
6818
+ this.specularityFactor = parseNumber(newValue, 1, name);
6819
+ break;
6820
+ case 'two-sided-lighting':
6821
+ this.twoSidedLighting = parseBool(newValue, false);
6822
+ break;
6823
+ case 'use-fog':
6824
+ this.useFog = parseBool(newValue, true);
6825
+ break;
6826
+ case 'use-lighting':
6827
+ this.useLighting = parseBool(newValue, true);
6828
+ break;
6829
+ case 'use-metalness':
6830
+ this.useMetalness = parseBool(newValue, true);
6831
+ break;
6832
+ case 'use-metalness-specular-color':
6833
+ this.useMetalnessSpecularColor = parseBool(newValue, false);
6834
+ break;
6835
+ case 'use-skybox':
6836
+ this.useSkybox = parseBool(newValue, true);
6837
+ break;
6838
+ case 'use-tonemap':
6839
+ this.useTonemap = parseBool(newValue, true);
4749
6840
  break;
4750
6841
  }
4751
6842
  }
@@ -4765,13 +6856,13 @@ customElements.define('pc-material', MaterialElement);
4765
6856
  * @category Components
4766
6857
  */
4767
6858
  class RenderComponentElement extends ComponentElement {
6859
+ _castShadows = true;
6860
+ _material = '';
6861
+ _receiveShadows = true;
6862
+ _type = 'box';
4768
6863
  /** @ignore */
4769
6864
  constructor() {
4770
6865
  super('render');
4771
- this._castShadows = true;
4772
- this._material = '';
4773
- this._receiveShadows = true;
4774
- this._type = 'box';
4775
6866
  }
4776
6867
  getInitialComponentData() {
4777
6868
  return {
@@ -4828,8 +6919,13 @@ class RenderComponentElement extends ComponentElement {
4828
6919
  */
4829
6920
  set material(value) {
4830
6921
  this._material = value;
4831
- if (this.component) {
4832
- this.component.material = MaterialElement.get(value);
6922
+ const material = MaterialElement.get(value);
6923
+ // Guarded like every other reference attribute in the library. Assigning an unresolved
6924
+ // lookup used to write `undefined` straight through to every mesh instance, and the
6925
+ // engine's MeshInstance setter takes that literally - it clears the material and skips
6926
+ // the ref/transparency/key bookkeeping, leaving the mesh with no material at all.
6927
+ if (this.component && material) {
6928
+ this.component.material = material;
4833
6929
  }
4834
6930
  }
4835
6931
  /**
@@ -4866,7 +6962,7 @@ class RenderComponentElement extends ComponentElement {
4866
6962
  this.castShadows = parseBool(newValue, true);
4867
6963
  break;
4868
6964
  case 'material':
4869
- this.material = newValue;
6965
+ this.material = newValue ?? '';
4870
6966
  break;
4871
6967
  case 'receive-shadows':
4872
6968
  this.receiveShadows = parseBool(newValue, true);
@@ -4888,45 +6984,45 @@ customElements.define('pc-render', RenderComponentElement);
4888
6984
  * @category Components
4889
6985
  */
4890
6986
  class RigidBodyComponentElement extends ComponentElement {
6987
+ /**
6988
+ * The angular damping of the rigidbody.
6989
+ */
6990
+ _angularDamping = 0;
6991
+ /**
6992
+ * The angular factor of the rigidbody.
6993
+ */
6994
+ _angularFactor = new playcanvas.Vec3(1, 1, 1);
6995
+ /**
6996
+ * The friction of the rigidbody.
6997
+ */
6998
+ _friction = 0.5;
6999
+ /**
7000
+ * The linear damping of the rigidbody.
7001
+ */
7002
+ _linearDamping = 0;
7003
+ /**
7004
+ * The linear factor of the rigidbody.
7005
+ */
7006
+ _linearFactor = new playcanvas.Vec3(1, 1, 1);
7007
+ /**
7008
+ * The mass of the rigidbody.
7009
+ */
7010
+ _mass = 1;
7011
+ /**
7012
+ * The restitution of the rigidbody.
7013
+ */
7014
+ _restitution = 0;
7015
+ /**
7016
+ * The rolling friction of the rigidbody.
7017
+ */
7018
+ _rollingFriction = 0;
7019
+ /**
7020
+ * The type of the rigidbody.
7021
+ */
7022
+ _type = 'static';
4891
7023
  /** @ignore */
4892
7024
  constructor() {
4893
7025
  super('rigidbody');
4894
- /**
4895
- * The angular damping of the rigidbody.
4896
- */
4897
- this._angularDamping = 0;
4898
- /**
4899
- * The angular factor of the rigidbody.
4900
- */
4901
- this._angularFactor = new playcanvas.Vec3(1, 1, 1);
4902
- /**
4903
- * The friction of the rigidbody.
4904
- */
4905
- this._friction = 0.5;
4906
- /**
4907
- * The linear damping of the rigidbody.
4908
- */
4909
- this._linearDamping = 0;
4910
- /**
4911
- * The linear factor of the rigidbody.
4912
- */
4913
- this._linearFactor = new playcanvas.Vec3(1, 1, 1);
4914
- /**
4915
- * The mass of the rigidbody.
4916
- */
4917
- this._mass = 1;
4918
- /**
4919
- * The restitution of the rigidbody.
4920
- */
4921
- this._restitution = 0;
4922
- /**
4923
- * The rolling friction of the rigidbody.
4924
- */
4925
- this._rollingFriction = 0;
4926
- /**
4927
- * The type of the rigidbody.
4928
- */
4929
- this._type = 'static';
4930
7026
  }
4931
7027
  getInitialComponentData() {
4932
7028
  return {
@@ -5076,15 +7172,15 @@ customElements.define('pc-rigidbody', RigidBodyComponentElement);
5076
7172
  * @category Components
5077
7173
  */
5078
7174
  class ScreenComponentElement extends ComponentElement {
7175
+ _screenSpace = false;
7176
+ _resolution = new playcanvas.Vec2(640, 320);
7177
+ _referenceResolution = new playcanvas.Vec2(640, 320);
7178
+ _priority = 0;
7179
+ _blend = false;
7180
+ _scaleBlend = 0.5;
5079
7181
  /** @ignore */
5080
7182
  constructor() {
5081
7183
  super('screen');
5082
- this._screenSpace = false;
5083
- this._resolution = new playcanvas.Vec2(640, 320);
5084
- this._referenceResolution = new playcanvas.Vec2(640, 320);
5085
- this._priority = 0;
5086
- this._blend = false;
5087
- this._scaleBlend = 0.5;
5088
7184
  }
5089
7185
  getInitialComponentData() {
5090
7186
  return {
@@ -5207,13 +7303,13 @@ const orientations = new Map([
5207
7303
  * @category Components
5208
7304
  */
5209
7305
  class ScrollbarComponentElement extends ComponentElement {
7306
+ _orientation = 'horizontal';
7307
+ _value = 0;
7308
+ _handleSize = 0.5;
7309
+ _handle = '';
5210
7310
  /** @ignore */
5211
7311
  constructor() {
5212
7312
  super('scrollbar');
5213
- this._orientation = 'horizontal';
5214
- this._value = 0;
5215
- this._handleSize = 0.5;
5216
- this._handle = '';
5217
7313
  }
5218
7314
  getInitialComponentData() {
5219
7315
  const data = {
@@ -5240,10 +7336,9 @@ class ScrollbarComponentElement extends ComponentElement {
5240
7336
  * @param value - The orientation.
5241
7337
  */
5242
7338
  set orientation(value) {
5243
- var _a;
5244
7339
  this._orientation = value;
5245
7340
  if (this.component) {
5246
- this.component.orientation = (_a = orientations.get(value)) !== null && _a !== void 0 ? _a : playcanvas.ORIENTATION_HORIZONTAL;
7341
+ this.component.orientation = orientations.get(value) ?? playcanvas.ORIENTATION_HORIZONTAL;
5247
7342
  }
5248
7343
  }
5249
7344
  /**
@@ -5328,7 +7423,7 @@ class ScrollbarComponentElement extends ComponentElement {
5328
7423
  this.handleSize = parseNumber(newValue, 0.5, name);
5329
7424
  break;
5330
7425
  case 'handle':
5331
- this.handle = newValue;
7426
+ this.handle = newValue ?? '';
5332
7427
  break;
5333
7428
  }
5334
7429
  }
@@ -5353,22 +7448,22 @@ const visibilities = new Map([
5353
7448
  * @category Components
5354
7449
  */
5355
7450
  class ScrollViewComponentElement extends ComponentElement {
7451
+ _horizontal = true;
7452
+ _vertical = true;
7453
+ _scrollMode = 'bounce';
7454
+ _bounceAmount = 0.1;
7455
+ _friction = 0.05;
7456
+ _useMouseWheel = true;
7457
+ _mouseWheelSensitivity = new playcanvas.Vec2(1, 1);
7458
+ _horizontalScrollbarVisibility = 'when-required';
7459
+ _verticalScrollbarVisibility = 'when-required';
7460
+ _viewport = '';
7461
+ _content = '';
7462
+ _horizontalScrollbar = '';
7463
+ _verticalScrollbar = '';
5356
7464
  /** @ignore */
5357
7465
  constructor() {
5358
7466
  super('scrollview');
5359
- this._horizontal = true;
5360
- this._vertical = true;
5361
- this._scrollMode = 'bounce';
5362
- this._bounceAmount = 0.1;
5363
- this._friction = 0.05;
5364
- this._useMouseWheel = true;
5365
- this._mouseWheelSensitivity = new playcanvas.Vec2(1, 1);
5366
- this._horizontalScrollbarVisibility = 'when-required';
5367
- this._verticalScrollbarVisibility = 'when-required';
5368
- this._viewport = '';
5369
- this._content = '';
5370
- this._horizontalScrollbar = '';
5371
- this._verticalScrollbar = '';
5372
7467
  }
5373
7468
  getInitialComponentData() {
5374
7469
  const data = {
@@ -5447,10 +7542,9 @@ class ScrollViewComponentElement extends ComponentElement {
5447
7542
  * @param value - The scroll mode.
5448
7543
  */
5449
7544
  set scrollMode(value) {
5450
- var _a;
5451
7545
  this._scrollMode = value;
5452
7546
  if (this.component) {
5453
- this.component.scrollMode = (_a = scrollModes.get(value)) !== null && _a !== void 0 ? _a : playcanvas.SCROLL_MODE_BOUNCE;
7547
+ this.component.scrollMode = scrollModes.get(value) ?? playcanvas.SCROLL_MODE_BOUNCE;
5454
7548
  }
5455
7549
  }
5456
7550
  /**
@@ -5536,10 +7630,9 @@ class ScrollViewComponentElement extends ComponentElement {
5536
7630
  * @param value - The horizontal scrollbar visibility.
5537
7631
  */
5538
7632
  set horizontalScrollbarVisibility(value) {
5539
- var _a;
5540
7633
  this._horizontalScrollbarVisibility = value;
5541
7634
  if (this.component) {
5542
- this.component.horizontalScrollbarVisibility = (_a = visibilities.get(value)) !== null && _a !== void 0 ? _a : playcanvas.SCROLLBAR_VISIBILITY_SHOW_WHEN_REQUIRED;
7635
+ this.component.horizontalScrollbarVisibility = visibilities.get(value) ?? playcanvas.SCROLLBAR_VISIBILITY_SHOW_WHEN_REQUIRED;
5543
7636
  }
5544
7637
  }
5545
7638
  /**
@@ -5555,10 +7648,9 @@ class ScrollViewComponentElement extends ComponentElement {
5555
7648
  * @param value - The vertical scrollbar visibility.
5556
7649
  */
5557
7650
  set verticalScrollbarVisibility(value) {
5558
- var _a;
5559
7651
  this._verticalScrollbarVisibility = value;
5560
7652
  if (this.component) {
5561
- this.component.verticalScrollbarVisibility = (_a = visibilities.get(value)) !== null && _a !== void 0 ? _a : playcanvas.SCROLLBAR_VISIBILITY_SHOW_WHEN_REQUIRED;
7653
+ this.component.verticalScrollbarVisibility = visibilities.get(value) ?? playcanvas.SCROLLBAR_VISIBILITY_SHOW_WHEN_REQUIRED;
5562
7654
  }
5563
7655
  }
5564
7656
  /**
@@ -5693,16 +7785,16 @@ class ScrollViewComponentElement extends ComponentElement {
5693
7785
  this.verticalScrollbarVisibility = parseEnum(newValue, visibilities, 'when-required', name);
5694
7786
  break;
5695
7787
  case 'viewport':
5696
- this.viewport = newValue;
7788
+ this.viewport = newValue ?? '';
5697
7789
  break;
5698
7790
  case 'content':
5699
- this.content = newValue;
7791
+ this.content = newValue ?? '';
5700
7792
  break;
5701
7793
  case 'horizontal-scrollbar':
5702
- this.horizontalScrollbar = newValue;
7794
+ this.horizontalScrollbar = newValue ?? '';
5703
7795
  break;
5704
7796
  case 'vertical-scrollbar':
5705
- this.verticalScrollbar = newValue;
7797
+ this.verticalScrollbar = newValue ?? '';
5706
7798
  break;
5707
7799
  }
5708
7800
  }
@@ -5734,24 +7826,28 @@ customElements.define('pc-scrollview', ScrollViewComponentElement);
5734
7826
  *
5735
7827
  * The element becomes ready once its script instance has been created by the parent
5736
7828
  * `<pc-scripts>` element.
7829
+ *
7830
+ * @fires {CustomEvent} scriptattributeschange - Fired when the script's attributes change. The
7831
+ * `detail` carries the new `attributes` object. Bubbles.
7832
+ * @fires {CustomEvent} scriptenablechange - Fired when the script's enabled state changes. The
7833
+ * `detail` carries the new `enabled` state. Bubbles.
7834
+ * @fires {CustomEvent} scriptnamechange - Fired when the script is renamed on a live element. The
7835
+ * `detail` carries `oldName` and `newName`. Bubbles.
5737
7836
  */
5738
7837
  class ScriptElement extends AsyncElement {
5739
- constructor() {
5740
- super(...arguments);
5741
- this._attributes = {};
5742
- this._enabled = true;
5743
- /**
5744
- * Whether readiness has been signalled. Creation can happen more than once over an
5745
- * element's life (a runtime `name` change recreates the instance), but `ready` is a
5746
- * one-shot signal, so only the first successful creation fires it.
5747
- */
5748
- this._readySignalled = false;
5749
- /**
5750
- * The Script instance created for this element by its parent `<pc-scripts>` element.
5751
- * @ignore
5752
- */
5753
- this._script = null;
5754
- }
7838
+ _attributes = {};
7839
+ _enabled = true;
7840
+ /**
7841
+ * Whether readiness has been signalled. Creation can happen more than once over an
7842
+ * element's life (a runtime `name` change recreates the instance), but `ready` is a
7843
+ * one-shot signal, so only the first successful creation fires it.
7844
+ */
7845
+ _readySignalled = false;
7846
+ /**
7847
+ * The Script instance created for this element by its parent `<pc-scripts>` element.
7848
+ * @ignore
7849
+ */
7850
+ _script = null;
5755
7851
  /**
5756
7852
  * Sets the attributes of the script as an object. Values are converted with the same rules
5757
7853
  * as the `attributes` attribute: `asset:`/`entity:` references and `vec2:`/`vec3:`/`vec4:`/
@@ -5761,7 +7857,7 @@ class ScriptElement extends AsyncElement {
5761
7857
  * @param value - The attributes of the script.
5762
7858
  */
5763
7859
  set scriptAttributes(value) {
5764
- this._attributes = value !== null && value !== void 0 ? value : {};
7860
+ this._attributes = value ?? {};
5765
7861
  this.dispatchEvent(new CustomEvent('scriptattributeschange', {
5766
7862
  detail: { attributes: this._attributes },
5767
7863
  bubbles: true
@@ -5813,8 +7909,7 @@ class ScriptElement extends AsyncElement {
5813
7909
  * @returns The name.
5814
7910
  */
5815
7911
  get name() {
5816
- var _a;
5817
- return (_a = this.getAttribute('name')) !== null && _a !== void 0 ? _a : '';
7912
+ return this.getAttribute('name') ?? '';
5818
7913
  }
5819
7914
  /**
5820
7915
  * Gets the {@link Script} instance created for this element. Returns `null` until the
@@ -5826,10 +7921,9 @@ class ScriptElement extends AsyncElement {
5826
7921
  return this._script;
5827
7922
  }
5828
7923
  connectedCallback() {
5829
- var _a;
5830
7924
  // Script instances are created by the parent pc-scripts element, so an element placed
5831
7925
  // anywhere else is inert and never becomes ready - warn rather than hang silently
5832
- if (((_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.tagName) !== 'PC-SCRIPTS') {
7926
+ if (this.parentElement?.tagName !== 'PC-SCRIPTS') {
5833
7927
  console.warn(`pc-script '${this.getAttribute('name')}' must be a direct child of pc-scripts - script not created`);
5834
7928
  }
5835
7929
  }
@@ -5981,8 +8075,7 @@ const vectorConversion = (length, Ctor) => {
5981
8075
  * @returns The color, or `raw`.
5982
8076
  */
5983
8077
  const colorConversion = (rest, raw) => {
5984
- var _a;
5985
- const components = (_a = parseComponents(rest, 4)) !== null && _a !== void 0 ? _a : parseComponents(rest, 3);
8078
+ const components = parseComponents(rest, 4) ?? parseComponents(rest, 3);
5986
8079
  if (components) {
5987
8080
  return new playcanvas.Color(components);
5988
8081
  }
@@ -5990,7 +8083,7 @@ const colorConversion = (rest, raw) => {
5990
8083
  return raw;
5991
8084
  };
5992
8085
  /**
5993
- * The conversion prefixes recognised in script attribute values, mapped to the conversion each
8086
+ * The conversion prefixes recognized in script attribute values, mapped to the conversion each
5994
8087
  * performs. These keys are the single source of truth for the prefix vocabulary: they drive both
5995
8088
  * the conversion in `convertAttributes` and the has-a-prefix test in `setScriptProperty`, so a
5996
8089
  * prefix added here is automatically known to both.
@@ -6006,10 +8099,10 @@ const CONVERSIONS = new Map([
6006
8099
  /**
6007
8100
  * Matches a value against the conversion prefixes. A prefix is the text before the first colon,
6008
8101
  * so a value whose remainder itself contains colons (`asset:a:b`) still resolves, and a value
6009
- * with an unrecognised prefix (`https://...`) or no colon does not match.
8102
+ * with an unrecognized prefix (`https://...`) or no colon does not match.
6010
8103
  * @param value - The value to inspect.
6011
8104
  * @returns The matching converter and the text after the prefix, or `null` if the value carries
6012
- * no recognised prefix.
8105
+ * no recognized prefix.
6013
8106
  */
6014
8107
  const matchConversion = (value) => {
6015
8108
  const index = value.indexOf(':');
@@ -6048,6 +8141,7 @@ const findCaseMatch = (script, key) => {
6048
8141
  * @category Components
6049
8142
  */
6050
8143
  class ScriptComponentElement extends ComponentElement {
8144
+ observer;
6051
8145
  /** @ignore */
6052
8146
  constructor() {
6053
8147
  super('script');
@@ -6302,8 +8396,7 @@ class ScriptComponentElement extends ComponentElement {
6302
8396
  * @param scriptElement - The `pc-script` element holding the attributes.
6303
8397
  */
6304
8398
  applyInlineAttributes(script, scriptElement) {
6305
- var _a;
6306
- const scriptName = (_a = scriptElement.getAttribute('name')) !== null && _a !== void 0 ? _a : '';
8399
+ const scriptName = scriptElement.getAttribute('name') ?? '';
6307
8400
  for (const attr of Array.from(scriptElement.attributes)) {
6308
8401
  if (!isReservedAttribute(attr.name)) {
6309
8402
  this.setScriptProperty(script, scriptName, attr.name, attr.value);
@@ -6318,7 +8411,6 @@ class ScriptComponentElement extends ComponentElement {
6318
8411
  * @param attributeName - The name of the changed attribute.
6319
8412
  */
6320
8413
  applyScriptProperty(scriptElement, attributeName) {
6321
- var _a;
6322
8414
  const script = this.scriptFor(scriptElement);
6323
8415
  if (!script)
6324
8416
  return;
@@ -6331,7 +8423,7 @@ class ScriptComponentElement extends ComponentElement {
6331
8423
  }
6332
8424
  return;
6333
8425
  }
6334
- this.setScriptProperty(script, (_a = scriptElement.getAttribute('name')) !== null && _a !== void 0 ? _a : '', attributeName, value);
8426
+ this.setScriptProperty(script, scriptElement.getAttribute('name') ?? '', attributeName, value);
6335
8427
  }
6336
8428
  /**
6337
8429
  * Applies one attribute string to a script property. A string-typed attribute takes the
@@ -6446,9 +8538,8 @@ class ScriptComponentElement extends ComponentElement {
6446
8538
  }
6447
8539
  }
6448
8540
  disconnectedCallback() {
6449
- var _a;
6450
8541
  this.observer.disconnect();
6451
- (_a = super.disconnectedCallback) === null || _a === void 0 ? void 0 : _a.call(this);
8542
+ super.disconnectedCallback?.();
6452
8543
  }
6453
8544
  /**
6454
8545
  * Gets the underlying PlayCanvas script component.
@@ -6469,16 +8560,16 @@ customElements.define('pc-scripts', ScriptComponentElement);
6469
8560
  * @category Components
6470
8561
  */
6471
8562
  class SoundComponentElement extends ComponentElement {
8563
+ _distanceModel = 'linear';
8564
+ _maxDistance = 10000;
8565
+ _pitch = 1;
8566
+ _positional = false;
8567
+ _refDistance = 1;
8568
+ _rollOffFactor = 1;
8569
+ _volume = 1;
6472
8570
  /** @ignore */
6473
8571
  constructor() {
6474
8572
  super('sound');
6475
- this._distanceModel = 'linear';
6476
- this._maxDistance = 10000;
6477
- this._pitch = 1;
6478
- this._positional = false;
6479
- this._refDistance = 1;
6480
- this._rollOffFactor = 1;
6481
- this._volume = 1;
6482
8573
  }
6483
8574
  getInitialComponentData() {
6484
8575
  return {
@@ -6664,25 +8755,37 @@ customElements.define('pc-sounds', SoundComponentElement);
6664
8755
  * methods of the {@link AsyncElement} interface.
6665
8756
  */
6666
8757
  class SoundSlotElement extends AsyncElement {
6667
- constructor() {
6668
- super(...arguments);
6669
- this._asset = '';
6670
- this._autoPlay = false;
6671
- this._duration = null;
6672
- this._loop = false;
6673
- this._name = '';
6674
- this._overlap = false;
6675
- this._pitch = 1;
6676
- this._startTime = 0;
6677
- this._volume = 1;
6678
- /**
6679
- * The sound slot.
6680
- */
6681
- this.soundSlot = null;
6682
- }
8758
+ _asset = '';
8759
+ _autoPlay = false;
8760
+ _duration = null;
8761
+ _loop = false;
8762
+ _name = '';
8763
+ _overlap = false;
8764
+ _pitch = 1;
8765
+ _startTime = 0;
8766
+ _volume = 1;
8767
+ /**
8768
+ * The `<pc-sounds>` this slot was added to, captured at connect time.
8769
+ *
8770
+ * `disconnectedCallback` cannot rediscover it: by the time the element is disconnected its
8771
+ * `parentElement` is already `null`, so a lookup would both fail to find the component and
8772
+ * emit a misleading "must be a direct child" warning for what is an ordinary removal.
8773
+ */
8774
+ _soundElement = null;
8775
+ /**
8776
+ * The sound slot.
8777
+ */
8778
+ soundSlot = null;
6683
8779
  async connectedCallback() {
6684
- var _a;
6685
- await ((_a = this.soundElement) === null || _a === void 0 ? void 0 : _a.ready());
8780
+ const soundElement = this.soundElement;
8781
+ await soundElement?.ready();
8782
+ // The element may have been removed, or its parent torn down, while we were waiting. A
8783
+ // <pc-app> disconnects before its children, so by the time we resume the component can
8784
+ // already be gone - see the matching guard in disconnectedCallback below.
8785
+ const component = soundElement?.component;
8786
+ if (!this.isConnected || !component) {
8787
+ return;
8788
+ }
6686
8789
  const options = {
6687
8790
  autoPlay: this._autoPlay,
6688
8791
  loop: this._loop,
@@ -6694,7 +8797,8 @@ class SoundSlotElement extends AsyncElement {
6694
8797
  if (this._duration) {
6695
8798
  options.duration = this._duration;
6696
8799
  }
6697
- this.soundSlot = this.soundElement.component.addSlot(this._name, options);
8800
+ this._soundElement = soundElement;
8801
+ this.soundSlot = component.addSlot(this._name, options);
6698
8802
  this.asset = this._asset;
6699
8803
  if (this._autoPlay) {
6700
8804
  this.soundSlot.play();
@@ -6702,10 +8806,12 @@ class SoundSlotElement extends AsyncElement {
6702
8806
  this._onReady();
6703
8807
  }
6704
8808
  disconnectedCallback() {
6705
- var _a, _b;
6706
- // The component is null if the parent <pc-sound> (or the whole <pc-app>) is being
6707
- // torn down — parents disconnect first and have already removed the component.
6708
- (_b = (_a = this.soundElement) === null || _a === void 0 ? void 0 : _a.component) === null || _b === void 0 ? void 0 : _b.removeSlot(this._name);
8809
+ // Uses the cached parent rather than a fresh lookup, since parentElement is already null
8810
+ // by now. The component itself is null if the parent <pc-sound> (or the whole <pc-app>) is
8811
+ // being torn down — parents disconnect first and have already removed the component.
8812
+ this._soundElement?.component?.removeSlot(this._name);
8813
+ this._soundElement = null;
8814
+ this.soundSlot = null;
6709
8815
  }
6710
8816
  get soundElement() {
6711
8817
  const soundElement = this.parentElement;
@@ -6720,10 +8826,9 @@ class SoundSlotElement extends AsyncElement {
6720
8826
  * @param value - The asset.
6721
8827
  */
6722
8828
  set asset(value) {
6723
- var _a;
6724
8829
  this._asset = value;
6725
8830
  if (this.soundSlot) {
6726
- const id = (_a = AssetElement.get(value)) === null || _a === void 0 ? void 0 : _a.id;
8831
+ const id = AssetElement.get(value)?.id;
6727
8832
  if (id) {
6728
8833
  this.soundSlot.asset = id;
6729
8834
  }
@@ -6878,7 +8983,7 @@ class SoundSlotElement extends AsyncElement {
6878
8983
  attributeChangedCallback(name, _oldValue, newValue) {
6879
8984
  switch (name) {
6880
8985
  case 'asset':
6881
- this.asset = newValue;
8986
+ this.asset = newValue ?? '';
6882
8987
  break;
6883
8988
  case 'auto-play':
6884
8989
  this.autoPlay = parseBool(newValue, false);
@@ -6890,7 +8995,7 @@ class SoundSlotElement extends AsyncElement {
6890
8995
  this.loop = parseBool(newValue, false);
6891
8996
  break;
6892
8997
  case 'name':
6893
- this.name = newValue;
8998
+ this.name = newValue ?? '';
6894
8999
  break;
6895
9000
  case 'overlap':
6896
9001
  this.overlap = parseBool(newValue, false);
@@ -6918,15 +9023,15 @@ customElements.define('pc-sound', SoundSlotElement);
6918
9023
  * @category Components
6919
9024
  */
6920
9025
  class GSplatComponentElement extends ComponentElement {
9026
+ _asset = '';
9027
+ _castShadows = false;
9028
+ _lodBaseDistance = 5;
9029
+ _lodMultiplier = 3;
9030
+ _lodRangeMin = 0;
9031
+ _lodRangeMax = 99;
6921
9032
  /** @ignore */
6922
9033
  constructor() {
6923
9034
  super('gsplat');
6924
- this._asset = '';
6925
- this._castShadows = false;
6926
- this._lodBaseDistance = 5;
6927
- this._lodMultiplier = 3;
6928
- this._lodRangeMin = 0;
6929
- this._lodRangeMax = 99;
6930
9035
  }
6931
9036
  getInitialComponentData() {
6932
9037
  return {
@@ -7075,7 +9180,7 @@ class GSplatComponentElement extends ComponentElement {
7075
9180
  super.attributeChangedCallback(name, _oldValue, newValue);
7076
9181
  switch (name) {
7077
9182
  case 'asset':
7078
- this.asset = newValue;
9183
+ this.asset = newValue ?? '';
7079
9184
  break;
7080
9185
  case 'cast-shadows':
7081
9186
  this.castShadows = parseBool(newValue, false);
@@ -7104,11 +9209,8 @@ customElements.define('pc-gsplat', GSplatComponentElement);
7104
9209
  * {@link HTMLElement} interface.
7105
9210
  */
7106
9211
  class ModelElement extends AsyncElement {
7107
- constructor() {
7108
- super(...arguments);
7109
- this._asset = '';
7110
- this._entity = null;
7111
- }
9212
+ _asset = '';
9213
+ _entity = null;
7112
9214
  connectedCallback() {
7113
9215
  this._loadModel();
7114
9216
  this._onReady();
@@ -7140,10 +9242,9 @@ class ModelElement extends AsyncElement {
7140
9242
  }
7141
9243
  }
7142
9244
  async _loadModel() {
7143
- var _a;
7144
9245
  this._unloadModel();
7145
- const appElement = await ((_a = this.closestApp) === null || _a === void 0 ? void 0 : _a.ready());
7146
- const app = appElement === null || appElement === void 0 ? void 0 : appElement.app;
9246
+ const appElement = await this.closestApp?.ready();
9247
+ const app = appElement?.app;
7147
9248
  const asset = AssetElement.get(this._asset);
7148
9249
  if (!asset) {
7149
9250
  return;
@@ -7159,8 +9260,7 @@ class ModelElement extends AsyncElement {
7159
9260
  }
7160
9261
  }
7161
9262
  _unloadModel() {
7162
- var _a;
7163
- (_a = this._entity) === null || _a === void 0 ? void 0 : _a.destroy();
9263
+ this._entity?.destroy();
7164
9264
  this._entity = null;
7165
9265
  }
7166
9266
  /**
@@ -7186,7 +9286,7 @@ class ModelElement extends AsyncElement {
7186
9286
  attributeChangedCallback(name, _oldValue, newValue) {
7187
9287
  switch (name) {
7188
9288
  case 'asset':
7189
- this.asset = newValue;
9289
+ this.asset = newValue ?? '';
7190
9290
  break;
7191
9291
  }
7192
9292
  }
@@ -7200,34 +9300,31 @@ customElements.define('pc-model', ModelElement);
7200
9300
  * {@link HTMLElement} interface.
7201
9301
  */
7202
9302
  class SceneElement extends AsyncElement {
7203
- constructor() {
7204
- super(...arguments);
7205
- /**
7206
- * The fog type of the scene.
7207
- */
7208
- this._fog = 'none';
7209
- /**
7210
- * The color of the fog.
7211
- */
7212
- this._fogColor = new playcanvas.Color(1, 1, 1);
7213
- /**
7214
- * The density of the fog.
7215
- */
7216
- this._fogDensity = 0;
7217
- /**
7218
- * The start distance of the fog.
7219
- */
7220
- this._fogStart = 0;
7221
- /**
7222
- * The end distance of the fog.
7223
- */
7224
- this._fogEnd = 1000;
7225
- /**
7226
- * The gravity of the scene.
7227
- */
7228
- this._gravity = new playcanvas.Vec3(0, -9.81, 0);
7229
- this._scene = null;
7230
- }
9303
+ /**
9304
+ * The fog type of the scene.
9305
+ */
9306
+ _fog = 'none';
9307
+ /**
9308
+ * The color of the fog.
9309
+ */
9310
+ _fogColor = new playcanvas.Color(1, 1, 1);
9311
+ /**
9312
+ * The density of the fog.
9313
+ */
9314
+ _fogDensity = 0;
9315
+ /**
9316
+ * The start distance of the fog.
9317
+ */
9318
+ _fogStart = 0;
9319
+ /**
9320
+ * The end distance of the fog.
9321
+ */
9322
+ _fogEnd = 1000;
9323
+ /**
9324
+ * The gravity of the scene.
9325
+ */
9326
+ _gravity = new playcanvas.Vec3(0, -9.81, 0);
9327
+ _scene = null;
7231
9328
  /**
7232
9329
  * The PlayCanvas scene instance. Available once the element is ready — await
7233
9330
  * {@link whenReady} or the element's `ready()` promise before accessing it.
@@ -7237,8 +9334,7 @@ class SceneElement extends AsyncElement {
7237
9334
  return this._scene;
7238
9335
  }
7239
9336
  async connectedCallback() {
7240
- var _a;
7241
- await ((_a = this.closestApp) === null || _a === void 0 ? void 0 : _a.ready());
9337
+ await this.closestApp?.ready();
7242
9338
  this._scene = this.closestApp.app.scene;
7243
9339
  this.updateSceneSettings();
7244
9340
  this._onReady();
@@ -7393,19 +9489,16 @@ customElements.define('pc-scene', SceneElement);
7393
9489
  * methods of the {@link HTMLElement} interface.
7394
9490
  */
7395
9491
  class SkyElement extends AsyncElement {
7396
- constructor() {
7397
- super(...arguments);
7398
- this._asset = '';
7399
- this._center = new playcanvas.Vec3(0, 0.01, 0);
7400
- this._intensity = 1;
7401
- this._rotation = new playcanvas.Vec3();
7402
- this._level = 0;
7403
- this._lighting = false;
7404
- this._scale = new playcanvas.Vec3(100, 100, 100);
7405
- this._type = 'infinite';
7406
- this._scene = null;
7407
- this._appElement = null;
7408
- }
9492
+ _asset = '';
9493
+ _center = new playcanvas.Vec3(0, 0.01, 0);
9494
+ _intensity = 1;
9495
+ _rotation = new playcanvas.Vec3();
9496
+ _level = 0;
9497
+ _lighting = false;
9498
+ _scale = new playcanvas.Vec3(100, 100, 100);
9499
+ _type = 'infinite';
9500
+ _scene = null;
9501
+ _appElement = null;
7409
9502
  connectedCallback() {
7410
9503
  this._loadSkybox();
7411
9504
  this._onReady();
@@ -7437,9 +9530,8 @@ class SkyElement extends AsyncElement {
7437
9530
  this._scene.skyboxMip = this._level;
7438
9531
  }
7439
9532
  async _loadSkybox() {
7440
- var _a;
7441
- const appElement = await ((_a = this.closestApp) === null || _a === void 0 ? void 0 : _a.ready());
7442
- const app = appElement === null || appElement === void 0 ? void 0 : appElement.app;
9533
+ const appElement = await this.closestApp?.ready();
9534
+ const app = appElement?.app;
7443
9535
  if (!appElement || !app) {
7444
9536
  return;
7445
9537
  }
@@ -7460,7 +9552,6 @@ class SkyElement extends AsyncElement {
7460
9552
  }
7461
9553
  }
7462
9554
  _unloadSkybox() {
7463
- var _a, _b, _c;
7464
9555
  const scene = this._scene;
7465
9556
  if (!scene)
7466
9557
  return;
@@ -7468,12 +9559,12 @@ class SkyElement extends AsyncElement {
7468
9559
  // If the owning application has already been destroyed (removing a <pc-app>
7469
9560
  // disconnects it before its children), the scene, graphics device and skybox
7470
9561
  // textures have all been destroyed along with it — nothing left to clean up.
7471
- if (!((_a = this._appElement) === null || _a === void 0 ? void 0 : _a.app))
9562
+ if (!this._appElement?.app)
7472
9563
  return;
7473
- (_b = scene.skybox) === null || _b === void 0 ? void 0 : _b.destroy();
9564
+ scene.skybox?.destroy();
7474
9565
  // @ts-ignore
7475
9566
  scene.skybox = null;
7476
- (_c = scene.envAtlas) === null || _c === void 0 ? void 0 : _c.destroy();
9567
+ scene.envAtlas?.destroy();
7477
9568
  // @ts-ignore
7478
9569
  scene.envAtlas = null;
7479
9570
  }
@@ -7620,7 +9711,7 @@ class SkyElement extends AsyncElement {
7620
9711
  attributeChangedCallback(name, _oldValue, newValue) {
7621
9712
  switch (name) {
7622
9713
  case 'asset':
7623
- this.asset = newValue;
9714
+ this.asset = newValue ?? '';
7624
9715
  break;
7625
9716
  case 'center':
7626
9717
  this.center = parseVec3(newValue, new playcanvas.Vec3(0, 0.01, 0), name);