@playcanvas/web-components 0.17.0 → 0.19.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.
- package/README.md +5 -3
- package/dist/app.d.cts +28 -3
- package/dist/app.d.ts +28 -3
- package/dist/asset.d.cts +13 -3
- package/dist/asset.d.ts +13 -3
- package/dist/components/anim-clip.d.cts +3 -1
- package/dist/components/anim-clip.d.ts +3 -1
- package/dist/components/anim-component.d.cts +5 -0
- package/dist/components/anim-component.d.ts +5 -0
- package/dist/components/audio-listener-component.d.cts +4 -0
- package/dist/components/audio-listener-component.d.ts +4 -0
- package/dist/components/button-component.d.cts +5 -0
- package/dist/components/button-component.d.ts +5 -0
- package/dist/components/camera-component.d.cts +35 -1
- package/dist/components/camera-component.d.ts +35 -1
- package/dist/components/collision-component.d.cts +4 -0
- package/dist/components/collision-component.d.ts +4 -0
- package/dist/components/element-component.d.cts +4 -0
- package/dist/components/element-component.d.ts +4 -0
- package/dist/components/gsplat-component.d.cts +4 -0
- package/dist/components/gsplat-component.d.ts +4 -0
- package/dist/components/joint-component.d.cts +12 -2
- package/dist/components/joint-component.d.ts +12 -2
- package/dist/components/layout-child-component.d.cts +4 -0
- package/dist/components/layout-child-component.d.ts +4 -0
- package/dist/components/layout-group-component.d.cts +4 -0
- package/dist/components/layout-group-component.d.ts +4 -0
- package/dist/components/light-component.d.cts +50 -1
- package/dist/components/light-component.d.ts +50 -1
- package/dist/components/particle-system-component.d.cts +4 -0
- package/dist/components/particle-system-component.d.ts +4 -0
- package/dist/components/render-component.d.cts +5 -0
- package/dist/components/render-component.d.ts +5 -0
- package/dist/components/rigid-body-component.d.cts +5 -0
- package/dist/components/rigid-body-component.d.ts +5 -0
- package/dist/components/screen-component.d.cts +4 -0
- package/dist/components/screen-component.d.ts +4 -0
- package/dist/components/script-component.d.cts +3 -0
- package/dist/components/script-component.d.ts +3 -0
- package/dist/components/script-instance.d.cts +4 -0
- package/dist/components/script-instance.d.ts +4 -0
- package/dist/components/scroll-view-component.d.cts +4 -0
- package/dist/components/scroll-view-component.d.ts +4 -0
- package/dist/components/scrollbar-component.d.cts +4 -0
- package/dist/components/scrollbar-component.d.ts +4 -0
- package/dist/components/sound-component.d.cts +4 -0
- package/dist/components/sound-component.d.ts +4 -0
- package/dist/components/sound-slot.d.cts +3 -0
- package/dist/components/sound-slot.d.ts +3 -0
- package/dist/custom-elements.json +338 -7
- package/dist/entity-base.d.cts +5 -4
- package/dist/entity-base.d.ts +5 -4
- package/dist/entity-owner.d.cts +2 -2
- package/dist/entity-owner.d.ts +2 -2
- package/dist/entity.d.cts +13 -2
- package/dist/entity.d.ts +13 -2
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/material.d.cts +18 -7
- package/dist/material.d.ts +18 -7
- package/dist/model.d.cts +11 -0
- package/dist/model.d.ts +11 -0
- package/dist/node.d.cts +12 -1
- package/dist/node.d.ts +12 -1
- package/dist/pwc.cjs +601 -206
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +601 -206
- package/dist/pwc.js.map +1 -1
- package/dist/pwc.min.js +1 -1
- package/dist/pwc.min.js.map +1 -1
- package/dist/pwc.min.mjs +1 -1
- package/dist/pwc.min.mjs.map +1 -1
- package/dist/pwc.mjs +602 -207
- package/dist/pwc.mjs.map +1 -1
- package/dist/scene.d.cts +19 -0
- package/dist/scene.d.ts +19 -0
- package/dist/sky.d.cts +4 -0
- package/dist/sky.d.ts +4 -0
- package/dist/vscode.html-custom-data.json +257 -62
- package/dist/wasm.d.cts +4 -0
- package/dist/wasm.d.ts +4 -0
- package/dist/web-types.json +169 -69
- package/package.json +3 -3
- package/src/app.ts +150 -71
- package/src/asset.ts +13 -4
- package/src/components/anim-clip.ts +3 -1
- package/src/components/anim-component.ts +5 -0
- package/src/components/audio-listener-component.ts +4 -0
- package/src/components/button-component.ts +5 -0
- package/src/components/camera-component.ts +63 -6
- package/src/components/collision-component.ts +4 -0
- package/src/components/element-component.ts +4 -0
- package/src/components/gsplat-component.ts +4 -0
- package/src/components/joint-component.ts +12 -2
- package/src/components/layout-child-component.ts +4 -0
- package/src/components/layout-group-component.ts +4 -0
- package/src/components/light-component.ts +108 -17
- package/src/components/particle-system-component.ts +4 -0
- package/src/components/render-component.ts +5 -0
- package/src/components/rigid-body-component.ts +5 -0
- package/src/components/screen-component.ts +4 -0
- package/src/components/script-component.ts +3 -0
- package/src/components/script-instance.ts +4 -0
- package/src/components/scroll-view-component.ts +4 -0
- package/src/components/scrollbar-component.ts +4 -0
- package/src/components/sound-component.ts +4 -0
- package/src/components/sound-slot.ts +3 -0
- package/src/entity-base.ts +28 -16
- package/src/entity-owner.ts +2 -2
- package/src/entity.ts +16 -4
- package/src/index.ts +12 -1
- package/src/material.ts +19 -8
- package/src/model.ts +14 -2
- package/src/node.ts +15 -3
- package/src/scene.ts +35 -1
- package/src/sky.ts +4 -0
- package/src/wasm.ts +4 -0
package/dist/pwc.cjs
CHANGED
|
@@ -123,6 +123,10 @@ async function whenReady(target) {
|
|
|
123
123
|
* A `<pc-wasm>` without a `name` warns and never becomes ready; a containing `<pc-app>` still
|
|
124
124
|
* boots.
|
|
125
125
|
*
|
|
126
|
+
* @elementSummary The `<pc-wasm>` element loads a WebAssembly module the engine needs before the
|
|
127
|
+
* application starts — `Ammo` for physics, `Basis` or `DracoDecoderModule` for compressed assets.
|
|
128
|
+
* Must be a direct child of `<pc-app>`.
|
|
129
|
+
*
|
|
126
130
|
* @attribute {string} name - The name of the WebAssembly module to configure, e.g. `Basis` or
|
|
127
131
|
* `Ammo`.
|
|
128
132
|
* @attribute {string} glue - The URL of the module's glue script.
|
|
@@ -175,6 +179,136 @@ class WasmElement extends AsyncElement {
|
|
|
175
179
|
}
|
|
176
180
|
customElements.define('pc-wasm', WasmElement);
|
|
177
181
|
|
|
182
|
+
/**
|
|
183
|
+
* The event types the containing `<pc-app>` synthesizes on entity-fronting elements via picking:
|
|
184
|
+
* the `pointer*` events, plus `click` — which concludes a primary-button press and release, and
|
|
185
|
+
* is delivered as a `PointerEvent` exactly as modern browsers deliver native clicks.
|
|
186
|
+
* @internal
|
|
187
|
+
*/
|
|
188
|
+
const SYNTHESIZED_EVENTS = [
|
|
189
|
+
'pointerenter',
|
|
190
|
+
'pointerleave',
|
|
191
|
+
'pointerdown',
|
|
192
|
+
'pointerup',
|
|
193
|
+
'pointermove',
|
|
194
|
+
'click'
|
|
195
|
+
];
|
|
196
|
+
const SYNTHESIZED_EVENT_SET = new Set(SYNTHESIZED_EVENTS);
|
|
197
|
+
/**
|
|
198
|
+
* The attribute names of the inline event handlers (`onpointerdown`, `onclick`, ...), shared by
|
|
199
|
+
* every element that fronts an engine entity. Spread into `observedAttributes` by subclasses.
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
const EVENT_ATTRIBUTES = SYNTHESIZED_EVENTS.map((type) => `on${type}`);
|
|
203
|
+
/**
|
|
204
|
+
* The base class for elements that front an engine {@link Entity}: `<pc-entity>` and
|
|
205
|
+
* `<pc-model>`, which create one, and `<pc-node>`, which binds to one inside a model's
|
|
206
|
+
* instantiated hierarchy. It carries what all of them need — the `entity` contract, registration
|
|
207
|
+
* with the owning application (which joins picked scene nodes back to elements by identity,
|
|
208
|
+
* never by name), and the pointer listener bookkeeping that lets the application lazily attach
|
|
209
|
+
* its canvas handlers.
|
|
210
|
+
*/
|
|
211
|
+
class EntityBaseElement extends AsyncElement {
|
|
212
|
+
_entity = null;
|
|
213
|
+
/**
|
|
214
|
+
* The application element this entity is registered with, cached at registration time so the
|
|
215
|
+
* entity can be unregistered even once this element has left the DOM.
|
|
216
|
+
*/
|
|
217
|
+
_appElement = null;
|
|
218
|
+
/**
|
|
219
|
+
* The event listeners registered on the element, by type.
|
|
220
|
+
*/
|
|
221
|
+
_listeners = {};
|
|
222
|
+
/**
|
|
223
|
+
* The event types for which an inline handler attribute (`onpointerdown`, `onclick`, ...)
|
|
224
|
+
* is currently present.
|
|
225
|
+
*/
|
|
226
|
+
_inlineHandlerTypes = new Set();
|
|
227
|
+
/**
|
|
228
|
+
* The PlayCanvas entity instance. `null` until the element is ready, and again once the
|
|
229
|
+
* entity is gone — await {@link whenReady} or the element's `ready()` promise before
|
|
230
|
+
* accessing it.
|
|
231
|
+
* @returns The entity instance, or `null`.
|
|
232
|
+
*/
|
|
233
|
+
get entity() {
|
|
234
|
+
return this._entity;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Registers `entity` as this element's backing entity with the owning application, which
|
|
238
|
+
* joins engine nodes back to elements by identity (never by name).
|
|
239
|
+
*
|
|
240
|
+
* @param entity - The entity to register.
|
|
241
|
+
*/
|
|
242
|
+
_registerEntity(entity) {
|
|
243
|
+
this._appElement = this.closestApp;
|
|
244
|
+
this._appElement?._registerEntityElement(entity, this);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Removes the registration for `entity`.
|
|
248
|
+
*
|
|
249
|
+
* @param entity - The entity to unregister.
|
|
250
|
+
*/
|
|
251
|
+
_unregisterEntity(entity) {
|
|
252
|
+
this._appElement?._unregisterEntityElement(entity);
|
|
253
|
+
this._appElement = null;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Tracks whether an inline handler attribute is present. The browser itself compiles and
|
|
257
|
+
* runs these attributes — they are standard `GlobalEventHandlers`, so setting one replaces
|
|
258
|
+
* the previous handler and removing it removes the handler, exactly like `onclick` on any
|
|
259
|
+
* HTML element. But because they bypass {@link EventTarget.addEventListener}, the connect/disconnect
|
|
260
|
+
* bookkeeping that lets the application lazily attach its canvas pointer handlers must be
|
|
261
|
+
* kept in sync here.
|
|
262
|
+
*
|
|
263
|
+
* @param name - The attribute name (e.g. 'onpointerdown').
|
|
264
|
+
* @param value - The attribute value, or `null` when the attribute has been removed.
|
|
265
|
+
*/
|
|
266
|
+
_updateInlineHandler(name, value) {
|
|
267
|
+
const type = name.substring(2);
|
|
268
|
+
const had = this._inlineHandlerTypes.has(type);
|
|
269
|
+
const has = value !== null;
|
|
270
|
+
if (has && !had) {
|
|
271
|
+
this._inlineHandlerTypes.add(type);
|
|
272
|
+
this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
|
|
273
|
+
}
|
|
274
|
+
else if (!has && had) {
|
|
275
|
+
this._inlineHandlerTypes.delete(type);
|
|
276
|
+
this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
addEventListener(type, listener, options) {
|
|
280
|
+
if (!this._listeners[type]) {
|
|
281
|
+
this._listeners[type] = [];
|
|
282
|
+
}
|
|
283
|
+
this._listeners[type].push(listener);
|
|
284
|
+
super.addEventListener(type, listener, options);
|
|
285
|
+
if (SYNTHESIZED_EVENT_SET.has(type)) {
|
|
286
|
+
this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
removeEventListener(type, listener, options) {
|
|
290
|
+
if (this._listeners[type]) {
|
|
291
|
+
this._listeners[type] = this._listeners[type].filter((l) => l !== listener);
|
|
292
|
+
}
|
|
293
|
+
super.removeEventListener(type, listener, options);
|
|
294
|
+
if (SYNTHESIZED_EVENT_SET.has(type)) {
|
|
295
|
+
this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Whether the element has a listener for an event type, registered either with
|
|
300
|
+
* {@link EventTarget.addEventListener} or with the matching inline handler attribute. Read by the
|
|
301
|
+
* containing `<pc-app>` element to gate event synthesis.
|
|
302
|
+
*
|
|
303
|
+
* @param type - The event type.
|
|
304
|
+
* @returns Whether a listener is registered.
|
|
305
|
+
* @internal
|
|
306
|
+
*/
|
|
307
|
+
_hasListeners(type) {
|
|
308
|
+
return Boolean(this._listeners[type]?.length) || this._inlineHandlerTypes.has(type);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
178
312
|
/** Covers the 0.2s opacity transition; jsdom never fires transitionend, so removal is timed. */
|
|
179
313
|
const REMOVAL_DELAY_MS = 250;
|
|
180
314
|
/**
|
|
@@ -728,14 +862,51 @@ const getEntity = (ref) => {
|
|
|
728
862
|
return element?.entity ?? null;
|
|
729
863
|
};
|
|
730
864
|
|
|
731
|
-
/** The pointer event types the application synthesizes on `<pc-entity>` elements via picking. */
|
|
732
|
-
const pointerEventTypes = ['pointermove', 'pointerdown', 'pointerup', 'pointerenter', 'pointerleave'];
|
|
733
865
|
/**
|
|
734
866
|
* The event types whose listeners make an element a hover target. Hover resolution walks past
|
|
735
867
|
* elements listening for none of them, so a silent element never swallows an ancestor's
|
|
736
868
|
* enter/leave pair.
|
|
737
869
|
*/
|
|
738
870
|
const hoverEventTypes = ['pointerenter', 'pointerleave', 'pointermove'];
|
|
871
|
+
/**
|
|
872
|
+
* The canvas listeners each synthesized event type is driven by. Enter and leave are derived
|
|
873
|
+
* from move picks. A click is concluded from the down/up pair, with pointercancel discarding a
|
|
874
|
+
* press the browser takes back (for example a touch that becomes a scroll).
|
|
875
|
+
*/
|
|
876
|
+
const canvasEventsFor = {
|
|
877
|
+
pointermove: ['pointermove'],
|
|
878
|
+
pointerenter: ['pointermove'],
|
|
879
|
+
pointerleave: ['pointermove'],
|
|
880
|
+
pointerdown: ['pointerdown'],
|
|
881
|
+
pointerup: ['pointerup'],
|
|
882
|
+
click: ['pointerdown', 'pointerup', 'pointercancel']
|
|
883
|
+
};
|
|
884
|
+
/**
|
|
885
|
+
* How long after a click a further click on the same target still raises the click count that
|
|
886
|
+
* `detail` carries, approximating the platform's double-click time.
|
|
887
|
+
*/
|
|
888
|
+
const CLICK_CHAIN_MS = 500;
|
|
889
|
+
/**
|
|
890
|
+
* Finds the nearest common inclusive ancestor of two picked nodes - the node a click belongs to
|
|
891
|
+
* when the press and the release picked different geometry, exactly as the DOM assigns a click
|
|
892
|
+
* whose down and up have different targets.
|
|
893
|
+
*
|
|
894
|
+
* @param a - The node the press picked, or `null`.
|
|
895
|
+
* @param b - The node the release picked, or `null`.
|
|
896
|
+
* @returns The nearest common inclusive ancestor, or `null` when there is none.
|
|
897
|
+
*/
|
|
898
|
+
const commonAncestor = (a, b) => {
|
|
899
|
+
const ancestors = new Set();
|
|
900
|
+
for (let node = a; node !== null; node = node.parent) {
|
|
901
|
+
ancestors.add(node);
|
|
902
|
+
}
|
|
903
|
+
for (let node = b; node !== null; node = node.parent) {
|
|
904
|
+
if (ancestors.has(node)) {
|
|
905
|
+
return node;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return null;
|
|
909
|
+
};
|
|
739
910
|
/**
|
|
740
911
|
* Gives `pc-app` the sizing contract of a replaced element (`<video>`, `<img>`): a block-level
|
|
741
912
|
* box that the page's CSS sizes, defaulting to the canvas's own 300x150 intrinsic size, with the
|
|
@@ -764,6 +935,10 @@ const ensureBaseStyles = () => {
|
|
|
764
935
|
* tracked live via a ResizeObserver — so the element can be embedded at any size, resized by
|
|
765
936
|
* its container, or made fullscreen with ordinary CSS such as `width: 100vw; height: 100dvh`.
|
|
766
937
|
*
|
|
938
|
+
* @elementSummary The `<pc-app>` element creates a PlayCanvas application and the canvas it renders
|
|
939
|
+
* into, and is the root of every scene. It holds the `<pc-asset>`, `<pc-material>`, `<pc-wasm>` and
|
|
940
|
+
* `<pc-scene>` elements, and the page's CSS sizes it, as it would a `<video>`.
|
|
941
|
+
*
|
|
767
942
|
* @fires {ProgressEvent} progress - Fired while the application preloads its assets. `loaded` and
|
|
768
943
|
* `total` are asset counts, not bytes, and an asset that fails to load still counts as loaded.
|
|
769
944
|
* Fired at least once per boot, and the final event always has `loaded` equal to `total`. Does
|
|
@@ -816,21 +991,30 @@ class AppElement extends AsyncElement {
|
|
|
816
991
|
*/
|
|
817
992
|
_entityElements = new Map();
|
|
818
993
|
_picker = null;
|
|
819
|
-
_hasPointerListeners = {
|
|
820
|
-
pointerenter: false,
|
|
821
|
-
pointerleave: false,
|
|
822
|
-
pointerdown: false,
|
|
823
|
-
pointerup: false,
|
|
824
|
-
pointermove: false
|
|
825
|
-
};
|
|
826
994
|
_hoveredEntity = null;
|
|
827
995
|
// Identifies the newest in-flight hover pick, so out-of-order results can be discarded
|
|
828
996
|
_pickToken = 0;
|
|
829
997
|
_pointerHandlers = {
|
|
830
998
|
pointermove: null,
|
|
831
999
|
pointerdown: null,
|
|
832
|
-
pointerup: null
|
|
1000
|
+
pointerup: null,
|
|
1001
|
+
pointercancel: null
|
|
833
1002
|
};
|
|
1003
|
+
/**
|
|
1004
|
+
* The pick of each pointer's primary-button press, keyed by pointerId and kept while a click
|
|
1005
|
+
* may still conclude it. The promise is stored rather than its result, so a release can
|
|
1006
|
+
* await a press pick that has not resolved yet. Entries are removed by the matching
|
|
1007
|
+
* pointerup or pointercancel, and only ever stored while some element listens for click -
|
|
1008
|
+
* which is also what keeps those two canvas listeners attached.
|
|
1009
|
+
*/
|
|
1010
|
+
_downPicks = new Map();
|
|
1011
|
+
/** Whether any element in the tree listens for click. Maintained by _syncCanvasListeners. */
|
|
1012
|
+
_clickListened = false;
|
|
1013
|
+
/**
|
|
1014
|
+
* The previous click's target, time and count, for chaining successive clicks into the
|
|
1015
|
+
* click count that `detail` carries. `null` until a click has fired.
|
|
1016
|
+
*/
|
|
1017
|
+
_lastClick = null;
|
|
834
1018
|
_app = null;
|
|
835
1019
|
_loadProgress = 0;
|
|
836
1020
|
/**
|
|
@@ -865,12 +1049,12 @@ class AppElement extends AsyncElement {
|
|
|
865
1049
|
*/
|
|
866
1050
|
constructor() {
|
|
867
1051
|
super();
|
|
868
|
-
// Track
|
|
869
|
-
// Registered once here rather than on every boot - the
|
|
870
|
-
// canvas, and a re-booted element must not stack a second set.
|
|
871
|
-
|
|
872
|
-
this.addEventListener(`${type}:connect`, () => this.
|
|
873
|
-
this.addEventListener(`${type}:disconnect`, () => this.
|
|
1052
|
+
// Track listeners for the synthesized events being added to and removed from descendant
|
|
1053
|
+
// entities. Registered once here rather than on every boot - the sync no-ops while there
|
|
1054
|
+
// is no canvas, and a re-booted element must not stack a second set.
|
|
1055
|
+
SYNTHESIZED_EVENTS.forEach((type) => {
|
|
1056
|
+
this.addEventListener(`${type}:connect`, () => this._syncCanvasListeners());
|
|
1057
|
+
this.addEventListener(`${type}:disconnect`, () => this._syncCanvasListeners());
|
|
874
1058
|
});
|
|
875
1059
|
}
|
|
876
1060
|
async connectedCallback() {
|
|
@@ -1188,15 +1372,13 @@ class AppElement extends AsyncElement {
|
|
|
1188
1372
|
this._pointerHandlers.pointermove = listener(this._onPointerMove);
|
|
1189
1373
|
this._pointerHandlers.pointerdown = listener(this._onPointerDown);
|
|
1190
1374
|
this._pointerHandlers.pointerup = listener(this._onPointerUp);
|
|
1191
|
-
|
|
1192
|
-
|
|
1375
|
+
this._pointerHandlers.pointercancel = (event) => {
|
|
1376
|
+
this._downPicks.delete(event.pointerId);
|
|
1377
|
+
};
|
|
1378
|
+
// Attach canvas listeners for element listeners registered before this boot (e.g.
|
|
1379
|
+
// handlers created from inline attributes when their elements were first upgraded, or
|
|
1193
1380
|
// listeners carried over from before a re-boot)
|
|
1194
|
-
|
|
1195
|
-
const anyListeners = Array.from(this.querySelectorAll('pc-entity, pc-model, pc-node')).some((entity) => entity._hasListeners(type));
|
|
1196
|
-
if (anyListeners) {
|
|
1197
|
-
this._onPointerListenerAdded(type);
|
|
1198
|
-
}
|
|
1199
|
-
});
|
|
1381
|
+
this._syncCanvasListeners();
|
|
1200
1382
|
}
|
|
1201
1383
|
_pickerDestroy() {
|
|
1202
1384
|
if (this._canvas) {
|
|
@@ -1211,15 +1393,12 @@ class AppElement extends AsyncElement {
|
|
|
1211
1393
|
this._pointerHandlers = {
|
|
1212
1394
|
pointermove: null,
|
|
1213
1395
|
pointerdown: null,
|
|
1214
|
-
pointerup: null
|
|
1215
|
-
|
|
1216
|
-
this._hasPointerListeners = {
|
|
1217
|
-
pointerenter: false,
|
|
1218
|
-
pointerleave: false,
|
|
1219
|
-
pointerdown: false,
|
|
1220
|
-
pointerup: false,
|
|
1221
|
-
pointermove: false
|
|
1396
|
+
pointerup: null,
|
|
1397
|
+
pointercancel: null
|
|
1222
1398
|
};
|
|
1399
|
+
this._downPicks.clear();
|
|
1400
|
+
this._clickListened = false;
|
|
1401
|
+
this._lastClick = null;
|
|
1223
1402
|
}
|
|
1224
1403
|
/**
|
|
1225
1404
|
* Registers the element that fronts an entity. Called by EntityElement when it creates its
|
|
@@ -1425,7 +1604,15 @@ class AppElement extends AsyncElement {
|
|
|
1425
1604
|
async _onPointerDown(event) {
|
|
1426
1605
|
if (!this._picker || !this.app)
|
|
1427
1606
|
return;
|
|
1428
|
-
const
|
|
1607
|
+
const pick = this._pickNode(event);
|
|
1608
|
+
// A click concludes on the matching pointerup, which needs to know what the press
|
|
1609
|
+
// picked. Primary button only - the only button a click can conclude from - and only
|
|
1610
|
+
// while click is listened for, since it is the click mapping that keeps the pointerup
|
|
1611
|
+
// and pointercancel listeners attached to clean the entry up again.
|
|
1612
|
+
if (this._clickListened && event.button === 0) {
|
|
1613
|
+
this._downPicks.set(event.pointerId, pick);
|
|
1614
|
+
}
|
|
1615
|
+
const node = await pick;
|
|
1429
1616
|
if (!this._picker)
|
|
1430
1617
|
return; // the element disconnected while the pick was in flight
|
|
1431
1618
|
const entityElement = this._elementWithListener(node, 'pointerdown');
|
|
@@ -1436,6 +1623,10 @@ class AppElement extends AsyncElement {
|
|
|
1436
1623
|
async _onPointerUp(event) {
|
|
1437
1624
|
if (!this._picker || !this.app)
|
|
1438
1625
|
return;
|
|
1626
|
+
// The press pick this release may conclude as a click. Claimed synchronously, so the
|
|
1627
|
+
// entry is gone before any other event for this pointer can be handled.
|
|
1628
|
+
const downPick = this._downPicks.get(event.pointerId);
|
|
1629
|
+
this._downPicks.delete(event.pointerId);
|
|
1439
1630
|
const node = await this._pickNode(event);
|
|
1440
1631
|
if (!this._picker)
|
|
1441
1632
|
return; // the element disconnected while the pick was in flight
|
|
@@ -1443,30 +1634,60 @@ class AppElement extends AsyncElement {
|
|
|
1443
1634
|
if (entityElement) {
|
|
1444
1635
|
entityElement.dispatchEvent(new PointerEvent('pointerup', event));
|
|
1445
1636
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1637
|
+
// A click fires where the DOM fires it: at the nearest common inclusive ancestor of
|
|
1638
|
+
// what the press and the release picked, for the primary button only. The press pick
|
|
1639
|
+
// may still be in flight - a quick tap resolves in pick order, not event order.
|
|
1640
|
+
if (!downPick || event.button !== 0)
|
|
1641
|
+
return;
|
|
1642
|
+
const downNode = await downPick;
|
|
1643
|
+
if (!this._picker)
|
|
1644
|
+
return;
|
|
1645
|
+
const clickElement = this._elementWithListener(commonAncestor(downNode, node), 'click');
|
|
1646
|
+
if (clickElement) {
|
|
1647
|
+
const click = new PointerEvent('click', event);
|
|
1648
|
+
// The init above copied pointerup's `detail`, which the Pointer Events spec fixes
|
|
1649
|
+
// at 0 - but click is exempt: its detail is the click count, chained here as the
|
|
1650
|
+
// platform chains it (same target, within the double-click window). Overridden
|
|
1651
|
+
// with defineProperty because an event instance used as an init dict cannot have
|
|
1652
|
+
// single fields replaced.
|
|
1653
|
+
const time = performance.now();
|
|
1654
|
+
const last = this._lastClick;
|
|
1655
|
+
const count = last && last.element === clickElement && time - last.time <= CLICK_CHAIN_MS ? last.count + 1 : 1;
|
|
1656
|
+
this._lastClick = { element: clickElement, time, count };
|
|
1657
|
+
Object.defineProperty(click, 'detail', { value: count });
|
|
1658
|
+
clickElement.dispatchEvent(click);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Attaches exactly the canvas listeners the tree's current element listeners need, and
|
|
1663
|
+
* detaches the rest. Recomputed whenever a listener connects or disconnects anywhere under
|
|
1664
|
+
* this element: several synthesized types can need the same canvas listener (enter, leave
|
|
1665
|
+
* and move all ride the move pick; click rides the down/up pair), so one type's removal
|
|
1666
|
+
* must not detach a listener another type still uses. Re-attaching an attached listener is
|
|
1667
|
+
* a no-op by EventTarget semantics, so no attach state is kept.
|
|
1668
|
+
*/
|
|
1669
|
+
_syncCanvasListeners() {
|
|
1670
|
+
const canvas = this._canvas;
|
|
1671
|
+
if (!canvas)
|
|
1672
|
+
return; // not booted yet: _pickerCreate syncs once the handlers exist
|
|
1673
|
+
const elements = Array.from(this.querySelectorAll('pc-entity, pc-model, pc-node'));
|
|
1674
|
+
const needed = new Set();
|
|
1675
|
+
for (const type of SYNTHESIZED_EVENTS) {
|
|
1676
|
+
if (elements.some((element) => element._hasListeners(type))) {
|
|
1677
|
+
canvasEventsFor[type].forEach((canvasType) => needed.add(canvasType));
|
|
1456
1678
|
}
|
|
1457
1679
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
? this._pointerHandlers.pointermove
|
|
1465
|
-
: this._pointerHandlers[type];
|
|
1466
|
-
if (handler) {
|
|
1467
|
-
this._canvas.removeEventListener(type === 'pointerenter' || type === 'pointerleave' ? 'pointermove' : type, handler);
|
|
1680
|
+
this._clickListened = elements.some((element) => element._hasListeners('click'));
|
|
1681
|
+
Object.entries(this._pointerHandlers).forEach(([canvasType, handler]) => {
|
|
1682
|
+
if (!handler)
|
|
1683
|
+
return;
|
|
1684
|
+
if (needed.has(canvasType)) {
|
|
1685
|
+
canvas.addEventListener(canvasType, handler);
|
|
1468
1686
|
}
|
|
1469
|
-
|
|
1687
|
+
else {
|
|
1688
|
+
canvas.removeEventListener(canvasType, handler);
|
|
1689
|
+
}
|
|
1690
|
+
});
|
|
1470
1691
|
}
|
|
1471
1692
|
/**
|
|
1472
1693
|
* Warns that a graphics option was written too late to have any effect. These options are read
|
|
@@ -1635,126 +1856,6 @@ class AppElement extends AsyncElement {
|
|
|
1635
1856
|
}
|
|
1636
1857
|
customElements.define('pc-app', AppElement);
|
|
1637
1858
|
|
|
1638
|
-
/**
|
|
1639
|
-
* The attribute names of the inline `onpointer*` event handlers, shared by every element that
|
|
1640
|
-
* fronts an engine entity. Spread into `observedAttributes` by subclasses.
|
|
1641
|
-
* @internal
|
|
1642
|
-
*/
|
|
1643
|
-
const POINTER_ATTRIBUTES = [
|
|
1644
|
-
'onpointerenter',
|
|
1645
|
-
'onpointerleave',
|
|
1646
|
-
'onpointerdown',
|
|
1647
|
-
'onpointerup',
|
|
1648
|
-
'onpointermove'
|
|
1649
|
-
];
|
|
1650
|
-
/**
|
|
1651
|
-
* The base class for elements that front an engine {@link Entity}: `<pc-entity>` and
|
|
1652
|
-
* `<pc-model>`, which create one, and `<pc-node>`, which binds to one inside a model's
|
|
1653
|
-
* instantiated hierarchy. It carries what all of them need — the `entity` contract, registration
|
|
1654
|
-
* with the owning application (which joins picked scene nodes back to elements by identity,
|
|
1655
|
-
* never by name), and the pointer listener bookkeeping that lets the application lazily attach
|
|
1656
|
-
* its canvas handlers.
|
|
1657
|
-
*/
|
|
1658
|
-
class EntityBaseElement extends AsyncElement {
|
|
1659
|
-
_entity = null;
|
|
1660
|
-
/**
|
|
1661
|
-
* The application element this entity is registered with, cached at registration time so the
|
|
1662
|
-
* entity can be unregistered even once this element has left the DOM.
|
|
1663
|
-
*/
|
|
1664
|
-
_appElement = null;
|
|
1665
|
-
/**
|
|
1666
|
-
* The pointer event listeners for the entity.
|
|
1667
|
-
*/
|
|
1668
|
-
_listeners = {};
|
|
1669
|
-
/**
|
|
1670
|
-
* The event types for which an inline `onpointer*` attribute is currently present.
|
|
1671
|
-
*/
|
|
1672
|
-
_inlineHandlerTypes = new Set();
|
|
1673
|
-
/**
|
|
1674
|
-
* The PlayCanvas entity instance. `null` until the element is ready, and again once the
|
|
1675
|
-
* entity is gone — await {@link whenReady} or the element's `ready()` promise before
|
|
1676
|
-
* accessing it.
|
|
1677
|
-
* @returns The entity instance, or `null`.
|
|
1678
|
-
*/
|
|
1679
|
-
get entity() {
|
|
1680
|
-
return this._entity;
|
|
1681
|
-
}
|
|
1682
|
-
/**
|
|
1683
|
-
* Registers `entity` as this element's backing entity with the owning application, which
|
|
1684
|
-
* joins engine nodes back to elements by identity (never by name).
|
|
1685
|
-
*
|
|
1686
|
-
* @param entity - The entity to register.
|
|
1687
|
-
*/
|
|
1688
|
-
_registerEntity(entity) {
|
|
1689
|
-
this._appElement = this.closestApp;
|
|
1690
|
-
this._appElement?._registerEntityElement(entity, this);
|
|
1691
|
-
}
|
|
1692
|
-
/**
|
|
1693
|
-
* Removes the registration for `entity`.
|
|
1694
|
-
*
|
|
1695
|
-
* @param entity - The entity to unregister.
|
|
1696
|
-
*/
|
|
1697
|
-
_unregisterEntity(entity) {
|
|
1698
|
-
this._appElement?._unregisterEntityElement(entity);
|
|
1699
|
-
this._appElement = null;
|
|
1700
|
-
}
|
|
1701
|
-
/**
|
|
1702
|
-
* Tracks whether an inline `onpointer*` attribute is present. The browser itself compiles and
|
|
1703
|
-
* runs these attributes — they are standard `GlobalEventHandlers`, so setting one replaces
|
|
1704
|
-
* the previous handler and removing it removes the handler, exactly like `onclick` on any
|
|
1705
|
-
* HTML element. But because they bypass {@link addEventListener}, the connect/disconnect
|
|
1706
|
-
* bookkeeping that lets the application lazily attach its canvas pointer handlers must be
|
|
1707
|
-
* kept in sync here.
|
|
1708
|
-
*
|
|
1709
|
-
* @param name - The attribute name (e.g. 'onpointerdown').
|
|
1710
|
-
* @param value - The attribute value, or `null` when the attribute has been removed.
|
|
1711
|
-
*/
|
|
1712
|
-
_updateInlineHandler(name, value) {
|
|
1713
|
-
const type = name.substring(2);
|
|
1714
|
-
const had = this._inlineHandlerTypes.has(type);
|
|
1715
|
-
const has = value !== null;
|
|
1716
|
-
if (has && !had) {
|
|
1717
|
-
this._inlineHandlerTypes.add(type);
|
|
1718
|
-
this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
|
|
1719
|
-
}
|
|
1720
|
-
else if (!has && had) {
|
|
1721
|
-
this._inlineHandlerTypes.delete(type);
|
|
1722
|
-
this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
addEventListener(type, listener, options) {
|
|
1726
|
-
if (!this._listeners[type]) {
|
|
1727
|
-
this._listeners[type] = [];
|
|
1728
|
-
}
|
|
1729
|
-
this._listeners[type].push(listener);
|
|
1730
|
-
super.addEventListener(type, listener, options);
|
|
1731
|
-
if (type.startsWith('pointer')) {
|
|
1732
|
-
this.dispatchEvent(new CustomEvent(`${type}:connect`, { bubbles: true }));
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
removeEventListener(type, listener, options) {
|
|
1736
|
-
if (this._listeners[type]) {
|
|
1737
|
-
this._listeners[type] = this._listeners[type].filter((l) => l !== listener);
|
|
1738
|
-
}
|
|
1739
|
-
super.removeEventListener(type, listener, options);
|
|
1740
|
-
if (type.startsWith('pointer')) {
|
|
1741
|
-
this.dispatchEvent(new CustomEvent(`${type}:disconnect`, { bubbles: true }));
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
/**
|
|
1745
|
-
* Whether the element has a listener for an event type, registered either with
|
|
1746
|
-
* {@link addEventListener} or with the matching inline `onpointer*` attribute. Read by the
|
|
1747
|
-
* containing `<pc-app>` element to gate pointer event synthesis.
|
|
1748
|
-
*
|
|
1749
|
-
* @param type - The event type.
|
|
1750
|
-
* @returns Whether a listener is registered.
|
|
1751
|
-
* @internal
|
|
1752
|
-
*/
|
|
1753
|
-
_hasListeners(type) {
|
|
1754
|
-
return Boolean(this._listeners[type]?.length) || this._inlineHandlerTypes.has(type);
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
1859
|
/**
|
|
1759
1860
|
* Creates and parents the entities of every descendant entity-owning element of `root`, in two
|
|
1760
1861
|
* passes so that no parent's existence depends on document order. Called wherever a subtree could
|
|
@@ -1812,7 +1913,7 @@ class EntityOwnerElement extends EntityBaseElement {
|
|
|
1812
1913
|
*/
|
|
1813
1914
|
_tags = [];
|
|
1814
1915
|
/**
|
|
1815
|
-
* Whether the hierarchy has been built for this entity — set once
|
|
1916
|
+
* Whether the hierarchy has been built for this entity — set once `_buildHierarchy` has
|
|
1816
1917
|
* parented it. Read by subclasses that gate work on the entity being in the scene graph.
|
|
1817
1918
|
*/
|
|
1818
1919
|
_built = false;
|
|
@@ -1901,7 +2002,7 @@ class EntityOwnerElement extends EntityBaseElement {
|
|
|
1901
2002
|
this._onBuilt();
|
|
1902
2003
|
}
|
|
1903
2004
|
/**
|
|
1904
|
-
* Called by
|
|
2005
|
+
* Called by `_buildHierarchy` once the backing entity has been parented — exactly once
|
|
1905
2006
|
* per build cycle. The default announces readiness, which is what a parented `<pc-entity>`
|
|
1906
2007
|
* means; `<pc-model>` overrides it to start loading content instead, because its readiness
|
|
1907
2008
|
* tracks the content settling rather than the host entering the scene graph.
|
|
@@ -2022,8 +2123,13 @@ class EntityOwnerElement extends EntityBaseElement {
|
|
|
2022
2123
|
*
|
|
2023
2124
|
* The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
|
|
2024
2125
|
* intersects this entity's geometry. They are only generated while the entity has a listener for
|
|
2025
|
-
* them, registered either with {@link addEventListener} or with the matching inline
|
|
2026
|
-
* attribute.
|
|
2126
|
+
* them, registered either with {@link EventTarget.addEventListener} or with the matching inline
|
|
2127
|
+
* attribute (`onpointerdown`, `onclick`, ...).
|
|
2128
|
+
*
|
|
2129
|
+
* @elementSummary The `<pc-entity>` element creates an entity: a named, transformable node of the
|
|
2130
|
+
* scene hierarchy, and the host for component elements such as `<pc-camera>`, `<pc-light>` and
|
|
2131
|
+
* `<pc-render>`. Place it in the `<pc-scene>`, or nest it under another `<pc-entity>`, a
|
|
2132
|
+
* `<pc-model>` or a `<pc-node>` to parent it there.
|
|
2027
2133
|
*
|
|
2028
2134
|
* @attribute {boolean} enabled - The enabled state of the entity.
|
|
2029
2135
|
* @attribute {string} name - The name of the entity.
|
|
@@ -2038,11 +2144,17 @@ class EntityOwnerElement extends EntityBaseElement {
|
|
|
2038
2144
|
* entity.
|
|
2039
2145
|
* @attribute {string} onpointerup - Script to run when a pointer button is released over the
|
|
2040
2146
|
* entity.
|
|
2147
|
+
* @attribute {string} onclick - Script to run when the entity is clicked: a primary pointer
|
|
2148
|
+
* button pressed and then released over it.
|
|
2041
2149
|
* @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the entity.
|
|
2042
2150
|
* @fires {PointerEvent} pointerleave - Fired when the pointer moves off the entity.
|
|
2043
2151
|
* @fires {PointerEvent} pointermove - Fired when the pointer moves over the entity.
|
|
2044
2152
|
* @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the entity.
|
|
2045
2153
|
* @fires {PointerEvent} pointerup - Fired when a pointer button is released over the entity.
|
|
2154
|
+
* @fires {PointerEvent} click - Fired when a primary pointer button is pressed and then released
|
|
2155
|
+
* over the entity. A press and release that picked different entities fires on their nearest
|
|
2156
|
+
* common ancestor instead, as in the DOM. `detail` carries the click count, so a double click
|
|
2157
|
+
* arrives as a click whose `detail` is 2.
|
|
2046
2158
|
*/
|
|
2047
2159
|
class EntityElement extends EntityOwnerElement {
|
|
2048
2160
|
connectedCallback() {
|
|
@@ -2077,7 +2189,7 @@ class EntityElement extends EntityOwnerElement {
|
|
|
2077
2189
|
this._entity?.destroy();
|
|
2078
2190
|
}
|
|
2079
2191
|
static get observedAttributes() {
|
|
2080
|
-
return ['enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...
|
|
2192
|
+
return ['enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...EVENT_ATTRIBUTES];
|
|
2081
2193
|
}
|
|
2082
2194
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
2083
2195
|
switch (name) {
|
|
@@ -2104,6 +2216,7 @@ class EntityElement extends EntityOwnerElement {
|
|
|
2104
2216
|
case 'onpointerdown':
|
|
2105
2217
|
case 'onpointerup':
|
|
2106
2218
|
case 'onpointermove':
|
|
2219
|
+
case 'onclick':
|
|
2107
2220
|
this._updateInlineHandler(name, newValue);
|
|
2108
2221
|
break;
|
|
2109
2222
|
}
|
|
@@ -2405,6 +2518,10 @@ const processBufferView = (gltfBuffer, buffers, continuation) => {
|
|
|
2405
2518
|
* Apart from `lazy` and the texture options, these attributes are read once when the asset is
|
|
2406
2519
|
* created, so changing them later has no effect.
|
|
2407
2520
|
*
|
|
2521
|
+
* @elementSummary The `<pc-asset>` element declares an asset for the application to load — a model,
|
|
2522
|
+
* texture, font, sound, script or JSON file — under an `id` that other elements reference. Must be
|
|
2523
|
+
* a direct child of `<pc-app>`.
|
|
2524
|
+
*
|
|
2408
2525
|
* @attribute {string} id - The identifier used to reference the asset from other elements.
|
|
2409
2526
|
* @attribute {string} src - The URL of the asset to load.
|
|
2410
2527
|
* @attribute {string} type - The asset type. Inferred from the `src` file extension when omitted.
|
|
@@ -3031,6 +3148,11 @@ const formatHierarchy = (root, counts) => {
|
|
|
3031
3148
|
* intersects the model's geometry, exactly as for `<pc-entity>` — a hit on a content node that no
|
|
3032
3149
|
* `pc-node` fronts resolves to this element.
|
|
3033
3150
|
*
|
|
3151
|
+
* @elementSummary The `<pc-model>` element instantiates a 3D model from a container asset
|
|
3152
|
+
* (typically a GLB) beneath an entity of its own, so the element's transform and tags place the
|
|
3153
|
+
* instance in the scene. Its `<pc-node>` children override what the asset authored. Place it in the
|
|
3154
|
+
* `<pc-scene>`, or nest it under a `<pc-entity>`, another `<pc-model>` or a `<pc-node>`.
|
|
3155
|
+
*
|
|
3034
3156
|
* @attribute {boolean} enabled - The enabled state of the model.
|
|
3035
3157
|
* @attribute {string} name - The name of the model.
|
|
3036
3158
|
* @attribute {string} position - The position of the model.
|
|
@@ -3044,11 +3166,17 @@ const formatHierarchy = (root, counts) => {
|
|
|
3044
3166
|
* model.
|
|
3045
3167
|
* @attribute {string} onpointerup - Script to run when a pointer button is released over the
|
|
3046
3168
|
* model.
|
|
3169
|
+
* @attribute {string} onclick - Script to run when the model is clicked: a primary pointer
|
|
3170
|
+
* button pressed and then released over it.
|
|
3047
3171
|
* @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the model.
|
|
3048
3172
|
* @fires {PointerEvent} pointerleave - Fired when the pointer moves off the model.
|
|
3049
3173
|
* @fires {PointerEvent} pointermove - Fired when the pointer moves over the model.
|
|
3050
3174
|
* @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the model.
|
|
3051
3175
|
* @fires {PointerEvent} pointerup - Fired when a pointer button is released over the model.
|
|
3176
|
+
* @fires {PointerEvent} click - Fired when a primary pointer button is pressed and then released
|
|
3177
|
+
* over the model. A press and release that picked different entities fires on their nearest
|
|
3178
|
+
* common ancestor instead, as in the DOM. `detail` carries the click count, so a double click
|
|
3179
|
+
* arrives as a click whose `detail` is 2.
|
|
3052
3180
|
* @fires {Event} load - Fired each time a container asset finishes instantiating, including
|
|
3053
3181
|
* re-instantiation after `asset` changes. Does not bubble — listen on this element, or use a
|
|
3054
3182
|
* capture-phase listener on an ancestor.
|
|
@@ -3297,7 +3425,7 @@ class ModelElement extends EntityOwnerElement {
|
|
|
3297
3425
|
return this._asset;
|
|
3298
3426
|
}
|
|
3299
3427
|
static get observedAttributes() {
|
|
3300
|
-
return ['asset', 'enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...
|
|
3428
|
+
return ['asset', 'enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...EVENT_ATTRIBUTES];
|
|
3301
3429
|
}
|
|
3302
3430
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
3303
3431
|
switch (name) {
|
|
@@ -3327,6 +3455,7 @@ class ModelElement extends EntityOwnerElement {
|
|
|
3327
3455
|
case 'onpointerdown':
|
|
3328
3456
|
case 'onpointerup':
|
|
3329
3457
|
case 'onpointermove':
|
|
3458
|
+
case 'onclick':
|
|
3330
3459
|
this._updateInlineHandler(name, newValue);
|
|
3331
3460
|
break;
|
|
3332
3461
|
}
|
|
@@ -3564,6 +3693,11 @@ class ComponentElement extends AsyncElement {
|
|
|
3564
3693
|
*
|
|
3565
3694
|
* Engine component: {@link AnimComponent} (`anim`).
|
|
3566
3695
|
*
|
|
3696
|
+
* @elementSummary The `<pc-anim>` element plays animation clips over its entity's hierarchy, taken
|
|
3697
|
+
* from `<pc-anim-clip>` children or from the enclosing `<pc-model>`'s own animations. The first
|
|
3698
|
+
* clip plays automatically, and the `clip` attribute switches between them. Must be a child of a
|
|
3699
|
+
* `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
3700
|
+
*
|
|
3567
3701
|
* @category Components
|
|
3568
3702
|
*/
|
|
3569
3703
|
class AnimComponentElement extends ComponentElement {
|
|
@@ -4182,7 +4316,9 @@ customElements.define('pc-anim', AnimComponentElement);
|
|
|
4182
4316
|
* named; in a multi-track source the track named `name` is chosen, falling back to the first
|
|
4183
4317
|
* with a warning. The element becomes ready once its resolved track is assigned.
|
|
4184
4318
|
*
|
|
4185
|
-
* @
|
|
4319
|
+
* @elementSummary The `<pc-anim-clip>` element declares one named animation clip on its parent
|
|
4320
|
+
* `<pc-anim>`, taken from the `asset` it names or, without one, from the enclosing `<pc-model>`'s
|
|
4321
|
+
* own animations. Must be a direct child of `<pc-anim>`.
|
|
4186
4322
|
*/
|
|
4187
4323
|
class AnimClipElement extends AsyncElement {
|
|
4188
4324
|
/**
|
|
@@ -4510,6 +4646,10 @@ customElements.define('pc-anim-clip', AnimClipElement);
|
|
|
4510
4646
|
*
|
|
4511
4647
|
* Engine component: {@link AudioListenerComponent} (`audiolistener`).
|
|
4512
4648
|
*
|
|
4649
|
+
* @elementSummary The `<pc-audio-listener>` element makes its entity the point from which
|
|
4650
|
+
* positional sounds are heard, typically the entity holding the `<pc-camera>`. Must be a child of a
|
|
4651
|
+
* `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
4652
|
+
*
|
|
4513
4653
|
* @category Components
|
|
4514
4654
|
*/
|
|
4515
4655
|
class AudioListenerComponentElement extends ComponentElement {
|
|
@@ -4539,6 +4679,11 @@ const transitionModes = new Map([
|
|
|
4539
4679
|
*
|
|
4540
4680
|
* Engine component: {@link ButtonComponent} (`button`).
|
|
4541
4681
|
*
|
|
4682
|
+
* @elementSummary The `<pc-button>` element makes its entity respond to pointer input, tinting or
|
|
4683
|
+
* swapping its image as the pointer hovers, presses and releases it. The entity also needs a
|
|
4684
|
+
* `<pc-element>` with `use-input` set. Must be a child of a `<pc-entity>`, `<pc-model>` or
|
|
4685
|
+
* `<pc-node>`.
|
|
4686
|
+
*
|
|
4542
4687
|
* @category Components
|
|
4543
4688
|
*/
|
|
4544
4689
|
class ButtonComponentElement extends ComponentElement {
|
|
@@ -4942,13 +5087,18 @@ const tonemaps = new Map([
|
|
|
4942
5087
|
*
|
|
4943
5088
|
* Engine component: {@link CameraComponent} (`camera`).
|
|
4944
5089
|
*
|
|
5090
|
+
* @elementSummary The `<pc-camera>` element renders the scene from its entity's transform, with
|
|
5091
|
+
* attributes for the projection, field of view, clip planes, clear color and tonemapping. Must be a
|
|
5092
|
+
* child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
5093
|
+
*
|
|
4945
5094
|
* @category Components
|
|
4946
5095
|
*/
|
|
4947
5096
|
class CameraComponentElement extends ComponentElement {
|
|
4948
5097
|
_clearColor = new playcanvas.Color(0.75, 0.75, 0.75, 1);
|
|
4949
5098
|
_clearColorBuffer = true;
|
|
5099
|
+
_clearDepth = 1;
|
|
4950
5100
|
_clearDepthBuffer = true;
|
|
4951
|
-
_clearStencilBuffer =
|
|
5101
|
+
_clearStencilBuffer = true;
|
|
4952
5102
|
_cullFaces = true;
|
|
4953
5103
|
_farClip = 1000;
|
|
4954
5104
|
_flipFaces = false;
|
|
@@ -4971,6 +5121,7 @@ class CameraComponentElement extends ComponentElement {
|
|
|
4971
5121
|
return {
|
|
4972
5122
|
clearColor: this._clearColor,
|
|
4973
5123
|
clearColorBuffer: this._clearColorBuffer,
|
|
5124
|
+
clearDepth: this._clearDepth,
|
|
4974
5125
|
clearDepthBuffer: this._clearDepthBuffer,
|
|
4975
5126
|
clearStencilBuffer: this._clearStencilBuffer,
|
|
4976
5127
|
cullFaces: this._cullFaces,
|
|
@@ -4989,9 +5140,31 @@ class CameraComponentElement extends ComponentElement {
|
|
|
4989
5140
|
toneMapping: tonemaps.get(this._tonemap) ?? playcanvas.TONEMAP_NONE
|
|
4990
5141
|
};
|
|
4991
5142
|
}
|
|
4992
|
-
|
|
5143
|
+
/**
|
|
5144
|
+
* Whether immersive AR is available. Independent of {@link vrAvailable}: a device can offer
|
|
5145
|
+
* either mode without the other.
|
|
5146
|
+
* @returns Whether immersive AR is available.
|
|
5147
|
+
*/
|
|
5148
|
+
get arAvailable() {
|
|
5149
|
+
return this._available(playcanvas.XRTYPE_AR);
|
|
5150
|
+
}
|
|
5151
|
+
/**
|
|
5152
|
+
* Whether immersive VR is available. Independent of {@link arAvailable}: a device can offer
|
|
5153
|
+
* either mode without the other.
|
|
5154
|
+
* @returns Whether immersive VR is available.
|
|
5155
|
+
*/
|
|
5156
|
+
get vrAvailable() {
|
|
5157
|
+
return this._available(playcanvas.XRTYPE_VR);
|
|
5158
|
+
}
|
|
5159
|
+
/**
|
|
5160
|
+
* Whether one XR session type is available on this device.
|
|
5161
|
+
*
|
|
5162
|
+
* @param type - The XR session type to test.
|
|
5163
|
+
* @returns Whether that type is available.
|
|
5164
|
+
*/
|
|
5165
|
+
_available(type) {
|
|
4993
5166
|
const xrManager = this.component?.system.app.xr;
|
|
4994
|
-
return xrManager
|
|
5167
|
+
return Boolean(xrManager?.supported && xrManager.isAvailable(type));
|
|
4995
5168
|
}
|
|
4996
5169
|
/**
|
|
4997
5170
|
* Starts the camera in XR mode.
|
|
@@ -4999,11 +5172,13 @@ class CameraComponentElement extends ComponentElement {
|
|
|
4999
5172
|
* @param space - The space to start the camera in.
|
|
5000
5173
|
*/
|
|
5001
5174
|
startXr(type, space) {
|
|
5002
|
-
|
|
5175
|
+
// Gated on the mode being started, not on XR in general: a device that offers only
|
|
5176
|
+
// one of the two would otherwise accept a session it cannot serve
|
|
5177
|
+
if (this.component && this._available(type)) {
|
|
5003
5178
|
this.component.startXr(type, space, {
|
|
5004
5179
|
callback: (err) => {
|
|
5005
5180
|
if (err)
|
|
5006
|
-
console.error(`WebXR
|
|
5181
|
+
console.error(`WebXR ${type} failed to start: ${err.message}`);
|
|
5007
5182
|
}
|
|
5008
5183
|
});
|
|
5009
5184
|
}
|
|
@@ -5057,6 +5232,23 @@ class CameraComponentElement extends ComponentElement {
|
|
|
5057
5232
|
get clearColorBuffer() {
|
|
5058
5233
|
return this._clearColorBuffer;
|
|
5059
5234
|
}
|
|
5235
|
+
/**
|
|
5236
|
+
* Sets the depth value the depth buffer is cleared to. Defaults to 1.
|
|
5237
|
+
* @param value - The clear depth value.
|
|
5238
|
+
*/
|
|
5239
|
+
set clearDepth(value) {
|
|
5240
|
+
this._clearDepth = value;
|
|
5241
|
+
if (this.component) {
|
|
5242
|
+
this.component.clearDepth = value;
|
|
5243
|
+
}
|
|
5244
|
+
}
|
|
5245
|
+
/**
|
|
5246
|
+
* Gets the depth value the depth buffer is cleared to.
|
|
5247
|
+
* @returns The clear depth value.
|
|
5248
|
+
*/
|
|
5249
|
+
get clearDepth() {
|
|
5250
|
+
return this._clearDepth;
|
|
5251
|
+
}
|
|
5060
5252
|
/**
|
|
5061
5253
|
* Sets the clear depth buffer of the camera.
|
|
5062
5254
|
* @param value - The clear depth buffer.
|
|
@@ -5335,6 +5527,7 @@ class CameraComponentElement extends ComponentElement {
|
|
|
5335
5527
|
...super.observedAttributes,
|
|
5336
5528
|
'clear-color',
|
|
5337
5529
|
'clear-color-buffer',
|
|
5530
|
+
'clear-depth',
|
|
5338
5531
|
'clear-depth-buffer',
|
|
5339
5532
|
'clear-stencil-buffer',
|
|
5340
5533
|
'cull-faces',
|
|
@@ -5362,11 +5555,14 @@ class CameraComponentElement extends ComponentElement {
|
|
|
5362
5555
|
case 'clear-color-buffer':
|
|
5363
5556
|
this.clearColorBuffer = parseBool(newValue, true);
|
|
5364
5557
|
break;
|
|
5558
|
+
case 'clear-depth':
|
|
5559
|
+
this.clearDepth = parseNumber(newValue, 1, name);
|
|
5560
|
+
break;
|
|
5365
5561
|
case 'clear-depth-buffer':
|
|
5366
5562
|
this.clearDepthBuffer = parseBool(newValue, true);
|
|
5367
5563
|
break;
|
|
5368
5564
|
case 'clear-stencil-buffer':
|
|
5369
|
-
this.clearStencilBuffer = parseBool(newValue,
|
|
5565
|
+
this.clearStencilBuffer = parseBool(newValue, true);
|
|
5370
5566
|
break;
|
|
5371
5567
|
case 'cull-faces':
|
|
5372
5568
|
this.cullFaces = parseBool(newValue, true);
|
|
@@ -5429,6 +5625,10 @@ customElements.define('pc-camera', CameraComponentElement);
|
|
|
5429
5625
|
*
|
|
5430
5626
|
* Engine component: {@link CollisionComponent} (`collision`).
|
|
5431
5627
|
*
|
|
5628
|
+
* @elementSummary The `<pc-collision>` element gives its entity a collision shape — a box, sphere,
|
|
5629
|
+
* capsule, cone, cylinder or mesh — for the physics simulation to collide against. Pair it with a
|
|
5630
|
+
* `<pc-rigid-body>`. Must be a child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
5631
|
+
*
|
|
5432
5632
|
* @category Components
|
|
5433
5633
|
*/
|
|
5434
5634
|
class CollisionComponentElement extends ComponentElement {
|
|
@@ -5619,6 +5819,10 @@ customElements.define('pc-collision', CollisionComponentElement);
|
|
|
5619
5819
|
*
|
|
5620
5820
|
* Engine component: {@link ElementComponent} (`element`).
|
|
5621
5821
|
*
|
|
5822
|
+
* @elementSummary The `<pc-element>` element gives its entity a 2D UI rectangle inside a
|
|
5823
|
+
* `<pc-screen>` hierarchy, drawing an image, a line of text or nothing (`type="image"`, `"text"` or
|
|
5824
|
+
* `"group"`). Must be a child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
5825
|
+
*
|
|
5622
5826
|
* @category Components
|
|
5623
5827
|
*/
|
|
5624
5828
|
class ElementComponentElement extends ComponentElement {
|
|
@@ -6303,6 +6507,11 @@ customElements.define('pc-element', ElementComponentElement);
|
|
|
6303
6507
|
* a rigid body component; leaving `entity-b` empty constrains `entity-a` to a fixed point in world
|
|
6304
6508
|
* space. The underlying engine component is in alpha, so its API may change.
|
|
6305
6509
|
*
|
|
6510
|
+
* @elementSummary The `<pc-joint>` element constrains two rigid bodies to each other — a hinged
|
|
6511
|
+
* door, a swinging chain, a sliding drawer. Its entity's transform is the joint frame, and
|
|
6512
|
+
* `entity-a` and `entity-b` name the bodies. Must be a child of a `<pc-entity>`, `<pc-model>` or
|
|
6513
|
+
* `<pc-node>`.
|
|
6514
|
+
*
|
|
6306
6515
|
* @fires {CustomEvent} break - Fired when the impulse on the joint exceeds `break-impulse` and the
|
|
6307
6516
|
* constraint breaks. A broken joint no longer constrains its bodies; calling `refreshFrames()` on
|
|
6308
6517
|
* the underlying component re-attaches it. Bubbles and is composed.
|
|
@@ -7173,6 +7382,10 @@ customElements.define('pc-joint', JointComponentElement);
|
|
|
7173
7382
|
*
|
|
7174
7383
|
* Engine component: {@link LayoutChildComponent} (`layoutchild`).
|
|
7175
7384
|
*
|
|
7385
|
+
* @elementSummary The `<pc-layout-child>` element controls how its entity is sized by the
|
|
7386
|
+
* `<pc-layout-group>` above it, through minimum and maximum sizes and fit proportions. Must be a
|
|
7387
|
+
* child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
7388
|
+
*
|
|
7176
7389
|
* @category Components
|
|
7177
7390
|
*/
|
|
7178
7391
|
class LayoutChildComponentElement extends ComponentElement {
|
|
@@ -7385,6 +7598,10 @@ const fittings = new Map([
|
|
|
7385
7598
|
*
|
|
7386
7599
|
* Engine component: {@link LayoutGroupComponent} (`layoutgroup`).
|
|
7387
7600
|
*
|
|
7601
|
+
* @elementSummary The `<pc-layout-group>` element arranges its entity's children in a row or
|
|
7602
|
+
* column, with spacing, padding, alignment and fitting. Must be a child of a `<pc-entity>`,
|
|
7603
|
+
* `<pc-model>` or `<pc-node>`.
|
|
7604
|
+
*
|
|
7388
7605
|
* @category Components
|
|
7389
7606
|
*/
|
|
7390
7607
|
class LayoutGroupComponentElement extends ComponentElement {
|
|
@@ -7645,23 +7862,30 @@ const shadowTypes = new Map([
|
|
|
7645
7862
|
*
|
|
7646
7863
|
* Engine component: {@link LightComponent} (`light`).
|
|
7647
7864
|
*
|
|
7865
|
+
* @elementSummary The `<pc-light>` element lights the scene from its entity — as a directional,
|
|
7866
|
+
* omni or spot light — with attributes for color, intensity, range and shadows. Must be a child of
|
|
7867
|
+
* a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
7868
|
+
*
|
|
7648
7869
|
* @category Components
|
|
7649
7870
|
*/
|
|
7650
7871
|
class LightComponentElement extends ComponentElement {
|
|
7872
|
+
_cascadeBlend = 0;
|
|
7873
|
+
_cascadeDistribution = 0.5;
|
|
7651
7874
|
_castShadows = false;
|
|
7652
7875
|
_color = new playcanvas.Color(1, 1, 1);
|
|
7653
7876
|
_innerConeAngle = 40;
|
|
7654
7877
|
_intensity = 1;
|
|
7655
|
-
_normalOffsetBias = 0
|
|
7878
|
+
_normalOffsetBias = 0;
|
|
7879
|
+
_numCascades = 1;
|
|
7656
7880
|
_outerConeAngle = 45;
|
|
7657
7881
|
_range = 10;
|
|
7658
|
-
_shadowBias = 0.
|
|
7659
|
-
_shadowDistance =
|
|
7882
|
+
_shadowBias = 0.05;
|
|
7883
|
+
_shadowDistance = 40;
|
|
7660
7884
|
_shadowIntensity = 1;
|
|
7661
7885
|
_shadowResolution = 1024;
|
|
7662
7886
|
_shadowType = 'pcf3-32f';
|
|
7663
7887
|
_type = 'directional';
|
|
7664
|
-
_vsmBias = 0.
|
|
7888
|
+
_vsmBias = 0.0025;
|
|
7665
7889
|
_vsmBlurSize = 11;
|
|
7666
7890
|
_penumbraSize = 1;
|
|
7667
7891
|
_penumbraFalloff = 1;
|
|
@@ -7673,11 +7897,14 @@ class LightComponentElement extends ComponentElement {
|
|
|
7673
7897
|
}
|
|
7674
7898
|
getInitialComponentData() {
|
|
7675
7899
|
return {
|
|
7900
|
+
cascadeBlend: this._cascadeBlend,
|
|
7901
|
+
cascadeDistribution: this._cascadeDistribution,
|
|
7676
7902
|
castShadows: this._castShadows,
|
|
7677
7903
|
color: this._color,
|
|
7678
7904
|
innerConeAngle: this._innerConeAngle,
|
|
7679
7905
|
intensity: this._intensity,
|
|
7680
7906
|
normalOffsetBias: this._normalOffsetBias,
|
|
7907
|
+
numCascades: this._numCascades,
|
|
7681
7908
|
outerConeAngle: this._outerConeAngle,
|
|
7682
7909
|
penumbraFalloff: this._penumbraFalloff,
|
|
7683
7910
|
penumbraSize: this._penumbraSize,
|
|
@@ -7688,7 +7915,7 @@ class LightComponentElement extends ComponentElement {
|
|
|
7688
7915
|
shadowIntensity: this._shadowIntensity,
|
|
7689
7916
|
shadowResolution: this._shadowResolution,
|
|
7690
7917
|
shadowSamples: this._shadowSamples,
|
|
7691
|
-
shadowType: shadowTypes.get(this._shadowType),
|
|
7918
|
+
shadowType: shadowTypes.get(this._shadowType) ?? playcanvas.SHADOW_PCF3_32F,
|
|
7692
7919
|
type: this._type,
|
|
7693
7920
|
vsmBias: this._vsmBias,
|
|
7694
7921
|
vsmBlurSize: this._vsmBlurSize
|
|
@@ -7701,6 +7928,47 @@ class LightComponentElement extends ComponentElement {
|
|
|
7701
7928
|
get component() {
|
|
7702
7929
|
return super.component;
|
|
7703
7930
|
}
|
|
7931
|
+
/**
|
|
7932
|
+
* Sets the fraction of each shadow cascade that is blended into the next one, from 0 (no
|
|
7933
|
+
* blending) to 1, which applies only to `directional` lights with `num-cascades` greater than
|
|
7934
|
+
* 1. Defaults to 0.
|
|
7935
|
+
* @param value - The cascade blend factor.
|
|
7936
|
+
*/
|
|
7937
|
+
set cascadeBlend(value) {
|
|
7938
|
+
this._cascadeBlend = value;
|
|
7939
|
+
if (this.component) {
|
|
7940
|
+
this.component.cascadeBlend = value;
|
|
7941
|
+
}
|
|
7942
|
+
}
|
|
7943
|
+
/**
|
|
7944
|
+
* Gets the cascade blend factor of the light, from 0 (no blending) to 1, which applies only to
|
|
7945
|
+
* `directional` lights with `num-cascades` greater than 1.
|
|
7946
|
+
* @returns The cascade blend factor.
|
|
7947
|
+
*/
|
|
7948
|
+
get cascadeBlend() {
|
|
7949
|
+
return this._cascadeBlend;
|
|
7950
|
+
}
|
|
7951
|
+
/**
|
|
7952
|
+
* Sets the distribution of the camera frustum split between shadow cascades, from 0 (linear
|
|
7953
|
+
* split) to 1 (logarithmic split, concentrating shadow resolution near the camera), which
|
|
7954
|
+
* applies only to `directional` lights with `num-cascades` greater than 1. Defaults to 0.5.
|
|
7955
|
+
* @param value - The cascade distribution.
|
|
7956
|
+
*/
|
|
7957
|
+
set cascadeDistribution(value) {
|
|
7958
|
+
this._cascadeDistribution = value;
|
|
7959
|
+
if (this.component) {
|
|
7960
|
+
this.component.cascadeDistribution = value;
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
/**
|
|
7964
|
+
* Gets the cascade distribution of the light, from 0 (linear split) to 1 (logarithmic split,
|
|
7965
|
+
* concentrating shadow resolution near the camera), which applies only to `directional` lights
|
|
7966
|
+
* with `num-cascades` greater than 1.
|
|
7967
|
+
* @returns The cascade distribution.
|
|
7968
|
+
*/
|
|
7969
|
+
get cascadeDistribution() {
|
|
7970
|
+
return this._cascadeDistribution;
|
|
7971
|
+
}
|
|
7704
7972
|
/**
|
|
7705
7973
|
* Sets the cast shadows flag of the light.
|
|
7706
7974
|
* @param value - The cast shadows flag.
|
|
@@ -7786,6 +8054,25 @@ class LightComponentElement extends ComponentElement {
|
|
|
7786
8054
|
get normalOffsetBias() {
|
|
7787
8055
|
return this._normalOffsetBias;
|
|
7788
8056
|
}
|
|
8057
|
+
/**
|
|
8058
|
+
* Sets the number of shadow cascades of the light, an integer from 1 (no cascades) to 4, which
|
|
8059
|
+
* applies only to `directional` lights. Defaults to 1.
|
|
8060
|
+
* @param value - The number of shadow cascades.
|
|
8061
|
+
*/
|
|
8062
|
+
set numCascades(value) {
|
|
8063
|
+
this._numCascades = value;
|
|
8064
|
+
if (this.component) {
|
|
8065
|
+
this.component.numCascades = value;
|
|
8066
|
+
}
|
|
8067
|
+
}
|
|
8068
|
+
/**
|
|
8069
|
+
* Gets the number of shadow cascades of the light, an integer from 1 (no cascades) to 4, which
|
|
8070
|
+
* applies only to `directional` lights.
|
|
8071
|
+
* @returns The number of shadow cascades.
|
|
8072
|
+
*/
|
|
8073
|
+
get numCascades() {
|
|
8074
|
+
return this._numCascades;
|
|
8075
|
+
}
|
|
7789
8076
|
/**
|
|
7790
8077
|
* Sets the outer cone angle of the light.
|
|
7791
8078
|
* @param value - The outer cone angle.
|
|
@@ -8038,11 +8325,14 @@ class LightComponentElement extends ComponentElement {
|
|
|
8038
8325
|
static get observedAttributes() {
|
|
8039
8326
|
return [
|
|
8040
8327
|
...super.observedAttributes,
|
|
8041
|
-
'
|
|
8328
|
+
'cascade-blend',
|
|
8329
|
+
'cascade-distribution',
|
|
8042
8330
|
'cast-shadows',
|
|
8043
|
-
'
|
|
8331
|
+
'color',
|
|
8044
8332
|
'inner-cone-angle',
|
|
8333
|
+
'intensity',
|
|
8045
8334
|
'normal-offset-bias',
|
|
8335
|
+
'num-cascades',
|
|
8046
8336
|
'outer-cone-angle',
|
|
8047
8337
|
'penumbra-falloff',
|
|
8048
8338
|
'penumbra-size',
|
|
@@ -8062,12 +8352,18 @@ class LightComponentElement extends ComponentElement {
|
|
|
8062
8352
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
8063
8353
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
8064
8354
|
switch (name) {
|
|
8065
|
-
case '
|
|
8066
|
-
this.
|
|
8355
|
+
case 'cascade-blend':
|
|
8356
|
+
this.cascadeBlend = parseNumber(newValue, 0, name);
|
|
8357
|
+
break;
|
|
8358
|
+
case 'cascade-distribution':
|
|
8359
|
+
this.cascadeDistribution = parseNumber(newValue, 0.5, name);
|
|
8067
8360
|
break;
|
|
8068
8361
|
case 'cast-shadows':
|
|
8069
8362
|
this.castShadows = parseBool(newValue, false);
|
|
8070
8363
|
break;
|
|
8364
|
+
case 'color':
|
|
8365
|
+
this.color = parseColor(newValue, playcanvas.Color.WHITE, name);
|
|
8366
|
+
break;
|
|
8071
8367
|
case 'inner-cone-angle':
|
|
8072
8368
|
this.innerConeAngle = parseNumber(newValue, 40, name);
|
|
8073
8369
|
break;
|
|
@@ -8075,7 +8371,10 @@ class LightComponentElement extends ComponentElement {
|
|
|
8075
8371
|
this.intensity = parseNumber(newValue, 1, name);
|
|
8076
8372
|
break;
|
|
8077
8373
|
case 'normal-offset-bias':
|
|
8078
|
-
this.normalOffsetBias = parseNumber(newValue, 0
|
|
8374
|
+
this.normalOffsetBias = parseNumber(newValue, 0, name);
|
|
8375
|
+
break;
|
|
8376
|
+
case 'num-cascades':
|
|
8377
|
+
this.numCascades = parseNumber(newValue, 1, name);
|
|
8079
8378
|
break;
|
|
8080
8379
|
case 'outer-cone-angle':
|
|
8081
8380
|
this.outerConeAngle = parseNumber(newValue, 45, name);
|
|
@@ -8090,20 +8389,20 @@ class LightComponentElement extends ComponentElement {
|
|
|
8090
8389
|
this.range = parseNumber(newValue, 10, name);
|
|
8091
8390
|
break;
|
|
8092
8391
|
case 'shadow-bias':
|
|
8093
|
-
this.shadowBias = parseNumber(newValue, 0.
|
|
8094
|
-
break;
|
|
8095
|
-
case 'shadow-distance':
|
|
8096
|
-
this.shadowDistance = parseNumber(newValue, 16, name);
|
|
8392
|
+
this.shadowBias = parseNumber(newValue, 0.05, name);
|
|
8097
8393
|
break;
|
|
8098
8394
|
case 'shadow-blocker-samples':
|
|
8099
8395
|
this.shadowBlockerSamples = parseNumber(newValue, 16, name);
|
|
8100
8396
|
break;
|
|
8101
|
-
case 'shadow-
|
|
8102
|
-
this.
|
|
8397
|
+
case 'shadow-distance':
|
|
8398
|
+
this.shadowDistance = parseNumber(newValue, 40, name);
|
|
8103
8399
|
break;
|
|
8104
8400
|
case 'shadow-intensity':
|
|
8105
8401
|
this.shadowIntensity = parseNumber(newValue, 1, name);
|
|
8106
8402
|
break;
|
|
8403
|
+
case 'shadow-resolution':
|
|
8404
|
+
this.shadowResolution = parseNumber(newValue, 1024, name);
|
|
8405
|
+
break;
|
|
8107
8406
|
case 'shadow-samples':
|
|
8108
8407
|
this.shadowSamples = parseNumber(newValue, 16, name);
|
|
8109
8408
|
break;
|
|
@@ -8114,7 +8413,7 @@ class LightComponentElement extends ComponentElement {
|
|
|
8114
8413
|
this.type = parseEnum(newValue, ['directional', 'omni', 'spot'], 'directional', name);
|
|
8115
8414
|
break;
|
|
8116
8415
|
case 'vsm-bias':
|
|
8117
|
-
this.vsmBias = parseNumber(newValue, 0.
|
|
8416
|
+
this.vsmBias = parseNumber(newValue, 0.0025, name);
|
|
8118
8417
|
break;
|
|
8119
8418
|
case 'vsm-blur-size':
|
|
8120
8419
|
this.vsmBlurSize = parseNumber(newValue, 11, name);
|
|
@@ -8132,6 +8431,10 @@ customElements.define('pc-light', LightComponentElement);
|
|
|
8132
8431
|
*
|
|
8133
8432
|
* Engine component: {@link ParticleSystemComponent} (`particlesystem`).
|
|
8134
8433
|
*
|
|
8434
|
+
* @elementSummary The `<pc-particle-system>` element emits particles from its entity, with
|
|
8435
|
+
* attributes for the emitter's shape, rate, lifetime, textures and blending. Must be a child of a
|
|
8436
|
+
* `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
8437
|
+
*
|
|
8135
8438
|
* @category Components
|
|
8136
8439
|
*/
|
|
8137
8440
|
class ParticleSystemComponentElement extends ComponentElement {
|
|
@@ -8280,6 +8583,8 @@ const occludeSpeculars = new Map([
|
|
|
8280
8583
|
['ao', playcanvas.SPECOCC_AO],
|
|
8281
8584
|
['gloss-dependent', playcanvas.SPECOCC_GLOSSDEPENDENT]
|
|
8282
8585
|
]);
|
|
8586
|
+
// The DITHER_* constants are strings whose values are exactly these names, so a parsed value is
|
|
8587
|
+
// assigned to the material unchanged rather than mapped through a table.
|
|
8283
8588
|
const opacityDithers = ['none', 'bayer8', 'bluenoise', 'ignnoise'];
|
|
8284
8589
|
const colorChannels = ['r', 'g', 'b', 'a', 'rgb'];
|
|
8285
8590
|
const scalarChannels = ['r', 'g', 'b', 'a'];
|
|
@@ -8316,6 +8621,10 @@ const roughnessAliases = ['roughness', 'roughness-map'];
|
|
|
8316
8621
|
* The two aliases are documented here rather than on an accessor, because they resolve to the
|
|
8317
8622
|
* `gloss` properties and would otherwise inherit gloss's description - which reads inverted.
|
|
8318
8623
|
*
|
|
8624
|
+
* @elementSummary The `<pc-material>` element defines a physically based material, which
|
|
8625
|
+
* `<pc-render>` elements apply by naming its `id`. It is metal/rough by default, with `metalness`
|
|
8626
|
+
* starting at 0. Must be a direct child of `<pc-app>`.
|
|
8627
|
+
*
|
|
8319
8628
|
* @attribute {number} roughness - The roughness of the material, from 0 (shiny) to 1 (rough). An
|
|
8320
8629
|
* alias for `gloss` that also inverts it, so do not combine it with the `gloss` attributes.
|
|
8321
8630
|
* @attribute {string} roughness-map - The id of the `pc-asset` to use as the roughness map. An
|
|
@@ -10484,6 +10793,11 @@ customElements.define('pc-material', MaterialElement);
|
|
|
10484
10793
|
*
|
|
10485
10794
|
* Engine component: {@link RenderComponent} (`render`).
|
|
10486
10795
|
*
|
|
10796
|
+
* @elementSummary The `<pc-render>` element renders one of the engine's built-in primitives at its
|
|
10797
|
+
* entity — box, sphere, capsule, cone, cylinder or plane — shaded by the `<pc-material>` its
|
|
10798
|
+
* `material` attribute names. For glTF content, use `<pc-model>` instead. Must be a child of a
|
|
10799
|
+
* `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
10800
|
+
*
|
|
10487
10801
|
* @category Components
|
|
10488
10802
|
*/
|
|
10489
10803
|
class RenderComponentElement extends ComponentElement {
|
|
@@ -10614,6 +10928,11 @@ customElements.define('pc-render', RenderComponentElement);
|
|
|
10614
10928
|
*
|
|
10615
10929
|
* Engine component: {@link RigidBodyComponent} (`rigidbody`).
|
|
10616
10930
|
*
|
|
10931
|
+
* @elementSummary The `<pc-rigid-body>` element hands its entity to the physics simulation, with
|
|
10932
|
+
* attributes for its type, mass, friction and restitution. It needs a sibling `<pc-collision>` for
|
|
10933
|
+
* its shape, and `Ammo` loaded through `<pc-wasm>`. Must be a child of a `<pc-entity>`,
|
|
10934
|
+
* `<pc-model>` or `<pc-node>`.
|
|
10935
|
+
*
|
|
10617
10936
|
* @category Components
|
|
10618
10937
|
*/
|
|
10619
10938
|
class RigidBodyComponentElement extends ComponentElement {
|
|
@@ -10823,6 +11142,10 @@ const scaleModes = new Map([
|
|
|
10823
11142
|
*
|
|
10824
11143
|
* Engine component: {@link ScreenComponent} (`screen`).
|
|
10825
11144
|
*
|
|
11145
|
+
* @elementSummary The `<pc-screen>` element gives its entity a 2D space — in screen space or in the
|
|
11146
|
+
* world — that a hierarchy of `<pc-element>` descendants lays out inside. Must be a child of a
|
|
11147
|
+
* `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
11148
|
+
*
|
|
10826
11149
|
* @category Components
|
|
10827
11150
|
*/
|
|
10828
11151
|
class ScreenComponentElement extends ComponentElement {
|
|
@@ -10977,6 +11300,10 @@ const orientations = new Map([
|
|
|
10977
11300
|
*
|
|
10978
11301
|
* Engine component: {@link ScrollbarComponent} (`scrollbar`).
|
|
10979
11302
|
*
|
|
11303
|
+
* @elementSummary The `<pc-scrollbar>` element gives its entity a draggable handle reporting a
|
|
11304
|
+
* position from 0 to 1, which a `<pc-scroll-view>` references to scroll its content. Must be a
|
|
11305
|
+
* child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
11306
|
+
*
|
|
10980
11307
|
* @category Components
|
|
10981
11308
|
*/
|
|
10982
11309
|
class ScrollbarComponentElement extends ComponentElement {
|
|
@@ -11118,6 +11445,10 @@ const visibilities = new Map([
|
|
|
11118
11445
|
*
|
|
11119
11446
|
* Engine component: {@link ScrollViewComponent} (`scrollview`).
|
|
11120
11447
|
*
|
|
11448
|
+
* @elementSummary The `<pc-scroll-view>` element scrolls a larger content entity within a clipped
|
|
11449
|
+
* viewport at its entity, optionally driven by the `<pc-scrollbar>` elements it references. Must be
|
|
11450
|
+
* a child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
11451
|
+
*
|
|
11121
11452
|
* @category Components
|
|
11122
11453
|
*/
|
|
11123
11454
|
class ScrollViewComponentElement extends ComponentElement {
|
|
@@ -11504,6 +11835,10 @@ customElements.define('pc-scroll-view', ScrollViewComponentElement);
|
|
|
11504
11835
|
* The element becomes ready once its script instance has been created by the parent
|
|
11505
11836
|
* `<pc-script>` element.
|
|
11506
11837
|
*
|
|
11838
|
+
* @elementSummary The `<pc-script-instance>` element attaches one script class, named by `name`, to
|
|
11839
|
+
* the entity of its parent `<pc-script>`. Its other attributes set script attributes of the same
|
|
11840
|
+
* name, and `attributes` takes a JSON object instead. Must be a direct child of `<pc-script>`.
|
|
11841
|
+
*
|
|
11507
11842
|
* @fires {CustomEvent} scriptattributeschange - Fired when the script's attributes change. The
|
|
11508
11843
|
* `detail` carries the new `attributes` object. Bubbles.
|
|
11509
11844
|
* @fires {CustomEvent} scriptenablechange - Fired when the script's enabled state changes. The
|
|
@@ -11848,6 +12183,9 @@ const findCaseMatch = (script, key) => {
|
|
|
11848
12183
|
*
|
|
11849
12184
|
* Engine component: {@link ScriptComponent} (`script`).
|
|
11850
12185
|
*
|
|
12186
|
+
* @elementSummary The `<pc-script>` element holds the `<pc-script-instance>` children that attach
|
|
12187
|
+
* scripts to its entity. Must be a child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
12188
|
+
*
|
|
11851
12189
|
* @category Components
|
|
11852
12190
|
*/
|
|
11853
12191
|
class ScriptComponentElement extends ComponentElement {
|
|
@@ -12297,6 +12635,10 @@ customElements.define('pc-script', ScriptComponentElement);
|
|
|
12297
12635
|
*
|
|
12298
12636
|
* Engine component: {@link SoundComponent} (`sound`).
|
|
12299
12637
|
*
|
|
12638
|
+
* @elementSummary The `<pc-sound>` element holds the `<pc-sound-slot>` children that play sounds at
|
|
12639
|
+
* its entity, along with the positional audio settings they share. Must be a child of a
|
|
12640
|
+
* `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
12641
|
+
*
|
|
12300
12642
|
* @category Components
|
|
12301
12643
|
*/
|
|
12302
12644
|
class SoundComponentElement extends ComponentElement {
|
|
@@ -12493,6 +12835,9 @@ customElements.define('pc-sound', SoundComponentElement);
|
|
|
12493
12835
|
* The SoundSlotElement interface provides properties and methods for manipulating
|
|
12494
12836
|
* `<pc-sound-slot>` elements. The SoundSlotElement interface also inherits the properties and
|
|
12495
12837
|
* methods of the {@link AsyncElement} interface.
|
|
12838
|
+
*
|
|
12839
|
+
* @elementSummary The `<pc-sound-slot>` element declares one named sound on its parent `<pc-sound>`
|
|
12840
|
+
* — its asset, volume, pitch, looping and autoplay. Must be a direct child of `<pc-sound>`.
|
|
12496
12841
|
*/
|
|
12497
12842
|
class SoundSlotElement extends AsyncElement {
|
|
12498
12843
|
_asset = '';
|
|
@@ -12774,6 +13119,10 @@ customElements.define('pc-sound-slot', SoundSlotElement);
|
|
|
12774
13119
|
*
|
|
12775
13120
|
* Engine component: {@link GSplatComponent} (`gsplat`).
|
|
12776
13121
|
*
|
|
13122
|
+
* @elementSummary The `<pc-gsplat>` element renders the 3D Gaussian splats of a `gsplat` asset at
|
|
13123
|
+
* its entity, with attributes for shadow casting and level of detail. Must be a child of a
|
|
13124
|
+
* `<pc-entity>`, `<pc-model>` or `<pc-node>`.
|
|
13125
|
+
*
|
|
12777
13126
|
* @category Components
|
|
12778
13127
|
*/
|
|
12779
13128
|
class GSplatComponentElement extends ComponentElement {
|
|
@@ -13069,6 +13418,11 @@ const levenshtein = (a, b) => {
|
|
|
13069
13418
|
* The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
|
|
13070
13419
|
* intersects the bound node's geometry, exactly as for `<pc-entity>`.
|
|
13071
13420
|
*
|
|
13421
|
+
* @elementSummary The `<pc-node>` element binds to a node inside the hierarchy a `<pc-model>`
|
|
13422
|
+
* instantiated and declares overrides against it: a transform, an enabled state, tags, components
|
|
13423
|
+
* to add, or content to attach. Its `name` is a lookup, never a rename. Must be a descendant of
|
|
13424
|
+
* `<pc-model>`.
|
|
13425
|
+
*
|
|
13072
13426
|
* @attribute {string} name - The name of the node to bind, resolved within the nearest ancestor
|
|
13073
13427
|
* `pc-model` (or `pc-node`) once it has instantiated.
|
|
13074
13428
|
* @attribute {number} index - Which match to bind when `name` matches more than one node,
|
|
@@ -13093,11 +13447,17 @@ const levenshtein = (a, b) => {
|
|
|
13093
13447
|
* node.
|
|
13094
13448
|
* @attribute {string} onpointerup - Script to run when a pointer button is released over the
|
|
13095
13449
|
* node.
|
|
13450
|
+
* @attribute {string} onclick - Script to run when the node is clicked: a primary pointer
|
|
13451
|
+
* button pressed and then released over it.
|
|
13096
13452
|
* @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the node.
|
|
13097
13453
|
* @fires {PointerEvent} pointerleave - Fired when the pointer moves off the node.
|
|
13098
13454
|
* @fires {PointerEvent} pointermove - Fired when the pointer moves over the node.
|
|
13099
13455
|
* @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the node.
|
|
13100
13456
|
* @fires {PointerEvent} pointerup - Fired when a pointer button is released over the node.
|
|
13457
|
+
* @fires {PointerEvent} click - Fired when a primary pointer button is pressed and then released
|
|
13458
|
+
* over the node. A press and release that picked different entities fires on their nearest
|
|
13459
|
+
* common ancestor instead, as in the DOM. `detail` carries the click count, so a double click
|
|
13460
|
+
* arrives as a click whose `detail` is 2.
|
|
13101
13461
|
*/
|
|
13102
13462
|
class NodeElement extends EntityBaseElement {
|
|
13103
13463
|
_name = '';
|
|
@@ -13747,7 +14107,7 @@ class NodeElement extends EntityBaseElement {
|
|
|
13747
14107
|
'rotation',
|
|
13748
14108
|
'scale',
|
|
13749
14109
|
'tags',
|
|
13750
|
-
...
|
|
14110
|
+
...EVENT_ATTRIBUTES
|
|
13751
14111
|
];
|
|
13752
14112
|
}
|
|
13753
14113
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
@@ -13797,6 +14157,7 @@ class NodeElement extends EntityBaseElement {
|
|
|
13797
14157
|
case 'onpointerdown':
|
|
13798
14158
|
case 'onpointerup':
|
|
13799
14159
|
case 'onpointermove':
|
|
14160
|
+
case 'onclick':
|
|
13800
14161
|
this._updateInlineHandler(name, newValue);
|
|
13801
14162
|
break;
|
|
13802
14163
|
}
|
|
@@ -13809,8 +14170,16 @@ customElements.define('pc-node', NodeElement);
|
|
|
13809
14170
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-scene/ | `<pc-scene>`} elements.
|
|
13810
14171
|
* The SceneElement interface also inherits the properties and methods of the
|
|
13811
14172
|
* {@link HTMLElement} interface.
|
|
14173
|
+
*
|
|
14174
|
+
* @elementSummary The `<pc-scene>` element holds the entity hierarchy the application renders,
|
|
14175
|
+
* along with the scene-wide fog, exposure and gravity settings. Must be a direct child of
|
|
14176
|
+
* `<pc-app>`.
|
|
13812
14177
|
*/
|
|
13813
14178
|
class SceneElement extends AsyncElement {
|
|
14179
|
+
/**
|
|
14180
|
+
* The exposure of the scene.
|
|
14181
|
+
*/
|
|
14182
|
+
_exposure = 1;
|
|
13814
14183
|
/**
|
|
13815
14184
|
* The fog type of the scene.
|
|
13816
14185
|
*/
|
|
@@ -13878,6 +14247,7 @@ class SceneElement extends AsyncElement {
|
|
|
13878
14247
|
}
|
|
13879
14248
|
_updateSceneSettings() {
|
|
13880
14249
|
if (this._scene) {
|
|
14250
|
+
this._scene.exposure = this._exposure;
|
|
13881
14251
|
this._scene.fog.type = this._fog;
|
|
13882
14252
|
this._scene.fog.color = this._fogColor;
|
|
13883
14253
|
this._scene.fog.density = this._fogDensity;
|
|
@@ -13896,6 +14266,24 @@ class SceneElement extends AsyncElement {
|
|
|
13896
14266
|
_applyGravity(value) {
|
|
13897
14267
|
this.closestApp?.app?.systems.rigidbody?.gravity.copy(value);
|
|
13898
14268
|
}
|
|
14269
|
+
/**
|
|
14270
|
+
* Sets the exposure of the scene, which tweaks the overall brightness of the rendered image.
|
|
14271
|
+
* Ignored if the scene is using physical units. Defaults to 1.
|
|
14272
|
+
* @param value - The exposure.
|
|
14273
|
+
*/
|
|
14274
|
+
set exposure(value) {
|
|
14275
|
+
this._exposure = value;
|
|
14276
|
+
if (this.scene) {
|
|
14277
|
+
this.scene.exposure = value;
|
|
14278
|
+
}
|
|
14279
|
+
}
|
|
14280
|
+
/**
|
|
14281
|
+
* Gets the exposure of the scene.
|
|
14282
|
+
* @returns The exposure.
|
|
14283
|
+
*/
|
|
14284
|
+
get exposure() {
|
|
14285
|
+
return this._exposure;
|
|
14286
|
+
}
|
|
13899
14287
|
/**
|
|
13900
14288
|
* Sets the fog type of the scene. Can be `none`, `linear`, `exp` or `exp2`. Defaults to
|
|
13901
14289
|
* `none`.
|
|
@@ -14000,10 +14388,13 @@ class SceneElement extends AsyncElement {
|
|
|
14000
14388
|
return this._gravity;
|
|
14001
14389
|
}
|
|
14002
14390
|
static get observedAttributes() {
|
|
14003
|
-
return ['fog', 'fog-color', 'fog-density', 'fog-start', 'fog-end', 'gravity'];
|
|
14391
|
+
return ['exposure', 'fog', 'fog-color', 'fog-density', 'fog-start', 'fog-end', 'gravity'];
|
|
14004
14392
|
}
|
|
14005
14393
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
14006
14394
|
switch (name) {
|
|
14395
|
+
case 'exposure':
|
|
14396
|
+
this.exposure = parseNumber(newValue, 1, name);
|
|
14397
|
+
break;
|
|
14007
14398
|
case 'fog':
|
|
14008
14399
|
this.fog = parseEnum(newValue, ['none', 'linear', 'exp', 'exp2'], 'none', name);
|
|
14009
14400
|
break;
|
|
@@ -14032,6 +14423,10 @@ customElements.define('pc-scene', SceneElement);
|
|
|
14032
14423
|
* The SkyElement interface provides properties and methods for manipulating
|
|
14033
14424
|
* `<pc-sky>` elements. The SkyElement interface also inherits the properties and
|
|
14034
14425
|
* methods of the {@link HTMLElement} interface.
|
|
14426
|
+
*
|
|
14427
|
+
* @elementSummary The `<pc-sky>` element renders a skybox from a texture asset, projected as an
|
|
14428
|
+
* infinite background, a box or a dome, and optionally lights the scene from it. Must be a direct
|
|
14429
|
+
* child of `<pc-scene>`.
|
|
14035
14430
|
*/
|
|
14036
14431
|
class SkyElement extends AsyncElement {
|
|
14037
14432
|
_asset = '';
|