@playcanvas/web-components 0.13.1 → 0.15.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/dist/colors.d.cts +1 -1
- package/dist/colors.d.ts +1 -1
- package/dist/components/joint-component.d.cts +521 -0
- package/dist/components/joint-component.d.ts +521 -0
- package/dist/custom-elements.json +1029 -0
- package/dist/entity-base.d.cts +1 -7
- package/dist/entity-base.d.ts +1 -7
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/loading-bar.d.cts +1 -35
- package/dist/loading-bar.d.ts +1 -35
- package/dist/material.d.cts +13 -0
- package/dist/material.d.ts +13 -0
- package/dist/model.d.cts +71 -0
- package/dist/model.d.ts +71 -0
- package/dist/node.d.cts +55 -0
- package/dist/node.d.ts +55 -0
- package/dist/parse.d.cts +1 -130
- package/dist/parse.d.ts +1 -130
- package/dist/pwc.cjs +1410 -148
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +1410 -148
- 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 +1410 -149
- package/dist/pwc.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +203 -1
- package/dist/web-types.json +368 -2
- package/package.json +5 -4
- package/src/app.ts +18 -0
- package/src/colors.ts +5 -0
- package/src/components/joint-component.ts +984 -0
- package/src/entity-base.ts +3 -3
- package/src/entity.ts +28 -7
- package/src/index.ts +6 -0
- package/src/loading-bar.ts +2 -3
- package/src/material.ts +29 -0
- package/src/model.ts +156 -0
- package/src/node.ts +279 -9
- package/src/parse.ts +11 -1
package/src/entity-base.ts
CHANGED
|
@@ -6,9 +6,9 @@ import { AsyncElement } from './async-element';
|
|
|
6
6
|
/**
|
|
7
7
|
* The attribute names of the inline `onpointer*` event handlers, shared by every element that
|
|
8
8
|
* fronts an engine entity. Spread into `observedAttributes` by subclasses.
|
|
9
|
-
* @
|
|
9
|
+
* @internal
|
|
10
10
|
*/
|
|
11
|
-
const POINTER_ATTRIBUTES = [
|
|
11
|
+
export const POINTER_ATTRIBUTES = [
|
|
12
12
|
'onpointerenter',
|
|
13
13
|
'onpointerleave',
|
|
14
14
|
'onpointerdown',
|
|
@@ -133,4 +133,4 @@ class EntityBaseElement extends AsyncElement {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
export { EntityBaseElement
|
|
136
|
+
export { EntityBaseElement };
|
package/src/entity.ts
CHANGED
|
@@ -4,6 +4,33 @@ import { Entity, Vec3 } from 'playcanvas';
|
|
|
4
4
|
import { EntityBaseElement, POINTER_ATTRIBUTES } from './entity-base';
|
|
5
5
|
import { parseBool, parseTags, parseVec3 } from './parse';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Creates and parents the entities of every descendant `<pc-entity>` of `root`, in two passes so
|
|
9
|
+
* that no parent's existence depends on document order. Called wherever a subtree could not build
|
|
10
|
+
* itself: an element inserted into an application that is already running, and a `<pc-node>` whose
|
|
11
|
+
* children waited for it to bind.
|
|
12
|
+
*
|
|
13
|
+
* Descendants that are not yet custom elements are skipped, because there is nothing useful to do
|
|
14
|
+
* for them and reaching for `_createEntity` would throw. A subtree cloned from a `<template>`
|
|
15
|
+
* arrives entirely unupgraded — template content lives in an inert document, where custom element
|
|
16
|
+
* definitions are never looked up — and appending the clone upgrades its elements in tree order,
|
|
17
|
+
* an element before its descendants. So a sweep from an element's own `connectedCallback` sees
|
|
18
|
+
* plain `HTMLElement`s below it. Each becomes an `EntityElement` moments later and its own
|
|
19
|
+
* `connectedCallback` creates and parents it, by which time the ancestor it parents under has its
|
|
20
|
+
* entity — the same guarantee tree order gives this sweep.
|
|
21
|
+
*
|
|
22
|
+
* @param root - The element whose descendant entities to build.
|
|
23
|
+
* @param app - The application to create the entities in.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export const buildDescendantEntities = (root: Element, app: AppBase) => {
|
|
27
|
+
const children = Array.from(root.querySelectorAll('pc-entity')).filter(
|
|
28
|
+
(child): child is EntityElement => child instanceof EntityElement
|
|
29
|
+
);
|
|
30
|
+
children.forEach((child) => child._createEntity(app));
|
|
31
|
+
children.forEach((child) => child._buildHierarchy(app));
|
|
32
|
+
};
|
|
33
|
+
|
|
7
34
|
/**
|
|
8
35
|
* The EntityElement interface provides properties and methods for manipulating
|
|
9
36
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-entity/ | `<pc-entity>`} elements.
|
|
@@ -172,13 +199,7 @@ class EntityElement extends EntityBaseElement {
|
|
|
172
199
|
this._buildHierarchy(app);
|
|
173
200
|
|
|
174
201
|
// Handle any child entities that might exist
|
|
175
|
-
|
|
176
|
-
childEntities.forEach((child) => {
|
|
177
|
-
child._createEntity(app);
|
|
178
|
-
});
|
|
179
|
-
childEntities.forEach((child) => {
|
|
180
|
-
child._buildHierarchy(app);
|
|
181
|
-
});
|
|
202
|
+
buildDescendantEntities(this, app);
|
|
182
203
|
}
|
|
183
204
|
}
|
|
184
205
|
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { CameraComponentElement } from './components/camera-component';
|
|
|
21
21
|
import { CollisionComponentElement } from './components/collision-component';
|
|
22
22
|
import { ComponentElement } from './components/component';
|
|
23
23
|
import { ElementComponentElement } from './components/element-component';
|
|
24
|
+
import { JointComponentElement } from './components/joint-component';
|
|
24
25
|
import { LayoutChildComponentElement } from './components/layoutchild-component';
|
|
25
26
|
import { LayoutGroupComponentElement } from './components/layoutgroup-component';
|
|
26
27
|
import { LightComponentElement } from './components/light-component';
|
|
@@ -50,6 +51,7 @@ import type {
|
|
|
50
51
|
|
|
51
52
|
declare global {
|
|
52
53
|
interface HTMLElementEventMap {
|
|
54
|
+
break: CustomEvent;
|
|
53
55
|
scriptattributeschange: ScriptAttributesChangeEvent;
|
|
54
56
|
scriptenablechange: ScriptEnableChangeEvent;
|
|
55
57
|
scriptnamechange: ScriptNameChangeEvent;
|
|
@@ -64,6 +66,7 @@ declare global {
|
|
|
64
66
|
'pc-element': ElementComponentElement;
|
|
65
67
|
'pc-entity': EntityElement;
|
|
66
68
|
'pc-gsplat': GSplatComponentElement;
|
|
69
|
+
'pc-joint': JointComponentElement;
|
|
67
70
|
'pc-layoutchild': LayoutChildComponentElement;
|
|
68
71
|
'pc-layoutgroup': LayoutGroupComponentElement;
|
|
69
72
|
'pc-light': LightComponentElement;
|
|
@@ -98,6 +101,7 @@ export {
|
|
|
98
101
|
CollisionComponentElement,
|
|
99
102
|
ComponentElement,
|
|
100
103
|
ElementComponentElement,
|
|
104
|
+
JointComponentElement,
|
|
101
105
|
LayoutChildComponentElement,
|
|
102
106
|
LayoutGroupComponentElement,
|
|
103
107
|
ParticleSystemComponentElement,
|
|
@@ -123,3 +127,5 @@ export {
|
|
|
123
127
|
};
|
|
124
128
|
|
|
125
129
|
export type { AsyncElementTagName } from './async-element';
|
|
130
|
+
export type { HierarchyMaterial, HierarchyNode } from './model';
|
|
131
|
+
export type { MaterialOverrides } from './node';
|
package/src/loading-bar.ts
CHANGED
|
@@ -8,8 +8,9 @@ const REMOVAL_DELAY_MS = 250;
|
|
|
8
8
|
* All styling is inline, so the library injects no stylesheet. The colors and height resolve CSS
|
|
9
9
|
* custom properties — `--pc-loading-bar-color`, `--pc-loading-bar-background` and
|
|
10
10
|
* `--pc-loading-bar-height` — so a page can theme the bar from `pc-app` or `:root`.
|
|
11
|
+
* @internal
|
|
11
12
|
*/
|
|
12
|
-
class LoadingBar {
|
|
13
|
+
export class LoadingBar {
|
|
13
14
|
private _track: HTMLDivElement;
|
|
14
15
|
|
|
15
16
|
private _fill: HTMLDivElement;
|
|
@@ -118,5 +119,3 @@ class LoadingBar {
|
|
|
118
119
|
this._track.remove();
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
-
export { LoadingBar };
|
package/src/material.ts
CHANGED
|
@@ -251,6 +251,8 @@ class MaterialElement extends HTMLElement {
|
|
|
251
251
|
|
|
252
252
|
private _metalnessMapUv = 0;
|
|
253
253
|
|
|
254
|
+
private _name = 'Untitled';
|
|
255
|
+
|
|
254
256
|
private _normalMap = '';
|
|
255
257
|
|
|
256
258
|
private _normalMapOffset = new Vec2(0, 0);
|
|
@@ -402,6 +404,7 @@ class MaterialElement extends HTMLElement {
|
|
|
402
404
|
material.metalnessMapRotation = this._metalnessMapRotation;
|
|
403
405
|
material.metalnessMapTiling = this._metalnessMapTiling;
|
|
404
406
|
material.metalnessMapUv = this._metalnessMapUv;
|
|
407
|
+
material.name = this._name;
|
|
405
408
|
material.normalMapOffset = this._normalMapOffset;
|
|
406
409
|
material.normalMapRotation = this._normalMapRotation;
|
|
407
410
|
material.normalMapTiling = this._normalMapTiling;
|
|
@@ -1615,6 +1618,28 @@ class MaterialElement extends HTMLElement {
|
|
|
1615
1618
|
return this._metalnessMapUv;
|
|
1616
1619
|
}
|
|
1617
1620
|
|
|
1621
|
+
/**
|
|
1622
|
+
* Sets the name of the material.
|
|
1623
|
+
* @param value - The material name.
|
|
1624
|
+
*/
|
|
1625
|
+
set name(value: string) {
|
|
1626
|
+
this._name = value;
|
|
1627
|
+
if (this.material) {
|
|
1628
|
+
// A label rather than shader state, so no update() is scheduled
|
|
1629
|
+
this.material.name = value;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
/**
|
|
1634
|
+
* Gets the name of the material - the label shown wherever materials surface by name, such
|
|
1635
|
+
* as profilers, GPU captures and the assignments `pc-model.hierarchy()` reports. Purely a
|
|
1636
|
+
* label: element references resolve through `id`.
|
|
1637
|
+
* @returns The material name.
|
|
1638
|
+
*/
|
|
1639
|
+
get name() {
|
|
1640
|
+
return this._name;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1618
1643
|
/**
|
|
1619
1644
|
* Sets the id of the `pc-asset` to use as the normal map.
|
|
1620
1645
|
* @param value - The asset id.
|
|
@@ -2243,6 +2268,7 @@ class MaterialElement extends HTMLElement {
|
|
|
2243
2268
|
'metalness-map-rotation',
|
|
2244
2269
|
'metalness-map-tiling',
|
|
2245
2270
|
'metalness-map-uv',
|
|
2271
|
+
'name',
|
|
2246
2272
|
'normal-map',
|
|
2247
2273
|
'normal-map-offset',
|
|
2248
2274
|
'normal-map-rotation',
|
|
@@ -2444,6 +2470,9 @@ class MaterialElement extends HTMLElement {
|
|
|
2444
2470
|
case 'metalness-map-uv':
|
|
2445
2471
|
this.metalnessMapUv = parseNumber(newValue, 0, name);
|
|
2446
2472
|
break;
|
|
2473
|
+
case 'name':
|
|
2474
|
+
this.name = newValue ?? 'Untitled';
|
|
2475
|
+
break;
|
|
2447
2476
|
case 'normal-map':
|
|
2448
2477
|
this.normalMap = newValue ?? '';
|
|
2449
2478
|
break;
|
package/src/model.ts
CHANGED
|
@@ -3,6 +3,101 @@ import type { ContainerResource, Entity, EventHandle } from 'playcanvas';
|
|
|
3
3
|
import { useAsset } from './asset';
|
|
4
4
|
import { AsyncElement } from './async-element';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* One material assignment of a {@link HierarchyNode} with a render component: a mesh instance's
|
|
8
|
+
* position within the component and the runtime name of its current material.
|
|
9
|
+
*/
|
|
10
|
+
type HierarchyMaterial = {
|
|
11
|
+
/** The mesh instance's position in the render component's `meshInstances` array. */
|
|
12
|
+
index: number;
|
|
13
|
+
/**
|
|
14
|
+
* The runtime name of the mesh instance's current material, reported as-is: the engine
|
|
15
|
+
* names an unnamed glTF material `Untitled`, and assigns a shared material named
|
|
16
|
+
* `defaultGlbMaterial` to a primitive authored without one — neither is a unique authored
|
|
17
|
+
* identifier. `null` when a script has cleared the assignment.
|
|
18
|
+
*/
|
|
19
|
+
name: string | null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* One node of the tree returned by {@link ModelElement.hierarchy}. A plain-data snapshot —
|
|
24
|
+
* `JSON.stringify` serializes it — whose `toString()` renders the node's subtree as a printable
|
|
25
|
+
* tree.
|
|
26
|
+
*/
|
|
27
|
+
type HierarchyNode = {
|
|
28
|
+
/**
|
|
29
|
+
* The node's name as instantiated, which is the name `pc-node` binding resolves: the engine
|
|
30
|
+
* parser synthesizes `node_<index>` names for unnamed nodes and renames identically named
|
|
31
|
+
* siblings apart (`Wheel`, `Wheel1`, ...), so it can differ from the name authored in the
|
|
32
|
+
* source asset.
|
|
33
|
+
*/
|
|
34
|
+
name: string;
|
|
35
|
+
/**
|
|
36
|
+
* The node's `/`-separated path below the model root — the path a `pc-node` bound to this
|
|
37
|
+
* node reports. The root's path is its own name.
|
|
38
|
+
*/
|
|
39
|
+
path: string;
|
|
40
|
+
/**
|
|
41
|
+
* The node's position among identically named nodes in the model, counted in depth-first
|
|
42
|
+
* order over the whole tree: the match a `pc-node`'s `index` attribute selects when `name`
|
|
43
|
+
* alone is ambiguous.
|
|
44
|
+
*/
|
|
45
|
+
index: number;
|
|
46
|
+
/** The types of the components attached to the node (e.g. 'render'), sorted. */
|
|
47
|
+
components: string[];
|
|
48
|
+
/**
|
|
49
|
+
* The material assignments of the node's render component, one entry per mesh instance in
|
|
50
|
+
* component order. Empty for a node without a render component.
|
|
51
|
+
*/
|
|
52
|
+
materials: HierarchyMaterial[];
|
|
53
|
+
/** The node's children. */
|
|
54
|
+
children: HierarchyNode[];
|
|
55
|
+
/**
|
|
56
|
+
* Renders the subtree rooted at this node as a printable tree, one line per node: the name,
|
|
57
|
+
* `[index]` after a name that several nodes in the model share, the component types in
|
|
58
|
+
* parentheses, and the material names of a render component in braces.
|
|
59
|
+
*/
|
|
60
|
+
toString(): string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Formats one line of the printable hierarchy: the node's name, an `[index]` marker when the
|
|
65
|
+
* name is shared by several nodes in the model, the attached component types, and the material
|
|
66
|
+
* names of a render component.
|
|
67
|
+
*
|
|
68
|
+
* @param node - The node to format.
|
|
69
|
+
* @param counts - The number of nodes bearing each name.
|
|
70
|
+
* @returns The formatted line.
|
|
71
|
+
*/
|
|
72
|
+
const formatNode = (node: HierarchyNode, counts: ReadonlyMap<string, number>): string => {
|
|
73
|
+
const index = (counts.get(node.name) ?? 0) > 1 ? ` [${node.index}]` : '';
|
|
74
|
+
const components = node.components.length > 0 ? ` (${node.components.join(', ')})` : '';
|
|
75
|
+
// Braces rather than brackets: `[N]` already means a match index on this line
|
|
76
|
+
const materials =
|
|
77
|
+
node.materials.length > 0 ? ` {${node.materials.map((slot) => slot.name ?? 'null').join(', ')}}` : '';
|
|
78
|
+
return `${node.name}${index}${components}${materials}`;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Formats the printable form of a hierarchy subtree.
|
|
83
|
+
*
|
|
84
|
+
* @param root - The subtree root.
|
|
85
|
+
* @param counts - The number of nodes bearing each name.
|
|
86
|
+
* @returns The tree, one line per node.
|
|
87
|
+
*/
|
|
88
|
+
const formatHierarchy = (root: HierarchyNode, counts: ReadonlyMap<string, number>): string => {
|
|
89
|
+
const lines = [formatNode(root, counts)];
|
|
90
|
+
const walk = (node: HierarchyNode, prefix: string) => {
|
|
91
|
+
node.children.forEach((child, i) => {
|
|
92
|
+
const last = i === node.children.length - 1;
|
|
93
|
+
lines.push(`${prefix}${last ? '└─ ' : '├─ '}${formatNode(child, counts)}`);
|
|
94
|
+
walk(child, `${prefix}${last ? ' ' : '│ '}`);
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
walk(root, '');
|
|
98
|
+
return lines.join('\n');
|
|
99
|
+
};
|
|
100
|
+
|
|
6
101
|
/**
|
|
7
102
|
* The ModelElement interface provides properties and methods for manipulating
|
|
8
103
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-model/ | `<pc-model>`} elements.
|
|
@@ -55,6 +150,66 @@ class ModelElement extends AsyncElement {
|
|
|
55
150
|
return this._entity;
|
|
56
151
|
}
|
|
57
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Returns a snapshot of the instantiated node tree, or `null` while there is none (the
|
|
155
|
+
* container asset has not loaded, or the element has left the document). One call grounds a
|
|
156
|
+
* session — a browser console, a test, an agent — in the vocabulary `pc-node` binding
|
|
157
|
+
* resolves against: the instantiated names ({@link HierarchyNode.name}), paths, match
|
|
158
|
+
* indices, attached component types and the material assignments of render components
|
|
159
|
+
* ({@link HierarchyNode.materials}). `String(...)` of the result, or of any node in it,
|
|
160
|
+
* is the printable form.
|
|
161
|
+
*
|
|
162
|
+
* The snapshot is plain data, computed afresh each call: it does not follow later changes
|
|
163
|
+
* to the hierarchy, and mutating it changes nothing.
|
|
164
|
+
*
|
|
165
|
+
* @returns The root of the instantiated node tree, or `null`.
|
|
166
|
+
*/
|
|
167
|
+
hierarchy(): HierarchyNode | null {
|
|
168
|
+
const root = this._entity;
|
|
169
|
+
if (!root) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Ordinals are assigned in the traversal resolution searches — pre-order depth-first
|
|
174
|
+
// from the model root, the root itself included — so each node's index is exactly what
|
|
175
|
+
// a pc-node's index attribute selects. Once the walk completes, the map holds the total
|
|
176
|
+
// count per name, which is what the printable form reads to annotate only shared names.
|
|
177
|
+
const ordinals = new Map<string, number>();
|
|
178
|
+
|
|
179
|
+
const describe = (entity: Entity, pathBelowRoot: string): HierarchyNode => {
|
|
180
|
+
const index = ordinals.get(entity.name) ?? 0;
|
|
181
|
+
ordinals.set(entity.name, index + 1);
|
|
182
|
+
|
|
183
|
+
const node: HierarchyNode = {
|
|
184
|
+
name: entity.name,
|
|
185
|
+
// The root has no path below itself; its own name stands in, as it does for
|
|
186
|
+
// the path a pc-node bound to the root reports.
|
|
187
|
+
path: pathBelowRoot || entity.name,
|
|
188
|
+
index,
|
|
189
|
+
// A plain GraphNode grafted into the hierarchy has no component storage
|
|
190
|
+
components: Object.keys(entity.c ?? {}).sort(),
|
|
191
|
+
materials: (entity.render?.meshInstances ?? []).map((meshInstance, slot) => ({
|
|
192
|
+
index: slot,
|
|
193
|
+
name: meshInstance.material?.name ?? null
|
|
194
|
+
})),
|
|
195
|
+
children: entity.children.map((child) =>
|
|
196
|
+
describe(child as Entity, pathBelowRoot ? `${pathBelowRoot}/${child.name}` : child.name)
|
|
197
|
+
)
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Non-enumerable, keeping the snapshot plain data under JSON.stringify, spreads and
|
|
201
|
+
// key enumeration. Deferred to call time, by which the ordinal map holds its totals.
|
|
202
|
+
Object.defineProperty(node, 'toString', {
|
|
203
|
+
enumerable: false,
|
|
204
|
+
value: () => formatHierarchy(node, ordinals)
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
return node;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
return describe(root, '');
|
|
211
|
+
}
|
|
212
|
+
|
|
58
213
|
connectedCallback() {
|
|
59
214
|
// A model outside an application is inert and never becomes ready, so awaiting it hangs.
|
|
60
215
|
// Warn rather than fail silently, naming the parent it requires, as every other misplaced
|
|
@@ -235,3 +390,4 @@ class ModelElement extends AsyncElement {
|
|
|
235
390
|
customElements.define('pc-model', ModelElement);
|
|
236
391
|
|
|
237
392
|
export { ModelElement };
|
|
393
|
+
export type { HierarchyMaterial, HierarchyNode };
|