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