lecodes-cli 1.0.0 → 2.0.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/dist/index.js +6211 -3649
- package/package.json +12 -8
- package/runtime/materials/lightmap.mat +115 -139
- package/runtime/materials/particles-quad.mat +6 -2
- package/runtime/materials/terrain-lightmap.mat +123 -0
- package/runtime/materials/terrain.mat +36 -55
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/VERSION.json +5 -0
- package/runtime/web/assets/camera-5XOM8eHl.js +1 -0
- package/runtime/web/assets/creator-2d-D9WKYM6h.js +1 -0
- package/runtime/web/assets/creator-2d-ukvEmNEp.wasm +0 -0
- package/runtime/web/assets/creator-full-BBXzQlhq.js +1 -0
- package/runtime/web/assets/creator-full-D2dSDdDj.wasm +0 -0
- package/runtime/web/assets/creator-full.js-BYswGsqR.symbols +8124 -0
- package/runtime/web/assets/creator-ui-DIc92ion.wasm +0 -0
- package/runtime/web/assets/creator-ui-_1Z_AX2M.js +1 -0
- package/runtime/web/assets/geolocation-BjTKN9_P.js +1 -0
- package/runtime/web/assets/inspect-C7x78PNs.js +2 -0
- package/runtime/web/assets/map-CyBxOSV0.js +1 -0
- package/runtime/web/assets/map-XzoOFt0E.js +2 -0
- package/runtime/web/assets/mapImpl-bxvQ2h1Z.js +823 -0
- package/runtime/web/assets/maplibre-gl-worker-B7LXXUkR.js +8 -0
- package/runtime/web/assets/materials-uri4FvPE.bin +0 -0
- package/runtime/web/assets/materials-world-CQWt8TAm.bin +0 -0
- package/runtime/web/assets/qr-scanner-CK_cYoJR.js +1 -0
- package/runtime/web/assets/uberarchive-DLH-kBhM.bin +0 -0
- package/runtime/web/index.html +36 -0
- package/runtime/web/player.js +21 -0
- package/runtime/web-page/index.html +41 -0
- package/runtime/web-page/page.css +1 -0
- package/runtime/web-page/page.js +31 -0
- package/src/cli/help.ts +25 -0
- package/src/cli/index.ts +7 -7
- package/src/commands/app/android.ts +3 -1
- package/src/commands/app/desktop.ts +1 -1
- package/src/commands/app/desktopMac.ts +10 -9
- package/src/commands/app/icon.ts +9 -6
- package/src/commands/app/index.ts +3 -1
- package/src/commands/app/shared.ts +13 -5
- package/src/commands/app/templates/android.ts +10 -11
- package/src/commands/app/templates/ios.ts +1 -1
- package/src/commands/assets.ts +1 -1
- package/src/commands/compile.ts +15 -2
- package/src/commands/design/index.ts +4 -4
- package/src/commands/design/snapshot.ts +3 -3
- package/src/commands/desktop.ts +5 -1
- package/src/commands/dev.ts +82 -29
- package/src/commands/index.ts +64 -60
- package/src/commands/init/index.ts +11 -15
- package/src/commands/init/templates.ts +64 -21
- package/src/commands/lightmap.ts +459 -200
- package/src/commands/navmesh.ts +2 -2
- package/src/commands/plugin.ts +46 -0
- package/src/commands/render.ts +22 -16
- package/src/commands/scene.ts +6 -1
- package/src/commands/serve.ts +58 -0
- package/src/commands/shaders/new.ts +1 -1
- package/src/commands/test.ts +402 -398
- package/src/commands/update/index.ts +3 -3
- package/src/compile/designCompile.ts +1 -1
- package/src/compile/headlessBundle.ts +133 -133
- package/src/compile/nativeStack.ts +1 -1
- package/src/compile/projectCompile.ts +10 -2
- package/src/compile/sceneCompile.ts +8 -22
- package/src/declarations/lecodes-3d-editor.d.ts +4 -0
- package/src/declarations/lecodes-assets.d.ts +1 -1
- package/src/declarations/{lecodes-renderer.d.ts → lecodes-headless.d.ts} +3 -3
- package/src/dev/clientTemplates.ts +18 -14
- package/src/dev/devServer.ts +75 -38
- package/src/dev/iosDev.ts +180 -0
- package/src/dev/webRunner.ts +82 -14
- package/src/hosts/cmgenTool.ts +1 -1
- package/src/hosts/desktopRenderer.ts +98 -27
- package/src/hosts/desktopScript.ts +150 -35
- package/src/hosts/distRoot.ts +1 -1
- package/src/hosts/matcTool.ts +1 -1
- package/src/hosts/peerInstall.ts +5 -5
- package/src/hosts/peers.ts +2 -2
- package/src/index.ts +4 -1
- package/src/plugin/contract.ts +438 -0
- package/src/plugin/emitKotlin.ts +357 -0
- package/src/plugin/emitSdk.ts +259 -0
- package/src/plugin/emitSwift.ts +336 -0
- package/src/plugin/emitWeb.ts +383 -0
- package/src/plugin/generate.ts +157 -0
- package/src/plugin/lines.ts +28 -0
- package/src/plugin/lower.ts +231 -0
- package/src/plugin/manifest.ts +192 -0
- package/src/project/ignore.ts +164 -163
- package/runtime/materials/lightmap-baked-lite.mat +0 -180
- package/runtime/materials/lightmap-baked.mat +0 -181
- package/runtime/scene-harness.json +0 -1
- package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +0 -1
- package/runtime/web/assets/basis-C64VHDVD.js +0 -1
- package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
- package/runtime/web/assets/createViewerLite-DHqTFhWB.js +0 -866
- package/runtime/web/assets/draco-BiISTFcR.js +0 -118
- package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
- package/runtime/web/assets/index-Q06c6oHx.js +0 -2
- package/runtime/web/assets/mapViewImpl-B2JcES8l.js +0 -810
- package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +0 -8
- package/runtime/web/assets/worker-Caf-yYEI.js +0 -2
- package/runtime/web/embed.html +0 -32
- package/runtime/web/embed.js +0 -171
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
// lightmap-baked: the lightmap material's CHEAP twin (SDK `Material.lightmapShading = "baked"`, a
|
|
2
|
-
// graphics-quality tier). Same parameters, same atlas / volume reads, the same picture on a level whose
|
|
3
|
-
// light is baked — but an UNLIT shading model with the lighting written out by hand, so the fragment
|
|
4
|
-
// skips filament's lit path (prefiltered-cubemap IBL specular + DFG LUT, the froxel loop over dynamic
|
|
5
|
-
// lights, sun shadows, energy compensation, multi-bounce AO).
|
|
6
|
-
//
|
|
7
|
-
// Measured on fps-demo's shooting range (2146 lightmapped props, 1280x720, integrated GPU): the lit
|
|
8
|
-
// material renders at ~62 fps, this one at ~91 — the same +45 % a no-shading stub gave, so the lit
|
|
9
|
-
// path was the cost. Filtering the light variants out of the lit material (variantFilter) gave only
|
|
10
|
-
// +10 %: the IBL evaluation is the expensive half, not the light loops. Mean frame brightness matches
|
|
11
|
-
// the lit material within 1 %.
|
|
12
|
-
//
|
|
13
|
-
// What it drops, deliberately:
|
|
14
|
-
// - real-time point lights: a muzzle flash / fireball no longer lights the level (the baked lamps
|
|
15
|
-
// still do — they come from the atlas / volume, not the light list);
|
|
16
|
-
// - the sun is unshadowed Lambert × the atlas' sun mask (R) — on a DYNAMIC object no shadows;
|
|
17
|
-
// - IBL specular is approximated: the SH irradiance at the reflected direction × an analytic DFG
|
|
18
|
-
// (Karis' EnvBRDFApprox) — right for rough surfaces, a blurred reflection on mirrors.
|
|
19
|
-
// Two traps, learnt the hard way (probes in the 2026-09-07 session):
|
|
20
|
-
// - unlit has NO vertex tangent frame (MATERIAL_NEEDS_TBN is lit-only): getWorldTangentFrame() returns
|
|
21
|
-
// garbage, NoV collapses to 0 and the specular lobe explodes to white. The frame is built here from
|
|
22
|
-
// screen-space derivatives of position and uv0 (Schueler's cotangent frame);
|
|
23
|
-
// - the SH coefficients are NOT pre-exposed (the CPU stores them raw); filament multiplies its Fd/Fr by
|
|
24
|
-
// iblLuminance in the shader, so this one does too.
|
|
25
|
-
// Keep the parameter list IDENTICAL to lightmap.mat: creator-gl's lightmapSetOptions / bindVolume set
|
|
26
|
-
// every one of them on every lightmap-material instance, and an undeclared uniform is a panic.
|
|
27
|
-
|
|
28
|
-
material {
|
|
29
|
-
name : lightmap_baked,
|
|
30
|
-
shadingModel : unlit,
|
|
31
|
-
doubleSided : true,
|
|
32
|
-
// opaque or masked: matc -TBLENDING (materials.json `variants`) — the masked twin is what the
|
|
33
|
-
// lightmap provider hands a glTF MASK material (foliage cards), same shader otherwise
|
|
34
|
-
blending : ${BLENDING},
|
|
35
|
-
flipUV : false,
|
|
36
|
-
requires : [ uv0, uv1, tangents ],
|
|
37
|
-
parameters : [
|
|
38
|
-
{ type : float4, name : baseColorFactor },
|
|
39
|
-
{ type : sampler2d, name : baseColorMap },
|
|
40
|
-
{ type : float, name : metallicFactor },
|
|
41
|
-
{ type : float, name : roughnessFactor },
|
|
42
|
-
{ type : float3, name : emissiveFactor },
|
|
43
|
-
{ type : float, name : normalScale },
|
|
44
|
-
{ type : float, name : aoStrength },
|
|
45
|
-
{ type : sampler2d, name : normalMap },
|
|
46
|
-
{ type : float, name : hasNormalMap },
|
|
47
|
-
{ type : sampler2d, name : metallicRoughnessMap },
|
|
48
|
-
{ type : sampler2d, name : occlusionMap },
|
|
49
|
-
{ type : sampler2d, name : emissiveMap },
|
|
50
|
-
{ type : sampler2d, name : lightmap },
|
|
51
|
-
{ type : sampler2d, name : lightmapLight },
|
|
52
|
-
{ type : float, name : lightScale },
|
|
53
|
-
{ type : float, name : lightGamma },
|
|
54
|
-
{ type : float4, name : lightmapST },
|
|
55
|
-
{ type : float, name : ambientScale },
|
|
56
|
-
{ type : float, name : sunStrength },
|
|
57
|
-
{ type : float, name : bakedAo },
|
|
58
|
-
{ type : sampler3d, name : probeVolume },
|
|
59
|
-
{ type : sampler3d, name : probeLight },
|
|
60
|
-
{ type : float4, name : probeMin },
|
|
61
|
-
{ type : float4, name : probeInvSize }
|
|
62
|
-
]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
fragment {
|
|
66
|
-
// filament's SH irradiance (Lambert baked into the coefficients, like Irradiance_SphericalHarmonics)
|
|
67
|
-
vec3 shIrradiance(const vec3 n) {
|
|
68
|
-
return max(
|
|
69
|
-
frameUniforms.iblSH[0].xyz
|
|
70
|
-
+ frameUniforms.iblSH[1].xyz * n.y + frameUniforms.iblSH[2].xyz * n.z + frameUniforms.iblSH[3].xyz * n.x
|
|
71
|
-
+ frameUniforms.iblSH[4].xyz * (n.y * n.x) + frameUniforms.iblSH[5].xyz * (n.y * n.z)
|
|
72
|
-
+ frameUniforms.iblSH[6].xyz * (3.0 * n.z * n.z - 1.0) + frameUniforms.iblSH[7].xyz * (n.z * n.x)
|
|
73
|
-
+ frameUniforms.iblSH[8].xyz * (n.x * n.x - n.y * n.y), 0.0);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Karis' analytic DFG (UE4 mobile): the split-sum's E term without the LUT
|
|
77
|
-
vec3 envBrdfApprox(const vec3 f0, float roughness, float NoV) {
|
|
78
|
-
vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
|
|
79
|
-
vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
|
|
80
|
-
vec4 r = roughness * c0 + c1;
|
|
81
|
-
float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
|
|
82
|
-
vec2 ab = vec2(-1.04, 1.04) * a004 + r.zw;
|
|
83
|
-
return f0 * ab.x + ab.y;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
void material(inout MaterialInputs material) {
|
|
87
|
-
prepareMaterial(material);
|
|
88
|
-
vec4 base = texture(materialParams_baseColorMap, getUV0()) * materialParams.baseColorFactor;
|
|
89
|
-
vec2 mr = texture(materialParams_metallicRoughnessMap, getUV0()).bg;
|
|
90
|
-
float metallic = materialParams.metallicFactor * mr.x;
|
|
91
|
-
float roughness = materialParams.roughnessFactor * mr.y;
|
|
92
|
-
vec3 glow = texture(materialParams_emissiveMap, getUV0()).rgb * materialParams.emissiveFactor;
|
|
93
|
-
float occ = 1.0 + materialParams.aoStrength * (texture(materialParams_occlusionMap, getUV0()).r - 1.0);
|
|
94
|
-
vec3 n = getWorldGeometricNormalVector();
|
|
95
|
-
vec3 ns = n;
|
|
96
|
-
if (materialParams.hasNormalMap > 0.5) {
|
|
97
|
-
vec3 tn = texture(materialParams_normalMap, getUV0()).xyz * 2.0 - 1.0;
|
|
98
|
-
tn.xy *= materialParams.normalScale;
|
|
99
|
-
// unlit has no vertex tangent frame (MATERIAL_NEEDS_TBN is lit-only): build one from the
|
|
100
|
-
// screen-space derivatives of position and uv0 (Schueler's cotangent frame)
|
|
101
|
-
highp vec3 p = getWorldPosition();
|
|
102
|
-
highp vec2 uv = getUV0();
|
|
103
|
-
highp vec3 dp1 = dFdx(p), dp2 = dFdy(p);
|
|
104
|
-
highp vec2 duv1 = dFdx(uv), duv2 = dFdy(uv);
|
|
105
|
-
highp vec3 dp2perp = cross(dp2, n), dp1perp = cross(n, dp1);
|
|
106
|
-
highp vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
|
|
107
|
-
highp vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
|
|
108
|
-
highp float invmax = inversesqrt(max(dot(T, T), dot(B, B)));
|
|
109
|
-
ns = normalize(mat3(T * invmax, B * invmax, n) * tn);
|
|
110
|
-
}
|
|
111
|
-
vec3 v = getWorldViewVector();
|
|
112
|
-
float NoV = max(dot(ns, v), 1e-4);
|
|
113
|
-
vec3 luma = vec3(0.2126, 0.7152, 0.0722);
|
|
114
|
-
float lg = materialParams.lightGamma > 0.0 ? materialParams.lightGamma : 1.0;
|
|
115
|
-
|
|
116
|
-
// the baked lamps: the volume for a dynamic object, the atlas for a static — as lightmap.mat
|
|
117
|
-
vec2 lm;
|
|
118
|
-
vec3 baked;
|
|
119
|
-
vec3 l;
|
|
120
|
-
if (materialParams.probeInvSize.w > 0.5) {
|
|
121
|
-
vec3 p = getUserWorldPosition() + n * materialParams.probeMin.w;
|
|
122
|
-
vec3 uvw = (p - materialParams.probeMin.xyz) * materialParams.probeInvSize.xyz;
|
|
123
|
-
lm = texture(materialParams_probeVolume, uvw).rg;
|
|
124
|
-
float nz = materialParams.probeInvSize.w;
|
|
125
|
-
float wz = clamp(uvw.z, 0.5 / nz, 1.0 - 0.5 / nz);
|
|
126
|
-
vec3 fx = vec3(uvw.xy, ((ns.x >= 0.0 ? 0.0 : 1.0) + wz) / 6.0);
|
|
127
|
-
vec3 fy = vec3(uvw.xy, ((ns.y >= 0.0 ? 2.0 : 3.0) + wz) / 6.0);
|
|
128
|
-
vec3 fz = vec3(uvw.xy, ((ns.z >= 0.0 ? 4.0 : 5.0) + wz) / 6.0);
|
|
129
|
-
vec3 ex = pow(texture(materialParams_probeLight, fx).rgb, vec3(lg));
|
|
130
|
-
vec3 ey = pow(texture(materialParams_probeLight, fy).rgb, vec3(lg));
|
|
131
|
-
vec3 ez = pow(texture(materialParams_probeLight, fz).rgb, vec3(lg));
|
|
132
|
-
vec3 w2 = ns * ns;
|
|
133
|
-
baked = (w2.x * ex + w2.y * ey + w2.z * ez) * materialParams.lightScale;
|
|
134
|
-
vec3 dirE = vec3(sign(ns.x) * dot(ex, luma), sign(ns.y) * dot(ey, luma), sign(ns.z) * dot(ez, luma));
|
|
135
|
-
l = dot(dirE, dirE) > 1e-12 ? normalize(dirE) : ns;
|
|
136
|
-
} else {
|
|
137
|
-
vec2 luv = getUV1() * materialParams.lightmapST.xy + materialParams.lightmapST.zw;
|
|
138
|
-
lm = texture(materialParams_lightmap, luv).rg;
|
|
139
|
-
baked = pow(texture(materialParams_lightmapLight, luv).rgb, vec3(lg)) * materialParams.lightScale;
|
|
140
|
-
l = ns;
|
|
141
|
-
}
|
|
142
|
-
lm.g = mix(1.0, lm.g, materialParams.bakedAo);
|
|
143
|
-
float ao = lm.g * occ;
|
|
144
|
-
float vis = mix(1.0, lm.r, materialParams.sunStrength);
|
|
145
|
-
|
|
146
|
-
vec3 diffuseColor = base.rgb * (1.0 - metallic);
|
|
147
|
-
vec3 f0 = mix(vec3(0.04), base.rgb, metallic);
|
|
148
|
-
vec3 E = envBrdfApprox(f0, roughness, NoV);
|
|
149
|
-
|
|
150
|
-
vec3 ambD = diffuseColor * shIrradiance(ns) * (1.0 - E) * ao * frameUniforms.iblLuminance;
|
|
151
|
-
vec3 ambS = E * shIrradiance(reflect(-v, ns)) * ao * frameUniforms.iblLuminance;
|
|
152
|
-
vec3 color = (ambD + ambS) * materialParams.ambientScale;
|
|
153
|
-
|
|
154
|
-
// the sun (a level with one): Lambert × the atlas' visibility mask; pre-exposed like filament's own
|
|
155
|
-
float sunW = frameUniforms.lightColorIntensity.w;
|
|
156
|
-
if (sunW > 0.0) {
|
|
157
|
-
float NoL = max(dot(ns, frameUniforms.lightDirection), 0.0);
|
|
158
|
-
color += diffuseColor * frameUniforms.lightColorIntensity.rgb * (sunW * NoL * vis / PI);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// the baked lamps: diffuse + the cheap specular lobe lightmap.mat also uses (physical units × exposure)
|
|
162
|
-
{
|
|
163
|
-
vec3 h = normalize(v + l);
|
|
164
|
-
float NoL = max(dot(ns, l), 0.0);
|
|
165
|
-
float NoH = max(dot(ns, h), 0.0);
|
|
166
|
-
float LoH = max(dot(l, h), 0.0);
|
|
167
|
-
float pr = max(roughness, 0.5);
|
|
168
|
-
float a2 = pr * pr;
|
|
169
|
-
a2 *= a2;
|
|
170
|
-
float dd = NoH * NoH * (a2 - 1.0) + 1.0;
|
|
171
|
-
float D = a2 / (PI * dd * dd);
|
|
172
|
-
float V = 0.5 / max(NoL * sqrt(NoV * NoV * (1.0 - a2) + a2) + NoV * sqrt(NoL * NoL * (1.0 - a2) + a2), 1e-4);
|
|
173
|
-
vec3 F = f0 + (1.0 - f0) * pow(1.0 - LoH, 5.0);
|
|
174
|
-
color += (diffuseColor / PI + D * V * F) * baked * getExposure();
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// the texture's alpha rides along: the masked twin (-TBLENDING=masked) cuts foliage cards on it,
|
|
178
|
-
// the opaque one ignores it (an unlit opaque material writes no alpha)
|
|
179
|
-
material.baseColor = vec4(color + glow, base.a);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"main.ts":"// The 3D scene-editor harness — a LeCodes app the editor host runs alongside a user scene bundle\n// (compiled with the modern SDK; replaces the legacy worker-SDK scene-viewer). The host:\n//\n// 1. runs the user's scene bundle with `__lecodesSceneEdit` set → the scene handle registers on\n// `globalThis.__lecodesScenes` (sources real, aspects inert — see sdk docs/3d/scene-files.md);\n// 2. runs this bundle → it installs `globalThis.__lecodesSceneHarness` (the controller);\n// 3. calls `controller.attach(handle)` and wires `controller.callbacks`.\n//\n// The viewport DISPLAY layer (grid, selection outline, transform gizmo, viewport picking) lives in\n// the ENGINE now — viewer-lite's editor layer (`_creator._editorConnect`), lite viewport only; the\n// filament preview renders the bare scene. This harness stays the POLICY half: the host feeds it\n// engine picks (`pickName` — locked nodes, GLB part drill-down) and completed gizmo drags\n// (`applyPose` — applied live, transform read back for the file), and `entityOf` hands the host\n// the entity id behind a selection key. Orbit is any drag; zoom/pan/focus come from the host\n// (DOM wheel/keys belong to the Vue viewport). Plugin viewport tools keep working: an active tool\n// receives clicks as raycast hits (physics bodies when the build has them, ground plane otherwise).\n\nimport { OrbitCamera } from \"./orbit\"\nimport { installTerrainTool } from \"./terrain\"\n\n// The low-level bridge global every bundle runs against — the harness only walks the entity\n// hierarchy with it (wrapping foreign entity ids in `new Node(id)` would OVERWRITE the real nodes\n// in the SDK's registry, so id-level walking is the safe form). `computeBoundingBox` (subtree AABB\n// in the entity's local space) is a lite-engine host API — absent on filament.\ndeclare const _creator: {\n getParent(entityId: number): number\n computeBoundingBox?(entityId: number): Float32Array\n getWorldMatrix(entityId: number, mat: Float32Array): void\n setMaterial(entityId: number, materialId: number, index: number): void\n getMaterial(entityId: number, index: number): number\n}\n\ntype SceneHandleLike = {\n /** `nodes` is keyed by ABSOLUTE PATH ('/'-joined def keys) — the SDK owns this record; the\n * harness holds the same object and must never re-key it itself (see renameNode). */\n load(): Promise<{ scene: Scene, nodes: Record<string, Node> }>\n /** SceneHandle._describeAspects — aspect field schemas for the inspector (plain data). */\n _describeAspects?(): unknown[]\n /** SceneHandle._patchNode — live single-node rebuild/add/remove; the path carries the mount\n * point (its parent must exist for adds). */\n _patchNode?(path: string, def: unknown): Promise<Node | null>\n /** SceneHandle._renameNode — re-keys the node record (subtree included) + the engine name;\n * returns the new path, null on refusal. */\n _renameNode?(path: string, newName: string): string | null\n /** SceneHandle._reparentNode — re-keys + setParent keeping the local transform. */\n _reparentNode?(path: string, newParentPath: string | null): string | null\n /** SceneHandle._modelParts — a model node's INTERNAL rows (the `overrides` path grammar). */\n _modelParts?(path: string): Promise<{ path: string, name: string, depth: number, node: Node }[]>\n /** SceneHandle._editorNodeChanged — rebuild editor-run aspects whose ref() deps cover `path`. */\n _editorNodeChanged?(path: string): void\n /** SceneHandle._editorGizmos — every editor run's `Gizmos` lines + a change version. */\n _editorGizmos?(): { version: number, batches: { color: string, alpha: number, segments: number[] }[] }\n /** SceneHandle._editorSetProp — live prop edit on one editor-run aspect (rebuilds it). */\n _editorSetProp?(hostPath: string, index: number, key: string, value: unknown): boolean\n /** SceneHandle._editorSetMakeArg — live arg edit on a `make()` node (re-calls the factory). */\n _editorSetMakeArg?(path: string, key: string, value: unknown): boolean\n /** SceneHandle._inspectorRender — run an aspect's custom `static inspector` card. */\n _inspectorRender?(\n hostPath: string, index: number,\n props: Record<string, unknown>, event?: { id: string, value?: unknown },\n ): unknown[] | null\n}\n\ntype Vec3Tuple = [number, number, number]\n/** The SDK Shape one-of, as the host names it (see fitShape / SceneHarness). */\ntype ShapeKindName = \"auto\" | \"box\" | \"sphere\" | \"cylinder\" | \"capsule\" | \"mesh\" | \"convex\"\n\ntype HarnessCallbacks = {\n /** Selection changed from INSIDE the world (a plugin's `editor.select`) — mirror host-side. */\n onSelect?(name: string | null): void\n}\n\n/** Document operations the HOST implements (its commit/undo/patch machinery) — the doc-op half of\n * the `editor` API handed to plugins (windows/tools). Installed via `controller.setEditorOps`. */\ntype EditorOps = {\n nodes(): { path: string, name: string, kind: string }[]\n uniqueName(base: string, parentPath?: string): string\n addNode(name: string, def: Record<string, unknown>): boolean\n setProp(path: string, key: string, value: unknown): boolean\n removeNode(path: string): void\n duplicate(path: string): string | null\n transact(fn: () => void): void\n /** One def prop as document data (`{ $asset }` refs kept). Absent on older hosts. */\n getProp?(path: string, key: string): unknown\n /** Binary sidecar write (see sdk editorPlugins.ts `EditorApi.writeAsset`). Absent on older hosts. */\n writeAsset?(path: string, ref: string | null, name: string, bytes: Uint8Array): Promise<string | null>\n}\n\nconst state = {\n handle: null as SceneHandleLike | null,\n scene: null as Scene | null,\n /** The SDK's path-keyed node record — the SAME object the handle owns (rename/reparent re-key\n * it SDK-side; the harness re-keys only its own part/selection state). */\n nodes: {} as Record<string, Node>,\n /** Reverse map: live node → absolute def path. */\n paths: new Map<Node, string>(),\n /** GLB internal nodes, addressable like nodes: key = `<defPath>::<partPath>` (+ reverse map). */\n parts: new Map<string, Node>(),\n partNames: new Map<Node, string>(),\n /** Def path → its part keys, so re-enumerating ONE node touches only its own rows (a scan of\n * every part key per node is quadratic — 9 k prefab instances × 60 k parts took half a minute). */\n partKeys: new Map<string, string[]>(),\n orbit: null as OrbitCamera | null,\n /** Selection key: absolute def path or `<defPath>::<partPath>`. */\n selected: null as string | null,\n /** The active viewport tool (`registerEditorTool` name) — clicks route to it. */\n activeTool: null as string | null,\n /** Host-implemented doc operations (the `editor` API's write half). */\n editorOps: null as EditorOps | null,\n}\n\n// ---- part keys ---------------------------------------------------------------------------------\n// A model's internal nodes are selectable/editable through PART KEYS: `<defPath>::<partPath>` (the\n// part-path grammar is the SDK's — SceneHandle._modelParts enumerates it, `overrides` keys store\n// it; names exclude ':', so the first `::` is always the boundary). Everything key-addressed\n// (select / setNodeProp / focus) resolves through `resolveKey`, so a part behaves like a node —\n// except its persistence: the editor writes the transform into the MODEL's `overrides` record.\n\nconst PART_SEP = \"::\"\n\n/** Re-key `key` after `oldPath` moved to `newPath` (the path itself, `/` descendants, and `::`\n * part keys of either). Null = untouched. Mirrors scene-editor/types.ts `rekeyPath` — the\n * harness compiles standalone and cannot import it. */\nconst rekeyPath = (key: string, oldPath: string, newPath: string): string | null => {\n if (key === oldPath) return newPath\n if (key.startsWith(oldPath + \"/\") || key.startsWith(oldPath + PART_SEP)) {\n return newPath + key.slice(oldPath.length)\n }\n return null\n}\n\nconst resolveKey = (key: string): Node | null => state.nodes[key] ?? state.parts.get(key) ?? null\n\n// ---- materials ---------------------------------------------------------------------------------\n// Live material edits (docs/scene-materials-plan.md §4): a parameter lands on the LIVE material of\n// a mesh node / a GLB-part slot / a material asset with `.set()` — the value tier, no rebuild; a\n// shader or kind switch reloads ONE material. Doc defs reach the harness as plain data in which a\n// material asset is `{ $material: \"./x.material.ts\" }` — lowered here to the handle the compile's\n// virtual entry registered on `__lecodesMaterials`.\n\ntype MaterialTarget = { node: string } | { part: string, slot?: number } | { asset: string }\n\n/** The registry captured at attach — a second bundle run in the same page (the preview host)\n * overwrites the global with ITS handles, so each harness keeps the one that was current when it\n * attached (the host sets it right before). Falls back to the global for older hosts. */\nlet capturedMaterials: Record<string, MaterialHandle> | null = null\nconst materialHandles = (): Record<string, MaterialHandle> =>\n capturedMaterials ?? ((globalThis as any).__lecodesMaterials as Record<string, MaterialHandle> | undefined) ?? {}\n\nconst isMaterialRef = (v: unknown): v is { $material: string } =>\n typeof v === \"object\" && v !== null && typeof (v as { $material?: unknown }).$material === \"string\"\n\n/** A doc material def → runtime def (asset refs resolved to handles). Null = unknown asset path. */\nconst lowerMaterial = (def: unknown): unknown => (isMaterialRef(def) ? materialHandles()[def.$material] ?? null : def)\n\n/** A prefab reference the host lowers a `prefab: X` identifier to: the scene file's project path\n * (`{ $prefab: \"./src/map/x.scene.ts\" }`). The edit bundle registers every scene file the open\n * scene imports under `__lecodesSceneFiles` (sdk/compile sceneImportsOf) — an unknown path means\n * the import is new to this bundle and only a compile can bring it in. */\nconst isPrefabRef = (v: unknown): v is { $prefab: string } =>\n typeof v === \"object\" && v !== null && typeof (v as { $prefab?: unknown }).$prefab === \"string\"\nconst sceneFileHandles = (): Record<string, unknown> =>\n ((globalThis as { __lecodesSceneFiles?: Record<string, unknown> }).__lecodesSceneFiles) ?? {}\n\n/** Lower every material position of a node def (its own, part-override slots, children). Null\n * when a reference doesn't resolve — the host falls back to a re-run. */\nconst lowerNodeDef = (def: Record<string, unknown>): Record<string, unknown> | null => {\n const out = { ...def }\n if (isPrefabRef(out.prefab)) {\n const handle = sceneFileHandles()[out.prefab.$prefab]\n if (!handle) return null\n out.prefab = handle\n }\n if (out.material !== undefined) {\n const m = lowerMaterial(out.material)\n if (m === null) return null\n out.material = m\n }\n if (out.overrides && typeof out.overrides === \"object\") {\n const ov: Record<string, unknown> = {}\n for (const [ p, entry ] of Object.entries(out.overrides as Record<string, Record<string, unknown>>)) {\n const e = { ...entry }\n if (e.materials && typeof e.materials === \"object\") {\n const mats: Record<string, unknown> = {}\n for (const [ slot, m ] of Object.entries(e.materials as Record<string, unknown>)) {\n const lowered = lowerMaterial(m)\n if (lowered === null) return null\n mats[slot] = lowered\n }\n e.materials = mats\n }\n ov[p] = e\n }\n out.overrides = ov\n }\n if (out.children && typeof out.children === \"object\") {\n const ch: Record<string, unknown> = {}\n for (const [ n, c ] of Object.entries(out.children as Record<string, Record<string, unknown>>)) {\n const lowered = lowerNodeDef(c)\n if (lowered === null) return null\n ch[n] = lowered\n }\n out.children = ch\n }\n return out\n}\n\n/** The live Material behind a target (null = none assigned yet / unknown key). */\nconst materialAt = (target: MaterialTarget): Material | null => {\n if (\"asset\" in target) return materialHandles()[target.asset]?.material ?? null\n if (\"node\" in target) return resolveKey(target.node)?.getMaterial(0) ?? null\n return resolveKey(target.part)?.getMaterial(target.slot ?? 0) ?? null\n}\n\n/** Push one parameter into a live material. Built-in materials take `color`/`map` through their\n * accessors (they know the shader's uniform names); everything else is a `.set()`, where a\n * non-`#` string is a texture URL that loads in. */\nconst pushMaterialParam = (m: Material, name: string, value: unknown): void => {\n const builtin = (m as unknown as { _colorKey: string | null })._colorKey !== null\n if (builtin && name === \"color\") { m.color = value as string; return }\n if (builtin && name === \"map\") {\n if (typeof value === \"string\") void Texture.load(value).then((t) => { m.map = t })\n else m.map = null\n return\n }\n void __applyMaterialParam(m, name, value as never)\n}\n\n/** The original (glTF) material of every part slot overridden live, so clearing restores it. */\nconst originalSlotMaterials = new Map<string, number>()\n\n/** (Re-)enumerate one model's internal nodes into the part maps; returns plain rows for the host. */\nconst refreshModelParts = async (path: string): Promise<{ path: string, name: string, depth: number }[]> => {\n const handle = state.handle\n if (!handle?._modelParts) return []\n const rows = await handle._modelParts(path)\n for (const key of state.partKeys.get(path) ?? []) {\n const node = state.parts.get(key)\n state.parts.delete(key)\n if (node) state.partNames.delete(node)\n }\n const keys: string[] = []\n for (const r of rows) {\n const key = path + PART_SEP + r.path\n state.parts.set(key, r.node)\n state.partNames.set(r.node, key)\n keys.push(key)\n }\n if (keys.length > 0) state.partKeys.set(path, keys)\n else state.partKeys.delete(path)\n return rows.map((r) => ({ path: r.path, name: r.name, depth: r.depth }))\n}\n\n/** Editor-tool gizmo lines (a tool's hooks draw through `Gizmos` too) — one buffer for the active\n * tool, cleared on every hook call and on tool switch. */\nconst toolGizmos = new __GizmoBuffer()\nlet toolGizmosVersion = 0\n\n/** A node (or a part inside a model) changed — let the scene's editor-run aspects (generators)\n * that reference it via ref() rebuild. Part keys collapse to their model's def path. */\nconst notifyEditorChanged = (key: string | null): void => {\n if (!key) return\n const i = key.indexOf(PART_SEP)\n state.handle?._editorNodeChanged?.(i < 0 ? key : key.slice(0, i))\n}\n\n/** After the SDK re-keyed the shared node record (rename/reparent), re-key the HARNESS-owned\n * state that mirrors those paths: the reverse map, part keys, node-card cache, selection. */\nconst rekeyLocal = (oldPath: string, newPath: string): void => {\n state.paths = new Map(Object.entries(state.nodes).map(([ p, n ]) => [ n, p ]))\n for (const [ key, part ] of [ ...state.parts ]) {\n const next = rekeyPath(key, oldPath, newPath)\n if (next === null) continue\n state.parts.delete(key)\n state.parts.set(next, part)\n state.partNames.set(part, next)\n }\n for (const [ path, keys ] of [ ...state.partKeys ]) {\n const next = rekeyPath(path, oldPath, newPath)\n if (next === null) continue\n state.partKeys.delete(path)\n state.partKeys.set(next, keys.map((k) => rekeyPath(k, oldPath, newPath) ?? k))\n }\n for (const [ key, ui ] of [ ...nodeCards ]) {\n const next = rekeyPath(key, oldPath, newPath)\n if (next === null) continue\n nodeCards.delete(key)\n nodeCards.set(next, ui)\n }\n if (state.selected) state.selected = rekeyPath(state.selected, oldPath, newPath) ?? state.selected\n}\n\nconst isEditorNode = (node: Node): boolean => (node.name ?? \"\").startsWith(\"__editor\")\n\n// ---- built-in node cards (immediate-mode inspector protocol — sdk core/InspectorUI.ts) ----------\n// The ANIMATION card on model nodes with baked clips: clip dropdown (live from model.anim.clips),\n// speed/loop, Play/Stop preview. Pure editor state — nothing here writes to the scene file\n// (it's a preview; play-mode behavior belongs in aspects/code). ModelAnimation is attached by the\n// Model constructor itself, so it is live even in edit mode.\n\nconst nodeCards = new Map<string, InspectorUI>()\n\nconst renderNodeCard = (name: string, event?: { id: string, value?: unknown }): unknown[] | null => {\n const node = state.nodes[name]\n if (!(node instanceof Model)) return null\n const clips = node.anim.clips\n if (clips.length === 0) return null\n let ui = nodeCards.get(name)\n if (!ui) { ui = new InspectorUI(); nodeCards.set(name, ui) }\n return ui._run((u) => {\n u.header(\"Animation\")\n const clip = String(u.select(\"clip\", clips.map((c) => c.name)))\n const speed = u.number(\"speed\", { min: 0.1, max: 4, step: 0.1, value: 1 })\n const loop = u.switch(\"loop\", { value: true })\n const playing = node.anim.busy || node.anim.loop !== undefined\n if (playing) node.anim.speed = speed\n if (u.button(playing ? \"Restart\" : \"Play\")) {\n node.anim.speed = speed\n if (loop) node.anim.playLoop(clip)\n else node.anim.play(clip, { restart: true })\n }\n if (u.button(\"Stop\")) node.anim.stop() // fades to the rest pose\n const duration = clips.find((c) => c.name === clip)?.duration\n u.info(playing ? \"playing…\" : duration !== undefined ? `${duration.toFixed(2)}s` : \"\")\n }, event)\n}\n\n/** The def path a raycast-hit entity belongs to (a GLB hit resolves to its Model, etc.). */\nconst ownerPath = (node: Node | null): string | null => {\n let cur: Node | null = node\n for (let i = 0; cur && i < 32; i++) {\n const path = state.paths.get(cur)\n if (path) return path\n cur = cur.parent\n }\n return null\n}\n\nconst applySelection = (name: string | null, notify: boolean): void => {\n state.selected = name\n if (notify) controller.callbacks.onSelect?.(name)\n}\n\n// ---- editor plugins (windows + viewport tools — sdk scene/editorPlugins.ts) ---------------------\n// `*.editor.ts` files register into the injected `__editorPlugins` registry (same bundle → same\n// module instance). Windows render through the immediate-mode InspectorUI protocol like inspector\n// cards; tools receive viewport clicks as raycast hits. Both get the `editor` scripting API:\n// selection/raycast are answered here, doc writes delegate to the host's EditorOps (plugins write\n// the DOCUMENT, never live state — one undo stack for humans and plugins alike).\n\nconst isInSubtree = (node: Node, root: Node): boolean => {\n let cur: Node | null = node\n for (let i = 0; cur && i < 64; i++) {\n if (cur === root) return true\n cur = cur.parent\n }\n return false\n}\n\n/** Raycast under a viewport pixel: precise mesh hit via Jolt bodies when the build/scene has any\n * (edit mode attaches none itself — aspects are inert), else / on miss the ground plane.\n * `exclude` steps the ray past any hit inside that subtree (editor nodes are always stepped). */\nconst raycastViewport = (x: number, y: number, exclude?: Node | null): { point: Vec3Tuple, normal: Vec3Tuple, node: string | null } | null => {\n const scene = state.scene\n if (!scene) return null\n const ray = scene.camera.getRay(x, y)\n if (Physics.supported) {\n let origin: Vec3Tuple = [ ray.origin.x, ray.origin.y, ray.origin.z ]\n let remaining = 2000\n for (let i = 0; i < 8 && remaining > 0; i++) {\n const hit = Physics.raycast(origin, ray.dir, remaining)\n if (!hit) break\n // (empties/cameras have no geometry — their markers are overlay gizmos, never hit here)\n if (hit.node && !isEditorNode(hit.node)\n && !(exclude && isInSubtree(hit.node, exclude))) {\n return {\n point: [ hit.point.x, hit.point.y, hit.point.z ],\n normal: [ hit.normal.x, hit.normal.y, hit.normal.z ],\n node: hit.node ? ownerPath(hit.node) : null,\n }\n }\n // an excluded/editor body — resume the cast just past it\n const step = remaining * hit.fraction + 0.01\n origin = [ origin[0] + ray.dir.x * step, origin[1] + ray.dir.y * step, origin[2] + ray.dir.z * step ]\n remaining -= step\n }\n }\n // terrains: exact against the drawn triangles (edit mode has no height-field body) — nearest wins\n let best: { point: Vec3Tuple, normal: Vec3Tuple, node: string | null } | null = null\n let bestDist = Infinity\n for (const [ path, node ] of Object.entries(state.nodes)) {\n const terrain = Terrain.of(node)\n if (!terrain || (exclude && isInSubtree(node, exclude))) continue\n const hit = terrain.raycastWorld(ray.origin, ray.dir, 1e5)\n if (hit && hit.distance < bestDist) {\n bestDist = hit.distance\n best = { point: [ hit.point.x, hit.point.y, hit.point.z ], normal: [ hit.normal.x, hit.normal.y, hit.normal.z ], node: path }\n }\n }\n if (best) return best\n const t = -ray.origin.y / ray.dir.y\n if (!Number.isFinite(t) || t <= 0) return null\n const p = ray.getPoint(t)\n return { point: [ p.x, p.y, p.z ], normal: [ 0, 1, 0 ], node: null }\n}\n\nconst editorApi = {\n get selection(): string | null { return state.selected },\n select(name: string | null): void { applySelection(name && resolveKey(name) ? name : null, true) },\n nodes: (): { name: string, kind: string }[] => state.editorOps?.nodes() ?? [],\n uniqueName: (base: string): string => state.editorOps?.uniqueName(base) ?? base,\n addNode: (name: string, def: Record<string, unknown>): boolean => state.editorOps?.addNode(name, def) === true,\n setProp: (name: string, key: string, value: unknown): boolean => state.editorOps?.setProp(name, key, value) === true,\n removeNode: (name: string): void => { state.editorOps?.removeNode(name) },\n duplicate: (name: string): string | null => state.editorOps?.duplicate(name) ?? null,\n raycast: raycastViewport,\n transact: (fn: () => void): void => { state.editorOps ? state.editorOps.transact(fn) : fn() },\n getProp: (name: string, key: string): unknown => state.editorOps?.getProp?.(name, key),\n writeAsset: (name: string, ref: string | null, fileName: string, bytes: Uint8Array): Promise<string | null> =>\n state.editorOps?.writeAsset ? state.editorOps.writeAsset(name, ref, fileName, bytes) : Promise.resolve(null),\n}\n\n/** Per-window InspectorUI instances (all their field keys are transient editor state). */\nconst windowUIs = new Map<number, InspectorUI>()\n\n/** A node's subtree AABB in WORLD space (the engine's local box through the node's own world\n * matrix), null while degenerate / unsupported. */\nconst worldBounds = (node: Node): { min: Vec3Tuple, max: Vec3Tuple } | null => {\n const box = _creator.computeBoundingBox?.(node.id)\n if (!box) return null\n if (!(box[3] - box[0] > 1e-6 || box[4] - box[1] > 1e-6 || box[5] - box[2] > 1e-6)) return null\n // world AABB from the 8 local-space corners (the local box doesn't include the node's own\n // transform — worldMatrix carries it)\n const m = node.worldMatrix\n const min: Vec3Tuple = [ Infinity, Infinity, Infinity ]\n const max: Vec3Tuple = [ -Infinity, -Infinity, -Infinity ]\n for (let i = 0; i < 8; i++) {\n const p = m.transformPoint([ box[i & 1 ? 3 : 0], box[i & 2 ? 4 : 1], box[i & 4 ? 5 : 2] ])\n const c = [ p.x, p.y, p.z ]\n for (let k = 0; k < 3; k++) {\n if (c[k] < min[k]) min[k] = c[k]\n if (c[k] > max[k]) max[k] = c[k]\n }\n }\n return { min, max }\n}\n\n/** Orbit onto a world AABB: target = center, distance from the largest extent. */\nconst frameBounds = (min: Vec3Tuple, max: Vec3Tuple) => {\n const size = Math.max(max[0] - min[0], max[1] - min[1], max[2] - min[2])\n state.orbit?.focus(\n [ (min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2 ],\n Math.max(1, size * 1.4),\n )\n}\n\nconst controller = {\n callbacks: {} as HarnessCallbacks,\n\n /** Attach to a user scene handle (from `globalThis.__lecodesScenes`). Returns the node PATHS\n * (build-completion order, not file order — the host sorts for display). */\n async attach(handle: SceneHandleLike): Promise<{ nodes: string[] }> {\n // synchronously, before any await: the host sets the registry for THIS world right around\n // the call (a second bundle run in the page — the preview host — writes the same global)\n capturedMaterials = ((globalThis as any).__lecodesMaterials as Record<string, MaterialHandle> | undefined) ?? null\n const { scene, nodes } = await handle.load()\n state.handle = handle\n state.scene = scene\n state.nodes = nodes\n state.paths = new Map(Object.entries(nodes).map(([ path, node ]) => [ node, path ]))\n\n scene.open()\n\n state.orbit = new OrbitCamera(scene.camera)\n\n // any drag orbits (nothing else claims viewport touches — no pick colliders in the world) —\n // unless the active tool is a DRAG tool (a brush): then the primary-button drag is its stroke\n // (the host drives right-button orbits itself through `rotate`, since touches carry no button)\n scene.addEventListener(\"touchstart\", (ev) => {\n const tool = state.activeTool ? __editorPlugins.tools.find((t) => t.name === state.activeTool) : null\n if (tool?.hooks.onDragStart) {\n const hooks = tool.hooks\n const run = (fn: () => void): void => {\n try { toolGizmosVersion++; __withGizmoScope(toolGizmos, fn) }\n catch (e) { console.error(\"[scene-editor] tool drag failed:\", e) }\n }\n const hit = raycastViewport(ev.clientX, ev.clientY)\n if (!hit) return\n run(() => hooks.onDragStart!(hit, editorApi))\n ev.track({\n onMove: (pos) => run(() => hooks.onDrag?.(raycastViewport(pos.clientX, pos.clientY), editorApi)),\n onEnd: () => run(() => hooks.onDragEnd?.(editorApi)),\n onCancel: () => run(() => hooks.onDragEnd?.(editorApi)),\n })\n return\n }\n ev.track({ onMove: (pos) => state.orbit?.rotate(pos.deltaX, pos.deltaY) })\n })\n\n // an active plugin tool owns viewport clicks — they arrive as raycast hits (selection itself\n // is host-driven from the tree, so a click outside a tool does nothing); a drag tool already\n // took the gesture as a stroke\n scene.addEventListener(\"click\", (ev) => {\n if (!state.activeTool) return\n const tool = __editorPlugins.tools.find((t) => t.name === state.activeTool)\n if (!tool?.hooks.onViewportClick || tool.hooks.onDragStart) return\n const hit = raycastViewport(ev.clientX, ev.clientY)\n if (hit) {\n // a throwing tool logs and skips — it can't take the editor down; Gizmos calls inside\n // the hook land in the tool's own buffer (cleared per call)\n try { toolGizmosVersion++; __withGizmoScope(toolGizmos, () => tool.hooks.onViewportClick!(hit, editorApi)) }\n catch (e) { console.error(\"[scene-editor] tool click failed:\", e) }\n }\n })\n\n // GLB internal hierarchies are part of the editable scene — enumerate them up front so part\n // keys resolve before the host asks for the rows\n await Promise.all(Object.keys(nodes).map((name) => refreshModelParts(name)))\n\n return { nodes: Object.keys(nodes) }\n },\n\n /** Host-driven selection (tree click). Accepts node names AND part keys. Does not echo onSelect. */\n select(name: string | null): void {\n applySelection(name && resolveKey(name) ? name : null, false)\n },\n\n /** The entity id behind a selection key (node name or GLB part key); 0 = unknown. The host\n * feeds it to the engine's editor layer (selection outline + gizmo target). */\n entityOf(name: string): number {\n return resolveKey(name)?.id ?? 0\n },\n\n /** Resolve an ENGINE pick (raw entity id) to a selection key — the policy half of viewport\n * picking. Walks the hit entity up to the NEAREST tracked def node (its absolute path — with\n * nested def nodes the deepest one wins); a hit inside the CURRENTLY SELECTED model resolves\n * to its deepest part key instead (click-again drill-down). Locked nodes yield null (the\n * click selects nothing — the host keeps the current selection). */\n pickName(entityId: number): string | null {\n if (!entityId) return null\n const nodeIds = new Map<number, string>()\n for (const [ path, node ] of Object.entries(state.nodes)) nodeIds.set(node.id, path)\n const partIds = new Map<number, string>()\n for (const [ key, node ] of state.parts) partIds.set(node.id, key)\n let part: string | null = null\n for (let id = entityId, i = 0; id !== 0 && i < 64; i++) {\n part ??= partIds.get(id) ?? null // the deepest enumerated part containing the hit\n const path = nodeIds.get(id)\n if (path !== undefined) {\n const node = state.nodes[path]\n if (node && (node as { _sceneLocked?: boolean })._sceneLocked) return null\n const inSelected = state.selected === path || state.selected?.startsWith(path + PART_SEP)\n return part && inSelected ? part : path\n }\n id = _creator.getParent(id)\n }\n return null\n },\n\n /** The selection key an entity belongs to — its deepest enumerated GLB part, else the owning\n * def node's path — with no lock / drill-down policy (drop targets: a material tile lands on\n * whatever is under the cursor). Null for editor-only / untracked entities. */\n hitKey(entityId: number): string | null {\n if (!entityId) return null\n const nodeIds = new Map<number, string>()\n for (const [ path, node ] of Object.entries(state.nodes)) nodeIds.set(node.id, path)\n const partIds = new Map<number, string>()\n for (const [ key, node ] of state.parts) partIds.set(node.id, key)\n let part: string | null = null\n for (let id = entityId, i = 0; id !== 0 && i < 64; i++) {\n part ??= partIds.get(id) ?? null\n const path = nodeIds.get(id)\n if (path !== undefined) return part ?? path\n id = _creator.getParent(id)\n }\n return null\n },\n\n /** Apply a completed gizmo drag: set the node's (or part's) local transform from the engine's\n * decomposed pose, then return position/eulerAngles/scale read BACK from the engine — exactly\n * the values the host should persist (same read-back rule as setFromView: never compose eulers\n * by hand, the engines' setter conventions differ). */\n applyPose(\n name: string,\n pose: { position: Vec3Tuple, quaternion: [number, number, number, number], scale: Vec3Tuple },\n ): { position: Vec3Tuple, eulerAngles: Vec3Tuple, scale: Vec3Tuple } | null {\n const node = resolveKey(name)\n if (!node) return null\n node.position = pose.position\n node.quaternion = pose.quaternion\n node.scale = pose.scale\n notifyEditorChanged(name)\n const p = node.position, e = node.eulerAngles, s = node.scale\n return {\n position: [ p.x, p.y, p.z ],\n eulerAngles: [ e.x, e.y, e.z ],\n scale: [ s.x, s.y, s.z ],\n }\n },\n\n /** A model node's internal GLB rows (path/name/depth) — the tree's expandable part list. Also\n * (re-)binds the part keys, so call it after anything that reloads the model. */\n modelParts(name: string): Promise<{ path: string, name: string, depth: number }[]> {\n return refreshModelParts(name)\n },\n\n /** The camera PREVIEW source for a node carrying a `CameraPlace` (edit mode registers the\n * generator instance on the node): its entity + lens. Null when the node is unknown or has no\n * place. (`hide` stays for older engine builds — markers are overlay gizmos now, not scene\n * entities, and the inset skips overlay roots by itself.) */\n cameraPreview(name: string): { entityId: number, fov: number, near: number, far: number, hide: number[] } | null {\n const node = resolveKey(name)\n if (!node) return null\n const place = (node as Node & { cameraPlace?: { fov: number, near: number, far: number } }).cameraPlace\n ?? (node.get(CameraPlace) as { fov: number, near: number, far: number } | undefined)\n if (!place) return null\n return { entityId: node.id, fov: place.fov, near: place.near, far: place.far, hide: [] }\n },\n\n /** Clip names of a loaded model node (the inspector's preview-pose dropdown); [] otherwise. */\n modelClips(name: string): string[] {\n const node = resolveKey(name) as (Node & { anim?: { clips: readonly { name: string }[] } }) | null\n return node?.anim ? node.anim.clips.map((c) => c.name) : []\n },\n\n /** Current live transform of a node OR part (part inspectors have no doc def to read from). */\n getNodeProps(name: string): { position: Vec3Tuple, eulerAngles: Vec3Tuple, scale: Vec3Tuple, visible: boolean } | null {\n const node = resolveKey(name)\n if (!node) return null\n const p = node.position, e = node.eulerAngles, s = node.scale\n return {\n position: [ p.x, p.y, p.z ],\n eulerAngles: [ e.x, e.y, e.z ],\n scale: [ s.x, s.y, s.z ],\n visible: !!node.visible, // native bridges answer 0/1 — normalize for the inspector\n }\n },\n\n /** Apply an inspector value edit to the live node or GLB part. Transform/visibility/locked only —\n * anything else is a structural change (the host recompiles the scene bundle instead). */\n setNodeProp(name: string, key: \"position\" | \"eulerAngles\" | \"scale\" | \"visible\" | \"locked\" | \"editor\", value: unknown): void {\n const node = resolveKey(name)\n if (!node) return\n if (key === \"editor\") {\n // the preview pose (`editor: { clip, time }`) — edit-mode only, never a runtime default\n __applyEditorPose(node, (value ?? undefined) as { clip?: string, time?: number } | undefined)\n return\n }\n if (key === \"position\") node.position = value as Vec3Tuple\n else if (key === \"eulerAngles\") node.eulerAngles = value as Vec3Tuple\n else if (key === \"scale\") node.scale = (typeof value === \"number\" ? value : value as Vec3Tuple) as any\n else if (key === \"visible\") node.visible = value === true\n else if (key === \"locked\") {\n // an editor-only flag kept on the live node (the coming selection/manipulation layer\n // consults it; fields edit regardless)\n ;(node as { _sceneLocked?: boolean })._sceneLocked = value === true\n return\n }\n notifyEditorChanged(name)\n },\n\n /** Live prop edit on an editor-run aspect (generator): update the instance + rebuild. False =\n * that entry isn't editor-run (inert doc data) or the bundle predates generators. */\n updateEditorAspect(name: string, index: number, key: string, value: unknown): boolean {\n return state.handle?._editorSetProp?.(name, index, key, value) === true\n },\n\n /** Every editor gizmo line currently drawn (generator rebuilds + the active tool) as world-space\n * LINES batches, with a version that changes whenever any of them re-ran — the host pushes to\n * the engine overlay only when it moves. Empty on bundles predating gizmos. */\n gizmos(): { version: number, batches: { color: string, alpha: number, segments: number[] }[] } {\n const runs = state.handle?._editorGizmos?.() ?? { version: 0, batches: [] }\n const batches = runs.batches.slice()\n for (const b of toolGizmos.batches) if (b.segments.length > 0) batches.push(b)\n return { version: runs.version * 1000003 + toolGizmosVersion, batches }\n },\n\n /** Live arg edit on a `make()` node: re-calls the factory through the tracked run — no compile\n * (the factory is already in the bundle). False = not a make node / older SDK. */\n setMakeArg(name: string, key: string, value: unknown): boolean {\n return state.handle?._editorSetMakeArg?.(name, key, value) === true\n },\n\n /** Custom inspector cards (immediate-mode widget lists — sdk core/InspectorUI.ts). An aspect\n * target runs the class's `static inspector` through the scene handle (`opts.props` = the\n * entry's CURRENT doc props, passed on every call; `opts.event` = buttons/state edits only);\n * a bare node name renders the built-in node card (the ANIMATION card on models with clips).\n * Null = no card — the host falls back to plain fields. */\n inspector(\n target: { name: string, aspect?: number },\n opts?: { props?: Record<string, unknown>, event?: { id: string, value?: unknown } },\n ): unknown[] | null {\n if (typeof target.aspect === \"number\") {\n return state.handle?._inspectorRender?.(target.name, target.aspect, opts?.props ?? {}, opts?.event) ?? null\n }\n return renderNodeCard(target.name, opts?.event)\n },\n\n /** Live parameter write on a mesh node's material, a GLB part's slot material, or a material\n * asset (every user follows). `value` is plain: number / bool / `#colour` / texture URL /\n * vector; null clears a texture. False = no live material there (the host re-runs/patches). */\n setMaterialParam(target: MaterialTarget, name: string, value: unknown): boolean {\n // `transparent` is not a uniform: it picks WHICH shader package the material is built from\n // (unlit.filamat vs unlit-transparent.filamat), and blending is baked in at compile time. Say\n // \"not live\" so the editor rebuilds the holder from the def instead of writing a uniform that\n // does not exist — a silent no-op until the next reload otherwise.\n if (name === \"transparent\") return false\n if (\"asset\" in target) {\n const h = materialHandles()[target.asset]\n if (!h) return false\n h.setParam(name, value as never)\n return true\n }\n const m = materialAt(target)\n if (!m) return false\n pushMaterialParam(m, name, value)\n return true\n },\n\n /** Replace a material asset's whole definition (a shader / kind switch): the shared instance\n * rebuilds and re-assigns to every node using it. False = unknown asset. */\n async patchMaterial(assetPath: string, def: Record<string, unknown>): Promise<boolean> {\n const h = materialHandles()[assetPath]\n if (!h) return false\n await h.reset(def as never)\n return true\n },\n\n /** Assign (def) or clear (null) the material of one GLB-part slot live. Clearing restores the\n * glTF material captured on the first live override; a slot never overridden live has nothing\n * to restore → false (the host re-runs, which rebuilds the model from its def). */\n async setPartMaterial(partKey: string, slot: number, def: Record<string, unknown> | null): Promise<boolean> {\n const node = resolveKey(partKey)\n if (!node) return false\n const key = `${partKey}#${slot}`\n if (def === null) {\n const orig = originalSlotMaterials.get(key)\n if (orig === undefined) return false\n _creator.setMaterial(node.id, orig, slot)\n const cache = (node as unknown as { _materials?: (Material | undefined)[] })._materials\n if (cache) cache[slot] = undefined\n return true\n }\n const lowered = lowerMaterial(def)\n if (lowered === null) return false\n if (!originalSlotMaterials.has(key)) originalSlotMaterials.set(key, _creator.getMaterial(node.id, slot))\n await __assignMaterialDef(node, slot, lowered as never)\n return true\n },\n\n /** Live structural patch: rebuild / add (`def`) or remove (`def === null`) ONE node at `path`\n * from plain def data — the SDK re-instantiates it through the same defineScene builder, so\n * nothing recompiles. The path carries the mount point. The HOST guarantees the def is plain\n * data (it bails to a re-run on `$expr` and `$asset` values). False = the SDK can't patch. */\n async patchNode(path: string, def: Record<string, unknown> | null): Promise<boolean> {\n const handle = state.handle\n if (!handle?._patchNode) return false\n // material asset refs → registered handles (an unknown one means the bundle predates the\n // asset — only a compile picks it up)\n const lowered = def === null ? null : lowerNodeDef(def)\n if (def !== null && lowered === null) return false\n const node = await handle._patchNode(path, lowered)\n state.paths = new Map(Object.entries(state.nodes).map(([ p, nd ]) => [ nd, p ]))\n notifyEditorChanged(path) // generators referencing the patched node follow it\n // the node object was replaced/removed — its part bindings (if it was a model) went with it\n await refreshModelParts(path)\n const sel = state.selected\n if (sel === path || sel?.startsWith(path + \"/\") || sel?.startsWith(path + PART_SEP)) {\n const alive = sel === path ? node : (sel ? resolveKey(sel) : null)\n if (!alive) state.selected = null\n }\n return true\n },\n\n /** Live rename (bare sibling segment). The SDK owns the shared node record and re-keys it —\n * subtree, editor runs, and the engine-side display name included; the harness re-keys only\n * its OWN state (reverse path map, part keys, node-card cache, selection). The document is\n * the source of truth. */\n renameNode(path: string, newName: string): boolean {\n const newPath = state.handle?._renameNode?.(path, newName) ?? null\n if (newPath === null) return false\n rekeyLocal(path, newPath)\n return true\n },\n\n /** Live reparent keeping the LOCAL transform — the same semantics the scene file describes\n * (a node under a transformed parent shifts in world space). `newParentPath` null = root.\n * The node's (and its subtree's) PATHS change with the move — same re-key story as rename. */\n reparentNode(path: string, newParentPath: string | null): boolean {\n const newPath = state.handle?._reparentNode?.(path, newParentPath) ?? null\n if (newPath === null) return false\n rekeyLocal(path, newPath)\n return true\n },\n\n /** Live scene-level tweak. Only `skybox` is applicable post-creation for now — the host falls\n * back to a re-run for everything else. */\n setSceneProp(key: string, value: unknown): boolean {\n if (key === \"skybox\" && state.scene && typeof value === \"string\") {\n state.scene.skybox = value\n return true\n }\n return false\n },\n\n zoom(delta: number): void { state.orbit?.zoom(delta) },\n /** Host-driven orbit (right-button drags — the touch system can't tell buttons apart). */\n rotate(deltaX: number, deltaY: number): void { state.orbit?.rotate(deltaX, deltaY) },\n /** Pointer hover (viewport px, no button): the active drag tool draws its cursor. */\n hover(x: number, y: number): void {\n const tool = state.activeTool ? __editorPlugins.tools.find((t) => t.name === state.activeTool) : null\n if (!tool?.hooks.onHover) return\n const hit = raycastViewport(x, y)\n try { toolGizmosVersion++; __withGizmoScope(toolGizmos, () => tool.hooks.onHover!(hit, editorApi)) }\n catch (e) { console.error(\"[scene-editor] tool hover failed:\", e) }\n },\n pan(deltaX: number, deltaY: number): void { state.orbit?.pan(deltaX, deltaY) },\n\n /** Raycast under a viewport pixel (physics bodies when present, ground-plane fallback) — the\n * host's drop-to-place path; same hit shape the plugin `editor.raycast` answers. */\n raycast(x: number, y: number): { point: Vec3Tuple, normal: Vec3Tuple, node: string | null } | null {\n return raycastViewport(x, y)\n },\n\n /** Aspect field schemas (AspectClassInfo[]) for every aspect class the scene references — the\n * inspector's editors. Computed in-bundle because only the bundle holds the ctors. */\n aspectSchemas(): unknown[] {\n return state.handle?._describeAspects?.() ?? []\n },\n\n /** Registered editor windows (`registerEditorWindow` in `*.editor.ts` files), display order. */\n editorWindows(): { title: string }[] {\n return __editorPlugins.windows.map((w) => ({ title: w.title }))\n },\n\n /** One immediate-mode pass of a registered window — same widget-list protocol as inspector\n * cards, except every field key is transient editor state (windows have no doc entry). */\n renderWindow(index: number, event?: { id: string, value?: unknown }): unknown[] | null {\n const win = __editorPlugins.windows[index]\n if (!win) return null\n let ui = windowUIs.get(index)\n if (!ui) { ui = new InspectorUI(); windowUIs.set(index, ui) }\n return ui._run((u) => win.render(u, editorApi), event)\n },\n\n /** Registered viewport tools (`registerEditorTool`) — the host's extra toolbar entries. */\n editorTools(): { name: string, cursor?: string, icon?: string }[] {\n return __editorPlugins.tools.map((t) => ({ name: t.name, cursor: t.hooks.cursor, icon: t.hooks.icon, drag: !!t.hooks.onDragStart }))\n },\n\n /** Activate a plugin tool (null = deactivate). While active, viewport clicks route to the\n * tool's hooks; selection itself is unaffected. */\n setActiveTool(name: string | null): void {\n state.activeTool = name && __editorPlugins.tools.some((t) => t.name === name) ? name : null\n toolGizmos.clear()\n toolGizmosVersion++\n },\n\n /** Install the host's document operations — the write half of the plugin `editor` API. */\n setEditorOps(ops: EditorOps): void {\n state.editorOps = ops\n },\n\n /** Introspection for driving the editor headlessly (CDP/tests): selection + tool state, plus a\n * live euler-semantics probe against the running engine (the setter convention differs across\n * shipped engines — see setFromView). */\n _debug(): unknown {\n const probe = new Node()\n probe.eulerAngles = [ 0, 0, 90 ]\n const y = probe.quaternion.rotateVec3([ 0, 1, 0 ])\n const probe2 = new Node()\n probe2.quaternion = Quat.fromAxisAngle([ 0, 0, 1 ], Math.PI / 2)\n const y2 = probe2.quaternion.rotateVec3([ 0, 1, 0 ])\n // world poses of every def node + any node named in `names` under them — a numeric probe\n const poses: Record<string, number[]> = {}\n const visit = (n: Node, key: string, depth: number): void => {\n const m = n.worldMatrix\n const pos = m.position, z = m.basisZ, y = m.basisY\n poses[key] = [ pos.x, pos.y, pos.z, z.x, z.y, z.z, y.x, y.y, y.z ].map((v) => Math.round(v * 1000) / 1000)\n if (depth > 6) return\n for (const c of n.children) if (c.name && !isEditorNode(c)) visit(c, `${key}/${c.name}`, depth + 1)\n }\n for (const [ path, n ] of Object.entries(state.nodes)) if (!path.includes(\"/\")) visit(n, path, 0)\n return {\n poses,\n selected: state.selected, // def PATH (\"city/in1/pt1\") OR `<defPath>::<partPath>` part key\n activeTool: state.activeTool,\n eulerZ90MapsYTo: [ y.x, y.y, y.z ],\n quatZ90MapsYTo: [ y2.x, y2.y, y2.z ], // standard/faithful: (−1, 0, 0)\n }\n },\n\n /** Pose the named node from the CURRENT editor viewpoint (the camera node's \"Set from view\"):\n * applies the live camera's world pose to the node quaternion-faithfully, then returns the\n * parent-local position/eulerAngles read BACK from the engine — exactly the values the host\n * should write to the file (never compose eulers by hand: the shipped engines apply the\n * eulerAngles setter with differing conventions, so only an engine read-back round-trips). */\n setFromView(name: string): { position: Vec3Tuple, eulerAngles: Vec3Tuple } | null {\n const node = resolveKey(name)\n const cam = state.scene?.camera\n if (!node || !cam) return null\n const parent = node.parent\n const camPos = cam.worldPosition\n node.quaternion = parent\n ? parent.worldQuaternion.invert().mul(cam.worldQuaternion)\n : cam.worldQuaternion\n node.position = parent ? parent.worldMatrix.invert().transformPoint(camPos) : camPos\n notifyEditorChanged(name)\n const p = node.position, e = node.eulerAngles\n return { position: [ p.x, p.y, p.z ], eulerAngles: [ e.x, e.y, e.z ] }\n },\n\n /** Frame the named node or part (or the selection). */\n focus(name?: string): void {\n const node = resolveKey(name ?? state.selected ?? \"\")\n if (!node) return\n const p = node.worldPosition\n state.orbit?.focus([ p.x, p.y, p.z ])\n },\n\n /** \"Fit to mesh\": dimensions + origin that make `kind` hug what the node actually renders, as\n * plain numbers for the host to write into the file (nothing is applied here — aspects are\n * inert in edit mode, and the collider overlay redraws from the document). Null while the\n * subtree measures as nothing: an empty node, or a model still loading — the host can retry.\n *\n * The measuring is `Shape.fitBounds` from the SDK, the same code `shape.fit()` runs at runtime.\n * Both inputs come from the ENGINE rather than from the node object: the node belongs to the\n * user's bundle, whose accessors the bundler may have shaken out (see the `_creator` note). */\n fitShape(name: string, kind?: ShapeKindName): unknown {\n const node = resolveKey(name)\n const box = node ? _creator.computeBoundingBox?.(node.id) : undefined\n if (!node || !box) return null\n if (!(box[3] - box[0] > 1e-6 || box[4] - box[1] > 1e-6 || box[5] - box[2] > 1e-6)) return null\n const m = new Float32Array(16)\n _creator.getWorldMatrix(node.id, m)\n const scale: Vec3Tuple = [\n Math.hypot(m[0], m[1], m[2]) || 1,\n Math.hypot(m[4], m[5], m[6]) || 1,\n Math.hypot(m[8], m[9], m[10]) || 1,\n ]\n const bounds = { min: [ box[0], box[1], box[2] ] as Vec3Tuple, max: [ box[3], box[4], box[5] ] as Vec3Tuple }\n return Shape.fitBounds(bounds, scale, kind ?? \"box\")\n },\n\n /** Frame the named node from its subtree AABB: orbit target = world-space center, distance from\n * the extent (the host's preview captures + a future \"frame selection\"). False while the AABB is\n * degenerate — the engine answers zeros for empty/not-yet-loaded subtrees, so the host can poll\n * this as its \"model loaded\" signal — or without engine bounds support (filament). */\n frameNode(name: string): boolean {\n const node = resolveKey(name)\n const bounds = node ? worldBounds(node) : null\n if (!bounds) return false\n frameBounds(bounds.min, bounds.max)\n return true\n },\n\n /** Frame the whole scene — the union AABB of every top-level node (a prefab file's preview\n * capture). False while nothing has real bounds yet (same polling contract as `frameNode`). */\n frameAll(): boolean {\n const min: Vec3Tuple = [ Infinity, Infinity, Infinity ]\n const max: Vec3Tuple = [ -Infinity, -Infinity, -Infinity ]\n let any = false\n for (const [ path, node ] of Object.entries(state.nodes)) {\n if (path.includes(\"/\")) continue\n const b = worldBounds(node)\n if (!b) continue\n any = true\n for (let k = 0; k < 3; k++) {\n if (b.min[k] < min[k]) min[k] = b.min[k]\n if (b.max[k] > max[k]) max[k] = b.max[k]\n }\n }\n if (!any) return false\n frameBounds(min, max)\n return true\n },\n}\n\n;(globalThis as any).__lecodesSceneHarness = controller\n\n// the built-in terrain brush (drag tool + settings window) — always registered; it no-ops until a\n// terrain node is under the pointer\ninstallTerrainTool({ nodeOf: (path) => state.nodes[path] ?? null })\n","math.ts":"// Pure orbit-camera math — plain number tuples in/out, no SDK dependencies, so it unit-tests\n// directly (packages/sdk/tests import it) and the harness bundle stays engine-agnostic here.\n// (The gizmo math that used to live beside it moved into the lite ENGINE's editor layer,\n// viewer-lite/src/gl/editor.ts, together with the gizmo itself.)\n\nexport type V3 = readonly [number, number, number]\n\nexport const dot = (a: V3, b: V3): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2]\n\n/** Yaw/pitch (radians) + radius around a target → camera position. */\nexport const orbitPosition = (target: V3, yaw: number, pitch: number, radius: number): V3 => {\n const cp = Math.cos(pitch)\n return [\n target[0] + radius * cp * Math.sin(yaw),\n target[1] + radius * Math.sin(pitch),\n target[2] + radius * cp * Math.cos(yaw),\n ]\n}\n\n/**\n * The camera's LOCAL screen basis at a yaw/pitch: `right` is horizontal (the orbit never rolls the\n * view) and `up` leans away from the camera as the pitch grows — both perpendicular to the view\n * direction, i.e. the plane a screen-space pan slides along (panning up must NOT climb world Y).\n */\nexport const orbitBasis = (yaw: number, pitch: number): { right: V3, up: V3 } => {\n const sy = Math.sin(yaw), cy = Math.cos(yaw)\n const sp = Math.sin(pitch), cp = Math.cos(pitch)\n return { right: [ cy, 0, -sy ], up: [ -sp * sy, cp, -sp * cy ] }\n}\n","orbit.ts":"// Orbit camera controller: drag anywhere (that the gizmo didn't claim) rotates around a target;\n// zoom/pan/focus are driven by the host (wheel events belong to the DOM, which the harness never\n// touches — the Vue viewport forwards them through the controller).\n\nimport { orbitBasis, orbitPosition, type V3 } from \"./math\"\n\nconst PITCH_LIMIT = Math.PI / 2 - 0.05\n\nexport class OrbitCamera {\n target: V3 = [ 0, 0, 0 ]\n yaw = Math.PI / 4\n pitch = 0.5\n radius = 8\n\n constructor(private readonly camera: Camera) {\n this.apply()\n }\n\n apply(): void {\n this.camera.position = orbitPosition(this.target, this.yaw, this.pitch, this.radius) as [number, number, number]\n this.camera.lookAt([ ...this.target ])\n }\n\n rotate(deltaX: number, deltaY: number): void {\n this.yaw -= deltaX * 0.008\n this.pitch = Math.min(PITCH_LIMIT, Math.max(-PITCH_LIMIT, this.pitch + deltaY * 0.008))\n this.apply()\n }\n\n /** Exponential zoom (host wheel: pass ±1-ish steps). */\n zoom(delta: number): void {\n this.radius = Math.min(200, Math.max(0.5, this.radius * Math.exp(delta * 0.2)))\n this.apply()\n }\n\n /** Pan the target in the camera's screen plane (host drives, e.g. middle-drag). */\n pan(deltaX: number, deltaY: number): void {\n const k = this.radius * 0.0015\n // Camera-LOCAL screen basis, so a vertical drag slides along the view plane (like the\n // horizontal one already did) instead of climbing the world Y axis.\n const { right, up } = orbitBasis(this.yaw, this.pitch)\n this.target = [\n this.target[0] - (right[0] * deltaX - up[0] * deltaY) * k,\n this.target[1] - (right[1] * deltaX - up[1] * deltaY) * k,\n this.target[2] - (right[2] * deltaX - up[2] * deltaY) * k,\n ]\n this.apply()\n }\n\n focus(point: V3, radius?: number): void {\n this.target = point\n if (radius !== undefined) this.radius = Math.max(0.5, radius)\n this.apply()\n }\n}\n","terrain.ts":"// The terrain brush — the scene editor's built-in sculpt / paint / hole tool over a scene's\n// `terrain:` nodes (docs/terrain-plan.md v2 \"scene-editor brushes\"). It is registered through the\n// same window + tool protocol plugins use (`registerEditorWindow` / `registerEditorTool`), but it\n// ships with the harness, so every scene editor has it.\n//\n// How an edit flows:\n// 1. a primary-button drag in the viewport is the STROKE (the harness routes drags to a tool that\n// declares `onDragStart`; right-drag still orbits — the host drives that);\n// 2. every pointer move dabs the LIVE Terrain under the hit (`sculpt` / `paintLayer` / `hole`,\n// time-scaled by the strength) and commits — the engine rebuilds only the touched chunks;\n// 3. on release the result is persisted as the node's `.terrain` file (LTER) through\n// `editor.writeAsset`. The FIRST save of a procedural block MATERIALIZES it: `size` /\n// `heights` / `stamps` / `holes` are dropped and `data: asset('./<node>.terrain')` takes\n// their place (the file holds the stamps' result too). Later saves overwrite the file with\n// no doc edit, so the scene's undo stack never sees terrain bytes — the tool keeps its own\n// stroke undo (a snapshot per stroke, eight deep).\n//\n// Picking is exact against the drawn triangles (`Terrain.raycastWorld`): edit mode attaches no\n// aspects, so there is no height-field body to ray-cast.\n\ntype Vec3Tuple = [number, number, number]\ntype Hit = { point: Vec3Tuple, normal: Vec3Tuple, node: string | null }\ntype EditorLike = {\n getProp(path: string, key: string): unknown\n setProp(path: string, key: string, value: unknown): boolean\n writeAsset(path: string, ref: string | null, name: string, bytes: Uint8Array): Promise<string | null>\n}\ntype TerrainLike = InstanceType<typeof Terrain>\n\ntype Mode = \"raise\" | \"lower\" | \"smooth\" | \"flatten\" | \"paint\" | \"hole\" | \"fill\"\nconst MODES: Mode[] = [ \"raise\", \"lower\", \"smooth\", \"flatten\", \"paint\", \"hole\", \"fill\" ]\n\n/** The window copies its widget values here; the drag hooks read them. */\nconst brush = { mode: \"raise\" as Mode, radius: 6, strength: 0.5, hardness: 0.5, layer: 0 }\n\ntype Stroke = {\n path: string\n terrain: TerrainLike\n /** Undo point taken at the first dab. */\n before: ReturnType<TerrainLike[\"snapshot\"]>\n /** Flatten keeps the height under the stroke's FIRST dab (Unity-style). */\n target: number\n lastDab: number\n /** Last dab position (local) — dabs are spaced by a fraction of the radius, so a slow drag\n * doesn't pile up a hundred dabs on one spot. */\n lastX: number\n lastZ: number\n}\n\nconst RING_SEGMENTS = 48\nconst UNDO_DEPTH = 8\nconst SAVE_DELAY_MS = 700\n\nexport const installTerrainTool = (deps: { nodeOf(path: string): Node | null }): void => {\n let stroke: Stroke | null = null\n const undo: { path: string, snap: ReturnType<TerrainLike[\"snapshot\"]> }[] = []\n let status = \"\"\n let saveTimer: number | null = null\n let pendingSave: { path: string } | null = null\n /** Until this time a materialization patch may be in flight — strokes wait (the node is being rebuilt). */\n let blockedUntil = 0\n let editorRef: EditorLike | null = null\n\n const terrainAt = (hit: Hit | null): { path: string, terrain: TerrainLike } | null => {\n if (!hit?.node) return null\n const t = Terrain.of(deps.nodeOf(hit.node))\n return t ? { path: hit.node, terrain: t } : null\n }\n\n // ---- the brush cursor: a ring lying ON the surface (each point sampled off the terrain) ----------\n const ring = (t: TerrainLike, hit: Hit, color: string): void => {\n const c = t.toLocal(hit.point)\n const pts: Vec3Tuple[] = []\n for (let i = 0; i < RING_SEGMENTS; i++) {\n const a = (i / RING_SEGMENTS) * Math.PI * 2\n const x = c.x + Math.cos(a) * brush.radius, z = c.z + Math.sin(a) * brush.radius\n let y = t.heightAt(x, z)\n if (Number.isNaN(y)) y = c.y\n const w = t.toWorld([ x, y + 0.15, z ])\n pts.push([ w.x, w.y, w.z ])\n }\n Gizmos.polyline(pts, { color, closed: true })\n // the hard core, fainter\n if (brush.hardness > 0 && brush.hardness < 1 && brush.mode !== \"hole\" && brush.mode !== \"fill\") {\n const inner: Vec3Tuple[] = []\n const r = brush.radius * brush.hardness\n for (let i = 0; i < RING_SEGMENTS; i++) {\n const a = (i / RING_SEGMENTS) * Math.PI * 2\n const x = c.x + Math.cos(a) * r, z = c.z + Math.sin(a) * r\n let y = t.heightAt(x, z)\n if (Number.isNaN(y)) y = c.y\n const w = t.toWorld([ x, y + 0.15, z ])\n inner.push([ w.x, w.y, w.z ])\n }\n Gizmos.polyline(inner, { color, alpha: 0.35, closed: true })\n }\n }\n const ringColor = (): string => {\n switch (brush.mode) {\n case \"raise\": return \"#7fd36b\"\n case \"lower\": return \"#e0894c\"\n case \"smooth\": return \"#6cc4e8\"\n case \"flatten\": return \"#e8d86c\"\n case \"paint\": return \"#d47fe8\"\n default: return \"#ff5d5d\"\n }\n }\n\n // ---- dabs -------------------------------------------------------------------------------------\n const dab = (s: Stroke, hit: Hit, dt: number): void => {\n const t = s.terrain\n const p = t.toLocal(hit.point)\n const spacing = Math.max(0.05, brush.radius * 0.15)\n if (s.lastDab > 0 && Math.hypot(p.x - s.lastX, p.z - s.lastZ) < spacing && brush.mode !== \"smooth\" && brush.mode !== \"flatten\") {\n // stayed put: still act, but on the clock (holding the brush keeps raising), not per move\n if (dt < 0.03) return\n }\n s.lastX = p.x; s.lastZ = p.z\n const k = Math.min(1, brush.strength * dt * 12) // 0…1 blends, time-scaled\n switch (brush.mode) {\n case \"raise\":\n case \"lower\":\n t.sculpt({ x: p.x, z: p.z, radius: brush.radius, mode: brush.mode, hardness: brush.hardness, amount: brush.strength * 6 * dt })\n break\n case \"smooth\":\n t.sculpt({ x: p.x, z: p.z, radius: brush.radius, mode: \"smooth\", hardness: brush.hardness, amount: k })\n break\n case \"flatten\":\n t.sculpt({ x: p.x, z: p.z, radius: brush.radius, mode: \"flatten\", hardness: brush.hardness, amount: k, target: s.target })\n break\n case \"paint\": {\n const inner = brush.radius * brush.hardness\n t.paintLayer({ circle: { x: p.x, z: p.z, r: inner } }, brush.layer, k, brush.radius - inner)\n break\n }\n case \"hole\":\n case \"fill\":\n t.hole({ circle: { x: p.x, z: p.z, r: brush.radius } }, brush.mode === \"hole\")\n break\n }\n t.commit()\n }\n\n // ---- persistence --------------------------------------------------------------------------------\n const isAssetRef = (v: unknown): v is { $asset: string } =>\n typeof v === \"object\" && v !== null && typeof (v as { $asset?: unknown }).$asset === \"string\"\n\n const save = async (path: string): Promise<void> => {\n const editor = editorRef\n const t = Terrain.of(deps.nodeOf(path))\n if (!editor || !t) return\n const block = editor.getProp(path, \"terrain\")\n const blockObj = typeof block === \"object\" && block !== null ? (block as Record<string, unknown>) : {}\n const ref = isAssetRef(blockObj.data) ? blockObj.data.$asset : null\n const name = `${path.split(\"/\").pop() || \"terrain\"}.terrain`\n let newRef: string | null\n try {\n newRef = await editor.writeAsset(path, ref, name, new Uint8Array(t.serialize()))\n } catch (e) {\n status = `save failed: ${(e as Error).message ?? e}`\n return\n }\n if (!newRef) { status = \"this host can't write files — edits are live only\"; return }\n if (ref) { status = `saved ${ref}`; return }\n // materialize: the file now carries heights + holes + control (stamps included), so the\n // procedural keys go; the doc points at the file from here on\n const { size: _s, sizeX: _sx, sizeZ: _sz, heights: _h, stamps: _st, holes: _ho, ...rest } = blockObj\n blockedUntil = Date.now() + 2500\n editor.setProp(path, \"terrain\", { ...rest, data: { $asset: newRef } })\n status = `saved ${newRef} (the node now loads it)`\n }\n\n const scheduleSave = (path: string): void => {\n pendingSave = { path }\n if (saveTimer !== null) clearTimeout(saveTimer)\n saveTimer = setTimeout(() => {\n saveTimer = null\n const p = pendingSave\n pendingSave = null\n if (p) void save(p.path)\n }, SAVE_DELAY_MS)\n }\n\n const undoStroke = (): void => {\n const last = undo.pop()\n if (!last) { status = \"nothing to undo\"; return }\n const t = Terrain.of(deps.nodeOf(last.path))\n if (!t) { status = `undo: ${last.path} is gone`; return }\n try { t.restore(last.snap).commit() } catch (e) { status = `undo failed: ${(e as Error).message}`; return }\n status = `undid a stroke on ${last.path} (${undo.length} left)`\n scheduleSave(last.path)\n }\n\n // ---- registrations ------------------------------------------------------------------------------\n registerEditorWindow(\"Terrain\", (ui, editor) => {\n editorRef = editor as unknown as EditorLike\n ui.toolButton(\"Brush\", \"terrain\")\n brush.mode = ui.select(\"mode\", MODES, { value: \"raise\" }) as Mode\n brush.radius = ui.slider(\"radius\", { min: 0.5, max: 80, step: 0.5, value: 6 })\n brush.strength = ui.slider(\"strength\", { min: 0.05, max: 1, step: 0.05, value: 0.5 })\n if (brush.mode !== \"hole\" && brush.mode !== \"fill\") {\n brush.hardness = ui.slider(\"hardness\", { min: 0, max: 1, step: 0.05, value: 0.5 })\n }\n if (brush.mode === \"paint\") brush.layer = Number(ui.select(\"layer\", [ 0, 1, 2, 3 ], { value: 0 }))\n if (ui.button(\"Undo stroke\")) undoStroke()\n ui.info(brush.mode === \"paint\"\n ? `paints layer ${brush.layer} (the terrain's layers[${brush.layer}] texture)`\n : brush.mode === \"hole\" || brush.mode === \"fill\"\n ? \"cuts (or fills back) samples under the disc — no triangles, no collision there\"\n : `${brush.mode}: drag on a terrain; right-drag orbits`)\n if (status) ui.info(status)\n })\n\n registerEditorTool(\"terrain\", {\n cursor: \"crosshair\",\n icon: \"⛰\",\n onHover(hit, editor) {\n editorRef = editor as unknown as EditorLike\n const at = terrainAt(hit)\n if (at && hit) ring(at.terrain, hit, ringColor())\n },\n onDragStart(hit, editor) {\n editorRef = editor as unknown as EditorLike\n if (Date.now() < blockedUntil) { status = \"the terrain is reloading — try again\"; return }\n const at = terrainAt(hit)\n if (!at) { stroke = null; return }\n const p = at.terrain.toLocal(hit.point)\n let target = at.terrain.heightAt(p.x, p.z)\n if (Number.isNaN(target)) target = p.y\n stroke = { path: at.path, terrain: at.terrain, before: at.terrain.snapshot(), target, lastDab: 0, lastX: p.x, lastZ: p.z }\n undo.push({ path: at.path, snap: stroke.before })\n while (undo.length > UNDO_DEPTH) undo.shift()\n const now = Date.now()\n dab(stroke, hit, 1 / 60)\n stroke.lastDab = now\n ring(at.terrain, hit, ringColor())\n },\n onDrag(hit) {\n const s = stroke\n if (!s || !hit) return\n if (hit.node !== s.path) { ring(s.terrain, hit, ringColor()); return } // slid off the terrain: no dab\n const now = Date.now()\n const dt = Math.min(0.1, Math.max(0.001, (now - s.lastDab) / 1000))\n dab(s, hit, dt)\n s.lastDab = now\n ring(s.terrain, hit, ringColor())\n },\n onDragEnd() {\n const s = stroke\n stroke = null\n if (!s) return\n scheduleSave(s.path)\n },\n })\n}\n"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e={};export{e as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as T}from"./worker-Caf-yYEI.js";const _='\nvar BASIS = (() => {\n var _scriptName = typeof document != \'undefined\' ? document.currentScript?.src : undefined;\n if (typeof __filename != \'undefined\') _scriptName ||= __filename;\n return (\nfunction(moduleArg = {}) {\n var moduleRtn;\n\nvar Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){}var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);var ret=fs.readFileSync(filename);return ret};readAsync=(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return new Promise((resolve,reject)=>{fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)reject(err);else resolve(binary?data.buffer:data)})})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>{if(isFileURI(url)){return new Promise((reject,resolve)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response)}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}return fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");function findWasmBinary(){var f="basis_transcoder.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["L"];updateMemoryViews();wasmTable=wasmExports["P"];addOnInit(wasmExports["M"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var noExitRuntime=Module["noExitRuntime"]||true;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}get_exception_ptr(){var isPointer=___cxa_is_pointer_type(this.get_type());if(isPointer){return HEAPU32[this.excPtr>>2]}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.excPtr}}var exceptionLast=0;var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var __abort_js=()=>{abort("")};var structRegistrations={};var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function readPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var InternalError;var throwInternalError=message=>{throw new InternalError(message)};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach((dt,i)=>{if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(()=>{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}};var __embind_finalize_value_object=structType=>{var reg=structRegistrations[structType];delete structRegistrations[structType];var rawConstructor=reg.rawConstructor;var rawDestructor=reg.rawDestructor;var fieldRecords=reg.fields;var fieldTypes=fieldRecords.map(field=>field.getterReturnType).concat(fieldRecords.map(field=>field.setterArgumentType));whenDependentTypesAreResolved([structType],fieldTypes,fieldTypes=>{var fields={};fieldRecords.forEach((field,i)=>{var fieldName=field.fieldName;var getterReturnType=fieldTypes[i];var getter=field.getter;var getterContext=field.getterContext;var setterArgumentType=fieldTypes[i+fieldRecords.length];var setter=field.setter;var setterContext=field.setterContext;fields[fieldName]={read:ptr=>getterReturnType["fromWireType"](getter(getterContext,ptr)),write:(ptr,o)=>{var destructors=[];setter(setterContext,ptr,setterArgumentType["toWireType"](destructors,o));runDestructors(destructors)}}});return[{name:reg.name,fromWireType:ptr=>{var rv={};for(var i in fields){rv[i]=fields[i].read(ptr)}rawDestructor(ptr);return rv},toWireType:(destructors,o)=>{for(var fieldName in fields){if(!(fieldName in o)){throw new TypeError(`Missing field: "${fieldName}"`)}}var ptr=rawConstructor();for(fieldName in fields){fields[fieldName].write(ptr,o[fieldName])}if(destructors!==null){destructors.push(rawDestructor,ptr)}return ptr},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction:rawDestructor}]})};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{};var embind_init_charCodes=()=>{var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes};var embind_charCodes;var readLatin1String=ptr=>{var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret};var BindingError;var throwBindingError=message=>{throw new BindingError(message)};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type \'${name}\' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}return sharedRegisterType(rawType,registeredInstance,options)}var GenericWireTypeSize=8;var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=readLatin1String(name);registerType(rawType,{name:name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},argPackAdvance:GenericWireTypeSize,readValueFromPointer:function(pointer){return this["fromWireType"](HEAPU8[pointer])},destructorFunction:null})};var shallowCopyInternalPointer=o=>({count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType});var throwInstanceAlreadyDeleted=obj=>{function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")};var finalizationRegistry=false;var detachFinalizer=handle=>{};var runDestructor=$$=>{if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}};var releaseClassHandle=$$=>{$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}};var downcastPointer=(ptr,ptrClass,desiredClass)=>{if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)};var registeredPointers={};var getInheritedInstanceCount=()=>Object.keys(registeredInstances).length;var getLiveInheritedInstances=()=>{var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};var delayFunction;var setDelayFunction=fn=>{delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}};var init_embind=()=>{Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction};var registeredInstances={};var getBasestPointer=(class_,ptr)=>{if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr};var getInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]};var makeClassHandle=(prototype,record)=>{if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record,writable:true}}))};function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}var attachFinalizer=handle=>{if("undefined"===typeof FinalizationRegistry){attachFinalizer=handle=>handle;return handle}finalizationRegistry=new FinalizationRegistry(info=>{releaseClassHandle(info.$$)});attachFinalizer=handle=>{var $$=handle.$$;var hasSmartPtr=!!$$.smartPtr;if(hasSmartPtr){var info={$$:$$};finalizationRegistry.register(handle,info,handle)}return handle};detachFinalizer=handle=>finalizationRegistry.unregister(handle);return attachFinalizer(handle)};var init_ClassHandle=()=>{Object.assign(ClassHandle.prototype,{isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;other.$$=other.$$;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right},clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}},delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}},isDeleted(){return!this.$$.ptr},deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}})};function ClassHandle(){}var createNamedFunction=(name,body)=>Object.defineProperty(body,"name",{value:name});var ensureOverloadTable=(proto,methodName,humanName)=>{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function \'${humanName}\' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name \'${name}\' twice`)}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return`_${name}`}return name};function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}var upcastPointer=(ptr,ptrClass,desiredClass)=>{while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr};function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle||!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,Emval.toHandle(()=>clonedHandle["delete"]()));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}var init_RegisteredPointer=()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr},destructor(ptr){this.rawDestructor?.(ptr)},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,fromWireType:RegisteredPointer_fromWireType})};function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var dynCallLegacy=(sig,ptr,args)=>{sig=sig.replace(/p/g,"i");var f=Module["dynCall_"+sig];return f(ptr,...args)};var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var dynCall=(sig,ptr,args=[])=>{if(sig.includes("j")){return dynCallLegacy(sig,ptr,args)}var rtn=getWasmTableEntry(ptr)(...args);return rtn};var getDynCaller=(sig,ptr)=>(...args)=>dynCall(sig,ptr,args);var embind__requireFunction=(signature,rawFunction)=>{signature=readLatin1String(signature);function makeDynCaller(){if(signature.includes("j")){return getDynCaller(signature,rawFunction)}return getWasmTableEntry(rawFunction)}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};var extendError=(baseErrorType,errorName)=>{var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\\n"+stack.replace(/^Error(:[^\\n]*)?\\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return`${this.name}: ${this.message}`}};return errorClass};var UnboundTypeError;var getTypeName=type=>{var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv};var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var __embind_register_class=(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor)=>{name=readLatin1String(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);upcast&&=embind__requireFunction(upcastSignature,upcast);downcast&&=embind__requireFunction(downcastSignature,downcast);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError(`Cannot construct ${name} due to unbound types`,[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],base=>{base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(name,function(...args){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError("Use \'new\' to construct "+name)}if(undefined===registeredClass.constructor_body){throw new BindingError(name+" has no accessible constructor")}var body=registeredClass.constructor_body[args.length];if(undefined===body){throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`)}return body.apply(this,args)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);if(registeredClass.baseClass){registeredClass.baseClass.__derivedClasses??=[];registeredClass.baseClass.__derivedClasses.push(registeredClass)}var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})};var heap32VectorToArray=(count,firstElement)=>{var array=[];for(var i=0;i<count;i++){array.push(HEAPU32[firstElement+i*4>>2])}return array};function usesDestructorStack(argTypes){for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){return true}}return false}function newFunc(constructor,argumentList){if(!(constructor instanceof Function)){throw new TypeError(`new_ called with constructor type ${typeof constructor} which is not a function`)}var dummy=createNamedFunction(constructor.name||"unknownFunctionName",function(){});dummy.prototype=constructor.prototype;var obj=new dummy;var r=constructor.apply(obj,argumentList);return r instanceof Object?r:obj}function createJsInvoker(argTypes,isClassMethodFunc,returns,isAsync){var needsDestructorStack=usesDestructorStack(argTypes);var argCount=argTypes.length;var argsList="";var argsListWired="";for(var i=0;i<argCount-2;++i){argsList+=(i!==0?", ":"")+"arg"+i;argsListWired+=(i!==0?", ":"")+"arg"+i+"Wired"}var invokerFnBody=`\\n return function (${argsList}) {\\n if (arguments.length !== ${argCount-2}) {\\n throwBindingError(\'function \' + humanName + \' called with \' + arguments.length + \' arguments, expected ${argCount-2}\');\\n }`;if(needsDestructorStack){invokerFnBody+="var destructors = [];\\n"}var dtorStack=needsDestructorStack?"destructors":"null";var args1=["humanName","throwBindingError","invoker","fn","runDestructors","retType","classParam"];if(isClassMethodFunc){invokerFnBody+="var thisWired = classParam[\'toWireType\']("+dtorStack+", this);\\n"}for(var i=0;i<argCount-2;++i){invokerFnBody+="var arg"+i+"Wired = argType"+i+"[\'toWireType\']("+dtorStack+", arg"+i+");\\n";args1.push("argType"+i)}if(isClassMethodFunc){argsListWired="thisWired"+(argsListWired.length>0?", ":"")+argsListWired}invokerFnBody+=(returns||isAsync?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\\n"}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){var paramName=i===1?"thisWired":"arg"+(i-2)+"Wired";if(argTypes[i].destructorFunction!==null){invokerFnBody+=`${paramName}_dtor(${paramName});\\n`;args1.push(`${paramName}_dtor`)}}}if(returns){invokerFnBody+="var ret = retType[\'fromWireType\'](rv);\\n"+"return ret;\\n"}else{}invokerFnBody+="}\\n";return[args1,invokerFnBody]}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc,isAsync){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and \'this\' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=usesDestructorStack(argTypes);var returns=argTypes[0].name!=="void";var closureArgs=[humanName,throwBindingError,cppInvokerFunc,cppTargetFunc,runDestructors,argTypes[0],argTypes[1]];for(var i=0;i<argCount-2;++i){closureArgs.push(argTypes[i+2])}if(!needsDestructorStack){for(var i=isClassMethodFunc?1:2;i<argTypes.length;++i){if(argTypes[i].destructorFunction!==null){closureArgs.push(argTypes[i].destructorFunction)}}}let[args,invokerFnBody]=createJsInvoker(argTypes,isClassMethodFunc,returns,isAsync);args.push(invokerFnBody);var invokerFn=newFunc(Function,args)(...closureArgs);return createNamedFunction(humanName,invokerFn)}var __embind_register_class_constructor=(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`constructor ${classType.name}`;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class \'${classType.name}\'! Overload resolution is currently only performed using the parameter count, not actual type info!`)}classType.registeredClass.constructor_body[argCount-1]=()=>{throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`,rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{argTypes.splice(1,0,null);classType.registeredClass.constructor_body[argCount-1]=craftInvokerFunction(humanName,argTypes,null,invoker,rawConstructor);return[]});return[]})};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex!==-1){return signature.substr(0,argsIndex)}else{return signature}};var __embind_register_class_function=(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,context,isPureVirtual,isAsync)=>{var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);methodName=getFunctionName(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],classType=>{classType=classType[0];var humanName=`${classType.name}.${methodName}`;if(methodName.startsWith("@@")){methodName=Symbol[methodName.substring(2)]}if(isPureVirtual){classType.registeredClass.pureVirtualFunctions.push(methodName)}function unboundTypesHandler(){throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`,rawArgTypes)}var proto=classType.registeredClass.instancePrototype;var method=proto[methodName];if(undefined===method||undefined===method.overloadTable&&method.className!==classType.name&&method.argCount===argCount-2){unboundTypesHandler.argCount=argCount-2;unboundTypesHandler.className=classType.name;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-2]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,argTypes=>{var memberFunction=craftInvokerFunction(humanName,argTypes,classType,rawInvoker,context,isAsync);if(undefined===proto[methodName].overloadTable){memberFunction.argCount=argCount-2;proto[methodName]=memberFunction}else{proto[methodName].overloadTable[argCount-2]=memberFunction}return[]});return[]})};var __embind_register_constant=(name,type,value)=>{name=readLatin1String(name);whenDependentTypesAreResolved([],[type],type=>{type=type[0];Module[name]=type["fromWireType"](value);return[]})};var emval_freelist=[];var emval_handles=[];var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var count_emval_handles=()=>emval_handles.length/2-5-emval_freelist.length;var init_emval=()=>{emval_handles.push(0,1,undefined,1,null,1,true,1,false,1);Module["count_emval_handles"]=count_emval_handles};var Emval={toValue:handle=>{if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction:null};var __embind_register_emval=rawType=>registerType(rawType,EmValType);var enumReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?function(pointer){return this["fromWireType"](HEAP8[pointer])}:function(pointer){return this["fromWireType"](HEAPU8[pointer])};case 2:return signed?function(pointer){return this["fromWireType"](HEAP16[pointer>>1])}:function(pointer){return this["fromWireType"](HEAPU16[pointer>>1])};case 4:return signed?function(pointer){return this["fromWireType"](HEAP32[pointer>>2])}:function(pointer){return this["fromWireType"](HEAPU32[pointer>>2])};default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_enum=(rawType,name,size,isSigned)=>{name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,fromWireType:function(c){return this.constructor.values[c]},toWireType:(destructors,c)=>c.value,argPackAdvance:GenericWireTypeSize,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var __embind_register_enum_value=(rawEnumType,name,enumValue)=>{var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(`${enumType.name}_${name}`,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value};var embindRepr=v=>{if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}};var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 8:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=readLatin1String(name);registerType(rawType,{name:name,fromWireType:value=>value,toWireType:(destructors,value)=>value,argPackAdvance:GenericWireTypeSize,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var fromWireType=value=>value;if(minRange===0){var bitshift=32-8*size;fromWireType=value=>value<<bitshift>>>bitshift}var isUnsignedType=name.includes("unsigned");var checkAssertions=(value,toTypeName)=>{};var toWireType;if(isUnsignedType){toWireType=function(destructors,value){checkAssertions(value,this.name);return value>>>0}}else{toWireType=function(destructors,value){checkAssertions(value,this.name);return value}}registerType(primitiveType,{name:name,fromWireType:fromWireType,toWireType:toWireType,argPackAdvance:GenericWireTypeSize,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,fromWireType:decodeMemoryView,argPackAdvance:GenericWireTypeSize,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var __embind_register_std_string=(rawType,name)=>{name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){var decodeStartPtr=payload;for(var i=0;i<=length;++i){var currentBytePtr=payload+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[payload+i])}str=a.join("")}_free(value);return str},toWireType(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var length;var valueIsOfTypeString=typeof value=="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){length=lengthBytesUTF8(value)}else{length=value.length}var base=_malloc(4+length+1);var ptr=base+4;HEAPU32[base>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+i]=value[i]}}}if(destructors!==null){destructors.push(_free,base)}return base},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var UTF16Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead)=>{var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder)return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr));var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str};var stringToUTF16=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead)=>{var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=readLatin1String(name);var decodeString,encodeString,readCharAt,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16;readCharAt=pointer=>HEAPU16[pointer>>1]}else if(charSize===4){decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32;readCharAt=pointer=>HEAPU32[pointer>>2]}registerType(rawType,{name:name,fromWireType:value=>{var length=HEAPU32[value>>2];var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||readCharAt(currentBytePtr)==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},argPackAdvance:GenericWireTypeSize,readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_value_object=(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor)=>{structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}};var __embind_register_value_object_field=(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext)=>{structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})};var __embind_register_void=(rawType,name)=>{name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,argPackAdvance:0,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);var emval_returnValue=(returnType,destructorsRef,handle)=>{var destructors=[];var result=returnType["toWireType"](destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors)}return result};var __emval_as=(handle,returnType,destructorsRef)=>{handle=Emval.toValue(handle);returnType=requireRegisteredType(returnType,"emval::as");return emval_returnValue(returnType,destructorsRef,handle)};var emval_methodCallers=[];var __emval_call=(caller,handle,destructorsRef,args)=>{caller=emval_methodCallers[caller];handle=Emval.toValue(handle);return caller(null,handle,destructorsRef,args)};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}return symbol};var __emval_call_method=(caller,objHandle,methodName,destructorsRef,args)=>{caller=emval_methodCallers[caller];objHandle=Emval.toValue(objHandle);methodName=getStringOrSymbol(methodName);return caller(objHandle,objHandle[methodName],destructorsRef,args)};var emval_get_global=()=>{if(typeof globalThis=="object"){return globalThis}return function(){return Function}()("return this")()};var __emval_get_global=name=>{if(name===0){return Emval.toHandle(emval_get_global())}else{name=getStringOrSymbol(name);return Emval.toHandle(emval_get_global()[name])}};var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i<argCount;++i){a[i]=requireRegisteredType(HEAPU32[argTypes+i*4>>2],"parameter "+i)}return a};var reflectConstruct=Reflect.construct;var __emval_get_method_caller=(argCount,argTypes,kind)=>{var types=emval_lookupTypes(argCount,argTypes);var retType=types.shift();argCount--;var functionBody=`return function (obj, func, destructorsRef, args) {\\n`;var offset=0;var argsList=[];if(kind===0){argsList.push("obj")}var params=["retType"];var args=[retType];for(var i=0;i<argCount;++i){argsList.push("arg"+i);params.push("argType"+i);args.push(types[i]);functionBody+=` var arg${i} = argType${i}.readValueFromPointer(args${offset?"+"+offset:""});\\n`;offset+=types[i]["argPackAdvance"]}var invoker=kind===1?"new func":"func.call";functionBody+=` var rv = ${invoker}(${argsList.join(", ")});\\n`;if(!retType.isVoid){params.push("emval_returnValue");args.push(emval_returnValue);functionBody+=" return emval_returnValue(retType, destructorsRef, rv);\\n"}functionBody+="};\\n";params.push(functionBody);var invokerFunction=newFunc(Function,params)(...args);var functionName=`methodCaller<(${types.map(t=>t.name).join(", ")}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};var __emval_get_module_property=name=>{name=getStringOrSymbol(name);return Emval.toHandle(Module[name])};var __emval_get_property=(handle,key)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);return Emval.toHandle(handle[key])};var __emval_incref=handle=>{if(handle>9){emval_handles[handle+1]+=1}};var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var getHeapMax=()=>2147483648;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _fd_close=fd=>52;var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);return 70}var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};InternalError=Module["InternalError"]=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};embind_init_charCodes();BindingError=Module["BindingError"]=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};init_ClassHandle();init_embind();init_RegisteredPointer();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();var wasmImports={K:___cxa_throw,G:__abort_js,s:__embind_finalize_value_object,C:__embind_register_bigint,I:__embind_register_bool,w:__embind_register_class,v:__embind_register_class_constructor,d:__embind_register_class_function,m:__embind_register_constant,H:__embind_register_emval,o:__embind_register_enum,a:__embind_register_enum_value,A:__embind_register_float,i:__embind_register_function,l:__embind_register_integer,f:__embind_register_memory_view,z:__embind_register_std_string,u:__embind_register_std_wstring,t:__embind_register_value_object,c:__embind_register_value_object_field,J:__embind_register_void,F:__emscripten_memcpy_js,n:__emval_as,q:__emval_call,p:__emval_call_method,b:__emval_decref,x:__emval_get_global,j:__emval_get_method_caller,r:__emval_get_module_property,g:__emval_get_property,k:__emval_incref,h:__emval_new_cstring,e:__emval_run_destructors,D:_emscripten_resize_heap,E:_fd_close,B:_fd_seek,y:_fd_write};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["M"])();var ___getTypeName=a0=>(___getTypeName=wasmExports["N"])(a0);var _malloc=a0=>(_malloc=wasmExports["O"])(a0);var _free=a0=>(_free=wasmExports["Q"])(a0);var ___cxa_is_pointer_type=a0=>(___cxa_is_pointer_type=wasmExports["R"])(a0);var dynCall_jiji=Module["dynCall_jiji"]=(a0,a1,a2,a3,a4)=>(dynCall_jiji=Module["dynCall_jiji"]=wasmExports["S"])(a0,a1,a2,a3,a4);var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise;\n\n\n return moduleRtn;\n}\n);\n})();\nif (typeof exports === \'object\' && typeof module === \'object\')\n module.exports = BASIS;\nelse if (typeof define === \'function\' && define[\'amd\'])\n define([], () => BASIS);\n',w=""+new URL("basis_transcoder-VXdx5NbI.wasm",import.meta.url).href,b={astc:{linear:37808,srgb:37840},bptc:{linear:36492,srgb:36493},s3tc:{linear:33779,srgb:35919}},P=t=>t.getExtension("WEBGL_compressed_texture_astc")?{format:"astc",srgbOk:!0}:t.getExtension("EXT_texture_compression_bptc")?{format:"bptc",srgbOk:!0}:t.getExtension("WEBGL_compressed_texture_s3tc")?{format:"s3tc",srgbOk:!!t.getExtension("WEBGL_compressed_texture_s3tc_srgb")}:{format:"rgba32",srgbOk:!0},C=()=>{const t=self;let s;const i={astc:10,bptc:6,s3tc:3,rgba32:13},l=async(o,r,n)=>{const e=new o.KTX2File(new Uint8Array(r));try{if(!e.isValid())throw new Error("invalid KTX2 file");const u=e.getWidth(),d=e.getHeight(),c=e.getLevels();if(!u||!d||!c)throw new Error("empty KTX2 file");if(!e.startTranscoding())throw new Error("KTX2 transcode init failed");const v=c>=Math.floor(Math.log2(Math.max(u,d)))+1;if(n!=="rgba32"&&v&&e.getFaces()===1){const y=i[n],p=[];for(let a=0;a<c;a++){const h=e.getImageLevelInfo(a,0,0),f=new Uint8Array(e.getImageTranscodedSizeInBytes(a,0,0,y));if(!e.transcodeImage(f,a,0,0,y,0,-1,-1))throw new Error("KTX2 level transcode failed");p.push({data:f,width:h.origWidth,height:h.origHeight})}return{msg:{levels:p},transfer:p.map(a=>a.data.buffer)}}const m=new Uint8Array(e.getImageTranscodedSizeInBytes(0,0,0,i.rgba32));if(!e.transcodeImage(m,0,0,0,i.rgba32,0,-1,-1))throw new Error("KTX2 transcode failed");const g=await createImageBitmap(new ImageData(new Uint8ClampedArray(m.buffer),u,d));return{msg:{bitmap:g},transfer:[g]}}finally{e.close(),e.delete()}};t.onmessage=o=>{const r=o.data;if(r.type==="init"){s=t.BASIS({wasmBinary:r.wasmBinary}).then(n=>(n.initializeBasis(),n));return}s.then(async n=>{try{const e=await l(n,r.buffer,r.format);t.postMessage({id:r.id,...e.msg},e.transfer)}catch(e){t.postMessage({id:r.id,error:String(e&&e.message||e)})}})}},E=async t=>{const{format:s,srgbOk:i}=t?P(t):{format:"rgba32",srgbOk:!0},l=await T(_,w,C);return{parseTexture:async o=>{const r=await l({buffer:o,format:s});if(r.bitmap)return r.bitmap.ready=Promise.resolve(),r.bitmap;const n=b[s];return Object.assign(r.levels,{isBasis:!0,isCompressedTexture:!0,internalFormat:n.linear,srgbInternalFormat:i?n.srgb:0,width:r.levels[0].width,height:r.levels[0].height})}}};export{b as BASIS_INTERNAL_FORMATS,E as createBasisManager};
|
|
Binary file
|