@viji-dev/core 0.2.11 → 0.2.13

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.
@@ -1,939 +0,0 @@
1
- class ShaderParameterParser {
2
- /**
3
- * Parse all parameter declarations from shader code
4
- */
5
- static parseParameters(shaderCode) {
6
- const parameters = [];
7
- const lines = shaderCode.split("\n");
8
- for (const line of lines) {
9
- const trimmed = line.trim();
10
- const match = trimmed.match(/\/\/\s*@viji-(\w+):(\w+)\s+(.+)/);
11
- if (match) {
12
- const [, type, uniformName, configStr] = match;
13
- try {
14
- const config = this.parseKeyValuePairs(configStr);
15
- const param = {
16
- type,
17
- uniformName,
18
- label: config.label || uniformName,
19
- default: config.default,
20
- config
21
- };
22
- this.validateParameter(param);
23
- parameters.push(param);
24
- } catch (error) {
25
- console.warn(`Failed to parse shader parameter: ${line}`, error);
26
- }
27
- }
28
- }
29
- return parameters;
30
- }
31
- /**
32
- * Parse key:value pairs from configuration string
33
- */
34
- static parseKeyValuePairs(configStr) {
35
- const config = {};
36
- const keyValueRegex = /(\w+):((?:"[^"]*"|\[[^\]]*\]|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|[^\s]+))/g;
37
- let match;
38
- while ((match = keyValueRegex.exec(configStr)) !== null) {
39
- const [, key, value] = match;
40
- config[key] = this.parseValue(value);
41
- }
42
- return config;
43
- }
44
- /**
45
- * Parse individual value from string
46
- */
47
- static parseValue(value) {
48
- if (value.startsWith('"') && value.endsWith('"')) {
49
- return value.slice(1, -1);
50
- }
51
- if (value.startsWith("[") && value.endsWith("]")) {
52
- try {
53
- return JSON.parse(value);
54
- } catch {
55
- const items = value.slice(1, -1).split(",").map((s) => s.trim());
56
- return items.map((item) => {
57
- if (item.startsWith('"') && item.endsWith('"')) {
58
- return item.slice(1, -1);
59
- }
60
- const num2 = parseFloat(item);
61
- return isNaN(num2) ? item : num2;
62
- });
63
- }
64
- }
65
- if (value.startsWith("#")) {
66
- return value;
67
- }
68
- if (value === "true") return true;
69
- if (value === "false") return false;
70
- const num = parseFloat(value);
71
- if (!isNaN(num)) return num;
72
- return value;
73
- }
74
- /**
75
- * Validate parameter definition
76
- */
77
- static validateParameter(param) {
78
- if (!param.type) {
79
- throw new Error("Parameter type is required");
80
- }
81
- if (!param.uniformName) {
82
- throw new Error("Parameter uniformName is required");
83
- }
84
- if (!param.config.label) {
85
- throw new Error(`Parameter ${param.uniformName} missing required 'label' key`);
86
- }
87
- switch (param.type) {
88
- case "slider":
89
- case "number":
90
- if (param.config.default === void 0) {
91
- throw new Error(`Parameter ${param.uniformName} of type ${param.type} missing required 'default' key`);
92
- }
93
- break;
94
- case "color":
95
- if (param.config.default === void 0) {
96
- throw new Error(`Parameter ${param.uniformName} of type 'color' missing required 'default' key`);
97
- }
98
- if (!param.config.default.startsWith("#")) {
99
- throw new Error(`Parameter ${param.uniformName} of type 'color' default must be hex color (e.g., #ff0000)`);
100
- }
101
- break;
102
- case "toggle":
103
- if (param.config.default === void 0) {
104
- throw new Error(`Parameter ${param.uniformName} of type 'toggle' missing required 'default' key`);
105
- }
106
- if (typeof param.config.default !== "boolean") {
107
- throw new Error(`Parameter ${param.uniformName} of type 'toggle' default must be boolean (true or false)`);
108
- }
109
- break;
110
- case "select":
111
- if (param.config.default === void 0) {
112
- throw new Error(`Parameter ${param.uniformName} of type 'select' missing required 'default' key`);
113
- }
114
- if (!param.config.options || !Array.isArray(param.config.options)) {
115
- throw new Error(`Parameter ${param.uniformName} of type 'select' missing required 'options' key (array)`);
116
- }
117
- break;
118
- case "image":
119
- break;
120
- default:
121
- console.warn(`Unknown parameter type: ${param.type}`);
122
- }
123
- if (param.uniformName.startsWith("u_")) {
124
- console.warn(`Parameter name "${param.uniformName}" uses reserved prefix "u_". Consider renaming to avoid conflicts with built-in uniforms.`);
125
- }
126
- }
127
- /**
128
- * Generate uniform declaration for a parameter
129
- */
130
- static generateUniformDeclaration(param) {
131
- switch (param.type) {
132
- case "slider":
133
- case "number":
134
- return `uniform float ${param.uniformName};`;
135
- case "color":
136
- return `uniform vec3 ${param.uniformName};`;
137
- case "toggle":
138
- return `uniform bool ${param.uniformName};`;
139
- case "select":
140
- return `uniform int ${param.uniformName};`;
141
- case "image":
142
- return `uniform sampler2D ${param.uniformName};`;
143
- default:
144
- return `// Unknown parameter type: ${param.type}`;
145
- }
146
- }
147
- }
148
- class ShaderWorkerAdapter {
149
- constructor(offscreenCanvas, _vijiAPI, shaderCode) {
150
- this.shaderCode = shaderCode;
151
- this.glslVersion = this.detectGLSLVersion(shaderCode);
152
- this.backbufferEnabled = shaderCode.includes("backbuffer");
153
- if (this.glslVersion === "glsl300") {
154
- const gl = offscreenCanvas.getContext("webgl2");
155
- if (!gl) {
156
- throw new Error("WebGL 2 not supported. Use GLSL ES 1.00 syntax instead.");
157
- }
158
- this.gl = gl;
159
- } else {
160
- const gl = offscreenCanvas.getContext("webgl");
161
- if (!gl) {
162
- throw new Error("WebGL not supported");
163
- }
164
- this.gl = gl;
165
- }
166
- }
167
- gl;
168
- program = null;
169
- uniformLocations = /* @__PURE__ */ new Map();
170
- textureUnits = /* @__PURE__ */ new Map();
171
- nextTextureUnit = 0;
172
- textures = /* @__PURE__ */ new Map();
173
- // Fullscreen quad
174
- quadBuffer = null;
175
- // Parameter definitions
176
- parameters = [];
177
- // GLSL version detection
178
- glslVersion = "glsl100";
179
- // Audio FFT texture
180
- audioFFTTexture = null;
181
- videoTexture = null;
182
- segmentationTexture = null;
183
- // Backbuffer support (ping-pong framebuffers)
184
- backbufferFramebuffer = null;
185
- backbufferTexture = null;
186
- currentFramebuffer = null;
187
- currentTexture = null;
188
- backbufferEnabled = false;
189
- /**
190
- * Initialize the shader adapter
191
- */
192
- async init() {
193
- try {
194
- this.parameters = ShaderParameterParser.parseParameters(this.shaderCode);
195
- this.createFullscreenQuad();
196
- const processedCode = this.injectUniforms(this.shaderCode);
197
- this.compileAndLinkShader(processedCode);
198
- this.cacheUniformLocations();
199
- this.reserveTextureUnits();
200
- if (this.backbufferEnabled) {
201
- this.createBackbufferFramebuffers();
202
- }
203
- } catch (error) {
204
- console.error("Failed to initialize ShaderWorkerAdapter:", error);
205
- throw error;
206
- }
207
- }
208
- /**
209
- * Detect GLSL version from shader code
210
- */
211
- detectGLSLVersion(code) {
212
- return code.includes("#version 300") ? "glsl300" : "glsl100";
213
- }
214
- /**
215
- * Create fullscreen quad geometry
216
- */
217
- createFullscreenQuad() {
218
- const vertices = new Float32Array([
219
- -1,
220
- -1,
221
- // Bottom-left
222
- 1,
223
- -1,
224
- // Bottom-right
225
- -1,
226
- 1,
227
- // Top-left
228
- 1,
229
- 1
230
- // Top-right
231
- ]);
232
- this.quadBuffer = this.gl.createBuffer();
233
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quadBuffer);
234
- this.gl.bufferData(this.gl.ARRAY_BUFFER, vertices, this.gl.STATIC_DRAW);
235
- }
236
- /**
237
- * Inject built-in and parameter uniforms into shader code
238
- */
239
- injectUniforms(artistCode) {
240
- let versionLine = "";
241
- let codeWithoutVersion = artistCode;
242
- const lines = artistCode.split("\n");
243
- for (let i = 0; i < lines.length; i++) {
244
- const trimmed = lines[i].trim();
245
- if (trimmed.startsWith("#version")) {
246
- versionLine = trimmed;
247
- lines[i] = "";
248
- codeWithoutVersion = lines.join("\n");
249
- break;
250
- }
251
- }
252
- const injectionPoint = this.findInjectionPoint(codeWithoutVersion);
253
- const builtInUniforms = this.getBuiltInUniforms();
254
- const parameterUniforms = this.parameters.map((p) => ShaderParameterParser.generateUniformDeclaration(p)).join("\n");
255
- const usesFwidth = artistCode.includes("fwidth");
256
- if (usesFwidth && this.glslVersion === "glsl100") {
257
- const ext = this.gl.getExtension("OES_standard_derivatives");
258
- if (!ext) {
259
- console.warn("Shader uses fwidth() but OES_standard_derivatives extension is not supported. Shader may not compile.");
260
- }
261
- }
262
- const parts = [];
263
- if (usesFwidth && this.glslVersion === "glsl100") {
264
- parts.push("#extension GL_OES_standard_derivatives : enable");
265
- }
266
- if (this.glslVersion === "glsl100") {
267
- parts.push("");
268
- parts.push("#ifdef GL_ES");
269
- parts.push("precision mediump float;");
270
- parts.push("#endif");
271
- } else {
272
- parts.push("");
273
- parts.push("precision mediump float;");
274
- }
275
- parts.push("");
276
- parts.push("// ===== VIJI AUTO-INJECTED UNIFORMS =====");
277
- parts.push("// Built-in uniforms (auto-provided)");
278
- parts.push(builtInUniforms);
279
- parts.push("");
280
- parts.push("// Parameter uniforms (from @viji-* declarations)");
281
- parts.push(parameterUniforms);
282
- parts.push("");
283
- parts.push("// ===== ARTIST CODE =====");
284
- const uniformBlock = parts.join("\n");
285
- const codeWithUniforms = codeWithoutVersion.slice(0, injectionPoint) + "\n" + uniformBlock + "\n" + codeWithoutVersion.slice(injectionPoint);
286
- const finalCode = versionLine ? versionLine + "\n" + codeWithUniforms : codeWithUniforms;
287
- console.log("=== INJECTED SHADER CODE (first 50 lines) ===");
288
- console.log(finalCode.split("\n").slice(0, 50).join("\n"));
289
- console.log("=== END INJECTED CODE ===");
290
- return finalCode;
291
- }
292
- /**
293
- * Find where to inject extensions and uniforms
294
- * Extensions must come after #version but before any code
295
- *
296
- * Strategy:
297
- * 1. If #version exists, inject right after it
298
- * 2. Otherwise, skip ALL comments (single and multi-line) and inject before first code
299
- */
300
- findInjectionPoint(code) {
301
- const lines = code.split("\n");
302
- for (let i = 0; i < lines.length; i++) {
303
- const line = lines[i].trim();
304
- if (line.startsWith("#version")) {
305
- return this.getLineEndPosition(code, i);
306
- }
307
- }
308
- let inMultiLineComment = false;
309
- let firstCodeLine = 0;
310
- for (let i = 0; i < lines.length; i++) {
311
- const line = lines[i].trim();
312
- if (line.includes("/*")) {
313
- inMultiLineComment = true;
314
- }
315
- if (line.includes("*/")) {
316
- inMultiLineComment = false;
317
- firstCodeLine = i + 1;
318
- continue;
319
- }
320
- if (inMultiLineComment) {
321
- continue;
322
- }
323
- if (line === "" || line.startsWith("//")) {
324
- firstCodeLine = i + 1;
325
- continue;
326
- }
327
- break;
328
- }
329
- if (firstCodeLine > 0 && firstCodeLine < lines.length) {
330
- return this.getLineEndPosition(code, firstCodeLine - 1);
331
- }
332
- return 0;
333
- }
334
- /**
335
- * Get byte position of end of line N
336
- */
337
- getLineEndPosition(code, lineNumber) {
338
- const lines = code.split("\n");
339
- let position = 0;
340
- for (let i = 0; i <= lineNumber && i < lines.length; i++) {
341
- position += lines[i].length + 1;
342
- }
343
- return position;
344
- }
345
- /**
346
- * Get built-in uniform declarations
347
- */
348
- getBuiltInUniforms() {
349
- return `// Core - Canvas & Timing
350
- uniform vec2 u_resolution;
351
- uniform float u_time;
352
- uniform float u_deltaTime;
353
- uniform int u_frame;
354
- uniform float u_pixelRatio;
355
- uniform float u_fps;
356
-
357
- // Mouse API
358
- uniform vec2 u_mouse;
359
- uniform bool u_mouseInCanvas;
360
- uniform bool u_mousePressed;
361
- uniform bool u_mouseLeft;
362
- uniform bool u_mouseRight;
363
- uniform bool u_mouseMiddle;
364
- uniform vec2 u_mouseVelocity;
365
-
366
- // Keyboard API - Common keys
367
- uniform bool u_keySpace;
368
- uniform bool u_keyShift;
369
- uniform bool u_keyCtrl;
370
- uniform bool u_keyAlt;
371
- uniform bool u_keyW;
372
- uniform bool u_keyA;
373
- uniform bool u_keyS;
374
- uniform bool u_keyD;
375
- uniform bool u_keyUp;
376
- uniform bool u_keyDown;
377
- uniform bool u_keyLeft;
378
- uniform bool u_keyRight;
379
-
380
- // Touch API
381
- uniform int u_touchCount;
382
- uniform vec2 u_touch0;
383
- uniform vec2 u_touch1;
384
- uniform vec2 u_touch2;
385
- uniform vec2 u_touch3;
386
- uniform vec2 u_touch4;
387
-
388
- // Audio
389
- uniform float u_audioVolume;
390
- uniform float u_audioPeak;
391
- uniform float u_audioBass;
392
- uniform float u_audioMid;
393
- uniform float u_audioTreble;
394
- uniform float u_audioSubBass;
395
- uniform float u_audioLowMid;
396
- uniform float u_audioHighMid;
397
- uniform float u_audioPresence;
398
- uniform float u_audioBrilliance;
399
- uniform sampler2D u_audioFFT;
400
-
401
- // Video
402
- uniform sampler2D u_video;
403
- uniform vec2 u_videoResolution;
404
- uniform float u_videoFrameRate;
405
-
406
- // CV - Face Detection
407
- uniform int u_faceCount;
408
- uniform vec4 u_face0Bounds;
409
- uniform vec3 u_face0HeadPose;
410
- uniform float u_face0Confidence;
411
- uniform float u_face0Happy;
412
- uniform float u_face0Sad;
413
- uniform float u_face0Angry;
414
- uniform float u_face0Surprised;
415
-
416
- // CV - Hand Tracking
417
- uniform int u_handCount;
418
- uniform vec3 u_leftHandPalm;
419
- uniform vec3 u_rightHandPalm;
420
- uniform float u_leftHandFist;
421
- uniform float u_leftHandOpen;
422
- uniform float u_rightHandFist;
423
- uniform float u_rightHandOpen;
424
-
425
- // CV - Pose Detection
426
- uniform bool u_poseDetected;
427
- uniform vec2 u_nosePosition;
428
- uniform vec2 u_leftWristPosition;
429
- uniform vec2 u_rightWristPosition;
430
- uniform vec2 u_leftAnklePosition;
431
- uniform vec2 u_rightAnklePosition;
432
-
433
- // CV - Segmentation
434
- uniform sampler2D u_segmentationMask;
435
- uniform vec2 u_segmentationRes;
436
-
437
- // Backbuffer (previous frame feedback)
438
- ${this.backbufferEnabled ? "uniform sampler2D backbuffer;" : "// backbuffer not enabled"}
439
- `;
440
- }
441
- /**
442
- * Compile and link shader program
443
- */
444
- compileAndLinkShader(fragmentShaderCode) {
445
- const gl = this.gl;
446
- const vertexShaderCode = this.glslVersion === "glsl300" ? `#version 300 es
447
- precision mediump float;
448
- in vec2 a_position;
449
- void main() {
450
- gl_Position = vec4(a_position, 0.0, 1.0);
451
- }` : `attribute vec2 a_position;
452
- void main() {
453
- gl_Position = vec4(a_position, 0.0, 1.0);
454
- }`;
455
- const vertexShader = this.compileShader(gl.VERTEX_SHADER, vertexShaderCode);
456
- const fragmentShader = this.compileShader(gl.FRAGMENT_SHADER, fragmentShaderCode);
457
- const program = gl.createProgram();
458
- if (!program) {
459
- throw new Error("Failed to create WebGL program");
460
- }
461
- gl.attachShader(program, vertexShader);
462
- gl.attachShader(program, fragmentShader);
463
- gl.linkProgram(program);
464
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
465
- const error = gl.getProgramInfoLog(program);
466
- throw new Error(`Shader program link failed: ${error}`);
467
- }
468
- this.program = program;
469
- gl.useProgram(program);
470
- gl.deleteShader(vertexShader);
471
- gl.deleteShader(fragmentShader);
472
- }
473
- /**
474
- * Compile a shader
475
- */
476
- compileShader(type, source) {
477
- const gl = this.gl;
478
- const shader = gl.createShader(type);
479
- if (!shader) {
480
- throw new Error("Failed to create shader");
481
- }
482
- gl.shaderSource(shader, source);
483
- gl.compileShader(shader);
484
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
485
- const error = gl.getShaderInfoLog(shader);
486
- const shaderType = type === gl.VERTEX_SHADER ? "vertex" : "fragment";
487
- throw new Error(`${shaderType} shader compilation failed:
488
- ${error}`);
489
- }
490
- return shader;
491
- }
492
- /**
493
- * Cache uniform locations for fast access
494
- */
495
- cacheUniformLocations() {
496
- if (!this.program) return;
497
- const gl = this.gl;
498
- const numUniforms = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);
499
- for (let i = 0; i < numUniforms; i++) {
500
- const info = gl.getActiveUniform(this.program, i);
501
- if (info) {
502
- const location = gl.getUniformLocation(this.program, info.name);
503
- this.uniformLocations.set(info.name, location);
504
- }
505
- }
506
- }
507
- /**
508
- * Reserve texture units for special textures
509
- */
510
- reserveTextureUnits() {
511
- this.textureUnits.set("u_audioFFT", this.nextTextureUnit++);
512
- this.textureUnits.set("u_video", this.nextTextureUnit++);
513
- this.textureUnits.set("u_segmentationMask", this.nextTextureUnit++);
514
- if (this.backbufferEnabled) {
515
- this.textureUnits.set("backbuffer", this.nextTextureUnit++);
516
- }
517
- }
518
- /**
519
- * Create ping-pong framebuffers for backbuffer support
520
- */
521
- createBackbufferFramebuffers() {
522
- const gl = this.gl;
523
- const width = gl.canvas.width;
524
- const height = gl.canvas.height;
525
- const createFBOTexture = () => {
526
- const texture = gl.createTexture();
527
- gl.bindTexture(gl.TEXTURE_2D, texture);
528
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
529
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
530
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
531
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
532
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
533
- const framebuffer = gl.createFramebuffer();
534
- gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
535
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
536
- return { framebuffer, texture };
537
- };
538
- const fbo1 = createFBOTexture();
539
- const fbo2 = createFBOTexture();
540
- this.backbufferFramebuffer = fbo1.framebuffer;
541
- this.backbufferTexture = fbo1.texture;
542
- this.currentFramebuffer = fbo2.framebuffer;
543
- this.currentTexture = fbo2.texture;
544
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
545
- gl.bindTexture(gl.TEXTURE_2D, null);
546
- }
547
- /**
548
- * Main render method
549
- */
550
- render(viji, parameterObjects) {
551
- const gl = this.gl;
552
- if (!this.program || !this.quadBuffer) {
553
- console.warn("Shader not initialized");
554
- return;
555
- }
556
- gl.useProgram(this.program);
557
- this.updateBuiltInUniforms(viji);
558
- this.updateParameterUniforms(parameterObjects);
559
- if (this.backbufferEnabled && this.backbufferTexture) {
560
- const backbufferUnit = this.textureUnits.get("backbuffer");
561
- if (backbufferUnit !== void 0) {
562
- gl.activeTexture(gl.TEXTURE0 + backbufferUnit);
563
- gl.bindTexture(gl.TEXTURE_2D, this.backbufferTexture);
564
- this.setUniform("backbuffer", "sampler2D", backbufferUnit);
565
- }
566
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.currentFramebuffer);
567
- }
568
- const positionLocation = gl.getAttribLocation(this.program, "a_position");
569
- gl.bindBuffer(gl.ARRAY_BUFFER, this.quadBuffer);
570
- gl.enableVertexAttribArray(positionLocation);
571
- gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
572
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
573
- if (this.backbufferEnabled) {
574
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
575
- gl.activeTexture(gl.TEXTURE0);
576
- gl.bindTexture(gl.TEXTURE_2D, this.currentTexture);
577
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
578
- const tempFB = this.backbufferFramebuffer;
579
- const tempTex = this.backbufferTexture;
580
- this.backbufferFramebuffer = this.currentFramebuffer;
581
- this.backbufferTexture = this.currentTexture;
582
- this.currentFramebuffer = tempFB;
583
- this.currentTexture = tempTex;
584
- }
585
- }
586
- /**
587
- * Update built-in uniforms from viji object
588
- */
589
- updateBuiltInUniforms(viji) {
590
- this.setUniform("u_resolution", "vec2", [viji.width, viji.height]);
591
- this.setUniform("u_time", "float", viji.time);
592
- this.setUniform("u_deltaTime", "float", viji.deltaTime);
593
- this.setUniform("u_frame", "int", viji.frameCount);
594
- this.setUniform("u_pixelRatio", "float", viji.pixelRatio);
595
- this.setUniform("u_fps", "float", viji.fps);
596
- this.setUniform("u_mouse", "vec2", [viji.mouse.x, viji.height - viji.mouse.y]);
597
- this.setUniform("u_mouseInCanvas", "bool", viji.mouse.isInCanvas);
598
- this.setUniform("u_mousePressed", "bool", viji.mouse.isPressed);
599
- this.setUniform("u_mouseLeft", "bool", viji.mouse.leftButton);
600
- this.setUniform("u_mouseRight", "bool", viji.mouse.rightButton);
601
- this.setUniform("u_mouseMiddle", "bool", viji.mouse.middleButton);
602
- this.setUniform("u_mouseVelocity", "vec2", [viji.mouse.velocity.x, -viji.mouse.velocity.y]);
603
- this.setUniform("u_keySpace", "bool", viji.keyboard.isPressed(" ") || viji.keyboard.isPressed("space"));
604
- this.setUniform("u_keyShift", "bool", viji.keyboard.shift);
605
- this.setUniform("u_keyCtrl", "bool", viji.keyboard.ctrl);
606
- this.setUniform("u_keyAlt", "bool", viji.keyboard.alt);
607
- this.setUniform("u_keyW", "bool", viji.keyboard.isPressed("w") || viji.keyboard.isPressed("W"));
608
- this.setUniform("u_keyA", "bool", viji.keyboard.isPressed("a") || viji.keyboard.isPressed("A"));
609
- this.setUniform("u_keyS", "bool", viji.keyboard.isPressed("s") || viji.keyboard.isPressed("S"));
610
- this.setUniform("u_keyD", "bool", viji.keyboard.isPressed("d") || viji.keyboard.isPressed("D"));
611
- this.setUniform("u_keyUp", "bool", viji.keyboard.isPressed("ArrowUp"));
612
- this.setUniform("u_keyDown", "bool", viji.keyboard.isPressed("ArrowDown"));
613
- this.setUniform("u_keyLeft", "bool", viji.keyboard.isPressed("ArrowLeft"));
614
- this.setUniform("u_keyRight", "bool", viji.keyboard.isPressed("ArrowRight"));
615
- this.setUniform("u_touchCount", "int", viji.touches.count);
616
- for (let i = 0; i < 5; i++) {
617
- const touch = viji.touches.points[i];
618
- if (touch) {
619
- this.setUniform(`u_touch${i}`, "vec2", [touch.x, viji.height - touch.y]);
620
- } else {
621
- this.setUniform(`u_touch${i}`, "vec2", [0, 0]);
622
- }
623
- }
624
- const audio = viji.audio;
625
- this.setUniform("u_audioVolume", "float", audio.volume?.rms || 0);
626
- this.setUniform("u_audioPeak", "float", audio.volume?.peak || 0);
627
- this.setUniform("u_audioBass", "float", audio.bands?.bass || 0);
628
- this.setUniform("u_audioMid", "float", audio.bands?.mid || 0);
629
- this.setUniform("u_audioTreble", "float", audio.bands?.treble || 0);
630
- this.setUniform("u_audioSubBass", "float", audio.bands?.subBass || 0);
631
- this.setUniform("u_audioLowMid", "float", audio.bands?.lowMid || 0);
632
- this.setUniform("u_audioHighMid", "float", audio.bands?.highMid || 0);
633
- this.setUniform("u_audioPresence", "float", audio.bands?.presence || 0);
634
- this.setUniform("u_audioBrilliance", "float", audio.bands?.brilliance || 0);
635
- if (audio.isConnected) {
636
- this.updateAudioFFTTexture(audio.getFrequencyData());
637
- }
638
- const video = viji.video;
639
- if (video.isConnected && video.currentFrame) {
640
- this.updateVideoTexture(video.currentFrame);
641
- this.setUniform("u_videoResolution", "vec2", [video.frameWidth, video.frameHeight]);
642
- this.setUniform("u_videoFrameRate", "float", video.frameRate);
643
- } else {
644
- this.setUniform("u_videoResolution", "vec2", [0, 0]);
645
- this.setUniform("u_videoFrameRate", "float", 0);
646
- }
647
- const faces = video.faces || [];
648
- this.setUniform("u_faceCount", "int", faces.length);
649
- if (faces.length > 0) {
650
- const face = faces[0];
651
- this.setUniform("u_face0Bounds", "vec4", [face.bounds.x, face.bounds.y, face.bounds.width, face.bounds.height]);
652
- this.setUniform("u_face0HeadPose", "vec3", [face.headPose.pitch, face.headPose.yaw, face.headPose.roll]);
653
- this.setUniform("u_face0Confidence", "float", face.confidence);
654
- this.setUniform("u_face0Happy", "float", face.expressions.happy);
655
- this.setUniform("u_face0Sad", "float", face.expressions.sad);
656
- this.setUniform("u_face0Angry", "float", face.expressions.angry);
657
- this.setUniform("u_face0Surprised", "float", face.expressions.surprised);
658
- } else {
659
- this.setUniform("u_face0Bounds", "vec4", [0, 0, 0, 0]);
660
- this.setUniform("u_face0HeadPose", "vec3", [0, 0, 0]);
661
- this.setUniform("u_face0Confidence", "float", 0);
662
- this.setUniform("u_face0Happy", "float", 0);
663
- this.setUniform("u_face0Sad", "float", 0);
664
- this.setUniform("u_face0Angry", "float", 0);
665
- this.setUniform("u_face0Surprised", "float", 0);
666
- }
667
- const hands = video.hands || [];
668
- this.setUniform("u_handCount", "int", hands.length);
669
- const leftHand = hands.find((h) => h.handedness === "left");
670
- const rightHand = hands.find((h) => h.handedness === "right");
671
- if (leftHand) {
672
- this.setUniform("u_leftHandPalm", "vec3", [leftHand.palm.x, leftHand.palm.y, leftHand.palm.z]);
673
- this.setUniform("u_leftHandFist", "float", leftHand.gestures?.fist || 0);
674
- this.setUniform("u_leftHandOpen", "float", leftHand.gestures?.openPalm || 0);
675
- } else {
676
- this.setUniform("u_leftHandPalm", "vec3", [0, 0, 0]);
677
- this.setUniform("u_leftHandFist", "float", 0);
678
- this.setUniform("u_leftHandOpen", "float", 0);
679
- }
680
- if (rightHand) {
681
- this.setUniform("u_rightHandPalm", "vec3", [rightHand.palm.x, rightHand.palm.y, rightHand.palm.z]);
682
- this.setUniform("u_rightHandFist", "float", rightHand.gestures?.fist || 0);
683
- this.setUniform("u_rightHandOpen", "float", rightHand.gestures?.openPalm || 0);
684
- } else {
685
- this.setUniform("u_rightHandPalm", "vec3", [0, 0, 0]);
686
- this.setUniform("u_rightHandFist", "float", 0);
687
- this.setUniform("u_rightHandOpen", "float", 0);
688
- }
689
- const pose = video.pose;
690
- this.setUniform("u_poseDetected", "bool", pose !== null);
691
- if (pose) {
692
- const nose = pose.landmarks[0];
693
- const leftWrist = pose.landmarks[15];
694
- const rightWrist = pose.landmarks[16];
695
- const leftAnkle = pose.landmarks[27];
696
- const rightAnkle = pose.landmarks[28];
697
- this.setUniform("u_nosePosition", "vec2", [nose?.x || 0, nose?.y || 0]);
698
- this.setUniform("u_leftWristPosition", "vec2", [leftWrist?.x || 0, leftWrist?.y || 0]);
699
- this.setUniform("u_rightWristPosition", "vec2", [rightWrist?.x || 0, rightWrist?.y || 0]);
700
- this.setUniform("u_leftAnklePosition", "vec2", [leftAnkle?.x || 0, leftAnkle?.y || 0]);
701
- this.setUniform("u_rightAnklePosition", "vec2", [rightAnkle?.x || 0, rightAnkle?.y || 0]);
702
- } else {
703
- this.setUniform("u_nosePosition", "vec2", [0, 0]);
704
- this.setUniform("u_leftWristPosition", "vec2", [0, 0]);
705
- this.setUniform("u_rightWristPosition", "vec2", [0, 0]);
706
- this.setUniform("u_leftAnklePosition", "vec2", [0, 0]);
707
- this.setUniform("u_rightAnklePosition", "vec2", [0, 0]);
708
- }
709
- const segmentation = video.segmentation;
710
- if (segmentation) {
711
- this.updateSegmentationTexture(segmentation.mask, segmentation.width, segmentation.height);
712
- this.setUniform("u_segmentationRes", "vec2", [segmentation.width, segmentation.height]);
713
- } else {
714
- this.setUniform("u_segmentationRes", "vec2", [0, 0]);
715
- }
716
- }
717
- /**
718
- * Update parameter uniforms from parameter objects
719
- */
720
- updateParameterUniforms(parameterObjects) {
721
- for (const param of this.parameters) {
722
- const paramObj = parameterObjects.get(param.uniformName);
723
- if (!paramObj) continue;
724
- const value = paramObj.value;
725
- switch (param.type) {
726
- case "slider":
727
- case "number":
728
- this.setUniform(param.uniformName, "float", value);
729
- break;
730
- case "color":
731
- const rgb = this.hexToRgb(value);
732
- this.setUniform(param.uniformName, "vec3", rgb);
733
- break;
734
- case "toggle":
735
- this.setUniform(param.uniformName, "bool", value);
736
- break;
737
- case "select":
738
- const index = param.config.options?.indexOf(value) || 0;
739
- this.setUniform(param.uniformName, "int", index);
740
- break;
741
- case "image":
742
- if (value) {
743
- this.updateImageTexture(param.uniformName, value);
744
- }
745
- break;
746
- }
747
- }
748
- }
749
- /**
750
- * Set uniform value
751
- */
752
- setUniform(name, type, value) {
753
- const location = this.uniformLocations.get(name);
754
- if (location === null || location === void 0) {
755
- return;
756
- }
757
- const gl = this.gl;
758
- switch (type) {
759
- case "float":
760
- gl.uniform1f(location, value);
761
- break;
762
- case "int":
763
- gl.uniform1i(location, value);
764
- break;
765
- case "bool":
766
- gl.uniform1i(location, value ? 1 : 0);
767
- break;
768
- case "vec2":
769
- gl.uniform2f(location, value[0], value[1]);
770
- break;
771
- case "vec3":
772
- gl.uniform3f(location, value[0], value[1], value[2]);
773
- break;
774
- case "vec4":
775
- gl.uniform4f(location, value[0], value[1], value[2], value[3]);
776
- break;
777
- }
778
- }
779
- /**
780
- * Convert hex color to RGB [0-1]
781
- */
782
- hexToRgb(hex) {
783
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
784
- if (result) {
785
- return [
786
- parseInt(result[1], 16) / 255,
787
- parseInt(result[2], 16) / 255,
788
- parseInt(result[3], 16) / 255
789
- ];
790
- }
791
- return [0, 0, 0];
792
- }
793
- /**
794
- * Update audio FFT texture
795
- */
796
- updateAudioFFTTexture(frequencyData) {
797
- const gl = this.gl;
798
- const unit = this.textureUnits.get("u_audioFFT");
799
- if (!this.audioFFTTexture) {
800
- this.audioFFTTexture = gl.createTexture();
801
- }
802
- gl.activeTexture(gl.TEXTURE0 + unit);
803
- gl.bindTexture(gl.TEXTURE_2D, this.audioFFTTexture);
804
- gl.texImage2D(
805
- gl.TEXTURE_2D,
806
- 0,
807
- gl.LUMINANCE,
808
- frequencyData.length,
809
- 1,
810
- 0,
811
- gl.LUMINANCE,
812
- gl.UNSIGNED_BYTE,
813
- frequencyData
814
- );
815
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
816
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
817
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
818
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
819
- const location = this.uniformLocations.get("u_audioFFT");
820
- if (location) {
821
- gl.uniform1i(location, unit);
822
- }
823
- }
824
- /**
825
- * Update video texture
826
- */
827
- updateVideoTexture(videoFrame) {
828
- const gl = this.gl;
829
- const unit = this.textureUnits.get("u_video");
830
- if (!this.videoTexture) {
831
- this.videoTexture = gl.createTexture();
832
- }
833
- gl.activeTexture(gl.TEXTURE0 + unit);
834
- gl.bindTexture(gl.TEXTURE_2D, this.videoTexture);
835
- gl.texImage2D(
836
- gl.TEXTURE_2D,
837
- 0,
838
- gl.RGBA,
839
- gl.RGBA,
840
- gl.UNSIGNED_BYTE,
841
- videoFrame
842
- );
843
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
844
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
845
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
846
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
847
- const location = this.uniformLocations.get("u_video");
848
- if (location) {
849
- gl.uniform1i(location, unit);
850
- }
851
- }
852
- /**
853
- * Update segmentation mask texture
854
- */
855
- updateSegmentationTexture(mask, width, height) {
856
- const gl = this.gl;
857
- const unit = this.textureUnits.get("u_segmentationMask");
858
- if (!this.segmentationTexture) {
859
- this.segmentationTexture = gl.createTexture();
860
- }
861
- gl.activeTexture(gl.TEXTURE0 + unit);
862
- gl.bindTexture(gl.TEXTURE_2D, this.segmentationTexture);
863
- gl.texImage2D(
864
- gl.TEXTURE_2D,
865
- 0,
866
- gl.LUMINANCE,
867
- width,
868
- height,
869
- 0,
870
- gl.LUMINANCE,
871
- gl.UNSIGNED_BYTE,
872
- mask
873
- );
874
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
875
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
876
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
877
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
878
- const location = this.uniformLocations.get("u_segmentationMask");
879
- if (location) {
880
- gl.uniform1i(location, unit);
881
- }
882
- }
883
- /**
884
- * Update image parameter texture
885
- */
886
- updateImageTexture(name, imageBitmap) {
887
- const gl = this.gl;
888
- if (!this.textureUnits.has(name)) {
889
- this.textureUnits.set(name, this.nextTextureUnit++);
890
- }
891
- const unit = this.textureUnits.get(name);
892
- if (!this.textures.has(name)) {
893
- const texture2 = gl.createTexture();
894
- if (texture2) {
895
- this.textures.set(name, texture2);
896
- }
897
- }
898
- const texture = this.textures.get(name);
899
- if (!texture) return;
900
- gl.activeTexture(gl.TEXTURE0 + unit);
901
- gl.bindTexture(gl.TEXTURE_2D, texture);
902
- gl.texImage2D(
903
- gl.TEXTURE_2D,
904
- 0,
905
- gl.RGBA,
906
- gl.RGBA,
907
- gl.UNSIGNED_BYTE,
908
- imageBitmap
909
- );
910
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
911
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
912
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
913
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
914
- const location = this.uniformLocations.get(name);
915
- if (location) {
916
- gl.uniform1i(location, unit);
917
- }
918
- }
919
- /**
920
- * Handle canvas resize
921
- */
922
- resize(width, height) {
923
- const gl = this.gl;
924
- gl.viewport(0, 0, width, height);
925
- if (this.backbufferEnabled) {
926
- this.createBackbufferFramebuffers();
927
- }
928
- }
929
- /**
930
- * Get parameter definitions for host
931
- */
932
- getParameterDefinitions() {
933
- return this.parameters;
934
- }
935
- }
936
- export {
937
- ShaderWorkerAdapter
938
- };
939
- //# sourceMappingURL=ShaderWorkerAdapter.js.map