@mirage-engine/core 0.1.0 → 0.2.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/CHANGELOG.md +11 -0
- package/dist/mirage-engine.js +422 -325
- package/dist/mirage-engine.umd.js +36 -33
- package/dist/src/core/Syncer.d.ts +7 -3
- package/dist/src/renderer/Renderer.d.ts +6 -1
- package/dist/src/types/common.d.ts +1 -0
- package/dist/src/types/config.d.ts +8 -2
- package/dist/src/types/flags.d.ts +4 -4
- package/package.json +2 -2
- package/src/core/Engine.ts +1 -1
- package/src/core/Syncer.ts +71 -8
- package/src/dom/Extractor.ts +26 -11
- package/src/renderer/Renderer.ts +129 -23
- package/src/types/common.ts +4 -4
- package/src/types/config.ts +9 -3
- package/src/types/flags.ts +4 -5
package/src/renderer/Renderer.ts
CHANGED
|
@@ -2,9 +2,11 @@ import * as THREE from "three";
|
|
|
2
2
|
import {
|
|
3
3
|
SceneNode,
|
|
4
4
|
DIRTY_CONTENT,
|
|
5
|
-
|
|
5
|
+
Quality,
|
|
6
6
|
CoreConfig,
|
|
7
7
|
MirageMode,
|
|
8
|
+
USER_LAYER,
|
|
9
|
+
SYSTEM_LAYER,
|
|
8
10
|
} from "../types";
|
|
9
11
|
import { Painter } from "@mirage-engine/painter";
|
|
10
12
|
|
|
@@ -13,8 +15,9 @@ export class Renderer {
|
|
|
13
15
|
private readonly scene: THREE.Scene;
|
|
14
16
|
private readonly camera: THREE.OrthographicCamera;
|
|
15
17
|
private readonly renderer: THREE.WebGLRenderer;
|
|
18
|
+
private renderTarget: THREE.WebGLRenderTarget | null = null;
|
|
16
19
|
private renderOrder: number = 0;
|
|
17
|
-
private
|
|
20
|
+
private qualityFactor: number = 2;
|
|
18
21
|
private mode: MirageMode = "overlay";
|
|
19
22
|
|
|
20
23
|
private target: HTMLElement;
|
|
@@ -53,6 +56,7 @@ export class Renderer {
|
|
|
53
56
|
1000,
|
|
54
57
|
);
|
|
55
58
|
this.camera.position.z = 100;
|
|
59
|
+
this.camera.layers.set(0);
|
|
56
60
|
|
|
57
61
|
// [new]
|
|
58
62
|
// THREE.ColorManagement.enabled = false;
|
|
@@ -68,24 +72,38 @@ export class Renderer {
|
|
|
68
72
|
this.renderer.setPixelRatio(window.devicePixelRatio);
|
|
69
73
|
this.renderer.setSize(width, height);
|
|
70
74
|
|
|
71
|
-
this.applyTextQuality(config.
|
|
75
|
+
this.applyTextQuality(config.quality ?? "medium");
|
|
76
|
+
}
|
|
77
|
+
public createRenderTarget() {
|
|
78
|
+
this.renderTarget = new THREE.WebGLRenderTarget(
|
|
79
|
+
this.targetRect.width * this.qualityFactor,
|
|
80
|
+
this.targetRect.height * this.qualityFactor,
|
|
81
|
+
{
|
|
82
|
+
minFilter: THREE.LinearFilter,
|
|
83
|
+
magFilter: THREE.LinearFilter,
|
|
84
|
+
format: THREE.RGBAFormat,
|
|
85
|
+
stencilBuffer: false,
|
|
86
|
+
depthBuffer: true,
|
|
87
|
+
samples: 4,
|
|
88
|
+
},
|
|
89
|
+
);
|
|
72
90
|
}
|
|
73
91
|
|
|
74
|
-
private applyTextQuality(quality:
|
|
92
|
+
private applyTextQuality(quality: Quality) {
|
|
75
93
|
if (typeof quality === "number") {
|
|
76
|
-
this.
|
|
94
|
+
this.qualityFactor = Math.max(0.1, quality);
|
|
77
95
|
return;
|
|
78
96
|
}
|
|
79
97
|
switch (quality) {
|
|
80
98
|
case "low":
|
|
81
|
-
this.
|
|
99
|
+
this.qualityFactor = 1;
|
|
82
100
|
break;
|
|
83
101
|
case "high":
|
|
84
|
-
this.
|
|
102
|
+
this.qualityFactor = 4;
|
|
85
103
|
break;
|
|
86
104
|
case "medium":
|
|
87
105
|
default:
|
|
88
|
-
this.
|
|
106
|
+
this.qualityFactor = 2;
|
|
89
107
|
break;
|
|
90
108
|
}
|
|
91
109
|
}
|
|
@@ -123,12 +141,16 @@ export class Renderer {
|
|
|
123
141
|
|
|
124
142
|
public setSize(width: number, height: number) {
|
|
125
143
|
this.renderer.setSize(width, height);
|
|
126
|
-
|
|
144
|
+
if (this.renderTarget) {
|
|
145
|
+
this.renderTarget.setSize(
|
|
146
|
+
width * this.qualityFactor,
|
|
147
|
+
height * this.qualityFactor,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
127
150
|
this.camera.left = width / -2;
|
|
128
151
|
this.camera.right = width / 2;
|
|
129
152
|
this.camera.top = height / 2;
|
|
130
153
|
this.camera.bottom = height / -2;
|
|
131
|
-
|
|
132
154
|
this.camera.updateProjectionMatrix();
|
|
133
155
|
}
|
|
134
156
|
|
|
@@ -146,14 +168,7 @@ export class Renderer {
|
|
|
146
168
|
|
|
147
169
|
if (isResized) {
|
|
148
170
|
this.targetRect = newRect;
|
|
149
|
-
this.
|
|
150
|
-
|
|
151
|
-
this.camera.left = this.targetRect.width / -2;
|
|
152
|
-
this.camera.right = this.targetRect.width / 2;
|
|
153
|
-
this.camera.top = this.targetRect.height / 2;
|
|
154
|
-
this.camera.bottom = this.targetRect.height / -2;
|
|
155
|
-
this.camera.layers.set(0);
|
|
156
|
-
this.camera.updateProjectionMatrix();
|
|
171
|
+
this.setSize(this.targetRect.width, this.targetRect.height);
|
|
157
172
|
|
|
158
173
|
this.updateCanvasLayout();
|
|
159
174
|
} else if (isMoved) {
|
|
@@ -186,16 +201,17 @@ export class Renderer {
|
|
|
186
201
|
let mesh = this.meshMap.get(node.element);
|
|
187
202
|
if (!mesh) {
|
|
188
203
|
const geometry = new THREE.PlaneGeometry(1, 1);
|
|
189
|
-
|
|
204
|
+
let material: THREE.MeshBasicMaterial | THREE.Material;
|
|
205
|
+
material = Painter.create(
|
|
190
206
|
"BOX",
|
|
191
207
|
node.styles,
|
|
192
208
|
"",
|
|
193
209
|
node.rect.width,
|
|
194
210
|
node.rect.height,
|
|
211
|
+
this.qualityFactor,
|
|
212
|
+
node.isTraveler ? this.renderTarget?.texture : undefined,
|
|
195
213
|
);
|
|
196
|
-
|
|
197
214
|
mesh = new THREE.Mesh(geometry, material);
|
|
198
|
-
mesh.layers.set(node.visibility);
|
|
199
215
|
if (node.type === "TEXT") mesh.name = "BG_MESH";
|
|
200
216
|
this.scene.add(mesh);
|
|
201
217
|
this.meshMap.set(node.element, mesh);
|
|
@@ -204,6 +220,8 @@ export class Renderer {
|
|
|
204
220
|
mesh.userData.domRect = node.rect;
|
|
205
221
|
|
|
206
222
|
this.updateMeshProperties(mesh, node);
|
|
223
|
+
this.updateMeshLayers(mesh, node);
|
|
224
|
+
if (node.isTraveler) mesh.layers.enable(28);
|
|
207
225
|
|
|
208
226
|
if (node.type === "BOX") {
|
|
209
227
|
for (const child of node.children) {
|
|
@@ -239,7 +257,7 @@ export class Renderer {
|
|
|
239
257
|
node.textContent || "",
|
|
240
258
|
node.rect.width,
|
|
241
259
|
node.rect.height,
|
|
242
|
-
this.
|
|
260
|
+
this.qualityFactor,
|
|
243
261
|
);
|
|
244
262
|
|
|
245
263
|
const geometry = new THREE.PlaneGeometry(1, 1);
|
|
@@ -247,7 +265,7 @@ export class Renderer {
|
|
|
247
265
|
|
|
248
266
|
textMesh.name = "TEXT_CHILD";
|
|
249
267
|
textMesh.userData = { styleHash: currentStyleHash };
|
|
250
|
-
|
|
268
|
+
this.updateMeshLayers(textMesh, node);
|
|
251
269
|
textMesh.position.z = 0.005;
|
|
252
270
|
parentMesh.add(textMesh);
|
|
253
271
|
}
|
|
@@ -297,10 +315,98 @@ export class Renderer {
|
|
|
297
315
|
"",
|
|
298
316
|
node.rect.width,
|
|
299
317
|
node.rect.height,
|
|
318
|
+
this.qualityFactor,
|
|
319
|
+
node.isTraveler ? this.renderTarget?.texture : undefined,
|
|
300
320
|
);
|
|
301
321
|
}
|
|
302
322
|
|
|
323
|
+
private updateMeshLayers(mesh: THREE.Mesh, node: SceneNode) {
|
|
324
|
+
const layerNum = (1 - (node.visibility & USER_LAYER)) * 30;
|
|
325
|
+
mesh.layers.set(layerNum);
|
|
326
|
+
if (node.visibility === (USER_LAYER | SYSTEM_LAYER)) mesh.layers.enable(29);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private captureRenderTarget() {
|
|
330
|
+
const travelers: THREE.Mesh[] = [];
|
|
331
|
+
for (const mesh of this.meshMap.values()) {
|
|
332
|
+
if ((mesh.layers.mask & (1 << 28)) !== 0) {
|
|
333
|
+
travelers.push(mesh);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (travelers.length === 0) return;
|
|
337
|
+
|
|
338
|
+
const oldClearColor = new THREE.Color();
|
|
339
|
+
const oldClearAlpha = this.renderer.getClearAlpha();
|
|
340
|
+
this.renderer.getClearColor(oldClearColor);
|
|
341
|
+
|
|
342
|
+
this.renderer.setClearColor(0x000000, 0);
|
|
343
|
+
this.renderer.setRenderTarget(this.renderTarget);
|
|
344
|
+
|
|
345
|
+
this.renderer.clear();
|
|
346
|
+
|
|
347
|
+
this.renderer.autoClear = false;
|
|
348
|
+
this.renderer.setScissorTest(true);
|
|
349
|
+
this.camera.layers.set(29);
|
|
350
|
+
|
|
351
|
+
const vector = new THREE.Vector3();
|
|
352
|
+
const canvasWidth = this.targetRect.width;
|
|
353
|
+
const canvasHeight = this.targetRect.height;
|
|
354
|
+
|
|
355
|
+
const pixelRatio = this.renderer.getPixelRatio();
|
|
356
|
+
|
|
357
|
+
for (const traveler of travelers) {
|
|
358
|
+
vector.setFromMatrixPosition(traveler.matrixWorld);
|
|
359
|
+
vector.project(this.camera);
|
|
360
|
+
|
|
361
|
+
const centerX = ((vector.x + 1) / 2) * canvasWidth;
|
|
362
|
+
const centerY = ((vector.y + 1) / 2) * canvasHeight;
|
|
363
|
+
|
|
364
|
+
const width = traveler.scale.x;
|
|
365
|
+
const height = traveler.scale.y;
|
|
366
|
+
|
|
367
|
+
const localX = centerX - width / 2;
|
|
368
|
+
const localY = centerY - height / 2;
|
|
369
|
+
|
|
370
|
+
const scissorX = (localX * this.qualityFactor) / pixelRatio;
|
|
371
|
+
const scissorY = (localY * this.qualityFactor) / pixelRatio;
|
|
372
|
+
const scissorW = (width * this.qualityFactor) / pixelRatio;
|
|
373
|
+
const scissorH = (height * this.qualityFactor) / pixelRatio;
|
|
374
|
+
|
|
375
|
+
this.renderer.setScissor(scissorX, scissorY, scissorW, scissorH);
|
|
376
|
+
this.renderer.render(this.scene, this.camera);
|
|
377
|
+
}
|
|
378
|
+
this.renderer.setScissorTest(false);
|
|
379
|
+
this.renderer.autoClear = true;
|
|
380
|
+
this.renderer.setRenderTarget(null);
|
|
381
|
+
this.camera.layers.set(28);
|
|
382
|
+
this.renderer.setClearColor(oldClearColor, oldClearAlpha);
|
|
383
|
+
}
|
|
384
|
+
|
|
303
385
|
public render() {
|
|
386
|
+
if (this.renderTarget) this.captureRenderTarget();
|
|
304
387
|
this.renderer.render(this.scene, this.camera);
|
|
305
388
|
}
|
|
389
|
+
|
|
390
|
+
// for debugging
|
|
391
|
+
public showScissoredRenderTarget() {
|
|
392
|
+
if (!this.renderTarget) return;
|
|
393
|
+
|
|
394
|
+
const w = this.targetRect.width;
|
|
395
|
+
const h = this.targetRect.height;
|
|
396
|
+
|
|
397
|
+
const geometry = new THREE.PlaneGeometry(w, h);
|
|
398
|
+
const material = new THREE.MeshBasicMaterial({
|
|
399
|
+
map: this.renderTarget.texture,
|
|
400
|
+
side: THREE.DoubleSide,
|
|
401
|
+
transparent: true,
|
|
402
|
+
opacity: 0.8,
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const debugMesh = new THREE.Mesh(geometry, material);
|
|
406
|
+
|
|
407
|
+
debugMesh.position.set(0, 0, 90);
|
|
408
|
+
debugMesh.layers.set(28);
|
|
409
|
+
|
|
410
|
+
this.scene.add(debugMesh);
|
|
411
|
+
}
|
|
306
412
|
}
|
package/src/types/common.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TextStyles, BoxStyles } from "@mirage-engine/painter";
|
|
2
2
|
import { Visibility } from "./flags";
|
|
3
3
|
|
|
4
|
-
export type NodeType = "BOX" | "TEXT"
|
|
4
|
+
export type NodeType = "BOX" | "TEXT";
|
|
5
5
|
|
|
6
6
|
export interface NodeRect {
|
|
7
7
|
x: number;
|
|
@@ -14,7 +14,7 @@ export interface SceneNode {
|
|
|
14
14
|
id: string;
|
|
15
15
|
type: NodeType;
|
|
16
16
|
element: HTMLElement;
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
rect: NodeRect;
|
|
19
19
|
styles: BoxStyles;
|
|
20
20
|
|
|
@@ -25,7 +25,7 @@ export interface SceneNode {
|
|
|
25
25
|
// TODO: SceneNode의 sort 로직 추가(아마도)
|
|
26
26
|
|
|
27
27
|
visibility: Visibility;
|
|
28
|
-
|
|
28
|
+
isTraveler: boolean;
|
|
29
|
+
|
|
29
30
|
children: SceneNode[];
|
|
30
31
|
}
|
|
31
|
-
|
package/src/types/config.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type Quality = "low" | "medium" | "high" | number;
|
|
2
2
|
export type MirageMode = "overlay" | "duplicate";
|
|
3
|
-
|
|
4
3
|
export interface FilterConfig {
|
|
5
4
|
includeTree?: string[];
|
|
6
5
|
excludeTree?: string[];
|
|
@@ -9,13 +8,20 @@ export interface FilterConfig {
|
|
|
9
8
|
end?: string[];
|
|
10
9
|
}
|
|
11
10
|
|
|
11
|
+
export interface ResizeConfig {
|
|
12
|
+
delay?: number;
|
|
13
|
+
onStart?: () => void;
|
|
14
|
+
onEnd?: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
interface BaseConfig {
|
|
13
18
|
debug?: boolean;
|
|
14
|
-
|
|
19
|
+
quality?: Quality;
|
|
15
20
|
style?: {
|
|
16
21
|
zIndex?: string;
|
|
17
22
|
};
|
|
18
23
|
filter?: FilterConfig;
|
|
24
|
+
resizeDebounce?: boolean | ResizeConfig;
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
export interface OverlayConfig extends BaseConfig {
|
package/src/types/flags.ts
CHANGED
|
@@ -7,12 +7,11 @@ export const DIRTY_STRUCTURE = 1 << 3; // 8: childNode add / delete (00001000)
|
|
|
7
7
|
export const DIRTY_CONTENT = 1 << 4; // 16: text content changed (00010000)
|
|
8
8
|
|
|
9
9
|
// Filtering Flag
|
|
10
|
-
export const
|
|
11
|
-
export const
|
|
12
|
-
export const EXCLUDED =
|
|
10
|
+
export const USER_LAYER = 1 << 0;
|
|
11
|
+
export const SYSTEM_LAYER = 1 << 1;
|
|
12
|
+
export const EXCLUDED = 0;
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
export type Visibility = typeof EXCLUDED | typeof SYSTEM | typeof INCLUDED;
|
|
14
|
+
export type Visibility = 0 | 1 | 2 | 3;
|
|
16
15
|
|
|
17
16
|
export const ALLOWED_FILTERS = [
|
|
18
17
|
"include-tree",
|