@playcanvas/web-components 0.13.0 → 0.13.1
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/app.d.cts +35 -0
- package/dist/app.d.ts +35 -0
- package/dist/asset.d.cts +7 -1
- package/dist/asset.d.ts +7 -1
- package/dist/custom-elements.json +1 -1
- package/dist/pwc.cjs +144 -51
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +144 -51
- 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 +144 -51
- package/dist/pwc.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +1 -1
- package/dist/web-types.json +2 -2
- package/package.json +1 -1
- package/src/app.ts +92 -19
- package/src/asset.ts +34 -2
- package/src/components/button-component.ts +7 -7
- package/src/components/element-component.ts +7 -7
- package/src/components/gsplat-component.ts +3 -3
- package/src/components/particlesystem-component.ts +7 -8
- package/src/components/script-component.ts +2 -2
- package/src/components/sound-slot.ts +2 -2
- package/src/material.ts +2 -2
- package/src/model.ts +2 -5
- package/src/sky.ts +2 -3
package/dist/app.d.cts
CHANGED
|
@@ -131,10 +131,45 @@ declare class AppElement extends AsyncElement {
|
|
|
131
131
|
* @returns The nearest listening element, or `null`.
|
|
132
132
|
*/
|
|
133
133
|
private _elementWithListener;
|
|
134
|
+
/**
|
|
135
|
+
* Converts a pointer event's client coordinates into drawing-buffer coordinates - the space
|
|
136
|
+
* the pick buffer and the camera viewports are laid out in. When the canvas has no CSS box
|
|
137
|
+
* to map through (jsdom; a hidden canvas receives no pointer events in a browser), the
|
|
138
|
+
* client coordinates are passed through unmapped and `mapped` is false, so callers know the
|
|
139
|
+
* coordinates correspond to no real geometry.
|
|
140
|
+
*
|
|
141
|
+
* @param event - The pointer event to convert.
|
|
142
|
+
* @param canvas - The canvas the event was dispatched on.
|
|
143
|
+
* @returns The buffer-space coordinates, and whether they were actually mapped.
|
|
144
|
+
*/
|
|
134
145
|
private _getPickerCoordinates;
|
|
146
|
+
/**
|
|
147
|
+
* Whether a camera's viewport contains the point. A camera renders into its normalized
|
|
148
|
+
* `rect`, whose origin is the bottom-left of the canvas while buffer coordinates run from
|
|
149
|
+
* the top-left - so the vertical test flips, as the engine's ElementInput flips it for UI
|
|
150
|
+
* input. The right and bottom edges are exclusive: a viewport rasterizes the half-open
|
|
151
|
+
* pixel range [left, right) x [top, bottom), so a coordinate on a shared edge belongs to
|
|
152
|
+
* the viewport whose first pixel it is - never to the one it just left, whose pick buffer
|
|
153
|
+
* holds nothing there.
|
|
154
|
+
*
|
|
155
|
+
* @param camera - The camera to test.
|
|
156
|
+
* @param x - The x coordinate, in buffer space.
|
|
157
|
+
* @param y - The y coordinate, in buffer space.
|
|
158
|
+
* @param canvas - The canvas the coordinates are relative to.
|
|
159
|
+
* @returns Whether the camera's viewport contains the point.
|
|
160
|
+
*/
|
|
161
|
+
private _cameraContains;
|
|
135
162
|
/**
|
|
136
163
|
* Picks the scene under the pointer and returns the graph node that was hit, or `null`.
|
|
137
164
|
*
|
|
165
|
+
* The camera is resolved the way the engine's ElementInput resolves it for UI input:
|
|
166
|
+
* enabled cameras are tried topmost-first (they render in ascending `priority` order),
|
|
167
|
+
* skipping cameras that render to a texture and cameras whose viewport `rect` does not
|
|
168
|
+
* contain the pointer. A camera that picks nothing ends the search if it clears the color
|
|
169
|
+
* buffer - its background visually owns the pixel - and otherwise cedes to the cameras
|
|
170
|
+
* beneath it, so an overlay camera only intercepts picks where it actually drew something.
|
|
171
|
+
* The pick buffer is prepared per camera, so each camera picks from its own layers.
|
|
172
|
+
*
|
|
138
173
|
* The read back is asynchronous because the synchronous {@link Picker.getSelection} is not
|
|
139
174
|
* supported on WebGPU, where it returns an empty selection rather than failing - which
|
|
140
175
|
* silently disabled every `onpointer*` handler once WebGPU became the resolved backend. The
|
package/dist/app.d.ts
CHANGED
|
@@ -131,10 +131,45 @@ declare class AppElement extends AsyncElement {
|
|
|
131
131
|
* @returns The nearest listening element, or `null`.
|
|
132
132
|
*/
|
|
133
133
|
private _elementWithListener;
|
|
134
|
+
/**
|
|
135
|
+
* Converts a pointer event's client coordinates into drawing-buffer coordinates - the space
|
|
136
|
+
* the pick buffer and the camera viewports are laid out in. When the canvas has no CSS box
|
|
137
|
+
* to map through (jsdom; a hidden canvas receives no pointer events in a browser), the
|
|
138
|
+
* client coordinates are passed through unmapped and `mapped` is false, so callers know the
|
|
139
|
+
* coordinates correspond to no real geometry.
|
|
140
|
+
*
|
|
141
|
+
* @param event - The pointer event to convert.
|
|
142
|
+
* @param canvas - The canvas the event was dispatched on.
|
|
143
|
+
* @returns The buffer-space coordinates, and whether they were actually mapped.
|
|
144
|
+
*/
|
|
134
145
|
private _getPickerCoordinates;
|
|
146
|
+
/**
|
|
147
|
+
* Whether a camera's viewport contains the point. A camera renders into its normalized
|
|
148
|
+
* `rect`, whose origin is the bottom-left of the canvas while buffer coordinates run from
|
|
149
|
+
* the top-left - so the vertical test flips, as the engine's ElementInput flips it for UI
|
|
150
|
+
* input. The right and bottom edges are exclusive: a viewport rasterizes the half-open
|
|
151
|
+
* pixel range [left, right) x [top, bottom), so a coordinate on a shared edge belongs to
|
|
152
|
+
* the viewport whose first pixel it is - never to the one it just left, whose pick buffer
|
|
153
|
+
* holds nothing there.
|
|
154
|
+
*
|
|
155
|
+
* @param camera - The camera to test.
|
|
156
|
+
* @param x - The x coordinate, in buffer space.
|
|
157
|
+
* @param y - The y coordinate, in buffer space.
|
|
158
|
+
* @param canvas - The canvas the coordinates are relative to.
|
|
159
|
+
* @returns Whether the camera's viewport contains the point.
|
|
160
|
+
*/
|
|
161
|
+
private _cameraContains;
|
|
135
162
|
/**
|
|
136
163
|
* Picks the scene under the pointer and returns the graph node that was hit, or `null`.
|
|
137
164
|
*
|
|
165
|
+
* The camera is resolved the way the engine's ElementInput resolves it for UI input:
|
|
166
|
+
* enabled cameras are tried topmost-first (they render in ascending `priority` order),
|
|
167
|
+
* skipping cameras that render to a texture and cameras whose viewport `rect` does not
|
|
168
|
+
* contain the pointer. A camera that picks nothing ends the search if it clears the color
|
|
169
|
+
* buffer - its background visually owns the pixel - and otherwise cedes to the cameras
|
|
170
|
+
* beneath it, so an overlay camera only intercepts picks where it actually drew something.
|
|
171
|
+
* The pick buffer is prepared per camera, so each camera picks from its own layers.
|
|
172
|
+
*
|
|
138
173
|
* The read back is asynchronous because the synchronous {@link Picker.getSelection} is not
|
|
139
174
|
* supported on WebGPU, where it returns an empty selection rather than failing - which
|
|
140
175
|
* silently disabled every `onpointer*` handler once WebGPU became the resolved backend. The
|
package/dist/asset.d.cts
CHANGED
|
@@ -16,6 +16,10 @@ type MagFilterMode = 'nearest' | 'linear';
|
|
|
16
16
|
* immediately unless `lazy`. A `pc-asset` must be a direct child of `pc-app` — elements placed
|
|
17
17
|
* elsewhere, or with an unsupported asset type, never become ready.
|
|
18
18
|
*
|
|
19
|
+
* A `lazy` asset loads on first use: the first time any element resolves it by `id` — a model,
|
|
20
|
+
* a material map, a sky, a script `asset:` reference — or when the `lazy` attribute is removed,
|
|
21
|
+
* whichever comes first. Until then it stays registered and unloaded.
|
|
22
|
+
*
|
|
19
23
|
* For `texture` and `textureatlas` assets, the texture options (`address-u`, `address-v`,
|
|
20
24
|
* `min-filter`, `mag-filter`, `anisotropy`, `mipmaps`, `srgb`, `flip-y`) apply when the texture is
|
|
21
25
|
* created and — like `lazy` — are observed: changing one updates a texture that has already
|
|
@@ -146,7 +150,9 @@ declare class AssetElement extends AsyncElement {
|
|
|
146
150
|
*/
|
|
147
151
|
get flipY(): boolean | null;
|
|
148
152
|
/**
|
|
149
|
-
* Sets whether the asset should be loaded lazily.
|
|
153
|
+
* Sets whether the asset should be loaded lazily. A lazy asset is registered without being
|
|
154
|
+
* loaded; it loads on first use - the first time any element resolves it by `id` - or when
|
|
155
|
+
* this flag is cleared on a registered asset, whichever comes first.
|
|
150
156
|
* @param value - The lazy loading flag.
|
|
151
157
|
*/
|
|
152
158
|
set lazy(value: boolean);
|
package/dist/asset.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ type MagFilterMode = 'nearest' | 'linear';
|
|
|
16
16
|
* immediately unless `lazy`. A `pc-asset` must be a direct child of `pc-app` — elements placed
|
|
17
17
|
* elsewhere, or with an unsupported asset type, never become ready.
|
|
18
18
|
*
|
|
19
|
+
* A `lazy` asset loads on first use: the first time any element resolves it by `id` — a model,
|
|
20
|
+
* a material map, a sky, a script `asset:` reference — or when the `lazy` attribute is removed,
|
|
21
|
+
* whichever comes first. Until then it stays registered and unloaded.
|
|
22
|
+
*
|
|
19
23
|
* For `texture` and `textureatlas` assets, the texture options (`address-u`, `address-v`,
|
|
20
24
|
* `min-filter`, `mag-filter`, `anisotropy`, `mipmaps`, `srgb`, `flip-y`) apply when the texture is
|
|
21
25
|
* created and — like `lazy` — are observed: changing one updates a texture that has already
|
|
@@ -146,7 +150,9 @@ declare class AssetElement extends AsyncElement {
|
|
|
146
150
|
*/
|
|
147
151
|
get flipY(): boolean | null;
|
|
148
152
|
/**
|
|
149
|
-
* Sets whether the asset should be loaded lazily.
|
|
153
|
+
* Sets whether the asset should be loaded lazily. A lazy asset is registered without being
|
|
154
|
+
* loaded; it loads on first use - the first time any element resolves it by `id` - or when
|
|
155
|
+
* this flag is cleared on a registered asset, whichever comes first.
|
|
150
156
|
* @param value - The lazy loading flag.
|
|
151
157
|
*/
|
|
152
158
|
set lazy(value: boolean);
|
|
@@ -346,7 +346,7 @@
|
|
|
346
346
|
"declarations": [
|
|
347
347
|
{
|
|
348
348
|
"kind": "class",
|
|
349
|
-
"description": "The AssetElement interface provides properties and methods for manipulating\n[`<pc-asset>`](https://developer.playcanvas.com/user-manual/web-components/tags/pc-asset/) elements.\nThe AssetElement interface also inherits the properties and methods of the\nHTMLElement interface.\n\nThe element becomes ready once the containing application has started and the asset is in the\nstate declared by the markup: loaded for preloaded assets (even if loading failed — check the\nasset's `resource`), or registered and awaiting a load for `lazy` assets. Elements inserted\nwhile the application is running are created and registered on insertion, and begin loading\nimmediately unless `lazy`. A `pc-asset` must be a direct child of `pc-app` — elements placed\nelsewhere, or with an unsupported asset type, never become ready.\n\nFor `texture` and `textureatlas` assets, the texture options (`address-u`, `address-v`,\n`min-filter`, `mag-filter`, `anisotropy`, `mipmaps`, `srgb`, `flip-y`) apply when the texture is\ncreated and — like `lazy` — are observed: changing one updates a texture that has already\nloaded, and removing one restores the engine default. Changing `srgb` or `mipmaps` on a loaded\ntexture recreates the underlying GPU resource, so prefer declaring those up front. Each option\noverrides the matching key in the `data` JSON; options left unset write nothing, leaving the\nengine's per-format defaults in force.\n\nApart from `lazy` and the texture options, these attributes are read once when the asset is\ncreated, so changing them later has no effect.\n",
|
|
349
|
+
"description": "The AssetElement interface provides properties and methods for manipulating\n[`<pc-asset>`](https://developer.playcanvas.com/user-manual/web-components/tags/pc-asset/) elements.\nThe AssetElement interface also inherits the properties and methods of the\nHTMLElement interface.\n\nThe element becomes ready once the containing application has started and the asset is in the\nstate declared by the markup: loaded for preloaded assets (even if loading failed — check the\nasset's `resource`), or registered and awaiting a load for `lazy` assets. Elements inserted\nwhile the application is running are created and registered on insertion, and begin loading\nimmediately unless `lazy`. A `pc-asset` must be a direct child of `pc-app` — elements placed\nelsewhere, or with an unsupported asset type, never become ready.\n\nA `lazy` asset loads on first use: the first time any element resolves it by `id` — a model,\na material map, a sky, a script `asset:` reference — or when the `lazy` attribute is removed,\nwhichever comes first. Until then it stays registered and unloaded.\n\nFor `texture` and `textureatlas` assets, the texture options (`address-u`, `address-v`,\n`min-filter`, `mag-filter`, `anisotropy`, `mipmaps`, `srgb`, `flip-y`) apply when the texture is\ncreated and — like `lazy` — are observed: changing one updates a texture that has already\nloaded, and removing one restores the engine default. Changing `srgb` or `mipmaps` on a loaded\ntexture recreates the underlying GPU resource, so prefer declaring those up front. Each option\noverrides the matching key in the `data` JSON; options left unset write nothing, leaving the\nengine's per-format defaults in force.\n\nApart from `lazy` and the texture options, these attributes are read once when the asset is\ncreated, so changing them later has no effect.\n",
|
|
350
350
|
"name": "AssetElement",
|
|
351
351
|
"members": [
|
|
352
352
|
{
|
package/dist/pwc.cjs
CHANGED
|
@@ -1250,21 +1250,63 @@ class AppElement extends AsyncElement {
|
|
|
1250
1250
|
}
|
|
1251
1251
|
return null;
|
|
1252
1252
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1253
|
+
/**
|
|
1254
|
+
* Converts a pointer event's client coordinates into drawing-buffer coordinates - the space
|
|
1255
|
+
* the pick buffer and the camera viewports are laid out in. When the canvas has no CSS box
|
|
1256
|
+
* to map through (jsdom; a hidden canvas receives no pointer events in a browser), the
|
|
1257
|
+
* client coordinates are passed through unmapped and `mapped` is false, so callers know the
|
|
1258
|
+
* coordinates correspond to no real geometry.
|
|
1259
|
+
*
|
|
1260
|
+
* @param event - The pointer event to convert.
|
|
1261
|
+
* @param canvas - The canvas the event was dispatched on.
|
|
1262
|
+
* @returns The buffer-space coordinates, and whether they were actually mapped.
|
|
1263
|
+
*/
|
|
1264
|
+
_getPickerCoordinates(event, canvas) {
|
|
1265
|
+
const canvasRect = canvas.getBoundingClientRect();
|
|
1266
|
+
if (canvasRect.width === 0 || canvasRect.height === 0) {
|
|
1267
|
+
return { x: event.clientX, y: event.clientY, mapped: false };
|
|
1268
|
+
}
|
|
1269
|
+
const scaleX = canvas.width / canvasRect.width;
|
|
1270
|
+
const scaleY = canvas.height / canvasRect.height;
|
|
1271
|
+
return {
|
|
1272
|
+
x: (event.clientX - canvasRect.left) * scaleX,
|
|
1273
|
+
y: (event.clientY - canvasRect.top) * scaleY,
|
|
1274
|
+
mapped: true
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Whether a camera's viewport contains the point. A camera renders into its normalized
|
|
1279
|
+
* `rect`, whose origin is the bottom-left of the canvas while buffer coordinates run from
|
|
1280
|
+
* the top-left - so the vertical test flips, as the engine's ElementInput flips it for UI
|
|
1281
|
+
* input. The right and bottom edges are exclusive: a viewport rasterizes the half-open
|
|
1282
|
+
* pixel range [left, right) x [top, bottom), so a coordinate on a shared edge belongs to
|
|
1283
|
+
* the viewport whose first pixel it is - never to the one it just left, whose pick buffer
|
|
1284
|
+
* holds nothing there.
|
|
1285
|
+
*
|
|
1286
|
+
* @param camera - The camera to test.
|
|
1287
|
+
* @param x - The x coordinate, in buffer space.
|
|
1288
|
+
* @param y - The y coordinate, in buffer space.
|
|
1289
|
+
* @param canvas - The canvas the coordinates are relative to.
|
|
1290
|
+
* @returns Whether the camera's viewport contains the point.
|
|
1291
|
+
*/
|
|
1292
|
+
_cameraContains(camera, x, y, canvas) {
|
|
1293
|
+
const rect = camera.rect;
|
|
1294
|
+
const left = rect.x * canvas.width;
|
|
1295
|
+
const bottom = (1 - rect.y) * canvas.height;
|
|
1296
|
+
const top = bottom - rect.w * canvas.height;
|
|
1297
|
+
return x >= left && x < left + rect.z * canvas.width && y >= top && y < bottom;
|
|
1264
1298
|
}
|
|
1265
1299
|
/**
|
|
1266
1300
|
* Picks the scene under the pointer and returns the graph node that was hit, or `null`.
|
|
1267
1301
|
*
|
|
1302
|
+
* The camera is resolved the way the engine's ElementInput resolves it for UI input:
|
|
1303
|
+
* enabled cameras are tried topmost-first (they render in ascending `priority` order),
|
|
1304
|
+
* skipping cameras that render to a texture and cameras whose viewport `rect` does not
|
|
1305
|
+
* contain the pointer. A camera that picks nothing ends the search if it clears the color
|
|
1306
|
+
* buffer - its background visually owns the pixel - and otherwise cedes to the cameras
|
|
1307
|
+
* beneath it, so an overlay camera only intercepts picks where it actually drew something.
|
|
1308
|
+
* The pick buffer is prepared per camera, so each camera picks from its own layers.
|
|
1309
|
+
*
|
|
1268
1310
|
* The read back is asynchronous because the synchronous {@link Picker.getSelection} is not
|
|
1269
1311
|
* supported on WebGPU, where it returns an empty selection rather than failing - which
|
|
1270
1312
|
* silently disabled every `onpointer*` handler once WebGPU became the resolved backend. The
|
|
@@ -1274,16 +1316,40 @@ class AppElement extends AsyncElement {
|
|
|
1274
1316
|
* @returns The graph node under the pointer, or `null` if nothing was hit.
|
|
1275
1317
|
*/
|
|
1276
1318
|
async _pickNode(event) {
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
this._picker.prepare(camera, this.app.scene);
|
|
1282
|
-
const selection = await this._picker.getSelectionAsync(x, y);
|
|
1283
|
-
if (selection.length === 0)
|
|
1319
|
+
const app = this.app;
|
|
1320
|
+
const picker = this._picker;
|
|
1321
|
+
const canvas = this._canvas;
|
|
1322
|
+
if (!app || !picker || !canvas)
|
|
1284
1323
|
return null;
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1324
|
+
const { x, y, mapped } = this._getPickerCoordinates(event, canvas);
|
|
1325
|
+
// Walked from the end: the array is sorted by ascending priority, so the last camera
|
|
1326
|
+
// renders last and sits on top. Read through .at() because a pick handler may remove
|
|
1327
|
+
// cameras while an earlier iteration's read back is in flight.
|
|
1328
|
+
const cameras = app.systems.camera?.cameras ?? [];
|
|
1329
|
+
for (let i = cameras.length - 1; i >= 0; i--) {
|
|
1330
|
+
const camera = cameras.at(i);
|
|
1331
|
+
// A camera rendering to a texture is not on the canvas.
|
|
1332
|
+
if (!camera || camera.renderTarget)
|
|
1333
|
+
continue;
|
|
1334
|
+
// Coordinates that could not be mapped cannot be tested for containment.
|
|
1335
|
+
if (mapped && !this._cameraContains(camera, x, y, canvas))
|
|
1336
|
+
continue;
|
|
1337
|
+
picker.prepare(camera, app.scene);
|
|
1338
|
+
const selection = await picker.getSelectionAsync(x, y);
|
|
1339
|
+
// The element may have disconnected while the read back was in flight.
|
|
1340
|
+
if (!this._picker || !this.app)
|
|
1341
|
+
return null;
|
|
1342
|
+
if (selection.length > 0) {
|
|
1343
|
+
const item = selection[0];
|
|
1344
|
+
return item instanceof playcanvas.MeshInstance ? item.node : item.entity;
|
|
1345
|
+
}
|
|
1346
|
+
// Nothing hit. A camera that clears the color buffer paints its background over
|
|
1347
|
+
// everything beneath it, so the miss is final; one that does not is an overlay
|
|
1348
|
+
// that the cameras beneath show through, so they get their turn.
|
|
1349
|
+
if (camera.clearColorBuffer)
|
|
1350
|
+
return null;
|
|
1351
|
+
}
|
|
1352
|
+
return null;
|
|
1287
1353
|
}
|
|
1288
1354
|
async _onPointerMove(event) {
|
|
1289
1355
|
if (!this._picker || !this.app)
|
|
@@ -2227,6 +2293,10 @@ const processBufferView = (gltfBuffer, buffers, continuation) => {
|
|
|
2227
2293
|
* immediately unless `lazy`. A `pc-asset` must be a direct child of `pc-app` — elements placed
|
|
2228
2294
|
* elsewhere, or with an unsupported asset type, never become ready.
|
|
2229
2295
|
*
|
|
2296
|
+
* A `lazy` asset loads on first use: the first time any element resolves it by `id` — a model,
|
|
2297
|
+
* a material map, a sky, a script `asset:` reference — or when the `lazy` attribute is removed,
|
|
2298
|
+
* whichever comes first. Until then it stays registered and unloaded.
|
|
2299
|
+
*
|
|
2230
2300
|
* For `texture` and `textureatlas` assets, the texture options (`address-u`, `address-v`,
|
|
2231
2301
|
* `min-filter`, `mag-filter`, `anisotropy`, `mipmaps`, `srgb`, `flip-y`) apply when the texture is
|
|
2232
2302
|
* created and — like `lazy` — are observed: changing one updates a texture that has already
|
|
@@ -2426,7 +2496,9 @@ class AssetElement extends AsyncElement {
|
|
|
2426
2496
|
if (type === 'sprite') {
|
|
2427
2497
|
data = data ?? {};
|
|
2428
2498
|
// Resolve the referenced texture atlas to its (numeric) asset id. The atlas must be
|
|
2429
|
-
// declared before the sprite so its asset already exists in the registry.
|
|
2499
|
+
// declared before the sprite so its asset already exists in the registry. Resolved
|
|
2500
|
+
// with get, not useAsset: creation-time wiring is not a use, and the engine's
|
|
2501
|
+
// sprite handler loads the atlas when the sprite itself loads.
|
|
2430
2502
|
const atlas = this.getAttribute('atlas') ?? data.textureAtlasAsset;
|
|
2431
2503
|
if (typeof atlas === 'string') {
|
|
2432
2504
|
const atlasAsset = AssetElement.get(atlas);
|
|
@@ -2587,13 +2659,18 @@ class AssetElement extends AsyncElement {
|
|
|
2587
2659
|
return this._flipY;
|
|
2588
2660
|
}
|
|
2589
2661
|
/**
|
|
2590
|
-
* Sets whether the asset should be loaded lazily.
|
|
2662
|
+
* Sets whether the asset should be loaded lazily. A lazy asset is registered without being
|
|
2663
|
+
* loaded; it loads on first use - the first time any element resolves it by `id` - or when
|
|
2664
|
+
* this flag is cleared on a registered asset, whichever comes first.
|
|
2591
2665
|
* @param value - The lazy loading flag.
|
|
2592
2666
|
*/
|
|
2593
2667
|
set lazy(value) {
|
|
2594
2668
|
this._lazy = value;
|
|
2595
2669
|
if (this.asset) {
|
|
2596
2670
|
this.asset.preload = !value;
|
|
2671
|
+
if (!value) {
|
|
2672
|
+
this.asset.registry?.load(this.asset);
|
|
2673
|
+
}
|
|
2597
2674
|
}
|
|
2598
2675
|
}
|
|
2599
2676
|
/**
|
|
@@ -2777,6 +2854,26 @@ class AssetElement extends AsyncElement {
|
|
|
2777
2854
|
}
|
|
2778
2855
|
}
|
|
2779
2856
|
customElements.define('pc-asset', AssetElement);
|
|
2857
|
+
/**
|
|
2858
|
+
* Resolves an asset reference for use: {@link AssetElement.get}, plus starting the load of a
|
|
2859
|
+
* registered asset that has not begun one - a `lazy` asset. Every element that consumes assets
|
|
2860
|
+
* resolves its references here, which is what makes `lazy` mean load on first use without any
|
|
2861
|
+
* consumer having to remember the load. The load is asynchronous - callers observe the asset's
|
|
2862
|
+
* `load` event for the resource.
|
|
2863
|
+
*
|
|
2864
|
+
* @param id - The `id` of the `<pc-asset>` element.
|
|
2865
|
+
* @returns The asset, or `undefined`.
|
|
2866
|
+
* @internal
|
|
2867
|
+
*/
|
|
2868
|
+
const useAsset = (id) => {
|
|
2869
|
+
const asset = AssetElement.get(id);
|
|
2870
|
+
// load() ignores an asset that is already loaded or loading, so repeated resolution
|
|
2871
|
+
// costs nothing.
|
|
2872
|
+
if (asset) {
|
|
2873
|
+
asset.registry?.load(asset);
|
|
2874
|
+
}
|
|
2875
|
+
return asset;
|
|
2876
|
+
};
|
|
2780
2877
|
|
|
2781
2878
|
/**
|
|
2782
2879
|
* Represents a component in the PlayCanvas engine.
|
|
@@ -3060,15 +3157,15 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
3060
3157
|
if (imageEntity) {
|
|
3061
3158
|
data.imageEntity = imageEntity;
|
|
3062
3159
|
}
|
|
3063
|
-
const hoverSpriteAsset =
|
|
3160
|
+
const hoverSpriteAsset = useAsset(this._hoverSpriteAsset);
|
|
3064
3161
|
if (hoverSpriteAsset) {
|
|
3065
3162
|
data.hoverSpriteAsset = hoverSpriteAsset.id;
|
|
3066
3163
|
}
|
|
3067
|
-
const pressedSpriteAsset =
|
|
3164
|
+
const pressedSpriteAsset = useAsset(this._pressedSpriteAsset);
|
|
3068
3165
|
if (pressedSpriteAsset) {
|
|
3069
3166
|
data.pressedSpriteAsset = pressedSpriteAsset.id;
|
|
3070
3167
|
}
|
|
3071
|
-
const inactiveSpriteAsset =
|
|
3168
|
+
const inactiveSpriteAsset = useAsset(this._inactiveSpriteAsset);
|
|
3072
3169
|
if (inactiveSpriteAsset) {
|
|
3073
3170
|
data.inactiveSpriteAsset = inactiveSpriteAsset.id;
|
|
3074
3171
|
}
|
|
@@ -3230,7 +3327,7 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
3230
3327
|
*/
|
|
3231
3328
|
set hoverSpriteAsset(value) {
|
|
3232
3329
|
this._hoverSpriteAsset = value;
|
|
3233
|
-
const asset =
|
|
3330
|
+
const asset = useAsset(value);
|
|
3234
3331
|
if (this.component && asset) {
|
|
3235
3332
|
this.component.hoverSpriteAsset = asset.id;
|
|
3236
3333
|
}
|
|
@@ -3266,7 +3363,7 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
3266
3363
|
*/
|
|
3267
3364
|
set pressedSpriteAsset(value) {
|
|
3268
3365
|
this._pressedSpriteAsset = value;
|
|
3269
|
-
const asset =
|
|
3366
|
+
const asset = useAsset(value);
|
|
3270
3367
|
if (this.component && asset) {
|
|
3271
3368
|
this.component.pressedSpriteAsset = asset.id;
|
|
3272
3369
|
}
|
|
@@ -3302,7 +3399,7 @@ class ButtonComponentElement extends ComponentElement {
|
|
|
3302
3399
|
*/
|
|
3303
3400
|
set inactiveSpriteAsset(value) {
|
|
3304
3401
|
this._inactiveSpriteAsset = value;
|
|
3305
|
-
const asset =
|
|
3402
|
+
const asset = useAsset(value);
|
|
3306
3403
|
if (this.component && asset) {
|
|
3307
3404
|
this.component.inactiveSpriteAsset = asset.id;
|
|
3308
3405
|
}
|
|
@@ -4160,15 +4257,15 @@ class ElementComponentElement extends ComponentElement {
|
|
|
4160
4257
|
};
|
|
4161
4258
|
// Asset references are resolved from `<pc-asset>` element ids to engine asset ids. They are
|
|
4162
4259
|
// only included when they resolve, so image/group elements (with no font) don't error.
|
|
4163
|
-
const fontAsset =
|
|
4260
|
+
const fontAsset = useAsset(this._fontAsset);
|
|
4164
4261
|
if (fontAsset) {
|
|
4165
4262
|
data.fontAsset = fontAsset.id;
|
|
4166
4263
|
}
|
|
4167
|
-
const spriteAsset =
|
|
4264
|
+
const spriteAsset = useAsset(this._spriteAsset);
|
|
4168
4265
|
if (spriteAsset) {
|
|
4169
4266
|
data.spriteAsset = spriteAsset.id;
|
|
4170
4267
|
}
|
|
4171
|
-
const textureAsset =
|
|
4268
|
+
const textureAsset = useAsset(this._textureAsset);
|
|
4172
4269
|
if (textureAsset) {
|
|
4173
4270
|
data.textureAsset = textureAsset.id;
|
|
4174
4271
|
}
|
|
@@ -4282,7 +4379,7 @@ class ElementComponentElement extends ComponentElement {
|
|
|
4282
4379
|
*/
|
|
4283
4380
|
set fontAsset(value) {
|
|
4284
4381
|
this._fontAsset = value;
|
|
4285
|
-
const asset =
|
|
4382
|
+
const asset = useAsset(value);
|
|
4286
4383
|
if (this.component && asset) {
|
|
4287
4384
|
this.component.fontAsset = asset.id;
|
|
4288
4385
|
}
|
|
@@ -4437,7 +4534,7 @@ class ElementComponentElement extends ComponentElement {
|
|
|
4437
4534
|
*/
|
|
4438
4535
|
set spriteAsset(value) {
|
|
4439
4536
|
this._spriteAsset = value;
|
|
4440
|
-
const asset =
|
|
4537
|
+
const asset = useAsset(value);
|
|
4441
4538
|
if (this.component && asset) {
|
|
4442
4539
|
this.component.spriteAsset = asset.id;
|
|
4443
4540
|
}
|
|
@@ -4489,7 +4586,7 @@ class ElementComponentElement extends ComponentElement {
|
|
|
4489
4586
|
*/
|
|
4490
4587
|
set textureAsset(value) {
|
|
4491
4588
|
this._textureAsset = value;
|
|
4492
|
-
const asset =
|
|
4589
|
+
const asset = useAsset(value);
|
|
4493
4590
|
if (this.component && asset) {
|
|
4494
4591
|
this.component.textureAsset = asset.id;
|
|
4495
4592
|
}
|
|
@@ -5724,13 +5821,14 @@ class ParticleSystemComponentElement extends ComponentElement {
|
|
|
5724
5821
|
super('particlesystem');
|
|
5725
5822
|
}
|
|
5726
5823
|
getInitialComponentData() {
|
|
5727
|
-
const asset =
|
|
5728
|
-
|
|
5824
|
+
const asset = useAsset(this._asset);
|
|
5825
|
+
// A lazy config has no resource yet - _loadAsset applies it once the load completes
|
|
5826
|
+
if (!asset || !asset.resource) {
|
|
5729
5827
|
return {};
|
|
5730
5828
|
}
|
|
5731
5829
|
if (asset.resource.colorMapAsset) {
|
|
5732
5830
|
const id = asset.resource.colorMapAsset;
|
|
5733
|
-
const colorMapAsset =
|
|
5831
|
+
const colorMapAsset = useAsset(id)?.id;
|
|
5734
5832
|
if (colorMapAsset) {
|
|
5735
5833
|
asset.resource.colorMapAsset = colorMapAsset;
|
|
5736
5834
|
}
|
|
@@ -5756,9 +5854,8 @@ class ParticleSystemComponentElement extends ComponentElement {
|
|
|
5756
5854
|
}
|
|
5757
5855
|
}
|
|
5758
5856
|
async _loadAsset() {
|
|
5759
|
-
|
|
5760
|
-
const
|
|
5761
|
-
const asset = AssetElement.get(this._asset);
|
|
5857
|
+
await this.closestApp?.ready();
|
|
5858
|
+
const asset = useAsset(this._asset);
|
|
5762
5859
|
if (!asset) {
|
|
5763
5860
|
return;
|
|
5764
5861
|
}
|
|
@@ -5769,7 +5866,6 @@ class ParticleSystemComponentElement extends ComponentElement {
|
|
|
5769
5866
|
asset.once('load', () => {
|
|
5770
5867
|
this.applyConfig(asset.resource);
|
|
5771
5868
|
});
|
|
5772
|
-
app.assets.load(asset);
|
|
5773
5869
|
}
|
|
5774
5870
|
}
|
|
5775
5871
|
/**
|
|
@@ -6185,7 +6281,7 @@ class MaterialElement extends HTMLElement {
|
|
|
6185
6281
|
this._scheduleUpdate();
|
|
6186
6282
|
return;
|
|
6187
6283
|
}
|
|
6188
|
-
const asset =
|
|
6284
|
+
const asset = useAsset(id);
|
|
6189
6285
|
if (!asset)
|
|
6190
6286
|
return;
|
|
6191
6287
|
if (asset.loaded) {
|
|
@@ -9286,7 +9382,7 @@ const camelToKebab = (name) => {
|
|
|
9286
9382
|
* @returns The asset, or `raw`.
|
|
9287
9383
|
*/
|
|
9288
9384
|
const assetConversion = (rest, raw) => {
|
|
9289
|
-
const asset =
|
|
9385
|
+
const asset = useAsset(rest);
|
|
9290
9386
|
if (asset) {
|
|
9291
9387
|
return asset;
|
|
9292
9388
|
}
|
|
@@ -10106,7 +10202,7 @@ class SoundSlotElement extends AsyncElement {
|
|
|
10106
10202
|
set asset(value) {
|
|
10107
10203
|
this._asset = value;
|
|
10108
10204
|
if (this.soundSlot) {
|
|
10109
|
-
const id =
|
|
10205
|
+
const id = useAsset(value)?.id;
|
|
10110
10206
|
if (id) {
|
|
10111
10207
|
this.soundSlot.asset = id;
|
|
10112
10208
|
}
|
|
@@ -10313,7 +10409,7 @@ class GSplatComponentElement extends ComponentElement {
|
|
|
10313
10409
|
}
|
|
10314
10410
|
getInitialComponentData() {
|
|
10315
10411
|
return {
|
|
10316
|
-
asset:
|
|
10412
|
+
asset: useAsset(this._asset),
|
|
10317
10413
|
castShadows: this._castShadows,
|
|
10318
10414
|
lodBaseDistance: this._lodBaseDistance,
|
|
10319
10415
|
lodMultiplier: this._lodMultiplier,
|
|
@@ -10334,7 +10430,7 @@ class GSplatComponentElement extends ComponentElement {
|
|
|
10334
10430
|
*/
|
|
10335
10431
|
set asset(value) {
|
|
10336
10432
|
this._asset = value;
|
|
10337
|
-
const asset =
|
|
10433
|
+
const asset = useAsset(value);
|
|
10338
10434
|
if (this.component && asset) {
|
|
10339
10435
|
this.component.asset = asset;
|
|
10340
10436
|
}
|
|
@@ -10612,8 +10708,7 @@ class ModelElement extends AsyncElement {
|
|
|
10612
10708
|
if (generation !== this._loadGeneration) {
|
|
10613
10709
|
return;
|
|
10614
10710
|
}
|
|
10615
|
-
const
|
|
10616
|
-
const asset = AssetElement.get(this._asset);
|
|
10711
|
+
const asset = useAsset(this._asset);
|
|
10617
10712
|
if (!asset) {
|
|
10618
10713
|
// An empty id is a legitimate transient (the asset may be assigned later); a
|
|
10619
10714
|
// non-empty one that resolves to nothing is a dead end - say so rather than staying
|
|
@@ -10649,7 +10744,6 @@ class ModelElement extends AsyncElement {
|
|
|
10649
10744
|
}));
|
|
10650
10745
|
this._onReady();
|
|
10651
10746
|
});
|
|
10652
|
-
app.assets.load(asset);
|
|
10653
10747
|
}
|
|
10654
10748
|
}
|
|
10655
10749
|
_unloadModel() {
|
|
@@ -11614,7 +11708,7 @@ class SkyElement extends AsyncElement {
|
|
|
11614
11708
|
return;
|
|
11615
11709
|
}
|
|
11616
11710
|
this._appElement = appElement;
|
|
11617
|
-
const asset =
|
|
11711
|
+
const asset = useAsset(this._asset);
|
|
11618
11712
|
if (!asset) {
|
|
11619
11713
|
return;
|
|
11620
11714
|
}
|
|
@@ -11633,7 +11727,6 @@ class SkyElement extends AsyncElement {
|
|
|
11633
11727
|
}
|
|
11634
11728
|
this._generateSkybox(asset);
|
|
11635
11729
|
});
|
|
11636
|
-
app.assets.load(asset);
|
|
11637
11730
|
}
|
|
11638
11731
|
}
|
|
11639
11732
|
_unloadSkybox() {
|