@playcanvas/web-components 0.11.1 → 0.13.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 +1 -1
- package/dist/app.d.cts +246 -0
- package/dist/app.d.ts +38 -13
- package/dist/asset.d.cts +218 -0
- package/dist/asset.d.ts +145 -6
- package/dist/async-element.d.cts +103 -0
- package/dist/async-element.d.ts +11 -8
- package/dist/colors.d.cts +1 -0
- package/dist/components/button-component.d.cts +186 -0
- package/dist/components/button-component.d.ts +1 -1
- package/dist/components/camera-component.d.cts +253 -0
- package/dist/components/camera-component.d.ts +1 -1
- package/dist/components/collision-component.d.cts +73 -0
- package/dist/components/collision-component.d.ts +17 -1
- package/dist/components/component.d.cts +82 -0
- package/dist/components/component.d.ts +20 -1
- package/dist/components/element-component.d.cts +316 -0
- package/dist/components/element-component.d.ts +1 -1
- package/dist/components/gsplat-component.d.cts +108 -0
- package/dist/components/gsplat-component.d.ts +1 -1
- package/dist/components/layoutchild-component.d.cts +110 -0
- package/dist/components/layoutchild-component.d.ts +1 -1
- package/dist/components/layoutgroup-component.d.cts +136 -0
- package/dist/components/layoutgroup-component.d.ts +1 -1
- package/dist/components/light-component.d.cts +264 -0
- package/dist/components/light-component.d.ts +1 -1
- package/dist/components/listener-component.d.cts +20 -0
- package/dist/components/listener-component.d.ts +1 -1
- package/dist/components/particlesystem-component.d.cts +52 -0
- package/dist/components/particlesystem-component.d.ts +1 -1
- package/dist/components/render-component.d.cts +76 -0
- package/dist/components/render-component.d.ts +1 -1
- package/dist/components/rigidbody-component.d.cts +88 -0
- package/dist/components/rigidbody-component.d.ts +1 -1
- package/dist/components/screen-component.d.cts +70 -0
- package/dist/components/screen-component.d.ts +1 -1
- package/dist/components/script-component.d.cts +163 -0
- package/dist/components/script-component.d.ts +1 -1
- package/dist/components/script.d.cts +94 -0
- package/dist/components/script.d.ts +1 -1
- package/dist/components/scrollbar-component.d.cts +69 -0
- package/dist/components/scrollbar-component.d.ts +1 -1
- package/dist/components/scrollview-component.d.cts +178 -0
- package/dist/components/scrollview-component.d.ts +1 -1
- package/dist/components/sound-component.d.cts +108 -0
- package/dist/components/sound-component.d.ts +1 -1
- package/dist/components/sound-slot.d.cts +134 -0
- package/dist/components/sound-slot.d.ts +2 -2
- package/dist/custom-elements.json +1127 -179
- package/dist/entity-base.d.cts +67 -0
- package/dist/entity-base.d.ts +67 -0
- package/dist/entity.d.cts +131 -0
- package/dist/entity.d.ts +3 -38
- package/dist/index.d.cts +79 -0
- package/dist/index.d.ts +35 -32
- package/dist/loading-bar.d.cts +35 -0
- package/dist/material.d.cts +1011 -0
- package/dist/material.d.ts +2 -1
- package/dist/model.d.cts +72 -0
- package/dist/model.d.ts +27 -5
- package/dist/module.d.cts +29 -0
- package/dist/module.d.ts +16 -10
- package/dist/node.d.cts +253 -0
- package/dist/node.d.ts +253 -0
- package/dist/parse.d.cts +147 -0
- package/dist/pwc.cjs +1496 -200
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +1497 -201
- 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 +1496 -202
- package/dist/pwc.mjs.map +1 -1
- package/dist/scene.d.cts +117 -0
- package/dist/scene.d.ts +1 -1
- package/dist/sky.d.cts +121 -0
- package/dist/sky.d.ts +1 -1
- package/dist/vscode.html-custom-data.json +127 -5
- package/dist/web-types.json +399 -59
- package/package.json +16 -7
- package/src/app.ts +137 -45
- package/src/asset.ts +439 -9
- package/src/async-element.ts +11 -8
- package/src/components/collision-component.ts +35 -0
- package/src/components/component.ts +93 -3
- package/src/entity-base.ts +136 -0
- package/src/entity.ts +23 -117
- package/src/index.ts +5 -0
- package/src/loading-bar.ts +2 -2
- package/src/material.ts +2 -2
- package/src/model.ts +79 -11
- package/src/module.ts +39 -20
- package/src/node.ts +715 -0
- package/src/sky.ts +0 -1
package/src/asset.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ADDRESS_CLAMP_TO_EDGE,
|
|
3
|
+
ADDRESS_MIRRORED_REPEAT,
|
|
4
|
+
ADDRESS_REPEAT,
|
|
5
|
+
Asset,
|
|
6
|
+
FILTER_LINEAR,
|
|
7
|
+
FILTER_LINEAR_MIPMAP_LINEAR,
|
|
8
|
+
FILTER_LINEAR_MIPMAP_NEAREST,
|
|
9
|
+
FILTER_NEAREST,
|
|
10
|
+
FILTER_NEAREST_MIPMAP_LINEAR,
|
|
11
|
+
FILTER_NEAREST_MIPMAP_NEAREST,
|
|
12
|
+
SPRITE_RENDERMODE_SIMPLE,
|
|
13
|
+
SPRITE_RENDERMODE_SLICED,
|
|
14
|
+
SPRITE_RENDERMODE_TILED
|
|
15
|
+
} from 'playcanvas';
|
|
16
|
+
import type { Texture, TextureAtlas } from 'playcanvas';
|
|
2
17
|
|
|
3
18
|
import { MeshoptDecoder } from '../lib/meshopt_decoder.module.js';
|
|
4
19
|
|
|
@@ -11,6 +26,66 @@ const renderModes = new Map<'simple' | 'sliced' | 'tiled', number>([
|
|
|
11
26
|
['tiled', SPRITE_RENDERMODE_TILED]
|
|
12
27
|
]);
|
|
13
28
|
|
|
29
|
+
type AddressMode = 'repeat' | 'clamp' | 'mirror';
|
|
30
|
+
|
|
31
|
+
const addressModes = new Map<AddressMode, number>([
|
|
32
|
+
['repeat', ADDRESS_REPEAT],
|
|
33
|
+
['clamp', ADDRESS_CLAMP_TO_EDGE],
|
|
34
|
+
['mirror', ADDRESS_MIRRORED_REPEAT]
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
type MinFilterMode =
|
|
38
|
+
'nearest' | 'linear' | 'nearest-mip-nearest' | 'linear-mip-nearest' | 'nearest-mip-linear' | 'linear-mip-linear';
|
|
39
|
+
|
|
40
|
+
const minFilterModes = new Map<MinFilterMode, number>([
|
|
41
|
+
['nearest', FILTER_NEAREST],
|
|
42
|
+
['linear', FILTER_LINEAR],
|
|
43
|
+
['nearest-mip-nearest', FILTER_NEAREST_MIPMAP_NEAREST],
|
|
44
|
+
['linear-mip-nearest', FILTER_LINEAR_MIPMAP_NEAREST],
|
|
45
|
+
['nearest-mip-linear', FILTER_NEAREST_MIPMAP_LINEAR],
|
|
46
|
+
['linear-mip-linear', FILTER_LINEAR_MIPMAP_LINEAR]
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
// Magnification has no mip variants - the engine (and the GPU) only accepts these two.
|
|
50
|
+
type MagFilterMode = 'nearest' | 'linear';
|
|
51
|
+
|
|
52
|
+
const magFilterModes = new Map<MagFilterMode, number>([
|
|
53
|
+
['nearest', FILTER_NEAREST],
|
|
54
|
+
['linear', FILTER_LINEAR]
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
// The engine's texture JSON spells the filter names with underscores ('linear_mip_linear'); the
|
|
58
|
+
// attribute values are kebab-case like every other enum attribute in this library. The address
|
|
59
|
+
// mode names contain no dashes, so for them the rename is the identity.
|
|
60
|
+
const toTextureJson = (name: string) => name.replace(/-/g, '_');
|
|
61
|
+
|
|
62
|
+
/** The Texture properties written by the texture option attributes. */
|
|
63
|
+
type TextureOptionProperty =
|
|
64
|
+
'addressU' | 'addressV' | 'anisotropy' | 'flipY' | 'magFilter' | 'minFilter' | 'mipmaps' | 'srgb';
|
|
65
|
+
|
|
66
|
+
// Engine Texture constructor defaults, restored on a loaded texture when a texture option
|
|
67
|
+
// attribute is removed.
|
|
68
|
+
const textureOptionDefaults: Record<TextureOptionProperty, number | boolean> = {
|
|
69
|
+
addressU: ADDRESS_REPEAT,
|
|
70
|
+
addressV: ADDRESS_REPEAT,
|
|
71
|
+
anisotropy: 1,
|
|
72
|
+
flipY: false,
|
|
73
|
+
magFilter: FILTER_LINEAR,
|
|
74
|
+
minFilter: FILTER_LINEAR_MIPMAP_LINEAR,
|
|
75
|
+
mipmaps: true,
|
|
76
|
+
srgb: false
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Attributes that only apply to certain asset types, used to warn when one is set on an asset of
|
|
80
|
+
// any other type (where it would otherwise be silently ignored).
|
|
81
|
+
const typeScopedAttributes: [attributes: string[], types: string[]][] = [
|
|
82
|
+
[
|
|
83
|
+
['address-u', 'address-v', 'anisotropy', 'flip-y', 'mag-filter', 'min-filter', 'mipmaps', 'srgb'],
|
|
84
|
+
['texture', 'textureatlas']
|
|
85
|
+
],
|
|
86
|
+
[['atlas', 'frame-keys', 'pixels-per-unit', 'render-mode'], ['sprite']]
|
|
87
|
+
];
|
|
88
|
+
|
|
14
89
|
const extToType = new Map([
|
|
15
90
|
['bin', 'binary'],
|
|
16
91
|
['css', 'css'],
|
|
@@ -78,8 +153,16 @@ const processBufferView = (
|
|
|
78
153
|
* immediately unless `lazy`. A `pc-asset` must be a direct child of `pc-app` — elements placed
|
|
79
154
|
* elsewhere, or with an unsupported asset type, never become ready.
|
|
80
155
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
156
|
+
* For `texture` and `textureatlas` assets, the texture options (`address-u`, `address-v`,
|
|
157
|
+
* `min-filter`, `mag-filter`, `anisotropy`, `mipmaps`, `srgb`, `flip-y`) apply when the texture is
|
|
158
|
+
* created and — like `lazy` — are observed: changing one updates a texture that has already
|
|
159
|
+
* loaded, and removing one restores the engine default. Changing `srgb` or `mipmaps` on a loaded
|
|
160
|
+
* texture recreates the underlying GPU resource, so prefer declaring those up front. Each option
|
|
161
|
+
* overrides the matching key in the `data` JSON; options left unset write nothing, leaving the
|
|
162
|
+
* engine's per-format defaults in force.
|
|
163
|
+
*
|
|
164
|
+
* Apart from `lazy` and the texture options, these attributes are read once when the asset is
|
|
165
|
+
* created, so changing them later has no effect.
|
|
83
166
|
*
|
|
84
167
|
* @attribute {string} id - The identifier used to reference the asset from other elements.
|
|
85
168
|
* @attribute {string} src - The URL of the asset to load.
|
|
@@ -101,8 +184,24 @@ const processBufferView = (
|
|
|
101
184
|
* not that it succeeded.
|
|
102
185
|
*/
|
|
103
186
|
class AssetElement extends AsyncElement {
|
|
187
|
+
private _addressU: AddressMode | null = null;
|
|
188
|
+
|
|
189
|
+
private _addressV: AddressMode | null = null;
|
|
190
|
+
|
|
191
|
+
private _anisotropy: number | null = null;
|
|
192
|
+
|
|
193
|
+
private _flipY: boolean | null = null;
|
|
194
|
+
|
|
104
195
|
private _lazy = false;
|
|
105
196
|
|
|
197
|
+
private _magFilter: MagFilterMode | null = null;
|
|
198
|
+
|
|
199
|
+
private _minFilter: MinFilterMode | null = null;
|
|
200
|
+
|
|
201
|
+
private _mipmaps: boolean | null = null;
|
|
202
|
+
|
|
203
|
+
private _srgb: boolean | null = null;
|
|
204
|
+
|
|
106
205
|
/**
|
|
107
206
|
* The asset that is loaded. Available once the element is ready — await
|
|
108
207
|
* {@link whenReady} or the element's `ready()` promise before accessing it.
|
|
@@ -188,6 +287,18 @@ class AssetElement extends AsyncElement {
|
|
|
188
287
|
return;
|
|
189
288
|
}
|
|
190
289
|
|
|
290
|
+
// Attributes scoped to other asset types have no effect here - say so rather than
|
|
291
|
+
// failing silently.
|
|
292
|
+
const inapplicable = typeScopedAttributes
|
|
293
|
+
.filter(([, types]) => !types.includes(type))
|
|
294
|
+
.flatMap(([attributes]) => attributes)
|
|
295
|
+
.filter((attribute) => this.hasAttribute(attribute));
|
|
296
|
+
if (inapplicable.length > 0) {
|
|
297
|
+
console.warn(
|
|
298
|
+
`pc-asset '${id || src}' has attributes that do not apply to asset type '${type}' and are ignored: ${inapplicable.join(', ')}`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
191
302
|
// Optional inline asset data, used by data-driven assets such as texture atlases (frame
|
|
192
303
|
// definitions) and sprites (atlas reference, frame keys, etc.).
|
|
193
304
|
const data = this._buildData(type);
|
|
@@ -217,9 +328,11 @@ class AssetElement extends AsyncElement {
|
|
|
217
328
|
}
|
|
218
329
|
|
|
219
330
|
/**
|
|
220
|
-
* Builds the `data` object for the asset from an optional inline `data` attribute (JSON)
|
|
221
|
-
*
|
|
222
|
-
* `render-mode`).
|
|
331
|
+
* Builds the `data` object for the asset from an optional inline `data` attribute (JSON), the
|
|
332
|
+
* texture option attributes (for `texture` and `textureatlas` assets), and the sprite
|
|
333
|
+
* convenience attributes (`atlas`, `frame-keys`, `pixels-per-unit`, `render-mode`). An
|
|
334
|
+
* attribute overrides the matching `data` JSON key. Returns `undefined` when there is no data
|
|
335
|
+
* to apply.
|
|
223
336
|
* @param type - The resolved asset type.
|
|
224
337
|
* @returns The asset data, or `undefined`.
|
|
225
338
|
*/
|
|
@@ -235,6 +348,39 @@ class AssetElement extends AsyncElement {
|
|
|
235
348
|
}
|
|
236
349
|
}
|
|
237
350
|
|
|
351
|
+
if (type === 'texture' || type === 'textureatlas') {
|
|
352
|
+
data = data ?? {};
|
|
353
|
+
|
|
354
|
+
// Only options the user actually set are written: the engine reads these keys with
|
|
355
|
+
// hasOwnProperty semantics, and an absent key leaves its per-format default (an HDR's
|
|
356
|
+
// 'rgbe' type, a KTX2's transcoded format) in force.
|
|
357
|
+
if (this._addressU !== null) {
|
|
358
|
+
data.addressu = this._addressU;
|
|
359
|
+
}
|
|
360
|
+
if (this._addressV !== null) {
|
|
361
|
+
data.addressv = this._addressV;
|
|
362
|
+
}
|
|
363
|
+
if (this._anisotropy !== null) {
|
|
364
|
+
data.anisotropy = this._anisotropy;
|
|
365
|
+
}
|
|
366
|
+
if (this._flipY !== null) {
|
|
367
|
+
// 'flipY' is the one camelCase key in the engine's texture JSON
|
|
368
|
+
data.flipY = this._flipY;
|
|
369
|
+
}
|
|
370
|
+
if (this._magFilter !== null) {
|
|
371
|
+
data.magfilter = toTextureJson(this._magFilter);
|
|
372
|
+
}
|
|
373
|
+
if (this._minFilter !== null) {
|
|
374
|
+
data.minfilter = toTextureJson(this._minFilter);
|
|
375
|
+
}
|
|
376
|
+
if (this._mipmaps !== null) {
|
|
377
|
+
data.mipmaps = this._mipmaps;
|
|
378
|
+
}
|
|
379
|
+
if (this._srgb !== null) {
|
|
380
|
+
data.srgb = this._srgb;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
238
384
|
if (type === 'sprite') {
|
|
239
385
|
data = data ?? {};
|
|
240
386
|
|
|
@@ -274,6 +420,60 @@ class AssetElement extends AsyncElement {
|
|
|
274
420
|
return data;
|
|
275
421
|
}
|
|
276
422
|
|
|
423
|
+
/**
|
|
424
|
+
* Returns the engine texture behind this asset, when there is one: the resource itself for a
|
|
425
|
+
* `texture` asset, the atlas's texture for a `textureatlas` asset, `null` otherwise
|
|
426
|
+
* (including before the asset has loaded).
|
|
427
|
+
* @returns The texture, or `null`.
|
|
428
|
+
*/
|
|
429
|
+
private _texture(): Texture | null {
|
|
430
|
+
const asset = this.asset;
|
|
431
|
+
if (!asset?.resource) return null;
|
|
432
|
+
if (asset.type === 'texture') return asset.resource as Texture;
|
|
433
|
+
if (asset.type === 'textureatlas') return (asset.resource as TextureAtlas).texture ?? null;
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Writes one texture option through to the created asset, if any. The engine-JSON key is
|
|
439
|
+
* written into `asset.data`, mutated in place - replacing the whole object would make the
|
|
440
|
+
* registry re-patch every key, and a re-patched `srgb` or `mipmaps` recreates the texture
|
|
441
|
+
* even when unchanged. The in-place key is what a not-yet-started load reads at texture
|
|
442
|
+
* construction, and what any later reload reads. When the texture already exists, the
|
|
443
|
+
* corresponding property is assigned directly; `null` (attribute removed) deletes the key
|
|
444
|
+
* and restores the engine default. Assets of any other type are left untouched.
|
|
445
|
+
*
|
|
446
|
+
* @param key - The engine texture JSON key in `asset.data`.
|
|
447
|
+
* @param property - The Texture property to assign.
|
|
448
|
+
* @param dataValue - The engine-JSON value for `asset.data`, or `null` to delete the key.
|
|
449
|
+
* @param textureValue - The value for the Texture property, or `null` for the engine default.
|
|
450
|
+
*/
|
|
451
|
+
private _applyTextureOption(
|
|
452
|
+
key: string,
|
|
453
|
+
property: TextureOptionProperty,
|
|
454
|
+
dataValue: string | number | boolean | null,
|
|
455
|
+
textureValue: number | boolean | null
|
|
456
|
+
) {
|
|
457
|
+
const asset = this.asset;
|
|
458
|
+
if (!asset || (asset.type !== 'texture' && asset.type !== 'textureatlas')) return;
|
|
459
|
+
|
|
460
|
+
const data = asset.data as Record<string, any>;
|
|
461
|
+
if (dataValue === null) {
|
|
462
|
+
delete data[key];
|
|
463
|
+
} else {
|
|
464
|
+
data[key] = dataValue;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const texture = this._texture();
|
|
468
|
+
if (texture) {
|
|
469
|
+
// Every option here is a number- or boolean-valued Texture property; the
|
|
470
|
+
// value/property pairing is fixed by the callers, which TypeScript cannot see
|
|
471
|
+
// through the union.
|
|
472
|
+
(texture as unknown as Record<TextureOptionProperty, number | boolean>)[property] =
|
|
473
|
+
textureValue ?? textureOptionDefaults[property];
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
277
477
|
private _destroyAsset() {
|
|
278
478
|
if (this.asset) {
|
|
279
479
|
// A caller that keeps the Asset alive must not dispatch on a removed element
|
|
@@ -286,6 +486,83 @@ class AssetElement extends AsyncElement {
|
|
|
286
486
|
}
|
|
287
487
|
}
|
|
288
488
|
|
|
489
|
+
/**
|
|
490
|
+
* Sets the texture's horizontal (U) address mode: how texture coordinates outside the 0 to 1
|
|
491
|
+
* range sample the texture. Applies to `texture` and `textureatlas` assets, both when the
|
|
492
|
+
* texture is created and after it has loaded.
|
|
493
|
+
* @param value - The address mode, or `null` to use the engine default of 'repeat'.
|
|
494
|
+
*/
|
|
495
|
+
set addressU(value: AddressMode | null) {
|
|
496
|
+
this._addressU = value;
|
|
497
|
+
const constant = value === null ? null : (addressModes.get(value) ?? ADDRESS_REPEAT);
|
|
498
|
+
this._applyTextureOption('addressu', 'addressU', value, constant);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Gets the texture's horizontal (U) address mode.
|
|
503
|
+
* @returns The address mode, or `null` when unset.
|
|
504
|
+
*/
|
|
505
|
+
get addressU(): AddressMode | null {
|
|
506
|
+
return this._addressU;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Sets the texture's vertical (V) address mode: how texture coordinates outside the 0 to 1
|
|
511
|
+
* range sample the texture. Applies to `texture` and `textureatlas` assets, both when the
|
|
512
|
+
* texture is created and after it has loaded.
|
|
513
|
+
* @param value - The address mode, or `null` to use the engine default of 'repeat'.
|
|
514
|
+
*/
|
|
515
|
+
set addressV(value: AddressMode | null) {
|
|
516
|
+
this._addressV = value;
|
|
517
|
+
const constant = value === null ? null : (addressModes.get(value) ?? ADDRESS_REPEAT);
|
|
518
|
+
this._applyTextureOption('addressv', 'addressV', value, constant);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Gets the texture's vertical (V) address mode.
|
|
523
|
+
* @returns The address mode, or `null` when unset.
|
|
524
|
+
*/
|
|
525
|
+
get addressV(): AddressMode | null {
|
|
526
|
+
return this._addressV;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Sets the texture's maximum anisotropic filtering level, which improves quality at oblique
|
|
531
|
+
* viewing angles. Applies to `texture` and `textureatlas` assets, both when the texture is
|
|
532
|
+
* created and after it has loaded.
|
|
533
|
+
* @param value - The anisotropy level, or `null` to use the engine default of 1.
|
|
534
|
+
*/
|
|
535
|
+
set anisotropy(value: number | null) {
|
|
536
|
+
this._anisotropy = value;
|
|
537
|
+
this._applyTextureOption('anisotropy', 'anisotropy', value, value);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Gets the texture's maximum anisotropic filtering level.
|
|
542
|
+
* @returns The anisotropy level, or `null` when unset.
|
|
543
|
+
*/
|
|
544
|
+
get anisotropy(): number | null {
|
|
545
|
+
return this._anisotropy;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Sets whether the texture's image data is flipped vertically at upload. Applies to `texture`
|
|
550
|
+
* and `textureatlas` assets, both when the texture is created and after it has loaded.
|
|
551
|
+
* @param value - The flip flag, or `null` to use the engine default of `false`.
|
|
552
|
+
*/
|
|
553
|
+
set flipY(value: boolean | null) {
|
|
554
|
+
this._flipY = value;
|
|
555
|
+
this._applyTextureOption('flipY', 'flipY', value, value);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Gets whether the texture's image data is flipped vertically at upload.
|
|
560
|
+
* @returns The flip flag, or `null` when unset.
|
|
561
|
+
*/
|
|
562
|
+
get flipY(): boolean | null {
|
|
563
|
+
return this._flipY;
|
|
564
|
+
}
|
|
565
|
+
|
|
289
566
|
/**
|
|
290
567
|
* Sets whether the asset should be loaded lazily.
|
|
291
568
|
* @param value - The lazy loading flag.
|
|
@@ -305,6 +582,87 @@ class AssetElement extends AsyncElement {
|
|
|
305
582
|
return this._lazy;
|
|
306
583
|
}
|
|
307
584
|
|
|
585
|
+
/**
|
|
586
|
+
* Sets the texture's magnification filter, used when the texture is displayed larger than its
|
|
587
|
+
* source size. Applies to `texture` and `textureatlas` assets, both when the texture is
|
|
588
|
+
* created and after it has loaded.
|
|
589
|
+
* @param value - The filter, or `null` to use the engine default of 'linear'.
|
|
590
|
+
*/
|
|
591
|
+
set magFilter(value: MagFilterMode | null) {
|
|
592
|
+
this._magFilter = value;
|
|
593
|
+
const json = value === null ? null : toTextureJson(value);
|
|
594
|
+
const constant = value === null ? null : (magFilterModes.get(value) ?? FILTER_LINEAR);
|
|
595
|
+
this._applyTextureOption('magfilter', 'magFilter', json, constant);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Gets the texture's magnification filter.
|
|
600
|
+
* @returns The filter, or `null` when unset.
|
|
601
|
+
*/
|
|
602
|
+
get magFilter(): MagFilterMode | null {
|
|
603
|
+
return this._magFilter;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Sets the texture's minification filter, used when the texture is displayed smaller than its
|
|
608
|
+
* source size. The mip variants blend within (and, for the second `linear`, between) mipmap
|
|
609
|
+
* levels. Applies to `texture` and `textureatlas` assets, both when the texture is created
|
|
610
|
+
* and after it has loaded.
|
|
611
|
+
* @param value - The filter, or `null` to use the engine default of 'linear-mip-linear'.
|
|
612
|
+
*/
|
|
613
|
+
set minFilter(value: MinFilterMode | null) {
|
|
614
|
+
this._minFilter = value;
|
|
615
|
+
const json = value === null ? null : toTextureJson(value);
|
|
616
|
+
const constant = value === null ? null : (minFilterModes.get(value) ?? FILTER_LINEAR_MIPMAP_LINEAR);
|
|
617
|
+
this._applyTextureOption('minfilter', 'minFilter', json, constant);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Gets the texture's minification filter.
|
|
622
|
+
* @returns The filter, or `null` when unset.
|
|
623
|
+
*/
|
|
624
|
+
get minFilter(): MinFilterMode | null {
|
|
625
|
+
return this._minFilter;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Sets whether the texture generates and uses mipmaps. Changing this on a loaded texture
|
|
630
|
+
* recreates the underlying GPU resource, so prefer declaring it up front. Applies to
|
|
631
|
+
* `texture` and `textureatlas` assets.
|
|
632
|
+
* @param value - The mipmaps flag, or `null` to use the engine default of `true`.
|
|
633
|
+
*/
|
|
634
|
+
set mipmaps(value: boolean | null) {
|
|
635
|
+
this._mipmaps = value;
|
|
636
|
+
this._applyTextureOption('mipmaps', 'mipmaps', value, value);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Gets whether the texture generates and uses mipmaps.
|
|
641
|
+
* @returns The mipmaps flag, or `null` when unset.
|
|
642
|
+
*/
|
|
643
|
+
get mipmaps(): boolean | null {
|
|
644
|
+
return this._mipmaps;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Sets whether the texture holds sRGB (gamma-encoded) color data, enabling hardware gamma
|
|
649
|
+
* decode. Free when set before the texture loads; changing it on a loaded texture recreates
|
|
650
|
+
* the underlying GPU resource. Applies to `texture` and `textureatlas` assets.
|
|
651
|
+
* @param value - The sRGB flag, or `null` to use the engine default of `false`.
|
|
652
|
+
*/
|
|
653
|
+
set srgb(value: boolean | null) {
|
|
654
|
+
this._srgb = value;
|
|
655
|
+
this._applyTextureOption('srgb', 'srgb', value, value);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Gets whether the texture holds sRGB (gamma-encoded) color data.
|
|
660
|
+
* @returns The sRGB flag, or `null` when unset.
|
|
661
|
+
*/
|
|
662
|
+
get srgb(): boolean | null {
|
|
663
|
+
return this._srgb;
|
|
664
|
+
}
|
|
665
|
+
|
|
308
666
|
/**
|
|
309
667
|
* Returns the {@link Asset} created by the `<pc-asset>` element with the given `id`, or
|
|
310
668
|
* `undefined` if there is no such element or its asset has not been created yet.
|
|
@@ -318,12 +676,84 @@ class AssetElement extends AsyncElement {
|
|
|
318
676
|
}
|
|
319
677
|
|
|
320
678
|
static get observedAttributes() {
|
|
321
|
-
return [
|
|
679
|
+
return [
|
|
680
|
+
'address-u',
|
|
681
|
+
'address-v',
|
|
682
|
+
'anisotropy',
|
|
683
|
+
'flip-y',
|
|
684
|
+
'lazy',
|
|
685
|
+
'mag-filter',
|
|
686
|
+
'min-filter',
|
|
687
|
+
'mipmaps',
|
|
688
|
+
'srgb'
|
|
689
|
+
];
|
|
322
690
|
}
|
|
323
691
|
|
|
324
692
|
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
325
|
-
|
|
326
|
-
|
|
693
|
+
// Each texture option keeps its parse* call as the branch's first assignment (the CEM
|
|
694
|
+
// manifest derives the attribute's type and default from it - a ternary would degrade
|
|
695
|
+
// both to plain string) and treats a removed attribute (null) as a reset to unset,
|
|
696
|
+
// which restores the engine default on a loaded texture.
|
|
697
|
+
switch (name) {
|
|
698
|
+
case 'address-u':
|
|
699
|
+
if (newValue !== null) {
|
|
700
|
+
this.addressU = parseEnum(newValue, addressModes, 'repeat', name);
|
|
701
|
+
} else {
|
|
702
|
+
this.addressU = null;
|
|
703
|
+
}
|
|
704
|
+
break;
|
|
705
|
+
case 'address-v':
|
|
706
|
+
if (newValue !== null) {
|
|
707
|
+
this.addressV = parseEnum(newValue, addressModes, 'repeat', name);
|
|
708
|
+
} else {
|
|
709
|
+
this.addressV = null;
|
|
710
|
+
}
|
|
711
|
+
break;
|
|
712
|
+
case 'anisotropy':
|
|
713
|
+
if (newValue !== null) {
|
|
714
|
+
this.anisotropy = parseNumber(newValue, 1, name);
|
|
715
|
+
} else {
|
|
716
|
+
this.anisotropy = null;
|
|
717
|
+
}
|
|
718
|
+
break;
|
|
719
|
+
case 'flip-y':
|
|
720
|
+
if (newValue !== null) {
|
|
721
|
+
this.flipY = parseBool(newValue, false);
|
|
722
|
+
} else {
|
|
723
|
+
this.flipY = null;
|
|
724
|
+
}
|
|
725
|
+
break;
|
|
726
|
+
case 'lazy':
|
|
727
|
+
this.lazy = parseBool(newValue, false);
|
|
728
|
+
break;
|
|
729
|
+
case 'mag-filter':
|
|
730
|
+
if (newValue !== null) {
|
|
731
|
+
this.magFilter = parseEnum(newValue, magFilterModes, 'linear', name);
|
|
732
|
+
} else {
|
|
733
|
+
this.magFilter = null;
|
|
734
|
+
}
|
|
735
|
+
break;
|
|
736
|
+
case 'min-filter':
|
|
737
|
+
if (newValue !== null) {
|
|
738
|
+
this.minFilter = parseEnum(newValue, minFilterModes, 'linear-mip-linear', name);
|
|
739
|
+
} else {
|
|
740
|
+
this.minFilter = null;
|
|
741
|
+
}
|
|
742
|
+
break;
|
|
743
|
+
case 'mipmaps':
|
|
744
|
+
if (newValue !== null) {
|
|
745
|
+
this.mipmaps = parseBool(newValue, true);
|
|
746
|
+
} else {
|
|
747
|
+
this.mipmaps = null;
|
|
748
|
+
}
|
|
749
|
+
break;
|
|
750
|
+
case 'srgb':
|
|
751
|
+
if (newValue !== null) {
|
|
752
|
+
this.srgb = parseBool(newValue, false);
|
|
753
|
+
} else {
|
|
754
|
+
this.srgb = null;
|
|
755
|
+
}
|
|
756
|
+
break;
|
|
327
757
|
}
|
|
328
758
|
}
|
|
329
759
|
}
|
package/src/async-element.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AppElement } from './app';
|
|
2
|
-
import type {
|
|
2
|
+
import type { EntityBaseElement } from './entity-base';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Base class for all PlayCanvas Web Components that initialize asynchronously.
|
|
@@ -33,12 +33,13 @@ class AsyncElement extends HTMLElement {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* The nearest ancestor
|
|
37
|
-
* ancestor. The search starts at the parent, so an element
|
|
38
|
-
*
|
|
36
|
+
* The nearest ancestor element that fronts an entity — `<pc-entity>` or `<pc-node>` — or
|
|
37
|
+
* `null` if this element has no such ancestor. The search starts at the parent, so an element
|
|
38
|
+
* never resolves to itself.
|
|
39
|
+
* @returns The closest entity-fronting element, or `null`.
|
|
39
40
|
*/
|
|
40
|
-
get closestEntity():
|
|
41
|
-
return (this.parentElement?.closest('pc-entity') as
|
|
41
|
+
get closestEntity(): EntityBaseElement | null {
|
|
42
|
+
return (this.parentElement?.closest('pc-entity, pc-node') as EntityBaseElement | null) ?? null;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
/**
|
|
@@ -95,7 +96,8 @@ type AsyncElementTagName = {
|
|
|
95
96
|
/**
|
|
96
97
|
* Waits for the first element matching the given tag name to be fully initialized. Note that the
|
|
97
98
|
* promise never settles if the element cannot finish initializing (for example, a `<pc-script>`
|
|
98
|
-
* that is not a direct child of `<pc-scripts
|
|
99
|
+
* that is not a direct child of `<pc-scripts>`, or a `<pc-app>` that could not create a graphics
|
|
100
|
+
* device — listen for its `error` event instead). A component element outside a `<pc-entity>` is
|
|
99
101
|
* the exception: it still becomes ready, but its `component` is `null`. Either way, a misplaced
|
|
100
102
|
* element logs a warning naming the parent it requires.
|
|
101
103
|
* @param target - The tag name of the element to wait for (e.g. `'pc-app'`).
|
|
@@ -119,7 +121,8 @@ function whenReady<T extends AsyncElement>(target: T): Promise<T>;
|
|
|
119
121
|
/**
|
|
120
122
|
* Waits for the first element matching the given CSS selector to be fully initialized. Note that
|
|
121
123
|
* the promise never settles if the element cannot finish initializing (for example, a `<pc-script>`
|
|
122
|
-
* that is not a direct child of `<pc-scripts
|
|
124
|
+
* that is not a direct child of `<pc-scripts>`, or a `<pc-app>` that could not create a graphics
|
|
125
|
+
* device — listen for its `error` event instead). A component element outside a `<pc-entity>` is
|
|
123
126
|
* the exception: it still becomes ready, but its `component` is `null`. Either way, a misplaced
|
|
124
127
|
* element logs a warning naming the parent it requires.
|
|
125
128
|
* @param target - A CSS selector matching the element to wait for (e.g. `'#my-app'`).
|
|
@@ -11,6 +11,12 @@ import { ComponentElement } from './component';
|
|
|
11
11
|
* The CollisionComponentElement interface also inherits the properties and methods of the
|
|
12
12
|
* {@link HTMLElement} interface.
|
|
13
13
|
*
|
|
14
|
+
* For `type="mesh"`, the collision geometry defaults to the host entity's own render component
|
|
15
|
+
* (its render asset) — a collider matching the visible mesh, which is what a mesh collider on a
|
|
16
|
+
* glTF node means. The default resolves each time the component applies, so a `pc-node` that
|
|
17
|
+
* retargets or rebinds picks up the new node's geometry. An entity with no asset-backed render
|
|
18
|
+
* component warns, and the collider has no shape.
|
|
19
|
+
*
|
|
14
20
|
* @category Components
|
|
15
21
|
*/
|
|
16
22
|
class CollisionComponentElement extends ComponentElement {
|
|
@@ -48,6 +54,34 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
48
54
|
};
|
|
49
55
|
}
|
|
50
56
|
|
|
57
|
+
protected initComponent() {
|
|
58
|
+
this._applyMeshGeometryDefault();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Defaults a mesh collider's geometry to the host entity's own render component. The
|
|
63
|
+
* engine's mesh collider only works with explicitly supplied geometry, and the element has
|
|
64
|
+
* no attribute to supply it - so the host's visible geometry, the meaning a mesh collider
|
|
65
|
+
* on a glTF node carries, fills the gap. Runs on every application (so a rebound `pc-node`
|
|
66
|
+
* recomputes it) and on a runtime switch to `type="mesh"`; an explicitly assigned
|
|
67
|
+
* `renderAsset` is never overwritten.
|
|
68
|
+
*/
|
|
69
|
+
private _applyMeshGeometryDefault() {
|
|
70
|
+
const component = this.component;
|
|
71
|
+
if (!component || this._type !== 'mesh' || component.renderAsset !== null) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const asset = component.entity.render?.asset ?? null;
|
|
76
|
+
if (asset === null) {
|
|
77
|
+
console.warn(
|
|
78
|
+
`pc-collision type="mesh" on '${component.entity.name}' found no asset-backed render component to take geometry from - collider has no shape`
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
component.renderAsset = asset;
|
|
83
|
+
}
|
|
84
|
+
|
|
51
85
|
/**
|
|
52
86
|
* Gets the underlying PlayCanvas collision component.
|
|
53
87
|
* @returns The collision component.
|
|
@@ -137,6 +171,7 @@ class CollisionComponentElement extends ComponentElement {
|
|
|
137
171
|
this._type = value;
|
|
138
172
|
if (this.component) {
|
|
139
173
|
this.component.type = value;
|
|
174
|
+
this._applyMeshGeometryDefault();
|
|
140
175
|
}
|
|
141
176
|
}
|
|
142
177
|
|