@holoscript/core 8.0.13 → 8.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/bin/holo-decision.mjs +133 -0
- package/dist/{GLTFPipeline-L76YTXKA.js → GLTFPipeline-6LS3UDYX.js} +6 -6
- package/dist/{GLTFPipeline-ZWJN7GI6.cjs → GLTFPipeline-VTI6PRRQ.cjs} +10 -10
- package/dist/{HoloCompositionParser-P4Y3CXKQ.js → HoloCompositionParser-GILABNN3.js} +4 -4
- package/dist/{HoloCompositionParser-6GW2BZID.cjs → HoloCompositionParser-ZVRYVEA5.cjs} +11 -11
- package/dist/{HoloScriptPlusParser-RFNHRMPE.cjs → HoloScriptPlusParser-WTPRPRPR.cjs} +7 -7
- package/dist/{HoloScriptPlusParser-HQV6IJAH.js → HoloScriptPlusParser-ZP53ECSL.js} +4 -4
- package/dist/{SceneIRCompiler-KWUFHZ4O.cjs → SceneIRCompiler-7KQ5NKAU.cjs} +11 -11
- package/dist/{SceneIRCompiler-63KA3M2F.js → SceneIRCompiler-VX75RB6H.js} +6 -6
- package/dist/{chunk-EM2OGPYE.js → chunk-2GKF7JQ6.js} +3 -3
- package/dist/{chunk-HVX7QQYK.js → chunk-3KHRZTNW.js} +3 -3
- package/dist/{chunk-UTUD6GJI.js → chunk-42Z3SVHQ.js} +3 -3
- package/dist/{chunk-A4OOLBZD.js → chunk-47XZ62KM.js} +5 -5
- package/dist/{chunk-GGR7QUKK.cjs → chunk-4CRMVEON.cjs} +37 -37
- package/dist/{chunk-QBY7LRN2.js → chunk-4NEPHK2K.js} +3 -3
- package/dist/{chunk-65S62L4D.cjs → chunk-5B2AJKA5.cjs} +20 -20
- package/dist/{chunk-PJFTTSKE.js → chunk-5T6BWIB7.js} +4 -4
- package/dist/{chunk-KFVNCW4Q.js → chunk-62OYZT6Q.js} +3 -3
- package/dist/{chunk-Y57AVOM2.cjs → chunk-645EWDZW.cjs} +4 -4
- package/dist/{chunk-BP4KTMLY.js → chunk-6FCNMTL5.js} +4 -4
- package/dist/{chunk-77QI552M.js → chunk-73EDVY5D.js} +3 -3
- package/dist/{chunk-7IDJDMPU.cjs → chunk-7FYSSI3C.cjs} +27 -2
- package/dist/{chunk-WNYXT4TE.cjs → chunk-7ITJP33Q.cjs} +3 -3
- package/dist/chunk-7ZKQ32XK.js +1571 -0
- package/dist/{chunk-P7JEKJTZ.cjs → chunk-B6JHTTMR.cjs} +25 -14
- package/dist/{chunk-FG6PYGIZ.js → chunk-BLCWQ225.js} +4 -4
- package/dist/{chunk-FRSKLX6I.js → chunk-C73GVAYC.js} +10 -7
- package/dist/{chunk-PXVAPGTR.cjs → chunk-CESMPUEZ.cjs} +3774 -1138
- package/dist/{chunk-SQXAZHDG.js → chunk-CHIZSMWD.js} +3 -3
- package/dist/{chunk-G35CAQ7I.cjs → chunk-D44CEFY4.cjs} +5 -5
- package/dist/{chunk-TC7JEO6R.cjs → chunk-ERYKKPHW.cjs} +711 -52
- package/dist/{chunk-DJ5BNRIS.js → chunk-EYHPTJ3F.js} +3675 -1046
- package/dist/{chunk-J3UHV5T4.cjs → chunk-FGXJPJ3W.cjs} +4 -4
- package/dist/{chunk-IJU6AS67.cjs → chunk-FHR2AVMA.cjs} +23 -23
- package/dist/{chunk-LMB555QB.cjs → chunk-FOM36K5I.cjs} +18 -18
- package/dist/{chunk-ZRPALBQB.cjs → chunk-GGH3ZYDB.cjs} +5 -5
- package/dist/{chunk-2BYAUHBO.js → chunk-GI5YYZH6.js} +3 -3
- package/dist/{chunk-VTEXJOSD.cjs → chunk-GYJLP4BO.cjs} +30 -30
- package/dist/{chunk-OC6GV5QH.cjs → chunk-HCONR4J6.cjs} +38 -38
- package/dist/{chunk-B3CWTCK2.js → chunk-IA7FCSRY.js} +27 -2
- package/dist/{chunk-5FYMCVBY.js → chunk-IXFNA5TT.js} +4 -4
- package/dist/{chunk-G6KACNMO.js → chunk-JCVSCKUH.js} +4 -4
- package/dist/{chunk-FHVPN7BN.js → chunk-JQCXLKAU.js} +3 -3
- package/dist/{chunk-F3YT6GUF.cjs → chunk-KLM6QD6D.cjs} +14 -14
- package/dist/{chunk-UGQ3QNV6.js → chunk-KOGPVAP6.js} +24 -8
- package/dist/{chunk-CKV5NYAE.js → chunk-KYJD2UMV.js} +4 -4
- package/dist/{chunk-LB3ZZMKT.cjs → chunk-L4DBUX3N.cjs} +10 -10
- package/dist/{chunk-RXQNRZ5F.js → chunk-LGRKBTPM.js} +3 -3
- package/dist/{chunk-PDXX5UI4.cjs → chunk-LQNL3IYV.cjs} +38 -38
- package/dist/{chunk-WWNCM473.js → chunk-M7XR3F53.js} +4 -4
- package/dist/{chunk-IFW5IW4L.cjs → chunk-ML4V5PVU.cjs} +670 -627
- package/dist/{chunk-Q4K2GEMW.cjs → chunk-N7IYRAFG.cjs} +13 -10
- package/dist/chunk-N7MIIDLI.cjs +1605 -0
- package/dist/{chunk-G6EKZHXY.js → chunk-NWKARJM2.js} +4 -4
- package/dist/{chunk-VVAAZITA.cjs → chunk-OOP4S3GU.cjs} +5 -5
- package/dist/{chunk-U3D4SYQ5.js → chunk-ORBVCOTC.js} +3 -3
- package/dist/{chunk-RRQIAJLQ.cjs → chunk-OSKGCXAQ.cjs} +5 -5
- package/dist/{chunk-QEDMOGF3.cjs → chunk-P65OIMJT.cjs} +7 -7
- package/dist/{chunk-QM7FJB5W.js → chunk-PSWCJQS4.js} +4 -4
- package/dist/{chunk-RZ2EPXO3.cjs → chunk-PZ46WRHX.cjs} +8 -8
- package/dist/{chunk-AATYYLYK.js → chunk-Q4J2K7FI.js} +16 -5
- package/dist/{chunk-ONPVM4IV.js → chunk-QLPRI3QW.js} +698 -45
- package/dist/{chunk-WTH23ISI.js → chunk-R5VKLUMR.js} +4 -4
- package/dist/{chunk-NQN5LFN4.cjs → chunk-REN3LHO6.cjs} +19 -19
- package/dist/{chunk-JEJ7TT54.js → chunk-RVN7OPCP.js} +4 -4
- package/dist/{chunk-ZK7KX5CD.cjs → chunk-SE2Y5BSL.cjs} +6 -6
- package/dist/{chunk-2X3IPCBK.cjs → chunk-SKVBUGMC.cjs} +7 -7
- package/dist/{chunk-GZPUA2TL.cjs → chunk-SNMQUWIO.cjs} +10 -10
- package/dist/{chunk-OIBRWLPY.cjs → chunk-TKDDIE3T.cjs} +54 -3
- package/dist/{chunk-SQAAWO4V.cjs → chunk-TMNAKNV2.cjs} +5 -5
- package/dist/{chunk-5LVFTGKH.js → chunk-TZQKJOPC.js} +3 -3
- package/dist/{chunk-TSTFYGGX.js → chunk-UKEK2NZJ.js} +3 -3
- package/dist/{chunk-3IUHTGDD.cjs → chunk-VA7THINF.cjs} +38 -38
- package/dist/{chunk-Z557DVYN.js → chunk-VMCR5QVI.js} +4 -4
- package/dist/{chunk-IS7NTKY5.cjs → chunk-VTFLKSJL.cjs} +14 -14
- package/dist/{chunk-RVNXBF2M.js → chunk-VXHB6ZHY.js} +4 -4
- package/dist/{chunk-S4O7CFHQ.cjs → chunk-VZI7S7P6.cjs} +7 -7
- package/dist/{chunk-BQJ3YBF3.js → chunk-W4AJMMQR.js} +667 -628
- package/dist/{chunk-2KKOYZYI.cjs → chunk-XZC75AJ4.cjs} +5 -5
- package/dist/{chunk-FWCHPCD4.js → chunk-Y2OCKXVD.js} +4 -4
- package/dist/{chunk-XF63CZ7E.cjs → chunk-YBM34FUJ.cjs} +6 -6
- package/dist/{chunk-DUG3OQAS.js → chunk-YDSNDCV3.js} +3 -3
- package/dist/{chunk-566ZB4LZ.cjs → chunk-YM7UUYR5.cjs} +5 -5
- package/dist/{chunk-BCP4E7MD.cjs → chunk-YMI3ED3B.cjs} +26 -10
- package/dist/{chunk-CP7V3ATL.js → chunk-Z7NUL73U.js} +51 -4
- package/dist/cli/holoscript-runner.cjs +10 -10
- package/dist/cli/holoscript-runner.js +3 -3
- package/dist/compiler/agent-inference.cjs +8 -8
- package/dist/compiler/agent-inference.js +4 -4
- package/dist/compiler/android-xr.cjs +7 -7
- package/dist/compiler/android-xr.js +5 -5
- package/dist/compiler/android.cjs +6 -6
- package/dist/compiler/android.js +4 -4
- package/dist/compiler/context.cjs +6 -6
- package/dist/compiler/context.js +3 -3
- package/dist/compiler/daimon-seed.cjs +9 -9
- package/dist/compiler/daimon-seed.js +4 -4
- package/dist/compiler/domain-block-utils.cjs +165 -165
- package/dist/compiler/domain-block-utils.js +4 -4
- package/dist/compiler/dtdl.cjs +7 -7
- package/dist/compiler/dtdl.js +4 -4
- package/dist/compiler/gltf-pipeline.cjs +8 -8
- package/dist/compiler/gltf-pipeline.js +1 -1
- package/dist/compiler/godot.cjs +6 -6
- package/dist/compiler/godot.js +5 -5
- package/dist/compiler/incremental.cjs +8 -8
- package/dist/compiler/incremental.js +3 -3
- package/dist/compiler/index.cjs +871 -207
- package/dist/compiler/index.d.ts +21 -1
- package/dist/compiler/index.js +696 -60
- package/dist/compiler/ios.cjs +6 -6
- package/dist/compiler/ios.js +4 -4
- package/dist/compiler/llm-provider-capabilities.cjs +6 -6
- package/dist/compiler/llm-provider-capabilities.js +3 -3
- package/dist/compiler/openxr.cjs +6 -6
- package/dist/compiler/openxr.js +5 -5
- package/dist/compiler/sdf.cjs +7 -7
- package/dist/compiler/sdf.js +5 -5
- package/dist/compiler/state.cjs +6 -6
- package/dist/compiler/state.js +4 -4
- package/dist/compiler/trait-composition.cjs +7 -7
- package/dist/compiler/trait-composition.js +3 -3
- package/dist/compiler/unity.cjs +6 -6
- package/dist/compiler/unity.js +5 -5
- package/dist/compiler/unreal.cjs +7 -7
- package/dist/compiler/unreal.js +5 -5
- package/dist/compiler/urdf.cjs +14 -14
- package/dist/compiler/urdf.js +5 -5
- package/dist/compiler/usd-physics.cjs +7 -7
- package/dist/compiler/usd-physics.js +4 -4
- package/dist/compiler/visionos.cjs +6 -6
- package/dist/compiler/visionos.js +5 -5
- package/dist/compiler/vrchat.cjs +7 -7
- package/dist/compiler/vrchat.js +5 -5
- package/dist/compiler/wasm.cjs +10 -10
- package/dist/compiler/wasm.js +5 -5
- package/dist/compiler/webgpu.cjs +6 -6
- package/dist/compiler/webgpu.js +5 -5
- package/dist/entries/scripting.cjs +5 -5
- package/dist/entries/scripting.js +1 -1
- package/dist/evolution/index.cjs +17 -17
- package/dist/evolution/index.js +5 -5
- package/dist/index.cjs +915 -704
- package/dist/index.d.ts +70 -5
- package/dist/index.js +191 -108
- package/dist/parser.cjs +18 -18
- package/dist/parser.js +4 -4
- package/dist/reconstruction/index.cjs +127 -38
- package/dist/reconstruction/index.d.ts +159 -0
- package/dist/reconstruction/index.js +11 -2
- package/dist/runtime.d.ts +1 -0
- package/dist/traitDocs/traitDocs.cjs +8 -8
- package/dist/traitDocs/traitDocs.js +2 -2
- package/dist/traits/index.cjs +566 -471
- package/dist/traits/index.js +93 -11
- package/package.json +20 -16
- package/dist/chunk-DVHZD5HV.cjs +0 -639
- package/dist/chunk-G72754QB.js +0 -625
|
@@ -1,8 +1,200 @@
|
|
|
1
|
-
import { init_DomainBlockCompilerMixin, datavizToWebGPU, compileDataVizBlock, compileMaterialBlock, materialToWebGPU, compileParticleBlock, compilePostProcessingBlock, compileProceduralBlock } from './chunk-
|
|
2
|
-
import { init_CompilerBase, CompilerBase } from './chunk-
|
|
3
|
-
import {
|
|
1
|
+
import { init_DomainBlockCompilerMixin, datavizToWebGPU, compileDataVizBlock, compileMaterialBlock, materialToWebGPU, compileParticleBlock, compilePostProcessingBlock, compileProceduralBlock } from './chunk-TZQKJOPC.js';
|
|
2
|
+
import { init_CompilerBase, CompilerBase } from './chunk-YDSNDCV3.js';
|
|
3
|
+
import { __esm, __export } from './chunk-W4GABFCR.js';
|
|
4
4
|
import { ANSCapabilityPath } from '@holoscript/core-types/ans';
|
|
5
5
|
|
|
6
|
+
// src/compiler/render-modules/geometry-registry.ts
|
|
7
|
+
function resolveGeometry(meshType) {
|
|
8
|
+
const key = String(meshType ?? "").trim().toLowerCase();
|
|
9
|
+
const kind = ALIAS[key] ?? "box";
|
|
10
|
+
return PRIMITIVE[kind];
|
|
11
|
+
}
|
|
12
|
+
var PRIMITIVE, ALIAS;
|
|
13
|
+
var init_geometry_registry = __esm({
|
|
14
|
+
"src/compiler/render-modules/geometry-registry.ts"() {
|
|
15
|
+
PRIMITIVE = {
|
|
16
|
+
box: { kind: "box", webgpuVertexFn: "generateCubeVertices(1.0)" },
|
|
17
|
+
sphere: { kind: "sphere", webgpuVertexFn: "generateSphereVertices(0.5, 16, 16)" },
|
|
18
|
+
plane: { kind: "plane", webgpuVertexFn: "generatePlaneVertices(1.0, 1.0)" },
|
|
19
|
+
cylinder: { kind: "cylinder", webgpuVertexFn: "generateCylinderVertices(0.5, 1.0, 16)" },
|
|
20
|
+
cone: { kind: "cone", webgpuVertexFn: "generateConeVertices(0.5, 1.0, 16)" },
|
|
21
|
+
torus: { kind: "torus", webgpuVertexFn: "generateTorusVertices(0.5, 0.15, 16, 32)" }
|
|
22
|
+
};
|
|
23
|
+
ALIAS = {
|
|
24
|
+
// box family (panels, rounded boxes read as boxes)
|
|
25
|
+
cube: "box",
|
|
26
|
+
box: "box",
|
|
27
|
+
rounded_panel: "box",
|
|
28
|
+
rounded_box: "box",
|
|
29
|
+
panel: "box",
|
|
30
|
+
card: "box",
|
|
31
|
+
// sphere family
|
|
32
|
+
sphere: "sphere",
|
|
33
|
+
orb: "sphere",
|
|
34
|
+
ball: "sphere",
|
|
35
|
+
bubble: "sphere",
|
|
36
|
+
procedural_upper_body: "sphere",
|
|
37
|
+
avatar: "sphere",
|
|
38
|
+
// plane / surface family
|
|
39
|
+
plane: "plane",
|
|
40
|
+
quad: "plane",
|
|
41
|
+
surface: "plane",
|
|
42
|
+
full_screen_fluid_plane: "plane",
|
|
43
|
+
fluid_plane: "plane",
|
|
44
|
+
water: "plane",
|
|
45
|
+
// cylinder family
|
|
46
|
+
cylinder: "cylinder",
|
|
47
|
+
tube: "cylinder",
|
|
48
|
+
pillar: "cylinder",
|
|
49
|
+
capsule: "cylinder",
|
|
50
|
+
// cone family
|
|
51
|
+
cone: "cone",
|
|
52
|
+
pyramid: "cone",
|
|
53
|
+
spike: "cone",
|
|
54
|
+
crystal: "cone",
|
|
55
|
+
diamond: "cone",
|
|
56
|
+
gem: "cone",
|
|
57
|
+
// torus family
|
|
58
|
+
torus: "torus",
|
|
59
|
+
ring: "torus",
|
|
60
|
+
donut: "torus"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// src/compiler/render-modules/skybox-registry.ts
|
|
66
|
+
function hexToRgb(hex) {
|
|
67
|
+
const h = hex.replace("#", "");
|
|
68
|
+
return [
|
|
69
|
+
Number((parseInt(h.substring(0, 2), 16) / 255).toFixed(4)),
|
|
70
|
+
Number((parseInt(h.substring(2, 4), 16) / 255).toFixed(4)),
|
|
71
|
+
Number((parseInt(h.substring(4, 6), 16) / 255).toFixed(4))
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
function resolveSkyboxColor(name) {
|
|
75
|
+
const key = String(name ?? "").trim().toLowerCase();
|
|
76
|
+
const hex = SKYBOX_HEX[key];
|
|
77
|
+
return hex ? hexToRgb(hex) : FALLBACK;
|
|
78
|
+
}
|
|
79
|
+
var SKYBOX_HEX, FALLBACK;
|
|
80
|
+
var init_skybox_registry = __esm({
|
|
81
|
+
"src/compiler/render-modules/skybox-registry.ts"() {
|
|
82
|
+
SKYBOX_HEX = {
|
|
83
|
+
// ── deep / space / void ─────────────────────────────────────────────
|
|
84
|
+
deep_machine_ocean: "#052430",
|
|
85
|
+
deep_space: "#05060d",
|
|
86
|
+
space: "#05060d",
|
|
87
|
+
nebula: "#0b0618",
|
|
88
|
+
night_sky: "#0b1020",
|
|
89
|
+
night_overcast: "#14161c",
|
|
90
|
+
void: "#050507",
|
|
91
|
+
midnight: "#070a12",
|
|
92
|
+
// ── water / underwater ──────────────────────────────────────────────
|
|
93
|
+
ocean: "#063240",
|
|
94
|
+
underwater: "#062a3a",
|
|
95
|
+
// ── studio / neutral (light backdrops kept light, just not pure white) ─
|
|
96
|
+
studio: "#c4c8cf",
|
|
97
|
+
neutral_studio: "#c4c8cf",
|
|
98
|
+
studio_soft: "#d2d5da",
|
|
99
|
+
hdr_studio: "#c9ccd2",
|
|
100
|
+
gallery: "#20222a",
|
|
101
|
+
indoor_museum: "#1c1e24",
|
|
102
|
+
interior_dark: "#14151a",
|
|
103
|
+
classroom: "#d2cfc6",
|
|
104
|
+
warehouse: "#3a3c40",
|
|
105
|
+
asset_pipeline_warehouse: "#3a3c40",
|
|
106
|
+
// ── sky / daylight ──────────────────────────────────────────────────
|
|
107
|
+
procedural_sky: "#8fbce6",
|
|
108
|
+
clear_sky: "#8fbce6",
|
|
109
|
+
daylight: "#9ec6ea",
|
|
110
|
+
fantasy_sky: "#2a3a6a",
|
|
111
|
+
sunset: "#46283a",
|
|
112
|
+
dusk: "#2e2740",
|
|
113
|
+
desert_dawn: "#caa079",
|
|
114
|
+
// ── tech / abstract ─────────────────────────────────────────────────
|
|
115
|
+
digital_grid: "#0a0f1e",
|
|
116
|
+
gradient: "#1a2030",
|
|
117
|
+
// ── environments / scenes ───────────────────────────────────────────
|
|
118
|
+
industrial: "#2a2d30",
|
|
119
|
+
urban_courtyard: "#3a3d42",
|
|
120
|
+
ancient_ruins: "#3a3428",
|
|
121
|
+
concert_venue: "#0e0a14",
|
|
122
|
+
lounge: "#1a1220",
|
|
123
|
+
farm: "#7fae5a",
|
|
124
|
+
vault_archive: "#1a140a",
|
|
125
|
+
passthrough: "#0c0c0e",
|
|
126
|
+
default: "#0a0e14"
|
|
127
|
+
};
|
|
128
|
+
FALLBACK = hexToRgb("#0a0e14");
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// src/compiler/render-modules/geometry-purpose.ts
|
|
133
|
+
function coerceBool(v) {
|
|
134
|
+
if (v === true || v === "true") return true;
|
|
135
|
+
if (v === false || v === "false") return false;
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
function resolveGeometryRole(opts) {
|
|
139
|
+
let purpose = "render";
|
|
140
|
+
const p = String(opts.purpose ?? "").trim().toLowerCase();
|
|
141
|
+
if (p && ALL_PURPOSES.has(p)) {
|
|
142
|
+
purpose = p;
|
|
143
|
+
} else {
|
|
144
|
+
for (const t of opts.traitNames ?? []) {
|
|
145
|
+
const mapped = PURPOSE_TRAITS[String(t).trim().toLowerCase()];
|
|
146
|
+
if (mapped) {
|
|
147
|
+
purpose = mapped;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const explicit = coerceBool(opts.visible);
|
|
153
|
+
const visible = explicit !== void 0 ? explicit : purpose === "render";
|
|
154
|
+
return { purpose, visible };
|
|
155
|
+
}
|
|
156
|
+
var INVISIBLE_PURPOSES, PURPOSE_TRAITS, ALL_PURPOSES;
|
|
157
|
+
var init_geometry_purpose = __esm({
|
|
158
|
+
"src/compiler/render-modules/geometry-purpose.ts"() {
|
|
159
|
+
INVISIBLE_PURPOSES = /* @__PURE__ */ new Set([
|
|
160
|
+
"collision",
|
|
161
|
+
"trigger",
|
|
162
|
+
"vfx_emitter",
|
|
163
|
+
"sim_domain",
|
|
164
|
+
"navmesh",
|
|
165
|
+
"occluder",
|
|
166
|
+
"audio_zone"
|
|
167
|
+
]);
|
|
168
|
+
PURPOSE_TRAITS = {
|
|
169
|
+
collider: "collision",
|
|
170
|
+
collision: "collision",
|
|
171
|
+
physics_collider: "collision",
|
|
172
|
+
rigidbody_collider: "collision",
|
|
173
|
+
trigger: "trigger",
|
|
174
|
+
trigger_volume: "trigger",
|
|
175
|
+
interaction_volume: "trigger",
|
|
176
|
+
emitter: "vfx_emitter",
|
|
177
|
+
vfx_emitter: "vfx_emitter",
|
|
178
|
+
particle_emitter: "vfx_emitter",
|
|
179
|
+
spawn_volume: "vfx_emitter",
|
|
180
|
+
sim_domain: "sim_domain",
|
|
181
|
+
sim_volume: "sim_domain",
|
|
182
|
+
fluid_domain: "sim_domain",
|
|
183
|
+
field_region: "sim_domain",
|
|
184
|
+
navmesh: "navmesh",
|
|
185
|
+
nav_mesh: "navmesh",
|
|
186
|
+
occluder: "occluder",
|
|
187
|
+
occlusion: "occluder",
|
|
188
|
+
audio_zone: "audio_zone",
|
|
189
|
+
audio_volume: "audio_zone"
|
|
190
|
+
};
|
|
191
|
+
ALL_PURPOSES = /* @__PURE__ */ new Set([
|
|
192
|
+
"render",
|
|
193
|
+
...Array.from(INVISIBLE_PURPOSES)
|
|
194
|
+
]);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
6
198
|
// src/compiler/WebGPUCompiler.ts
|
|
7
199
|
var WebGPUCompiler_exports = {};
|
|
8
200
|
__export(WebGPUCompiler_exports, {
|
|
@@ -18,6 +210,9 @@ var WebGPUCompiler;
|
|
|
18
210
|
var init_WebGPUCompiler = __esm({
|
|
19
211
|
"src/compiler/WebGPUCompiler.ts"() {
|
|
20
212
|
init_CompilerBase();
|
|
213
|
+
init_geometry_registry();
|
|
214
|
+
init_skybox_registry();
|
|
215
|
+
init_geometry_purpose();
|
|
21
216
|
init_DomainBlockCompilerMixin();
|
|
22
217
|
WebGPUCompiler = class extends CompilerBase {
|
|
23
218
|
constructor(options = {}) {
|
|
@@ -26,6 +221,23 @@ var init_WebGPUCompiler = __esm({
|
|
|
26
221
|
this.lines = [];
|
|
27
222
|
this.indentLevel = 0;
|
|
28
223
|
this.objectIndex = 0;
|
|
224
|
+
// Resolved once per compile() so findObjProp can fall back to template defaults.
|
|
225
|
+
this.templatesByName = /* @__PURE__ */ new Map();
|
|
226
|
+
// True when any object is a water/fluid surface — gates the animated water
|
|
227
|
+
// pipeline, its WGSL, the tessellated-plane helper, and the shared time uniform.
|
|
228
|
+
this.hasWater = false;
|
|
229
|
+
/** World facts collected at emission time, serialized as the HoloScene Manifest. */
|
|
230
|
+
this.manifestObjects = [];
|
|
231
|
+
/** Accumulated spatial-group translation for the objects currently being
|
|
232
|
+
* emitted — manifest positions are WORLD-space (render applies the same
|
|
233
|
+
* offsets via GroupXform; local coords would be a false fact for grouped
|
|
234
|
+
* scenes). */
|
|
235
|
+
this.manifestGroupOffset = [0, 0, 0];
|
|
236
|
+
// The named channels the water surface's clarity/ripple are bound to (from the
|
|
237
|
+
// .holo's water_clarity_binding / ripple_intensity_binding props). Host state
|
|
238
|
+
// (e.g. Brittney receipts) drives these BY NAME via hsSetChannel.
|
|
239
|
+
this.waterClarityChannel = "environmentWaterClarity";
|
|
240
|
+
this.waterRippleChannel = "environmentRippleIntensity";
|
|
29
241
|
this.options = {
|
|
30
242
|
entryPoint: options.entryPoint || "main",
|
|
31
243
|
enableCompute: options.enableCompute ?? true,
|
|
@@ -42,6 +254,13 @@ var init_WebGPUCompiler = __esm({
|
|
|
42
254
|
this.lines = [];
|
|
43
255
|
this.indentLevel = 0;
|
|
44
256
|
this.objectIndex = 0;
|
|
257
|
+
this.manifestObjects = [];
|
|
258
|
+
this.manifestGroupOffset = [0, 0, 0];
|
|
259
|
+
this.templatesByName = new Map(
|
|
260
|
+
(composition.templates ?? []).map((t) => [t.name, t])
|
|
261
|
+
);
|
|
262
|
+
this.hasWater = this.compositionHasWater(composition);
|
|
263
|
+
if (this.hasWater) this.resolveWaterChannels(composition);
|
|
45
264
|
this.emit("// Auto-generated by HoloScript WebGPUCompiler");
|
|
46
265
|
this.emit(
|
|
47
266
|
`// Source: composition "${this.escapeStringValue(composition.name, "TypeScript")}"`
|
|
@@ -55,7 +274,27 @@ var init_WebGPUCompiler = __esm({
|
|
|
55
274
|
this.emitGeometryHelpers();
|
|
56
275
|
this.emitShaderSources();
|
|
57
276
|
this.emitEnvironment(composition.environment ?? { type: "Environment", properties: [] });
|
|
58
|
-
this.emitCamera(composition
|
|
277
|
+
this.emitCamera(composition);
|
|
278
|
+
this.emitSceneLights(composition);
|
|
279
|
+
if (this.hasWater) {
|
|
280
|
+
const cc = JSON.stringify(this.waterClarityChannel);
|
|
281
|
+
const rc = JSON.stringify(this.waterRippleChannel);
|
|
282
|
+
this.emit(`const hsChannels = { [${cc}]: 1.0, [${rc}]: 0.0 };`);
|
|
283
|
+
this.emit("let hsWaterAmp = 1.0;");
|
|
284
|
+
this.emit(
|
|
285
|
+
"function hsSetChannel(name, value) { if (name in hsChannels) hsChannels[name] = Math.max(0, Math.min(1, value)); return hsChannels[name]; }"
|
|
286
|
+
);
|
|
287
|
+
this.emit(
|
|
288
|
+
`function hsSetWater(o) { if (o) { if (o.clarity !== undefined) hsSetChannel(${cc}, o.clarity); if (o.ripple !== undefined) hsSetChannel(${rc}, o.ripple); if (o.amplitude !== undefined) hsWaterAmp = Math.max(0, o.amplitude); } return { clarity: hsChannels[${cc}], ripple: hsChannels[${rc}], amplitude: hsWaterAmp }; }`
|
|
289
|
+
);
|
|
290
|
+
this.emit(
|
|
291
|
+
'if (typeof window !== "undefined") { window.hsSetChannel = hsSetChannel; window.hsSetWater = hsSetWater; }'
|
|
292
|
+
);
|
|
293
|
+
this.emit(
|
|
294
|
+
`const waterFrameU = createBuffer(device, new Float32Array([0,0,0,0, hsChannels[${cc}], hsChannels[${rc}], hsWaterAmp, 0]), GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST);`
|
|
295
|
+
);
|
|
296
|
+
this.emit("");
|
|
297
|
+
}
|
|
59
298
|
this.emit("const holoGraphObjects = [];");
|
|
60
299
|
this.emit("");
|
|
61
300
|
if (composition.objects) {
|
|
@@ -64,9 +303,7 @@ var init_WebGPUCompiler = __esm({
|
|
|
64
303
|
if (composition.spatialGroups) {
|
|
65
304
|
for (const group of composition.spatialGroups) this.emitGroup(group);
|
|
66
305
|
}
|
|
67
|
-
|
|
68
|
-
for (const light of composition.lights) this.emitLight(light);
|
|
69
|
-
}
|
|
306
|
+
this.emitSceneManifest(composition);
|
|
70
307
|
if (this.options.enableCompute) this.emitComputeShaders(composition);
|
|
71
308
|
this.emitWebGPUDomainBlocks(composition);
|
|
72
309
|
this.emitViewportRuntime();
|
|
@@ -350,6 +587,18 @@ var init_WebGPUCompiler = __esm({
|
|
|
350
587
|
this.emit("return new Float32Array([-x,0,-z,0,1,0, x,0,-z,0,1,0, x,0,z,0,1,0, -x,0,-z,0,1,0, x,0,z,0,1,0, -x,0,z,0,1,0]);");
|
|
351
588
|
this.dedent();
|
|
352
589
|
this.emit("}");
|
|
590
|
+
if (this.hasWater) {
|
|
591
|
+
this.emit("function generateWaterPlaneVertices(w, h, n) {");
|
|
592
|
+
this.indent();
|
|
593
|
+
this.emit("const out = [], hw = w/2, hh = h/2, N = Math.max(2, n|0);");
|
|
594
|
+
this.emit("for (let iy=0; iy<N; iy++) { for (let ix=0; ix<N; ix++) {");
|
|
595
|
+
this.emit(" const x0=-hw+w*ix/N, x1=-hw+w*(ix+1)/N, y0=-hh+h*iy/N, y1=-hh+h*(iy+1)/N;");
|
|
596
|
+
this.emit(" const a=[x0,y0,0,0,0,1], b=[x1,y0,0,0,0,1], c=[x1,y1,0,0,0,1], d=[x0,y1,0,0,0,1];");
|
|
597
|
+
this.emit(" out.push(...a,...b,...c, ...a,...c,...d);");
|
|
598
|
+
this.emit("} } return new Float32Array(out);");
|
|
599
|
+
this.dedent();
|
|
600
|
+
this.emit("}");
|
|
601
|
+
}
|
|
353
602
|
this.emit("function generateSphereVertices(radius, lat, lon) {");
|
|
354
603
|
this.indent();
|
|
355
604
|
this.emit("const rings = Math.max(3, lat | 0), segs = Math.max(3, lon | 0); const out = [];");
|
|
@@ -415,7 +664,7 @@ var init_WebGPUCompiler = __esm({
|
|
|
415
664
|
const envData = {};
|
|
416
665
|
for (const prop of env.properties) envData[prop.key] = prop.value;
|
|
417
666
|
const bg = envData.background || envData.skybox || "#000000";
|
|
418
|
-
const bgColor = this.parseColor(bg);
|
|
667
|
+
const bgColor = typeof bg === "string" && bg.startsWith("#") || Array.isArray(bg) ? this.parseColor(bg) : resolveSkyboxColor(bg);
|
|
419
668
|
this.emit(
|
|
420
669
|
`const clearColor: GPUColor = { r: ${bgColor[0]}, g: ${bgColor[1]}, b: ${bgColor[2]}, a: 1.0 };`
|
|
421
670
|
);
|
|
@@ -442,6 +691,41 @@ var init_WebGPUCompiler = __esm({
|
|
|
442
691
|
const isModel = meshType === "model" || !!modelSrc;
|
|
443
692
|
const traits = obj.traits || [];
|
|
444
693
|
this.emit(`// Object: ${this.escapeStringValue(obj.name, "TypeScript")}`);
|
|
694
|
+
const role = resolveGeometryRole({
|
|
695
|
+
purpose: this.findObjProp(obj, "purpose"),
|
|
696
|
+
visible: this.findObjProp(obj, "visible"),
|
|
697
|
+
traitNames: traits.map((t) => t.name)
|
|
698
|
+
});
|
|
699
|
+
if (!role.visible) {
|
|
700
|
+
const prim = resolveGeometry(meshType).kind;
|
|
701
|
+
const pos = this.findObjProp(obj, "position");
|
|
702
|
+
const [px, py, pz] = Array.isArray(pos) ? pos : [0, 0, 0];
|
|
703
|
+
this.emit(
|
|
704
|
+
`// Functional geometry \u2014 purpose="${role.purpose}", not rendered; primitive="${prim}" is shared with physics/vfx/sim consumers.`
|
|
705
|
+
);
|
|
706
|
+
this.emit(
|
|
707
|
+
`holoGraphObjects.push({ id: "${this.escapeStringValue(obj.name, "TypeScript")}", name: "${this.escapeStringValue(obj.name, "TypeScript")}", geometry: "${this.escapeStringValue(meshType, "TypeScript")}", primitive: "${prim}", purpose: "${role.purpose}", visible: false, traits: ${this.json(traits.map((t) => t.name))}, basePosition: [${px},${py},${pz}], position: [${px},${py},${pz}] });`
|
|
708
|
+
);
|
|
709
|
+
this.manifestObjects.push({
|
|
710
|
+
id: String(obj.name ?? ""),
|
|
711
|
+
geometry: meshType,
|
|
712
|
+
primitive: prim,
|
|
713
|
+
purpose: role.purpose,
|
|
714
|
+
visible: false,
|
|
715
|
+
traits: traits.map((t) => t.name),
|
|
716
|
+
affordances: this.extractAffordanceNames(obj),
|
|
717
|
+
position: [
|
|
718
|
+
px + this.manifestGroupOffset[0],
|
|
719
|
+
py + this.manifestGroupOffset[1],
|
|
720
|
+
pz + this.manifestGroupOffset[2]
|
|
721
|
+
]
|
|
722
|
+
});
|
|
723
|
+
this.emit("");
|
|
724
|
+
if (obj.children) {
|
|
725
|
+
for (const child of obj.children) this.emitObject(child);
|
|
726
|
+
}
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
445
729
|
const isGaussianSplat = traits.some((t) => t.name === "gaussian_splat");
|
|
446
730
|
const isPointCloud = traits.some((t) => t.name === "point_cloud");
|
|
447
731
|
const isGpuParticle = traits.some((t) => isGpuParticleTraitName(t.name));
|
|
@@ -460,6 +744,8 @@ var init_WebGPUCompiler = __esm({
|
|
|
460
744
|
this.emitPointCloudObject(v, obj);
|
|
461
745
|
} else if (isGpuParticle) {
|
|
462
746
|
this.emitGpuParticleObject(v, obj);
|
|
747
|
+
} else if (this.isWaterObject(obj)) {
|
|
748
|
+
this.emitWaterObject(v, obj);
|
|
463
749
|
} else {
|
|
464
750
|
this.emitMeshObject(v, obj, meshType);
|
|
465
751
|
}
|
|
@@ -477,6 +763,203 @@ var init_WebGPUCompiler = __esm({
|
|
|
477
763
|
for (const child of obj.children) this.emitObject(child);
|
|
478
764
|
}
|
|
479
765
|
}
|
|
766
|
+
// ─── Water / fluid surfaces ────────────────────────────────────────────────
|
|
767
|
+
/**
|
|
768
|
+
* Resolve which named channels the water surface's clarity/ripple are bound to,
|
|
769
|
+
* from the first water object's `water_clarity_binding` / `ripple_intensity_binding`
|
|
770
|
+
* props. These names match the .holo's environment state fields, so host state
|
|
771
|
+
* (Brittney receipts) driving those fields drives the water by name.
|
|
772
|
+
*/
|
|
773
|
+
resolveWaterChannels(composition) {
|
|
774
|
+
const all = [...composition.objects ?? []];
|
|
775
|
+
for (const g of composition.spatialGroups ?? []) all.push(...g.objects ?? []);
|
|
776
|
+
const water = all.find((o) => this.isWaterObject(o));
|
|
777
|
+
if (!water) return;
|
|
778
|
+
const clarity = this.findObjProp(water, "water_clarity_binding");
|
|
779
|
+
const ripple = this.findObjProp(water, "ripple_intensity_binding");
|
|
780
|
+
if (typeof clarity === "string" && clarity.trim()) this.waterClarityChannel = clarity.trim();
|
|
781
|
+
if (typeof ripple === "string" && ripple.trim()) this.waterRippleChannel = ripple.trim();
|
|
782
|
+
}
|
|
783
|
+
/** True if any object (incl. nested groups/children) is a water/fluid surface. */
|
|
784
|
+
compositionHasWater(composition) {
|
|
785
|
+
const scan = (objs) => (objs ?? []).some((o) => this.isWaterObject(o) || scan(o.children));
|
|
786
|
+
if (scan(composition.objects)) return true;
|
|
787
|
+
const walk = (g) => scan(g.objects) || (g.groups ?? []).some(walk);
|
|
788
|
+
return (composition.spatialGroups ?? []).some(walk);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* A water surface is a fluid-plane mesh (full_screen_fluid_plane / water / ...),
|
|
792
|
+
* or a water-ish MATERIAL on a plane-like geometry. It is NOT a water material on
|
|
793
|
+
* a sphere — emitWaterObject always renders a tessellated plane.
|
|
794
|
+
*/
|
|
795
|
+
isWaterObject(obj) {
|
|
796
|
+
const mesh = String(this.findObjProp(obj, "mesh") ?? this.findObjProp(obj, "geometry") ?? "").trim().toLowerCase();
|
|
797
|
+
const material = String(this.findObjProp(obj, "material") ?? "").trim().toLowerCase();
|
|
798
|
+
const WATER_MESH = ["full_screen_fluid_plane", "fluid_plane", "water", "water_plane", "ocean"];
|
|
799
|
+
const WATER_MAT = ["liquid_glass", "water", "liquid", "fluid"];
|
|
800
|
+
if (WATER_MESH.includes(mesh)) return true;
|
|
801
|
+
return WATER_MAT.includes(material) && resolveGeometry(mesh).kind === "plane";
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Emit an animated water surface: a tessellated plane driven by the shared
|
|
805
|
+
* per-frame time uniform through the WGSL_WATER shader (Gerstner-lite height
|
|
806
|
+
* waves + analytic normals + texture-free caustics + fresnel). Binding layout
|
|
807
|
+
* mirrors the mesh path (0=Model, 2=vpUniform) plus 1=WaterStatic, 3=waterFrameU.
|
|
808
|
+
*/
|
|
809
|
+
emitWaterObject(v, obj) {
|
|
810
|
+
const pos = this.findObjProp(obj, "position");
|
|
811
|
+
const scale = this.findObjProp(obj, "scale") || this.findObjProp(obj, "size");
|
|
812
|
+
const [px, py, pz] = Array.isArray(pos) ? pos : [0, 0, 0];
|
|
813
|
+
const [sx, sy, sz] = Array.isArray(scale) ? scale : typeof scale === "number" ? [scale, scale, scale] : [1, 1, 1];
|
|
814
|
+
const deep = this.parseColor(this.findObjProp(obj, "color") ?? "#06131f");
|
|
815
|
+
const shallow = [
|
|
816
|
+
Math.min(1, deep[0] * 2.2 + 0.05),
|
|
817
|
+
Math.min(1, deep[1] * 2.2 + 0.2),
|
|
818
|
+
Math.min(1, deep[2] * 2 + 0.24)
|
|
819
|
+
];
|
|
820
|
+
const amplitude = 0.18, choppiness = 0.4, clarity = 1, ripple = 0;
|
|
821
|
+
const L = [0.3714, 0.7428, 0.5571];
|
|
822
|
+
this.emit(`// Water surface: ${this.escapeStringValue(obj.name, "TypeScript")}`);
|
|
823
|
+
this.emit(`const ${v}Vertices = generateWaterPlaneVertices(1.0, 1.0, 48);`);
|
|
824
|
+
this.emit(`const ${v}VBO = createBuffer(device, ${v}Vertices, GPUBufferUsage.VERTEX);`);
|
|
825
|
+
this.emit(`const ${v}VertexCount = ${v}Vertices.length / 6;`);
|
|
826
|
+
this.emit(
|
|
827
|
+
`const ${v}Model = createBuffer(device, new Float32Array([${sx},0,0,0, 0,${sy},0,0, 0,0,${sz},0, ${px},${py},${pz},1]), GPUBufferUsage.UNIFORM);`
|
|
828
|
+
);
|
|
829
|
+
this.emit(
|
|
830
|
+
`const ${v}WaterStatic = createBuffer(device, new Float32Array([${amplitude},${choppiness},${clarity},${ripple}, ${deep[0]},${deep[1]},${deep[2]},0, ${shallow[0]},${shallow[1]},${shallow[2]},0, ${L[0]},${L[1]},${L[2]},0]), GPUBufferUsage.UNIFORM);`
|
|
831
|
+
);
|
|
832
|
+
this.emit(`const ${v}WaterPipeline = device.createRenderPipeline({`);
|
|
833
|
+
this.indent();
|
|
834
|
+
this.emit('layout: "auto",');
|
|
835
|
+
this.emit(
|
|
836
|
+
'vertex: { module: device.createShaderModule({ code: WGSL_WATER }), entryPoint: "vs_water",'
|
|
837
|
+
);
|
|
838
|
+
this.emit(
|
|
839
|
+
' buffers: [{ arrayStride: 24, attributes: [{ shaderLocation: 0, offset: 0, format: "float32x3" }, { shaderLocation: 1, offset: 12, format: "float32x3" }] }] },'
|
|
840
|
+
);
|
|
841
|
+
this.emit(
|
|
842
|
+
'fragment: { module: device.createShaderModule({ code: WGSL_WATER }), entryPoint: "fs_water", targets: [{ format }] },'
|
|
843
|
+
);
|
|
844
|
+
this.emit('primitive: { topology: "triangle-list", cullMode: "none" },');
|
|
845
|
+
this.emit(
|
|
846
|
+
`depthStencil: { format: "depth24plus", depthWriteEnabled: true, depthCompare: "less" }, multisample: { count: MSAA_COUNT },`
|
|
847
|
+
);
|
|
848
|
+
this.dedent();
|
|
849
|
+
this.emit("});");
|
|
850
|
+
this.emit(
|
|
851
|
+
`const ${v}WaterBind = device.createBindGroup({ layout: ${v}WaterPipeline.getBindGroupLayout(0), entries: [`
|
|
852
|
+
);
|
|
853
|
+
this.emit(
|
|
854
|
+
` { binding: 0, resource: { buffer: ${v}Model } }, { binding: 1, resource: { buffer: ${v}WaterStatic } }, { binding: 2, resource: { buffer: vpUniform } }, { binding: 3, resource: { buffer: waterFrameU } }] });`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
/** Emit the shared WGSL_WATER module (vs_water + fs_water + noise helpers). */
|
|
858
|
+
emitWaterShaderSource() {
|
|
859
|
+
this.emit("const WGSL_WATER = /* wgsl */`");
|
|
860
|
+
this.emit("struct Model { m: mat4x4<f32> };");
|
|
861
|
+
this.emit("struct WStatic { p: vec4<f32>, deep: vec4<f32>, shallow: vec4<f32>, lightDir: vec4<f32> };");
|
|
862
|
+
this.emit("struct Cam { viewProj: mat4x4<f32> };");
|
|
863
|
+
this.emit("struct WFrame { tc: vec4<f32>, dyn: vec4<f32> };");
|
|
864
|
+
this.emit("@group(0) @binding(0) var<uniform> model: Model;");
|
|
865
|
+
this.emit("@group(0) @binding(1) var<uniform> ws: WStatic;");
|
|
866
|
+
this.emit("@group(0) @binding(2) var<uniform> cam: Cam;");
|
|
867
|
+
this.emit("@group(0) @binding(3) var<uniform> wf: WFrame;");
|
|
868
|
+
this.emit("struct VIn { @location(0) pos: vec3<f32>, @location(1) norm: vec3<f32> };");
|
|
869
|
+
this.emit("struct VOut { @builtin(position) clip: vec4<f32>, @location(0) wNorm: vec3<f32>, @location(1) wPos: vec3<f32> };");
|
|
870
|
+
this.emit("@vertex fn vs_water(in: VIn) -> VOut {");
|
|
871
|
+
this.emit(" var o: VOut;");
|
|
872
|
+
this.emit(" let sx = length(model.m[0].xyz); let sy = length(model.m[1].xyz); let sz = max(length(model.m[2].xyz), 1e-4);");
|
|
873
|
+
this.emit(" let wp = in.pos.xy * vec2<f32>(sx, sy);");
|
|
874
|
+
this.emit(" let t = wf.tc.x; let amp = ws.p.x * wf.dyn.z * (0.4 + wf.dyn.y);");
|
|
875
|
+
this.emit(" var h = 0.0; var dhdx = 0.0; var dhdy = 0.0;");
|
|
876
|
+
this.emit(" var dx = 0.0; var dy = 0.0; var k = 0.0; var spd = 0.0; var a = 0.0;");
|
|
877
|
+
this.emit(" for (var i = 0; i < 4; i = i + 1) {");
|
|
878
|
+
this.emit(" if (i == 0) { dx = 1.0; dy = 0.0; k = 0.7; spd = 0.6; a = 1.0; }");
|
|
879
|
+
this.emit(" else if (i == 1) { dx = 0.5; dy = 0.86; k = 1.2; spd = 0.9; a = 0.55; }");
|
|
880
|
+
this.emit(" else if (i == 2) { dx = -0.8; dy = 0.6; k = 1.9; spd = 1.15; a = 0.3; }");
|
|
881
|
+
this.emit(" else { dx = 0.2; dy = -0.98; k = 2.7; spd = 1.4; a = 0.18; }");
|
|
882
|
+
this.emit(" let d = normalize(vec2<f32>(dx, dy));");
|
|
883
|
+
this.emit(" let ph = k * dot(d, wp) + spd * t;");
|
|
884
|
+
this.emit(" h = h + a * amp * sin(ph);");
|
|
885
|
+
this.emit(" dhdx = dhdx + a * amp * k * d.x * cos(ph);");
|
|
886
|
+
this.emit(" dhdy = dhdy + a * amp * k * d.y * cos(ph);");
|
|
887
|
+
this.emit(" }");
|
|
888
|
+
this.emit(" var lp = in.pos; lp.z = lp.z + h / sz;");
|
|
889
|
+
this.emit(" let world = model.m * vec4<f32>(lp, 1.0);");
|
|
890
|
+
this.emit(" o.wPos = world.xyz;");
|
|
891
|
+
this.emit(" o.wNorm = normalize(vec3<f32>(-dhdx, -dhdy, 1.0));");
|
|
892
|
+
this.emit(" o.clip = cam.viewProj * world;");
|
|
893
|
+
this.emit(" return o;");
|
|
894
|
+
this.emit("}");
|
|
895
|
+
this.emit("fn hash21(p: vec2<f32>) -> f32 { let h = dot(p, vec2<f32>(127.1, 311.7)); return fract(sin(h) * 43758.5453); }");
|
|
896
|
+
this.emit("fn vnoise(p: vec2<f32>) -> f32 {");
|
|
897
|
+
this.emit(" let i = floor(p); let f = fract(p); let u = f * f * (3.0 - 2.0 * f);");
|
|
898
|
+
this.emit(" let a = hash21(i); let b = hash21(i + vec2<f32>(1.0, 0.0));");
|
|
899
|
+
this.emit(" let c = hash21(i + vec2<f32>(0.0, 1.0)); let dd = hash21(i + vec2<f32>(1.0, 1.0));");
|
|
900
|
+
this.emit(" return mix(mix(a, b, u.x), mix(c, dd, u.x), u.y);");
|
|
901
|
+
this.emit("}");
|
|
902
|
+
this.emit("struct FIn { @location(0) wNorm: vec3<f32>, @location(1) wPos: vec3<f32> };");
|
|
903
|
+
this.emit("@fragment fn fs_water(in: FIn) -> @location(0) vec4<f32> {");
|
|
904
|
+
this.emit(" let N = normalize(in.wNorm);");
|
|
905
|
+
this.emit(" let V = normalize(wf.tc.yzw - in.wPos);");
|
|
906
|
+
this.emit(" let L = normalize(ws.lightDir.xyz);");
|
|
907
|
+
this.emit(" let t = wf.tc.x;");
|
|
908
|
+
this.emit(" let ndv = max(dot(N, V), 0.0);");
|
|
909
|
+
this.emit(" let fres = 0.02 + 0.98 * pow(1.0 - ndv, 5.0);");
|
|
910
|
+
this.emit(" let tilt = clamp(1.0 - ndv, 0.0, 1.0);");
|
|
911
|
+
this.emit(" var col = mix(ws.deep.rgb, ws.shallow.rgb, clamp(tilt * 1.3, 0.0, 1.0));");
|
|
912
|
+
this.emit(" let p = in.wPos.xy;");
|
|
913
|
+
this.emit(" let n1 = vnoise(p * 0.9 + vec2<f32>(t * 0.15, t * 0.08));");
|
|
914
|
+
this.emit(" let n2 = vnoise(p * 1.7 - vec2<f32>(t * 0.11, t * 0.19));");
|
|
915
|
+
this.emit(" let caustic = pow(n1 * n2, 3.0);");
|
|
916
|
+
this.emit(" col = col + vec3<f32>(0.10, 0.18, 0.20) * caustic * wf.dyn.x;");
|
|
917
|
+
this.emit(" let H = normalize(L + V);");
|
|
918
|
+
this.emit(" let spec = pow(max(dot(N, H), 0.0), 200.0);");
|
|
919
|
+
this.emit(" col = col + vec3<f32>(0.85, 0.92, 1.0) * spec;");
|
|
920
|
+
this.emit(" let sky = vec3<f32>(0.10, 0.26, 0.36);");
|
|
921
|
+
this.emit(" col = mix(col, sky, fres * 0.3);");
|
|
922
|
+
this.emit(" return vec4<f32>(col, 1.0);");
|
|
923
|
+
this.emit("}`;");
|
|
924
|
+
this.emit("");
|
|
925
|
+
}
|
|
926
|
+
/** Tool-affordance names declared on an object's @tool traits (both trait
|
|
927
|
+
* config formats), for the scene manifest — the artifact-level answer to
|
|
928
|
+
* "which named actions does this entity offer" that the numeric registry
|
|
929
|
+
* cannot express. */
|
|
930
|
+
extractAffordanceNames(obj) {
|
|
931
|
+
const names = [];
|
|
932
|
+
for (const trait of obj.traits ?? []) {
|
|
933
|
+
const t = trait;
|
|
934
|
+
if (t.name !== "tool") continue;
|
|
935
|
+
const config = "config" in t && typeof t.config === "object" && t.config !== null ? t.config : t;
|
|
936
|
+
if (typeof config["name"] === "string" && config["name"] !== "tool") {
|
|
937
|
+
names.push(config["name"]);
|
|
938
|
+
} else {
|
|
939
|
+
names.push("unnamed_tool");
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return names;
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* HoloScene Manifest — a machine-readable world-facts block INSIDE the
|
|
946
|
+
* delivered artifact (g9tk). Collected at EMISSION time (each emitObject/
|
|
947
|
+
* emitMeshObject records what it actually emitted), so the manifest describes
|
|
948
|
+
* the artifact, not the input. Strict JSON payload: independent consumers
|
|
949
|
+
* (perceiver derivations, tooling) JSON.parse it instead of regexing the
|
|
950
|
+
* imperative holoGraphObjects push literals.
|
|
951
|
+
*/
|
|
952
|
+
emitSceneManifest(composition) {
|
|
953
|
+
const manifest = {
|
|
954
|
+
version: "holo-scene-manifest-v1",
|
|
955
|
+
source: String(composition.name ?? ""),
|
|
956
|
+
objects: this.manifestObjects
|
|
957
|
+
};
|
|
958
|
+
this.emit("// === HoloScene Manifest (machine-readable world facts; part of the delivered artifact) ===");
|
|
959
|
+
this.emit(`const holoSceneManifest = ${this.json(manifest)};`);
|
|
960
|
+
this.emit('if (typeof globalThis !== "undefined") { globalThis.holoSceneManifest = holoSceneManifest; }');
|
|
961
|
+
this.emit("");
|
|
962
|
+
}
|
|
480
963
|
emitMeshObject(v, obj, meshType) {
|
|
481
964
|
this.emit(`const ${v}Vertices = ${this.geometryVertexDataFn(meshType)};`);
|
|
482
965
|
this.emit(`const ${v}VBO = createBuffer(device, ${v}Vertices, GPUBufferUsage.VERTEX);`);
|
|
@@ -493,14 +976,35 @@ var init_WebGPUCompiler = __esm({
|
|
|
493
976
|
const [cr, cg, cb] = this.extractMaterialColor(material, color);
|
|
494
977
|
const rough = this.extractMaterialProp(material, "roughness", 0.5);
|
|
495
978
|
const metal = this.extractMaterialProp(material, "metalness", 0);
|
|
496
|
-
|
|
497
|
-
|
|
979
|
+
let [er, eg, eb] = this.extractEmissive(material, obj);
|
|
980
|
+
let emissiveStrength = this.extractMaterialProp(material, "emissive_intensity", 1);
|
|
981
|
+
const glow = this.materialGlow(material);
|
|
982
|
+
if (glow > 0 && er === 0 && eg === 0 && eb === 0) {
|
|
983
|
+
er = cr;
|
|
984
|
+
eg = cg;
|
|
985
|
+
eb = cb;
|
|
986
|
+
emissiveStrength = glow;
|
|
987
|
+
}
|
|
498
988
|
this.emit(
|
|
499
989
|
`const ${v}Mat = createBuffer(device, new Float32Array([${cr},${cg},${cb},1.0, ${rough},${metal},${emissiveStrength},0, ${er},${eg},${eb},0]), GPUBufferUsage.UNIFORM);`
|
|
500
990
|
);
|
|
501
991
|
this.emit(
|
|
502
992
|
`holoGraphObjects.push({ id: "${this.escapeStringValue(obj.name, "TypeScript")}", name: "${this.escapeStringValue(obj.name, "TypeScript")}", geometry: "${this.escapeStringValue(meshType, "TypeScript")}", traits: ${this.json((obj.traits || []).map((t) => t.name))}, properties: ${this.json(this.extractObjectProperties(obj))}, basePosition: [${px},${py},${pz}], position: [${px},${py},${pz}], baseScale: [${sx},${sy},${sz}], scale: [${sx},${sy},${sz}], baseColor: [${cr},${cg},${cb}], color: [${cr},${cg},${cb}], material: { roughness: ${rough}, metalness: ${metal}, emissiveStrength: ${emissiveStrength}, emissive: [${er},${eg},${eb}] }, modelBuffer: ${v}Model, matBuffer: ${v}Mat, visible: true });`
|
|
503
993
|
);
|
|
994
|
+
this.manifestObjects.push({
|
|
995
|
+
id: String(obj.name ?? ""),
|
|
996
|
+
geometry: meshType,
|
|
997
|
+
visible: true,
|
|
998
|
+
traits: (obj.traits || []).map((t) => t.name),
|
|
999
|
+
affordances: this.extractAffordanceNames(obj),
|
|
1000
|
+
position: [
|
|
1001
|
+
px + this.manifestGroupOffset[0],
|
|
1002
|
+
py + this.manifestGroupOffset[1],
|
|
1003
|
+
pz + this.manifestGroupOffset[2]
|
|
1004
|
+
],
|
|
1005
|
+
scale: [sx, sy, sz],
|
|
1006
|
+
color: [cr, cg, cb]
|
|
1007
|
+
});
|
|
504
1008
|
this.emit(`const ${v}Pipeline = device.createRenderPipeline({`);
|
|
505
1009
|
this.indent();
|
|
506
1010
|
this.emit('layout: "auto",');
|
|
@@ -523,7 +1027,7 @@ var init_WebGPUCompiler = __esm({
|
|
|
523
1027
|
`const ${v}Bind = device.createBindGroup({ layout: ${v}Pipeline.getBindGroupLayout(0), entries: [`
|
|
524
1028
|
);
|
|
525
1029
|
this.emit(
|
|
526
|
-
` { binding: 0, resource: { buffer: ${v}Model } }, { binding: 1, resource: { buffer: ${v}Mat } }, { binding: 2, resource: { buffer: vpUniform } }] });`
|
|
1030
|
+
` { binding: 0, resource: { buffer: ${v}Model } }, { binding: 1, resource: { buffer: ${v}Mat } }, { binding: 2, resource: { buffer: vpUniform } }, { binding: 3, resource: { buffer: sceneLights } }] });`
|
|
527
1031
|
);
|
|
528
1032
|
}
|
|
529
1033
|
emitGaussianSplatObject(v, obj) {
|
|
@@ -630,43 +1134,140 @@ var init_WebGPUCompiler = __esm({
|
|
|
630
1134
|
this.emit(
|
|
631
1135
|
`const ${v}GroupXform = createBuffer(device, new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, ${px},${py},${pz},1]), GPUBufferUsage.UNIFORM);`
|
|
632
1136
|
);
|
|
1137
|
+
const saved = this.manifestGroupOffset;
|
|
1138
|
+
this.manifestGroupOffset = [saved[0] + px, saved[1] + py, saved[2] + pz];
|
|
633
1139
|
for (const obj of group.objects) this.emitObject(obj);
|
|
634
1140
|
if (group.groups) {
|
|
635
1141
|
for (const sub of group.groups) this.emitGroup(sub);
|
|
636
1142
|
}
|
|
1143
|
+
this.manifestGroupOffset = saved;
|
|
637
1144
|
this.emit("");
|
|
638
1145
|
}
|
|
639
1146
|
// ─── Light ────────────────────────────────────────────────────────────────
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
1147
|
+
// Aggregate the scene's declared lights into ONE shared uniform buffer that every
|
|
1148
|
+
// mesh binds (binding 3). Shape math — color*intensity, type tag, world position,
|
|
1149
|
+
// travel direction — is resolved here; the fragment shader consumes up to
|
|
1150
|
+
// MAX_LIGHTS. A light-less scene gets a synthesized key light so it stays lit.
|
|
1151
|
+
emitSceneLights(composition) {
|
|
1152
|
+
const MAX_LIGHTS = 4;
|
|
1153
|
+
const typeIndex = {
|
|
1154
|
+
directional: 0,
|
|
1155
|
+
point: 1,
|
|
1156
|
+
spot: 2,
|
|
1157
|
+
hemisphere: 3,
|
|
1158
|
+
ambient: 4,
|
|
1159
|
+
area: 5
|
|
1160
|
+
};
|
|
1161
|
+
const lights = (composition.lights ?? []).slice(0, MAX_LIGHTS).map((light) => {
|
|
1162
|
+
let color = [1, 1, 1], intensity = 1, pos = [0, 10, 0], dir = [0, -1, 0], hasDir = false;
|
|
1163
|
+
for (const prop of light.properties) {
|
|
1164
|
+
if (prop.key === "color") color = this.parseColor(prop.value);
|
|
1165
|
+
else if (prop.key === "intensity") intensity = prop.value;
|
|
1166
|
+
else if (prop.key === "position" && Array.isArray(prop.value)) pos = prop.value;
|
|
1167
|
+
else if (prop.key === "direction" && Array.isArray(prop.value)) {
|
|
1168
|
+
dir = prop.value;
|
|
1169
|
+
hasDir = true;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
const ti = typeIndex[light.lightType] ?? 0;
|
|
1173
|
+
if (ti === 0 && !hasDir && Array.isArray(pos)) dir = [-pos[0], -pos[1], -pos[2]];
|
|
1174
|
+
return { name: String(light.name ?? ""), kind: String(light.lightType ?? ""), color, intensity, pos, dir, ti };
|
|
1175
|
+
});
|
|
1176
|
+
if (lights.length === 0) {
|
|
1177
|
+
lights.push({ name: "(synthesized key light)", kind: "directional", color: [1, 1, 1], intensity: 1, pos: [1, 2, 1.5], dir: [-1, -2, -1.5], ti: 0 });
|
|
1178
|
+
}
|
|
1179
|
+
const segs = [`${lights.length},0,0,0`];
|
|
1180
|
+
for (let i = 0; i < MAX_LIGHTS; i++) {
|
|
1181
|
+
const l = lights[i];
|
|
1182
|
+
if (l) {
|
|
1183
|
+
segs.push(`${l.color[0] * l.intensity},${l.color[1] * l.intensity},${l.color[2] * l.intensity},${l.ti}`);
|
|
1184
|
+
segs.push(`${l.pos[0]},${l.pos[1]},${l.pos[2]},0`);
|
|
1185
|
+
segs.push(`${l.dir[0]},${l.dir[1]},${l.dir[2]},0`);
|
|
1186
|
+
} else {
|
|
1187
|
+
segs.push("0,0,0,0", "0,0,0,0", "0,0,0,0");
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
this.emit("// === Scene Lights (shared uniform, bound by every mesh at binding 3) ===");
|
|
1191
|
+
for (let i = 0; i < lights.length; i++) {
|
|
1192
|
+
const safeName = lights[i].name.replace(/[\r\n*]+/g, " ");
|
|
1193
|
+
this.emit(`// Light[${i}]: "${safeName}" (${lights[i].kind}, intensity ${lights[i].intensity})`);
|
|
648
1194
|
}
|
|
649
|
-
const ti = { directional: 0, point: 1, spot: 2, hemisphere: 3, ambient: 4, area: 5 }[light.lightType] ?? 0;
|
|
650
|
-
this.emit(
|
|
651
|
-
`// Light: ${this.escapeStringValue(light.name, "TypeScript")} (${light.lightType})`
|
|
652
|
-
);
|
|
653
1195
|
this.emit(
|
|
654
|
-
`const
|
|
1196
|
+
`const sceneLights = createBuffer(device, new Float32Array([${segs.join(", ")}]), GPUBufferUsage.UNIFORM);`
|
|
655
1197
|
);
|
|
656
1198
|
this.emit("");
|
|
657
1199
|
}
|
|
658
1200
|
// ─── Camera ───────────────────────────────────────────────────────────────
|
|
659
|
-
|
|
1201
|
+
// Compute a bounding sphere (center + radius) over every object's world position,
|
|
1202
|
+
// including spatial-group offsets and per-object scale. Used to auto-frame the
|
|
1203
|
+
// camera when the author declared no explicit position. Returns null for an
|
|
1204
|
+
// object-less scene (nothing to frame → keep the neutral default).
|
|
1205
|
+
sceneBoundsForCamera(composition) {
|
|
1206
|
+
const pts = [];
|
|
1207
|
+
const R_CAP = 3;
|
|
1208
|
+
const addObj = (obj, offset) => {
|
|
1209
|
+
const pos = this.findObjProp(obj, "position");
|
|
1210
|
+
const scale = this.findObjProp(obj, "scale") || this.findObjProp(obj, "size");
|
|
1211
|
+
const [px, py, pz] = Array.isArray(pos) ? pos : [0, 0, 0];
|
|
1212
|
+
const rawR = Array.isArray(scale) ? Math.max(...scale.map((n) => Math.abs(n))) : typeof scale === "number" ? Math.abs(scale) : 1;
|
|
1213
|
+
pts.push({ p: [px + offset[0], py + offset[1], pz + offset[2]], r: Math.min(rawR, R_CAP) });
|
|
1214
|
+
};
|
|
1215
|
+
for (const obj of composition.objects ?? []) addObj(obj, [0, 0, 0]);
|
|
1216
|
+
const walkGroup = (g, parent) => {
|
|
1217
|
+
const gp = g.properties?.find((p) => p.key === "position")?.value;
|
|
1218
|
+
const [gx, gy, gz] = Array.isArray(gp) ? gp : [0, 0, 0];
|
|
1219
|
+
const off = [parent[0] + gx, parent[1] + gy, parent[2] + gz];
|
|
1220
|
+
for (const obj of g.objects ?? []) addObj(obj, off);
|
|
1221
|
+
for (const sub of g.groups ?? []) walkGroup(sub, off);
|
|
1222
|
+
};
|
|
1223
|
+
for (const g of composition.spatialGroups ?? []) walkGroup(g, [0, 0, 0]);
|
|
1224
|
+
if (pts.length === 0) return null;
|
|
1225
|
+
const min = [Infinity, Infinity, Infinity];
|
|
1226
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
1227
|
+
for (const { p, r } of pts) {
|
|
1228
|
+
for (let i = 0; i < 3; i++) {
|
|
1229
|
+
min[i] = Math.min(min[i], p[i] - r);
|
|
1230
|
+
max[i] = Math.max(max[i], p[i] + r);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
const center = [(min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2];
|
|
1234
|
+
let radius = 0;
|
|
1235
|
+
for (const { p, r } of pts) {
|
|
1236
|
+
radius = Math.max(radius, Math.hypot(p[0] - center[0], p[1] - center[1], p[2] - center[2]) + r);
|
|
1237
|
+
}
|
|
1238
|
+
return { center, radius: radius || 1 };
|
|
1239
|
+
}
|
|
1240
|
+
emitCamera(composition) {
|
|
1241
|
+
const cam = composition.camera;
|
|
660
1242
|
this.emit("// === Camera ===");
|
|
661
|
-
let fov = 60, near = 0.1, far = 1e3, pos = [4, 3, 6], lookAt = [0, 0, 0];
|
|
1243
|
+
let fov = 60, near = 0.1, far = 1e3, pos = [4, 3, 6], lookAt = [0, 0, 0], hasPos = false;
|
|
662
1244
|
for (const prop of cam?.properties ?? []) {
|
|
663
1245
|
if (prop.key === "fov" || prop.key === "field_of_view") fov = prop.value;
|
|
664
1246
|
else if (prop.key === "near") near = prop.value;
|
|
665
1247
|
else if (prop.key === "far") far = prop.value;
|
|
666
|
-
else if (prop.key === "position" && Array.isArray(prop.value))
|
|
667
|
-
|
|
1248
|
+
else if (prop.key === "position" && Array.isArray(prop.value)) {
|
|
1249
|
+
pos = prop.value;
|
|
1250
|
+
hasPos = true;
|
|
1251
|
+
} else if ((prop.key === "look_at" || prop.key === "lookAt") && Array.isArray(prop.value))
|
|
668
1252
|
lookAt = prop.value;
|
|
669
1253
|
}
|
|
1254
|
+
if (!hasPos) {
|
|
1255
|
+
const b = this.sceneBoundsForCamera(composition);
|
|
1256
|
+
if (b) {
|
|
1257
|
+
const r4 = (n) => Number(n.toFixed(4));
|
|
1258
|
+
const fovRad = fov * Math.PI / 180;
|
|
1259
|
+
const dist = b.radius / Math.sin(fovRad / 2) * 1.1;
|
|
1260
|
+
const dl = Math.hypot(0.35, 0.45, 1);
|
|
1261
|
+
const dir = [0.35 / dl, 0.45 / dl, 1 / dl];
|
|
1262
|
+
pos = [
|
|
1263
|
+
r4(b.center[0] + dir[0] * dist),
|
|
1264
|
+
r4(b.center[1] + dir[1] * dist),
|
|
1265
|
+
r4(b.center[2] + dir[2] * dist)
|
|
1266
|
+
];
|
|
1267
|
+
lookAt = [r4(b.center[0]), r4(b.center[1]), r4(b.center[2])];
|
|
1268
|
+
far = Math.max(far, r4(dist + b.radius * 2 + 10));
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
670
1271
|
this.emit(`const cameraFov = ${fov} * Math.PI / 180;`);
|
|
671
1272
|
this.emit(`const cameraNear = ${near}, cameraFar = ${far};`);
|
|
672
1273
|
this.emit(`const cameraPos = new Float32Array([${pos.join(",")}]);`);
|
|
@@ -766,15 +1367,27 @@ var init_WebGPUCompiler = __esm({
|
|
|
766
1367
|
this.emit("");
|
|
767
1368
|
this.emit("const WGSL_FRAGMENT = /* wgsl */`");
|
|
768
1369
|
this.emit("struct Mat { color: vec4<f32>, rm: vec4<f32>, emissive: vec4<f32> };");
|
|
1370
|
+
this.emit("struct Light { ct: vec4<f32>, posW: vec4<f32>, dirW: vec4<f32> };");
|
|
1371
|
+
this.emit("struct Lights { count: vec4<f32>, items: array<Light, 4> };");
|
|
769
1372
|
this.emit("@group(0) @binding(1) var<uniform> mat: Mat;");
|
|
1373
|
+
this.emit("@group(0) @binding(3) var<uniform> lights: Lights;");
|
|
770
1374
|
this.emit("struct FIn { @location(0) wNorm: vec3<f32>, @location(1) wPos: vec3<f32> };");
|
|
771
1375
|
this.emit("@fragment fn fs_main(i: FIn) -> @location(0) vec4<f32> {");
|
|
772
|
-
this.emit(" let N = normalize(i.wNorm);
|
|
773
|
-
this.emit("
|
|
774
|
-
this.emit(" let
|
|
1376
|
+
this.emit(" let N = normalize(i.wNorm);");
|
|
1377
|
+
this.emit(" var acc = vec3<f32>(0.0, 0.0, 0.0);");
|
|
1378
|
+
this.emit(" let n = i32(lights.count.x);");
|
|
1379
|
+
this.emit(" for (var k = 0; k < n; k = k + 1) {");
|
|
1380
|
+
this.emit(" let lt = lights.items[k];");
|
|
1381
|
+
this.emit(" var L = normalize(-lt.dirW.xyz);");
|
|
1382
|
+
this.emit(" if (lt.ct.w > 0.5) { L = normalize(lt.posW.xyz - i.wPos); }");
|
|
1383
|
+
this.emit(" let d = max(dot(N, L), 0.0) * (1.0 - mat.rm[0] * 0.5);");
|
|
1384
|
+
this.emit(" acc = acc + lt.ct.rgb * d;");
|
|
1385
|
+
this.emit(" }");
|
|
1386
|
+
this.emit(" let lit = mat.color.rgb * (0.15 + acc);");
|
|
775
1387
|
this.emit(" let emit = mat.emissive.rgb * mat.rm[2];");
|
|
776
1388
|
this.emit(" return vec4<f32>(lit + emit, mat.color.a); }`;");
|
|
777
1389
|
this.emit("");
|
|
1390
|
+
if (this.hasWater) this.emitWaterShaderSource();
|
|
778
1391
|
this.emit("const WGSL_SPLAT = /* wgsl */`");
|
|
779
1392
|
this.emit(
|
|
780
1393
|
"struct Splat { center: vec3<f32>, opacity: f32, cov: array<f32,6>, sh: vec3<f32> };"
|
|
@@ -1009,6 +1622,11 @@ var init_WebGPUCompiler = __esm({
|
|
|
1009
1622
|
this.emit("frameCount++;");
|
|
1010
1623
|
this.emit("const time = (performance.now() - t0) / 1000.0;");
|
|
1011
1624
|
this.emit("device.queue.writeBuffer(vpUniform, 0, buildViewProjection());");
|
|
1625
|
+
if (this.hasWater) {
|
|
1626
|
+
this.emit(
|
|
1627
|
+
`device.queue.writeBuffer(waterFrameU, 0, new Float32Array([time, cameraPos[0], cameraPos[1], cameraPos[2], hsChannels[${JSON.stringify(this.waterClarityChannel)}], hsChannels[${JSON.stringify(this.waterRippleChannel)}], hsWaterAmp, 0]));`
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1012
1630
|
this.emit("hsRenderLabels();");
|
|
1013
1631
|
this.emit("const enc = device.createCommandEncoder();");
|
|
1014
1632
|
const gpuObjs = (composition.objects || []).filter(
|
|
@@ -1065,6 +1683,17 @@ var init_WebGPUCompiler = __esm({
|
|
|
1065
1683
|
emitDrawCall(obj) {
|
|
1066
1684
|
const v = this.sanitizeName(obj.name);
|
|
1067
1685
|
const traits = obj.traits || [];
|
|
1686
|
+
const role = resolveGeometryRole({
|
|
1687
|
+
purpose: this.findObjProp(obj, "purpose"),
|
|
1688
|
+
visible: this.findObjProp(obj, "visible"),
|
|
1689
|
+
traitNames: traits.map((t) => t.name)
|
|
1690
|
+
});
|
|
1691
|
+
if (!role.visible) {
|
|
1692
|
+
if (obj.children) {
|
|
1693
|
+
for (const child of obj.children) this.emitDrawCall(child);
|
|
1694
|
+
}
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1068
1697
|
if (traits.some((t) => t.name === "gaussian_splat")) {
|
|
1069
1698
|
this.emit(`rp.setPipeline(${v}SplatPipeline); rp.draw(4, ${v}SplatCount.value);`);
|
|
1070
1699
|
} else if (traits.some((t) => t.name === "point_cloud")) {
|
|
@@ -1073,6 +1702,10 @@ var init_WebGPUCompiler = __esm({
|
|
|
1073
1702
|
this.emit(
|
|
1074
1703
|
`rp.setPipeline(${v}ParticlePipeline); rp.setVertexBuffer(0, ${v}ParticleBufA); rp.draw(${v}ParticleCount);`
|
|
1075
1704
|
);
|
|
1705
|
+
} else if (this.isWaterObject(obj)) {
|
|
1706
|
+
this.emit(
|
|
1707
|
+
`rp.setPipeline(${v}WaterPipeline); rp.setBindGroup(0, ${v}WaterBind); rp.setVertexBuffer(0, ${v}VBO); rp.draw(${v}VertexCount);`
|
|
1708
|
+
);
|
|
1076
1709
|
} else {
|
|
1077
1710
|
this.emit(
|
|
1078
1711
|
`rp.setPipeline(${v}Pipeline); rp.setBindGroup(0, ${v}Bind); rp.setVertexBuffer(0, ${v}VBO); rp.draw(${v}VertexCount);`
|
|
@@ -1150,7 +1783,14 @@ var init_WebGPUCompiler = __esm({
|
|
|
1150
1783
|
return h >>> 0;
|
|
1151
1784
|
}
|
|
1152
1785
|
findObjProp(obj, key) {
|
|
1153
|
-
|
|
1786
|
+
const own = obj.properties?.find((p) => p.key === key)?.value;
|
|
1787
|
+
if (own !== void 0) return own;
|
|
1788
|
+
if (obj.template) {
|
|
1789
|
+
const tpl = this.templatesByName.get(obj.template);
|
|
1790
|
+
const fromTemplate = tpl?.properties?.find((p) => p.key === key)?.value;
|
|
1791
|
+
if (fromTemplate !== void 0) return fromTemplate;
|
|
1792
|
+
}
|
|
1793
|
+
return void 0;
|
|
1154
1794
|
}
|
|
1155
1795
|
extractObjectProperties(obj) {
|
|
1156
1796
|
const properties = {};
|
|
@@ -1202,21 +1842,34 @@ var init_WebGPUCompiler = __esm({
|
|
|
1202
1842
|
return [0, 0, 0];
|
|
1203
1843
|
}
|
|
1204
1844
|
geometryVertexDataFn(meshType) {
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
}
|
|
1214
|
-
return
|
|
1845
|
+
return resolveGeometry(meshType).webgpuVertexFn;
|
|
1846
|
+
}
|
|
1847
|
+
materialName(material) {
|
|
1848
|
+
if (typeof material === "string") return material;
|
|
1849
|
+
if (material && typeof material === "object") {
|
|
1850
|
+
const m = material;
|
|
1851
|
+
if (typeof m.type === "string") return m.type;
|
|
1852
|
+
if (typeof m.name === "string") return m.name;
|
|
1853
|
+
}
|
|
1854
|
+
return void 0;
|
|
1855
|
+
}
|
|
1856
|
+
// Self-illumination strength for a named material when no explicit emissive is
|
|
1857
|
+
// authored. Holographic/neon/emissive surfaces glow in their own color; glass/gel
|
|
1858
|
+
// gets a soft sheen; everything else stays purely lit (0).
|
|
1859
|
+
materialGlow(material) {
|
|
1860
|
+
const name = this.materialName(material);
|
|
1861
|
+
if (!name) return 0;
|
|
1862
|
+
const n = name.toLowerCase();
|
|
1863
|
+
if (/hologram|holo|liquid_glass|liquid_hologram|neon|glow|emissive|aura|plasma|energy/.test(n)) {
|
|
1864
|
+
return 0.65;
|
|
1865
|
+
}
|
|
1866
|
+
if (/glass|crystal|gel|water/.test(n)) return 0.28;
|
|
1867
|
+
return 0;
|
|
1215
1868
|
}
|
|
1216
1869
|
};
|
|
1217
1870
|
}
|
|
1218
1871
|
});
|
|
1219
1872
|
|
|
1220
|
-
export { WebGPUCompiler, WebGPUCompiler_exports, init_WebGPUCompiler };
|
|
1221
|
-
//# sourceMappingURL=chunk-
|
|
1222
|
-
//# sourceMappingURL=chunk-
|
|
1873
|
+
export { WebGPUCompiler, WebGPUCompiler_exports, init_WebGPUCompiler, init_geometry_purpose, init_geometry_registry, init_skybox_registry, resolveGeometry, resolveGeometryRole, resolveSkyboxColor };
|
|
1874
|
+
//# sourceMappingURL=chunk-QLPRI3QW.js.map
|
|
1875
|
+
//# sourceMappingURL=chunk-QLPRI3QW.js.map
|