@playcanvas/web-components 0.16.0 → 0.18.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 +18 -13
- package/dist/app.d.ts +18 -13
- package/dist/asset.d.cts +13 -3
- package/dist/asset.d.ts +13 -3
- package/dist/async-element.d.cts +13 -13
- package/dist/async-element.d.ts +13 -13
- package/dist/components/anim-clip.d.cts +4 -0
- package/dist/components/anim-clip.d.ts +4 -0
- package/dist/components/anim-component.d.cts +38 -5
- package/dist/components/anim-component.d.ts +38 -5
- package/dist/components/audio-listener-component.d.cts +26 -0
- package/dist/components/audio-listener-component.d.ts +26 -0
- package/dist/components/button-component.d.cts +10 -1
- package/dist/components/button-component.d.ts +10 -1
- package/dist/components/camera-component.d.cts +37 -1
- package/dist/components/camera-component.d.ts +37 -1
- package/dist/components/collision-component.d.cts +9 -1
- package/dist/components/collision-component.d.ts +9 -1
- package/dist/components/component.d.cts +3 -2
- package/dist/components/component.d.ts +3 -2
- package/dist/components/element-component.d.cts +11 -0
- package/dist/components/element-component.d.ts +11 -0
- package/dist/components/gsplat-component.d.cts +6 -0
- package/dist/components/gsplat-component.d.ts +6 -0
- package/dist/components/joint-component.d.cts +14 -2
- package/dist/components/joint-component.d.ts +14 -2
- package/dist/components/{layoutchild-component.d.cts → layout-child-component.d.cts} +7 -1
- package/dist/components/{layoutchild-component.d.ts → layout-child-component.d.ts} +7 -1
- package/dist/components/{layoutgroup-component.d.cts → layout-group-component.d.cts} +7 -1
- package/dist/components/{layoutgroup-component.d.ts → layout-group-component.d.ts} +7 -1
- package/dist/components/light-component.d.cts +52 -1
- package/dist/components/light-component.d.ts +52 -1
- package/dist/components/{particlesystem-component.d.cts → particle-system-component.d.cts} +7 -1
- package/dist/components/{particlesystem-component.d.ts → particle-system-component.d.ts} +7 -1
- package/dist/components/render-component.d.cts +7 -0
- package/dist/components/render-component.d.ts +7 -0
- package/dist/components/{rigidbody-component.d.cts → rigid-body-component.d.cts} +8 -1
- package/dist/components/{rigidbody-component.d.ts → rigid-body-component.d.ts} +8 -1
- package/dist/components/screen-component.d.cts +6 -0
- package/dist/components/screen-component.d.ts +6 -0
- package/dist/components/script-component.d.cts +26 -12
- package/dist/components/script-component.d.ts +26 -12
- package/dist/components/{script.d.cts → script-instance.d.cts} +11 -7
- package/dist/components/{script.d.ts → script-instance.d.ts} +11 -7
- package/dist/components/{scrollview-component.d.cts → scroll-view-component.d.cts} +7 -1
- package/dist/components/{scrollview-component.d.ts → scroll-view-component.d.ts} +7 -1
- package/dist/components/scrollbar-component.d.cts +6 -0
- package/dist/components/scrollbar-component.d.ts +6 -0
- package/dist/components/sound-component.d.cts +7 -1
- package/dist/components/sound-component.d.ts +7 -1
- package/dist/components/sound-slot.d.cts +5 -2
- package/dist/components/sound-slot.d.ts +5 -2
- package/dist/custom-elements.json +1852 -974
- package/dist/entity-base.d.cts +7 -6
- package/dist/entity-base.d.ts +7 -6
- package/dist/entity-owner.d.cts +118 -0
- package/dist/entity-owner.d.ts +118 -0
- package/dist/entity.d.cts +14 -102
- package/dist/entity.d.ts +14 -102
- package/dist/index.d.cts +26 -22
- package/dist/index.d.ts +26 -22
- package/dist/material.d.cts +18 -7
- package/dist/material.d.ts +18 -7
- package/dist/model.d.cts +72 -24
- package/dist/model.d.ts +72 -24
- package/dist/node.d.cts +6 -1
- package/dist/node.d.ts +6 -1
- package/dist/pwc.cjs +807 -293
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +807 -293
- 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 +805 -292
- package/dist/pwc.mjs.map +1 -1
- package/dist/scene.d.cts +3 -0
- package/dist/scene.d.ts +3 -0
- package/dist/sky.d.cts +4 -0
- package/dist/sky.d.ts +4 -0
- package/dist/vscode.html-custom-data.json +351 -121
- package/dist/{module.d.cts → wasm.d.cts} +10 -6
- package/dist/{module.d.ts → wasm.d.ts} +10 -6
- package/dist/web-types.json +465 -291
- package/package.json +7 -7
- package/src/app.ts +49 -35
- package/src/asset.ts +13 -4
- package/src/async-element.ts +14 -14
- package/src/components/anim-clip.ts +4 -0
- package/src/components/anim-component.ts +89 -14
- package/src/components/audio-listener-component.ts +36 -0
- package/src/components/button-component.ts +10 -1
- package/src/components/camera-component.ts +65 -6
- package/src/components/collision-component.ts +9 -1
- package/src/components/component.ts +4 -3
- package/src/components/element-component.ts +11 -0
- package/src/components/gsplat-component.ts +6 -0
- package/src/components/joint-component.ts +14 -2
- package/src/components/{layoutchild-component.ts → layout-child-component.ts} +8 -2
- package/src/components/{layoutgroup-component.ts → layout-group-component.ts} +8 -2
- package/src/components/light-component.ts +110 -17
- package/src/components/{particlesystem-component.ts → particle-system-component.ts} +8 -2
- package/src/components/render-component.ts +7 -0
- package/src/components/{rigidbody-component.ts → rigid-body-component.ts} +9 -2
- package/src/components/screen-component.ts +6 -0
- package/src/components/script-component.ts +66 -42
- package/src/components/{script.ts → script-instance.ts} +18 -14
- package/src/components/{scrollview-component.ts → scroll-view-component.ts} +8 -2
- package/src/components/scrollbar-component.ts +6 -0
- package/src/components/sound-component.ts +8 -2
- package/src/components/sound-slot.ts +7 -4
- package/src/entity-base.ts +8 -7
- package/src/entity-owner.ts +298 -0
- package/src/entity.ts +21 -271
- package/src/index.ts +38 -25
- package/src/material.ts +19 -8
- package/src/model.ts +164 -72
- package/src/node.ts +15 -6
- package/src/scene.ts +3 -0
- package/src/sky.ts +4 -0
- package/src/{module.ts → wasm.ts} +12 -8
- package/dist/components/listener-component.d.cts +0 -20
- package/dist/components/listener-component.d.ts +0 -20
- package/src/components/listener-component.ts +0 -30
package/src/index.ts
CHANGED
|
@@ -9,38 +9,39 @@
|
|
|
9
9
|
|
|
10
10
|
/* eslint-disable import-x/order */
|
|
11
11
|
|
|
12
|
-
// Note that order matters here (e.g. pc-entity must be defined before components)
|
|
12
|
+
// Note that order matters here (e.g. pc-entity and pc-model must be defined before components)
|
|
13
13
|
import { AsyncElement, whenReady } from './async-element';
|
|
14
|
-
import {
|
|
14
|
+
import { WasmElement } from './wasm';
|
|
15
15
|
import { AppElement } from './app';
|
|
16
16
|
import { EntityElement } from './entity';
|
|
17
|
+
import { ModelElement } from './model';
|
|
17
18
|
import { AssetElement } from './asset';
|
|
18
19
|
import { AnimComponentElement } from './components/anim-component';
|
|
19
20
|
import { AnimClipElement } from './components/anim-clip';
|
|
20
|
-
import {
|
|
21
|
+
import { AudioListenerComponentElement } from './components/audio-listener-component';
|
|
21
22
|
import { ButtonComponentElement } from './components/button-component';
|
|
22
23
|
import { CameraComponentElement } from './components/camera-component';
|
|
23
24
|
import { CollisionComponentElement } from './components/collision-component';
|
|
24
25
|
import { ComponentElement } from './components/component';
|
|
25
26
|
import { ElementComponentElement } from './components/element-component';
|
|
26
27
|
import { JointComponentElement } from './components/joint-component';
|
|
27
|
-
import { LayoutChildComponentElement } from './components/
|
|
28
|
-
import { LayoutGroupComponentElement } from './components/
|
|
28
|
+
import { LayoutChildComponentElement } from './components/layout-child-component';
|
|
29
|
+
import { LayoutGroupComponentElement } from './components/layout-group-component';
|
|
29
30
|
import { LightComponentElement } from './components/light-component';
|
|
30
|
-
import { ParticleSystemComponentElement } from './components/
|
|
31
|
+
import { ParticleSystemComponentElement } from './components/particle-system-component';
|
|
31
32
|
import { RenderComponentElement } from './components/render-component';
|
|
32
|
-
import { RigidBodyComponentElement } from './components/
|
|
33
|
+
import { RigidBodyComponentElement } from './components/rigid-body-component';
|
|
33
34
|
import { ScreenComponentElement } from './components/screen-component';
|
|
34
35
|
import { ScrollbarComponentElement } from './components/scrollbar-component';
|
|
35
|
-
import { ScrollViewComponentElement } from './components/
|
|
36
|
+
import { ScrollViewComponentElement } from './components/scroll-view-component';
|
|
36
37
|
import { ScriptComponentElement } from './components/script-component';
|
|
37
|
-
import {
|
|
38
|
+
import { ScriptInstanceElement } from './components/script-instance';
|
|
38
39
|
import { SoundComponentElement } from './components/sound-component';
|
|
39
40
|
import { SoundSlotElement } from './components/sound-slot';
|
|
40
41
|
import { GSplatComponentElement } from './components/gsplat-component';
|
|
41
42
|
import { EntityBaseElement } from './entity-base';
|
|
43
|
+
import { EntityOwnerElement } from './entity-owner';
|
|
42
44
|
import { MaterialElement } from './material';
|
|
43
|
-
import { ModelElement } from './model';
|
|
44
45
|
import { NodeElement } from './node';
|
|
45
46
|
import { SceneElement } from './scene';
|
|
46
47
|
import { SkyElement } from './sky';
|
|
@@ -71,32 +72,32 @@ declare global {
|
|
|
71
72
|
'pc-entity': EntityElement;
|
|
72
73
|
'pc-gsplat': GSplatComponentElement;
|
|
73
74
|
'pc-joint': JointComponentElement;
|
|
74
|
-
'pc-
|
|
75
|
-
'pc-
|
|
75
|
+
'pc-layout-child': LayoutChildComponentElement;
|
|
76
|
+
'pc-layout-group': LayoutGroupComponentElement;
|
|
76
77
|
'pc-light': LightComponentElement;
|
|
77
|
-
'pc-listener':
|
|
78
|
+
'pc-audio-listener': AudioListenerComponentElement;
|
|
78
79
|
'pc-material': MaterialElement;
|
|
79
80
|
'pc-model': ModelElement;
|
|
80
|
-
'pc-
|
|
81
|
+
'pc-wasm': WasmElement;
|
|
81
82
|
'pc-node': NodeElement;
|
|
82
|
-
'pc-
|
|
83
|
+
'pc-particle-system': ParticleSystemComponentElement;
|
|
83
84
|
'pc-render': RenderComponentElement;
|
|
84
|
-
'pc-
|
|
85
|
+
'pc-rigid-body': RigidBodyComponentElement;
|
|
85
86
|
'pc-scene': SceneElement;
|
|
86
87
|
'pc-screen': ScreenComponentElement;
|
|
87
|
-
'pc-script':
|
|
88
|
-
'pc-
|
|
88
|
+
'pc-script-instance': ScriptInstanceElement;
|
|
89
|
+
'pc-script': ScriptComponentElement;
|
|
89
90
|
'pc-scrollbar': ScrollbarComponentElement;
|
|
90
|
-
'pc-
|
|
91
|
+
'pc-scroll-view': ScrollViewComponentElement;
|
|
91
92
|
'pc-sky': SkyElement;
|
|
92
|
-
'pc-sound': SoundSlotElement;
|
|
93
|
-
'pc-
|
|
93
|
+
'pc-sound-slot': SoundSlotElement;
|
|
94
|
+
'pc-sound': SoundComponentElement;
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
export {
|
|
98
99
|
AsyncElement,
|
|
99
|
-
|
|
100
|
+
WasmElement,
|
|
100
101
|
AppElement,
|
|
101
102
|
EntityElement,
|
|
102
103
|
AssetElement,
|
|
@@ -112,18 +113,19 @@ export {
|
|
|
112
113
|
LayoutGroupComponentElement,
|
|
113
114
|
ParticleSystemComponentElement,
|
|
114
115
|
LightComponentElement,
|
|
115
|
-
|
|
116
|
+
AudioListenerComponentElement,
|
|
116
117
|
RenderComponentElement,
|
|
117
118
|
RigidBodyComponentElement,
|
|
118
119
|
ScreenComponentElement,
|
|
119
120
|
ScrollbarComponentElement,
|
|
120
121
|
ScrollViewComponentElement,
|
|
121
122
|
ScriptComponentElement,
|
|
122
|
-
|
|
123
|
+
ScriptInstanceElement,
|
|
123
124
|
SoundComponentElement,
|
|
124
125
|
SoundSlotElement,
|
|
125
126
|
GSplatComponentElement,
|
|
126
127
|
EntityBaseElement,
|
|
128
|
+
EntityOwnerElement,
|
|
127
129
|
MaterialElement,
|
|
128
130
|
ModelElement,
|
|
129
131
|
NodeElement,
|
|
@@ -132,6 +134,17 @@ export {
|
|
|
132
134
|
whenReady
|
|
133
135
|
};
|
|
134
136
|
|
|
137
|
+
export type { AddressMode, MagFilterMode, MinFilterMode } from './asset';
|
|
135
138
|
export type { AsyncElementTagName } from './async-element';
|
|
139
|
+
export type { JointType, MotionMode } from './components/joint-component';
|
|
140
|
+
export type {
|
|
141
|
+
BlendType,
|
|
142
|
+
ColorChannel,
|
|
143
|
+
CullMode,
|
|
144
|
+
FresnelModel,
|
|
145
|
+
OccludeSpecular,
|
|
146
|
+
OpacityDither,
|
|
147
|
+
ScalarChannel
|
|
148
|
+
} from './material';
|
|
136
149
|
export type { HierarchyMaterial, HierarchyNode } from './model';
|
|
137
|
-
export type { MaterialOverrides } from './node';
|
|
150
|
+
export type { MaterialOverrides, NodeBindingState } from './node';
|
package/src/material.ts
CHANGED
|
@@ -29,7 +29,8 @@ import type { AppElement } from './app';
|
|
|
29
29
|
import { useAsset } from './asset';
|
|
30
30
|
import { parseBool, parseColor, parseEnum, parseNumber, parseVec2 } from './parse';
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
/** The blend modes for a material. */
|
|
33
|
+
export type BlendType =
|
|
33
34
|
| 'none'
|
|
34
35
|
| 'normal'
|
|
35
36
|
| 'additive'
|
|
@@ -56,7 +57,8 @@ const blendTypes = new Map<BlendType, number>([
|
|
|
56
57
|
['subtractive', BLEND_SUBTRACTIVE]
|
|
57
58
|
]);
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
/** The face culling modes for a material. */
|
|
61
|
+
export type CullMode = 'none' | 'back' | 'front' | 'front-and-back';
|
|
60
62
|
|
|
61
63
|
const cullModes = new Map<CullMode, number>([
|
|
62
64
|
['none', CULLFACE_NONE],
|
|
@@ -65,14 +67,16 @@ const cullModes = new Map<CullMode, number>([
|
|
|
65
67
|
['front-and-back', CULLFACE_FRONTANDBACK]
|
|
66
68
|
]);
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
/** The Fresnel models for a material. */
|
|
71
|
+
export type FresnelModel = 'none' | 'schlick';
|
|
69
72
|
|
|
70
73
|
const fresnelModels = new Map<FresnelModel, number>([
|
|
71
74
|
['none', FRESNEL_NONE],
|
|
72
75
|
['schlick', FRESNEL_SCHLICK]
|
|
73
76
|
]);
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
/** The specular occlusion modes for a material. */
|
|
79
|
+
export type OccludeSpecular = 'none' | 'ao' | 'gloss-dependent';
|
|
76
80
|
|
|
77
81
|
const occludeSpeculars = new Map<OccludeSpecular, number>([
|
|
78
82
|
['none', SPECOCC_NONE],
|
|
@@ -80,17 +84,20 @@ const occludeSpeculars = new Map<OccludeSpecular, number>([
|
|
|
80
84
|
['gloss-dependent', SPECOCC_GLOSSDEPENDENT]
|
|
81
85
|
]);
|
|
82
86
|
|
|
87
|
+
/** The opacity dithering modes for a material. */
|
|
88
|
+
export type OpacityDither = 'none' | 'bayer8' | 'bluenoise' | 'ignnoise';
|
|
89
|
+
|
|
83
90
|
// The DITHER_* constants are strings whose values are exactly these names, so a parsed value is
|
|
84
91
|
// assigned to the material unchanged rather than mapped through a table.
|
|
85
|
-
type OpacityDither = 'none' | 'bayer8' | 'bluenoise' | 'ignnoise';
|
|
86
|
-
|
|
87
92
|
const opacityDithers: OpacityDither[] = ['none', 'bayer8', 'bluenoise', 'ignnoise'];
|
|
88
93
|
|
|
89
|
-
|
|
94
|
+
/** The texture channels a color map can sample. */
|
|
95
|
+
export type ColorChannel = 'r' | 'g' | 'b' | 'a' | 'rgb';
|
|
90
96
|
|
|
91
97
|
const colorChannels: ColorChannel[] = ['r', 'g', 'b', 'a', 'rgb'];
|
|
92
98
|
|
|
93
|
-
|
|
99
|
+
/** The texture channels a scalar map can sample. */
|
|
100
|
+
export type ScalarChannel = 'r' | 'g' | 'b' | 'a';
|
|
94
101
|
|
|
95
102
|
const scalarChannels: ScalarChannel[] = ['r', 'g', 'b', 'a'];
|
|
96
103
|
|
|
@@ -136,6 +143,10 @@ type TextureSlot =
|
|
|
136
143
|
* The two aliases are documented here rather than on an accessor, because they resolve to the
|
|
137
144
|
* `gloss` properties and would otherwise inherit gloss's description - which reads inverted.
|
|
138
145
|
*
|
|
146
|
+
* @elementSummary The `<pc-material>` element defines a physically based material, which
|
|
147
|
+
* `<pc-render>` elements apply by naming its `id`. It is metal/rough by default, with `metalness`
|
|
148
|
+
* starting at 0. Must be a direct child of `<pc-app>`.
|
|
149
|
+
*
|
|
139
150
|
* @attribute {number} roughness - The roughness of the material, from 0 (shiny) to 1 (rough). An
|
|
140
151
|
* alias for `gloss` that also inverts it, so do not combine it with the `gloss` attributes.
|
|
141
152
|
* @attribute {string} roughness-map - The id of the `pc-asset` to use as the roughness map. An
|
package/src/model.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ContainerResource, Entity, EventHandle } from 'playcanvas';
|
|
2
|
+
import { Vec3 } from 'playcanvas';
|
|
2
3
|
|
|
3
4
|
import { useAsset } from './asset';
|
|
4
|
-
import {
|
|
5
|
+
import { POINTER_ATTRIBUTES } from './entity-base';
|
|
6
|
+
import { buildDescendantEntities, EntityOwnerElement } from './entity-owner';
|
|
7
|
+
import { parseBool, parseTags, parseVec3 } from './parse';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* One material assignment of a {@link HierarchyNode} with a render component: a mesh instance's
|
|
@@ -104,14 +107,47 @@ const formatHierarchy = (root: HierarchyNode, counts: ReadonlyMap<string, number
|
|
|
104
107
|
* The ModelElement interface also inherits the properties and methods of the
|
|
105
108
|
* {@link HTMLElement} interface.
|
|
106
109
|
*
|
|
107
|
-
* The element
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* hierarchy. A `pc-model` outside a `pc-app`, or referencing an unknown asset id, warns and never
|
|
113
|
-
* becomes ready.
|
|
110
|
+
* The element creates and fronts a stable host entity: `entity` is that host, created when the
|
|
111
|
+
* application builds its hierarchy and kept across `asset` changes, so the element's transform
|
|
112
|
+
* and tags are instance placement that composes with whatever transform the asset authored on
|
|
113
|
+
* its root. The instantiated content is parented beneath the host and exposed as
|
|
114
|
+
* {@link contentEntity}.
|
|
114
115
|
*
|
|
116
|
+
* The element becomes ready once its current asset selection has settled: the container asset
|
|
117
|
+
* has loaded and its content root has been parented beneath the host, the load has failed
|
|
118
|
+
* (`contentEntity` stays `null` — listen for `error`, or check `contentEntity`, to tell the
|
|
119
|
+
* outcomes apart), or no asset is assigned at all. Changing `asset` re-arms readiness and
|
|
120
|
+
* instantiates anew, so a `ready()` obtained after the change resolves against the new content.
|
|
121
|
+
* A `pc-model` outside a `pc-app`, or referencing an unknown asset id, warns and never becomes
|
|
122
|
+
* ready.
|
|
123
|
+
*
|
|
124
|
+
* The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
|
|
125
|
+
* intersects the model's geometry, exactly as for `<pc-entity>` — a hit on a content node that no
|
|
126
|
+
* `pc-node` fronts resolves to this element.
|
|
127
|
+
*
|
|
128
|
+
* @elementSummary The `<pc-model>` element instantiates a 3D model from a container asset
|
|
129
|
+
* (typically a GLB) beneath an entity of its own, so the element's transform and tags place the
|
|
130
|
+
* instance in the scene. Its `<pc-node>` children override what the asset authored. Place it in the
|
|
131
|
+
* `<pc-scene>`, or nest it under a `<pc-entity>`, another `<pc-model>` or a `<pc-node>`.
|
|
132
|
+
*
|
|
133
|
+
* @attribute {boolean} enabled - The enabled state of the model.
|
|
134
|
+
* @attribute {string} name - The name of the model.
|
|
135
|
+
* @attribute {string} position - The position of the model.
|
|
136
|
+
* @attribute {string} rotation - The rotation of the model.
|
|
137
|
+
* @attribute {string} scale - The scale of the model.
|
|
138
|
+
* @attribute {string} tags - The tags of the model.
|
|
139
|
+
* @attribute {string} onpointerenter - Script to run when the pointer moves onto the model.
|
|
140
|
+
* @attribute {string} onpointerleave - Script to run when the pointer moves off the model.
|
|
141
|
+
* @attribute {string} onpointermove - Script to run when the pointer moves over the model.
|
|
142
|
+
* @attribute {string} onpointerdown - Script to run when a pointer button is pressed over the
|
|
143
|
+
* model.
|
|
144
|
+
* @attribute {string} onpointerup - Script to run when a pointer button is released over the
|
|
145
|
+
* model.
|
|
146
|
+
* @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the model.
|
|
147
|
+
* @fires {PointerEvent} pointerleave - Fired when the pointer moves off the model.
|
|
148
|
+
* @fires {PointerEvent} pointermove - Fired when the pointer moves over the model.
|
|
149
|
+
* @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the model.
|
|
150
|
+
* @fires {PointerEvent} pointerup - Fired when a pointer button is released over the model.
|
|
115
151
|
* @fires {Event} load - Fired each time a container asset finishes instantiating, including
|
|
116
152
|
* re-instantiation after `asset` changes. Does not bubble — listen on this element, or use a
|
|
117
153
|
* capture-phase listener on an ancestor.
|
|
@@ -119,16 +155,16 @@ const formatHierarchy = (root: HierarchyNode, counts: ReadonlyMap<string, number
|
|
|
119
155
|
* error in `message`. Does not bubble. The element still becomes ready — readiness means the load
|
|
120
156
|
* settled, not that it succeeded.
|
|
121
157
|
*/
|
|
122
|
-
class ModelElement extends
|
|
158
|
+
class ModelElement extends EntityOwnerElement {
|
|
123
159
|
private _asset = '';
|
|
124
160
|
|
|
125
|
-
private
|
|
161
|
+
private _contentEntity: Entity | null = null;
|
|
126
162
|
|
|
127
163
|
/**
|
|
128
|
-
* Incremented on every new load
|
|
129
|
-
* load that resumes from an await or a load callback abandons
|
|
130
|
-
* on, so a superseded load can neither instantiate a second
|
|
131
|
-
*
|
|
164
|
+
* Incremented on every new load, on disconnect, and when the host entity dies, and captured
|
|
165
|
+
* by a load when it starts. A load that resumes from an await or a load callback abandons
|
|
166
|
+
* itself if the value has moved on, so a superseded load can neither instantiate a second
|
|
167
|
+
* content root nor parent one under a host a newer cycle has already replaced.
|
|
132
168
|
*/
|
|
133
169
|
private _loadGeneration = 0;
|
|
134
170
|
|
|
@@ -142,12 +178,13 @@ class ModelElement extends AsyncElement {
|
|
|
142
178
|
private _errorHandle: EventHandle | null = null;
|
|
143
179
|
|
|
144
180
|
/**
|
|
145
|
-
* The root entity of the instantiated model
|
|
146
|
-
*
|
|
147
|
-
*
|
|
181
|
+
* The root entity of the instantiated model content, parented beneath the host entity.
|
|
182
|
+
* `null` until the container asset has loaded and been instantiated, after a failed load,
|
|
183
|
+
* and again once the element has been removed from the document.
|
|
184
|
+
* @returns The content root entity, or `null`.
|
|
148
185
|
*/
|
|
149
|
-
get
|
|
150
|
-
return this.
|
|
186
|
+
get contentEntity(): Entity | null {
|
|
187
|
+
return this._contentEntity;
|
|
151
188
|
}
|
|
152
189
|
|
|
153
190
|
/**
|
|
@@ -160,12 +197,13 @@ class ModelElement extends AsyncElement {
|
|
|
160
197
|
* is the printable form.
|
|
161
198
|
*
|
|
162
199
|
* The snapshot is plain data, computed afresh each call: it does not follow later changes
|
|
163
|
-
* to the hierarchy, and mutating it changes nothing.
|
|
200
|
+
* to the hierarchy, and mutating it changes nothing. It covers the instantiated content
|
|
201
|
+
* only — the host entity the element fronts is not part of the asset's node tree.
|
|
164
202
|
*
|
|
165
203
|
* @returns The root of the instantiated node tree, or `null`.
|
|
166
204
|
*/
|
|
167
205
|
hierarchy(): HierarchyNode | null {
|
|
168
|
-
const root = this.
|
|
206
|
+
const root = this._contentEntity;
|
|
169
207
|
if (!root) {
|
|
170
208
|
return null;
|
|
171
209
|
}
|
|
@@ -214,19 +252,59 @@ class ModelElement extends AsyncElement {
|
|
|
214
252
|
// A model outside an application is inert and never becomes ready, so awaiting it hangs.
|
|
215
253
|
// Warn rather than fail silently, naming the parent it requires, as every other misplaced
|
|
216
254
|
// element does.
|
|
217
|
-
|
|
255
|
+
const closestApp = this.closestApp;
|
|
256
|
+
if (!closestApp) {
|
|
218
257
|
const label = this._asset ? ` '${this._asset}'` : '';
|
|
219
258
|
console.warn(`pc-model${label} must be a descendant of pc-app - model not created`);
|
|
220
259
|
return;
|
|
221
260
|
}
|
|
222
|
-
|
|
261
|
+
|
|
262
|
+
// If the app is already running, create the host immediately; during a boot, the app's
|
|
263
|
+
// own sweep does it. Either way, _onBuilt starts the content load once the host is
|
|
264
|
+
// parented.
|
|
265
|
+
if (closestApp._hierarchyReady) {
|
|
266
|
+
const app = closestApp.app!;
|
|
267
|
+
|
|
268
|
+
this._createEntity(app);
|
|
269
|
+
this._buildHierarchy(app);
|
|
270
|
+
|
|
271
|
+
// A build that deferred (an unresolved pc-node above) defers the whole subtree with
|
|
272
|
+
// it - the node's bind sweeps it.
|
|
273
|
+
if (this._built) {
|
|
274
|
+
buildDescendantEntities(this, app);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
223
277
|
}
|
|
224
278
|
|
|
225
279
|
disconnectedCallback() {
|
|
280
|
+
// Destroying the host destroys the instantiated content with it, and the destroy hook
|
|
281
|
+
// resets the element. The generation guard comes first so a load suspended on an await
|
|
282
|
+
// cannot resume against the torn-down element.
|
|
226
283
|
this._loadGeneration++;
|
|
227
284
|
this._detachLoadHandlers();
|
|
228
|
-
this.
|
|
229
|
-
|
|
285
|
+
this._entity?.destroy();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Starts (or restarts) the content load once the host has been parented. Readiness is not
|
|
290
|
+
* announced here — it tracks the content settling, not the host entering the scene graph.
|
|
291
|
+
*/
|
|
292
|
+
protected override _onBuilt() {
|
|
293
|
+
this._loadContent();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Extends the owner reset for the content: the engine's destroy cascade has already taken
|
|
298
|
+
* the content root down with the host subtree, so only the reference and the in-flight load
|
|
299
|
+
* are dropped here. The next build re-creates the host and re-instantiates the content.
|
|
300
|
+
*
|
|
301
|
+
* @param entity - The host entity that was destroyed.
|
|
302
|
+
*/
|
|
303
|
+
protected override _onEntityDestroy(entity: Entity) {
|
|
304
|
+
this._loadGeneration++;
|
|
305
|
+
this._detachLoadHandlers();
|
|
306
|
+
this._contentEntity = null;
|
|
307
|
+
super._onEntityDestroy(entity);
|
|
230
308
|
}
|
|
231
309
|
|
|
232
310
|
private _detachLoadHandlers() {
|
|
@@ -237,9 +315,9 @@ class ModelElement extends AsyncElement {
|
|
|
237
315
|
}
|
|
238
316
|
|
|
239
317
|
/**
|
|
240
|
-
* Resolves readiness and dispatches the `load` event. Called once the instantiated
|
|
241
|
-
* has been parented —
|
|
242
|
-
* model's
|
|
318
|
+
* Resolves readiness and dispatches the `load` event. Called once the instantiated content
|
|
319
|
+
* has been parented beneath the host — the host itself is already in the scene graph by
|
|
320
|
+
* then, so a ready model's content always has world transforms.
|
|
243
321
|
*/
|
|
244
322
|
private _announceLoad() {
|
|
245
323
|
this._onReady();
|
|
@@ -247,46 +325,29 @@ class ModelElement extends AsyncElement {
|
|
|
247
325
|
}
|
|
248
326
|
|
|
249
327
|
private _instantiate(container: ContainerResource) {
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
this.
|
|
328
|
+
const content = container.instantiateRenderEntity();
|
|
329
|
+
this._contentEntity = content;
|
|
330
|
+
this._entity!.addChild(content);
|
|
331
|
+
this._announceLoad();
|
|
332
|
+
}
|
|
254
333
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const parentEntityElement = this.closestEntity;
|
|
259
|
-
if (parentEntityElement) {
|
|
260
|
-
parentEntityElement.ready().then(() => {
|
|
261
|
-
if (generation !== this._loadGeneration) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
parentEntityElement.entity!.addChild(entity);
|
|
265
|
-
this._announceLoad();
|
|
266
|
-
});
|
|
267
|
-
} else {
|
|
268
|
-
const appElement = this.closestApp;
|
|
269
|
-
if (appElement) {
|
|
270
|
-
appElement.ready().then(() => {
|
|
271
|
-
if (generation !== this._loadGeneration) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
appElement.app!.root.addChild(entity);
|
|
275
|
-
this._announceLoad();
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
}
|
|
334
|
+
private _destroyContent() {
|
|
335
|
+
this._contentEntity?.destroy();
|
|
336
|
+
this._contentEntity = null;
|
|
279
337
|
}
|
|
280
338
|
|
|
281
|
-
private async
|
|
282
|
-
|
|
339
|
+
private async _loadContent() {
|
|
340
|
+
// The old content goes down synchronously, so a reader that checks after an asset change
|
|
341
|
+
// never sees the outgoing hierarchy. The host survives - components and child entities
|
|
342
|
+
// attached to it carry over to the new content.
|
|
343
|
+
this._destroyContent();
|
|
283
344
|
|
|
284
345
|
// Supersede any load already in flight - only the newest load may instantiate
|
|
285
346
|
const generation = ++this._loadGeneration;
|
|
286
347
|
this._detachLoadHandlers();
|
|
287
348
|
|
|
288
349
|
// Re-arm readiness so a waiter obtained after an asset change resolves against the new
|
|
289
|
-
//
|
|
350
|
+
// content. A no-op on first connection, where readiness is still pending.
|
|
290
351
|
this._resetReady();
|
|
291
352
|
|
|
292
353
|
const appElement = this.closestApp;
|
|
@@ -302,14 +363,25 @@ class ModelElement extends AsyncElement {
|
|
|
302
363
|
return;
|
|
303
364
|
}
|
|
304
365
|
|
|
366
|
+
// The host may not be parented yet - a model under a pc-node that has not bound, reached
|
|
367
|
+
// through the asset setter. Nothing settles here: _onBuilt re-runs this load once the
|
|
368
|
+
// host builds.
|
|
369
|
+
if (!this._entity || !this._built) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (this._asset === '') {
|
|
374
|
+
// No asset assigned is a settled selection: the element is a usable host (components
|
|
375
|
+
// attach, waiters resolve) with no content. Assigning an asset later re-arms.
|
|
376
|
+
this._onReady();
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
305
380
|
const asset = useAsset(this._asset);
|
|
306
381
|
if (!asset) {
|
|
307
|
-
//
|
|
308
|
-
// non-empty one that resolves to nothing is a dead end - say so rather than staying
|
|
382
|
+
// A non-empty id that resolves to nothing is a dead end - say so rather than staying
|
|
309
383
|
// silently pending.
|
|
310
|
-
|
|
311
|
-
console.warn(`pc-model could not find asset '${this._asset}' - model not created`);
|
|
312
|
-
}
|
|
384
|
+
console.warn(`pc-model could not find asset '${this._asset}' - model not created`);
|
|
313
385
|
return;
|
|
314
386
|
}
|
|
315
387
|
|
|
@@ -331,7 +403,7 @@ class ModelElement extends AsyncElement {
|
|
|
331
403
|
if (generation !== this._loadGeneration) {
|
|
332
404
|
return;
|
|
333
405
|
}
|
|
334
|
-
// A failed load settles readiness with a null
|
|
406
|
+
// A failed load settles readiness with a null contentEntity, mirroring pc-asset:
|
|
335
407
|
// readiness means the load settled, not that it succeeded.
|
|
336
408
|
this.dispatchEvent(
|
|
337
409
|
new ErrorEvent('error', {
|
|
@@ -343,11 +415,6 @@ class ModelElement extends AsyncElement {
|
|
|
343
415
|
}
|
|
344
416
|
}
|
|
345
417
|
|
|
346
|
-
private _unloadModel() {
|
|
347
|
-
this._entity?.destroy();
|
|
348
|
-
this._entity = null;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
418
|
/**
|
|
352
419
|
* Sets the id of the `pc-asset` to use for the model.
|
|
353
420
|
* @param value - The asset ID.
|
|
@@ -355,7 +422,7 @@ class ModelElement extends AsyncElement {
|
|
|
355
422
|
set asset(value: string) {
|
|
356
423
|
this._asset = value;
|
|
357
424
|
if (this.isConnected) {
|
|
358
|
-
this.
|
|
425
|
+
this._loadContent();
|
|
359
426
|
}
|
|
360
427
|
}
|
|
361
428
|
|
|
@@ -368,7 +435,7 @@ class ModelElement extends AsyncElement {
|
|
|
368
435
|
}
|
|
369
436
|
|
|
370
437
|
static get observedAttributes() {
|
|
371
|
-
return ['asset'];
|
|
438
|
+
return ['asset', 'enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...POINTER_ATTRIBUTES];
|
|
372
439
|
}
|
|
373
440
|
|
|
374
441
|
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
@@ -376,6 +443,31 @@ class ModelElement extends AsyncElement {
|
|
|
376
443
|
case 'asset':
|
|
377
444
|
this.asset = newValue ?? '';
|
|
378
445
|
break;
|
|
446
|
+
case 'enabled':
|
|
447
|
+
this.enabled = parseBool(newValue, true);
|
|
448
|
+
break;
|
|
449
|
+
case 'name':
|
|
450
|
+
this.name = newValue ?? 'Untitled';
|
|
451
|
+
break;
|
|
452
|
+
case 'position':
|
|
453
|
+
this.position = parseVec3(newValue, Vec3.ZERO, name);
|
|
454
|
+
break;
|
|
455
|
+
case 'rotation':
|
|
456
|
+
this.rotation = parseVec3(newValue, Vec3.ZERO, name);
|
|
457
|
+
break;
|
|
458
|
+
case 'scale':
|
|
459
|
+
this.scale = parseVec3(newValue, Vec3.ONE, name);
|
|
460
|
+
break;
|
|
461
|
+
case 'tags':
|
|
462
|
+
this.tags = parseTags(newValue);
|
|
463
|
+
break;
|
|
464
|
+
case 'onpointerenter':
|
|
465
|
+
case 'onpointerleave':
|
|
466
|
+
case 'onpointerdown':
|
|
467
|
+
case 'onpointerup':
|
|
468
|
+
case 'onpointermove':
|
|
469
|
+
this._updateInlineHandler(name, newValue);
|
|
470
|
+
break;
|
|
379
471
|
}
|
|
380
472
|
}
|
|
381
473
|
}
|
package/src/node.ts
CHANGED
|
@@ -2,9 +2,9 @@ import type { Entity, EventHandle, GraphNode, Material, MeshInstance, Quat, Rend
|
|
|
2
2
|
import { Vec3 } from 'playcanvas';
|
|
3
3
|
|
|
4
4
|
import { ComponentElement } from './components/component';
|
|
5
|
-
import { buildDescendantEntities } from './entity';
|
|
6
|
-
import type { EntityElement } from './entity';
|
|
7
5
|
import { EntityBaseElement, POINTER_ATTRIBUTES } from './entity-base';
|
|
6
|
+
import { buildDescendantEntities } from './entity-owner';
|
|
7
|
+
import type { EntityOwnerElement } from './entity-owner';
|
|
8
8
|
import { MaterialElement } from './material';
|
|
9
9
|
import { ModelElement } from './model';
|
|
10
10
|
import { parseBool, parseTags, parseVec3 } from './parse';
|
|
@@ -15,7 +15,7 @@ import { parseBool, parseTags, parseVec3 } from './parse';
|
|
|
15
15
|
* `missing`/`ambiguous`/`duplicate` when resolution failed — each accompanied by a warning
|
|
16
16
|
* naming the cause.
|
|
17
17
|
*/
|
|
18
|
-
type NodeBindingState = 'pending' | 'bound' | 'missing' | 'ambiguous' | 'duplicate';
|
|
18
|
+
export type NodeBindingState = 'pending' | 'bound' | 'missing' | 'ambiguous' | 'duplicate';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* The authored values a bound node's overrides displaced, captured per property when the first
|
|
@@ -164,6 +164,11 @@ const levenshtein = (a: string, b: string): number => {
|
|
|
164
164
|
* The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
|
|
165
165
|
* intersects the bound node's geometry, exactly as for `<pc-entity>`.
|
|
166
166
|
*
|
|
167
|
+
* @elementSummary The `<pc-node>` element binds to a node inside the hierarchy a `<pc-model>`
|
|
168
|
+
* instantiated and declares overrides against it: a transform, an enabled state, tags, components
|
|
169
|
+
* to add, or content to attach. Its `name` is a lookup, never a rename. Must be a descendant of
|
|
170
|
+
* `<pc-model>`.
|
|
171
|
+
*
|
|
167
172
|
* @attribute {string} name - The name of the node to bind, resolved within the nearest ancestor
|
|
168
173
|
* `pc-model` (or `pc-node`) once it has instantiated.
|
|
169
174
|
* @attribute {number} index - Which match to bind when `name` matches more than one node,
|
|
@@ -318,7 +323,10 @@ class NodeElement extends EntityBaseElement {
|
|
|
318
323
|
* is retained untouched — a redundant edit must not flicker overrides through a revert.
|
|
319
324
|
*/
|
|
320
325
|
private _rebind() {
|
|
321
|
-
|
|
326
|
+
// A model fronts a host entity of its own; the names this element resolves are the
|
|
327
|
+
// asset's, so the search starts at the instantiated content root, not the wrapper.
|
|
328
|
+
const host = this._host;
|
|
329
|
+
const hostEntity = (host instanceof ModelElement ? host.contentEntity : host?.entity) ?? null;
|
|
322
330
|
|
|
323
331
|
if (!hostEntity || !this._name) {
|
|
324
332
|
// Host not instantiated (or nothing to look up yet): return to pending. An assigned
|
|
@@ -435,8 +443,9 @@ class NodeElement extends EntityBaseElement {
|
|
|
435
443
|
this._revertOverrides();
|
|
436
444
|
|
|
437
445
|
// Attachment points anchor to the bound node, so they cannot outlive the binding. Each
|
|
438
|
-
// destroyed entity resets its element, which the next _buildChildren re-creates
|
|
439
|
-
|
|
446
|
+
// destroyed entity resets its element, which the next _buildChildren re-creates - a
|
|
447
|
+
// model host among them re-instantiates its content when it rebuilds.
|
|
448
|
+
this.querySelectorAll<EntityOwnerElement>('pc-entity, pc-model').forEach((child) => {
|
|
440
449
|
if (child.closestEntity === this) {
|
|
441
450
|
child.entity?.destroy();
|
|
442
451
|
}
|
package/src/scene.ts
CHANGED
|
@@ -9,6 +9,9 @@ import { parseColor, parseEnum, parseNumber, parseVec3 } from './parse';
|
|
|
9
9
|
* {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-scene/ | `<pc-scene>`} elements.
|
|
10
10
|
* The SceneElement interface also inherits the properties and methods of the
|
|
11
11
|
* {@link HTMLElement} interface.
|
|
12
|
+
*
|
|
13
|
+
* @elementSummary The `<pc-scene>` element holds the entity hierarchy the application renders,
|
|
14
|
+
* along with the scene-wide fog and gravity settings. Must be a direct child of `<pc-app>`.
|
|
12
15
|
*/
|
|
13
16
|
class SceneElement extends AsyncElement {
|
|
14
17
|
/**
|
package/src/sky.ts
CHANGED
|
@@ -10,6 +10,10 @@ import { parseBool, parseEnum, parseNumber, parseVec3 } from './parse';
|
|
|
10
10
|
* The SkyElement interface provides properties and methods for manipulating
|
|
11
11
|
* `<pc-sky>` elements. The SkyElement interface also inherits the properties and
|
|
12
12
|
* methods of the {@link HTMLElement} interface.
|
|
13
|
+
*
|
|
14
|
+
* @elementSummary The `<pc-sky>` element renders a skybox from a texture asset, projected as an
|
|
15
|
+
* infinite background, a box or a dome, and optionally lights the scene from it. Must be a direct
|
|
16
|
+
* child of `<pc-scene>`.
|
|
13
17
|
*/
|
|
14
18
|
class SkyElement extends AsyncElement {
|
|
15
19
|
private _asset = '';
|