@miris-inc/components 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bus.ts +33 -0
- package/camera.ts +67 -0
- package/elements.ts +4 -0
- package/group.ts +12 -0
- package/index.html +11 -0
- package/index.ts +10 -0
- package/package.json +16 -0
- package/primitive.ts +202 -0
- package/scene.ts +127 -0
- package/stream.ts +217 -0
- package/test/carousel-multiple.html +386 -0
- package/test/carousel-single.html +494 -0
- package/test/controls.html +71 -0
- package/test/hero-test.html +99 -0
- package/test/index.html +29 -0
- package/test/sdk.html +10 -0
- package/vite.config.ts +32 -0
package/bus.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type Primitive from "./primitive";
|
|
2
|
+
import type Scene from "./scene";
|
|
3
|
+
|
|
4
|
+
type Subscriber = Primitive | Scene;
|
|
5
|
+
type Callback = (...params: any[]) => void;
|
|
6
|
+
|
|
7
|
+
export default class Bus {
|
|
8
|
+
static #subscriptions = new Map<Subscriber, Map<string, Callback>>();
|
|
9
|
+
|
|
10
|
+
static subscribe(key: Subscriber, name: string, callback: Callback) {
|
|
11
|
+
if (!this.#subscriptions.has(key)) {
|
|
12
|
+
this.#subscriptions.set(key, new Map());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
this.#subscriptions.get(key)!.set(name, callback);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static unsubscribe(key: Subscriber, name: string) {
|
|
19
|
+
if (!this.#subscriptions.has(key)) return;
|
|
20
|
+
|
|
21
|
+
this.#subscriptions.get(key)!.delete(name);
|
|
22
|
+
|
|
23
|
+
if (this.#subscriptions.get(key)!.size) {
|
|
24
|
+
this.#subscriptions.delete(key);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static publish(key: Subscriber, name: string, ...args: any[]) {
|
|
29
|
+
const callback = this.#subscriptions.get(key)?.get(name);
|
|
30
|
+
|
|
31
|
+
if (callback) callback(...args);
|
|
32
|
+
}
|
|
33
|
+
}
|
package/camera.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { PerspectiveCamera as ThreeCamera } from "three";
|
|
2
|
+
import Primitive from "./primitive";
|
|
3
|
+
|
|
4
|
+
export default class MirisCamera extends Primitive {
|
|
5
|
+
#fov = 50;
|
|
6
|
+
// prettier-ignore
|
|
7
|
+
get fov() { return this.#fov }
|
|
8
|
+
set fov(fov) {
|
|
9
|
+
if (this._threeObject) {
|
|
10
|
+
this._threeObject.fov = fov;
|
|
11
|
+
this._threeObject.updateProjectionMatrix();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.#fov = fov;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#aspect = 1;
|
|
18
|
+
// prettier-ignore
|
|
19
|
+
get aspect() { return this.#aspect }
|
|
20
|
+
set aspect(aspect) {
|
|
21
|
+
if (this._threeObject) {
|
|
22
|
+
this._threeObject.aspect = aspect;
|
|
23
|
+
this._threeObject.updateProjectionMatrix();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this.#aspect = aspect;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#near = 0.1;
|
|
30
|
+
// prettier-ignore
|
|
31
|
+
get near() { return this.#near }
|
|
32
|
+
set near(near) {
|
|
33
|
+
if (this._threeObject) {
|
|
34
|
+
this._threeObject.near = near;
|
|
35
|
+
this._threeObject.updateProjectionMatrix();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.#near = near;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#far = 2000;
|
|
42
|
+
// prettier-ignore
|
|
43
|
+
get far() { return this.#far }
|
|
44
|
+
set far(far) {
|
|
45
|
+
if (this._threeObject) {
|
|
46
|
+
this._threeObject.far = far;
|
|
47
|
+
this._threeObject.updateProjectionMatrix();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.#far = far;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected override _threeObject: ThreeCamera | null = null;
|
|
54
|
+
|
|
55
|
+
override _enable() {
|
|
56
|
+
this._threeObject = new ThreeCamera(
|
|
57
|
+
this.fov,
|
|
58
|
+
this.aspect,
|
|
59
|
+
this.near,
|
|
60
|
+
this.far
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
this._threeObject.name = "supplied camera";
|
|
64
|
+
|
|
65
|
+
super._enable();
|
|
66
|
+
}
|
|
67
|
+
}
|
package/elements.ts
ADDED
package/group.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Group as ThreeGroup } from "three";
|
|
2
|
+
import Primitive from "./primitive";
|
|
3
|
+
|
|
4
|
+
export default class MirisGroup extends Primitive {
|
|
5
|
+
protected override _threeObject: ThreeGroup | null = null;
|
|
6
|
+
|
|
7
|
+
override _enable() {
|
|
8
|
+
this._threeObject = new ThreeGroup();
|
|
9
|
+
|
|
10
|
+
super._enable();
|
|
11
|
+
}
|
|
12
|
+
}
|
package/index.html
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta http-equiv="refresh" content="0; url=./test/index.html">
|
|
6
|
+
<title>Redirecting...</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<p>Redirecting to <a href="./test/index.html">test page</a>...</p>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@miris-inc/components",
|
|
3
|
+
"exports": "./index.ts",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"d": "bun dev",
|
|
6
|
+
"dev": "vite",
|
|
7
|
+
"b": "vite build",
|
|
8
|
+
"build": "vite build"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@miris-inc/three": "*",
|
|
12
|
+
"@types/three": "^0.182.0",
|
|
13
|
+
"three": "^0.182.0"
|
|
14
|
+
},
|
|
15
|
+
"version": "0.0.1"
|
|
16
|
+
}
|
package/primitive.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { Euler, type Object3D, Vector3 as ThreeVector3 } from "three";
|
|
2
|
+
import Bus from "./bus";
|
|
3
|
+
import type Scene from "./scene";
|
|
4
|
+
import type Group from "./group";
|
|
5
|
+
|
|
6
|
+
type Vector3 = {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
z: number;
|
|
10
|
+
set: (x: number, y: number, z: number) => void;
|
|
11
|
+
};
|
|
12
|
+
export default class MirisPrimitive extends HTMLElement {
|
|
13
|
+
#position = new ThreeVector3();
|
|
14
|
+
#rotation = new Euler();
|
|
15
|
+
#scale = new ThreeVector3(1, 1, 1);
|
|
16
|
+
declare readonly position: Vector3;
|
|
17
|
+
declare readonly rotation: Vector3;
|
|
18
|
+
declare readonly scale: Vector3;
|
|
19
|
+
|
|
20
|
+
#zoom = 1;
|
|
21
|
+
// prettier-ignore
|
|
22
|
+
get zoom () { return this.#zoom }
|
|
23
|
+
set zoom(zoom) {
|
|
24
|
+
this.#zoom = zoom;
|
|
25
|
+
const { x, y, z } = this.scale;
|
|
26
|
+
this._threeObject?.scale.set(zoom * x, zoom * y, zoom * z);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected _parent: Group | Scene | null = null;
|
|
30
|
+
protected _threeObject: Object3D | null = null;
|
|
31
|
+
|
|
32
|
+
#initPosition() {
|
|
33
|
+
const self = this;
|
|
34
|
+
|
|
35
|
+
// prettier-ignore
|
|
36
|
+
Object.defineProperty(this, "position", {
|
|
37
|
+
value: {
|
|
38
|
+
get x() { return self.#position.x },
|
|
39
|
+
set x(x) {
|
|
40
|
+
self.#position.x = x;
|
|
41
|
+
if (self._threeObject) self._threeObject.position.x = x;
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
get y() { return self.#position.y },
|
|
45
|
+
set y(y) {
|
|
46
|
+
self.#position.y = y;
|
|
47
|
+
if (self._threeObject) self._threeObject.position.y = y;
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
get z() { return self.#position.z },
|
|
51
|
+
set z(z) {
|
|
52
|
+
self.#position.z = z;
|
|
53
|
+
if (self._threeObject) self._threeObject.position.z = z;
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
set(x: number, y: number, z: number) {
|
|
57
|
+
self.#position.x = x;
|
|
58
|
+
self.#position.y = y;
|
|
59
|
+
self.#position.z = z;
|
|
60
|
+
self._threeObject?.position.set(x, y, z);
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
Object.freeze(this.position);
|
|
65
|
+
|
|
66
|
+
const position = this.getAttribute("position");
|
|
67
|
+
const [x = 0, y = 0, z = 0] = position?.split(" ")?.map(parseFloat) ?? [];
|
|
68
|
+
this.position.set(x, y, z);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#initRotation() {
|
|
72
|
+
const self = this;
|
|
73
|
+
|
|
74
|
+
// prettier-ignore
|
|
75
|
+
Object.defineProperty(this, "rotation", {
|
|
76
|
+
value: {
|
|
77
|
+
get x() { return self.#rotation.x },
|
|
78
|
+
set x(x) {
|
|
79
|
+
self.#rotation.x = x;
|
|
80
|
+
if (self._threeObject) self._threeObject.rotation.x = x;
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
get y() { return self.#rotation.y },
|
|
84
|
+
set y(y) {
|
|
85
|
+
self.#rotation.y = y;
|
|
86
|
+
if (self._threeObject) self._threeObject.rotation.y = y;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
get z() { return self.#rotation.z },
|
|
90
|
+
set z(z) {
|
|
91
|
+
self.#rotation.z = z;
|
|
92
|
+
if (self._threeObject) self._threeObject.rotation.z = z;
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
set(x: number, y: number, z: number) {
|
|
96
|
+
self.#rotation.x = x;
|
|
97
|
+
self.#rotation.y = y;
|
|
98
|
+
self.#rotation.z = z;
|
|
99
|
+
self._threeObject?.rotation.set(x, y, z);
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
Object.freeze(this.rotation);
|
|
104
|
+
|
|
105
|
+
const rotation = this.getAttribute("rotation");
|
|
106
|
+
const [x = 0, y = 0, z = 0] = rotation?.split(" ")?.map(parseFloat) ?? [];
|
|
107
|
+
this.rotation.set(x, y, z);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#initScale() {
|
|
111
|
+
const self = this;
|
|
112
|
+
|
|
113
|
+
// prettier-ignore
|
|
114
|
+
Object.defineProperty(this, "scale", {
|
|
115
|
+
value: {
|
|
116
|
+
get x() { return self.#scale.x },
|
|
117
|
+
set x(x) {
|
|
118
|
+
self.#scale.x = x;
|
|
119
|
+
if (self._threeObject) self._threeObject.scale.x = self.zoom * x;
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
get y() { return self.#scale.y },
|
|
123
|
+
set y(y) {
|
|
124
|
+
self.#scale.y = y;
|
|
125
|
+
if (self._threeObject) self._threeObject.scale.y = self.zoom * y;
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
get z() { return self.#scale.z },
|
|
129
|
+
set z(z) {
|
|
130
|
+
self.#scale.z = z;
|
|
131
|
+
if (self._threeObject) self._threeObject.scale.z = self.zoom * z;
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
set(x: number, y: number, z: number) {
|
|
135
|
+
const { zoom } = self
|
|
136
|
+
self.#scale.x = x;
|
|
137
|
+
self.#scale.y = y;
|
|
138
|
+
self.#scale.z = z;
|
|
139
|
+
self._threeObject?.scale.set(zoom * x, zoom * y, zoom * z);
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
Object.freeze(this.scale);
|
|
144
|
+
|
|
145
|
+
this.#zoom = parseFloat(this.getAttribute("zoom") ?? `${this.#zoom}`);
|
|
146
|
+
const scale = this.getAttribute("scale");
|
|
147
|
+
const [x = 1, y = 1, z = 1] = scale?.split(" ")?.map(parseFloat) ?? [];
|
|
148
|
+
this.scale.set(x, y, z);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
constructor() {
|
|
152
|
+
super();
|
|
153
|
+
this.#initPosition();
|
|
154
|
+
this.#initRotation();
|
|
155
|
+
this.#initScale();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
protected _enable() {
|
|
159
|
+
if (!document.body.contains(this) || !this._threeObject) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const { position, rotation, scale, zoom } = this;
|
|
164
|
+
const { x: pX, y: pY, z: pZ } = position;
|
|
165
|
+
const { x: rX, y: rY, z: rZ } = rotation;
|
|
166
|
+
const { x: sX, y: sY, z: sZ } = scale;
|
|
167
|
+
this._threeObject.position.set(pX, pY, pZ);
|
|
168
|
+
this._threeObject.rotation.set(rX, rY, rZ);
|
|
169
|
+
this._threeObject.scale.set(zoom * sX, zoom * sY, zoom * sZ);
|
|
170
|
+
|
|
171
|
+
if (this._parent) {
|
|
172
|
+
Bus.publish(this._parent, "add", this._threeObject);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected _disable() {
|
|
177
|
+
if (this._parent) Bus.publish(this._parent, "remove", this._threeObject);
|
|
178
|
+
|
|
179
|
+
this._threeObject = null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
connectedCallback() {
|
|
183
|
+
const selector = "miris-group, miris-scene";
|
|
184
|
+
this._parent = this.parentElement?.closest(selector) ?? null;
|
|
185
|
+
|
|
186
|
+
this._enable();
|
|
187
|
+
|
|
188
|
+
Bus.subscribe(this, "add", (object) => {
|
|
189
|
+
if (this._threeObject?.children.includes(object)) return;
|
|
190
|
+
|
|
191
|
+
this._threeObject?.add(object);
|
|
192
|
+
});
|
|
193
|
+
Bus.subscribe(this, "remove", (object) => {
|
|
194
|
+
this._threeObject?.remove(object);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
disconnectedCallback() {
|
|
199
|
+
Bus.unsubscribe(this, "add");
|
|
200
|
+
Bus.unsubscribe(this, "remove");
|
|
201
|
+
}
|
|
202
|
+
}
|
package/scene.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MirisControls as ThreeControls,
|
|
3
|
+
MirisScene as ThreeScene,
|
|
4
|
+
} from "@miris-inc/three";
|
|
5
|
+
import {
|
|
6
|
+
type Object3D,
|
|
7
|
+
PerspectiveCamera as ThreeCamera,
|
|
8
|
+
WebGLRenderer as ThreeRenderer,
|
|
9
|
+
} from "three";
|
|
10
|
+
import Bus from "./bus";
|
|
11
|
+
|
|
12
|
+
export default class MirisScene extends HTMLElement {
|
|
13
|
+
static observedAttributes = ["key"];
|
|
14
|
+
|
|
15
|
+
#renderer: ThreeRenderer | null = null;
|
|
16
|
+
#scene: ThreeScene | null = null;
|
|
17
|
+
#camera: ThreeCamera | null = null;
|
|
18
|
+
#observer: ResizeObserver | null = null;
|
|
19
|
+
|
|
20
|
+
// prettier-ignore
|
|
21
|
+
get camera() : ThreeCamera { return this.#camera }
|
|
22
|
+
// prettier-ignore
|
|
23
|
+
set camera(camera: ThreeCamera) { this.#camera = camera }
|
|
24
|
+
|
|
25
|
+
#key: string | null = null;
|
|
26
|
+
// prettier-ignore
|
|
27
|
+
get key() { return this.#key }
|
|
28
|
+
set key(key) {
|
|
29
|
+
if (key === this.#key) return;
|
|
30
|
+
|
|
31
|
+
this.#key = key;
|
|
32
|
+
|
|
33
|
+
if (key) this.setAttribute("key", key);
|
|
34
|
+
else this.removeAttribute("key");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
connectedCallback() {
|
|
38
|
+
const camera = new ThreeCamera();
|
|
39
|
+
const renderer = new ThreeRenderer({ alpha: true });
|
|
40
|
+
const controls = new ThreeControls(null, camera, renderer.domElement);
|
|
41
|
+
const scene = new ThreeScene(this.key);
|
|
42
|
+
const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
|
43
|
+
const entry = entries[entries.length - 1]!;
|
|
44
|
+
const width = entry.contentBoxSize[0]!.inlineSize;
|
|
45
|
+
const height = entry.contentBoxSize[0]!.blockSize;
|
|
46
|
+
|
|
47
|
+
renderer.setPixelRatio(devicePixelRatio);
|
|
48
|
+
renderer.setSize(width, height, false);
|
|
49
|
+
|
|
50
|
+
camera.aspect = width / height;
|
|
51
|
+
camera.updateProjectionMatrix();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const sheet = new CSSStyleSheet();
|
|
55
|
+
const shadow = this.attachShadow({ mode: "closed" });
|
|
56
|
+
sheet.insertRule(":host { display: block }");
|
|
57
|
+
sheet.insertRule("canvas { display: block; width: 100%; height: 100% }");
|
|
58
|
+
shadow.adoptedStyleSheets.push(sheet);
|
|
59
|
+
shadow.append(renderer.domElement);
|
|
60
|
+
|
|
61
|
+
observer.observe(this);
|
|
62
|
+
renderer.setAnimationLoop(() => renderer.render(scene, camera));
|
|
63
|
+
this.#renderer = renderer;
|
|
64
|
+
this.#scene = scene;
|
|
65
|
+
this.#observer = observer;
|
|
66
|
+
this.#camera = camera;
|
|
67
|
+
|
|
68
|
+
Bus.subscribe(this, "add", (object: Object3D) => {
|
|
69
|
+
if (scene.children.includes(object)) return;
|
|
70
|
+
if (object instanceof ThreeCamera) return;
|
|
71
|
+
scene.add(object);
|
|
72
|
+
});
|
|
73
|
+
Bus.subscribe(this, "remove", (object: Object3D) => {
|
|
74
|
+
scene.remove(object);
|
|
75
|
+
});
|
|
76
|
+
Bus.subscribe(this, "control", (object: Object3D) => {
|
|
77
|
+
controls.objects.add(object);
|
|
78
|
+
});
|
|
79
|
+
Bus.subscribe(this, "uncontrol", (object: Object3D) => {
|
|
80
|
+
controls.objects.delete(object);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getRenderStats() {
|
|
85
|
+
return {
|
|
86
|
+
callCount: this.#renderer?.info.render.calls,
|
|
87
|
+
programCount: this.#renderer?.info.programs.length,
|
|
88
|
+
triangleCount: this.#renderer?.info.render.triangles,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
disconnectedCallback() {
|
|
93
|
+
this.#observer?.disconnect();
|
|
94
|
+
this.#observer = null;
|
|
95
|
+
this.#renderer?.dispose();
|
|
96
|
+
this.#renderer = null;
|
|
97
|
+
|
|
98
|
+
Bus.unsubscribe(this, "add");
|
|
99
|
+
Bus.unsubscribe(this, "remove");
|
|
100
|
+
Bus.unsubscribe(this, "control");
|
|
101
|
+
Bus.unsubscribe(this, "uncontrol");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
attributeChangedCallback(
|
|
105
|
+
name: string,
|
|
106
|
+
_oldValue: string | null,
|
|
107
|
+
newValue: string | null
|
|
108
|
+
) {
|
|
109
|
+
switch (name) {
|
|
110
|
+
case "key":
|
|
111
|
+
this.key = newValue;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
setViewerKey(viewerKey: string) {
|
|
117
|
+
this.#scene.setViewerKey(viewerKey);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async fetchAssets(...args: Parameters<ThreeScene["fetchAssets"]>) {
|
|
121
|
+
if (this.#scene) return this.#scene.fetchAssets(...args);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get coreScene() {
|
|
125
|
+
return this.#scene.coreScene;
|
|
126
|
+
}
|
|
127
|
+
}
|
package/stream.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PerspectiveCamera as ThreeCamera,
|
|
3
|
+
WebGLRenderer as ThreeRenderer,
|
|
4
|
+
} from "three";
|
|
5
|
+
import {
|
|
6
|
+
MirisScene as ThreeScene,
|
|
7
|
+
MirisStream as ThreeStream,
|
|
8
|
+
MirisControls as ThreeControls,
|
|
9
|
+
} from "@miris-inc/three";
|
|
10
|
+
import Bus from "./bus";
|
|
11
|
+
import Primitive from "./primitive";
|
|
12
|
+
import type Scene from "./scene";
|
|
13
|
+
|
|
14
|
+
export default class MirisStream extends Primitive {
|
|
15
|
+
static observedAttributes = ["uuid", "key", "disabled", "controls"];
|
|
16
|
+
|
|
17
|
+
#disabled = false;
|
|
18
|
+
// prettier-ignore
|
|
19
|
+
get disabled() { return this.#disabled }
|
|
20
|
+
set disabled(disabled) {
|
|
21
|
+
if (disabled === this.#disabled) return;
|
|
22
|
+
|
|
23
|
+
this.#disabled = disabled;
|
|
24
|
+
|
|
25
|
+
if (disabled) {
|
|
26
|
+
this.setAttribute("disabled", "");
|
|
27
|
+
this._disable();
|
|
28
|
+
} else {
|
|
29
|
+
this.removeAttribute("disabled");
|
|
30
|
+
this._enable();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#uuid: string | null = null;
|
|
35
|
+
// prettier-ignore
|
|
36
|
+
get uuid() { return this.#uuid }
|
|
37
|
+
set uuid(uuid) {
|
|
38
|
+
if (uuid === this.#uuid) return;
|
|
39
|
+
|
|
40
|
+
if (this._threeObject) this._threeObject.clearBoxes();
|
|
41
|
+
const oldUuid = this.#uuid;
|
|
42
|
+
|
|
43
|
+
this.#uuid = uuid;
|
|
44
|
+
|
|
45
|
+
if (uuid) {
|
|
46
|
+
this.setAttribute("uuid", uuid);
|
|
47
|
+
if (oldUuid) this._disable();
|
|
48
|
+
this._enable();
|
|
49
|
+
} else {
|
|
50
|
+
this.removeAttribute("uuid");
|
|
51
|
+
this._disable();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#controls = false;
|
|
56
|
+
// prettier-ignore
|
|
57
|
+
get controls() { return this.#controls }
|
|
58
|
+
set controls(controls) {
|
|
59
|
+
if (controls === this.#controls) return;
|
|
60
|
+
|
|
61
|
+
this.#controls = controls;
|
|
62
|
+
|
|
63
|
+
if (controls) this.#control();
|
|
64
|
+
else this.#uncontrol();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#threeControls: ThreeControls | null = null;
|
|
68
|
+
|
|
69
|
+
#key: string | null = null;
|
|
70
|
+
// prettier-ignore
|
|
71
|
+
get key() { return this.#key }
|
|
72
|
+
set key(key) {
|
|
73
|
+
if (key === this.#key) return;
|
|
74
|
+
|
|
75
|
+
this.#key = key;
|
|
76
|
+
|
|
77
|
+
if (key) this.setAttribute("key", key);
|
|
78
|
+
else this.removeAttribute("key");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected override _threeObject: ThreeStream | null = null;
|
|
82
|
+
|
|
83
|
+
#renderer: ThreeRenderer | null = null;
|
|
84
|
+
#observer: ResizeObserver | null = null;
|
|
85
|
+
#shadow: ShadowRoot | null = null;
|
|
86
|
+
#scene: ThreeScene | null = null;
|
|
87
|
+
|
|
88
|
+
constructor() {
|
|
89
|
+
super();
|
|
90
|
+
|
|
91
|
+
this.#key = this.getAttribute("key");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#enableScene() {
|
|
95
|
+
if (this.#renderer) return;
|
|
96
|
+
|
|
97
|
+
const renderer = new ThreeRenderer({ alpha: true });
|
|
98
|
+
const scene = new ThreeScene(this.key);
|
|
99
|
+
const camera = new ThreeCamera();
|
|
100
|
+
const controls = new ThreeControls(null, camera, renderer.domElement);
|
|
101
|
+
const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
|
102
|
+
const entry = entries[entries.length - 1]!;
|
|
103
|
+
const width = entry.contentBoxSize[0]!.inlineSize;
|
|
104
|
+
const height = entry.contentBoxSize[0]!.blockSize;
|
|
105
|
+
|
|
106
|
+
renderer.setPixelRatio(devicePixelRatio);
|
|
107
|
+
renderer.setSize(width, height, false);
|
|
108
|
+
|
|
109
|
+
camera.aspect = width / height;
|
|
110
|
+
camera.updateProjectionMatrix();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!this.#shadow) {
|
|
114
|
+
this.#shadow = this.attachShadow({ mode: "closed" });
|
|
115
|
+
const sheet = new CSSStyleSheet();
|
|
116
|
+
sheet.insertRule(":host { display: block }");
|
|
117
|
+
sheet.insertRule("canvas { display: block; width: 100%; height: 100% }");
|
|
118
|
+
this.#shadow.adoptedStyleSheets.push(sheet);
|
|
119
|
+
this.#shadow.append(renderer.domElement);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (this._threeObject) scene.add(this._threeObject);
|
|
123
|
+
|
|
124
|
+
observer.observe(this);
|
|
125
|
+
renderer.setAnimationLoop(() => renderer.render(scene, camera));
|
|
126
|
+
this.#renderer = renderer;
|
|
127
|
+
this.#scene = scene;
|
|
128
|
+
this.#threeControls = controls;
|
|
129
|
+
this.#observer = observer;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
override _enable() {
|
|
133
|
+
if (this.disabled) return;
|
|
134
|
+
|
|
135
|
+
if (!this._threeObject && this.uuid) {
|
|
136
|
+
this._threeObject = new ThreeStream(this.uuid, this.key);
|
|
137
|
+
this._threeObject.name = this.uuid;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
super._enable();
|
|
141
|
+
|
|
142
|
+
if (this.#scene && this._threeObject) this.#scene.add(this._threeObject);
|
|
143
|
+
if (!this._parent && this.uuid) this.#enableScene();
|
|
144
|
+
|
|
145
|
+
if (this.controls) this.#control();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#disableScene() {
|
|
149
|
+
if (this.#renderer) {
|
|
150
|
+
this.#renderer?.dispose();
|
|
151
|
+
this.#renderer = null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (this.#observer) {
|
|
155
|
+
this.#observer?.disconnect();
|
|
156
|
+
this.#observer = null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
override _disable() {
|
|
161
|
+
// // Cannot implement until Aqua allows multiple scenes per page
|
|
162
|
+
// if (!this._parent) this.#disableScene();
|
|
163
|
+
|
|
164
|
+
if (this._threeObject) this.#scene?.remove(this._threeObject);
|
|
165
|
+
|
|
166
|
+
if (this.controls) this.#uncontrol();
|
|
167
|
+
|
|
168
|
+
super._disable();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#control() {
|
|
172
|
+
if (!this.controls || !this._threeObject) return;
|
|
173
|
+
|
|
174
|
+
const scene = this.closest("miris-scene") as Scene | null;
|
|
175
|
+
|
|
176
|
+
if (scene) Bus.publish(scene, "control", this._threeObject);
|
|
177
|
+
else this.#threeControls?.objects.add(this._threeObject);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#uncontrol() {
|
|
181
|
+
if (this.controls || !this._threeObject) return;
|
|
182
|
+
|
|
183
|
+
const scene = this.closest("miris-scene") as Scene | null;
|
|
184
|
+
|
|
185
|
+
if (scene) Bus.publish(scene, "uncontrol", this._threeObject);
|
|
186
|
+
else this.#threeControls?.objects.delete(this._threeObject);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
attributeChangedCallback(
|
|
190
|
+
name: string,
|
|
191
|
+
_oldValue: string | null,
|
|
192
|
+
newValue: string | null
|
|
193
|
+
) {
|
|
194
|
+
switch (name) {
|
|
195
|
+
case "controls":
|
|
196
|
+
this.controls = newValue !== null;
|
|
197
|
+
break;
|
|
198
|
+
case "disabled":
|
|
199
|
+
this.disabled = newValue !== null;
|
|
200
|
+
break;
|
|
201
|
+
case "key":
|
|
202
|
+
this.#key = newValue;
|
|
203
|
+
break;
|
|
204
|
+
case "uuid":
|
|
205
|
+
this.uuid = newValue;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
update() {
|
|
211
|
+
if (this._threeObject) this._threeObject?.update();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
toggleRenderBounds(shouldRender: boolean) {
|
|
215
|
+
if (this._threeObject) this._threeObject?.toggleRenderBounds(shouldRender);
|
|
216
|
+
}
|
|
217
|
+
}
|