@playcanvas/web-components 0.1.4 → 0.1.6
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 +43 -61
- package/dist/app.d.ts +56 -5
- package/dist/asset.d.ts +0 -1
- package/dist/async-element.d.ts +23 -0
- package/dist/components/camera-component.d.ts +3 -0
- package/dist/components/component.d.ts +4 -2
- package/dist/components/element-component.d.ts +1 -1
- package/dist/components/render-component.d.ts +2 -2
- package/dist/components/script-component.d.ts +1 -1
- package/dist/components/sound-component.d.ts +48 -0
- package/dist/components/sound-slot.d.ts +2 -1
- package/dist/entity.d.ts +2 -3
- package/dist/index.d.ts +10 -1
- package/dist/model.d.ts +2 -1
- package/dist/pwc.cjs +426 -141
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +426 -141
- 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.mjs +427 -143
- package/dist/pwc.mjs.map +1 -1
- package/dist/scene.d.ts +2 -1
- package/dist/sky.d.ts +15 -6
- package/package.json +9 -8
- package/src/app.ts +103 -17
- package/src/asset.ts +1 -3
- package/src/async-element.ts +45 -0
- package/src/components/camera-component.ts +22 -1
- package/src/components/component.ts +15 -26
- package/src/components/element-component.ts +15 -4
- package/src/components/render-component.ts +1 -6
- package/src/components/script-component.ts +25 -7
- package/src/components/sound-component.ts +110 -1
- package/src/components/sound-slot.ts +7 -10
- package/src/entity.ts +19 -29
- package/src/index.ts +11 -0
- package/src/model.ts +20 -15
- package/src/module.ts +18 -10
- package/src/scene.ts +6 -11
- package/src/sky.ts +82 -40
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AppElement } from './app';
|
|
2
|
+
import { EntityElement } from './entity';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base class for all PlayCanvas web components that initialize asynchronously.
|
|
6
|
+
*/
|
|
7
|
+
class AsyncElement extends HTMLElement {
|
|
8
|
+
private _readyPromise: Promise<void>;
|
|
9
|
+
|
|
10
|
+
private _readyResolve!: () => void;
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this._readyPromise = new Promise<void>((resolve) => {
|
|
15
|
+
this._readyResolve = resolve;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get closestApp(): AppElement {
|
|
20
|
+
return this.parentElement?.closest('pc-app') as AppElement;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get closestEntity(): EntityElement {
|
|
24
|
+
return this.parentElement?.closest('pc-entity') as EntityElement;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Called when the element is fully initialized and ready.
|
|
29
|
+
* Subclasses should call this when they're ready.
|
|
30
|
+
*/
|
|
31
|
+
protected _onReady() {
|
|
32
|
+
this._readyResolve();
|
|
33
|
+
this.dispatchEvent(new CustomEvent('ready'));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Returns a promise that resolves with this element when it's ready.
|
|
38
|
+
* @returns A promise that resolves with this element when it's ready.
|
|
39
|
+
*/
|
|
40
|
+
ready(): Promise<this> {
|
|
41
|
+
return this._readyPromise.then(() => this);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { AsyncElement };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PROJECTION_ORTHOGRAPHIC, PROJECTION_PERSPECTIVE, CameraComponent, Color, Vec4 } from 'playcanvas';
|
|
1
|
+
import { PROJECTION_ORTHOGRAPHIC, PROJECTION_PERSPECTIVE, CameraComponent, Color, Vec4, XRTYPE_VR } from 'playcanvas';
|
|
2
2
|
|
|
3
3
|
import { ComponentElement } from './component';
|
|
4
4
|
import { parseColor, parseVec4 } from '../utils';
|
|
@@ -66,6 +66,27 @@ class CameraComponentElement extends ComponentElement {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
get xrAvailable() {
|
|
70
|
+
const xrManager = this.component?.system.app.xr;
|
|
71
|
+
return xrManager && xrManager.supported && xrManager.isAvailable(XRTYPE_VR);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
startXr(type: 'immersive-ar' | 'immersive-vr', space: 'bounded-floor' | 'local' | 'local-floor' | 'unbounded' | 'viewer') {
|
|
75
|
+
if (this.component && this.xrAvailable) {
|
|
76
|
+
this.component.startXr(type, space, {
|
|
77
|
+
callback: (err: any) => {
|
|
78
|
+
if (err) console.error(`WebXR Immersive VR failed to start: ${err.message}`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
endXr() {
|
|
85
|
+
if (this.component) {
|
|
86
|
+
this.component.endXr();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
69
90
|
/**
|
|
70
91
|
* Gets the camera component.
|
|
71
92
|
* @returns The camera component.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { Component } from 'playcanvas';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { EntityElement } from '../entity';
|
|
3
|
+
import { AsyncElement } from '../async-element';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Represents a component in the PlayCanvas engine.
|
|
8
7
|
*
|
|
9
8
|
* @category Components
|
|
10
9
|
*/
|
|
11
|
-
class ComponentElement extends
|
|
10
|
+
class ComponentElement extends AsyncElement {
|
|
12
11
|
private _componentName: string;
|
|
13
12
|
|
|
14
13
|
private _enabled = true;
|
|
@@ -30,33 +29,23 @@ class ComponentElement extends HTMLElement {
|
|
|
30
29
|
return {};
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
async
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
async addComponent() {
|
|
33
|
+
const entityElement = this.closestEntity;
|
|
34
|
+
if (entityElement) {
|
|
35
|
+
await entityElement.ready();
|
|
36
|
+
// Add the component to the entity
|
|
37
|
+
const data = this.getInitialComponentData();
|
|
38
|
+
this._component = entityElement.entity!.addComponent(this._componentName, data);
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
-
await appElement.getApplication();
|
|
41
|
-
|
|
42
|
-
this.addComponent();
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
// Access the parent pc-entity's 'entity' property
|
|
47
|
-
const entityElement = this.closest('pc-entity') as EntityElement | null;
|
|
48
|
-
if (!entityElement) {
|
|
49
|
-
console.error(`${this.tagName.toLowerCase()} should be a child of pc-entity`);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
42
|
+
initComponent() {}
|
|
52
43
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
);
|
|
59
|
-
}
|
|
44
|
+
async connectedCallback() {
|
|
45
|
+
await this.closestApp?.ready();
|
|
46
|
+
await this.addComponent();
|
|
47
|
+
this.initComponent();
|
|
48
|
+
this._onReady();
|
|
60
49
|
}
|
|
61
50
|
|
|
62
51
|
disconnectedCallback() {
|
|
@@ -37,9 +37,7 @@ class ElementComponentElement extends ComponentElement {
|
|
|
37
37
|
super('element');
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
await super.connectedCallback();
|
|
42
|
-
|
|
40
|
+
initComponent() {
|
|
43
41
|
this.component!._text._material.useFog = true;
|
|
44
42
|
}
|
|
45
43
|
|
|
@@ -198,7 +196,20 @@ class ElementComponentElement extends ComponentElement {
|
|
|
198
196
|
}
|
|
199
197
|
|
|
200
198
|
static get observedAttributes() {
|
|
201
|
-
return [
|
|
199
|
+
return [
|
|
200
|
+
...super.observedAttributes,
|
|
201
|
+
'anchor',
|
|
202
|
+
'asset',
|
|
203
|
+
'auto-width',
|
|
204
|
+
'color',
|
|
205
|
+
'font-size',
|
|
206
|
+
'line-height',
|
|
207
|
+
'pivot',
|
|
208
|
+
'text',
|
|
209
|
+
'type',
|
|
210
|
+
'width',
|
|
211
|
+
'wrap-lines'
|
|
212
|
+
];
|
|
202
213
|
}
|
|
203
214
|
|
|
204
215
|
attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
|
|
@@ -21,16 +21,11 @@ class RenderComponentElement extends ComponentElement {
|
|
|
21
21
|
super('render');
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
async connectedCallback() {
|
|
25
|
-
await super.connectedCallback();
|
|
26
|
-
|
|
27
|
-
this.material = this._material;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
24
|
getInitialComponentData() {
|
|
31
25
|
return {
|
|
32
26
|
type: this._type,
|
|
33
27
|
castShadows: this._castShadows,
|
|
28
|
+
material: MaterialElement.get(this._material),
|
|
34
29
|
receiveShadows: this._receiveShadows
|
|
35
30
|
};
|
|
36
31
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { ScriptComponent,
|
|
1
|
+
import { ScriptComponent, Script, Vec2, Vec3, Vec4 } from 'playcanvas';
|
|
2
2
|
|
|
3
3
|
import { ComponentElement } from './component';
|
|
4
4
|
import { ScriptElement } from './script';
|
|
5
5
|
|
|
6
|
+
const tmpV2 = new Vec2();
|
|
7
|
+
const tmpV3 = new Vec3();
|
|
8
|
+
const tmpV4 = new Vec4();
|
|
9
|
+
|
|
6
10
|
// Add these interfaces at the top of the file, after the imports
|
|
7
11
|
interface ScriptAttributesChangeEvent extends CustomEvent {
|
|
8
12
|
detail: { attributes: any };
|
|
@@ -42,9 +46,7 @@ class ScriptComponentElement extends ComponentElement {
|
|
|
42
46
|
this.addEventListener('scriptenablechange', this.handleScriptEnableChange.bind(this));
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
await super.connectedCallback();
|
|
47
|
-
|
|
49
|
+
initComponent() {
|
|
48
50
|
// Handle initial script elements
|
|
49
51
|
this.querySelectorAll<ScriptElement>(':scope > pc-script').forEach((scriptElement) => {
|
|
50
52
|
const scriptName = scriptElement.getAttribute('name');
|
|
@@ -55,11 +57,27 @@ class ScriptComponentElement extends ComponentElement {
|
|
|
55
57
|
});
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
private applyAttributes(script:
|
|
60
|
+
private applyAttributes(script: any, attributes: string | null) {
|
|
59
61
|
try {
|
|
60
62
|
// Parse the attributes string into an object and set them on the script
|
|
61
63
|
const attributesObject = attributes ? JSON.parse(attributes) : {};
|
|
62
|
-
|
|
64
|
+
for (const key in attributesObject) {
|
|
65
|
+
const value = attributesObject[key];
|
|
66
|
+
if (Array.isArray(value) && script[key] instanceof Vec2) {
|
|
67
|
+
script[key] = tmpV2.set(value[0], value[1]);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(value) && script[key] instanceof Vec3) {
|
|
71
|
+
script[key] = tmpV3.set(value[0], value[1], value[2]);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (Array.isArray(value) && script[key] instanceof Vec4) {
|
|
75
|
+
script[key] = tmpV4.set(value[0], value[1], value[2], value[3]);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
script[key] = value;
|
|
80
|
+
}
|
|
63
81
|
} catch (error) {
|
|
64
82
|
console.error(`Error parsing attributes JSON string ${attributes}:`, error);
|
|
65
83
|
}
|
|
@@ -87,7 +105,7 @@ class ScriptComponentElement extends ComponentElement {
|
|
|
87
105
|
}
|
|
88
106
|
}
|
|
89
107
|
|
|
90
|
-
private createScript(name: string, attributes: string | null):
|
|
108
|
+
private createScript(name: string, attributes: string | null): Script | null {
|
|
91
109
|
if (!this.component) return null;
|
|
92
110
|
|
|
93
111
|
this.component.on(`create:${name}`, (script) => {
|
|
@@ -8,10 +8,18 @@ import { ComponentElement } from './component';
|
|
|
8
8
|
* @category Components
|
|
9
9
|
*/
|
|
10
10
|
class SoundComponentElement extends ComponentElement {
|
|
11
|
+
private _distanceModel: 'exponential' | 'inverse' | 'linear' = 'linear';
|
|
12
|
+
|
|
13
|
+
private _maxDistance: number = 10000;
|
|
14
|
+
|
|
11
15
|
private _pitch: number = 1;
|
|
12
16
|
|
|
13
17
|
private _positional: boolean = false;
|
|
14
18
|
|
|
19
|
+
private _refDistance: number = 1;
|
|
20
|
+
|
|
21
|
+
private _rollOffFactor: number = 1;
|
|
22
|
+
|
|
15
23
|
private _volume: number = 1;
|
|
16
24
|
|
|
17
25
|
constructor() {
|
|
@@ -20,8 +28,12 @@ class SoundComponentElement extends ComponentElement {
|
|
|
20
28
|
|
|
21
29
|
getInitialComponentData() {
|
|
22
30
|
return {
|
|
31
|
+
distanceModel: this._distanceModel,
|
|
32
|
+
maxDistance: this._maxDistance,
|
|
23
33
|
pitch: this._pitch,
|
|
24
34
|
positional: this._positional,
|
|
35
|
+
refDistance: this._refDistance,
|
|
36
|
+
rollOffFactor: this._rollOffFactor,
|
|
25
37
|
volume: this._volume
|
|
26
38
|
};
|
|
27
39
|
}
|
|
@@ -34,6 +46,44 @@ class SoundComponentElement extends ComponentElement {
|
|
|
34
46
|
return super.component as SoundComponent | null;
|
|
35
47
|
}
|
|
36
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Sets which algorithm to use to reduce the volume of the sound as it moves away from the listener.
|
|
51
|
+
* @param value - The distance model.
|
|
52
|
+
*/
|
|
53
|
+
set distanceModel(value: 'exponential' | 'inverse' | 'linear') {
|
|
54
|
+
this._distanceModel = value;
|
|
55
|
+
if (this.component) {
|
|
56
|
+
this.component.distanceModel = value;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Gets which algorithm to use to reduce the volume of the sound as it moves away from the listener.
|
|
62
|
+
* @returns The distance model.
|
|
63
|
+
*/
|
|
64
|
+
get distanceModel(): 'exponential' | 'inverse' | 'linear' {
|
|
65
|
+
return this._distanceModel;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Sets the maximum distance from the listener at which audio falloff stops.
|
|
70
|
+
* @param value - The max distance.
|
|
71
|
+
*/
|
|
72
|
+
set maxDistance(value: number) {
|
|
73
|
+
this._maxDistance = value;
|
|
74
|
+
if (this.component) {
|
|
75
|
+
this.component.maxDistance = value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Gets the maximum distance from the listener at which audio falloff stops.
|
|
81
|
+
* @returns The max distance.
|
|
82
|
+
*/
|
|
83
|
+
get maxDistance() {
|
|
84
|
+
return this._maxDistance;
|
|
85
|
+
}
|
|
86
|
+
|
|
37
87
|
/**
|
|
38
88
|
* Sets the pitch of the sound.
|
|
39
89
|
* @param value - The pitch.
|
|
@@ -72,6 +122,44 @@ class SoundComponentElement extends ComponentElement {
|
|
|
72
122
|
return this._positional;
|
|
73
123
|
}
|
|
74
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Sets the reference distance for reducing volume as the sound source moves further from the listener. Defaults to 1.
|
|
127
|
+
* @param value - The ref distance.
|
|
128
|
+
*/
|
|
129
|
+
set refDistance(value: number) {
|
|
130
|
+
this._refDistance = value;
|
|
131
|
+
if (this.component) {
|
|
132
|
+
this.component.refDistance = value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Gets the reference distance for reducing volume as the sound source moves further from the listener.
|
|
138
|
+
* @returns The ref distance.
|
|
139
|
+
*/
|
|
140
|
+
get refDistance() {
|
|
141
|
+
return this._refDistance;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Sets the factor used in the falloff equation. Defaults to 1.
|
|
146
|
+
* @param value - The roll-off factor.
|
|
147
|
+
*/
|
|
148
|
+
set rollOffFactor(value: number) {
|
|
149
|
+
this._rollOffFactor = value;
|
|
150
|
+
if (this.component) {
|
|
151
|
+
this.component.rollOffFactor = value;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Gets the factor used in the falloff equation.
|
|
157
|
+
* @returns The roll-off factor.
|
|
158
|
+
*/
|
|
159
|
+
get rollOffFactor() {
|
|
160
|
+
return this._rollOffFactor;
|
|
161
|
+
}
|
|
162
|
+
|
|
75
163
|
/**
|
|
76
164
|
* Sets the volume of the sound.
|
|
77
165
|
* @param value - The volume.
|
|
@@ -92,19 +180,40 @@ class SoundComponentElement extends ComponentElement {
|
|
|
92
180
|
}
|
|
93
181
|
|
|
94
182
|
static get observedAttributes() {
|
|
95
|
-
return [
|
|
183
|
+
return [
|
|
184
|
+
...super.observedAttributes,
|
|
185
|
+
'distance-model',
|
|
186
|
+
'max-distance',
|
|
187
|
+
'pitch',
|
|
188
|
+
'positional',
|
|
189
|
+
'ref-distance',
|
|
190
|
+
'roll-off-factor',
|
|
191
|
+
'volume'
|
|
192
|
+
];
|
|
96
193
|
}
|
|
97
194
|
|
|
98
195
|
attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
|
|
99
196
|
super.attributeChangedCallback(name, _oldValue, newValue);
|
|
100
197
|
|
|
101
198
|
switch (name) {
|
|
199
|
+
case 'distance-model':
|
|
200
|
+
this.distanceModel = newValue as 'exponential' | 'inverse' | 'linear';
|
|
201
|
+
break;
|
|
202
|
+
case 'max-distance':
|
|
203
|
+
this.maxDistance = parseFloat(newValue);
|
|
204
|
+
break;
|
|
102
205
|
case 'pitch':
|
|
103
206
|
this.pitch = parseFloat(newValue);
|
|
104
207
|
break;
|
|
105
208
|
case 'positional':
|
|
106
209
|
this.positional = this.hasAttribute('positional');
|
|
107
210
|
break;
|
|
211
|
+
case 'ref-distance':
|
|
212
|
+
this.refDistance = parseFloat(newValue);
|
|
213
|
+
break;
|
|
214
|
+
case 'roll-off-factor':
|
|
215
|
+
this.rollOffFactor = parseFloat(newValue);
|
|
216
|
+
break;
|
|
108
217
|
case 'volume':
|
|
109
218
|
this.volume = parseFloat(newValue);
|
|
110
219
|
break;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { SoundSlot } from 'playcanvas';
|
|
2
2
|
|
|
3
|
-
import { AppElement } from '../app';
|
|
4
|
-
import { SoundComponentElement } from './sound-component';
|
|
5
3
|
import { AssetElement } from '../asset';
|
|
4
|
+
import { AsyncElement } from '../async-element';
|
|
5
|
+
import { SoundComponentElement } from './sound-component';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Represents a sound slot in the PlayCanvas engine.
|
|
9
9
|
*/
|
|
10
|
-
class SoundSlotElement extends
|
|
10
|
+
class SoundSlotElement extends AsyncElement {
|
|
11
11
|
private _asset: string = '';
|
|
12
12
|
|
|
13
13
|
private _autoPlay: boolean = false;
|
|
@@ -32,13 +32,7 @@ class SoundSlotElement extends HTMLElement {
|
|
|
32
32
|
soundSlot: SoundSlot | null = null;
|
|
33
33
|
|
|
34
34
|
async connectedCallback() {
|
|
35
|
-
|
|
36
|
-
if (!appElement) {
|
|
37
|
-
console.error(`${this.tagName.toLowerCase()} should be a descendant of pc-app`);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
await appElement.getApplication();
|
|
35
|
+
await this.soundElement?.ready();
|
|
42
36
|
|
|
43
37
|
const options = {
|
|
44
38
|
autoPlay: this._autoPlay,
|
|
@@ -51,9 +45,12 @@ class SoundSlotElement extends HTMLElement {
|
|
|
51
45
|
if (this._duration) {
|
|
52
46
|
options.duration = this._duration;
|
|
53
47
|
}
|
|
48
|
+
|
|
54
49
|
this.soundSlot = this.soundElement!.component!.addSlot(this._name, options);
|
|
55
50
|
this.asset = this._asset;
|
|
56
51
|
this.soundSlot!.play();
|
|
52
|
+
|
|
53
|
+
this._onReady();
|
|
57
54
|
}
|
|
58
55
|
|
|
59
56
|
disconnectedCallback() {
|
package/src/entity.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Entity, Vec3 } from 'playcanvas';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { AsyncElement } from './async-element';
|
|
4
4
|
import { parseVec3 } from './utils';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Represents an entity in the PlayCanvas engine.
|
|
8
8
|
*/
|
|
9
|
-
class EntityElement extends
|
|
9
|
+
class EntityElement extends AsyncElement {
|
|
10
10
|
/**
|
|
11
11
|
* Whether the entity is enabled.
|
|
12
12
|
*/
|
|
@@ -37,38 +37,22 @@ class EntityElement extends HTMLElement {
|
|
|
37
37
|
*/
|
|
38
38
|
private _tags: string[] = [];
|
|
39
39
|
|
|
40
|
-
private _resolveEntity!: (entity: Entity) => void;
|
|
41
|
-
|
|
42
|
-
private _entityReady = new Promise<Entity>((resolve) => {
|
|
43
|
-
this._resolveEntity = resolve;
|
|
44
|
-
});
|
|
45
|
-
|
|
46
40
|
/**
|
|
47
41
|
* The PlayCanvas entity instance.
|
|
48
42
|
*/
|
|
49
43
|
entity: Entity | null = null;
|
|
50
44
|
|
|
51
45
|
async connectedCallback() {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
46
|
+
const closestApp = this.closestApp;
|
|
47
|
+
if (!closestApp) return;
|
|
48
|
+
|
|
49
|
+
// Wait for the app to complete initialization
|
|
50
|
+
await closestApp.ready();
|
|
58
51
|
|
|
59
|
-
const app =
|
|
52
|
+
const app = closestApp.app!;
|
|
60
53
|
|
|
61
54
|
// Create a new entity
|
|
62
55
|
this.entity = new Entity(this._name, app);
|
|
63
|
-
this._resolveEntity(this.entity);
|
|
64
|
-
|
|
65
|
-
if (this.parentElement &&
|
|
66
|
-
this.parentElement.tagName.toLowerCase() === 'pc-entity') {
|
|
67
|
-
const parentEntity = await (this.parentElement as EntityElement)._entityReady;
|
|
68
|
-
parentEntity.addChild(this.entity);
|
|
69
|
-
} else {
|
|
70
|
-
app.root.addChild(this.entity);
|
|
71
|
-
}
|
|
72
56
|
|
|
73
57
|
// Initialize from attributes
|
|
74
58
|
const nameAttr = this.getAttribute('name');
|
|
@@ -82,6 +66,17 @@ class EntityElement extends HTMLElement {
|
|
|
82
66
|
if (rotationAttr) this.rotation = parseVec3(rotationAttr);
|
|
83
67
|
if (scaleAttr) this.scale = parseVec3(scaleAttr);
|
|
84
68
|
if (tagsAttr) this.tags = tagsAttr.split(',').map(tag => tag.trim());
|
|
69
|
+
|
|
70
|
+
const closestEntity = this.closestEntity;
|
|
71
|
+
if (closestEntity) {
|
|
72
|
+
closestEntity.ready().then(() => {
|
|
73
|
+
closestEntity.entity!.addChild(this.entity!);
|
|
74
|
+
this._onReady();
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
app.root.addChild(this.entity);
|
|
78
|
+
this._onReady();
|
|
79
|
+
}
|
|
85
80
|
}
|
|
86
81
|
|
|
87
82
|
disconnectedCallback() {
|
|
@@ -95,11 +90,6 @@ class EntityElement extends HTMLElement {
|
|
|
95
90
|
this.entity.destroy();
|
|
96
91
|
this.entity = null;
|
|
97
92
|
}
|
|
98
|
-
|
|
99
|
-
// Reset the promise for potential reconnection
|
|
100
|
-
this._entityReady = new Promise<Entity>((resolve) => {
|
|
101
|
-
this._resolveEntity = resolve;
|
|
102
|
-
});
|
|
103
93
|
}
|
|
104
94
|
|
|
105
95
|
/**
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Engine Web Components module provides a set of Web Components for the PlayCanvas Engine.
|
|
3
|
+
* While these components are normally instantiated in a declarative fashion using HTML, this
|
|
4
|
+
* reference covers the TypeScript/JavaScript API that allows these components to be created
|
|
5
|
+
* programmatically.
|
|
6
|
+
*
|
|
7
|
+
* @module EngineWebComponents
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
/* eslint-disable import/order */
|
|
2
11
|
|
|
3
12
|
// Note that order matters here (e.g. pc-entity must be defined before components)
|
|
13
|
+
import { AsyncElement } from './async-element';
|
|
4
14
|
import { ModuleElement } from './module';
|
|
5
15
|
import { AppElement } from './app';
|
|
6
16
|
import { EntityElement } from './entity';
|
|
@@ -25,6 +35,7 @@ import { SceneElement } from './scene';
|
|
|
25
35
|
import { SkyElement } from './sky';
|
|
26
36
|
|
|
27
37
|
export {
|
|
38
|
+
AsyncElement,
|
|
28
39
|
ModuleElement,
|
|
29
40
|
AppElement,
|
|
30
41
|
EntityElement,
|
package/src/model.ts
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
|
-
import { AppElement } from './app';
|
|
2
1
|
import { AssetElement } from './asset';
|
|
3
|
-
import {
|
|
2
|
+
import { AsyncElement } from './async-element';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Represents a model in the PlayCanvas engine.
|
|
7
6
|
*/
|
|
8
|
-
class ModelElement extends
|
|
7
|
+
class ModelElement extends AsyncElement {
|
|
9
8
|
private _asset: string = '';
|
|
10
9
|
|
|
11
10
|
async connectedCallback() {
|
|
12
|
-
|
|
13
|
-
const appElement = this.closest('pc-app') as AppElement;
|
|
14
|
-
if (!appElement) {
|
|
15
|
-
console.warn(`${this.tagName} must be a child of pc-app`);
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
await appElement.getApplication();
|
|
11
|
+
await this.closestApp?.ready();
|
|
20
12
|
|
|
21
13
|
const asset = this.getAttribute('asset');
|
|
22
14
|
if (asset) {
|
|
23
15
|
this.asset = asset;
|
|
24
16
|
}
|
|
17
|
+
|
|
18
|
+
this._onReady();
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
_loadModel() {
|
|
@@ -31,12 +25,23 @@ class ModelElement extends HTMLElement {
|
|
|
31
25
|
}
|
|
32
26
|
const entity = asset.resource.instantiateRenderEntity();
|
|
33
27
|
|
|
34
|
-
|
|
28
|
+
if (asset.resource.animations.length > 0) {
|
|
29
|
+
entity.addComponent('anim');
|
|
30
|
+
entity.anim.assignAnimation('animation', asset.resource.animations[0].resource);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const parentEntityElement = this.closestEntity;
|
|
35
34
|
if (parentEntityElement) {
|
|
36
|
-
parentEntityElement.
|
|
35
|
+
parentEntityElement.ready().then(() => {
|
|
36
|
+
parentEntityElement.entity!.addChild(entity);
|
|
37
|
+
});
|
|
37
38
|
} else {
|
|
38
|
-
const appElement = this.
|
|
39
|
-
appElement
|
|
39
|
+
const appElement = this.closestApp;
|
|
40
|
+
if (appElement) {
|
|
41
|
+
appElement.ready().then(() => {
|
|
42
|
+
appElement.app!.root.addChild(entity);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
40
45
|
}
|
|
41
46
|
}
|
|
42
47
|
|
package/src/module.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WasmModule } from 'playcanvas';
|
|
1
|
+
import { basisInitialize, WasmModule } from 'playcanvas';
|
|
2
2
|
|
|
3
3
|
class ModuleElement extends HTMLElement {
|
|
4
4
|
private loadPromise: Promise<void>;
|
|
@@ -14,15 +14,23 @@ class ModuleElement extends HTMLElement {
|
|
|
14
14
|
const wasm = this.getAttribute('wasm')!;
|
|
15
15
|
const fallback = this.getAttribute('fallback')!;
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
WasmModule.
|
|
25
|
-
|
|
17
|
+
if (name === 'Basis') {
|
|
18
|
+
basisInitialize({
|
|
19
|
+
glueUrl: glue,
|
|
20
|
+
wasmUrl: wasm,
|
|
21
|
+
fallbackUrl: fallback
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
WasmModule.setConfig(name, {
|
|
25
|
+
glueUrl: glue,
|
|
26
|
+
wasmUrl: wasm,
|
|
27
|
+
fallbackUrl: fallback
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await new Promise<void>((resolve) => {
|
|
31
|
+
WasmModule.getInstance(name, () => resolve());
|
|
32
|
+
});
|
|
33
|
+
}
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
public getLoadPromise(): Promise<void> {
|