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