@holoscript/core 2.0.2 → 2.1.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/{chunk-KWYIVRIH.js → chunk-2XXE34KS.js} +2 -2
- package/dist/chunk-2XXE34KS.js.map +1 -0
- package/dist/{chunk-EU6CZMGJ.js → chunk-AFFVFO4D.js} +511 -118
- package/dist/chunk-AFFVFO4D.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +10 -0
- package/dist/{chunk-4CV4JOE5.js.map → chunk-DGUM43GV.js.map} +1 -1
- package/dist/{chunk-VYIDLUCV.js → chunk-DOY73HDH.js} +4 -4
- package/dist/{chunk-VYIDLUCV.js.map → chunk-DOY73HDH.js.map} +1 -1
- package/dist/chunk-JEQ2X3Z6.cjs +12 -0
- package/dist/{chunk-CZLDE2OZ.cjs.map → chunk-JEQ2X3Z6.cjs.map} +1 -1
- package/dist/{chunk-3N67RLQP.cjs → chunk-L6VLNVKP.cjs} +511 -118
- package/dist/chunk-L6VLNVKP.cjs.map +1 -0
- package/dist/{chunk-VMZN4EVR.cjs → chunk-MFNO57XL.cjs} +2 -2
- package/dist/chunk-MFNO57XL.cjs.map +1 -0
- package/dist/{chunk-WFI4T3XB.cjs → chunk-R75MREOS.cjs} +6 -6
- package/dist/{chunk-WFI4T3XB.cjs.map → chunk-R75MREOS.cjs.map} +1 -1
- package/dist/{chunk-4OHVW4XR.cjs → chunk-T57ZL7KR.cjs} +299 -45
- package/dist/chunk-T57ZL7KR.cjs.map +1 -0
- package/dist/{chunk-MCP6D4LT.js → chunk-U72GEJZT.js} +299 -45
- package/dist/chunk-U72GEJZT.js.map +1 -0
- package/dist/debugger.cjs +6 -6
- package/dist/debugger.d.cts +1 -1
- package/dist/debugger.d.ts +1 -1
- package/dist/debugger.js +4 -4
- package/dist/index.cjs +1896 -1099
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3145 -1534
- package/dist/index.d.ts +3145 -1534
- package/dist/index.js +1922 -1133
- package/dist/index.js.map +1 -1
- package/dist/parser.cjs +3 -3
- package/dist/parser.d.cts +34 -1
- package/dist/parser.d.ts +34 -1
- package/dist/parser.js +2 -2
- package/dist/runtime.cjs +3 -3
- package/dist/runtime.d.cts +47 -27
- package/dist/runtime.d.ts +47 -27
- package/dist/runtime.js +2 -2
- package/dist/type-checker.cjs +4 -4
- package/dist/type-checker.d.cts +3 -3
- package/dist/type-checker.d.ts +3 -3
- package/dist/type-checker.js +2 -2
- package/dist/{types-D6g4ACjP.d.cts → types-4h8cbtF_.d.cts} +80 -13
- package/dist/{types-D6g4ACjP.d.ts → types-4h8cbtF_.d.ts} +80 -13
- package/package.json +21 -20
- package/LICENSE +0 -21
- package/dist/chunk-3N67RLQP.cjs.map +0 -1
- package/dist/chunk-4CV4JOE5.js +0 -24
- package/dist/chunk-4OHVW4XR.cjs.map +0 -1
- package/dist/chunk-CZLDE2OZ.cjs +0 -28
- package/dist/chunk-EU6CZMGJ.js.map +0 -1
- package/dist/chunk-KWYIVRIH.js.map +0 -1
- package/dist/chunk-MCP6D4LT.js.map +0 -1
- package/dist/chunk-VMZN4EVR.cjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,859 +1,12 @@
|
|
|
1
|
-
export { HoloScriptTypeChecker, createTypeChecker } from './chunk-
|
|
2
|
-
export { HoloScriptDebugger, createDebugger } from './chunk-
|
|
3
|
-
import { HoloScriptCodeParser } from './chunk-
|
|
4
|
-
export { HoloScriptCodeParser } from './chunk-
|
|
5
|
-
import { HoloScriptRuntime } from './chunk-
|
|
6
|
-
export { HoloScriptRuntime } from './chunk-
|
|
1
|
+
export { HoloScriptTypeChecker, createTypeChecker } from './chunk-2XXE34KS.js';
|
|
2
|
+
export { HoloScriptDebugger, createDebugger } from './chunk-DOY73HDH.js';
|
|
3
|
+
import { HoloScriptCodeParser } from './chunk-U72GEJZT.js';
|
|
4
|
+
export { HoloScriptCodeParser } from './chunk-U72GEJZT.js';
|
|
5
|
+
import { HoloScriptRuntime } from './chunk-AFFVFO4D.js';
|
|
6
|
+
export { HoloScriptRuntime } from './chunk-AFFVFO4D.js';
|
|
7
7
|
import { logger } from './chunk-SATNCODL.js';
|
|
8
8
|
export { ConsoleLogger, NoOpLogger, enableConsoleLogging, logger, resetLogger, setHoloScriptLogger } from './chunk-SATNCODL.js';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
// src/traits/MaterialTrait.ts
|
|
12
|
-
var MaterialTrait_exports = {};
|
|
13
|
-
__export(MaterialTrait_exports, {
|
|
14
|
-
MATERIAL_PRESETS: () => MATERIAL_PRESETS,
|
|
15
|
-
MaterialTrait: () => MaterialTrait,
|
|
16
|
-
createMaterialTrait: () => createMaterialTrait
|
|
17
|
-
});
|
|
18
|
-
function createMaterialTrait(config) {
|
|
19
|
-
return new MaterialTrait(config);
|
|
20
|
-
}
|
|
21
|
-
var MaterialTrait, MATERIAL_PRESETS;
|
|
22
|
-
var init_MaterialTrait = __esm({
|
|
23
|
-
"src/traits/MaterialTrait.ts"() {
|
|
24
|
-
MaterialTrait = class {
|
|
25
|
-
constructor(config) {
|
|
26
|
-
this.textureCache = /* @__PURE__ */ new Map();
|
|
27
|
-
this.material = {
|
|
28
|
-
...{ type: "pbr" },
|
|
29
|
-
...config
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Get material properties
|
|
34
|
-
*/
|
|
35
|
-
getMaterial() {
|
|
36
|
-
return { ...this.material };
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Update material property
|
|
40
|
-
*/
|
|
41
|
-
setProperty(key, value) {
|
|
42
|
-
this.material[key] = value;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Get PBR properties
|
|
46
|
-
*/
|
|
47
|
-
getPBRProperties() {
|
|
48
|
-
return this.material.pbr;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Update PBR material
|
|
52
|
-
*/
|
|
53
|
-
updatePBR(pbr) {
|
|
54
|
-
if (!this.material.pbr) {
|
|
55
|
-
this.material.pbr = {
|
|
56
|
-
baseColor: { r: 1, g: 1, b: 1 },
|
|
57
|
-
metallic: 0,
|
|
58
|
-
roughness: 0.5
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
this.material.pbr = { ...this.material.pbr, ...pbr };
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Add texture map
|
|
65
|
-
*/
|
|
66
|
-
addTexture(texture) {
|
|
67
|
-
if (!this.material.textures) {
|
|
68
|
-
this.material.textures = [];
|
|
69
|
-
}
|
|
70
|
-
this.material.textures.push(texture);
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Get all textures
|
|
74
|
-
*/
|
|
75
|
-
getTextures() {
|
|
76
|
-
return [...this.material.textures || []];
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Clear texture cache (for memory optimization)
|
|
80
|
-
*/
|
|
81
|
-
clearTextureCache() {
|
|
82
|
-
this.textureCache.clear();
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Get shader code if custom
|
|
86
|
-
*/
|
|
87
|
-
getCustomShader() {
|
|
88
|
-
return this.material.customShader;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Set custom shader
|
|
92
|
-
*/
|
|
93
|
-
setCustomShader(shader) {
|
|
94
|
-
this.material.customShader = shader;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Get optimization hints
|
|
98
|
-
*/
|
|
99
|
-
getOptimization() {
|
|
100
|
-
return this.material.optimization;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Enable/disable texture streaming
|
|
104
|
-
*/
|
|
105
|
-
setTextureStreaming(enabled) {
|
|
106
|
-
if (!this.material.optimization) {
|
|
107
|
-
this.material.optimization = {};
|
|
108
|
-
}
|
|
109
|
-
this.material.optimization.streamTextures = enabled;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Set texture compression
|
|
113
|
-
*/
|
|
114
|
-
setCompression(compression) {
|
|
115
|
-
if (!this.material.optimization) {
|
|
116
|
-
this.material.optimization = {};
|
|
117
|
-
}
|
|
118
|
-
this.material.optimization.compression = compression;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Enable material instancing for performance
|
|
122
|
-
*/
|
|
123
|
-
setInstanced(instanced) {
|
|
124
|
-
if (!this.material.optimization) {
|
|
125
|
-
this.material.optimization = {};
|
|
126
|
-
}
|
|
127
|
-
this.material.optimization.instanced = instanced;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Dispose and cleanup
|
|
131
|
-
*/
|
|
132
|
-
dispose() {
|
|
133
|
-
this.textureCache.clear();
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
MATERIAL_PRESETS = {
|
|
137
|
-
/** Shiny metal */
|
|
138
|
-
chrome: () => ({
|
|
139
|
-
type: "pbr",
|
|
140
|
-
pbr: {
|
|
141
|
-
baseColor: { r: 0.77, g: 0.77, b: 0.77 },
|
|
142
|
-
metallic: 1,
|
|
143
|
-
roughness: 0.1
|
|
144
|
-
}
|
|
145
|
-
}),
|
|
146
|
-
/** Rough plastic */
|
|
147
|
-
plastic: () => ({
|
|
148
|
-
type: "pbr",
|
|
149
|
-
pbr: {
|
|
150
|
-
baseColor: { r: 1, g: 1, b: 1 },
|
|
151
|
-
metallic: 0,
|
|
152
|
-
roughness: 0.8
|
|
153
|
-
}
|
|
154
|
-
}),
|
|
155
|
-
/** Wood texture */
|
|
156
|
-
wood: () => ({
|
|
157
|
-
type: "pbr",
|
|
158
|
-
pbr: {
|
|
159
|
-
baseColor: { r: 0.6, g: 0.4, b: 0.2 },
|
|
160
|
-
metallic: 0,
|
|
161
|
-
roughness: 0.4
|
|
162
|
-
}
|
|
163
|
-
}),
|
|
164
|
-
/** Glass */
|
|
165
|
-
glass: () => ({
|
|
166
|
-
type: "transparent",
|
|
167
|
-
blendMode: "blend",
|
|
168
|
-
pbr: {
|
|
169
|
-
baseColor: { r: 1, g: 1, b: 1, a: 0.3 },
|
|
170
|
-
metallic: 0,
|
|
171
|
-
roughness: 0,
|
|
172
|
-
ior: 1.5,
|
|
173
|
-
transmission: 0.9
|
|
174
|
-
}
|
|
175
|
-
}),
|
|
176
|
-
/** Emissive (glowing) */
|
|
177
|
-
emissive: () => ({
|
|
178
|
-
type: "pbr",
|
|
179
|
-
pbr: {
|
|
180
|
-
baseColor: { r: 0, g: 1, b: 0 },
|
|
181
|
-
metallic: 0,
|
|
182
|
-
roughness: 1,
|
|
183
|
-
emission: {
|
|
184
|
-
color: { r: 0, g: 1, b: 0 },
|
|
185
|
-
intensity: 2
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}),
|
|
189
|
-
/** Skin material */
|
|
190
|
-
skin: () => ({
|
|
191
|
-
type: "pbr",
|
|
192
|
-
pbr: {
|
|
193
|
-
baseColor: { r: 1, g: 0.8, b: 0.7 },
|
|
194
|
-
metallic: 0,
|
|
195
|
-
roughness: 0.5,
|
|
196
|
-
ambientOcclusion: 0.8
|
|
197
|
-
}
|
|
198
|
-
})
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// src/traits/LightingTrait.ts
|
|
204
|
-
var LightingTrait_exports = {};
|
|
205
|
-
__export(LightingTrait_exports, {
|
|
206
|
-
LIGHTING_PRESETS: () => LIGHTING_PRESETS,
|
|
207
|
-
LightingTrait: () => LightingTrait,
|
|
208
|
-
createLightingTrait: () => createLightingTrait
|
|
209
|
-
});
|
|
210
|
-
function createLightingTrait(config) {
|
|
211
|
-
return new LightingTrait(config);
|
|
212
|
-
}
|
|
213
|
-
var LightingTrait, LIGHTING_PRESETS;
|
|
214
|
-
var init_LightingTrait = __esm({
|
|
215
|
-
"src/traits/LightingTrait.ts"() {
|
|
216
|
-
LightingTrait = class {
|
|
217
|
-
constructor(config) {
|
|
218
|
-
this.lights = /* @__PURE__ */ new Map();
|
|
219
|
-
this.lightIdCounter = 0;
|
|
220
|
-
this.globalIllumination = {
|
|
221
|
-
enabled: true,
|
|
222
|
-
intensity: 1,
|
|
223
|
-
skyColor: { r: 0.5, g: 0.7, b: 1 },
|
|
224
|
-
skyIntensity: 1,
|
|
225
|
-
groundColor: { r: 0.4, g: 0.4, b: 0.4 },
|
|
226
|
-
groundIntensity: 0.5,
|
|
227
|
-
probes: true,
|
|
228
|
-
indirectDiffuse: 1,
|
|
229
|
-
indirectSpecular: 1,
|
|
230
|
-
aoIntensity: 1,
|
|
231
|
-
screenSpaceAO: true,
|
|
232
|
-
...config
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Add a light to the scene
|
|
237
|
-
*/
|
|
238
|
-
addLight(light) {
|
|
239
|
-
const id = light.name || `light_${this.lightIdCounter++}`;
|
|
240
|
-
this.lights.set(id, light);
|
|
241
|
-
return id;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Get light by ID
|
|
245
|
-
*/
|
|
246
|
-
getLight(id) {
|
|
247
|
-
return this.lights.get(id);
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Get all lights
|
|
251
|
-
*/
|
|
252
|
-
getLights() {
|
|
253
|
-
return Array.from(this.lights.values());
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Get lights by type
|
|
257
|
-
*/
|
|
258
|
-
getLightsByType(type) {
|
|
259
|
-
return Array.from(this.lights.values()).filter((l) => l.type === type);
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Update light properties
|
|
263
|
-
*/
|
|
264
|
-
updateLight(id, updates) {
|
|
265
|
-
const light = this.lights.get(id);
|
|
266
|
-
if (!light) return false;
|
|
267
|
-
this.lights.set(id, { ...light, ...updates });
|
|
268
|
-
return true;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Remove light
|
|
272
|
-
*/
|
|
273
|
-
removeLight(id) {
|
|
274
|
-
return this.lights.delete(id);
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Clear all lights
|
|
278
|
-
*/
|
|
279
|
-
clearLights() {
|
|
280
|
-
this.lights.clear();
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* Get global illumination config
|
|
284
|
-
*/
|
|
285
|
-
getGlobalIllumination() {
|
|
286
|
-
return { ...this.globalIllumination };
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Update global illumination
|
|
290
|
-
*/
|
|
291
|
-
updateGlobalIllumination(updates) {
|
|
292
|
-
this.globalIllumination = {
|
|
293
|
-
...this.globalIllumination,
|
|
294
|
-
...updates
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Enable/disable GI
|
|
299
|
-
*/
|
|
300
|
-
setGIEnabled(enabled) {
|
|
301
|
-
this.globalIllumination.enabled = enabled;
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Set ambient light colors (skybox mode)
|
|
305
|
-
*/
|
|
306
|
-
setAmbientLight(skyColor, groundColor, intensity = 1) {
|
|
307
|
-
this.globalIllumination.skyColor = skyColor;
|
|
308
|
-
this.globalIllumination.groundColor = groundColor;
|
|
309
|
-
this.globalIllumination.skyIntensity = intensity;
|
|
310
|
-
this.globalIllumination.groundIntensity = intensity * 0.5;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Enable/disable screen-space ambient occlusion
|
|
314
|
-
*/
|
|
315
|
-
setScreenSpaceAO(enabled, intensity = 1) {
|
|
316
|
-
this.globalIllumination.screenSpaceAO = enabled;
|
|
317
|
-
this.globalIllumination.aoIntensity = intensity;
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Create directional light (sun)
|
|
321
|
-
*/
|
|
322
|
-
createDirectionalLight(direction, color, intensity = 1, castShadows = true) {
|
|
323
|
-
const light = {
|
|
324
|
-
type: "directional",
|
|
325
|
-
name: `sun_${this.lightIdCounter}`,
|
|
326
|
-
direction,
|
|
327
|
-
color,
|
|
328
|
-
intensity,
|
|
329
|
-
shadow: castShadows ? {
|
|
330
|
-
type: "soft",
|
|
331
|
-
resolution: 2048,
|
|
332
|
-
cascades: 4,
|
|
333
|
-
softness: 1
|
|
334
|
-
} : void 0,
|
|
335
|
-
volumetric: true,
|
|
336
|
-
priority: 100
|
|
337
|
-
};
|
|
338
|
-
return this.addLight(light);
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Create point light
|
|
342
|
-
*/
|
|
343
|
-
createPointLight(position, color, intensity, range, castShadows = false) {
|
|
344
|
-
const light = {
|
|
345
|
-
type: "point",
|
|
346
|
-
name: `point_${this.lightIdCounter}`,
|
|
347
|
-
position,
|
|
348
|
-
color,
|
|
349
|
-
intensity,
|
|
350
|
-
range,
|
|
351
|
-
shadow: castShadows ? {
|
|
352
|
-
type: "soft",
|
|
353
|
-
resolution: 512,
|
|
354
|
-
softness: 0.5
|
|
355
|
-
} : void 0,
|
|
356
|
-
priority: 50
|
|
357
|
-
};
|
|
358
|
-
return this.addLight(light);
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Create spot light
|
|
362
|
-
*/
|
|
363
|
-
createSpotLight(position, direction, color, intensity, range, spotAngle = 45, castShadows = true) {
|
|
364
|
-
const light = {
|
|
365
|
-
type: "spot",
|
|
366
|
-
name: `spot_${this.lightIdCounter}`,
|
|
367
|
-
position,
|
|
368
|
-
direction,
|
|
369
|
-
color,
|
|
370
|
-
intensity,
|
|
371
|
-
range,
|
|
372
|
-
spotAngle,
|
|
373
|
-
innerSpotAngle: spotAngle * 0.5,
|
|
374
|
-
shadow: castShadows ? {
|
|
375
|
-
type: "soft",
|
|
376
|
-
resolution: 1024,
|
|
377
|
-
softness: 0.8
|
|
378
|
-
} : void 0,
|
|
379
|
-
priority: 75
|
|
380
|
-
};
|
|
381
|
-
return this.addLight(light);
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Create area light for soft lighting
|
|
385
|
-
*/
|
|
386
|
-
createAreaLight(position, color, intensity, width, height) {
|
|
387
|
-
const light = {
|
|
388
|
-
type: "area",
|
|
389
|
-
name: `area_${this.lightIdCounter}`,
|
|
390
|
-
position,
|
|
391
|
-
color,
|
|
392
|
-
intensity,
|
|
393
|
-
range: Math.max(width, height) * 2,
|
|
394
|
-
priority: 25
|
|
395
|
-
};
|
|
396
|
-
return this.addLight(light);
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Get shadow-casting lights
|
|
400
|
-
*/
|
|
401
|
-
getShadowCastingLights() {
|
|
402
|
-
return Array.from(this.lights.values()).filter((l) => l.shadow && l.shadow.type !== "none");
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Get light count by type
|
|
406
|
-
*/
|
|
407
|
-
getLightCount() {
|
|
408
|
-
const counts = {
|
|
409
|
-
directional: 0,
|
|
410
|
-
point: 0,
|
|
411
|
-
spot: 0,
|
|
412
|
-
area: 0,
|
|
413
|
-
probe: 0
|
|
414
|
-
};
|
|
415
|
-
for (const light of this.lights.values()) {
|
|
416
|
-
counts[light.type]++;
|
|
417
|
-
}
|
|
418
|
-
return counts;
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Estimate light impact for performance optimization
|
|
422
|
-
*/
|
|
423
|
-
getPerformanceImpact() {
|
|
424
|
-
const totalLights = this.lights.size;
|
|
425
|
-
const shadowCasters = this.getShadowCastingLights().length;
|
|
426
|
-
let estimatedGPUCost = "low";
|
|
427
|
-
if (totalLights > 16 || shadowCasters > 4) {
|
|
428
|
-
estimatedGPUCost = "high";
|
|
429
|
-
} else if (totalLights > 8 || shadowCasters > 2) {
|
|
430
|
-
estimatedGPUCost = "medium";
|
|
431
|
-
}
|
|
432
|
-
return {
|
|
433
|
-
totalLights,
|
|
434
|
-
shadowCasters,
|
|
435
|
-
estimatedGPUCost
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Get scene complexity info
|
|
440
|
-
*/
|
|
441
|
-
getSceneInfo() {
|
|
442
|
-
const counts = this.getLightCount();
|
|
443
|
-
const impact = this.getPerformanceImpact();
|
|
444
|
-
return `Lighting: ${counts.directional} dir, ${counts.point} point, ${counts.spot} spot | Shadows: ${impact.shadowCasters} | GPU: ${impact.estimatedGPUCost}`;
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Dispose and cleanup
|
|
448
|
-
*/
|
|
449
|
-
dispose() {
|
|
450
|
-
this.lights.clear();
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
LIGHTING_PRESETS = {
|
|
454
|
-
/** Neutral studio lighting */
|
|
455
|
-
studio: () => ({
|
|
456
|
-
enabled: true,
|
|
457
|
-
intensity: 1,
|
|
458
|
-
skyColor: { r: 0.5, g: 0.5, b: 0.5 },
|
|
459
|
-
skyIntensity: 0.5,
|
|
460
|
-
groundColor: { r: 0.3, g: 0.3, b: 0.3 },
|
|
461
|
-
groundIntensity: 0.3
|
|
462
|
-
}),
|
|
463
|
-
/** Bright outdoor lighting */
|
|
464
|
-
outdoor: () => ({
|
|
465
|
-
enabled: true,
|
|
466
|
-
intensity: 1.2,
|
|
467
|
-
skyColor: { r: 0.7, g: 0.85, b: 1 },
|
|
468
|
-
skyIntensity: 1,
|
|
469
|
-
groundColor: { r: 0.4, g: 0.4, b: 0.35 },
|
|
470
|
-
groundIntensity: 0.6,
|
|
471
|
-
indirectDiffuse: 1.2
|
|
472
|
-
}),
|
|
473
|
-
/** Dim interior lighting */
|
|
474
|
-
interior: () => ({
|
|
475
|
-
enabled: true,
|
|
476
|
-
intensity: 0.6,
|
|
477
|
-
skyColor: { r: 0.3, g: 0.3, b: 0.35 },
|
|
478
|
-
skyIntensity: 0.4,
|
|
479
|
-
groundColor: { r: 0.2, g: 0.2, b: 0.2 },
|
|
480
|
-
groundIntensity: 0.2
|
|
481
|
-
}),
|
|
482
|
-
/** Night scene */
|
|
483
|
-
night: () => ({
|
|
484
|
-
enabled: true,
|
|
485
|
-
intensity: 0.3,
|
|
486
|
-
skyColor: { r: 0.01, g: 0.01, b: 0.02 },
|
|
487
|
-
skyIntensity: 0.1,
|
|
488
|
-
groundColor: { r: 0.02, g: 0.02, b: 0.02 },
|
|
489
|
-
groundIntensity: 0.05,
|
|
490
|
-
screenSpaceAO: false
|
|
491
|
-
}),
|
|
492
|
-
/** Sunset/golden hour */
|
|
493
|
-
sunset: () => ({
|
|
494
|
-
enabled: true,
|
|
495
|
-
intensity: 1.1,
|
|
496
|
-
skyColor: { r: 1, g: 0.7, b: 0.3 },
|
|
497
|
-
skyIntensity: 1,
|
|
498
|
-
groundColor: { r: 0.6, g: 0.4, b: 0.2 },
|
|
499
|
-
groundIntensity: 0.8
|
|
500
|
-
})
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
// src/traits/RenderingTrait.ts
|
|
506
|
-
var RenderingTrait_exports = {};
|
|
507
|
-
__export(RenderingTrait_exports, {
|
|
508
|
-
RenderingTrait: () => RenderingTrait,
|
|
509
|
-
createRenderingTrait: () => createRenderingTrait
|
|
510
|
-
});
|
|
511
|
-
function createRenderingTrait(config) {
|
|
512
|
-
return new RenderingTrait(config);
|
|
513
|
-
}
|
|
514
|
-
var RenderingTrait;
|
|
515
|
-
var init_RenderingTrait = __esm({
|
|
516
|
-
"src/traits/RenderingTrait.ts"() {
|
|
517
|
-
RenderingTrait = class {
|
|
518
|
-
constructor(config) {
|
|
519
|
-
this.optimization = {
|
|
520
|
-
lodStrategy: "automatic",
|
|
521
|
-
culling: {
|
|
522
|
-
mode: "back",
|
|
523
|
-
frustum: true,
|
|
524
|
-
occlusion: true
|
|
525
|
-
},
|
|
526
|
-
batching: {
|
|
527
|
-
static: true,
|
|
528
|
-
dynamic: true,
|
|
529
|
-
instancing: true,
|
|
530
|
-
maxInstanceCount: 1e3
|
|
531
|
-
},
|
|
532
|
-
textures: {
|
|
533
|
-
streaming: true,
|
|
534
|
-
compression: "auto",
|
|
535
|
-
mipmaps: true,
|
|
536
|
-
maxResolution: 2048
|
|
537
|
-
},
|
|
538
|
-
shaders: {
|
|
539
|
-
simplifiedShaders: true,
|
|
540
|
-
lodBias: 0
|
|
541
|
-
},
|
|
542
|
-
targetGPUTier: "high",
|
|
543
|
-
adaptiveQuality: true,
|
|
544
|
-
targetFrameRate: 60,
|
|
545
|
-
...config
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Get rendering optimization config
|
|
550
|
-
*/
|
|
551
|
-
getOptimization() {
|
|
552
|
-
return JSON.parse(JSON.stringify(this.optimization));
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Update rendering configuration
|
|
556
|
-
*/
|
|
557
|
-
updateOptimization(updates) {
|
|
558
|
-
this.optimization = { ...this.optimization, ...updates };
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* Setup LOD levels (3 levels is typical)
|
|
562
|
-
*/
|
|
563
|
-
setupLODLevels(strategy = "automatic") {
|
|
564
|
-
const levels = [
|
|
565
|
-
{
|
|
566
|
-
level: 0,
|
|
567
|
-
screenRelativeSize: 0.5,
|
|
568
|
-
polygonReduction: 1,
|
|
569
|
-
textureScale: 1
|
|
570
|
-
},
|
|
571
|
-
{
|
|
572
|
-
level: 1,
|
|
573
|
-
screenRelativeSize: 0.25,
|
|
574
|
-
polygonReduction: 0.6,
|
|
575
|
-
disabledFeatures: ["specular"],
|
|
576
|
-
textureScale: 0.5
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
level: 2,
|
|
580
|
-
screenRelativeSize: 0.1,
|
|
581
|
-
polygonReduction: 0.3,
|
|
582
|
-
disabledFeatures: ["specular", "normals"],
|
|
583
|
-
textureScale: 0.25
|
|
584
|
-
}
|
|
585
|
-
];
|
|
586
|
-
this.optimization.lodStrategy = strategy;
|
|
587
|
-
this.optimization.lodLevels = levels;
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Get LOD levels
|
|
591
|
-
*/
|
|
592
|
-
getLODLevels() {
|
|
593
|
-
return [...this.optimization.lodLevels || []];
|
|
594
|
-
}
|
|
595
|
-
/**
|
|
596
|
-
* Configure culling
|
|
597
|
-
*/
|
|
598
|
-
setCulling(config) {
|
|
599
|
-
const defaultCulling = { mode: "back" };
|
|
600
|
-
this.optimization.culling = {
|
|
601
|
-
...defaultCulling,
|
|
602
|
-
...this.optimization.culling,
|
|
603
|
-
...config
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* Enable frustum culling
|
|
608
|
-
*/
|
|
609
|
-
setFrustumCulling(enabled) {
|
|
610
|
-
if (!this.optimization.culling) {
|
|
611
|
-
this.optimization.culling = { mode: "back" };
|
|
612
|
-
}
|
|
613
|
-
this.optimization.culling.frustum = enabled;
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Enable occlusion culling
|
|
617
|
-
*/
|
|
618
|
-
setOcclusionCulling(enabled, distance) {
|
|
619
|
-
if (!this.optimization.culling) {
|
|
620
|
-
this.optimization.culling = { mode: "back" };
|
|
621
|
-
}
|
|
622
|
-
this.optimization.culling.occlusion = enabled;
|
|
623
|
-
if (distance) {
|
|
624
|
-
this.optimization.culling.occlusionDistance = distance;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
/**
|
|
628
|
-
* Configure batching
|
|
629
|
-
*/
|
|
630
|
-
setBatching(config) {
|
|
631
|
-
this.optimization.batching = {
|
|
632
|
-
...this.optimization.batching,
|
|
633
|
-
...config
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
/**
|
|
637
|
-
* Enable GPU instancing
|
|
638
|
-
*/
|
|
639
|
-
setInstancing(enabled, maxInstances) {
|
|
640
|
-
if (!this.optimization.batching) {
|
|
641
|
-
this.optimization.batching = {};
|
|
642
|
-
}
|
|
643
|
-
this.optimization.batching.instancing = enabled;
|
|
644
|
-
if (maxInstances) {
|
|
645
|
-
this.optimization.batching.maxInstanceCount = maxInstances;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
/**
|
|
649
|
-
* Configure texture optimization
|
|
650
|
-
*/
|
|
651
|
-
setTextureOptimization(config) {
|
|
652
|
-
this.optimization.textures = {
|
|
653
|
-
...this.optimization.textures,
|
|
654
|
-
...config
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* Enable texture streaming
|
|
659
|
-
*/
|
|
660
|
-
setTextureStreaming(enabled, budgetMB) {
|
|
661
|
-
if (!this.optimization.textures) {
|
|
662
|
-
this.optimization.textures = {};
|
|
663
|
-
}
|
|
664
|
-
this.optimization.textures.streaming = enabled;
|
|
665
|
-
if (budgetMB) {
|
|
666
|
-
this.optimization.textures.streamingBudget = budgetMB;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
/**
|
|
670
|
-
* Set texture compression
|
|
671
|
-
*/
|
|
672
|
-
setTextureCompression(compression) {
|
|
673
|
-
if (!this.optimization.textures) {
|
|
674
|
-
this.optimization.textures = {};
|
|
675
|
-
}
|
|
676
|
-
this.optimization.textures.compression = compression;
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* Set max texture resolution
|
|
680
|
-
*/
|
|
681
|
-
setMaxTextureResolution(resolution) {
|
|
682
|
-
if (!this.optimization.textures) {
|
|
683
|
-
this.optimization.textures = {};
|
|
684
|
-
}
|
|
685
|
-
this.optimization.textures.maxResolution = resolution;
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Configure shader optimization
|
|
689
|
-
*/
|
|
690
|
-
setShaderOptimization(config) {
|
|
691
|
-
this.optimization.shaders = {
|
|
692
|
-
...this.optimization.shaders,
|
|
693
|
-
...config
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
/**
|
|
697
|
-
* Set target GPU tier
|
|
698
|
-
*/
|
|
699
|
-
setTargetGPUTier(tier) {
|
|
700
|
-
this.optimization.targetGPUTier = tier;
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
|
-
* Enable adaptive quality (adjust based on frame rate)
|
|
704
|
-
*/
|
|
705
|
-
setAdaptiveQuality(enabled, targetFrameRate) {
|
|
706
|
-
this.optimization.adaptiveQuality = enabled;
|
|
707
|
-
if (targetFrameRate) {
|
|
708
|
-
this.optimization.targetFrameRate = targetFrameRate;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* Set fixed timestep for VR/AR
|
|
713
|
-
*/
|
|
714
|
-
setFixedTimestep(timestep) {
|
|
715
|
-
this.optimization.fixedTimestep = timestep;
|
|
716
|
-
}
|
|
717
|
-
/**
|
|
718
|
-
* Get rendering preset for quality level
|
|
719
|
-
*/
|
|
720
|
-
getPresetForQuality(quality) {
|
|
721
|
-
const presets = {
|
|
722
|
-
low: {
|
|
723
|
-
targetGPUTier: "low",
|
|
724
|
-
lodStrategy: "automatic",
|
|
725
|
-
culling: { mode: "back", frustum: true, occlusion: false },
|
|
726
|
-
batching: { instancing: true, maxInstanceCount: 500 },
|
|
727
|
-
textures: {
|
|
728
|
-
compression: "astc",
|
|
729
|
-
maxResolution: 512,
|
|
730
|
-
streaming: true,
|
|
731
|
-
streamingBudget: 128
|
|
732
|
-
},
|
|
733
|
-
adaptiveQuality: true,
|
|
734
|
-
targetFrameRate: 30
|
|
735
|
-
},
|
|
736
|
-
medium: {
|
|
737
|
-
targetGPUTier: "medium",
|
|
738
|
-
lodStrategy: "automatic",
|
|
739
|
-
culling: { mode: "back", frustum: true, occlusion: true },
|
|
740
|
-
batching: { instancing: true, maxInstanceCount: 1e3 },
|
|
741
|
-
textures: {
|
|
742
|
-
compression: "basis",
|
|
743
|
-
maxResolution: 1024,
|
|
744
|
-
streaming: true,
|
|
745
|
-
streamingBudget: 256
|
|
746
|
-
},
|
|
747
|
-
adaptiveQuality: true,
|
|
748
|
-
targetFrameRate: 60
|
|
749
|
-
},
|
|
750
|
-
high: {
|
|
751
|
-
targetGPUTier: "high",
|
|
752
|
-
lodStrategy: "automatic",
|
|
753
|
-
culling: { mode: "back", frustum: true, occlusion: true },
|
|
754
|
-
batching: { instancing: true, maxInstanceCount: 2e3 },
|
|
755
|
-
textures: {
|
|
756
|
-
compression: "dxt",
|
|
757
|
-
maxResolution: 2048,
|
|
758
|
-
streaming: true,
|
|
759
|
-
streamingBudget: 512
|
|
760
|
-
},
|
|
761
|
-
adaptiveQuality: false,
|
|
762
|
-
targetFrameRate: 60
|
|
763
|
-
},
|
|
764
|
-
ultra: {
|
|
765
|
-
targetGPUTier: "ultra",
|
|
766
|
-
lodStrategy: "manual",
|
|
767
|
-
culling: {
|
|
768
|
-
mode: "back",
|
|
769
|
-
frustum: true,
|
|
770
|
-
occlusion: true,
|
|
771
|
-
hierarchicalZ: true
|
|
772
|
-
},
|
|
773
|
-
batching: { instancing: true, maxInstanceCount: 5e3 },
|
|
774
|
-
textures: {
|
|
775
|
-
compression: "none",
|
|
776
|
-
maxResolution: 4096,
|
|
777
|
-
virtualTexturing: true,
|
|
778
|
-
streaming: true,
|
|
779
|
-
streamingBudget: 1024
|
|
780
|
-
},
|
|
781
|
-
adaptiveQuality: false,
|
|
782
|
-
targetFrameRate: 120
|
|
783
|
-
}
|
|
784
|
-
};
|
|
785
|
-
return { ...this.optimization, ...presets[quality] };
|
|
786
|
-
}
|
|
787
|
-
/**
|
|
788
|
-
* Apply quality preset
|
|
789
|
-
*/
|
|
790
|
-
applyQualityPreset(quality) {
|
|
791
|
-
const preset = this.getPresetForQuality(quality);
|
|
792
|
-
this.optimization = preset;
|
|
793
|
-
}
|
|
794
|
-
/**
|
|
795
|
-
* Estimate GPU memory usage
|
|
796
|
-
*/
|
|
797
|
-
estimateGPUMemory() {
|
|
798
|
-
let textureMemory = 0;
|
|
799
|
-
let vertexBuffers = 0;
|
|
800
|
-
const maxRes = this.optimization.textures?.maxResolution || 2048;
|
|
801
|
-
textureMemory = maxRes * maxRes * 4 / (1024 * 1024);
|
|
802
|
-
const instanceCount = this.optimization.batching?.maxInstanceCount || 1e3;
|
|
803
|
-
const verticesPerMesh = 1e4;
|
|
804
|
-
vertexBuffers = verticesPerMesh * 36 * instanceCount / (1024 * 1024) * 0.1;
|
|
805
|
-
return {
|
|
806
|
-
textureMemory: Math.round(textureMemory),
|
|
807
|
-
vertexBuffers: Math.max(1, Math.round(vertexBuffers)),
|
|
808
|
-
// At least 1MB
|
|
809
|
-
estimatedTotal: Math.round(textureMemory + Math.max(1, vertexBuffers))
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
/**
|
|
813
|
-
* Get rendering statistics/info
|
|
814
|
-
*/
|
|
815
|
-
getInfo() {
|
|
816
|
-
const tier = this.optimization.targetGPUTier;
|
|
817
|
-
const lod = this.optimization.lodStrategy;
|
|
818
|
-
const culling = this.optimization.culling?.mode;
|
|
819
|
-
const instancing = this.optimization.batching?.instancing ? "yes" : "no";
|
|
820
|
-
const memory = this.estimateGPUMemory();
|
|
821
|
-
return `Rendering: tier=${tier} | LOD=${lod} | culling=${culling} | instancing=${instancing} | memory=${memory.estimatedTotal}MB`;
|
|
822
|
-
}
|
|
823
|
-
/**
|
|
824
|
-
* Optimize for VR/AR (fixed timestep, fast culling)
|
|
825
|
-
*/
|
|
826
|
-
optimizeForVRAR(targetFPS = 90) {
|
|
827
|
-
this.optimization.fixedTimestep = 1 / targetFPS;
|
|
828
|
-
this.optimization.targetFrameRate = targetFPS;
|
|
829
|
-
this.setOcclusionCulling(true, 50);
|
|
830
|
-
this.setInstancing(true, 5e3);
|
|
831
|
-
this.setAdaptiveQuality(true, targetFPS);
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Optimize for mobile (lower resources)
|
|
835
|
-
*/
|
|
836
|
-
optimizeForMobile() {
|
|
837
|
-
this.applyQualityPreset("low");
|
|
838
|
-
this.setTextureCompression("astc");
|
|
839
|
-
this.setInstancing(true, 256);
|
|
840
|
-
}
|
|
841
|
-
/**
|
|
842
|
-
* Optimize for desktop (higher resources)
|
|
843
|
-
*/
|
|
844
|
-
optimizeForDesktop() {
|
|
845
|
-
this.applyQualityPreset("ultra");
|
|
846
|
-
this.setTextureCompression("none");
|
|
847
|
-
this.setInstancing(true, 5e3);
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Dispose and cleanup
|
|
851
|
-
*/
|
|
852
|
-
dispose() {
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
}
|
|
856
|
-
});
|
|
9
|
+
import { __require } from './chunk-DGUM43GV.js';
|
|
857
10
|
|
|
858
11
|
// src/HoloScript2DParser.ts
|
|
859
12
|
var UI_SECURITY_CONFIG = {
|
|
@@ -1090,9 +243,7 @@ var HOLOSCRIPT_SECURITY_CONFIG = {
|
|
|
1090
243
|
"__proto__",
|
|
1091
244
|
"fs",
|
|
1092
245
|
"child_process",
|
|
1093
|
-
"exec",
|
|
1094
246
|
"spawn",
|
|
1095
|
-
"fetch",
|
|
1096
247
|
"xmlhttprequest"
|
|
1097
248
|
],
|
|
1098
249
|
allowedUIElements: [
|
|
@@ -1128,7 +279,7 @@ var HoloScriptParser = class {
|
|
|
1128
279
|
});
|
|
1129
280
|
return [];
|
|
1130
281
|
}
|
|
1131
|
-
const rawTokens = this.tokenizeCommand(command.command
|
|
282
|
+
const rawTokens = this.tokenizeCommand(command.command);
|
|
1132
283
|
const tokens = this.sanitizeTokens(rawTokens);
|
|
1133
284
|
if (tokens.length === 0) return [];
|
|
1134
285
|
if (tokens.length > HOLOSCRIPT_SECURITY_CONFIG.maxTokens) {
|
|
@@ -1138,7 +289,7 @@ var HoloScriptParser = class {
|
|
|
1138
289
|
});
|
|
1139
290
|
return [];
|
|
1140
291
|
}
|
|
1141
|
-
const commandType = tokens[0];
|
|
292
|
+
const commandType = tokens[0].toLowerCase();
|
|
1142
293
|
if ((commandType === "create" || commandType === "add") && tokens.length > 1) {
|
|
1143
294
|
const elementType = tokens[1];
|
|
1144
295
|
if (HOLOSCRIPT_SECURITY_CONFIG.allowedUIElements.includes(elementType)) {
|
|
@@ -1158,6 +309,21 @@ var HoloScriptParser = class {
|
|
|
1158
309
|
return this.parseDebugCommand(tokens.slice(1));
|
|
1159
310
|
case "visualize":
|
|
1160
311
|
return this.parseVisualizeCommand(tokens.slice(1));
|
|
312
|
+
case "composition":
|
|
313
|
+
return this.parseComposition(tokens.slice(1));
|
|
314
|
+
case "environment":
|
|
315
|
+
return this.parseEnvironment(tokens.slice(1));
|
|
316
|
+
case "template":
|
|
317
|
+
return this.parseTemplate(tokens.slice(1));
|
|
318
|
+
case "@state":
|
|
319
|
+
return this.parseStateDirective(tokens.slice(1));
|
|
320
|
+
case "every":
|
|
321
|
+
case "on_user_gesture":
|
|
322
|
+
return this.parseGlobalHandler(commandType, tokens.slice(1));
|
|
323
|
+
case "scale":
|
|
324
|
+
return this.parseScale(tokens.slice(1));
|
|
325
|
+
case "focus":
|
|
326
|
+
return this.parseFocus(tokens.slice(1));
|
|
1161
327
|
default:
|
|
1162
328
|
return this.parseGenericCommand(tokens);
|
|
1163
329
|
}
|
|
@@ -1196,19 +362,43 @@ var HoloScriptParser = class {
|
|
|
1196
362
|
if (tokens.length < 2) return [];
|
|
1197
363
|
const shape = tokens[0];
|
|
1198
364
|
const name = tokens[1];
|
|
365
|
+
let res = [];
|
|
1199
366
|
switch (shape) {
|
|
1200
367
|
case "orb":
|
|
1201
368
|
case "sphere":
|
|
1202
|
-
|
|
369
|
+
res = [this.createOrbNode(name, position)];
|
|
370
|
+
const orbProps = this.parseProperties(tokens.slice(2));
|
|
371
|
+
res[0].properties = orbProps;
|
|
372
|
+
break;
|
|
1203
373
|
case "function":
|
|
1204
|
-
|
|
374
|
+
res = [this.createFunctionNode(name, tokens.slice(2), position)];
|
|
375
|
+
break;
|
|
1205
376
|
case "gate":
|
|
1206
|
-
|
|
377
|
+
res = [this.createGateNode(name, tokens.slice(2), position)];
|
|
378
|
+
break;
|
|
1207
379
|
case "stream":
|
|
1208
|
-
|
|
380
|
+
res = [this.createStreamNode(name, tokens.slice(2), position)];
|
|
381
|
+
break;
|
|
382
|
+
case "server":
|
|
383
|
+
res = [this.createServerNode(tokens.slice(1), position)];
|
|
384
|
+
break;
|
|
385
|
+
case "database":
|
|
386
|
+
res = [this.createDatabaseNode(tokens.slice(1), position)];
|
|
387
|
+
break;
|
|
388
|
+
case "fetch":
|
|
389
|
+
res = [this.createFetchNode(tokens.slice(1), position)];
|
|
390
|
+
break;
|
|
1209
391
|
default:
|
|
1210
|
-
|
|
392
|
+
res = [this.createGenericNode(shape, name, position)];
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
if (res.length > 0) {
|
|
396
|
+
const directives = this.extractDirectives(tokens.slice(2));
|
|
397
|
+
if (directives.length > 0) {
|
|
398
|
+
res[0].directives = directives;
|
|
399
|
+
}
|
|
1211
400
|
}
|
|
401
|
+
return res;
|
|
1212
402
|
}
|
|
1213
403
|
parseConnectCommand(tokens) {
|
|
1214
404
|
if (tokens.length < 3) return [];
|
|
@@ -1317,6 +507,61 @@ var HoloScriptParser = class {
|
|
|
1317
507
|
}
|
|
1318
508
|
};
|
|
1319
509
|
}
|
|
510
|
+
createServerNode(params, position) {
|
|
511
|
+
const portIndex = params.indexOf("port");
|
|
512
|
+
const port = portIndex !== -1 ? parseInt(params[portIndex + 1]) : 3e3;
|
|
513
|
+
const routesIndex = params.indexOf("routes");
|
|
514
|
+
const routes = routesIndex !== -1 ? params[routesIndex + 1].split(",") : [];
|
|
515
|
+
return {
|
|
516
|
+
type: "server",
|
|
517
|
+
port,
|
|
518
|
+
routes,
|
|
519
|
+
position: position || { x: 0, y: 0, z: 0 },
|
|
520
|
+
hologram: {
|
|
521
|
+
shape: "cube",
|
|
522
|
+
color: "#000000",
|
|
523
|
+
// Black box
|
|
524
|
+
size: 2,
|
|
525
|
+
glow: true,
|
|
526
|
+
interactive: false
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
createDatabaseNode(params, position) {
|
|
531
|
+
const queryIndex = params.indexOf("query");
|
|
532
|
+
const query = queryIndex !== -1 ? params.slice(queryIndex + 1).join(" ") : "";
|
|
533
|
+
return {
|
|
534
|
+
type: "database",
|
|
535
|
+
query,
|
|
536
|
+
position: position || { x: 0, y: 0, z: 0 },
|
|
537
|
+
hologram: {
|
|
538
|
+
shape: "cylinder",
|
|
539
|
+
color: "#ffd700",
|
|
540
|
+
// Gold
|
|
541
|
+
size: 1.5,
|
|
542
|
+
glow: true,
|
|
543
|
+
interactive: true
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
createFetchNode(params, position) {
|
|
548
|
+
const urlIndex = params.indexOf("url");
|
|
549
|
+
const url = urlIndex !== -1 ? params[urlIndex + 1] : "";
|
|
550
|
+
return {
|
|
551
|
+
type: "fetch",
|
|
552
|
+
url,
|
|
553
|
+
method: "GET",
|
|
554
|
+
position: position || { x: 0, y: 0, z: 0 },
|
|
555
|
+
hologram: {
|
|
556
|
+
shape: "orb",
|
|
557
|
+
color: "#00ff00",
|
|
558
|
+
// Green
|
|
559
|
+
size: 0.8,
|
|
560
|
+
glow: true,
|
|
561
|
+
interactive: true
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
}
|
|
1320
565
|
parsePinchGesture(gesture) {
|
|
1321
566
|
return [{
|
|
1322
567
|
type: "create",
|
|
@@ -1347,7 +592,7 @@ var HoloScriptParser = class {
|
|
|
1347
592
|
}];
|
|
1348
593
|
}
|
|
1349
594
|
tokenizeCommand(command) {
|
|
1350
|
-
return command.
|
|
595
|
+
return command.replace(/[^\w\s.,:/=?&"'*()\[\]@%${}-]/g, " ").split(/\s+/).filter((token) => token.length > 0);
|
|
1351
596
|
}
|
|
1352
597
|
sanitizeTokens(tokens) {
|
|
1353
598
|
return tokens.filter((token) => {
|
|
@@ -1389,6 +634,167 @@ var HoloScriptParser = class {
|
|
|
1389
634
|
hologram: { shape: "orb", color: "#808080", size: 0.5, glow: false, interactive: true }
|
|
1390
635
|
}];
|
|
1391
636
|
}
|
|
637
|
+
parseComposition(tokens) {
|
|
638
|
+
const name = tokens[0] || "unnamed_composition";
|
|
639
|
+
return [{
|
|
640
|
+
type: "composition",
|
|
641
|
+
name,
|
|
642
|
+
children: [],
|
|
643
|
+
position: { x: 0, y: 0, z: 0 }
|
|
644
|
+
}];
|
|
645
|
+
}
|
|
646
|
+
parseEnvironment(tokens) {
|
|
647
|
+
const settings = {};
|
|
648
|
+
if (tokens.includes("fog")) settings.fog = true;
|
|
649
|
+
if (tokens.includes("audio")) settings.audio = tokens[tokens.indexOf("audio") + 1];
|
|
650
|
+
if (tokens.includes("theme")) settings.theme = tokens[tokens.indexOf("theme") + 1];
|
|
651
|
+
return [{
|
|
652
|
+
type: "environment",
|
|
653
|
+
settings
|
|
654
|
+
}];
|
|
655
|
+
}
|
|
656
|
+
parseTemplate(tokens) {
|
|
657
|
+
const name = tokens[0] || "template";
|
|
658
|
+
const parameters = tokens.slice(1).filter((t) => t !== "with" && t !== "params");
|
|
659
|
+
return [{
|
|
660
|
+
type: "template",
|
|
661
|
+
name,
|
|
662
|
+
parameters,
|
|
663
|
+
body: []
|
|
664
|
+
}];
|
|
665
|
+
}
|
|
666
|
+
parseGlobalHandler(type, tokens) {
|
|
667
|
+
return [{
|
|
668
|
+
type: "global_handler",
|
|
669
|
+
handlerType: type === "every" ? "every" : "on_gesture",
|
|
670
|
+
config: { value: tokens[0] },
|
|
671
|
+
action: tokens.slice(1).join(" ")
|
|
672
|
+
}];
|
|
673
|
+
}
|
|
674
|
+
parseScale(tokens) {
|
|
675
|
+
const magnitude = tokens[0] || "standard";
|
|
676
|
+
const multipliers = {
|
|
677
|
+
"galactic": 1e6,
|
|
678
|
+
"macro": 1e3,
|
|
679
|
+
"standard": 1,
|
|
680
|
+
"micro": 1e-3,
|
|
681
|
+
"atomic": 1e-6
|
|
682
|
+
};
|
|
683
|
+
return [{
|
|
684
|
+
type: "scale",
|
|
685
|
+
magnitude,
|
|
686
|
+
multiplier: multipliers[magnitude] || 1,
|
|
687
|
+
body: []
|
|
688
|
+
}];
|
|
689
|
+
}
|
|
690
|
+
parseFocus(tokens) {
|
|
691
|
+
return [{
|
|
692
|
+
type: "focus",
|
|
693
|
+
target: tokens[0] || "origin",
|
|
694
|
+
body: []
|
|
695
|
+
}];
|
|
696
|
+
}
|
|
697
|
+
// ============================================================================
|
|
698
|
+
// HS+ Directive Parsing
|
|
699
|
+
// ============================================================================
|
|
700
|
+
parseStateDirective(tokens) {
|
|
701
|
+
const body = {};
|
|
702
|
+
for (let i = 0; i < tokens.length; i += 2) {
|
|
703
|
+
if (tokens[i] && tokens[i + 1]) {
|
|
704
|
+
const key = tokens[i].replace(":", "");
|
|
705
|
+
const val = this.parseLiteral(tokens[i + 1]);
|
|
706
|
+
body[key] = val;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return [{
|
|
710
|
+
type: "state-declaration",
|
|
711
|
+
directives: [{ type: "state", body }]
|
|
712
|
+
}];
|
|
713
|
+
}
|
|
714
|
+
extractDirectives(tokens) {
|
|
715
|
+
const directives = [];
|
|
716
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
717
|
+
const token = tokens[i];
|
|
718
|
+
if (token.startsWith("@")) {
|
|
719
|
+
const name = token.slice(1);
|
|
720
|
+
if (this.isTrait(name)) {
|
|
721
|
+
let config = {};
|
|
722
|
+
if (tokens[i + 1] === "{") {
|
|
723
|
+
const closingIndex = this.findClosingBrace(tokens, i + 1);
|
|
724
|
+
if (closingIndex !== -1) {
|
|
725
|
+
config = this.parseProperties(tokens.slice(i + 2, closingIndex));
|
|
726
|
+
i = closingIndex;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
directives.push({
|
|
730
|
+
type: "trait",
|
|
731
|
+
name,
|
|
732
|
+
config
|
|
733
|
+
});
|
|
734
|
+
} else if (this.isLifecycleHook(name)) {
|
|
735
|
+
directives.push({
|
|
736
|
+
type: "lifecycle",
|
|
737
|
+
hook: name,
|
|
738
|
+
body: tokens[i + 1] || ""
|
|
739
|
+
// Assume next token is body for now
|
|
740
|
+
});
|
|
741
|
+
i++;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return directives;
|
|
746
|
+
}
|
|
747
|
+
isTrait(name) {
|
|
748
|
+
const traits = ["grabbable", "throwable", "pointable", "hoverable", "scalable", "rotatable", "stackable", "snappable", "breakable"];
|
|
749
|
+
return traits.includes(name);
|
|
750
|
+
}
|
|
751
|
+
isLifecycleHook(name) {
|
|
752
|
+
const hooks = ["on_mount", "on_unmount", "on_update", "on_data_update", "on_grab", "on_release", "on_click"];
|
|
753
|
+
return hooks.includes(name);
|
|
754
|
+
}
|
|
755
|
+
parseLiteral(val) {
|
|
756
|
+
if (val === "true") return true;
|
|
757
|
+
if (val === "false") return false;
|
|
758
|
+
if (val === "null") return null;
|
|
759
|
+
if (!isNaN(Number(val))) return Number(val);
|
|
760
|
+
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
761
|
+
return val.slice(1, -1);
|
|
762
|
+
}
|
|
763
|
+
return val;
|
|
764
|
+
}
|
|
765
|
+
parseProperties(tokens) {
|
|
766
|
+
const props = {};
|
|
767
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
768
|
+
const token = tokens[i];
|
|
769
|
+
if (token === "{" || token === "}") continue;
|
|
770
|
+
if (token.startsWith("@")) break;
|
|
771
|
+
if (token.endsWith(":")) {
|
|
772
|
+
const key = token.slice(0, -1);
|
|
773
|
+
const val = tokens[i + 1];
|
|
774
|
+
if (val) {
|
|
775
|
+
props[key] = this.parseLiteral(val);
|
|
776
|
+
i++;
|
|
777
|
+
}
|
|
778
|
+
} else if (tokens[i + 1] === ":") {
|
|
779
|
+
const key = token;
|
|
780
|
+
const val = tokens[i + 2];
|
|
781
|
+
if (val) {
|
|
782
|
+
props[key] = this.parseLiteral(val);
|
|
783
|
+
i += 2;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return props;
|
|
788
|
+
}
|
|
789
|
+
findClosingBrace(tokens, startIndex) {
|
|
790
|
+
let depth = 0;
|
|
791
|
+
for (let i = startIndex; i < tokens.length; i++) {
|
|
792
|
+
if (tokens[i] === "{") depth++;
|
|
793
|
+
else if (tokens[i] === "}") depth--;
|
|
794
|
+
if (depth === 0) return i;
|
|
795
|
+
}
|
|
796
|
+
return -1;
|
|
797
|
+
}
|
|
1392
798
|
getAST() {
|
|
1393
799
|
return [...this.ast];
|
|
1394
800
|
}
|
|
@@ -1418,8 +824,9 @@ var HoloScriptParser = class {
|
|
|
1418
824
|
}
|
|
1419
825
|
};
|
|
1420
826
|
|
|
1421
|
-
// src/
|
|
827
|
+
// src/constants.ts
|
|
1422
828
|
var VR_TRAITS = [
|
|
829
|
+
// Core VR Interaction Traits
|
|
1423
830
|
"grabbable",
|
|
1424
831
|
"throwable",
|
|
1425
832
|
"pointable",
|
|
@@ -1428,7 +835,56 @@ var VR_TRAITS = [
|
|
|
1428
835
|
"rotatable",
|
|
1429
836
|
"stackable",
|
|
1430
837
|
"snappable",
|
|
1431
|
-
"breakable"
|
|
838
|
+
"breakable",
|
|
839
|
+
"stretchable",
|
|
840
|
+
"moldable",
|
|
841
|
+
// Humanoid/Avatar Traits
|
|
842
|
+
"skeleton",
|
|
843
|
+
"body",
|
|
844
|
+
"face",
|
|
845
|
+
"expressive",
|
|
846
|
+
"hair",
|
|
847
|
+
"clothing",
|
|
848
|
+
"hands",
|
|
849
|
+
"character_voice",
|
|
850
|
+
"locomotion",
|
|
851
|
+
"poseable",
|
|
852
|
+
"morph",
|
|
853
|
+
// Networking & AI
|
|
854
|
+
"networked",
|
|
855
|
+
"proactive",
|
|
856
|
+
// Media Production Traits
|
|
857
|
+
"recordable",
|
|
858
|
+
"streamable",
|
|
859
|
+
"camera",
|
|
860
|
+
"video",
|
|
861
|
+
// Analytics & Research Traits
|
|
862
|
+
"trackable",
|
|
863
|
+
"survey",
|
|
864
|
+
"abtest",
|
|
865
|
+
"heatmap",
|
|
866
|
+
// Social & Viral Traits
|
|
867
|
+
"shareable",
|
|
868
|
+
"embeddable",
|
|
869
|
+
"qr",
|
|
870
|
+
"collaborative",
|
|
871
|
+
// Effects Traits
|
|
872
|
+
"particle",
|
|
873
|
+
"transition",
|
|
874
|
+
"filter",
|
|
875
|
+
"trail",
|
|
876
|
+
// Audio Traits
|
|
877
|
+
"spatial_audio",
|
|
878
|
+
"voice",
|
|
879
|
+
"reactive_audio",
|
|
880
|
+
// AI & Generative Traits
|
|
881
|
+
"narrator",
|
|
882
|
+
"responsive",
|
|
883
|
+
"procedural",
|
|
884
|
+
"captioned",
|
|
885
|
+
// Timeline & Choreography Traits
|
|
886
|
+
"timeline",
|
|
887
|
+
"choreography"
|
|
1432
888
|
];
|
|
1433
889
|
var LIFECYCLE_HOOKS = [
|
|
1434
890
|
// Standard lifecycle
|
|
@@ -1453,8 +909,152 @@ var LIFECYCLE_HOOKS = [
|
|
|
1453
909
|
"on_trigger_hold",
|
|
1454
910
|
"on_trigger_release",
|
|
1455
911
|
"on_grip_hold",
|
|
1456
|
-
"on_grip_release"
|
|
912
|
+
"on_grip_release",
|
|
913
|
+
// Stretchable/Moldable hooks
|
|
914
|
+
"on_stretch",
|
|
915
|
+
"on_sculpt",
|
|
916
|
+
// Humanoid/Avatar hooks
|
|
917
|
+
"on_pose_change",
|
|
918
|
+
"on_expression_change",
|
|
919
|
+
"on_gesture",
|
|
920
|
+
"on_speak",
|
|
921
|
+
"on_pose_save",
|
|
922
|
+
"on_morph_change",
|
|
923
|
+
// Media Production hooks
|
|
924
|
+
"on_record_start",
|
|
925
|
+
"on_record_stop",
|
|
926
|
+
"on_record_pause",
|
|
927
|
+
"on_stream_start",
|
|
928
|
+
"on_stream_stop",
|
|
929
|
+
"on_viewer_join",
|
|
930
|
+
"on_viewer_leave",
|
|
931
|
+
"on_chat_message",
|
|
932
|
+
"on_camera_switch",
|
|
933
|
+
"on_video_end",
|
|
934
|
+
"on_video_error",
|
|
935
|
+
// Analytics hooks
|
|
936
|
+
"on_track_event",
|
|
937
|
+
"on_survey_start",
|
|
938
|
+
"on_survey_complete",
|
|
939
|
+
"on_survey_skip",
|
|
940
|
+
"on_variant_assigned",
|
|
941
|
+
"on_conversion",
|
|
942
|
+
"on_hotspot_detected",
|
|
943
|
+
// Social hooks
|
|
944
|
+
"on_share",
|
|
945
|
+
"on_share_complete",
|
|
946
|
+
"on_embed",
|
|
947
|
+
"on_scan",
|
|
948
|
+
"on_user_join",
|
|
949
|
+
"on_user_leave",
|
|
950
|
+
"on_draw_stroke",
|
|
951
|
+
"on_object_lock",
|
|
952
|
+
"on_object_unlock",
|
|
953
|
+
// Effects hooks
|
|
954
|
+
"on_particle_spawn",
|
|
955
|
+
"on_particle_death",
|
|
956
|
+
"on_transition_start",
|
|
957
|
+
"on_transition_complete",
|
|
958
|
+
"on_filter_change",
|
|
959
|
+
// Audio hooks
|
|
960
|
+
"on_audio_start",
|
|
961
|
+
"on_audio_end",
|
|
962
|
+
"on_voice_command",
|
|
963
|
+
"on_speech_start",
|
|
964
|
+
"on_speech_end",
|
|
965
|
+
"on_beat",
|
|
966
|
+
"on_frequency_peak",
|
|
967
|
+
// AI hooks
|
|
968
|
+
"on_narration_start",
|
|
969
|
+
"on_narration_end",
|
|
970
|
+
"on_user_question",
|
|
971
|
+
"on_response_ready",
|
|
972
|
+
"on_emotion_change",
|
|
973
|
+
"on_generation_complete",
|
|
974
|
+
// Timeline hooks
|
|
975
|
+
"on_timeline_start",
|
|
976
|
+
"on_timeline_complete",
|
|
977
|
+
"on_keyframe_hit",
|
|
978
|
+
"on_keyframe_add",
|
|
979
|
+
"on_beat_sync",
|
|
980
|
+
"on_move_complete"
|
|
981
|
+
];
|
|
982
|
+
|
|
983
|
+
// src/HoloScriptValidator.ts
|
|
984
|
+
var _HoloScriptValidator = class _HoloScriptValidator {
|
|
985
|
+
constructor() {
|
|
986
|
+
this.parser = new HoloScriptCodeParser();
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Validates source code and returns a list of errors.
|
|
990
|
+
*/
|
|
991
|
+
validate(code) {
|
|
992
|
+
const errors = [];
|
|
993
|
+
try {
|
|
994
|
+
this.parser.parse(code);
|
|
995
|
+
} catch (e) {
|
|
996
|
+
errors.push({
|
|
997
|
+
line: e.line || 1,
|
|
998
|
+
// Fallback if parser doesn't provide line
|
|
999
|
+
column: e.column || 1,
|
|
1000
|
+
message: e.message || "Syntax Error",
|
|
1001
|
+
severity: "error"
|
|
1002
|
+
});
|
|
1003
|
+
return errors;
|
|
1004
|
+
}
|
|
1005
|
+
const lines = code.split("\n");
|
|
1006
|
+
lines.forEach((line, index) => {
|
|
1007
|
+
const lineNum = index + 1;
|
|
1008
|
+
const trimmed = line.trim();
|
|
1009
|
+
if (!trimmed || trimmed.startsWith("//")) return;
|
|
1010
|
+
if (trimmed.startsWith("@")) {
|
|
1011
|
+
const match = trimmed.match(/^@(\w+)/);
|
|
1012
|
+
if (match) {
|
|
1013
|
+
const directive = match[1];
|
|
1014
|
+
if (!_HoloScriptValidator.VALID_DIRECTIVES.includes(directive)) {
|
|
1015
|
+
errors.push({
|
|
1016
|
+
line: lineNum,
|
|
1017
|
+
column: line.indexOf("@") + 1,
|
|
1018
|
+
message: `Unknown directive '@${directive}'. Allowed: ${_HoloScriptValidator.VALID_DIRECTIVES.join(", ")}`,
|
|
1019
|
+
severity: "warning"
|
|
1020
|
+
// directives might be custom, so warning for now
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
if (trimmed.endsWith("{")) {
|
|
1026
|
+
const firstWord = trimmed.split(" ")[0];
|
|
1027
|
+
if (line.match(/^[a-z]+\s+[\w#]+\s*\{$/)) {
|
|
1028
|
+
if (!_HoloScriptValidator.VALID_KEYWORDS.includes(firstWord) && !trimmed.startsWith("trait")) ;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
return errors;
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
// Whitelists
|
|
1036
|
+
_HoloScriptValidator.VALID_DIRECTIVES = [
|
|
1037
|
+
"trait",
|
|
1038
|
+
"state",
|
|
1039
|
+
"on_enter",
|
|
1040
|
+
"on_exit",
|
|
1041
|
+
"on_mount",
|
|
1042
|
+
"on_tick",
|
|
1043
|
+
"on_create",
|
|
1044
|
+
"bot_config",
|
|
1045
|
+
"lifecycle"
|
|
1046
|
+
];
|
|
1047
|
+
_HoloScriptValidator.VALID_KEYWORDS = [
|
|
1048
|
+
"world",
|
|
1049
|
+
"scene",
|
|
1050
|
+
"prefab",
|
|
1051
|
+
"object",
|
|
1052
|
+
"import",
|
|
1053
|
+
"export"
|
|
1457
1054
|
];
|
|
1055
|
+
var HoloScriptValidator = _HoloScriptValidator;
|
|
1056
|
+
|
|
1057
|
+
// src/parser/HoloScriptPlusParser.ts
|
|
1458
1058
|
var Lexer = class {
|
|
1459
1059
|
constructor(source) {
|
|
1460
1060
|
this.pos = 0;
|
|
@@ -1848,6 +1448,10 @@ var HoloScriptPlusParser = class {
|
|
|
1848
1448
|
this.pos = 0;
|
|
1849
1449
|
const lexer = new Lexer(source);
|
|
1850
1450
|
this.tokens = lexer.tokenize();
|
|
1451
|
+
try {
|
|
1452
|
+
__require("fs").writeFileSync("tokens.log", JSON.stringify(this.tokens, null, 2));
|
|
1453
|
+
} catch (e) {
|
|
1454
|
+
}
|
|
1851
1455
|
const root = this.parseDocument();
|
|
1852
1456
|
const ast = {
|
|
1853
1457
|
type: "Program",
|
|
@@ -1885,6 +1489,20 @@ var HoloScriptPlusParser = class {
|
|
|
1885
1489
|
}
|
|
1886
1490
|
this.skipNewlines();
|
|
1887
1491
|
}
|
|
1492
|
+
if (this.check("EOF") && directives.length > 0) {
|
|
1493
|
+
return {
|
|
1494
|
+
type: "fragment",
|
|
1495
|
+
id: "root",
|
|
1496
|
+
properties: {},
|
|
1497
|
+
directives,
|
|
1498
|
+
children: [],
|
|
1499
|
+
traits: /* @__PURE__ */ new Map(),
|
|
1500
|
+
loc: {
|
|
1501
|
+
start: { line: 1, column: 1 },
|
|
1502
|
+
end: { line: this.current().line, column: this.current().column }
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1888
1506
|
const root = this.parseNode();
|
|
1889
1507
|
root.directives = [...directives, ...root.directives];
|
|
1890
1508
|
return root;
|
|
@@ -2018,6 +1636,20 @@ var HoloScriptPlusParser = class {
|
|
|
2018
1636
|
const body = this.parseControlFlowBody();
|
|
2019
1637
|
return { type: "for", variable, iterable, body };
|
|
2020
1638
|
}
|
|
1639
|
+
if (name === "forEach") {
|
|
1640
|
+
this.hasControlFlow = true;
|
|
1641
|
+
const variable = this.expect("IDENTIFIER", "Expected variable name").value;
|
|
1642
|
+
this.expect("IDENTIFIER", 'Expected "in"');
|
|
1643
|
+
const collection = this.parseInlineExpression();
|
|
1644
|
+
const body = this.parseControlFlowBody();
|
|
1645
|
+
return { type: "forEach", variable, collection, body };
|
|
1646
|
+
}
|
|
1647
|
+
if (name === "while") {
|
|
1648
|
+
this.hasControlFlow = true;
|
|
1649
|
+
const condition = this.parseInlineExpression();
|
|
1650
|
+
const body = this.parseControlFlowBody();
|
|
1651
|
+
return { type: "while", condition, body };
|
|
1652
|
+
}
|
|
2021
1653
|
if (name === "if") {
|
|
2022
1654
|
this.hasControlFlow = true;
|
|
2023
1655
|
const condition = this.parseInlineExpression();
|
|
@@ -2050,6 +1682,39 @@ var HoloScriptPlusParser = class {
|
|
|
2050
1682
|
this.imports.push({ path, alias });
|
|
2051
1683
|
return { type: "import", path, alias };
|
|
2052
1684
|
}
|
|
1685
|
+
if (name === "external_api") {
|
|
1686
|
+
const config = this.parseTraitConfig();
|
|
1687
|
+
const url = config.url || "";
|
|
1688
|
+
const method = config.method || "GET";
|
|
1689
|
+
const interval = config.interval || "0s";
|
|
1690
|
+
let body = [];
|
|
1691
|
+
if (this.check("LBRACE")) {
|
|
1692
|
+
body = this.parseControlFlowBody();
|
|
1693
|
+
}
|
|
1694
|
+
return { type: "external_api", url, method, interval, body };
|
|
1695
|
+
}
|
|
1696
|
+
if (name === "generate") {
|
|
1697
|
+
const config = this.parseTraitConfig();
|
|
1698
|
+
const prompt = config.prompt || "";
|
|
1699
|
+
const context = config.context || "";
|
|
1700
|
+
const target = config.target || "children";
|
|
1701
|
+
return { type: "generate", prompt, context, target };
|
|
1702
|
+
}
|
|
1703
|
+
if (name === "npc") {
|
|
1704
|
+
try {
|
|
1705
|
+
__require("fs").appendFileSync("debug_parser.log", `ENTERED NPC BLOCK. Name length: ${name.length}
|
|
1706
|
+
`);
|
|
1707
|
+
} catch (e) {
|
|
1708
|
+
}
|
|
1709
|
+
const npcName = this.expect("STRING", "Expected NPC name").value;
|
|
1710
|
+
const props = this.parsePropsBlock();
|
|
1711
|
+
return { type: "npc", name: npcName, props };
|
|
1712
|
+
}
|
|
1713
|
+
if (name === "dialog") {
|
|
1714
|
+
const dialogName = this.expect("STRING", "Expected dialog name").value;
|
|
1715
|
+
const { props, options } = this.parseDialogBlock();
|
|
1716
|
+
return { type: "dialog", name: dialogName, props, options };
|
|
1717
|
+
}
|
|
2053
1718
|
if (this.options.strict) {
|
|
2054
1719
|
this.error(`Unknown directive @${name}`);
|
|
2055
1720
|
} else {
|
|
@@ -2057,34 +1722,89 @@ var HoloScriptPlusParser = class {
|
|
|
2057
1722
|
}
|
|
2058
1723
|
return null;
|
|
2059
1724
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
1725
|
+
parsePropsBlock() {
|
|
1726
|
+
this.skipNewlines();
|
|
1727
|
+
const props = {};
|
|
1728
|
+
if (this.check("LBRACE")) {
|
|
2063
1729
|
this.advance();
|
|
2064
|
-
|
|
1730
|
+
this.skipNewlines();
|
|
1731
|
+
while (!this.check("RBRACE") && !this.check("EOF")) {
|
|
2065
1732
|
const key = this.expect("IDENTIFIER", "Expected property name").value;
|
|
2066
1733
|
if (this.check("COLON") || this.check("EQUALS")) {
|
|
2067
1734
|
this.advance();
|
|
2068
|
-
|
|
1735
|
+
props[key] = this.parseValue();
|
|
2069
1736
|
} else {
|
|
2070
|
-
|
|
1737
|
+
props[key] = true;
|
|
2071
1738
|
}
|
|
2072
|
-
|
|
1739
|
+
this.skipNewlines();
|
|
2073
1740
|
}
|
|
2074
|
-
this.expect("
|
|
1741
|
+
this.expect("RBRACE", "Expected }");
|
|
2075
1742
|
}
|
|
2076
|
-
return
|
|
1743
|
+
return props;
|
|
2077
1744
|
}
|
|
2078
|
-
|
|
2079
|
-
|
|
1745
|
+
parseDialogBlock() {
|
|
1746
|
+
this.skipNewlines();
|
|
1747
|
+
const props = {};
|
|
1748
|
+
const options = [];
|
|
2080
1749
|
if (this.check("LBRACE")) {
|
|
2081
1750
|
this.advance();
|
|
2082
1751
|
this.skipNewlines();
|
|
2083
1752
|
while (!this.check("RBRACE") && !this.check("EOF")) {
|
|
2084
|
-
|
|
2085
|
-
if (this.check("COLON") || this.check("EQUALS")) {
|
|
1753
|
+
if (this.check("IDENTIFIER") && this.current().value === "option") {
|
|
2086
1754
|
this.advance();
|
|
2087
|
-
|
|
1755
|
+
const text = this.expect("STRING", "Expected option text").value;
|
|
1756
|
+
this.expect("ARROW", "Expected ->");
|
|
1757
|
+
let target;
|
|
1758
|
+
if (this.check("AT")) {
|
|
1759
|
+
const d = this.parseDirective();
|
|
1760
|
+
target = { type: "directive", value: d };
|
|
1761
|
+
} else {
|
|
1762
|
+
target = this.expect("STRING", "Expected target ID").value;
|
|
1763
|
+
}
|
|
1764
|
+
options.push({ text, target });
|
|
1765
|
+
} else {
|
|
1766
|
+
const key = this.expect("IDENTIFIER", "Expected property name").value;
|
|
1767
|
+
if (this.check("COLON") || this.check("EQUALS")) {
|
|
1768
|
+
this.advance();
|
|
1769
|
+
props[key] = this.parseValue();
|
|
1770
|
+
} else {
|
|
1771
|
+
props[key] = true;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
this.skipNewlines();
|
|
1775
|
+
}
|
|
1776
|
+
this.expect("RBRACE", "Expected }");
|
|
1777
|
+
}
|
|
1778
|
+
return { props, options };
|
|
1779
|
+
}
|
|
1780
|
+
parseTraitConfig() {
|
|
1781
|
+
const config = {};
|
|
1782
|
+
if (this.check("LPAREN")) {
|
|
1783
|
+
this.advance();
|
|
1784
|
+
while (!this.check("RPAREN") && !this.check("EOF")) {
|
|
1785
|
+
const key = this.expect("IDENTIFIER", "Expected property name").value;
|
|
1786
|
+
if (this.check("COLON") || this.check("EQUALS")) {
|
|
1787
|
+
this.advance();
|
|
1788
|
+
config[key] = this.parseValue();
|
|
1789
|
+
} else {
|
|
1790
|
+
config[key] = true;
|
|
1791
|
+
}
|
|
1792
|
+
if (this.check("COMMA")) this.advance();
|
|
1793
|
+
}
|
|
1794
|
+
this.expect("RPAREN", "Expected )");
|
|
1795
|
+
}
|
|
1796
|
+
return config;
|
|
1797
|
+
}
|
|
1798
|
+
parseStateBlock() {
|
|
1799
|
+
const state = {};
|
|
1800
|
+
if (this.check("LBRACE")) {
|
|
1801
|
+
this.advance();
|
|
1802
|
+
this.skipNewlines();
|
|
1803
|
+
while (!this.check("RBRACE") && !this.check("EOF")) {
|
|
1804
|
+
const key = this.expect("IDENTIFIER", "Expected state variable name").value;
|
|
1805
|
+
if (this.check("COLON") || this.check("EQUALS")) {
|
|
1806
|
+
this.advance();
|
|
1807
|
+
state[key] = this.parseValue();
|
|
2088
1808
|
} else {
|
|
2089
1809
|
state[key] = null;
|
|
2090
1810
|
}
|
|
@@ -2233,51 +1953,866 @@ var HoloScriptPlusParser = class {
|
|
|
2233
1953
|
current() {
|
|
2234
1954
|
return this.tokens[this.pos] || { type: "EOF", value: "", line: 0, column: 0 };
|
|
2235
1955
|
}
|
|
2236
|
-
check(type) {
|
|
2237
|
-
return this.current().type === type;
|
|
1956
|
+
check(type) {
|
|
1957
|
+
return this.current().type === type;
|
|
1958
|
+
}
|
|
1959
|
+
advance() {
|
|
1960
|
+
const token = this.current();
|
|
1961
|
+
if (this.pos < this.tokens.length) {
|
|
1962
|
+
this.pos++;
|
|
1963
|
+
}
|
|
1964
|
+
return token;
|
|
1965
|
+
}
|
|
1966
|
+
expect(type, message) {
|
|
1967
|
+
if (!this.check(type)) {
|
|
1968
|
+
this.error(`${message}. Got ${this.current().type} "${this.current().value}"`);
|
|
1969
|
+
return { type, value: "", line: this.current().line, column: this.current().column };
|
|
1970
|
+
}
|
|
1971
|
+
return this.advance();
|
|
1972
|
+
}
|
|
1973
|
+
skipNewlines() {
|
|
1974
|
+
while (this.check("NEWLINE") || this.check("INDENT") || this.check("DEDENT")) {
|
|
1975
|
+
this.advance();
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
error(message) {
|
|
1979
|
+
const token = this.current();
|
|
1980
|
+
this.errors.push({
|
|
1981
|
+
message,
|
|
1982
|
+
line: token.line,
|
|
1983
|
+
column: token.column
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
warn(message) {
|
|
1987
|
+
const token = this.current();
|
|
1988
|
+
this.warnings.push({
|
|
1989
|
+
message,
|
|
1990
|
+
line: token.line,
|
|
1991
|
+
column: token.column
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
function createParser(options) {
|
|
1996
|
+
return new HoloScriptPlusParser(options);
|
|
1997
|
+
}
|
|
1998
|
+
function parse(source, options) {
|
|
1999
|
+
const parser = createParser(options);
|
|
2000
|
+
return parser.parse(source);
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
// src/traits/MaterialTrait.ts
|
|
2004
|
+
var MaterialTrait = class {
|
|
2005
|
+
constructor(config) {
|
|
2006
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
2007
|
+
this.material = {
|
|
2008
|
+
...{ type: "pbr" },
|
|
2009
|
+
...config
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Get material properties
|
|
2014
|
+
*/
|
|
2015
|
+
getMaterial() {
|
|
2016
|
+
return { ...this.material };
|
|
2017
|
+
}
|
|
2018
|
+
/**
|
|
2019
|
+
* Update material property
|
|
2020
|
+
*/
|
|
2021
|
+
setProperty(key, value) {
|
|
2022
|
+
this.material[key] = value;
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Get PBR properties
|
|
2026
|
+
*/
|
|
2027
|
+
getPBRProperties() {
|
|
2028
|
+
return this.material.pbr;
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Update PBR material
|
|
2032
|
+
*/
|
|
2033
|
+
updatePBR(pbr) {
|
|
2034
|
+
if (!this.material.pbr) {
|
|
2035
|
+
this.material.pbr = {
|
|
2036
|
+
baseColor: { r: 1, g: 1, b: 1 },
|
|
2037
|
+
metallic: 0,
|
|
2038
|
+
roughness: 0.5
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
this.material.pbr = { ...this.material.pbr, ...pbr };
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Add texture map
|
|
2045
|
+
*/
|
|
2046
|
+
addTexture(texture) {
|
|
2047
|
+
if (!this.material.textures) {
|
|
2048
|
+
this.material.textures = [];
|
|
2049
|
+
}
|
|
2050
|
+
this.material.textures.push(texture);
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Get all textures
|
|
2054
|
+
*/
|
|
2055
|
+
getTextures() {
|
|
2056
|
+
return [...this.material.textures || []];
|
|
2057
|
+
}
|
|
2058
|
+
/**
|
|
2059
|
+
* Clear texture cache (for memory optimization)
|
|
2060
|
+
*/
|
|
2061
|
+
clearTextureCache() {
|
|
2062
|
+
this.textureCache.clear();
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* Get shader code if custom
|
|
2066
|
+
*/
|
|
2067
|
+
getCustomShader() {
|
|
2068
|
+
return this.material.customShader;
|
|
2069
|
+
}
|
|
2070
|
+
/**
|
|
2071
|
+
* Set custom shader
|
|
2072
|
+
*/
|
|
2073
|
+
setCustomShader(shader) {
|
|
2074
|
+
this.material.customShader = shader;
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Get optimization hints
|
|
2078
|
+
*/
|
|
2079
|
+
getOptimization() {
|
|
2080
|
+
return this.material.optimization;
|
|
2081
|
+
}
|
|
2082
|
+
/**
|
|
2083
|
+
* Enable/disable texture streaming
|
|
2084
|
+
*/
|
|
2085
|
+
setTextureStreaming(enabled) {
|
|
2086
|
+
if (!this.material.optimization) {
|
|
2087
|
+
this.material.optimization = {};
|
|
2088
|
+
}
|
|
2089
|
+
this.material.optimization.streamTextures = enabled;
|
|
2090
|
+
}
|
|
2091
|
+
/**
|
|
2092
|
+
* Set texture compression
|
|
2093
|
+
*/
|
|
2094
|
+
setCompression(compression) {
|
|
2095
|
+
if (!this.material.optimization) {
|
|
2096
|
+
this.material.optimization = {};
|
|
2097
|
+
}
|
|
2098
|
+
this.material.optimization.compression = compression;
|
|
2099
|
+
}
|
|
2100
|
+
/**
|
|
2101
|
+
* Enable material instancing for performance
|
|
2102
|
+
*/
|
|
2103
|
+
setInstanced(instanced) {
|
|
2104
|
+
if (!this.material.optimization) {
|
|
2105
|
+
this.material.optimization = {};
|
|
2106
|
+
}
|
|
2107
|
+
this.material.optimization.instanced = instanced;
|
|
2108
|
+
}
|
|
2109
|
+
/**
|
|
2110
|
+
* Dispose and cleanup
|
|
2111
|
+
*/
|
|
2112
|
+
dispose() {
|
|
2113
|
+
this.textureCache.clear();
|
|
2114
|
+
}
|
|
2115
|
+
};
|
|
2116
|
+
function createMaterialTrait(config) {
|
|
2117
|
+
return new MaterialTrait(config);
|
|
2118
|
+
}
|
|
2119
|
+
var MATERIAL_PRESETS = {
|
|
2120
|
+
/** Shiny metal */
|
|
2121
|
+
chrome: () => ({
|
|
2122
|
+
type: "pbr",
|
|
2123
|
+
pbr: {
|
|
2124
|
+
baseColor: { r: 0.77, g: 0.77, b: 0.77 },
|
|
2125
|
+
metallic: 1,
|
|
2126
|
+
roughness: 0.1
|
|
2127
|
+
}
|
|
2128
|
+
}),
|
|
2129
|
+
/** Rough plastic */
|
|
2130
|
+
plastic: () => ({
|
|
2131
|
+
type: "pbr",
|
|
2132
|
+
pbr: {
|
|
2133
|
+
baseColor: { r: 1, g: 1, b: 1 },
|
|
2134
|
+
metallic: 0,
|
|
2135
|
+
roughness: 0.8
|
|
2136
|
+
}
|
|
2137
|
+
}),
|
|
2138
|
+
/** Wood texture */
|
|
2139
|
+
wood: () => ({
|
|
2140
|
+
type: "pbr",
|
|
2141
|
+
pbr: {
|
|
2142
|
+
baseColor: { r: 0.6, g: 0.4, b: 0.2 },
|
|
2143
|
+
metallic: 0,
|
|
2144
|
+
roughness: 0.4
|
|
2145
|
+
}
|
|
2146
|
+
}),
|
|
2147
|
+
/** Glass */
|
|
2148
|
+
glass: () => ({
|
|
2149
|
+
type: "transparent",
|
|
2150
|
+
blendMode: "blend",
|
|
2151
|
+
pbr: {
|
|
2152
|
+
baseColor: { r: 1, g: 1, b: 1, a: 0.3 },
|
|
2153
|
+
metallic: 0,
|
|
2154
|
+
roughness: 0,
|
|
2155
|
+
ior: 1.5,
|
|
2156
|
+
transmission: 0.9
|
|
2157
|
+
}
|
|
2158
|
+
}),
|
|
2159
|
+
/** Emissive (glowing) */
|
|
2160
|
+
emissive: () => ({
|
|
2161
|
+
type: "pbr",
|
|
2162
|
+
pbr: {
|
|
2163
|
+
baseColor: { r: 0, g: 1, b: 0 },
|
|
2164
|
+
metallic: 0,
|
|
2165
|
+
roughness: 1,
|
|
2166
|
+
emission: {
|
|
2167
|
+
color: { r: 0, g: 1, b: 0 },
|
|
2168
|
+
intensity: 2
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}),
|
|
2172
|
+
/** Skin material */
|
|
2173
|
+
skin: () => ({
|
|
2174
|
+
type: "pbr",
|
|
2175
|
+
pbr: {
|
|
2176
|
+
baseColor: { r: 1, g: 0.8, b: 0.7 },
|
|
2177
|
+
metallic: 0,
|
|
2178
|
+
roughness: 0.5,
|
|
2179
|
+
ambientOcclusion: 0.8
|
|
2180
|
+
}
|
|
2181
|
+
})
|
|
2182
|
+
};
|
|
2183
|
+
|
|
2184
|
+
// src/traits/LightingTrait.ts
|
|
2185
|
+
var LightingTrait = class {
|
|
2186
|
+
constructor(config) {
|
|
2187
|
+
this.lights = /* @__PURE__ */ new Map();
|
|
2188
|
+
this.lightIdCounter = 0;
|
|
2189
|
+
this.globalIllumination = {
|
|
2190
|
+
enabled: true,
|
|
2191
|
+
intensity: 1,
|
|
2192
|
+
skyColor: { r: 0.5, g: 0.7, b: 1 },
|
|
2193
|
+
skyIntensity: 1,
|
|
2194
|
+
groundColor: { r: 0.4, g: 0.4, b: 0.4 },
|
|
2195
|
+
groundIntensity: 0.5,
|
|
2196
|
+
probes: true,
|
|
2197
|
+
indirectDiffuse: 1,
|
|
2198
|
+
indirectSpecular: 1,
|
|
2199
|
+
aoIntensity: 1,
|
|
2200
|
+
screenSpaceAO: true,
|
|
2201
|
+
...config
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
/**
|
|
2205
|
+
* Add a light to the scene
|
|
2206
|
+
*/
|
|
2207
|
+
addLight(light) {
|
|
2208
|
+
const id = light.name || `light_${this.lightIdCounter++}`;
|
|
2209
|
+
this.lights.set(id, light);
|
|
2210
|
+
return id;
|
|
2211
|
+
}
|
|
2212
|
+
/**
|
|
2213
|
+
* Get light by ID
|
|
2214
|
+
*/
|
|
2215
|
+
getLight(id) {
|
|
2216
|
+
return this.lights.get(id);
|
|
2217
|
+
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Get all lights
|
|
2220
|
+
*/
|
|
2221
|
+
getLights() {
|
|
2222
|
+
return Array.from(this.lights.values());
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* Get lights by type
|
|
2226
|
+
*/
|
|
2227
|
+
getLightsByType(type) {
|
|
2228
|
+
return Array.from(this.lights.values()).filter((l) => l.type === type);
|
|
2229
|
+
}
|
|
2230
|
+
/**
|
|
2231
|
+
* Update light properties
|
|
2232
|
+
*/
|
|
2233
|
+
updateLight(id, updates) {
|
|
2234
|
+
const light = this.lights.get(id);
|
|
2235
|
+
if (!light) return false;
|
|
2236
|
+
this.lights.set(id, { ...light, ...updates });
|
|
2237
|
+
return true;
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Remove light
|
|
2241
|
+
*/
|
|
2242
|
+
removeLight(id) {
|
|
2243
|
+
return this.lights.delete(id);
|
|
2244
|
+
}
|
|
2245
|
+
/**
|
|
2246
|
+
* Clear all lights
|
|
2247
|
+
*/
|
|
2248
|
+
clearLights() {
|
|
2249
|
+
this.lights.clear();
|
|
2250
|
+
}
|
|
2251
|
+
/**
|
|
2252
|
+
* Get global illumination config
|
|
2253
|
+
*/
|
|
2254
|
+
getGlobalIllumination() {
|
|
2255
|
+
return { ...this.globalIllumination };
|
|
2256
|
+
}
|
|
2257
|
+
/**
|
|
2258
|
+
* Update global illumination
|
|
2259
|
+
*/
|
|
2260
|
+
updateGlobalIllumination(updates) {
|
|
2261
|
+
this.globalIllumination = {
|
|
2262
|
+
...this.globalIllumination,
|
|
2263
|
+
...updates
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Enable/disable GI
|
|
2268
|
+
*/
|
|
2269
|
+
setGIEnabled(enabled) {
|
|
2270
|
+
this.globalIllumination.enabled = enabled;
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Set ambient light colors (skybox mode)
|
|
2274
|
+
*/
|
|
2275
|
+
setAmbientLight(skyColor, groundColor, intensity = 1) {
|
|
2276
|
+
this.globalIllumination.skyColor = skyColor;
|
|
2277
|
+
this.globalIllumination.groundColor = groundColor;
|
|
2278
|
+
this.globalIllumination.skyIntensity = intensity;
|
|
2279
|
+
this.globalIllumination.groundIntensity = intensity * 0.5;
|
|
2280
|
+
}
|
|
2281
|
+
/**
|
|
2282
|
+
* Enable/disable screen-space ambient occlusion
|
|
2283
|
+
*/
|
|
2284
|
+
setScreenSpaceAO(enabled, intensity = 1) {
|
|
2285
|
+
this.globalIllumination.screenSpaceAO = enabled;
|
|
2286
|
+
this.globalIllumination.aoIntensity = intensity;
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Create directional light (sun)
|
|
2290
|
+
*/
|
|
2291
|
+
createDirectionalLight(direction, color, intensity = 1, castShadows = true) {
|
|
2292
|
+
const light = {
|
|
2293
|
+
type: "directional",
|
|
2294
|
+
name: `sun_${this.lightIdCounter}`,
|
|
2295
|
+
direction,
|
|
2296
|
+
color,
|
|
2297
|
+
intensity,
|
|
2298
|
+
shadow: castShadows ? {
|
|
2299
|
+
type: "soft",
|
|
2300
|
+
resolution: 2048,
|
|
2301
|
+
cascades: 4,
|
|
2302
|
+
softness: 1
|
|
2303
|
+
} : void 0,
|
|
2304
|
+
volumetric: true,
|
|
2305
|
+
priority: 100
|
|
2306
|
+
};
|
|
2307
|
+
return this.addLight(light);
|
|
2308
|
+
}
|
|
2309
|
+
/**
|
|
2310
|
+
* Create point light
|
|
2311
|
+
*/
|
|
2312
|
+
createPointLight(position, color, intensity, range, castShadows = false) {
|
|
2313
|
+
const light = {
|
|
2314
|
+
type: "point",
|
|
2315
|
+
name: `point_${this.lightIdCounter}`,
|
|
2316
|
+
position,
|
|
2317
|
+
color,
|
|
2318
|
+
intensity,
|
|
2319
|
+
range,
|
|
2320
|
+
shadow: castShadows ? {
|
|
2321
|
+
type: "soft",
|
|
2322
|
+
resolution: 512,
|
|
2323
|
+
softness: 0.5
|
|
2324
|
+
} : void 0,
|
|
2325
|
+
priority: 50
|
|
2326
|
+
};
|
|
2327
|
+
return this.addLight(light);
|
|
2328
|
+
}
|
|
2329
|
+
/**
|
|
2330
|
+
* Create spot light
|
|
2331
|
+
*/
|
|
2332
|
+
createSpotLight(position, direction, color, intensity, range, spotAngle = 45, castShadows = true) {
|
|
2333
|
+
const light = {
|
|
2334
|
+
type: "spot",
|
|
2335
|
+
name: `spot_${this.lightIdCounter}`,
|
|
2336
|
+
position,
|
|
2337
|
+
direction,
|
|
2338
|
+
color,
|
|
2339
|
+
intensity,
|
|
2340
|
+
range,
|
|
2341
|
+
spotAngle,
|
|
2342
|
+
innerSpotAngle: spotAngle * 0.5,
|
|
2343
|
+
shadow: castShadows ? {
|
|
2344
|
+
type: "soft",
|
|
2345
|
+
resolution: 1024,
|
|
2346
|
+
softness: 0.8
|
|
2347
|
+
} : void 0,
|
|
2348
|
+
priority: 75
|
|
2349
|
+
};
|
|
2350
|
+
return this.addLight(light);
|
|
2351
|
+
}
|
|
2352
|
+
/**
|
|
2353
|
+
* Create area light for soft lighting
|
|
2354
|
+
*/
|
|
2355
|
+
createAreaLight(position, color, intensity, width, height) {
|
|
2356
|
+
const light = {
|
|
2357
|
+
type: "area",
|
|
2358
|
+
name: `area_${this.lightIdCounter}`,
|
|
2359
|
+
position,
|
|
2360
|
+
color,
|
|
2361
|
+
intensity,
|
|
2362
|
+
range: Math.max(width, height) * 2,
|
|
2363
|
+
priority: 25
|
|
2364
|
+
};
|
|
2365
|
+
return this.addLight(light);
|
|
2366
|
+
}
|
|
2367
|
+
/**
|
|
2368
|
+
* Get shadow-casting lights
|
|
2369
|
+
*/
|
|
2370
|
+
getShadowCastingLights() {
|
|
2371
|
+
return Array.from(this.lights.values()).filter((l) => l.shadow && l.shadow.type !== "none");
|
|
2372
|
+
}
|
|
2373
|
+
/**
|
|
2374
|
+
* Get light count by type
|
|
2375
|
+
*/
|
|
2376
|
+
getLightCount() {
|
|
2377
|
+
const counts = {
|
|
2378
|
+
directional: 0,
|
|
2379
|
+
point: 0,
|
|
2380
|
+
spot: 0,
|
|
2381
|
+
area: 0,
|
|
2382
|
+
probe: 0
|
|
2383
|
+
};
|
|
2384
|
+
for (const light of Array.from(this.lights.values())) {
|
|
2385
|
+
counts[light.type]++;
|
|
2386
|
+
}
|
|
2387
|
+
return counts;
|
|
2388
|
+
}
|
|
2389
|
+
/**
|
|
2390
|
+
* Estimate light impact for performance optimization
|
|
2391
|
+
*/
|
|
2392
|
+
getPerformanceImpact() {
|
|
2393
|
+
const totalLights = this.lights.size;
|
|
2394
|
+
const shadowCasters = this.getShadowCastingLights().length;
|
|
2395
|
+
let estimatedGPUCost = "low";
|
|
2396
|
+
if (totalLights > 16 || shadowCasters > 4) {
|
|
2397
|
+
estimatedGPUCost = "high";
|
|
2398
|
+
} else if (totalLights > 8 || shadowCasters > 2) {
|
|
2399
|
+
estimatedGPUCost = "medium";
|
|
2400
|
+
}
|
|
2401
|
+
return {
|
|
2402
|
+
totalLights,
|
|
2403
|
+
shadowCasters,
|
|
2404
|
+
estimatedGPUCost
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
/**
|
|
2408
|
+
* Get scene complexity info
|
|
2409
|
+
*/
|
|
2410
|
+
getSceneInfo() {
|
|
2411
|
+
const counts = this.getLightCount();
|
|
2412
|
+
const impact = this.getPerformanceImpact();
|
|
2413
|
+
return `Lighting: ${counts.directional} dir, ${counts.point} point, ${counts.spot} spot | Shadows: ${impact.shadowCasters} | GPU: ${impact.estimatedGPUCost}`;
|
|
2414
|
+
}
|
|
2415
|
+
/**
|
|
2416
|
+
* Dispose and cleanup
|
|
2417
|
+
*/
|
|
2418
|
+
dispose() {
|
|
2419
|
+
this.lights.clear();
|
|
2420
|
+
}
|
|
2421
|
+
};
|
|
2422
|
+
function createLightingTrait(config) {
|
|
2423
|
+
return new LightingTrait(config);
|
|
2424
|
+
}
|
|
2425
|
+
var LIGHTING_PRESETS = {
|
|
2426
|
+
/** Neutral studio lighting */
|
|
2427
|
+
studio: () => ({
|
|
2428
|
+
enabled: true,
|
|
2429
|
+
intensity: 1,
|
|
2430
|
+
skyColor: { r: 0.5, g: 0.5, b: 0.5 },
|
|
2431
|
+
skyIntensity: 0.5,
|
|
2432
|
+
groundColor: { r: 0.3, g: 0.3, b: 0.3 },
|
|
2433
|
+
groundIntensity: 0.3
|
|
2434
|
+
}),
|
|
2435
|
+
/** Bright outdoor lighting */
|
|
2436
|
+
outdoor: () => ({
|
|
2437
|
+
enabled: true,
|
|
2438
|
+
intensity: 1.2,
|
|
2439
|
+
skyColor: { r: 0.7, g: 0.85, b: 1 },
|
|
2440
|
+
skyIntensity: 1,
|
|
2441
|
+
groundColor: { r: 0.4, g: 0.4, b: 0.35 },
|
|
2442
|
+
groundIntensity: 0.6,
|
|
2443
|
+
indirectDiffuse: 1.2
|
|
2444
|
+
}),
|
|
2445
|
+
/** Dim interior lighting */
|
|
2446
|
+
interior: () => ({
|
|
2447
|
+
enabled: true,
|
|
2448
|
+
intensity: 0.6,
|
|
2449
|
+
skyColor: { r: 0.3, g: 0.3, b: 0.35 },
|
|
2450
|
+
skyIntensity: 0.4,
|
|
2451
|
+
groundColor: { r: 0.2, g: 0.2, b: 0.2 },
|
|
2452
|
+
groundIntensity: 0.2
|
|
2453
|
+
}),
|
|
2454
|
+
/** Night scene */
|
|
2455
|
+
night: () => ({
|
|
2456
|
+
enabled: true,
|
|
2457
|
+
intensity: 0.3,
|
|
2458
|
+
skyColor: { r: 0.01, g: 0.01, b: 0.02 },
|
|
2459
|
+
skyIntensity: 0.1,
|
|
2460
|
+
groundColor: { r: 0.02, g: 0.02, b: 0.02 },
|
|
2461
|
+
groundIntensity: 0.05,
|
|
2462
|
+
screenSpaceAO: false
|
|
2463
|
+
}),
|
|
2464
|
+
/** Sunset/golden hour */
|
|
2465
|
+
sunset: () => ({
|
|
2466
|
+
enabled: true,
|
|
2467
|
+
intensity: 1.1,
|
|
2468
|
+
skyColor: { r: 1, g: 0.7, b: 0.3 },
|
|
2469
|
+
skyIntensity: 1,
|
|
2470
|
+
groundColor: { r: 0.6, g: 0.4, b: 0.2 },
|
|
2471
|
+
groundIntensity: 0.8
|
|
2472
|
+
})
|
|
2473
|
+
};
|
|
2474
|
+
|
|
2475
|
+
// src/traits/RenderingTrait.ts
|
|
2476
|
+
var RenderingTrait = class {
|
|
2477
|
+
constructor(config) {
|
|
2478
|
+
this.optimization = {
|
|
2479
|
+
lodStrategy: "automatic",
|
|
2480
|
+
culling: {
|
|
2481
|
+
mode: "back",
|
|
2482
|
+
frustum: true,
|
|
2483
|
+
occlusion: true
|
|
2484
|
+
},
|
|
2485
|
+
batching: {
|
|
2486
|
+
static: true,
|
|
2487
|
+
dynamic: true,
|
|
2488
|
+
instancing: true,
|
|
2489
|
+
maxInstanceCount: 1e3
|
|
2490
|
+
},
|
|
2491
|
+
textures: {
|
|
2492
|
+
streaming: true,
|
|
2493
|
+
compression: "auto",
|
|
2494
|
+
mipmaps: true,
|
|
2495
|
+
maxResolution: 2048
|
|
2496
|
+
},
|
|
2497
|
+
shaders: {
|
|
2498
|
+
simplifiedShaders: true,
|
|
2499
|
+
lodBias: 0
|
|
2500
|
+
},
|
|
2501
|
+
targetGPUTier: "high",
|
|
2502
|
+
adaptiveQuality: true,
|
|
2503
|
+
targetFrameRate: 60,
|
|
2504
|
+
...config
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
/**
|
|
2508
|
+
* Get rendering optimization config
|
|
2509
|
+
*/
|
|
2510
|
+
getOptimization() {
|
|
2511
|
+
return JSON.parse(JSON.stringify(this.optimization));
|
|
2512
|
+
}
|
|
2513
|
+
/**
|
|
2514
|
+
* Update rendering configuration
|
|
2515
|
+
*/
|
|
2516
|
+
updateOptimization(updates) {
|
|
2517
|
+
this.optimization = { ...this.optimization, ...updates };
|
|
2518
|
+
}
|
|
2519
|
+
/**
|
|
2520
|
+
* Setup LOD levels (3 levels is typical)
|
|
2521
|
+
*/
|
|
2522
|
+
setupLODLevels(strategy = "automatic") {
|
|
2523
|
+
const levels = [
|
|
2524
|
+
{
|
|
2525
|
+
level: 0,
|
|
2526
|
+
screenRelativeSize: 0.5,
|
|
2527
|
+
polygonReduction: 1,
|
|
2528
|
+
textureScale: 1
|
|
2529
|
+
},
|
|
2530
|
+
{
|
|
2531
|
+
level: 1,
|
|
2532
|
+
screenRelativeSize: 0.25,
|
|
2533
|
+
polygonReduction: 0.6,
|
|
2534
|
+
disabledFeatures: ["specular"],
|
|
2535
|
+
textureScale: 0.5
|
|
2536
|
+
},
|
|
2537
|
+
{
|
|
2538
|
+
level: 2,
|
|
2539
|
+
screenRelativeSize: 0.1,
|
|
2540
|
+
polygonReduction: 0.3,
|
|
2541
|
+
disabledFeatures: ["specular", "normals"],
|
|
2542
|
+
textureScale: 0.25
|
|
2543
|
+
}
|
|
2544
|
+
];
|
|
2545
|
+
this.optimization.lodStrategy = strategy;
|
|
2546
|
+
this.optimization.lodLevels = levels;
|
|
2547
|
+
}
|
|
2548
|
+
/**
|
|
2549
|
+
* Get LOD levels
|
|
2550
|
+
*/
|
|
2551
|
+
getLODLevels() {
|
|
2552
|
+
return [...this.optimization.lodLevels || []];
|
|
2553
|
+
}
|
|
2554
|
+
/**
|
|
2555
|
+
* Configure culling
|
|
2556
|
+
*/
|
|
2557
|
+
setCulling(config) {
|
|
2558
|
+
const defaultCulling = { mode: "back" };
|
|
2559
|
+
this.optimization.culling = {
|
|
2560
|
+
...defaultCulling,
|
|
2561
|
+
...this.optimization.culling,
|
|
2562
|
+
...config
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
/**
|
|
2566
|
+
* Enable frustum culling
|
|
2567
|
+
*/
|
|
2568
|
+
setFrustumCulling(enabled) {
|
|
2569
|
+
if (!this.optimization.culling) {
|
|
2570
|
+
this.optimization.culling = { mode: "back" };
|
|
2571
|
+
}
|
|
2572
|
+
this.optimization.culling.frustum = enabled;
|
|
2573
|
+
}
|
|
2574
|
+
/**
|
|
2575
|
+
* Enable occlusion culling
|
|
2576
|
+
*/
|
|
2577
|
+
setOcclusionCulling(enabled, distance) {
|
|
2578
|
+
if (!this.optimization.culling) {
|
|
2579
|
+
this.optimization.culling = { mode: "back" };
|
|
2580
|
+
}
|
|
2581
|
+
this.optimization.culling.occlusion = enabled;
|
|
2582
|
+
if (distance) {
|
|
2583
|
+
this.optimization.culling.occlusionDistance = distance;
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
/**
|
|
2587
|
+
* Configure batching
|
|
2588
|
+
*/
|
|
2589
|
+
setBatching(config) {
|
|
2590
|
+
this.optimization.batching = {
|
|
2591
|
+
...this.optimization.batching,
|
|
2592
|
+
...config
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
* Enable GPU instancing
|
|
2597
|
+
*/
|
|
2598
|
+
setInstancing(enabled, maxInstances) {
|
|
2599
|
+
if (!this.optimization.batching) {
|
|
2600
|
+
this.optimization.batching = {};
|
|
2601
|
+
}
|
|
2602
|
+
this.optimization.batching.instancing = enabled;
|
|
2603
|
+
if (maxInstances) {
|
|
2604
|
+
this.optimization.batching.maxInstanceCount = maxInstances;
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
/**
|
|
2608
|
+
* Configure texture optimization
|
|
2609
|
+
*/
|
|
2610
|
+
setTextureOptimization(config) {
|
|
2611
|
+
this.optimization.textures = {
|
|
2612
|
+
...this.optimization.textures,
|
|
2613
|
+
...config
|
|
2614
|
+
};
|
|
2615
|
+
}
|
|
2616
|
+
/**
|
|
2617
|
+
* Enable texture streaming
|
|
2618
|
+
*/
|
|
2619
|
+
setTextureStreaming(enabled, budgetMB) {
|
|
2620
|
+
if (!this.optimization.textures) {
|
|
2621
|
+
this.optimization.textures = {};
|
|
2622
|
+
}
|
|
2623
|
+
this.optimization.textures.streaming = enabled;
|
|
2624
|
+
if (budgetMB) {
|
|
2625
|
+
this.optimization.textures.streamingBudget = budgetMB;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
/**
|
|
2629
|
+
* Set texture compression
|
|
2630
|
+
*/
|
|
2631
|
+
setTextureCompression(compression) {
|
|
2632
|
+
if (!this.optimization.textures) {
|
|
2633
|
+
this.optimization.textures = {};
|
|
2634
|
+
}
|
|
2635
|
+
this.optimization.textures.compression = compression;
|
|
2636
|
+
}
|
|
2637
|
+
/**
|
|
2638
|
+
* Set max texture resolution
|
|
2639
|
+
*/
|
|
2640
|
+
setMaxTextureResolution(resolution) {
|
|
2641
|
+
if (!this.optimization.textures) {
|
|
2642
|
+
this.optimization.textures = {};
|
|
2643
|
+
}
|
|
2644
|
+
this.optimization.textures.maxResolution = resolution;
|
|
2645
|
+
}
|
|
2646
|
+
/**
|
|
2647
|
+
* Configure shader optimization
|
|
2648
|
+
*/
|
|
2649
|
+
setShaderOptimization(config) {
|
|
2650
|
+
this.optimization.shaders = {
|
|
2651
|
+
...this.optimization.shaders,
|
|
2652
|
+
...config
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2655
|
+
/**
|
|
2656
|
+
* Set target GPU tier
|
|
2657
|
+
*/
|
|
2658
|
+
setTargetGPUTier(tier) {
|
|
2659
|
+
this.optimization.targetGPUTier = tier;
|
|
2660
|
+
}
|
|
2661
|
+
/**
|
|
2662
|
+
* Enable adaptive quality (adjust based on frame rate)
|
|
2663
|
+
*/
|
|
2664
|
+
setAdaptiveQuality(enabled, targetFrameRate) {
|
|
2665
|
+
this.optimization.adaptiveQuality = enabled;
|
|
2666
|
+
if (targetFrameRate) {
|
|
2667
|
+
this.optimization.targetFrameRate = targetFrameRate;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
/**
|
|
2671
|
+
* Set fixed timestep for VR/AR
|
|
2672
|
+
*/
|
|
2673
|
+
setFixedTimestep(timestep) {
|
|
2674
|
+
this.optimization.fixedTimestep = timestep;
|
|
2675
|
+
}
|
|
2676
|
+
/**
|
|
2677
|
+
* Get rendering preset for quality level
|
|
2678
|
+
*/
|
|
2679
|
+
getPresetForQuality(quality) {
|
|
2680
|
+
const presets = {
|
|
2681
|
+
low: {
|
|
2682
|
+
targetGPUTier: "low",
|
|
2683
|
+
lodStrategy: "automatic",
|
|
2684
|
+
culling: { mode: "back", frustum: true, occlusion: false },
|
|
2685
|
+
batching: { instancing: true, maxInstanceCount: 500 },
|
|
2686
|
+
textures: {
|
|
2687
|
+
compression: "astc",
|
|
2688
|
+
maxResolution: 512,
|
|
2689
|
+
streaming: true,
|
|
2690
|
+
streamingBudget: 128
|
|
2691
|
+
},
|
|
2692
|
+
adaptiveQuality: true,
|
|
2693
|
+
targetFrameRate: 30
|
|
2694
|
+
},
|
|
2695
|
+
medium: {
|
|
2696
|
+
targetGPUTier: "medium",
|
|
2697
|
+
lodStrategy: "automatic",
|
|
2698
|
+
culling: { mode: "back", frustum: true, occlusion: true },
|
|
2699
|
+
batching: { instancing: true, maxInstanceCount: 1e3 },
|
|
2700
|
+
textures: {
|
|
2701
|
+
compression: "basis",
|
|
2702
|
+
maxResolution: 1024,
|
|
2703
|
+
streaming: true,
|
|
2704
|
+
streamingBudget: 256
|
|
2705
|
+
},
|
|
2706
|
+
adaptiveQuality: true,
|
|
2707
|
+
targetFrameRate: 60
|
|
2708
|
+
},
|
|
2709
|
+
high: {
|
|
2710
|
+
targetGPUTier: "high",
|
|
2711
|
+
lodStrategy: "automatic",
|
|
2712
|
+
culling: { mode: "back", frustum: true, occlusion: true },
|
|
2713
|
+
batching: { instancing: true, maxInstanceCount: 2e3 },
|
|
2714
|
+
textures: {
|
|
2715
|
+
compression: "dxt",
|
|
2716
|
+
maxResolution: 2048,
|
|
2717
|
+
streaming: true,
|
|
2718
|
+
streamingBudget: 512
|
|
2719
|
+
},
|
|
2720
|
+
adaptiveQuality: false,
|
|
2721
|
+
targetFrameRate: 60
|
|
2722
|
+
},
|
|
2723
|
+
ultra: {
|
|
2724
|
+
targetGPUTier: "ultra",
|
|
2725
|
+
lodStrategy: "manual",
|
|
2726
|
+
culling: {
|
|
2727
|
+
mode: "back",
|
|
2728
|
+
frustum: true,
|
|
2729
|
+
occlusion: true,
|
|
2730
|
+
hierarchicalZ: true
|
|
2731
|
+
},
|
|
2732
|
+
batching: { instancing: true, maxInstanceCount: 5e3 },
|
|
2733
|
+
textures: {
|
|
2734
|
+
compression: "none",
|
|
2735
|
+
maxResolution: 4096,
|
|
2736
|
+
virtualTexturing: true,
|
|
2737
|
+
streaming: true,
|
|
2738
|
+
streamingBudget: 1024
|
|
2739
|
+
},
|
|
2740
|
+
adaptiveQuality: false,
|
|
2741
|
+
targetFrameRate: 120
|
|
2742
|
+
}
|
|
2743
|
+
};
|
|
2744
|
+
return { ...this.optimization, ...presets[quality] };
|
|
2745
|
+
}
|
|
2746
|
+
/**
|
|
2747
|
+
* Apply quality preset
|
|
2748
|
+
*/
|
|
2749
|
+
applyQualityPreset(quality) {
|
|
2750
|
+
const preset = this.getPresetForQuality(quality);
|
|
2751
|
+
this.optimization = preset;
|
|
2752
|
+
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Estimate GPU memory usage
|
|
2755
|
+
*/
|
|
2756
|
+
estimateGPUMemory() {
|
|
2757
|
+
let textureMemory = 0;
|
|
2758
|
+
let vertexBuffers = 0;
|
|
2759
|
+
const maxRes = this.optimization.textures?.maxResolution || 2048;
|
|
2760
|
+
textureMemory = maxRes * maxRes * 4 / (1024 * 1024);
|
|
2761
|
+
const instanceCount = this.optimization.batching?.maxInstanceCount || 1e3;
|
|
2762
|
+
const verticesPerMesh = 1e4;
|
|
2763
|
+
vertexBuffers = verticesPerMesh * 36 * instanceCount / (1024 * 1024) * 0.1;
|
|
2764
|
+
return {
|
|
2765
|
+
textureMemory: Math.round(textureMemory),
|
|
2766
|
+
vertexBuffers: Math.max(1, Math.round(vertexBuffers)),
|
|
2767
|
+
// At least 1MB
|
|
2768
|
+
estimatedTotal: Math.round(textureMemory + Math.max(1, vertexBuffers))
|
|
2769
|
+
};
|
|
2238
2770
|
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2771
|
+
/**
|
|
2772
|
+
* Get rendering statistics/info
|
|
2773
|
+
*/
|
|
2774
|
+
getInfo() {
|
|
2775
|
+
const tier = this.optimization.targetGPUTier;
|
|
2776
|
+
const lod = this.optimization.lodStrategy;
|
|
2777
|
+
const culling = this.optimization.culling?.mode;
|
|
2778
|
+
const instancing = this.optimization.batching?.instancing ? "yes" : "no";
|
|
2779
|
+
const memory = this.estimateGPUMemory();
|
|
2780
|
+
return `Rendering: tier=${tier} | LOD=${lod} | culling=${culling} | instancing=${instancing} | memory=${memory.estimatedTotal}MB`;
|
|
2245
2781
|
}
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2782
|
+
/**
|
|
2783
|
+
* Optimize for VR/AR (fixed timestep, fast culling)
|
|
2784
|
+
*/
|
|
2785
|
+
optimizeForVRAR(targetFPS = 90) {
|
|
2786
|
+
this.optimization.fixedTimestep = 1 / targetFPS;
|
|
2787
|
+
this.optimization.targetFrameRate = targetFPS;
|
|
2788
|
+
this.setOcclusionCulling(true, 50);
|
|
2789
|
+
this.setInstancing(true, 5e3);
|
|
2790
|
+
this.setAdaptiveQuality(true, targetFPS);
|
|
2252
2791
|
}
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2792
|
+
/**
|
|
2793
|
+
* Optimize for mobile (lower resources)
|
|
2794
|
+
*/
|
|
2795
|
+
optimizeForMobile() {
|
|
2796
|
+
this.applyQualityPreset("low");
|
|
2797
|
+
this.setTextureCompression("astc");
|
|
2798
|
+
this.setInstancing(true, 256);
|
|
2257
2799
|
}
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2800
|
+
/**
|
|
2801
|
+
* Optimize for desktop (higher resources)
|
|
2802
|
+
*/
|
|
2803
|
+
optimizeForDesktop() {
|
|
2804
|
+
this.applyQualityPreset("ultra");
|
|
2805
|
+
this.setTextureCompression("none");
|
|
2806
|
+
this.setInstancing(true, 5e3);
|
|
2265
2807
|
}
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
line: token.line,
|
|
2271
|
-
column: token.column
|
|
2272
|
-
});
|
|
2808
|
+
/**
|
|
2809
|
+
* Dispose and cleanup
|
|
2810
|
+
*/
|
|
2811
|
+
dispose() {
|
|
2273
2812
|
}
|
|
2274
2813
|
};
|
|
2275
|
-
function
|
|
2276
|
-
return new
|
|
2277
|
-
}
|
|
2278
|
-
function parse(source, options) {
|
|
2279
|
-
const parser = createParser(options);
|
|
2280
|
-
return parser.parse(source);
|
|
2814
|
+
function createRenderingTrait(config) {
|
|
2815
|
+
return new RenderingTrait(config);
|
|
2281
2816
|
}
|
|
2282
2817
|
|
|
2283
2818
|
// src/HoloScriptPlusParser.ts
|
|
@@ -2531,8 +3066,7 @@ var HoloScriptPlusParser2 = class {
|
|
|
2531
3066
|
* Create MaterialTrait from config
|
|
2532
3067
|
*/
|
|
2533
3068
|
createMaterialTrait(config) {
|
|
2534
|
-
const
|
|
2535
|
-
const material = new MaterialTrait2({
|
|
3069
|
+
const material = new MaterialTrait({
|
|
2536
3070
|
type: config.type || "pbr",
|
|
2537
3071
|
pbr: config.pbr
|
|
2538
3072
|
});
|
|
@@ -2556,13 +3090,13 @@ var HoloScriptPlusParser2 = class {
|
|
|
2556
3090
|
* Create LightingTrait from config
|
|
2557
3091
|
*/
|
|
2558
3092
|
createLightingTrait(config) {
|
|
2559
|
-
const { LightingTrait: LightingTrait2, LIGHTING_PRESETS: LIGHTING_PRESETS2 } = (init_LightingTrait(), __toCommonJS(LightingTrait_exports));
|
|
2560
3093
|
let lighting;
|
|
2561
3094
|
if (config.preset) {
|
|
2562
|
-
const
|
|
2563
|
-
|
|
3095
|
+
const presetFactory = LIGHTING_PRESETS[config.preset];
|
|
3096
|
+
const presetConfig = presetFactory ? presetFactory() : void 0;
|
|
3097
|
+
lighting = new LightingTrait(presetConfig);
|
|
2564
3098
|
} else {
|
|
2565
|
-
lighting = new
|
|
3099
|
+
lighting = new LightingTrait();
|
|
2566
3100
|
}
|
|
2567
3101
|
if (config.globalIllumination) {
|
|
2568
3102
|
lighting.setGlobalIllumination(config.globalIllumination);
|
|
@@ -2578,8 +3112,7 @@ var HoloScriptPlusParser2 = class {
|
|
|
2578
3112
|
* Create RenderingTrait from config
|
|
2579
3113
|
*/
|
|
2580
3114
|
createRenderingTrait(config) {
|
|
2581
|
-
const
|
|
2582
|
-
const rendering = new RenderingTrait2();
|
|
3115
|
+
const rendering = new RenderingTrait();
|
|
2583
3116
|
if (config.quality) {
|
|
2584
3117
|
rendering.applyQualityPreset(config.quality);
|
|
2585
3118
|
}
|
|
@@ -2910,6 +3443,25 @@ var ExpressionEvaluator = class {
|
|
|
2910
3443
|
};
|
|
2911
3444
|
}
|
|
2912
3445
|
evaluate(expression) {
|
|
3446
|
+
const dangerousPatterns = [
|
|
3447
|
+
/\beval\s*\(/,
|
|
3448
|
+
/\brequire\s*\(/,
|
|
3449
|
+
/\bimport\s*\(/,
|
|
3450
|
+
/\bprocess\s*\./,
|
|
3451
|
+
/\bglobal\s*\./,
|
|
3452
|
+
/\b__dirname\b/,
|
|
3453
|
+
/\b__filename\b/,
|
|
3454
|
+
/\bfs\s*\./,
|
|
3455
|
+
/\bchild_process\s*\./,
|
|
3456
|
+
/\bfs\.writeFileSync/,
|
|
3457
|
+
/\bfs\.readFileSync/
|
|
3458
|
+
];
|
|
3459
|
+
for (const pattern of dangerousPatterns) {
|
|
3460
|
+
if (pattern.test(expression)) {
|
|
3461
|
+
console.warn(`Security: Blocked suspicious expression: ${expression}`);
|
|
3462
|
+
return void 0;
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
2913
3465
|
const contextKeys = Object.keys(this.context);
|
|
2914
3466
|
const contextValues = Object.values(this.context);
|
|
2915
3467
|
const builtinKeys = Object.keys(this.builtins);
|
|
@@ -2989,7 +3541,8 @@ var grabbableHandler = {
|
|
|
2989
3541
|
const distance = Math.sqrt(
|
|
2990
3542
|
Math.pow(handPos[0] - nodePos2[0], 2) + Math.pow(handPos[1] - nodePos2[1], 2) + Math.pow(handPos[2] - nodePos2[2], 2)
|
|
2991
3543
|
);
|
|
2992
|
-
|
|
3544
|
+
const maxDist = (config.max_grab_distance || 3) * context.getScaleMultiplier();
|
|
3545
|
+
if (distance > maxDist) return;
|
|
2993
3546
|
}
|
|
2994
3547
|
state.isGrabbed = true;
|
|
2995
3548
|
state.grabbingHand = event.hand;
|
|
@@ -3020,7 +3573,7 @@ var grabbableHandler = {
|
|
|
3020
3573
|
];
|
|
3021
3574
|
if (node.traits.has("throwable")) {
|
|
3022
3575
|
const throwConfig = node.traits.get("throwable");
|
|
3023
|
-
const multiplier = throwConfig.velocity_multiplier || 1;
|
|
3576
|
+
const multiplier = (throwConfig.velocity_multiplier || 1) * context.getScaleMultiplier();
|
|
3024
3577
|
context.physics.applyVelocity(node, [
|
|
3025
3578
|
velocity[0] * multiplier,
|
|
3026
3579
|
velocity[1] * multiplier,
|
|
@@ -3198,6 +3751,21 @@ var scalableHandler = {
|
|
|
3198
3751
|
const scaleFactor = currentDistance / state.initialDistance;
|
|
3199
3752
|
let newScale = state.initialScale * scaleFactor;
|
|
3200
3753
|
newScale = Math.max(config.min_scale || 0.1, Math.min(config.max_scale || 10, newScale));
|
|
3754
|
+
const scaleMultiplier = context.getScaleMultiplier();
|
|
3755
|
+
const effectiveScale = newScale * scaleMultiplier;
|
|
3756
|
+
if (effectiveScale > 1e6 && scaleMultiplier < 1e6) {
|
|
3757
|
+
context.setScaleContext("galactic");
|
|
3758
|
+
newScale /= 1e6;
|
|
3759
|
+
} else if (effectiveScale > 1e3 && scaleMultiplier < 1e3) {
|
|
3760
|
+
context.setScaleContext("macro");
|
|
3761
|
+
newScale /= 1e3;
|
|
3762
|
+
} else if (effectiveScale < 1e-3 && scaleMultiplier > 1e-3) {
|
|
3763
|
+
context.setScaleContext("micro");
|
|
3764
|
+
newScale *= 1e3;
|
|
3765
|
+
} else if (effectiveScale < 1e-6 && scaleMultiplier > 1e-6) {
|
|
3766
|
+
context.setScaleContext("atomic");
|
|
3767
|
+
newScale *= 1e6;
|
|
3768
|
+
}
|
|
3201
3769
|
node.properties.scale = newScale;
|
|
3202
3770
|
context.emit("scale_update", { node, scale: newScale });
|
|
3203
3771
|
},
|
|
@@ -3373,12 +3941,12 @@ var snappableHandler = {
|
|
|
3373
3941
|
snap_rotation: false,
|
|
3374
3942
|
magnetic: false
|
|
3375
3943
|
},
|
|
3376
|
-
onUpdate(node, config,
|
|
3944
|
+
onUpdate(node, config, context, _delta) {
|
|
3377
3945
|
if (!config.snap_points || config.snap_points.length === 0) return;
|
|
3378
3946
|
if (!config.magnetic) return;
|
|
3379
3947
|
const nodePos = node.properties.position || [0, 0, 0];
|
|
3380
3948
|
let closestPoint = null;
|
|
3381
|
-
let closestDistance = config.snap_distance || 0.3;
|
|
3949
|
+
let closestDistance = (config.snap_distance || 0.3) * context.getScaleMultiplier();
|
|
3382
3950
|
for (const snapPoint of config.snap_points) {
|
|
3383
3951
|
const distance = Math.sqrt(
|
|
3384
3952
|
Math.pow(nodePos[0] - snapPoint[0], 2) + Math.pow(nodePos[1] - snapPoint[1], 2) + Math.pow(nodePos[2] - snapPoint[2], 2)
|
|
@@ -3402,7 +3970,7 @@ var snappableHandler = {
|
|
|
3402
3970
|
if (!config.snap_points || config.snap_points.length === 0) return;
|
|
3403
3971
|
const nodePos = node.properties.position || [0, 0, 0];
|
|
3404
3972
|
let closestPoint = null;
|
|
3405
|
-
let closestDistance = config.snap_distance || 0.3;
|
|
3973
|
+
let closestDistance = (config.snap_distance || 0.3) * context.getScaleMultiplier();
|
|
3406
3974
|
for (const snapPoint of config.snap_points) {
|
|
3407
3975
|
const distance = Math.sqrt(
|
|
3408
3976
|
Math.pow(nodePos[0] - snapPoint[0], 2) + Math.pow(nodePos[1] - snapPoint[1], 2) + Math.pow(nodePos[2] - snapPoint[2], 2)
|
|
@@ -3458,202 +4026,138 @@ var breakableHandler = {
|
|
|
3458
4026
|
}
|
|
3459
4027
|
context.emit("break", { node, impactVelocity, collision });
|
|
3460
4028
|
if (config.respawn) {
|
|
3461
|
-
const delay = parseDuration(config.respawn_delay || "5s");
|
|
3462
|
-
setTimeout(() => {
|
|
3463
|
-
context.emit("respawn", { node });
|
|
3464
|
-
}, delay);
|
|
3465
|
-
}
|
|
3466
|
-
node.properties.__destroyed = true;
|
|
3467
|
-
}
|
|
3468
|
-
};
|
|
3469
|
-
function parseDuration(duration) {
|
|
3470
|
-
const match = duration.match(/^(\d+(?:\.\d+)?)(ms|s|m)$/);
|
|
3471
|
-
if (!match) return 0;
|
|
3472
|
-
const value = parseFloat(match[1]);
|
|
3473
|
-
const unit = match[2];
|
|
3474
|
-
switch (unit) {
|
|
3475
|
-
case "ms":
|
|
3476
|
-
return value;
|
|
3477
|
-
case "s":
|
|
3478
|
-
return value * 1e3;
|
|
3479
|
-
case "m":
|
|
3480
|
-
return value * 60 * 1e3;
|
|
3481
|
-
default:
|
|
3482
|
-
return value;
|
|
3483
|
-
}
|
|
3484
|
-
}
|
|
3485
|
-
var VRTraitRegistry = class {
|
|
3486
|
-
constructor() {
|
|
3487
|
-
this.handlers = /* @__PURE__ */ new Map();
|
|
3488
|
-
this.register(grabbableHandler);
|
|
3489
|
-
this.register(throwableHandler);
|
|
3490
|
-
this.register(pointableHandler);
|
|
3491
|
-
this.register(hoverableHandler);
|
|
3492
|
-
this.register(scalableHandler);
|
|
3493
|
-
this.register(rotatableHandler);
|
|
3494
|
-
this.register(stackableHandler);
|
|
3495
|
-
this.register(snappableHandler);
|
|
3496
|
-
this.register(breakableHandler);
|
|
3497
|
-
}
|
|
3498
|
-
register(handler) {
|
|
3499
|
-
this.handlers.set(handler.name, handler);
|
|
3500
|
-
}
|
|
3501
|
-
getHandler(name) {
|
|
3502
|
-
return this.handlers.get(name);
|
|
3503
|
-
}
|
|
3504
|
-
attachTrait(node, traitName, config, context) {
|
|
3505
|
-
const handler = this.handlers.get(traitName);
|
|
3506
|
-
if (!handler) return;
|
|
3507
|
-
const mergedConfig = { ...handler.defaultConfig, ...config };
|
|
3508
|
-
node.traits.set(traitName, mergedConfig);
|
|
3509
|
-
if (handler.onAttach) {
|
|
3510
|
-
handler.onAttach(node, mergedConfig, context);
|
|
3511
|
-
}
|
|
3512
|
-
}
|
|
3513
|
-
detachTrait(node, traitName, context) {
|
|
3514
|
-
const handler = this.handlers.get(traitName);
|
|
3515
|
-
if (!handler) return;
|
|
3516
|
-
const config = node.traits.get(traitName);
|
|
3517
|
-
if (config && handler.onDetach) {
|
|
3518
|
-
handler.onDetach(node, config, context);
|
|
3519
|
-
}
|
|
3520
|
-
node.traits.delete(traitName);
|
|
3521
|
-
}
|
|
3522
|
-
updateTrait(node, traitName, context, delta) {
|
|
3523
|
-
const handler = this.handlers.get(traitName);
|
|
3524
|
-
if (!handler || !handler.onUpdate) return;
|
|
3525
|
-
const config = node.traits.get(traitName);
|
|
3526
|
-
if (config) {
|
|
3527
|
-
handler.onUpdate(node, config, context, delta);
|
|
3528
|
-
}
|
|
3529
|
-
}
|
|
3530
|
-
handleEvent(node, traitName, context, event) {
|
|
3531
|
-
const handler = this.handlers.get(traitName);
|
|
3532
|
-
if (!handler || !handler.onEvent) return;
|
|
3533
|
-
const config = node.traits.get(traitName);
|
|
3534
|
-
if (config) {
|
|
3535
|
-
handler.onEvent(node, config, context, event);
|
|
3536
|
-
}
|
|
3537
|
-
}
|
|
3538
|
-
updateAllTraits(node, context, delta) {
|
|
3539
|
-
for (const traitName of node.traits.keys()) {
|
|
3540
|
-
this.updateTrait(node, traitName, context, delta);
|
|
3541
|
-
}
|
|
3542
|
-
}
|
|
3543
|
-
handleEventForAllTraits(node, context, event) {
|
|
3544
|
-
for (const traitName of node.traits.keys()) {
|
|
3545
|
-
this.handleEvent(node, traitName, context, event);
|
|
3546
|
-
}
|
|
3547
|
-
}
|
|
3548
|
-
};
|
|
3549
|
-
var vrTraitRegistry = new VRTraitRegistry();
|
|
3550
|
-
|
|
3551
|
-
// src/runtime/HoloScriptPlusRuntime.ts
|
|
3552
|
-
function createBuiltins(runtime) {
|
|
3553
|
-
return {
|
|
3554
|
-
Math,
|
|
3555
|
-
range: (start, end, step = 1) => {
|
|
3556
|
-
const result = [];
|
|
3557
|
-
if (step > 0) {
|
|
3558
|
-
for (let i = start; i < end; i += step) {
|
|
3559
|
-
result.push(i);
|
|
3560
|
-
}
|
|
3561
|
-
} else if (step < 0) {
|
|
3562
|
-
for (let i = start; i > end; i += step) {
|
|
3563
|
-
result.push(i);
|
|
3564
|
-
}
|
|
3565
|
-
}
|
|
3566
|
-
return result;
|
|
3567
|
-
},
|
|
3568
|
-
interpolate_color: (t, from, to) => {
|
|
3569
|
-
const parseHex = (hex) => {
|
|
3570
|
-
const clean = hex.replace("#", "");
|
|
3571
|
-
return [
|
|
3572
|
-
parseInt(clean.substring(0, 2), 16),
|
|
3573
|
-
parseInt(clean.substring(2, 4), 16),
|
|
3574
|
-
parseInt(clean.substring(4, 6), 16)
|
|
3575
|
-
];
|
|
3576
|
-
};
|
|
3577
|
-
const toHex = (r, g, b) => {
|
|
3578
|
-
const clamp = (v) => Math.max(0, Math.min(255, Math.round(v)));
|
|
3579
|
-
return `#${clamp(r).toString(16).padStart(2, "0")}${clamp(g).toString(16).padStart(2, "0")}${clamp(b).toString(16).padStart(2, "0")}`;
|
|
3580
|
-
};
|
|
3581
|
-
const [r1, g1, b1] = parseHex(from);
|
|
3582
|
-
const [r2, g2, b2] = parseHex(to);
|
|
3583
|
-
return toHex(
|
|
3584
|
-
r1 + (r2 - r1) * t,
|
|
3585
|
-
g1 + (g2 - g1) * t,
|
|
3586
|
-
b1 + (b2 - b1) * t
|
|
3587
|
-
);
|
|
3588
|
-
},
|
|
3589
|
-
distance_to: (point) => {
|
|
3590
|
-
const viewer = runtime.vrContext.headset.position;
|
|
3591
|
-
return Math.sqrt(
|
|
3592
|
-
Math.pow(point[0] - viewer[0], 2) + Math.pow(point[1] - viewer[1], 2) + Math.pow(point[2] - viewer[2], 2)
|
|
3593
|
-
);
|
|
3594
|
-
},
|
|
3595
|
-
distance_to_viewer: () => {
|
|
3596
|
-
return 0;
|
|
3597
|
-
},
|
|
3598
|
-
hand_position: (handId) => {
|
|
3599
|
-
const hand = handId === "left" ? runtime.vrContext.hands.left : runtime.vrContext.hands.right;
|
|
3600
|
-
return hand?.position || [0, 0, 0];
|
|
3601
|
-
},
|
|
3602
|
-
hand_velocity: (handId) => {
|
|
3603
|
-
const hand = handId === "left" ? runtime.vrContext.hands.left : runtime.vrContext.hands.right;
|
|
3604
|
-
return hand?.velocity || [0, 0, 0];
|
|
3605
|
-
},
|
|
3606
|
-
dominant_hand: () => {
|
|
3607
|
-
return runtime.vrContext.hands.right || runtime.vrContext.hands.left || {
|
|
3608
|
-
id: "right",
|
|
3609
|
-
position: [0, 0, 0],
|
|
3610
|
-
rotation: [0, 0, 0],
|
|
3611
|
-
velocity: [0, 0, 0],
|
|
3612
|
-
grip: 0,
|
|
3613
|
-
trigger: 0
|
|
3614
|
-
};
|
|
3615
|
-
},
|
|
3616
|
-
play_sound: (source, options) => {
|
|
3617
|
-
runtime.emit("play_sound", { source, ...options });
|
|
3618
|
-
},
|
|
3619
|
-
haptic_feedback: (hand, intensity) => {
|
|
3620
|
-
const handId = typeof hand === "string" ? hand : hand.id;
|
|
3621
|
-
runtime.emit("haptic", { hand: handId, intensity });
|
|
3622
|
-
},
|
|
3623
|
-
haptic_pulse: (intensity) => {
|
|
3624
|
-
runtime.emit("haptic", { hand: "both", intensity });
|
|
3625
|
-
},
|
|
3626
|
-
apply_velocity: (node, velocity) => {
|
|
3627
|
-
runtime.emit("apply_velocity", { node, velocity });
|
|
3628
|
-
},
|
|
3629
|
-
spawn: (template, position) => {
|
|
3630
|
-
return runtime.spawnTemplate(template, position);
|
|
3631
|
-
},
|
|
3632
|
-
destroy: (node) => {
|
|
3633
|
-
runtime.destroyNode(node);
|
|
3634
|
-
},
|
|
3635
|
-
api_call: async (url, method, body) => {
|
|
3636
|
-
const response = await fetch(url, {
|
|
3637
|
-
method,
|
|
3638
|
-
headers: body ? { "Content-Type": "application/json" } : void 0,
|
|
3639
|
-
body: body ? JSON.stringify(body) : void 0
|
|
3640
|
-
});
|
|
3641
|
-
return response.json();
|
|
3642
|
-
},
|
|
3643
|
-
open_modal: (modalId) => {
|
|
3644
|
-
runtime.emit("open_modal", { id: modalId });
|
|
3645
|
-
},
|
|
3646
|
-
close_modal: (modalId) => {
|
|
3647
|
-
runtime.emit("close_modal", { id: modalId });
|
|
3648
|
-
},
|
|
3649
|
-
setTimeout: (callback, delay) => {
|
|
3650
|
-
return window.setTimeout(callback, delay);
|
|
3651
|
-
},
|
|
3652
|
-
clearTimeout: (id) => {
|
|
3653
|
-
window.clearTimeout(id);
|
|
4029
|
+
const delay = parseDuration(config.respawn_delay || "5s");
|
|
4030
|
+
setTimeout(() => {
|
|
4031
|
+
context.emit("respawn", { node });
|
|
4032
|
+
}, delay);
|
|
3654
4033
|
}
|
|
3655
|
-
|
|
4034
|
+
node.properties.__destroyed = true;
|
|
4035
|
+
}
|
|
4036
|
+
};
|
|
4037
|
+
var proactiveHandler = {
|
|
4038
|
+
name: "proactive",
|
|
4039
|
+
defaultConfig: {
|
|
4040
|
+
intelligence_tier: "basic",
|
|
4041
|
+
observation_range: 5,
|
|
4042
|
+
learning_rate: 0.1,
|
|
4043
|
+
auto_suggest: true,
|
|
4044
|
+
context_window: 10
|
|
4045
|
+
},
|
|
4046
|
+
onAttach(node, config, context) {
|
|
4047
|
+
console.log(`[Proactive] Neural bridge attached to ${node.id || node.type}`);
|
|
4048
|
+
context.emit("proactive_init", { nodeId: node.id, tier: config.intelligence_tier });
|
|
4049
|
+
},
|
|
4050
|
+
onUpdate(node, config, context, delta) {
|
|
4051
|
+
if (!config || !config.auto_suggest) return;
|
|
4052
|
+
const vr = context.vr;
|
|
4053
|
+
const pos = node.properties.position;
|
|
4054
|
+
if (!pos || !vr.headset.position) return;
|
|
4055
|
+
const dx = pos[0] - vr.headset.position[0];
|
|
4056
|
+
const dy = pos[1] - vr.headset.position[1];
|
|
4057
|
+
const dz = pos[2] - vr.headset.position[2];
|
|
4058
|
+
const distanceToHead = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
4059
|
+
if (distanceToHead < (config.observation_range || 5)) {
|
|
4060
|
+
if (Math.random() < 0.01 * (config.learning_rate || 0.1) * delta) {
|
|
4061
|
+
context.emit("proactive_suggestion", {
|
|
4062
|
+
nodeId: node.id,
|
|
4063
|
+
type: "interaction_hint",
|
|
4064
|
+
suggestion: "Object is observing your proximity. Suggesting engagement."
|
|
4065
|
+
});
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
};
|
|
4070
|
+
function parseDuration(duration) {
|
|
4071
|
+
const match = duration.match(/^(\d+(?:\.\d+)?)(ms|s|m)$/);
|
|
4072
|
+
if (!match) return 0;
|
|
4073
|
+
const value = parseFloat(match[1]);
|
|
4074
|
+
const unit = match[2];
|
|
4075
|
+
switch (unit) {
|
|
4076
|
+
case "ms":
|
|
4077
|
+
return value;
|
|
4078
|
+
case "s":
|
|
4079
|
+
return value * 1e3;
|
|
4080
|
+
case "m":
|
|
4081
|
+
return value * 60 * 1e3;
|
|
4082
|
+
default:
|
|
4083
|
+
return value;
|
|
4084
|
+
}
|
|
3656
4085
|
}
|
|
4086
|
+
var VRTraitRegistry = class {
|
|
4087
|
+
constructor() {
|
|
4088
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
4089
|
+
this.register(grabbableHandler);
|
|
4090
|
+
this.register(throwableHandler);
|
|
4091
|
+
this.register(pointableHandler);
|
|
4092
|
+
this.register(hoverableHandler);
|
|
4093
|
+
this.register(scalableHandler);
|
|
4094
|
+
this.register(rotatableHandler);
|
|
4095
|
+
this.register(stackableHandler);
|
|
4096
|
+
this.register(snappableHandler);
|
|
4097
|
+
this.register(breakableHandler);
|
|
4098
|
+
this.register(proactiveHandler);
|
|
4099
|
+
}
|
|
4100
|
+
register(handler) {
|
|
4101
|
+
this.handlers.set(handler.name, handler);
|
|
4102
|
+
}
|
|
4103
|
+
getHandler(name) {
|
|
4104
|
+
return this.handlers.get(name);
|
|
4105
|
+
}
|
|
4106
|
+
attachTrait(node, traitName, config, context) {
|
|
4107
|
+
const handler = this.handlers.get(traitName);
|
|
4108
|
+
if (!handler) return;
|
|
4109
|
+
const mergedConfig = { ...handler.defaultConfig, ...config };
|
|
4110
|
+
node.traits.set(traitName, mergedConfig);
|
|
4111
|
+
if (handler.onAttach) {
|
|
4112
|
+
handler.onAttach(node, mergedConfig, context);
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
detachTrait(node, traitName, context) {
|
|
4116
|
+
const handler = this.handlers.get(traitName);
|
|
4117
|
+
if (!handler) return;
|
|
4118
|
+
const config = node.traits.get(traitName);
|
|
4119
|
+
if (config && handler.onDetach) {
|
|
4120
|
+
handler.onDetach(node, config, context);
|
|
4121
|
+
}
|
|
4122
|
+
node.traits.delete(traitName);
|
|
4123
|
+
}
|
|
4124
|
+
updateTrait(node, traitName, context, delta) {
|
|
4125
|
+
const handler = this.handlers.get(traitName);
|
|
4126
|
+
if (!handler || !handler.onUpdate) return;
|
|
4127
|
+
const config = node.traits.get(traitName);
|
|
4128
|
+
if (config) {
|
|
4129
|
+
handler.onUpdate(node, config, context, delta);
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
handleEvent(node, traitName, context, event) {
|
|
4133
|
+
const handler = this.handlers.get(traitName);
|
|
4134
|
+
if (!handler || !handler.onEvent) return;
|
|
4135
|
+
const config = node.traits.get(traitName);
|
|
4136
|
+
if (config) {
|
|
4137
|
+
handler.onEvent(node, config, context, event);
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4140
|
+
updateAllTraits(node, context, delta) {
|
|
4141
|
+
for (const traitName of node.traits.keys()) {
|
|
4142
|
+
this.updateTrait(node, traitName, context, delta);
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
handleEventForAllTraits(node, context, event) {
|
|
4146
|
+
for (const traitName of node.traits.keys()) {
|
|
4147
|
+
this.handleEvent(node, traitName, context, event);
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
};
|
|
4151
|
+
var vrTraitRegistry = new VRTraitRegistry();
|
|
4152
|
+
|
|
4153
|
+
// src/runtime/HoloScriptPlusRuntime.ts
|
|
4154
|
+
var StateSync = class {
|
|
4155
|
+
constructor(_options) {
|
|
4156
|
+
}
|
|
4157
|
+
getInterpolatedState(_id) {
|
|
4158
|
+
return null;
|
|
4159
|
+
}
|
|
4160
|
+
};
|
|
3657
4161
|
var HoloScriptPlusRuntimeImpl = class {
|
|
3658
4162
|
constructor(ast, options = {}) {
|
|
3659
4163
|
this.rootInstance = null;
|
|
@@ -3662,6 +4166,7 @@ var HoloScriptPlusRuntimeImpl = class {
|
|
|
3662
4166
|
this.updateLoopId = null;
|
|
3663
4167
|
this.lastUpdateTime = 0;
|
|
3664
4168
|
this.mounted = false;
|
|
4169
|
+
this.scaleMultiplier = 1;
|
|
3665
4170
|
// VR context
|
|
3666
4171
|
this.vrContext = {
|
|
3667
4172
|
hands: {
|
|
@@ -3677,12 +4182,15 @@ var HoloScriptPlusRuntimeImpl = class {
|
|
|
3677
4182
|
right: null
|
|
3678
4183
|
}
|
|
3679
4184
|
};
|
|
4185
|
+
this.generatedNodes = /* @__PURE__ */ new Set();
|
|
4186
|
+
this.apiPollingTimers = /* @__PURE__ */ new Map();
|
|
3680
4187
|
this.ast = ast;
|
|
3681
4188
|
this.options = options;
|
|
3682
4189
|
this.state = createState({});
|
|
3683
4190
|
this.traitRegistry = vrTraitRegistry;
|
|
3684
4191
|
this.companions = options.companions || {};
|
|
3685
4192
|
this.builtins = createBuiltins(this);
|
|
4193
|
+
this.networkSync = new StateSync({ interpolation: true });
|
|
3686
4194
|
this.evaluator = new ExpressionEvaluator(
|
|
3687
4195
|
this.state.getSnapshot(),
|
|
3688
4196
|
this.builtins
|
|
@@ -3951,6 +4459,20 @@ var HoloScriptPlusRuntimeImpl = class {
|
|
|
3951
4459
|
if (instance.destroyed) return;
|
|
3952
4460
|
const traitContext = this.createTraitContext(instance);
|
|
3953
4461
|
this.traitRegistry.updateAllTraits(instance.node, traitContext, delta);
|
|
4462
|
+
if (instance.node.type === "avatar") {
|
|
4463
|
+
this.syncAvatarParts(instance);
|
|
4464
|
+
}
|
|
4465
|
+
if (instance.node.traits.has("networked")) {
|
|
4466
|
+
const interpolated = this.networkSync.getInterpolatedState(instance.node.id || "");
|
|
4467
|
+
if (interpolated) {
|
|
4468
|
+
if (interpolated.position) {
|
|
4469
|
+
instance.node.properties.position = [interpolated.position.x, interpolated.position.y, interpolated.position.z];
|
|
4470
|
+
}
|
|
4471
|
+
if (interpolated.rotation) {
|
|
4472
|
+
instance.node.properties.rotation = [interpolated.rotation.x, interpolated.rotation.y, interpolated.rotation.z];
|
|
4473
|
+
}
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
3954
4476
|
if (this.options.renderer && instance.renderedNode) {
|
|
3955
4477
|
const properties = this.evaluateProperties(instance.node.properties);
|
|
3956
4478
|
this.options.renderer.updateElement(instance.renderedNode, properties);
|
|
@@ -3958,6 +4480,91 @@ var HoloScriptPlusRuntimeImpl = class {
|
|
|
3958
4480
|
for (const child of instance.children) {
|
|
3959
4481
|
this.updateInstance(child, delta);
|
|
3960
4482
|
}
|
|
4483
|
+
this.updateExternalApis(instance, delta);
|
|
4484
|
+
this.processGenerateDirectives(instance);
|
|
4485
|
+
}
|
|
4486
|
+
syncAvatarParts(instance) {
|
|
4487
|
+
const vrHands = this.vrContext.hands;
|
|
4488
|
+
const vrHead = this.vrContext.headset;
|
|
4489
|
+
if (instance.node.id === "local_player") {
|
|
4490
|
+
instance.node.properties.position = vrHead.position;
|
|
4491
|
+
instance.node.properties.rotation = vrHead.rotation;
|
|
4492
|
+
instance.children.forEach((child) => {
|
|
4493
|
+
if (child.node.id === "left_hand" && vrHands.left) {
|
|
4494
|
+
child.node.properties.position = vrHands.left.position;
|
|
4495
|
+
child.node.properties.rotation = vrHands.left.rotation;
|
|
4496
|
+
} else if (child.node.id === "right_hand" && vrHands.right) {
|
|
4497
|
+
child.node.properties.position = vrHands.right.position;
|
|
4498
|
+
child.node.properties.rotation = vrHands.right.rotation;
|
|
4499
|
+
}
|
|
4500
|
+
});
|
|
4501
|
+
if (instance.node.traits.has("networked")) {
|
|
4502
|
+
this.emit("network_snapshot", {
|
|
4503
|
+
objectId: instance.node.id,
|
|
4504
|
+
position: { x: vrHead.position[0], y: vrHead.position[1], z: vrHead.position[2] },
|
|
4505
|
+
rotation: { x: vrHead.rotation[0], y: vrHead.rotation[1], z: vrHead.rotation[2] },
|
|
4506
|
+
timestamp: Date.now()
|
|
4507
|
+
});
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
processGenerateDirectives(instance) {
|
|
4512
|
+
const generateDirectives = instance.node.directives.filter((d) => d.type === "generate");
|
|
4513
|
+
for (const d of generateDirectives) {
|
|
4514
|
+
const directive = d;
|
|
4515
|
+
const genId = `${instance.node.id || "node"}_${directive.prompt.substring(0, 10)}`;
|
|
4516
|
+
if (this.generatedNodes.has(genId)) continue;
|
|
4517
|
+
console.log(`[Generate] AI Bridge Request: "${directive.prompt}"`);
|
|
4518
|
+
this.emit("generate_request", {
|
|
4519
|
+
id: genId,
|
|
4520
|
+
nodeId: instance.node.id,
|
|
4521
|
+
prompt: directive.prompt,
|
|
4522
|
+
context: directive.context,
|
|
4523
|
+
target: directive.target || "children"
|
|
4524
|
+
});
|
|
4525
|
+
this.generatedNodes.add(genId);
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4528
|
+
updateExternalApis(instance, _delta) {
|
|
4529
|
+
const apiDirectives = instance.node.directives.filter((d) => d.type === "external_api");
|
|
4530
|
+
for (const d of apiDirectives) {
|
|
4531
|
+
const directive = d;
|
|
4532
|
+
if (directive.type !== "external_api") continue;
|
|
4533
|
+
const intervalStr = directive.interval || "0s";
|
|
4534
|
+
const intervalMs = this.parseDurationToMs(intervalStr);
|
|
4535
|
+
if (intervalMs <= 0) continue;
|
|
4536
|
+
let lastTime = this.apiPollingTimers.get(instance) || 0;
|
|
4537
|
+
const now = performance.now();
|
|
4538
|
+
if (now - lastTime >= intervalMs) {
|
|
4539
|
+
this.apiPollingTimers.set(instance, now);
|
|
4540
|
+
this.executeExternalApi(instance, directive);
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
}
|
|
4544
|
+
async executeExternalApi(instance, directive) {
|
|
4545
|
+
try {
|
|
4546
|
+
const data = await this.builtins.api_call(directive.url, directive.method || "GET");
|
|
4547
|
+
this.state.set("api_data", data);
|
|
4548
|
+
this.updateData(data);
|
|
4549
|
+
} catch (error) {
|
|
4550
|
+
console.error(`External API error for ${directive.url}:`, error);
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
parseDurationToMs(duration) {
|
|
4554
|
+
const match = duration.match(/^(\d+)(ms|s|m)$/);
|
|
4555
|
+
if (!match) return 0;
|
|
4556
|
+
const value = parseInt(match[1], 10);
|
|
4557
|
+
const unit = match[2];
|
|
4558
|
+
switch (unit) {
|
|
4559
|
+
case "ms":
|
|
4560
|
+
return value;
|
|
4561
|
+
case "s":
|
|
4562
|
+
return value * 1e3;
|
|
4563
|
+
case "m":
|
|
4564
|
+
return value * 6e4;
|
|
4565
|
+
default:
|
|
4566
|
+
return 0;
|
|
4567
|
+
}
|
|
3961
4568
|
}
|
|
3962
4569
|
// ==========================================================================
|
|
3963
4570
|
// TRAIT CONTEXT
|
|
@@ -4007,7 +4614,22 @@ var HoloScriptPlusRuntimeImpl = class {
|
|
|
4007
4614
|
},
|
|
4008
4615
|
emit: this.emit.bind(this),
|
|
4009
4616
|
getState: () => this.state.getSnapshot(),
|
|
4010
|
-
setState: (updates) => this.state.update(updates)
|
|
4617
|
+
setState: (updates) => this.state.update(updates),
|
|
4618
|
+
getScaleMultiplier: () => this.scaleMultiplier,
|
|
4619
|
+
setScaleContext: (magnitude) => {
|
|
4620
|
+
const multipliers = {
|
|
4621
|
+
"galactic": 1e6,
|
|
4622
|
+
"macro": 1e3,
|
|
4623
|
+
"standard": 1,
|
|
4624
|
+
"micro": 1e-3,
|
|
4625
|
+
"atomic": 1e-6
|
|
4626
|
+
};
|
|
4627
|
+
const newMultiplier = multipliers[magnitude] || 1;
|
|
4628
|
+
if (this.scaleMultiplier !== newMultiplier) {
|
|
4629
|
+
this.scaleMultiplier = newMultiplier;
|
|
4630
|
+
this.emit("scale_change", { magnitude, multiplier: newMultiplier });
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4011
4633
|
};
|
|
4012
4634
|
}
|
|
4013
4635
|
// ==========================================================================
|
|
@@ -4039,6 +4661,54 @@ var HoloScriptPlusRuntimeImpl = class {
|
|
|
4039
4661
|
getState() {
|
|
4040
4662
|
return this.state.getSnapshot();
|
|
4041
4663
|
}
|
|
4664
|
+
// ==========================================================================
|
|
4665
|
+
// COMPATIBILITY METHODS
|
|
4666
|
+
// ==========================================================================
|
|
4667
|
+
getVariable(name) {
|
|
4668
|
+
return this.state.get(name);
|
|
4669
|
+
}
|
|
4670
|
+
setVariable(name, value) {
|
|
4671
|
+
this.state.set(name, value);
|
|
4672
|
+
}
|
|
4673
|
+
getContext() {
|
|
4674
|
+
const spatialMemory = /* @__PURE__ */ new Map();
|
|
4675
|
+
const hologramState = /* @__PURE__ */ new Map();
|
|
4676
|
+
const traverse = (instance) => {
|
|
4677
|
+
if (instance.node.id) {
|
|
4678
|
+
spatialMemory.set(instance.node.id, instance.node.properties.position || { x: 0, y: 0, z: 0 });
|
|
4679
|
+
hologramState.set(instance.node.id, {
|
|
4680
|
+
shape: instance.node.properties.shape || instance.node.type,
|
|
4681
|
+
color: instance.node.properties.color,
|
|
4682
|
+
size: instance.node.properties.size,
|
|
4683
|
+
glow: instance.node.properties.glow,
|
|
4684
|
+
interactive: instance.node.properties.interactive
|
|
4685
|
+
});
|
|
4686
|
+
}
|
|
4687
|
+
instance.children.forEach(traverse);
|
|
4688
|
+
};
|
|
4689
|
+
if (this.rootInstance) traverse(this.rootInstance);
|
|
4690
|
+
return {
|
|
4691
|
+
spatialMemory,
|
|
4692
|
+
hologramState,
|
|
4693
|
+
state: this.state,
|
|
4694
|
+
builtins: this.builtins,
|
|
4695
|
+
vr: this.vrContext
|
|
4696
|
+
};
|
|
4697
|
+
}
|
|
4698
|
+
reset() {
|
|
4699
|
+
this.unmount();
|
|
4700
|
+
this.state = createState({});
|
|
4701
|
+
this.mounted = false;
|
|
4702
|
+
}
|
|
4703
|
+
updateAnimations() {
|
|
4704
|
+
this.update(1 / 60);
|
|
4705
|
+
}
|
|
4706
|
+
updateParticles(delta) {
|
|
4707
|
+
this.update(delta);
|
|
4708
|
+
}
|
|
4709
|
+
getHologramStates() {
|
|
4710
|
+
return this.getContext().hologramState;
|
|
4711
|
+
}
|
|
4042
4712
|
setState(updates) {
|
|
4043
4713
|
this.state.update(updates);
|
|
4044
4714
|
}
|
|
@@ -4054,6 +4724,22 @@ var HoloScriptPlusRuntimeImpl = class {
|
|
|
4054
4724
|
});
|
|
4055
4725
|
}
|
|
4056
4726
|
}
|
|
4727
|
+
updateEntity(id, properties) {
|
|
4728
|
+
if (!this.rootInstance) return false;
|
|
4729
|
+
let found = false;
|
|
4730
|
+
const traverse = (instance) => {
|
|
4731
|
+
if (instance.node.id === id) {
|
|
4732
|
+
instance.node.properties = { ...instance.node.properties, ...properties };
|
|
4733
|
+
if (this.options.renderer && instance.renderedNode) {
|
|
4734
|
+
this.options.renderer.updateElement(instance.renderedNode, properties);
|
|
4735
|
+
}
|
|
4736
|
+
found = true;
|
|
4737
|
+
}
|
|
4738
|
+
instance.children.forEach(traverse);
|
|
4739
|
+
};
|
|
4740
|
+
traverse(this.rootInstance);
|
|
4741
|
+
return found;
|
|
4742
|
+
}
|
|
4057
4743
|
on(event, handler) {
|
|
4058
4744
|
if (!this.eventHandlers.has(event)) {
|
|
4059
4745
|
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
@@ -4139,6 +4825,114 @@ var HoloScriptPlusRuntimeImpl = class {
|
|
|
4139
4825
|
this.destroyInstance(instance);
|
|
4140
4826
|
}
|
|
4141
4827
|
};
|
|
4828
|
+
function createBuiltins(runtime) {
|
|
4829
|
+
return {
|
|
4830
|
+
Math,
|
|
4831
|
+
range: (start, end, step = 1) => {
|
|
4832
|
+
const result = [];
|
|
4833
|
+
if (step > 0) {
|
|
4834
|
+
for (let i = start; i < end; i += step) {
|
|
4835
|
+
result.push(i);
|
|
4836
|
+
}
|
|
4837
|
+
} else if (step < 0) {
|
|
4838
|
+
for (let i = start; i > end; i += step) {
|
|
4839
|
+
result.push(i);
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
return result;
|
|
4843
|
+
},
|
|
4844
|
+
interpolate_color: (t, from, to) => {
|
|
4845
|
+
const parseHex = (hex) => {
|
|
4846
|
+
const clean = hex.replace("#", "");
|
|
4847
|
+
return [
|
|
4848
|
+
parseInt(clean.substring(0, 2), 16),
|
|
4849
|
+
parseInt(clean.substring(2, 4), 16),
|
|
4850
|
+
parseInt(clean.substring(4, 6), 16)
|
|
4851
|
+
];
|
|
4852
|
+
};
|
|
4853
|
+
const toHex = (r, g, b) => {
|
|
4854
|
+
const clamp = (v) => Math.max(0, Math.min(255, Math.round(v)));
|
|
4855
|
+
return `#${clamp(r).toString(16).padStart(2, "0")}${clamp(g).toString(16).padStart(2, "0")}${clamp(b).toString(16).padStart(2, "0")}`;
|
|
4856
|
+
};
|
|
4857
|
+
const [r1, g1, b1] = parseHex(from);
|
|
4858
|
+
const [r2, g2, b2] = parseHex(to);
|
|
4859
|
+
return toHex(
|
|
4860
|
+
r1 + (r2 - r1) * t,
|
|
4861
|
+
g1 + (g2 - g1) * t,
|
|
4862
|
+
b1 + (b2 - b1) * t
|
|
4863
|
+
);
|
|
4864
|
+
},
|
|
4865
|
+
distance_to: (point) => {
|
|
4866
|
+
const viewer = runtime.vrContext.headset.position;
|
|
4867
|
+
return Math.sqrt(
|
|
4868
|
+
Math.pow(point[0] - viewer[0], 2) + Math.pow(point[1] - viewer[1], 2) + Math.pow(point[2] - viewer[2], 2)
|
|
4869
|
+
);
|
|
4870
|
+
},
|
|
4871
|
+
distance_to_viewer: () => {
|
|
4872
|
+
return 0;
|
|
4873
|
+
},
|
|
4874
|
+
hand_position: (handId) => {
|
|
4875
|
+
const hand = handId === "left" ? runtime.vrContext.hands.left : runtime.vrContext.hands.right;
|
|
4876
|
+
return hand?.position || [0, 0, 0];
|
|
4877
|
+
},
|
|
4878
|
+
hand_velocity: (handId) => {
|
|
4879
|
+
const hand = handId === "left" ? runtime.vrContext.hands.left : runtime.vrContext.hands.right;
|
|
4880
|
+
return hand?.velocity || [0, 0, 0];
|
|
4881
|
+
},
|
|
4882
|
+
dominant_hand: () => {
|
|
4883
|
+
return runtime.vrContext.hands.right || runtime.vrContext.hands.left || {
|
|
4884
|
+
id: "right",
|
|
4885
|
+
position: [0, 0, 0],
|
|
4886
|
+
rotation: [0, 0, 0],
|
|
4887
|
+
velocity: [0, 0, 0],
|
|
4888
|
+
grip: 0,
|
|
4889
|
+
trigger: 0
|
|
4890
|
+
};
|
|
4891
|
+
},
|
|
4892
|
+
play_sound: (source, options) => {
|
|
4893
|
+
runtime.emit("play_sound", { source, ...options });
|
|
4894
|
+
},
|
|
4895
|
+
haptic_feedback: (hand, intensity) => {
|
|
4896
|
+
const handId = typeof hand === "string" ? hand : hand.id;
|
|
4897
|
+
runtime.emit("haptic", { hand: handId, intensity });
|
|
4898
|
+
},
|
|
4899
|
+
haptic_pulse: (intensity) => {
|
|
4900
|
+
runtime.emit("haptic", { hand: "both", intensity });
|
|
4901
|
+
},
|
|
4902
|
+
apply_velocity: (node, velocity) => {
|
|
4903
|
+
runtime.emit("apply_velocity", { node, velocity });
|
|
4904
|
+
},
|
|
4905
|
+
spawn: (template, position) => {
|
|
4906
|
+
return runtime.spawnTemplate(template, position);
|
|
4907
|
+
},
|
|
4908
|
+
assistant_generate: (prompt, context) => {
|
|
4909
|
+
runtime.emit("assistant_generate", { prompt, context });
|
|
4910
|
+
},
|
|
4911
|
+
destroy: (node) => {
|
|
4912
|
+
runtime.destroyNode(node);
|
|
4913
|
+
},
|
|
4914
|
+
api_call: async (url, method, body) => {
|
|
4915
|
+
const response = await fetch(url, {
|
|
4916
|
+
method,
|
|
4917
|
+
headers: body ? { "Content-Type": "application/json" } : void 0,
|
|
4918
|
+
body: body ? JSON.stringify(body) : void 0
|
|
4919
|
+
});
|
|
4920
|
+
return response.json();
|
|
4921
|
+
},
|
|
4922
|
+
open_modal: (modalId) => {
|
|
4923
|
+
runtime.emit("open_modal", { id: modalId });
|
|
4924
|
+
},
|
|
4925
|
+
close_modal: (modalId) => {
|
|
4926
|
+
runtime.emit("close_modal", { id: modalId });
|
|
4927
|
+
},
|
|
4928
|
+
setTimeout: (callback, delay) => {
|
|
4929
|
+
return window.setTimeout(callback, delay);
|
|
4930
|
+
},
|
|
4931
|
+
clearTimeout: (id) => {
|
|
4932
|
+
window.clearTimeout(id);
|
|
4933
|
+
}
|
|
4934
|
+
};
|
|
4935
|
+
}
|
|
4142
4936
|
|
|
4143
4937
|
// src/traits/VoiceInputTrait.ts
|
|
4144
4938
|
var VoiceInputTrait = class {
|
|
@@ -4681,11 +5475,6 @@ function createAIDriverTrait(config) {
|
|
|
4681
5475
|
return new AIDriverTrait(config);
|
|
4682
5476
|
}
|
|
4683
5477
|
|
|
4684
|
-
// src/index.ts
|
|
4685
|
-
init_MaterialTrait();
|
|
4686
|
-
init_LightingTrait();
|
|
4687
|
-
init_RenderingTrait();
|
|
4688
|
-
|
|
4689
5478
|
// src/runtime/PerformanceTelemetry.ts
|
|
4690
5479
|
var PerformanceTelemetry = class {
|
|
4691
5480
|
// Export every 10s
|
|
@@ -5921,6 +6710,6 @@ function isHoloScriptSupported() {
|
|
|
5921
6710
|
return !!(win.window.navigator?.xr || win.window.navigator?.getVRDisplays || win.window.webkitGetUserMedia);
|
|
5922
6711
|
}
|
|
5923
6712
|
|
|
5924
|
-
export { AIDriverTrait, BehaviorTreeRunner, GOAPPlanner, HOLOSCRIPT_DEMO_SCRIPTS, HOLOSCRIPT_GESTURES, HOLOSCRIPT_SUPPORTED_PLATFORMS, HOLOSCRIPT_VERSION, HOLOSCRIPT_VOICE_COMMANDS, HoloScript2DParser, HoloScriptParser, HoloScriptPlusParser, HoloScriptPlusRuntimeImpl, HoloScriptPlusParser2 as HoloScriptTraitAnnotationParser, HololandGraphicsPipelineService, LIGHTING_PRESETS, LightingTrait, MATERIAL_PRESETS, MaterialTrait, PerformanceTelemetry, PlatformPerformanceOptimizer, ReactiveState, RenderingTrait, VRTraitRegistry, VoiceInputTrait, bind, computed, createAIDriverTrait, createHoloScriptEnvironment, createLightingTrait, createMaterialTrait, createParser, createRenderingTrait, createState, createVoiceInputTrait, effect, getPerformanceTelemetry, isHoloScriptSupported, parse as parseHoloScriptPlus, reactive };
|
|
6713
|
+
export { AIDriverTrait, BehaviorTreeRunner, GOAPPlanner, HOLOSCRIPT_DEMO_SCRIPTS, HOLOSCRIPT_GESTURES, HOLOSCRIPT_SUPPORTED_PLATFORMS, HOLOSCRIPT_VERSION, HOLOSCRIPT_VOICE_COMMANDS, HoloScript2DParser, HoloScriptParser, HoloScriptPlusParser, HoloScriptPlusRuntimeImpl, HoloScriptPlusParser2 as HoloScriptTraitAnnotationParser, HoloScriptValidator, HololandGraphicsPipelineService, LIFECYCLE_HOOKS, LIGHTING_PRESETS, LightingTrait, MATERIAL_PRESETS, MaterialTrait, PerformanceTelemetry, PlatformPerformanceOptimizer, ReactiveState, RenderingTrait, VRTraitRegistry, VR_TRAITS, VoiceInputTrait, bind, computed, createAIDriverTrait, createHoloScriptEnvironment, createLightingTrait, createMaterialTrait, createParser, createRenderingTrait, createState, createVoiceInputTrait, effect, getPerformanceTelemetry, isHoloScriptSupported, parse as parseHoloScriptPlus, reactive };
|
|
5925
6714
|
//# sourceMappingURL=index.js.map
|
|
5926
6715
|
//# sourceMappingURL=index.js.map
|