@playcanvas/web-components 0.1.9 → 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.
package/dist/pwc.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { basisInitialize, WasmModule, Application, Keyboard, Mouse, FILLMODE_FILL_WINDOW, RESOLUTION_AUTO, Color, Quat, Vec2, Vec3, Vec4, Entity, Asset, XRTYPE_VR, PROJECTION_ORTHOGRAPHIC, PROJECTION_PERSPECTIVE, StandardMaterial, SCALEMODE_BLEND, SCALEMODE_NONE, EnvLighting, LAYERID_SKYBOX } from 'playcanvas';
1
+ import { basisInitialize, WasmModule, Application, Keyboard, Mouse, FILLMODE_FILL_WINDOW, RESOLUTION_AUTO, Picker, Color, Quat, Vec2, Vec3, Vec4, Entity, Asset, GAMMA_SRGB, GAMMA_NONE, XRTYPE_VR, PROJECTION_ORTHOGRAPHIC, PROJECTION_PERSPECTIVE, TONEMAP_NONE, TONEMAP_LINEAR, TONEMAP_FILMIC, TONEMAP_HEJL, TONEMAP_ACES, TONEMAP_ACES2, TONEMAP_NEUTRAL, 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, EnvLighting, LAYERID_SKYBOX } from 'playcanvas';
2
2
 
3
3
  /**
4
4
  * Base class for all PlayCanvas Web Components that initialize asynchronously.
@@ -97,6 +97,20 @@ class AppElement extends AsyncElement {
97
97
  this._stencil = true;
98
98
  this._highResolution = false;
99
99
  this._hierarchyReady = false;
100
+ this._picker = null;
101
+ this._hasPointerListeners = {
102
+ pointerenter: false,
103
+ pointerleave: false,
104
+ pointerdown: false,
105
+ pointerup: false,
106
+ pointermove: false
107
+ };
108
+ this._hoveredEntity = null;
109
+ this._pointerHandlers = {
110
+ pointermove: null,
111
+ pointerdown: null,
112
+ pointerup: null
113
+ };
100
114
  /**
101
115
  * The PlayCanvas application instance.
102
116
  */
@@ -126,6 +140,7 @@ class AppElement extends AsyncElement {
126
140
  this.app.graphicsDevice.maxPixelRatio = this._highResolution ? window.devicePixelRatio : 1;
127
141
  this.app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
128
142
  this.app.setCanvasResolution(RESOLUTION_AUTO);
143
+ this._pickerCreate();
129
144
  // Get all pc-asset elements that are direct children of the pc-app element
130
145
  const assetElements = this.querySelectorAll(':scope > pc-asset');
131
146
  Array.from(assetElements).forEach((assetElement) => {
@@ -160,6 +175,7 @@ class AppElement extends AsyncElement {
160
175
  });
161
176
  }
162
177
  disconnectedCallback() {
178
+ this._pickerDestroy();
163
179
  // Clean up the application
164
180
  if (this.app) {
165
181
  this.app.destroy();
@@ -178,6 +194,139 @@ class AppElement extends AsyncElement {
178
194
  this.app.resizeCanvas();
179
195
  }
180
196
  }
197
+ _pickerCreate() {
198
+ const { width, height } = this.app.graphicsDevice;
199
+ this._picker = new Picker(this.app, width, height);
200
+ // Create bound handlers but don't attach them yet
201
+ this._pointerHandlers.pointermove = this._onPointerMove.bind(this);
202
+ this._pointerHandlers.pointerdown = this._onPointerDown.bind(this);
203
+ this._pointerHandlers.pointerup = this._onPointerUp.bind(this);
204
+ // Listen for pointer listeners being added/removed
205
+ ['pointermove', 'pointerdown', 'pointerup', 'pointerenter', 'pointerleave'].forEach((type) => {
206
+ this.addEventListener(`${type}:connect`, () => this._onPointerListenerAdded(type));
207
+ this.addEventListener(`${type}:disconnect`, () => this._onPointerListenerRemoved(type));
208
+ });
209
+ }
210
+ _pickerDestroy() {
211
+ if (this._canvas) {
212
+ Object.entries(this._pointerHandlers).forEach(([type, handler]) => {
213
+ if (handler) {
214
+ this._canvas.removeEventListener(type, handler);
215
+ }
216
+ });
217
+ }
218
+ this._picker = null;
219
+ this._pointerHandlers = {
220
+ pointermove: null,
221
+ pointerdown: null,
222
+ pointerup: null
223
+ };
224
+ }
225
+ _onPointerMove(event) {
226
+ if (!this._picker || !this.app)
227
+ return;
228
+ const camera = this.app.root.findComponent('camera');
229
+ if (!camera)
230
+ return;
231
+ const canvasRect = this._canvas.getBoundingClientRect();
232
+ const x = event.clientX - canvasRect.left;
233
+ const y = event.clientY - canvasRect.top;
234
+ this._picker.prepare(camera, this.app.scene);
235
+ const selection = this._picker.getSelection(x, y);
236
+ // Get the currently hovered entity by walking up the hierarchy
237
+ let newHoverEntity = null;
238
+ if (selection.length > 0) {
239
+ let node = selection[0].node;
240
+ while (node && !newHoverEntity) {
241
+ const entityElement = this.querySelector(`pc-entity[name="${node.name}"]`);
242
+ if (entityElement) {
243
+ newHoverEntity = entityElement;
244
+ }
245
+ node = node.parent;
246
+ }
247
+ }
248
+ // Handle enter/leave events
249
+ if (this._hoveredEntity !== newHoverEntity) {
250
+ if (this._hoveredEntity && this._hoveredEntity.hasListeners('pointerleave')) {
251
+ this._hoveredEntity.dispatchEvent(new PointerEvent('pointerleave', event));
252
+ }
253
+ if (newHoverEntity && newHoverEntity.hasListeners('pointerenter')) {
254
+ newHoverEntity.dispatchEvent(new PointerEvent('pointerenter', event));
255
+ }
256
+ }
257
+ // Update hover state
258
+ this._hoveredEntity = newHoverEntity;
259
+ // Handle pointermove event
260
+ if (newHoverEntity && newHoverEntity.hasListeners('pointermove')) {
261
+ newHoverEntity.dispatchEvent(new PointerEvent('pointermove', event));
262
+ }
263
+ }
264
+ _onPointerDown(event) {
265
+ if (!this._picker || !this.app)
266
+ return;
267
+ const camera = this.app.root.findComponent('camera');
268
+ if (!camera)
269
+ return;
270
+ const canvasRect = this._canvas.getBoundingClientRect();
271
+ const x = event.clientX - canvasRect.left;
272
+ const y = event.clientY - canvasRect.top;
273
+ this._picker.prepare(camera, this.app.scene);
274
+ const selection = this._picker.getSelection(x, y);
275
+ if (selection.length > 0) {
276
+ let node = selection[0].node;
277
+ while (node) {
278
+ const entityElement = this.querySelector(`pc-entity[name="${node.name}"]`);
279
+ if (entityElement && entityElement.hasListeners('pointerdown')) {
280
+ entityElement.dispatchEvent(new PointerEvent('pointerdown', event));
281
+ break;
282
+ }
283
+ node = node.parent;
284
+ }
285
+ }
286
+ }
287
+ _onPointerUp(event) {
288
+ if (!this._picker || !this.app)
289
+ return;
290
+ const camera = this.app.root.findComponent('camera');
291
+ if (!camera)
292
+ return;
293
+ const canvasRect = this._canvas.getBoundingClientRect();
294
+ const x = event.clientX - canvasRect.left;
295
+ const y = event.clientY - canvasRect.top;
296
+ this._picker.prepare(camera, this.app.scene);
297
+ const selection = this._picker.getSelection(x, y);
298
+ if (selection.length > 0) {
299
+ const entityElement = this.querySelector(`pc-entity[name="${selection[0].node.name}"]`);
300
+ if (entityElement && entityElement.hasListeners('pointerup')) {
301
+ entityElement.dispatchEvent(new PointerEvent('pointerup', event));
302
+ }
303
+ }
304
+ }
305
+ _onPointerListenerAdded(type) {
306
+ if (!this._hasPointerListeners[type] && this._canvas) {
307
+ this._hasPointerListeners[type] = true;
308
+ // For enter/leave events, we need the move handler
309
+ const handler = (type === 'pointerenter' || type === 'pointerleave') ?
310
+ this._pointerHandlers.pointermove :
311
+ this._pointerHandlers[type];
312
+ if (handler) {
313
+ this._canvas.addEventListener(type === 'pointerenter' || type === 'pointerleave' ? 'pointermove' : type, handler);
314
+ }
315
+ }
316
+ }
317
+ _onPointerListenerRemoved(type) {
318
+ const hasListeners = Array.from(this.querySelectorAll('pc-entity'))
319
+ .some(entity => entity.hasListeners(type));
320
+ if (!hasListeners && this._canvas) {
321
+ this._hasPointerListeners[type] = false;
322
+ const handler = (type === 'pointerenter' || type === 'pointerleave') ?
323
+ this._pointerHandlers.pointermove :
324
+ this._pointerHandlers[type];
325
+ if (handler) {
326
+ this._canvas.removeEventListener(type === 'pointerenter' || type === 'pointerleave' ? 'pointermove' : type, handler);
327
+ }
328
+ }
329
+ }
181
330
  /**
182
331
  * Sets the alpha flag.
183
332
  * @param value - The alpha flag.
@@ -530,6 +679,10 @@ class EntityElement extends AsyncElement {
530
679
  * The tags of the entity.
531
680
  */
532
681
  this._tags = [];
682
+ /**
683
+ * The pointer event listeners for the entity.
684
+ */
685
+ this._listeners = {};
533
686
  /**
534
687
  * The PlayCanvas entity instance.
535
688
  */
@@ -558,6 +711,30 @@ class EntityElement extends AsyncElement {
558
711
  if (tags) {
559
712
  this.entity.tags.add(tags.split(',').map(tag => tag.trim()));
560
713
  }
714
+ // Handle pointer events
715
+ const pointerEvents = [
716
+ 'onpointerenter',
717
+ 'onpointerleave',
718
+ 'onpointerdown',
719
+ 'onpointerup',
720
+ 'onpointermove'
721
+ ];
722
+ pointerEvents.forEach((eventName) => {
723
+ const handler = this.getAttribute(eventName);
724
+ if (handler) {
725
+ const eventType = eventName.substring(2); // remove 'on' prefix
726
+ const eventHandler = (event) => {
727
+ try {
728
+ /* eslint-disable-next-line no-new-func */
729
+ new Function('event', handler).call(this, event);
730
+ }
731
+ catch (e) {
732
+ console.error('Error in event handler:', e);
733
+ }
734
+ };
735
+ this.addEventListener(eventType, eventHandler);
736
+ }
737
+ });
561
738
  }
562
739
  buildHierarchy(app) {
563
740
  if (!this.entity)
@@ -707,7 +884,19 @@ class EntityElement extends AsyncElement {
707
884
  return this._tags;
708
885
  }
709
886
  static get observedAttributes() {
710
- return ['enabled', 'name', 'position', 'rotation', 'scale', 'tags'];
887
+ return [
888
+ 'enabled',
889
+ 'name',
890
+ 'position',
891
+ 'rotation',
892
+ 'scale',
893
+ 'tags',
894
+ 'onpointerenter',
895
+ 'onpointerleave',
896
+ 'onpointerdown',
897
+ 'onpointerup',
898
+ 'onpointermove'
899
+ ];
711
900
  }
712
901
  attributeChangedCallback(name, _oldValue, newValue) {
713
902
  switch (name) {
@@ -729,7 +918,51 @@ class EntityElement extends AsyncElement {
729
918
  case 'tags':
730
919
  this.tags = newValue.split(',').map(tag => tag.trim());
731
920
  break;
921
+ case 'onpointerenter':
922
+ case 'onpointerleave':
923
+ case 'onpointerdown':
924
+ case 'onpointerup':
925
+ case 'onpointermove':
926
+ if (newValue) {
927
+ const eventName = name.substring(2);
928
+ // Use Function.prototype.bind to avoid new Function
929
+ const handler = (event) => {
930
+ try {
931
+ const handlerStr = this.getAttribute(eventName) || '';
932
+ /* eslint-disable-next-line no-new-func */
933
+ new Function('event', handlerStr).call(this, event);
934
+ }
935
+ catch (e) {
936
+ console.error('Error in event handler:', e);
937
+ }
938
+ };
939
+ this.addEventListener(eventName, handler);
940
+ }
941
+ break;
942
+ }
943
+ }
944
+ addEventListener(type, listener, options) {
945
+ if (!this._listeners[type]) {
946
+ this._listeners[type] = [];
732
947
  }
948
+ this._listeners[type].push(listener);
949
+ super.addEventListener(type, listener, options);
950
+ if (type.startsWith('pointer')) {
951
+ this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
952
+ }
953
+ }
954
+ removeEventListener(type, listener, options) {
955
+ if (this._listeners[type]) {
956
+ this._listeners[type] = this._listeners[type].filter(l => l !== listener);
957
+ }
958
+ super.removeEventListener(type, listener, options);
959
+ if (type.startsWith('pointer')) {
960
+ this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
961
+ }
962
+ }
963
+ hasListeners(type) {
964
+ var _a;
965
+ return Boolean((_a = this._listeners[type]) === null || _a === void 0 ? void 0 : _a.length);
733
966
  }
734
967
  }
735
968
  customElements.define('pc-entity', EntityElement);
@@ -925,6 +1158,15 @@ class ListenerComponentElement extends ComponentElement {
925
1158
  }
926
1159
  customElements.define('pc-listener', ListenerComponentElement);
927
1160
 
1161
+ const tonemaps = new Map([
1162
+ ['none', TONEMAP_NONE],
1163
+ ['linear', TONEMAP_LINEAR],
1164
+ ['filmic', TONEMAP_FILMIC],
1165
+ ['hejl', TONEMAP_HEJL],
1166
+ ['aces', TONEMAP_ACES],
1167
+ ['aces2', TONEMAP_ACES2],
1168
+ ['neutral', TONEMAP_NEUTRAL]
1169
+ ]);
928
1170
  /**
929
1171
  * The CameraComponentElement interface provides properties and methods for manipulating
930
1172
  * `<pc-camera>` elements. The CameraComponentElement interface also inherits the properties and
@@ -945,12 +1187,14 @@ class CameraComponentElement extends ComponentElement {
945
1187
  this._flipFaces = false;
946
1188
  this._fov = 45;
947
1189
  this._frustumCulling = true;
1190
+ this._gamma = 'srgb';
948
1191
  this._nearClip = 0.1;
949
1192
  this._orthographic = false;
950
1193
  this._orthoHeight = 10;
951
1194
  this._priority = 0;
952
1195
  this._rect = new Vec4(0, 0, 1, 1);
953
1196
  this._scissorRect = new Vec4(0, 0, 1, 1);
1197
+ this._tonemap = 'none';
954
1198
  }
955
1199
  getInitialComponentData() {
956
1200
  return {
@@ -963,12 +1207,14 @@ class CameraComponentElement extends ComponentElement {
963
1207
  flipFaces: this._flipFaces,
964
1208
  fov: this._fov,
965
1209
  frustumCulling: this._frustumCulling,
1210
+ gammaCorrection: this._gamma === 'srgb' ? GAMMA_SRGB : GAMMA_NONE,
966
1211
  nearClip: this._nearClip,
967
1212
  orthographic: this._orthographic,
968
1213
  orthoHeight: this._orthoHeight,
969
1214
  priority: this._priority,
970
1215
  rect: this._rect,
971
- scissorRect: this._scissorRect
1216
+ scissorRect: this._scissorRect,
1217
+ toneMapping: tonemaps.get(this._tonemap)
972
1218
  };
973
1219
  }
974
1220
  get xrAvailable() {
@@ -1151,6 +1397,23 @@ class CameraComponentElement extends ComponentElement {
1151
1397
  get frustumCulling() {
1152
1398
  return this._frustumCulling;
1153
1399
  }
1400
+ /**
1401
+ * Sets the gamma correction of the camera.
1402
+ * @param value - The gamma correction.
1403
+ */
1404
+ set gamma(value) {
1405
+ this._gamma = value;
1406
+ if (this.component) {
1407
+ this.component.gammaCorrection = value === 'srgb' ? GAMMA_SRGB : GAMMA_NONE;
1408
+ }
1409
+ }
1410
+ /**
1411
+ * Gets the gamma correction of the camera.
1412
+ * @returns The gamma correction.
1413
+ */
1414
+ get gamma() {
1415
+ return this._gamma;
1416
+ }
1154
1417
  /**
1155
1418
  * Sets the near clip distance of the camera.
1156
1419
  * @param value - The near clip distance.
@@ -1253,6 +1516,24 @@ class CameraComponentElement extends ComponentElement {
1253
1516
  get scissorRect() {
1254
1517
  return this._scissorRect;
1255
1518
  }
1519
+ /**
1520
+ * Sets the tone mapping of the camera.
1521
+ * @param value - The tone mapping.
1522
+ */
1523
+ set tonemap(value) {
1524
+ var _a;
1525
+ this._tonemap = value;
1526
+ if (this.component) {
1527
+ this.component.toneMapping = (_a = tonemaps.get(value)) !== null && _a !== void 0 ? _a : TONEMAP_NONE;
1528
+ }
1529
+ }
1530
+ /**
1531
+ * Gets the tone mapping of the camera.
1532
+ * @returns The tone mapping.
1533
+ */
1534
+ get tonemap() {
1535
+ return this._tonemap;
1536
+ }
1256
1537
  static get observedAttributes() {
1257
1538
  return [
1258
1539
  ...super.observedAttributes,
@@ -1265,12 +1546,14 @@ class CameraComponentElement extends ComponentElement {
1265
1546
  'flip-faces',
1266
1547
  'fov',
1267
1548
  'frustum-culling',
1549
+ 'gamma',
1268
1550
  'near-clip',
1269
1551
  'orthographic',
1270
1552
  'ortho-height',
1271
1553
  'priority',
1272
1554
  'rect',
1273
- 'scissor-rect'
1555
+ 'scissor-rect',
1556
+ 'tonemap'
1274
1557
  ];
1275
1558
  }
1276
1559
  attributeChangedCallback(name, _oldValue, newValue) {
@@ -1303,6 +1586,9 @@ class CameraComponentElement extends ComponentElement {
1303
1586
  case 'frustum-culling':
1304
1587
  this.frustumCulling = newValue !== 'false';
1305
1588
  break;
1589
+ case 'gamma':
1590
+ this.gamma = newValue;
1591
+ break;
1306
1592
  case 'near-clip':
1307
1593
  this.nearClip = parseFloat(newValue);
1308
1594
  break;
@@ -1321,6 +1607,9 @@ class CameraComponentElement extends ComponentElement {
1321
1607
  case 'scissor-rect':
1322
1608
  this.scissorRect = parseVec4(newValue);
1323
1609
  break;
1610
+ case 'tonemap':
1611
+ this.tonemap = newValue;
1612
+ break;
1324
1613
  }
1325
1614
  }
1326
1615
  }
@@ -1734,6 +2023,17 @@ class GSplatComponentElement extends ComponentElement {
1734
2023
  }
1735
2024
  customElements.define('pc-splat', GSplatComponentElement);
1736
2025
 
2026
+ const shadowTypes = new Map([
2027
+ ['pcf1-16f', SHADOW_PCF1_16F],
2028
+ ['pcf1-32f', SHADOW_PCF1_32F],
2029
+ ['pcf3-16f', SHADOW_PCF3_16F],
2030
+ ['pcf3-32f', SHADOW_PCF3_32F],
2031
+ ['pcf5-16f', SHADOW_PCF5_16F],
2032
+ ['pcf5-32f', SHADOW_PCF5_32F],
2033
+ ['vsm-16f', SHADOW_VSM_16F],
2034
+ ['vsm-32f', SHADOW_VSM_32F],
2035
+ ['pcss-32f', SHADOW_PCSS_32F]
2036
+ ]);
1737
2037
  /**
1738
2038
  * The LightComponentElement interface provides properties and methods for manipulating
1739
2039
  * `<pc-light>` elements. The LightComponentElement interface also inherits the properties and
@@ -1754,8 +2054,11 @@ class LightComponentElement extends ComponentElement {
1754
2054
  this._range = 10;
1755
2055
  this._shadowBias = 0.2;
1756
2056
  this._shadowDistance = 16;
2057
+ this._shadowIntensity = 1;
1757
2058
  this._shadowResolution = 1024;
2059
+ this._shadowType = 'pcf3-32f';
1758
2060
  this._type = 'directional';
2061
+ this._vsmBias = 0.01;
1759
2062
  }
1760
2063
  getInitialComponentData() {
1761
2064
  return {
@@ -1768,8 +2071,11 @@ class LightComponentElement extends ComponentElement {
1768
2071
  range: this._range,
1769
2072
  shadowBias: this._shadowBias,
1770
2073
  shadowDistance: this._shadowDistance,
2074
+ shadowIntensity: this._shadowIntensity,
1771
2075
  shadowResolution: this._shadowResolution,
1772
- type: this._type
2076
+ shadowType: shadowTypes.get(this._shadowType),
2077
+ type: this._type,
2078
+ vsmBias: this._vsmBias
1773
2079
  };
1774
2080
  }
1775
2081
  /**
@@ -1932,6 +2238,23 @@ class LightComponentElement extends ComponentElement {
1932
2238
  get shadowDistance() {
1933
2239
  return this._shadowDistance;
1934
2240
  }
2241
+ /**
2242
+ * Sets the shadow intensity of the light.
2243
+ * @param value - The shadow intensity.
2244
+ */
2245
+ set shadowIntensity(value) {
2246
+ this._shadowIntensity = value;
2247
+ if (this.component) {
2248
+ this.component.shadowIntensity = value;
2249
+ }
2250
+ }
2251
+ /**
2252
+ * Gets the shadow intensity of the light.
2253
+ * @returns The shadow intensity.
2254
+ */
2255
+ get shadowIntensity() {
2256
+ return this._shadowIntensity;
2257
+ }
1935
2258
  /**
1936
2259
  * Sets the shadow resolution of the light.
1937
2260
  * @param value - The shadow resolution.
@@ -1949,6 +2272,34 @@ class LightComponentElement extends ComponentElement {
1949
2272
  get shadowResolution() {
1950
2273
  return this._shadowResolution;
1951
2274
  }
2275
+ /**
2276
+ * Sets the shadow type of the light.
2277
+ * @param value - The shadow type. Can be:
2278
+ *
2279
+ * - `pcf1-16f` - 1-tap percentage-closer filtered shadow map with 16-bit depth.
2280
+ * - `pcf1-32f` - 1-tap percentage-closer filtered shadow map with 32-bit depth.
2281
+ * - `pcf3-16f` - 3-tap percentage-closer filtered shadow map with 16-bit depth.
2282
+ * - `pcf3-32f` - 3-tap percentage-closer filtered shadow map with 32-bit depth.
2283
+ * - `pcf5-16f` - 5-tap percentage-closer filtered shadow map with 16-bit depth.
2284
+ * - `pcf5-32f` - 5-tap percentage-closer filtered shadow map with 32-bit depth.
2285
+ * - `vsm-16f` - Variance shadow map with 16-bit depth.
2286
+ * - `vsm-32f` - Variance shadow map with 32-bit depth.
2287
+ * - `pcss-32f` - Percentage-closer soft shadow with 32-bit depth.
2288
+ */
2289
+ set shadowType(value) {
2290
+ var _a;
2291
+ this._shadowType = value;
2292
+ if (this.component) {
2293
+ this.component.shadowType = (_a = shadowTypes.get(value)) !== null && _a !== void 0 ? _a : SHADOW_PCF3_32F;
2294
+ }
2295
+ }
2296
+ /**
2297
+ * Gets the shadow type of the light.
2298
+ * @returns The shadow type.
2299
+ */
2300
+ get shadowType() {
2301
+ return this._shadowType;
2302
+ }
1952
2303
  /**
1953
2304
  * Sets the type of the light.
1954
2305
  * @param value - The type.
@@ -1970,6 +2321,23 @@ class LightComponentElement extends ComponentElement {
1970
2321
  get type() {
1971
2322
  return this._type;
1972
2323
  }
2324
+ /**
2325
+ * Sets the VSM bias of the light.
2326
+ * @param value - The VSM bias.
2327
+ */
2328
+ set vsmBias(value) {
2329
+ this._vsmBias = value;
2330
+ if (this.component) {
2331
+ this.component.vsmBias = value;
2332
+ }
2333
+ }
2334
+ /**
2335
+ * Gets the VSM bias of the light.
2336
+ * @returns The VSM bias.
2337
+ */
2338
+ get vsmBias() {
2339
+ return this._vsmBias;
2340
+ }
1973
2341
  static get observedAttributes() {
1974
2342
  return [
1975
2343
  ...super.observedAttributes,
@@ -1982,8 +2350,11 @@ class LightComponentElement extends ComponentElement {
1982
2350
  'range',
1983
2351
  'shadow-bias',
1984
2352
  'shadow-distance',
2353
+ 'shadow-intensity',
1985
2354
  'shadow-resolution',
1986
- 'type'
2355
+ 'shadow-type',
2356
+ 'type',
2357
+ 'vsm-bias'
1987
2358
  ];
1988
2359
  }
1989
2360
  attributeChangedCallback(name, _oldValue, newValue) {
@@ -2019,9 +2390,18 @@ class LightComponentElement extends ComponentElement {
2019
2390
  case 'shadow-resolution':
2020
2391
  this.shadowResolution = Number(newValue);
2021
2392
  break;
2393
+ case 'shadow-intensity':
2394
+ this.shadowIntensity = Number(newValue);
2395
+ break;
2396
+ case 'shadow-type':
2397
+ this.shadowType = newValue;
2398
+ break;
2022
2399
  case 'type':
2023
2400
  this.type = newValue;
2024
2401
  break;
2402
+ case 'vsm-bias':
2403
+ this.vsmBias = Number(newValue);
2404
+ break;
2025
2405
  }
2026
2406
  }
2027
2407
  }
@@ -2044,8 +2424,8 @@ class MaterialElement extends HTMLElement {
2044
2424
  }
2045
2425
  createMaterial() {
2046
2426
  this.material = new StandardMaterial();
2047
- this.material.glossInvert = true;
2048
- this.material.useMetalness = true;
2427
+ this.material.glossInvert = false;
2428
+ this.material.useMetalness = false;
2049
2429
  this.material.diffuse = this._diffuse;
2050
2430
  this.diffuseMap = this._diffuseMap;
2051
2431
  this.metalnessMap = this._metalnessMap;
@@ -2578,9 +2958,6 @@ class ScreenComponentElement extends ComponentElement {
2578
2958
  }
2579
2959
  customElements.define('pc-screen', ScreenComponentElement);
2580
2960
 
2581
- const tmpV2 = new Vec2();
2582
- const tmpV3 = new Vec3();
2583
- const tmpV4 = new Vec4();
2584
2961
  /**
2585
2962
  * The ScriptComponentElement interface provides properties and methods for manipulating
2586
2963
  * `<pc-scripts>` elements. The ScriptComponentElement interface also inherits the properties and
@@ -2624,22 +3001,34 @@ class ScriptComponentElement extends ComponentElement {
2624
3001
  return;
2625
3002
  }
2626
3003
  }
2627
- // Handle vectors
2628
- if (Array.isArray(value)) {
2629
- if (target[key] instanceof Vec2) {
2630
- target[key] = tmpV2.set(value[0], value[1]);
3004
+ // Handle arrays
3005
+ if (value && typeof value === 'object' && Array.isArray(value)) {
3006
+ // If it's an array of objects, recursively apply to each object
3007
+ if (value.length > 0 && typeof value[0] === 'object') {
3008
+ target[key] = value.map((item) => {
3009
+ const obj = {};
3010
+ for (const itemKey in item) {
3011
+ applyValue(obj, itemKey, item[itemKey]);
3012
+ }
3013
+ return obj;
3014
+ });
3015
+ return;
3016
+ }
3017
+ // Handle vectors
3018
+ if (value.length === 2 && typeof value[0] === 'number') {
3019
+ target[key] = new Vec2(value[0], value[1]);
2631
3020
  return;
2632
3021
  }
2633
- if (target[key] instanceof Vec3) {
2634
- target[key] = tmpV3.set(value[0], value[1], value[2]);
3022
+ if (value.length === 3 && typeof value[0] === 'number') {
3023
+ target[key] = new Vec3(value[0], value[1], value[2]);
2635
3024
  return;
2636
3025
  }
2637
- if (target[key] instanceof Vec4) {
2638
- target[key] = tmpV4.set(value[0], value[1], value[2], value[3]);
3026
+ if (value.length === 4 && typeof value[0] === 'number') {
3027
+ target[key] = new Vec4(value[0], value[1], value[2], value[3]);
2639
3028
  return;
2640
3029
  }
2641
3030
  }
2642
- // Handle nested objects
3031
+ // Handle nested objects (non-array)
2643
3032
  if (value && typeof value === 'object' && !Array.isArray(value)) {
2644
3033
  if (!target[key] || typeof target[key] !== 'object') {
2645
3034
  target[key] = {};