@mirage-engine/core 0.3.20 → 0.3.22
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 +20 -0
- package/dist/mirage-engine.js +1416 -1203
- package/dist/mirage-engine.umd.js +18 -18
- package/dist/src/animation/Animator.d.ts +1 -1
- package/dist/src/core/Engine.d.ts +1 -1
- package/dist/src/core/Syncer.d.ts +3 -1
- package/dist/src/dom/Extractor.d.ts +6 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/renderer/Renderer.d.ts +16 -1
- package/dist/src/types/common.d.ts +7 -0
- package/dist/src/wasm/WasmSynchronizer.d.ts +12 -0
- package/package.json +4 -3
- package/src/animation/Animator.ts +96 -3
- package/src/core/Engine.ts +2 -2
- package/src/core/Syncer.ts +33 -5
- package/src/dom/Extractor.ts +104 -49
- package/src/index.ts +1 -0
- package/src/renderer/Renderer.ts +111 -165
- package/src/types/common.ts +9 -0
- package/src/wasm/WasmSynchronizer.ts +44 -0
package/src/renderer/Renderer.ts
CHANGED
|
@@ -8,9 +8,14 @@ import {
|
|
|
8
8
|
USER_LAYER,
|
|
9
9
|
SELECT_LAYER,
|
|
10
10
|
travelerClipArea,
|
|
11
|
-
THREE_LAYERS,
|
|
12
11
|
ATTR_TRAVEL,
|
|
12
|
+
THREE_LAYERS,
|
|
13
13
|
LayerTarget,
|
|
14
|
+
WASM_STRIDE,
|
|
15
|
+
OFFSET_WORLD_X,
|
|
16
|
+
OFFSET_WORLD_Y,
|
|
17
|
+
OFFSET_LOCAL_X,
|
|
18
|
+
OFFSET_LOCAL_Y,
|
|
14
19
|
} from "../types";
|
|
15
20
|
import { Painter, TextStyles, BoxStyles } from "@mirage-engine/painter";
|
|
16
21
|
import { MeshRegistry } from "../store/MeshRegistry";
|
|
@@ -38,6 +43,14 @@ export class Renderer {
|
|
|
38
43
|
private mountContainer: HTMLElement;
|
|
39
44
|
private registry: MeshRegistry;
|
|
40
45
|
private targetRect: DOMRect;
|
|
46
|
+
private targetPageX: number = 0;
|
|
47
|
+
private targetPageY: number = 0;
|
|
48
|
+
public size: { width: number; height: number } = { width: 0, height: 0 };
|
|
49
|
+
|
|
50
|
+
private currentScrollX: number = 0;
|
|
51
|
+
private currentScrollY: number = 0;
|
|
52
|
+
private lastTargetLeft: number = 0;
|
|
53
|
+
private lastTargetTop: number = 0;
|
|
41
54
|
|
|
42
55
|
private travelersByLayer: Set<THREE.Mesh>[] = Array.from(
|
|
43
56
|
{ length: ATTR_TRAVEL.MAX_LAYERS },
|
|
@@ -70,10 +83,17 @@ export class Renderer {
|
|
|
70
83
|
}
|
|
71
84
|
}, this.isViewport);
|
|
72
85
|
|
|
86
|
+
this.qualityFactor = this.getQualityFactor(config.quality);
|
|
87
|
+
|
|
73
88
|
this.canvas = document.createElement("canvas");
|
|
74
89
|
this.scene = new THREE.Scene();
|
|
75
90
|
|
|
76
91
|
this.targetRect = this.target.getBoundingClientRect();
|
|
92
|
+
this.lastTargetLeft = this.targetRect.left;
|
|
93
|
+
this.lastTargetTop = this.targetRect.top;
|
|
94
|
+
|
|
95
|
+
this.targetPageX = this.targetRect.left + this.getScrollX();
|
|
96
|
+
this.targetPageY = this.targetRect.top + this.getScrollY();
|
|
77
97
|
const width = this.isViewport
|
|
78
98
|
? window.innerWidth + this.overscan * 2
|
|
79
99
|
: this.targetRect.width;
|
|
@@ -169,6 +189,31 @@ export class Renderer {
|
|
|
169
189
|
}
|
|
170
190
|
}
|
|
171
191
|
|
|
192
|
+
private getQualityFactor(quality: Quality | undefined): number {
|
|
193
|
+
if (typeof quality === "number") return Math.max(0.1, quality);
|
|
194
|
+
switch (quality) {
|
|
195
|
+
case "low": return 1;
|
|
196
|
+
case "high": return 4;
|
|
197
|
+
default: return 2;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
public updateScroll() {
|
|
202
|
+
const rect = this.target.getBoundingClientRect();
|
|
203
|
+
this.currentScrollX += (this.lastTargetLeft - rect.left);
|
|
204
|
+
this.currentScrollY += (this.lastTargetTop - rect.top);
|
|
205
|
+
this.lastTargetLeft = rect.left;
|
|
206
|
+
this.lastTargetTop = rect.top;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
public getScrollX(): number {
|
|
210
|
+
return this.currentScrollX;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
public getScrollY(): number {
|
|
214
|
+
return this.currentScrollY;
|
|
215
|
+
}
|
|
216
|
+
|
|
172
217
|
public mount() {
|
|
173
218
|
this.mountContainer.prepend(this.canvas);
|
|
174
219
|
|
|
@@ -274,14 +319,20 @@ export class Renderer {
|
|
|
274
319
|
|
|
275
320
|
if (isResized) {
|
|
276
321
|
this.targetRect = newRect;
|
|
322
|
+
this.targetPageX = newRect.left + this.getScrollX();
|
|
323
|
+
this.targetPageY = newRect.top + this.getScrollY();
|
|
277
324
|
this.setSize(newWidth, newHeight);
|
|
278
325
|
|
|
279
326
|
this.updateCanvasLayout();
|
|
280
327
|
} else if (isMoved) {
|
|
281
328
|
this.targetRect = newRect;
|
|
329
|
+
this.targetPageX = newRect.left + this.getScrollX();
|
|
330
|
+
this.targetPageY = newRect.top + this.getScrollY();
|
|
282
331
|
this.updateCanvasLayout();
|
|
283
332
|
} else {
|
|
284
333
|
this.targetRect = newRect;
|
|
334
|
+
this.targetPageX = newRect.left + this.getScrollX();
|
|
335
|
+
this.targetPageY = newRect.top + this.getScrollY();
|
|
285
336
|
}
|
|
286
337
|
|
|
287
338
|
this.renderOrder = 0;
|
|
@@ -381,6 +432,7 @@ export class Renderer {
|
|
|
381
432
|
}
|
|
382
433
|
|
|
383
434
|
mesh.userData.clipElements = node.clipElements;
|
|
435
|
+
mesh.userData.wasmIndex = node.wasmIndex;
|
|
384
436
|
|
|
385
437
|
if (node.nativeStyles?.transform) {
|
|
386
438
|
mesh.userData.nativeTransform = node.nativeStyles.transform;
|
|
@@ -573,20 +625,21 @@ export class Renderer {
|
|
|
573
625
|
width: rect.width,
|
|
574
626
|
height: rect.height,
|
|
575
627
|
};
|
|
628
|
+
mesh.userData.needsNativeReset = true;
|
|
576
629
|
|
|
577
630
|
const Z_MICRO_OFFSET = 0.001;
|
|
578
631
|
this.renderOrder++;
|
|
579
632
|
|
|
580
|
-
const targetPageX = this.targetRect.left +
|
|
581
|
-
const targetPageY = this.targetRect.top +
|
|
633
|
+
// const targetPageX = this.targetRect.left + this.getScrollX();
|
|
634
|
+
// const targetPageY = this.targetRect.top + this.getScrollY();
|
|
582
635
|
|
|
583
636
|
let baseX: number, baseY: number;
|
|
584
637
|
if (this.isViewport) {
|
|
585
638
|
baseX = rect.x - window.innerWidth / 2 + rect.width / 2;
|
|
586
639
|
baseY = -rect.y + window.innerHeight / 2 - rect.height / 2;
|
|
587
640
|
} else {
|
|
588
|
-
const localX = rect.x - targetPageX;
|
|
589
|
-
const localY = rect.y - targetPageY;
|
|
641
|
+
const localX = rect.x - this.targetPageX;
|
|
642
|
+
const localY = rect.y - this.targetPageY;
|
|
590
643
|
baseX = localX - canvasWidth / 2 + rect.width / 2;
|
|
591
644
|
baseY = -localY + canvasHeight / 2 - rect.height / 2;
|
|
592
645
|
}
|
|
@@ -652,7 +705,7 @@ export class Renderer {
|
|
|
652
705
|
this.qualityFactor,
|
|
653
706
|
node.isTraveler
|
|
654
707
|
? this.renderTargets[node.captureLayer - 2]?.texture
|
|
655
|
-
: this.textureManager.get(node.element),
|
|
708
|
+
: ((node.nativeStyles as any).imageSrc && (node.nativeStyles as any).imageSrc !== (node.styles as any).imageSrc) ? new THREE.TextureLoader().load((node.nativeStyles as any).imageSrc) : this.textureManager.get(node.element),
|
|
656
709
|
node.shaderHooks,
|
|
657
710
|
);
|
|
658
711
|
const nativeMesh = new THREE.Mesh(mesh.geometry, nativeMaterial);
|
|
@@ -671,8 +724,8 @@ export class Renderer {
|
|
|
671
724
|
window.innerHeight / 2 -
|
|
672
725
|
node.nativeRect.height / 2;
|
|
673
726
|
} else {
|
|
674
|
-
const nativeLocalX = node.nativeRect.x - targetPageX;
|
|
675
|
-
const nativeLocalY = node.nativeRect.y - targetPageY;
|
|
727
|
+
const nativeLocalX = node.nativeRect.x - this.targetPageX;
|
|
728
|
+
const nativeLocalY = node.nativeRect.y - this.targetPageY;
|
|
676
729
|
nativeBaseX =
|
|
677
730
|
nativeLocalX - canvasWidth / 2 + node.nativeRect.width / 2;
|
|
678
731
|
nativeBaseY =
|
|
@@ -757,7 +810,7 @@ export class Renderer {
|
|
|
757
810
|
this.qualityFactor,
|
|
758
811
|
node.isTraveler
|
|
759
812
|
? this.renderTargets[node.captureLayer - 2]?.texture
|
|
760
|
-
: this.textureManager.get(node.element),
|
|
813
|
+
: ((nativeMesh.material as THREE.ShaderMaterial).uniforms.uTexture.value) || this.textureManager.get(node.element),
|
|
761
814
|
);
|
|
762
815
|
} else {
|
|
763
816
|
if (mesh.userData.nativeMesh) {
|
|
@@ -943,177 +996,70 @@ export class Renderer {
|
|
|
943
996
|
this.renderer.render(this.scene, this.camera);
|
|
944
997
|
}
|
|
945
998
|
|
|
946
|
-
public
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
999
|
+
public saveInitialLocals(sharedArray: Float32Array) {
|
|
1000
|
+
this.scene.children.forEach((child) => {
|
|
1001
|
+
const mesh = child as THREE.Mesh;
|
|
1002
|
+
if (mesh.userData.wasmIndex !== undefined) {
|
|
1003
|
+
const offset = mesh.userData.wasmIndex * WASM_STRIDE;
|
|
1004
|
+
mesh.userData.initialLocalX = sharedArray[offset + OFFSET_LOCAL_X];
|
|
1005
|
+
mesh.userData.initialLocalY = sharedArray[offset + OFFSET_LOCAL_Y];
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
950
1009
|
|
|
1010
|
+
public syncMeshesByWasm(sharedArray: Float32Array) {
|
|
951
1011
|
const pixelRatio = this.renderer.getPixelRatio();
|
|
952
1012
|
const canvasWidth = this.renderer.domElement.width / pixelRatio;
|
|
953
1013
|
const canvasHeight = this.renderer.domElement.height / pixelRatio;
|
|
954
1014
|
|
|
955
1015
|
this.scene.children.forEach((child) => {
|
|
956
1016
|
const mesh = child as THREE.Mesh;
|
|
957
|
-
if (!mesh.userData ||
|
|
958
|
-
|
|
959
|
-
const element = mesh.userData.domElement as HTMLElement;
|
|
960
|
-
if (!element.isConnected) return;
|
|
961
|
-
let rect: DOMRect;
|
|
962
|
-
if (element.nodeType === Node.TEXT_NODE) {
|
|
963
|
-
const range = document.createRange();
|
|
964
|
-
range.selectNode(element);
|
|
965
|
-
rect = range.getBoundingClientRect();
|
|
966
|
-
} else {
|
|
967
|
-
rect = element.getBoundingClientRect();
|
|
968
|
-
}
|
|
969
|
-
const cached = mesh.userData.domRect;
|
|
970
|
-
|
|
971
|
-
// Update if position or size changed by more than 0.5px
|
|
972
|
-
if (
|
|
973
|
-
!cached ||
|
|
974
|
-
Math.abs(rect.x - cached.x) > 0.5 ||
|
|
975
|
-
Math.abs(rect.y - cached.y) > 0.5 ||
|
|
976
|
-
Math.abs(rect.width - cached.width) > 0.5 ||
|
|
977
|
-
Math.abs(rect.height - cached.height) > 0.5
|
|
978
|
-
) {
|
|
979
|
-
mesh.userData.domRect = {
|
|
980
|
-
x: rect.x,
|
|
981
|
-
y: rect.y,
|
|
982
|
-
width: rect.width,
|
|
983
|
-
height: rect.height,
|
|
984
|
-
};
|
|
985
|
-
|
|
986
|
-
// Compute scissorRect from clipElements (overflow:hidden ancestors)
|
|
987
|
-
const clipEls = mesh.userData.clipElements as HTMLElement[] | undefined;
|
|
988
|
-
if (clipEls && clipEls.length > 0) {
|
|
989
|
-
let clipLeft = -Infinity;
|
|
990
|
-
let clipTop = -Infinity;
|
|
991
|
-
let clipRight = Infinity;
|
|
992
|
-
let clipBottom = Infinity;
|
|
993
|
-
for (const clipEl of clipEls) {
|
|
994
|
-
const cr = clipEl.getBoundingClientRect();
|
|
995
|
-
clipLeft = Math.max(clipLeft, cr.left);
|
|
996
|
-
clipTop = Math.max(clipTop, cr.top);
|
|
997
|
-
clipRight = Math.min(clipRight, cr.right);
|
|
998
|
-
clipBottom = Math.min(clipBottom, cr.bottom);
|
|
999
|
-
}
|
|
1000
|
-
// Intersect with the mesh's own rect to avoid scissoring larger than the mesh
|
|
1001
|
-
mesh.userData.scissorRect = {
|
|
1002
|
-
x: clipLeft,
|
|
1003
|
-
y: clipTop,
|
|
1004
|
-
width: Math.max(0, clipRight - clipLeft),
|
|
1005
|
-
height: Math.max(0, clipBottom - clipTop),
|
|
1006
|
-
};
|
|
1007
|
-
} else {
|
|
1008
|
-
mesh.userData.scissorRect = undefined;
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
let baseX: number, baseY: number;
|
|
1012
|
-
if (this.isViewport) {
|
|
1013
|
-
baseX = rect.x - window.innerWidth / 2 + rect.width / 2;
|
|
1014
|
-
baseY = -rect.y + window.innerHeight / 2 - rect.height / 2;
|
|
1015
|
-
} else {
|
|
1016
|
-
const localX = rect.x - targetPageX;
|
|
1017
|
-
const localY = rect.y - targetPageY;
|
|
1018
|
-
baseX = localX - canvasWidth / 2 + rect.width / 2;
|
|
1019
|
-
baseY = -localY + canvasHeight / 2 - rect.height / 2;
|
|
1020
|
-
}
|
|
1017
|
+
if (!mesh.userData || mesh.userData.wasmIndex === undefined) return;
|
|
1021
1018
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
mesh.updateMatrixWorld();
|
|
1028
|
-
|
|
1029
|
-
// Update uniforms so shader-based drawing (like border-radius) doesn't stretch
|
|
1030
|
-
if (mesh.material instanceof THREE.ShaderMaterial) {
|
|
1031
|
-
Painter.forceUpdateUniforms(mesh.material, {
|
|
1032
|
-
width: rect.width,
|
|
1033
|
-
height: rect.height,
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
// Update native mesh if exists
|
|
1038
|
-
if (mesh.userData.nativeMesh) {
|
|
1039
|
-
const nativeMesh = mesh.userData.nativeMesh as THREE.Mesh;
|
|
1040
|
-
let nScaleX = rect.width;
|
|
1041
|
-
let nScaleY = rect.height;
|
|
1042
|
-
let nPosX = baseX;
|
|
1043
|
-
let nPosY = baseY;
|
|
1044
|
-
|
|
1045
|
-
if (mesh.userData.nativeTransform) {
|
|
1046
|
-
const transformStr = mesh.userData.nativeTransform as string;
|
|
1047
|
-
|
|
1048
|
-
const scaleMatch = transformStr.match(/scale\(([\d.]+%?)\)/);
|
|
1049
|
-
if (scaleMatch) {
|
|
1050
|
-
let scaleVal = parseFloat(scaleMatch[1]);
|
|
1051
|
-
if (scaleMatch[1].includes("%")) scaleVal /= 100;
|
|
1052
|
-
nScaleX *= scaleVal;
|
|
1053
|
-
nScaleY *= scaleVal;
|
|
1054
|
-
}
|
|
1019
|
+
const wasmIndex = mesh.userData.wasmIndex as number;
|
|
1020
|
+
const offset = wasmIndex * WASM_STRIDE;
|
|
1021
|
+
|
|
1022
|
+
const worldX = sharedArray[offset + OFFSET_WORLD_X];
|
|
1023
|
+
const worldY = sharedArray[offset + OFFSET_WORLD_Y];
|
|
1055
1024
|
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
let scaleVal = parseFloat(scaleXMatch[1]);
|
|
1059
|
-
if (scaleXMatch[1].includes("%")) scaleVal /= 100;
|
|
1060
|
-
nScaleX *= scaleVal;
|
|
1061
|
-
}
|
|
1025
|
+
const rect = mesh.userData.domRect;
|
|
1026
|
+
if (!rect) return;
|
|
1062
1027
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
if (scaleYMatch[1].includes("%")) scaleVal /= 100;
|
|
1067
|
-
nScaleY *= scaleVal;
|
|
1068
|
-
}
|
|
1028
|
+
const isFixed = this.fixedMeshes.has(mesh);
|
|
1029
|
+
const effectiveScrollX = isFixed ? 0 : this.getScrollX();
|
|
1030
|
+
const effectiveScrollY = isFixed ? 0 : this.getScrollY();
|
|
1069
1031
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
nPosY -= ty;
|
|
1083
|
-
}
|
|
1032
|
+
let baseX: number, baseY: number;
|
|
1033
|
+
if (this.isViewport) {
|
|
1034
|
+
const viewportX = worldX - effectiveScrollX;
|
|
1035
|
+
const viewportY = worldY - effectiveScrollY;
|
|
1036
|
+
baseX = viewportX - window.innerWidth / 2 + rect.width / 2;
|
|
1037
|
+
baseY = -viewportY + window.innerHeight / 2 - rect.height / 2;
|
|
1038
|
+
} else {
|
|
1039
|
+
const localX = worldX - this.targetPageX;
|
|
1040
|
+
const localY = worldY - this.targetPageY;
|
|
1041
|
+
baseX = localX - canvasWidth / 2 + rect.width / 2;
|
|
1042
|
+
baseY = -localY + canvasHeight / 2 - rect.height / 2;
|
|
1043
|
+
}
|
|
1084
1044
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
const txStr = translateXMatch[1].trim();
|
|
1088
|
-
let tx = parseFloat(txStr);
|
|
1089
|
-
if (txStr.includes("%")) tx = (tx / 100) * rect.width;
|
|
1090
|
-
nPosX += tx;
|
|
1091
|
-
}
|
|
1045
|
+
const currentZ = mesh.position.z;
|
|
1046
|
+
mesh.position.set(baseX, baseY, currentZ);
|
|
1092
1047
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
nativeMesh.position.setX(nPosX);
|
|
1104
|
-
nativeMesh.position.setY(nPosY);
|
|
1105
|
-
nativeMesh.scale.set(nScaleX + nPad * 2, nScaleY + nPad * 2, 1);
|
|
1106
|
-
nativeMesh.updateMatrixWorld();
|
|
1107
|
-
|
|
1108
|
-
// Update uniforms so native shader-based drawing doesn't stretch
|
|
1109
|
-
if (nativeMesh.material instanceof THREE.ShaderMaterial) {
|
|
1110
|
-
Painter.forceUpdateUniforms(nativeMesh.material, {
|
|
1111
|
-
width: nScaleX,
|
|
1112
|
-
height: nScaleY,
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1048
|
+
if (mesh.userData.nativeMesh) {
|
|
1049
|
+
const nativeMesh = mesh.userData.nativeMesh as THREE.Mesh;
|
|
1050
|
+
if (mesh.userData.needsNativeReset) {
|
|
1051
|
+
mesh.userData.needsNativeReset = false;
|
|
1052
|
+
} else {
|
|
1053
|
+
const deltaX = baseX - mesh.userData.basePosition.x;
|
|
1054
|
+
const deltaY = baseY - mesh.userData.basePosition.y;
|
|
1055
|
+
|
|
1056
|
+
nativeMesh.position.setX(nativeMesh.position.x + deltaX);
|
|
1057
|
+
nativeMesh.position.setY(nativeMesh.position.y + deltaY);
|
|
1115
1058
|
}
|
|
1116
1059
|
}
|
|
1060
|
+
|
|
1061
|
+
mesh.userData.basePosition.x = baseX;
|
|
1062
|
+
mesh.userData.basePosition.y = baseY;
|
|
1117
1063
|
});
|
|
1118
1064
|
}
|
|
1119
1065
|
}
|
package/src/types/common.ts
CHANGED
|
@@ -10,6 +10,13 @@ export interface NodeRect {
|
|
|
10
10
|
height: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export const WASM_STRIDE = 5;
|
|
14
|
+
export const OFFSET_PARENT = 0;
|
|
15
|
+
export const OFFSET_LOCAL_X = 1;
|
|
16
|
+
export const OFFSET_LOCAL_Y = 2;
|
|
17
|
+
export const OFFSET_WORLD_X = 3;
|
|
18
|
+
export const OFFSET_WORLD_Y = 4;
|
|
19
|
+
|
|
13
20
|
export interface SceneNode {
|
|
14
21
|
id: string;
|
|
15
22
|
type: NodeType;
|
|
@@ -35,5 +42,7 @@ export interface SceneNode {
|
|
|
35
42
|
shaderHooks?: ShaderHooks;
|
|
36
43
|
clipElements?: HTMLElement[];
|
|
37
44
|
|
|
45
|
+
wasmIndex?: number;
|
|
46
|
+
|
|
38
47
|
children: SceneNode[];
|
|
39
48
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import init, { MemoryManager } from "../../../wasm-compute/pkg/wasm_compute.js";
|
|
2
|
+
|
|
3
|
+
export class WasmSynchronizer {
|
|
4
|
+
private memoryManager: MemoryManager | null = null;
|
|
5
|
+
public sharedArray: Float32Array | null = null;
|
|
6
|
+
private isInitialized = false;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Initialize Wasm instance and allocate a 1D Float32Array shared memory
|
|
10
|
+
* @param capacity The number of Float32 elements to allocate
|
|
11
|
+
*/
|
|
12
|
+
async initialize(capacity: number) {
|
|
13
|
+
if (this.isInitialized) return;
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const wasm = await init();
|
|
17
|
+
|
|
18
|
+
this.memoryManager = new MemoryManager(capacity);
|
|
19
|
+
|
|
20
|
+
const pointer = this.memoryManager.get_pointer();
|
|
21
|
+
const length = this.memoryManager.get_length();
|
|
22
|
+
this.sharedArray = new Float32Array(wasm.memory.buffer, pointer, length);
|
|
23
|
+
|
|
24
|
+
this.isInitialized = true;
|
|
25
|
+
console.log(`[WasmSynchronizer] Initialized with capacity: ${capacity} floats (${capacity * 4} bytes)`);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error("[WasmSynchronizer] Failed to initialize Wasm:", error);
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getSharedArray(): Float32Array {
|
|
33
|
+
if (!this.isInitialized || !this.sharedArray) {
|
|
34
|
+
throw new Error("WasmSynchronizer is not initialized yet. Call initialize() first.");
|
|
35
|
+
}
|
|
36
|
+
return this.sharedArray;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
updatePhysics(nodeCount: number) {
|
|
40
|
+
if (this.isInitialized && this.memoryManager) {
|
|
41
|
+
this.memoryManager.update_physics(nodeCount);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|