@playcanvas/web-components 0.1.8 → 0.1.10

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 (58) hide show
  1. package/README.md +2 -2
  2. package/dist/app.d.ts +21 -1
  3. package/dist/asset.d.ts +3 -1
  4. package/dist/async-element.d.ts +2 -1
  5. package/dist/components/camera-component.d.ts +28 -4
  6. package/dist/components/collision-component.d.ts +4 -4
  7. package/dist/components/component.d.ts +3 -1
  8. package/dist/components/element-component.d.ts +4 -1
  9. package/dist/components/gsplat-component.d.ts +4 -1
  10. package/dist/components/light-component.d.ts +50 -4
  11. package/dist/components/listener-component.d.ts +4 -1
  12. package/dist/components/render-component.d.ts +4 -1
  13. package/dist/components/rigidbody-component.d.ts +4 -4
  14. package/dist/components/screen-component.d.ts +4 -1
  15. package/dist/components/script-component.d.ts +4 -1
  16. package/dist/components/script.d.ts +3 -1
  17. package/dist/components/sound-component.d.ts +5 -2
  18. package/dist/components/sound-slot.d.ts +3 -1
  19. package/dist/entity.d.ts +14 -3
  20. package/dist/fog.d.ts +28 -0
  21. package/dist/material.d.ts +3 -1
  22. package/dist/model.d.ts +3 -1
  23. package/dist/module.d.ts +6 -0
  24. package/dist/pwc.cjs +569 -96
  25. package/dist/pwc.cjs.map +1 -1
  26. package/dist/pwc.js +569 -96
  27. package/dist/pwc.js.map +1 -1
  28. package/dist/pwc.min.js +1 -1
  29. package/dist/pwc.min.js.map +1 -1
  30. package/dist/pwc.mjs +570 -97
  31. package/dist/pwc.mjs.map +1 -1
  32. package/dist/scene.d.ts +3 -1
  33. package/dist/sky.d.ts +3 -1
  34. package/package.json +10 -9
  35. package/src/app.ts +208 -2
  36. package/src/asset.ts +3 -1
  37. package/src/async-element.ts +2 -1
  38. package/src/components/camera-component.ts +69 -7
  39. package/src/components/collision-component.ts +4 -4
  40. package/src/components/component.ts +3 -1
  41. package/src/components/element-component.ts +4 -2
  42. package/src/components/gsplat-component.ts +4 -1
  43. package/src/components/light-component.ts +107 -7
  44. package/src/components/listener-component.ts +4 -1
  45. package/src/components/render-component.ts +4 -1
  46. package/src/components/rigidbody-component.ts +4 -4
  47. package/src/components/screen-component.ts +4 -1
  48. package/src/components/script-component.ts +26 -14
  49. package/src/components/script.ts +3 -1
  50. package/src/components/sound-component.ts +4 -1
  51. package/src/components/sound-slot.ts +3 -1
  52. package/src/entity.ts +149 -32
  53. package/src/fog.ts +121 -0
  54. package/src/material.ts +5 -3
  55. package/src/model.ts +3 -1
  56. package/src/module.ts +6 -0
  57. package/src/scene.ts +13 -11
  58. package/src/sky.ts +3 -1
package/src/app.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { Application, FILLMODE_FILL_WINDOW, Keyboard, Mouse, RESOLUTION_AUTO } from 'playcanvas';
1
+ import { Application, CameraComponent, FILLMODE_FILL_WINDOW, Keyboard, Mouse, Picker, RESOLUTION_AUTO } from 'playcanvas';
2
2
 
3
3
  import { AssetElement } from './asset';
4
4
  import { AsyncElement } from './async-element';
5
+ import { EntityElement } from './entity';
5
6
  import { MaterialElement } from './material';
6
7
  import { ModuleElement } from './module';
7
8
 
@@ -24,13 +25,35 @@ class AppElement extends AsyncElement {
24
25
 
25
26
  private _highResolution = false;
26
27
 
28
+ private _hierarchyReady = false;
29
+
30
+ private _picker: Picker | null = null;
31
+
32
+ private _hasPointerListeners: { [key: string]: boolean } = {
33
+ pointerenter: false,
34
+ pointerleave: false,
35
+ pointerdown: false,
36
+ pointerup: false,
37
+ pointermove: false
38
+ };
39
+
40
+ private _hoveredEntity: EntityElement | null = null;
41
+
42
+ private _pointerHandlers: { [key: string]: EventListener | null } = {
43
+ pointermove: null,
44
+ pointerdown: null,
45
+ pointerup: null
46
+ };
47
+
27
48
  /**
28
49
  * The PlayCanvas application instance.
29
50
  */
30
51
  app: Application | null = null;
31
52
 
32
53
  /**
33
- * Creates a new AppElement.
54
+ * Creates a new AppElement instance.
55
+ *
56
+ * @ignore
34
57
  */
35
58
  constructor() {
36
59
  super();
@@ -66,6 +89,8 @@ class AppElement extends AsyncElement {
66
89
  this.app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
67
90
  this.app.setCanvasResolution(RESOLUTION_AUTO);
68
91
 
92
+ this._pickerCreate();
93
+
69
94
  // Get all pc-asset elements that are direct children of the pc-app element
70
95
  const assetElements = this.querySelectorAll<AssetElement>(':scope > pc-asset');
71
96
  Array.from(assetElements).forEach((assetElement) => {
@@ -82,6 +107,19 @@ class AppElement extends AsyncElement {
82
107
  materialElement.createMaterial();
83
108
  });
84
109
 
110
+ // Create all entities
111
+ const entityElements = this.querySelectorAll<EntityElement>('pc-entity');
112
+ Array.from(entityElements).forEach((entityElement) => {
113
+ entityElement.createEntity(this.app!);
114
+ });
115
+
116
+ // Build hierarchy
117
+ entityElements.forEach((entityElement) => {
118
+ entityElement.buildHierarchy(this.app!);
119
+ });
120
+
121
+ this._hierarchyReady = true;
122
+
85
123
  // Load assets before starting the application
86
124
  this.app.preload(() => {
87
125
  // Start the application
@@ -95,6 +133,8 @@ class AppElement extends AsyncElement {
95
133
  }
96
134
 
97
135
  disconnectedCallback() {
136
+ this._pickerDestroy();
137
+
98
138
  // Clean up the application
99
139
  if (this.app) {
100
140
  this.app.destroy();
@@ -117,6 +157,163 @@ class AppElement extends AsyncElement {
117
157
  }
118
158
  }
119
159
 
160
+ _pickerCreate() {
161
+ const { width, height } = this.app!.graphicsDevice;
162
+ this._picker = new Picker(this.app!, width, height);
163
+
164
+ // Create bound handlers but don't attach them yet
165
+ this._pointerHandlers.pointermove = this._onPointerMove.bind(this) as EventListener;
166
+ this._pointerHandlers.pointerdown = this._onPointerDown.bind(this) as EventListener;
167
+ this._pointerHandlers.pointerup = this._onPointerUp.bind(this) as EventListener;
168
+
169
+ // Listen for pointer listeners being added/removed
170
+ ['pointermove', 'pointerdown', 'pointerup', 'pointerenter', 'pointerleave'].forEach((type) => {
171
+ this.addEventListener(`${type}:connect`, () => this._onPointerListenerAdded(type));
172
+ this.addEventListener(`${type}:disconnect`, () => this._onPointerListenerRemoved(type));
173
+ });
174
+ }
175
+
176
+ _pickerDestroy() {
177
+ if (this._canvas) {
178
+ Object.entries(this._pointerHandlers).forEach(([type, handler]) => {
179
+ if (handler) {
180
+ this._canvas!.removeEventListener(type, handler);
181
+ }
182
+ });
183
+ }
184
+
185
+ this._picker = null;
186
+ this._pointerHandlers = {
187
+ pointermove: null,
188
+ pointerdown: null,
189
+ pointerup: null
190
+ };
191
+ }
192
+
193
+ _onPointerMove(event: PointerEvent) {
194
+ if (!this._picker || !this.app) return;
195
+
196
+ const camera = this.app!.root.findComponent('camera') as CameraComponent;
197
+ if (!camera) return;
198
+
199
+ const canvasRect = this._canvas!.getBoundingClientRect();
200
+ const x = event.clientX - canvasRect.left;
201
+ const y = event.clientY - canvasRect.top;
202
+
203
+ this._picker.prepare(camera, this.app!.scene);
204
+ const selection = this._picker.getSelection(x, y);
205
+
206
+ // Get the currently hovered entity by walking up the hierarchy
207
+ let newHoverEntity = null;
208
+ if (selection.length > 0) {
209
+ let node = selection[0].node;
210
+ while (node && !newHoverEntity) {
211
+ const entityElement = this.querySelector(`pc-entity[name="${node.name}"]`) as EntityElement;
212
+ if (entityElement) {
213
+ newHoverEntity = entityElement;
214
+ }
215
+ node = node.parent;
216
+ }
217
+ }
218
+
219
+ // Handle enter/leave events
220
+ if (this._hoveredEntity !== newHoverEntity) {
221
+ if (this._hoveredEntity && this._hoveredEntity.hasListeners('pointerleave')) {
222
+ this._hoveredEntity.dispatchEvent(new PointerEvent('pointerleave', event));
223
+ }
224
+ if (newHoverEntity && newHoverEntity.hasListeners('pointerenter')) {
225
+ newHoverEntity.dispatchEvent(new PointerEvent('pointerenter', event));
226
+ }
227
+ }
228
+
229
+ // Update hover state
230
+ this._hoveredEntity = newHoverEntity;
231
+
232
+ // Handle pointermove event
233
+ if (newHoverEntity && newHoverEntity.hasListeners('pointermove')) {
234
+ newHoverEntity.dispatchEvent(new PointerEvent('pointermove', event));
235
+ }
236
+ }
237
+
238
+ _onPointerDown(event: PointerEvent) {
239
+ if (!this._picker || !this.app) return;
240
+
241
+ const camera = this.app!.root.findComponent('camera') as CameraComponent;
242
+ if (!camera) return;
243
+
244
+ const canvasRect = this._canvas!.getBoundingClientRect();
245
+ const x = event.clientX - canvasRect.left;
246
+ const y = event.clientY - canvasRect.top;
247
+
248
+ this._picker.prepare(camera, this.app!.scene);
249
+ const selection = this._picker.getSelection(x, y);
250
+
251
+ if (selection.length > 0) {
252
+ let node = selection[0].node;
253
+ while (node) {
254
+ const entityElement = this.querySelector(`pc-entity[name="${node.name}"]`) as EntityElement;
255
+ if (entityElement && entityElement.hasListeners('pointerdown')) {
256
+ entityElement.dispatchEvent(new PointerEvent('pointerdown', event));
257
+ break;
258
+ }
259
+ node = node.parent;
260
+ }
261
+ }
262
+ }
263
+
264
+ _onPointerUp(event: PointerEvent) {
265
+ if (!this._picker || !this.app) return;
266
+
267
+ const camera = this.app!.root.findComponent('camera') as CameraComponent;
268
+ if (!camera) return;
269
+
270
+ const canvasRect = this._canvas!.getBoundingClientRect();
271
+ const x = event.clientX - canvasRect.left;
272
+ const y = event.clientY - canvasRect.top;
273
+
274
+ this._picker.prepare(camera, this.app!.scene);
275
+ const selection = this._picker.getSelection(x, y);
276
+
277
+ if (selection.length > 0) {
278
+ const entityElement = this.querySelector(`pc-entity[name="${selection[0].node.name}"]`) as EntityElement;
279
+ if (entityElement && entityElement.hasListeners('pointerup')) {
280
+ entityElement.dispatchEvent(new PointerEvent('pointerup', event));
281
+ }
282
+ }
283
+ }
284
+
285
+ _onPointerListenerAdded(type: string) {
286
+ if (!this._hasPointerListeners[type] && this._canvas) {
287
+ this._hasPointerListeners[type] = true;
288
+
289
+ // For enter/leave events, we need the move handler
290
+ const handler = (type === 'pointerenter' || type === 'pointerleave') ?
291
+ this._pointerHandlers.pointermove :
292
+ this._pointerHandlers[type];
293
+
294
+ if (handler) {
295
+ this._canvas.addEventListener(type === 'pointerenter' || type === 'pointerleave' ? 'pointermove' : type, handler);
296
+ }
297
+ }
298
+ }
299
+
300
+ _onPointerListenerRemoved(type: string) {
301
+ const hasListeners = Array.from(this.querySelectorAll<EntityElement>('pc-entity'))
302
+ .some(entity => entity.hasListeners(type));
303
+
304
+ if (!hasListeners && this._canvas) {
305
+ this._hasPointerListeners[type] = false;
306
+
307
+ const handler = (type === 'pointerenter' || type === 'pointerleave') ?
308
+ this._pointerHandlers.pointermove :
309
+ this._pointerHandlers[type];
310
+
311
+ if (handler) {
312
+ this._canvas.removeEventListener(type === 'pointerenter' || type === 'pointerleave' ? 'pointermove' : type, handler);
313
+ }
314
+ }
315
+ }
316
+
120
317
  /**
121
318
  * Sets the alpha flag.
122
319
  * @param value - The alpha flag.
@@ -165,6 +362,15 @@ class AppElement extends AsyncElement {
165
362
  return this._depth;
166
363
  }
167
364
 
365
+ /**
366
+ * Gets the hierarchy ready flag.
367
+ * @returns The hierarchy ready flag.
368
+ * @ignore
369
+ */
370
+ get hierarchyReady() {
371
+ return this._hierarchyReady;
372
+ }
373
+
168
374
  /**
169
375
  * Sets the high resolution flag. When true, the application will render at the device's
170
376
  * physical resolution. When false, the application will render at CSS resolution.
package/src/asset.ts CHANGED
@@ -21,7 +21,9 @@ const extToType = new Map([
21
21
  ]);
22
22
 
23
23
  /**
24
- * Loads an asset into the PlayCanvas engine.
24
+ * The AssetElement interface provides properties and methods for manipulating
25
+ * `<pc-asset>` elements. The AssetElement interface also inherits the properties and
26
+ * methods of the {@link HTMLElement} interface.
25
27
  */
26
28
  class AssetElement extends HTMLElement {
27
29
  private _preload: boolean = false;
@@ -2,13 +2,14 @@ import { AppElement } from './app';
2
2
  import { EntityElement } from './entity';
3
3
 
4
4
  /**
5
- * Base class for all PlayCanvas web components that initialize asynchronously.
5
+ * Base class for all PlayCanvas Web Components that initialize asynchronously.
6
6
  */
7
7
  class AsyncElement extends HTMLElement {
8
8
  private _readyPromise: Promise<void>;
9
9
 
10
10
  private _readyResolve!: () => void;
11
11
 
12
+ /** @ignore */
12
13
  constructor() {
13
14
  super();
14
15
  this._readyPromise = new Promise<void>((resolve) => {
@@ -1,10 +1,22 @@
1
- import { PROJECTION_ORTHOGRAPHIC, PROJECTION_PERSPECTIVE, CameraComponent, Color, Vec4, XRTYPE_VR } from 'playcanvas';
1
+ import { CameraComponent, Color, Vec4, GAMMA_NONE, GAMMA_SRGB, PROJECTION_ORTHOGRAPHIC, PROJECTION_PERSPECTIVE, TONEMAP_LINEAR, TONEMAP_FILMIC, TONEMAP_NEUTRAL, TONEMAP_ACES2, TONEMAP_ACES, TONEMAP_HEJL, TONEMAP_NONE, XRTYPE_VR } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
4
  import { parseColor, parseVec4 } from '../utils';
5
5
 
6
+ const tonemaps = new Map([
7
+ ['none', TONEMAP_NONE],
8
+ ['linear', TONEMAP_LINEAR],
9
+ ['filmic', TONEMAP_FILMIC],
10
+ ['hejl', TONEMAP_HEJL],
11
+ ['aces', TONEMAP_ACES],
12
+ ['aces2', TONEMAP_ACES2],
13
+ ['neutral', TONEMAP_NEUTRAL]
14
+ ]);
15
+
6
16
  /**
7
- * Represents a camera component in the PlayCanvas engine.
17
+ * The CameraComponentElement interface provides properties and methods for manipulating
18
+ * `<pc-camera>` elements. The CameraComponentElement interface also inherits the properties and
19
+ * methods of the {@link HTMLElement} interface.
8
20
  *
9
21
  * @category Components
10
22
  */
@@ -27,6 +39,8 @@ class CameraComponentElement extends ComponentElement {
27
39
 
28
40
  private _frustumCulling = true;
29
41
 
42
+ private _gamma: 'none' | 'srgb' = 'srgb';
43
+
30
44
  private _nearClip = 0.1;
31
45
 
32
46
  private _orthographic = false;
@@ -39,9 +53,9 @@ class CameraComponentElement extends ComponentElement {
39
53
 
40
54
  private _scissorRect = new Vec4(0, 0, 1, 1);
41
55
 
42
- /**
43
- * Creates a new CameraComponentElement.
44
- */
56
+ private _tonemap: 'none' | 'linear' | 'filmic' | 'hejl' | 'aces' | 'aces2' | 'neutral' = 'none';
57
+
58
+ /** @ignore */
45
59
  constructor() {
46
60
  super('camera');
47
61
  }
@@ -57,12 +71,14 @@ class CameraComponentElement extends ComponentElement {
57
71
  flipFaces: this._flipFaces,
58
72
  fov: this._fov,
59
73
  frustumCulling: this._frustumCulling,
74
+ gammaCorrection: this._gamma === 'srgb' ? GAMMA_SRGB : GAMMA_NONE,
60
75
  nearClip: this._nearClip,
61
76
  orthographic: this._orthographic,
62
77
  orthoHeight: this._orthoHeight,
63
78
  priority: this._priority,
64
79
  rect: this._rect,
65
- scissorRect: this._scissorRect
80
+ scissorRect: this._scissorRect,
81
+ toneMapping: tonemaps.get(this._tonemap)
66
82
  };
67
83
  }
68
84
 
@@ -266,6 +282,25 @@ class CameraComponentElement extends ComponentElement {
266
282
  return this._frustumCulling;
267
283
  }
268
284
 
285
+ /**
286
+ * Sets the gamma correction of the camera.
287
+ * @param value - The gamma correction.
288
+ */
289
+ set gamma(value: 'none' | 'srgb') {
290
+ this._gamma = value;
291
+ if (this.component) {
292
+ this.component.gammaCorrection = value === 'srgb' ? GAMMA_SRGB : GAMMA_NONE;
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Gets the gamma correction of the camera.
298
+ * @returns The gamma correction.
299
+ */
300
+ get gamma(): 'none' | 'srgb' {
301
+ return this._gamma;
302
+ }
303
+
269
304
  /**
270
305
  * Sets the near clip distance of the camera.
271
306
  * @param value - The near clip distance.
@@ -380,6 +415,25 @@ class CameraComponentElement extends ComponentElement {
380
415
  return this._scissorRect;
381
416
  }
382
417
 
418
+ /**
419
+ * Sets the tone mapping of the camera.
420
+ * @param value - The tone mapping.
421
+ */
422
+ set tonemap(value: 'none' | 'linear' | 'filmic' | 'hejl' | 'aces' | 'aces2' | 'neutral') {
423
+ this._tonemap = value;
424
+ if (this.component) {
425
+ this.component.toneMapping = tonemaps.get(value) ?? TONEMAP_NONE;
426
+ }
427
+ }
428
+
429
+ /**
430
+ * Gets the tone mapping of the camera.
431
+ * @returns The tone mapping.
432
+ */
433
+ get tonemap(): 'none' | 'linear' | 'filmic' | 'hejl' | 'aces' | 'aces2' | 'neutral' {
434
+ return this._tonemap;
435
+ }
436
+
383
437
  static get observedAttributes() {
384
438
  return [
385
439
  ...super.observedAttributes,
@@ -392,12 +446,14 @@ class CameraComponentElement extends ComponentElement {
392
446
  'flip-faces',
393
447
  'fov',
394
448
  'frustum-culling',
449
+ 'gamma',
395
450
  'near-clip',
396
451
  'orthographic',
397
452
  'ortho-height',
398
453
  'priority',
399
454
  'rect',
400
- 'scissor-rect'
455
+ 'scissor-rect',
456
+ 'tonemap'
401
457
  ];
402
458
  }
403
459
 
@@ -432,6 +488,9 @@ class CameraComponentElement extends ComponentElement {
432
488
  case 'frustum-culling':
433
489
  this.frustumCulling = newValue !== 'false';
434
490
  break;
491
+ case 'gamma':
492
+ this.gamma = newValue as 'none' | 'srgb';
493
+ break;
435
494
  case 'near-clip':
436
495
  this.nearClip = parseFloat(newValue);
437
496
  break;
@@ -450,6 +509,9 @@ class CameraComponentElement extends ComponentElement {
450
509
  case 'scissor-rect':
451
510
  this.scissorRect = parseVec4(newValue);
452
511
  break;
512
+ case 'tonemap':
513
+ this.tonemap = newValue as 'none' | 'linear' | 'filmic' | 'hejl' | 'aces' | 'aces2' | 'neutral';
514
+ break;
453
515
  }
454
516
  }
455
517
  }
@@ -4,7 +4,9 @@ import { ComponentElement } from './component';
4
4
  import { parseQuat, parseVec3 } from '../utils';
5
5
 
6
6
  /**
7
- * Represents a collision component in the PlayCanvas engine.
7
+ * The CollisionComponentElement interface provides properties and methods for manipulating
8
+ * `<pc-collision>` elements. The CollisionComponentElement interface also inherits the properties
9
+ * and methods of the {@link HTMLElement} interface.
8
10
  *
9
11
  * @category Components
10
12
  */
@@ -25,9 +27,7 @@ class CollisionComponentElement extends ComponentElement {
25
27
 
26
28
  private _type: string = 'box';
27
29
 
28
- /**
29
- * Creates a new CollisionComponentElement.
30
- */
30
+ /** @ignore */
31
31
  constructor() {
32
32
  super('collision');
33
33
  }
@@ -15,8 +15,10 @@ class ComponentElement extends AsyncElement {
15
15
  private _component: Component | null = null;
16
16
 
17
17
  /**
18
- * Constructor for the ComponentElement.
18
+ * Creates a new ComponentElement instance.
19
+ *
19
20
  * @param componentName - The name of the component.
21
+ * @ignore
20
22
  */
21
23
  constructor(componentName: string) {
22
24
  super();
@@ -4,9 +4,10 @@ import { AssetElement } from '../asset';
4
4
  import { ComponentElement } from './component';
5
5
  import { parseColor, parseVec2, parseVec4 } from '../utils';
6
6
 
7
-
8
7
  /**
9
- * Represents an element component in the PlayCanvas engine.
8
+ * The ElementComponentElement interface provides properties and methods for manipulating
9
+ * `<pc-element>` elements. The ElementComponentElement interface also inherits the properties and
10
+ * methods of the {@link HTMLElement} interface.
10
11
  *
11
12
  * @category Components
12
13
  */
@@ -33,6 +34,7 @@ class ElementComponentElement extends ComponentElement {
33
34
 
34
35
  private _wrapLines: boolean = false;
35
36
 
37
+ /** @ignore */
36
38
  constructor() {
37
39
  super('element');
38
40
  }
@@ -4,13 +4,16 @@ import { ComponentElement } from './component';
4
4
  import { AssetElement } from '../asset';
5
5
 
6
6
  /**
7
- * Represents a gsplat component in the PlayCanvas engine.
7
+ * The GSplatComponentElement interface provides properties and methods for manipulating
8
+ * `<pc-splat>` elements. The GSplatComponentElement interface also inherits the properties and
9
+ * methods of the {@link HTMLElement} interface.
8
10
  *
9
11
  * @category Components
10
12
  */
11
13
  class GSplatComponentElement extends ComponentElement {
12
14
  private _asset: string = '';
13
15
 
16
+ /** @ignore */
14
17
  constructor() {
15
18
  super('gsplat');
16
19
  }
@@ -1,10 +1,24 @@
1
- import { Color, LightComponent } from 'playcanvas';
1
+ import { Color, LightComponent, SHADOW_PCF1_16F, SHADOW_PCF1_32F, SHADOW_PCF3_16F, SHADOW_PCF3_32F, SHADOW_PCF5_16F, SHADOW_PCF5_32F, SHADOW_PCSS_32F, SHADOW_VSM_16F, SHADOW_VSM_32F } from 'playcanvas';
2
2
 
3
3
  import { ComponentElement } from './component';
4
4
  import { parseColor } from '../utils';
5
5
 
6
+ const shadowTypes = new Map([
7
+ ['pcf1-16f', SHADOW_PCF1_16F],
8
+ ['pcf1-32f', SHADOW_PCF1_32F],
9
+ ['pcf3-16f', SHADOW_PCF3_16F],
10
+ ['pcf3-32f', SHADOW_PCF3_32F],
11
+ ['pcf5-16f', SHADOW_PCF5_16F],
12
+ ['pcf5-32f', SHADOW_PCF5_32F],
13
+ ['vsm-16f', SHADOW_VSM_16F],
14
+ ['vsm-32f', SHADOW_VSM_32F],
15
+ ['pcss-32f', SHADOW_PCSS_32F]
16
+ ]);
17
+
6
18
  /**
7
- * Represents a light component in the PlayCanvas engine.
19
+ * The LightComponentElement interface provides properties and methods for manipulating
20
+ * `<pc-light>` elements. The LightComponentElement interface also inherits the properties and
21
+ * methods of the {@link HTMLElement} interface.
8
22
  *
9
23
  * @category Components
10
24
  */
@@ -27,13 +41,17 @@ class LightComponentElement extends ComponentElement {
27
41
 
28
42
  private _shadowDistance = 16;
29
43
 
44
+ private _shadowIntensity = 1;
45
+
30
46
  private _shadowResolution = 1024;
31
47
 
48
+ private _shadowType: 'pcf1-16f' | 'pcf1-32f' | 'pcf3-16f' | 'pcf3-32f' | 'pcf5-16f' | 'pcf5-32f' | 'vsm-16f' | 'vsm-32f' | 'pcss-32f' = 'pcf3-32f';
49
+
32
50
  private _type = 'directional';
33
51
 
34
- /**
35
- * Creates a new LightComponentElement.
36
- */
52
+ private _vsmBias = 0.01;
53
+
54
+ /** @ignore */
37
55
  constructor() {
38
56
  super('light');
39
57
  }
@@ -49,8 +67,11 @@ class LightComponentElement extends ComponentElement {
49
67
  range: this._range,
50
68
  shadowBias: this._shadowBias,
51
69
  shadowDistance: this._shadowDistance,
70
+ shadowIntensity: this._shadowIntensity,
52
71
  shadowResolution: this._shadowResolution,
53
- type: this._type
72
+ shadowType: shadowTypes.get(this._shadowType),
73
+ type: this._type,
74
+ vsmBias: this._vsmBias
54
75
  };
55
76
  }
56
77
 
@@ -233,6 +254,25 @@ class LightComponentElement extends ComponentElement {
233
254
  return this._shadowDistance;
234
255
  }
235
256
 
257
+ /**
258
+ * Sets the shadow intensity of the light.
259
+ * @param value - The shadow intensity.
260
+ */
261
+ set shadowIntensity(value: number) {
262
+ this._shadowIntensity = value;
263
+ if (this.component) {
264
+ this.component.shadowIntensity = value;
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Gets the shadow intensity of the light.
270
+ * @returns The shadow intensity.
271
+ */
272
+ get shadowIntensity() {
273
+ return this._shadowIntensity;
274
+ }
275
+
236
276
  /**
237
277
  * Sets the shadow resolution of the light.
238
278
  * @param value - The shadow resolution.
@@ -252,6 +292,35 @@ class LightComponentElement extends ComponentElement {
252
292
  return this._shadowResolution;
253
293
  }
254
294
 
295
+ /**
296
+ * Sets the shadow type of the light.
297
+ * @param value - The shadow type. Can be:
298
+ *
299
+ * - `pcf1-16f` - 1-tap percentage-closer filtered shadow map with 16-bit depth.
300
+ * - `pcf1-32f` - 1-tap percentage-closer filtered shadow map with 32-bit depth.
301
+ * - `pcf3-16f` - 3-tap percentage-closer filtered shadow map with 16-bit depth.
302
+ * - `pcf3-32f` - 3-tap percentage-closer filtered shadow map with 32-bit depth.
303
+ * - `pcf5-16f` - 5-tap percentage-closer filtered shadow map with 16-bit depth.
304
+ * - `pcf5-32f` - 5-tap percentage-closer filtered shadow map with 32-bit depth.
305
+ * - `vsm-16f` - Variance shadow map with 16-bit depth.
306
+ * - `vsm-32f` - Variance shadow map with 32-bit depth.
307
+ * - `pcss-32f` - Percentage-closer soft shadow with 32-bit depth.
308
+ */
309
+ set shadowType(value: 'pcf1-16f' | 'pcf1-32f' | 'pcf3-16f' | 'pcf3-32f' | 'pcf5-16f' | 'pcf5-32f' | 'vsm-16f' | 'vsm-32f' | 'pcss-32f') {
310
+ this._shadowType = value;
311
+ if (this.component) {
312
+ this.component.shadowType = shadowTypes.get(value) ?? SHADOW_PCF3_32F;
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Gets the shadow type of the light.
318
+ * @returns The shadow type.
319
+ */
320
+ get shadowType() {
321
+ return this._shadowType;
322
+ }
323
+
255
324
  /**
256
325
  * Sets the type of the light.
257
326
  * @param value - The type.
@@ -276,6 +345,25 @@ class LightComponentElement extends ComponentElement {
276
345
  return this._type;
277
346
  }
278
347
 
348
+ /**
349
+ * Sets the VSM bias of the light.
350
+ * @param value - The VSM bias.
351
+ */
352
+ set vsmBias(value: number) {
353
+ this._vsmBias = value;
354
+ if (this.component) {
355
+ this.component.vsmBias = value;
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Gets the VSM bias of the light.
361
+ * @returns The VSM bias.
362
+ */
363
+ get vsmBias() {
364
+ return this._vsmBias;
365
+ }
366
+
279
367
  static get observedAttributes() {
280
368
  return [
281
369
  ...super.observedAttributes,
@@ -288,8 +376,11 @@ class LightComponentElement extends ComponentElement {
288
376
  'range',
289
377
  'shadow-bias',
290
378
  'shadow-distance',
379
+ 'shadow-intensity',
291
380
  'shadow-resolution',
292
- 'type'
381
+ 'shadow-type',
382
+ 'type',
383
+ 'vsm-bias'
293
384
  ];
294
385
  }
295
386
 
@@ -327,9 +418,18 @@ class LightComponentElement extends ComponentElement {
327
418
  case 'shadow-resolution':
328
419
  this.shadowResolution = Number(newValue);
329
420
  break;
421
+ case 'shadow-intensity':
422
+ this.shadowIntensity = Number(newValue);
423
+ break;
424
+ case 'shadow-type':
425
+ this.shadowType = newValue as 'pcf1-16f' | 'pcf1-32f' | 'pcf3-16f' | 'pcf3-32f' | 'pcf5-16f' | 'pcf5-32f' | 'vsm-16f' | 'vsm-32f' | 'pcss-32f';
426
+ break;
330
427
  case 'type':
331
428
  this.type = newValue;
332
429
  break;
430
+ case 'vsm-bias':
431
+ this.vsmBias = Number(newValue);
432
+ break;
333
433
  }
334
434
  }
335
435
  }