@viji-dev/core 0.3.23 → 0.3.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1427 -1439
- package/dist/artist-dts-p5.js +1 -1
- package/dist/artist-dts.js +1 -1
- package/dist/artist-global.d.ts +35 -31
- package/dist/artist-js-ambient.d.ts +2 -35
- package/dist/artist-jsdoc.d.ts +406 -13
- package/dist/assets/{viji.worker-DTQvTudb.js → viji.worker-Zg128woJ.js} +321 -121
- package/dist/assets/viji.worker-Zg128woJ.js.map +1 -0
- package/dist/docs-api.d.ts +7 -0
- package/dist/docs-api.js +1129 -124
- package/dist/{essentia-wasm.web-0nilrUD3.js → essentia-wasm.web-Dq7KVAx1.js} +2 -2
- package/dist/{essentia-wasm.web-0nilrUD3.js.map → essentia-wasm.web-Dq7KVAx1.js.map} +1 -1
- package/dist/{index-Bu1euCdl.js → index-C6fMmKQj.js} +48 -167
- package/dist/index-C6fMmKQj.js.map +1 -0
- package/dist/index.d.ts +38 -32
- package/dist/index.js +1 -1
- package/dist/shader-uniforms.js +52 -7
- package/package.json +1 -1
- package/dist/assets/viji.worker-DTQvTudb.js.map +0 -1
- package/dist/index-Bu1euCdl.js.map +0 -1
|
@@ -201,6 +201,27 @@ class ParameterSystem {
|
|
|
201
201
|
this.parameterObjects.set(paramName, imageObject);
|
|
202
202
|
return imageObject;
|
|
203
203
|
}
|
|
204
|
+
createButtonParameter(config) {
|
|
205
|
+
const paramName = config.label;
|
|
206
|
+
const buttonObject = {
|
|
207
|
+
value: false,
|
|
208
|
+
label: config.label,
|
|
209
|
+
description: config.description ?? "",
|
|
210
|
+
group: config.group ?? "general",
|
|
211
|
+
category: config.category ?? "general"
|
|
212
|
+
};
|
|
213
|
+
const definition = {
|
|
214
|
+
type: "button",
|
|
215
|
+
defaultValue: false,
|
|
216
|
+
label: buttonObject.label,
|
|
217
|
+
description: buttonObject.description,
|
|
218
|
+
group: buttonObject.group,
|
|
219
|
+
category: buttonObject.category
|
|
220
|
+
};
|
|
221
|
+
this.storeParameterDefinition(paramName, definition);
|
|
222
|
+
this.parameterObjects.set(paramName, buttonObject);
|
|
223
|
+
return buttonObject;
|
|
224
|
+
}
|
|
204
225
|
storeParameterDefinition(name, definition) {
|
|
205
226
|
this.parameterDefinitions.set(name, definition);
|
|
206
227
|
this.parameterValues.set(name, definition.defaultValue);
|
|
@@ -277,8 +298,14 @@ class ParameterSystem {
|
|
|
277
298
|
}
|
|
278
299
|
break;
|
|
279
300
|
case "image":
|
|
280
|
-
if (value !== null && !(value instanceof ImageBitmap)
|
|
281
|
-
console.error(`Parameter '${name}' must be null
|
|
301
|
+
if (value !== null && !(value instanceof ImageBitmap)) {
|
|
302
|
+
console.error(`Parameter '${name}' must be null or ImageBitmap, got: ${value}`);
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
break;
|
|
306
|
+
case "button":
|
|
307
|
+
if (typeof value !== "boolean") {
|
|
308
|
+
console.error(`Parameter '${name}' must be a boolean, got: ${value}`);
|
|
282
309
|
return false;
|
|
283
310
|
}
|
|
284
311
|
break;
|
|
@@ -338,10 +365,29 @@ class ParameterSystem {
|
|
|
338
365
|
getAllParameterObjects() {
|
|
339
366
|
return this.parameterObjects;
|
|
340
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Reset all button parameters to false after each frame.
|
|
370
|
+
* Buttons are momentary: true for exactly 1 frame when pressed, then auto-reset.
|
|
371
|
+
*/
|
|
372
|
+
resetButtonParameters() {
|
|
373
|
+
for (const [name, definition] of this.parameterDefinitions) {
|
|
374
|
+
if (definition.type === "button") {
|
|
375
|
+
const paramObj = this.parameterObjects.get(name);
|
|
376
|
+
if (paramObj && paramObj.value === true) {
|
|
377
|
+
paramObj.value = false;
|
|
378
|
+
this.parameterValues.set(name, false);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
341
383
|
}
|
|
342
384
|
class InteractionSystem {
|
|
343
385
|
// Interaction enabled state
|
|
344
386
|
isEnabled = true;
|
|
387
|
+
// Previous button bitmask for detecting press/release transitions
|
|
388
|
+
previousButtons = 0;
|
|
389
|
+
// Previous touch positions for computing deltas and velocity
|
|
390
|
+
previousTouchPositions = /* @__PURE__ */ new Map();
|
|
345
391
|
// Mouse interaction state
|
|
346
392
|
mouseState = {
|
|
347
393
|
x: 0,
|
|
@@ -351,7 +397,6 @@ class InteractionSystem {
|
|
|
351
397
|
leftButton: false,
|
|
352
398
|
rightButton: false,
|
|
353
399
|
middleButton: false,
|
|
354
|
-
velocity: { x: 0, y: 0 },
|
|
355
400
|
deltaX: 0,
|
|
356
401
|
deltaY: 0,
|
|
357
402
|
wheelDelta: 0,
|
|
@@ -374,7 +419,9 @@ class InteractionSystem {
|
|
|
374
419
|
shift: false,
|
|
375
420
|
ctrl: false,
|
|
376
421
|
alt: false,
|
|
377
|
-
meta: false
|
|
422
|
+
meta: false,
|
|
423
|
+
textureData: null
|
|
424
|
+
// Assigned in constructor body
|
|
378
425
|
};
|
|
379
426
|
// Touch interaction state
|
|
380
427
|
touchState = {
|
|
@@ -383,23 +430,26 @@ class InteractionSystem {
|
|
|
383
430
|
started: [],
|
|
384
431
|
moved: [],
|
|
385
432
|
ended: [],
|
|
386
|
-
primary: null
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
433
|
+
primary: null
|
|
434
|
+
};
|
|
435
|
+
// Keyboard texture buffer (256x3, Shadertoy-compatible)
|
|
436
|
+
// Row 0: held state, Row 1: pressed this frame, Row 2: toggle
|
|
437
|
+
keyboardTextureData = new Uint8Array(256 * 3);
|
|
438
|
+
// Unified pointer state (derived from mouse + touch)
|
|
439
|
+
pointerState = {
|
|
440
|
+
x: 0,
|
|
441
|
+
y: 0,
|
|
442
|
+
deltaX: 0,
|
|
443
|
+
deltaY: 0,
|
|
444
|
+
isDown: false,
|
|
445
|
+
wasPressed: false,
|
|
446
|
+
wasReleased: false,
|
|
447
|
+
isInCanvas: false,
|
|
448
|
+
type: "none"
|
|
401
449
|
};
|
|
450
|
+
previousPointerDown = false;
|
|
402
451
|
constructor() {
|
|
452
|
+
this.keyboardState.textureData = this.keyboardTextureData;
|
|
403
453
|
this.handleMouseUpdate = this.handleMouseUpdate.bind(this);
|
|
404
454
|
this.handleKeyboardUpdate = this.handleKeyboardUpdate.bind(this);
|
|
405
455
|
this.handleTouchUpdate = this.handleTouchUpdate.bind(this);
|
|
@@ -412,7 +462,8 @@ class InteractionSystem {
|
|
|
412
462
|
return {
|
|
413
463
|
mouse: this.mouseState,
|
|
414
464
|
keyboard: this.keyboardState,
|
|
415
|
-
touches: this.touchState
|
|
465
|
+
touches: this.touchState,
|
|
466
|
+
pointer: this.pointerState
|
|
416
467
|
};
|
|
417
468
|
}
|
|
418
469
|
/**
|
|
@@ -422,17 +473,17 @@ class InteractionSystem {
|
|
|
422
473
|
this.mouseState.wasPressed = false;
|
|
423
474
|
this.mouseState.wasReleased = false;
|
|
424
475
|
this.mouseState.wasMoved = false;
|
|
476
|
+
this.mouseState.deltaX = 0;
|
|
477
|
+
this.mouseState.deltaY = 0;
|
|
425
478
|
this.mouseState.wheelDelta = 0;
|
|
426
479
|
this.mouseState.wheelX = 0;
|
|
427
480
|
this.mouseState.wheelY = 0;
|
|
428
481
|
this.keyboardState.pressedThisFrame.clear();
|
|
429
482
|
this.keyboardState.releasedThisFrame.clear();
|
|
483
|
+
this.keyboardTextureData.fill(0, 256, 512);
|
|
430
484
|
this.touchState.started = [];
|
|
431
485
|
this.touchState.moved = [];
|
|
432
486
|
this.touchState.ended = [];
|
|
433
|
-
this.touchState.gestures.isTapping = false;
|
|
434
|
-
this.touchState.gestures.pinchDelta = 0;
|
|
435
|
-
this.touchState.gestures.rotationDelta = 0;
|
|
436
487
|
}
|
|
437
488
|
/**
|
|
438
489
|
* Handle mouse update messages from the host
|
|
@@ -446,16 +497,17 @@ class InteractionSystem {
|
|
|
446
497
|
this.mouseState.rightButton = (data.buttons & 2) !== 0;
|
|
447
498
|
this.mouseState.middleButton = (data.buttons & 4) !== 0;
|
|
448
499
|
this.mouseState.isPressed = data.buttons > 0;
|
|
449
|
-
this.mouseState.deltaX
|
|
450
|
-
this.mouseState.deltaY
|
|
500
|
+
this.mouseState.deltaX += data.deltaX || 0;
|
|
501
|
+
this.mouseState.deltaY += data.deltaY || 0;
|
|
451
502
|
this.mouseState.wheelDelta += data.wheelDeltaY || 0;
|
|
452
503
|
this.mouseState.wheelX += data.wheelDeltaX || 0;
|
|
453
504
|
this.mouseState.wheelY += data.wheelDeltaY || 0;
|
|
454
|
-
|
|
455
|
-
this.
|
|
456
|
-
this.mouseState.wasPressed = this.mouseState.wasPressed ||
|
|
457
|
-
this.mouseState.wasReleased = this.mouseState.wasReleased ||
|
|
505
|
+
const justPressed = data.buttons & ~this.previousButtons;
|
|
506
|
+
const justReleased = this.previousButtons & ~data.buttons;
|
|
507
|
+
this.mouseState.wasPressed = this.mouseState.wasPressed || justPressed !== 0;
|
|
508
|
+
this.mouseState.wasReleased = this.mouseState.wasReleased || justReleased !== 0;
|
|
458
509
|
this.mouseState.wasMoved = this.mouseState.wasMoved || (data.deltaX !== 0 || data.deltaY !== 0);
|
|
510
|
+
this.previousButtons = data.buttons;
|
|
459
511
|
}
|
|
460
512
|
/**
|
|
461
513
|
* Handle keyboard update messages from the host
|
|
@@ -468,11 +520,21 @@ class InteractionSystem {
|
|
|
468
520
|
this.keyboardState.activeKeys.add(key);
|
|
469
521
|
this.keyboardState.pressedThisFrame.add(key);
|
|
470
522
|
this.keyboardState.lastKeyPressed = data.key;
|
|
523
|
+
const kc = data.keyCode;
|
|
524
|
+
if (kc !== void 0 && kc >= 0 && kc <= 255) {
|
|
525
|
+
this.keyboardTextureData[kc] = 255;
|
|
526
|
+
this.keyboardTextureData[256 + kc] = 255;
|
|
527
|
+
this.keyboardTextureData[512 + kc] ^= 255;
|
|
528
|
+
}
|
|
471
529
|
}
|
|
472
530
|
} else if (data.type === "keyup") {
|
|
473
531
|
this.keyboardState.activeKeys.delete(key);
|
|
474
532
|
this.keyboardState.releasedThisFrame.add(key);
|
|
475
533
|
this.keyboardState.lastKeyReleased = data.key;
|
|
534
|
+
const kc = data.keyCode;
|
|
535
|
+
if (kc !== void 0 && kc >= 0 && kc <= 255) {
|
|
536
|
+
this.keyboardTextureData[kc] = 0;
|
|
537
|
+
}
|
|
476
538
|
}
|
|
477
539
|
this.keyboardState.shift = data.shiftKey;
|
|
478
540
|
this.keyboardState.ctrl = data.ctrlKey;
|
|
@@ -484,52 +546,92 @@ class InteractionSystem {
|
|
|
484
546
|
*/
|
|
485
547
|
handleTouchUpdate(data) {
|
|
486
548
|
if (!this.isEnabled) return;
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
549
|
+
const touches = data.touches.map((touch) => {
|
|
550
|
+
const id = touch.identifier;
|
|
551
|
+
const ended = !!touch.ended;
|
|
552
|
+
let deltaX = 0;
|
|
553
|
+
let deltaY = 0;
|
|
554
|
+
let vx = 0;
|
|
555
|
+
let vy = 0;
|
|
556
|
+
const prev = this.previousTouchPositions.get(id);
|
|
557
|
+
if (prev) {
|
|
558
|
+
deltaX = touch.clientX - prev.x;
|
|
559
|
+
deltaY = touch.clientY - prev.y;
|
|
560
|
+
const dt = (data.timestamp - prev.timestamp) / 1e3;
|
|
561
|
+
if (dt > 0) {
|
|
562
|
+
vx = deltaX / dt;
|
|
563
|
+
vy = deltaY / dt;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (ended) {
|
|
567
|
+
this.previousTouchPositions.delete(id);
|
|
568
|
+
} else {
|
|
569
|
+
this.previousTouchPositions.set(id, {
|
|
570
|
+
x: touch.clientX,
|
|
571
|
+
y: touch.clientY,
|
|
572
|
+
timestamp: data.timestamp
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
id,
|
|
577
|
+
x: touch.clientX,
|
|
578
|
+
y: touch.clientY,
|
|
579
|
+
pressure: touch.force,
|
|
580
|
+
radius: Math.max(touch.radiusX, touch.radiusY),
|
|
581
|
+
radiusX: touch.radiusX,
|
|
582
|
+
radiusY: touch.radiusY,
|
|
583
|
+
rotationAngle: touch.rotationAngle,
|
|
584
|
+
force: touch.force,
|
|
585
|
+
isInCanvas: touch.isInCanvas,
|
|
586
|
+
deltaX,
|
|
587
|
+
deltaY,
|
|
588
|
+
velocity: { x: vx, y: vy },
|
|
589
|
+
isNew: data.type === "touchstart" && !prev,
|
|
590
|
+
isActive: !ended,
|
|
591
|
+
isEnding: ended
|
|
592
|
+
};
|
|
593
|
+
});
|
|
594
|
+
const activePoints = touches.filter((t) => t.isActive);
|
|
595
|
+
const endedPoints = touches.filter((t) => t.isEnding);
|
|
596
|
+
this.touchState.points = activePoints;
|
|
597
|
+
this.touchState.count = activePoints.length;
|
|
598
|
+
this.touchState.primary = activePoints[0] || null;
|
|
512
599
|
if (data.type === "touchstart") {
|
|
513
|
-
this.touchState.started
|
|
600
|
+
this.touchState.started.push(...touches.filter((t) => t.isNew));
|
|
514
601
|
} else if (data.type === "touchmove") {
|
|
515
|
-
this.touchState.moved =
|
|
602
|
+
this.touchState.moved = activePoints;
|
|
516
603
|
} else if (data.type === "touchend" || data.type === "touchcancel") {
|
|
517
|
-
this.touchState.ended
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
604
|
+
this.touchState.ended.push(...endedPoints);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Compute unified pointer state from current mouse and touch state.
|
|
609
|
+
* Call after all interaction events are processed, before render.
|
|
610
|
+
*/
|
|
611
|
+
updatePointerState() {
|
|
612
|
+
const touch = this.touchState;
|
|
613
|
+
const mouse = this.mouseState;
|
|
614
|
+
if (touch.count > 0 && touch.primary) {
|
|
615
|
+
const p = touch.primary;
|
|
616
|
+
this.pointerState.x = p.x;
|
|
617
|
+
this.pointerState.y = p.y;
|
|
618
|
+
this.pointerState.deltaX = p.deltaX;
|
|
619
|
+
this.pointerState.deltaY = p.deltaY;
|
|
620
|
+
this.pointerState.isDown = true;
|
|
621
|
+
this.pointerState.isInCanvas = p.isInCanvas;
|
|
622
|
+
this.pointerState.type = "touch";
|
|
623
|
+
} else {
|
|
624
|
+
this.pointerState.x = mouse.x;
|
|
625
|
+
this.pointerState.y = mouse.y;
|
|
626
|
+
this.pointerState.deltaX = mouse.deltaX;
|
|
627
|
+
this.pointerState.deltaY = mouse.deltaY;
|
|
628
|
+
this.pointerState.isDown = mouse.leftButton;
|
|
629
|
+
this.pointerState.isInCanvas = mouse.isInCanvas;
|
|
630
|
+
this.pointerState.type = mouse.isInCanvas ? "mouse" : "none";
|
|
631
|
+
}
|
|
632
|
+
this.pointerState.wasPressed = this.pointerState.isDown && !this.previousPointerDown;
|
|
633
|
+
this.pointerState.wasReleased = !this.pointerState.isDown && this.previousPointerDown;
|
|
634
|
+
this.previousPointerDown = this.pointerState.isDown;
|
|
533
635
|
}
|
|
534
636
|
/**
|
|
535
637
|
* Reset all interaction state (called when loading new scene)
|
|
@@ -543,7 +645,6 @@ class InteractionSystem {
|
|
|
543
645
|
leftButton: false,
|
|
544
646
|
rightButton: false,
|
|
545
647
|
middleButton: false,
|
|
546
|
-
velocity: { x: 0, y: 0 },
|
|
547
648
|
deltaX: 0,
|
|
548
649
|
deltaY: 0,
|
|
549
650
|
wheelDelta: 0,
|
|
@@ -562,26 +663,26 @@ class InteractionSystem {
|
|
|
562
663
|
this.keyboardState.ctrl = false;
|
|
563
664
|
this.keyboardState.alt = false;
|
|
564
665
|
this.keyboardState.meta = false;
|
|
666
|
+
this.keyboardTextureData.fill(0);
|
|
565
667
|
this.touchState.points = [];
|
|
566
668
|
this.touchState.count = 0;
|
|
567
669
|
this.touchState.started = [];
|
|
568
670
|
this.touchState.moved = [];
|
|
569
671
|
this.touchState.ended = [];
|
|
570
672
|
this.touchState.primary = null;
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
lastTapTime: 0,
|
|
583
|
-
tapPosition: null
|
|
673
|
+
this.previousTouchPositions.clear();
|
|
674
|
+
Object.assign(this.pointerState, {
|
|
675
|
+
x: 0,
|
|
676
|
+
y: 0,
|
|
677
|
+
deltaX: 0,
|
|
678
|
+
deltaY: 0,
|
|
679
|
+
isDown: false,
|
|
680
|
+
wasPressed: false,
|
|
681
|
+
wasReleased: false,
|
|
682
|
+
isInCanvas: false,
|
|
683
|
+
type: "none"
|
|
584
684
|
});
|
|
685
|
+
this.previousPointerDown = false;
|
|
585
686
|
}
|
|
586
687
|
/**
|
|
587
688
|
* Enable or disable interaction processing
|
|
@@ -609,8 +710,6 @@ class InteractionSystem {
|
|
|
609
710
|
this.mouseState.leftButton = false;
|
|
610
711
|
this.mouseState.rightButton = false;
|
|
611
712
|
this.mouseState.middleButton = false;
|
|
612
|
-
this.mouseState.velocity.x = 0;
|
|
613
|
-
this.mouseState.velocity.y = 0;
|
|
614
713
|
this.mouseState.deltaX = 0;
|
|
615
714
|
this.mouseState.deltaY = 0;
|
|
616
715
|
this.mouseState.wheelDelta = 0;
|
|
@@ -628,24 +727,24 @@ class InteractionSystem {
|
|
|
628
727
|
this.keyboardState.ctrl = false;
|
|
629
728
|
this.keyboardState.alt = false;
|
|
630
729
|
this.keyboardState.meta = false;
|
|
730
|
+
this.keyboardTextureData.fill(0);
|
|
631
731
|
this.touchState.points = [];
|
|
632
732
|
this.touchState.count = 0;
|
|
633
733
|
this.touchState.started = [];
|
|
634
734
|
this.touchState.moved = [];
|
|
635
735
|
this.touchState.ended = [];
|
|
636
736
|
this.touchState.primary = null;
|
|
637
|
-
this.
|
|
638
|
-
this.
|
|
639
|
-
this.
|
|
640
|
-
this.
|
|
641
|
-
this.
|
|
642
|
-
this.
|
|
643
|
-
this.
|
|
644
|
-
this.
|
|
645
|
-
this.
|
|
646
|
-
this.
|
|
647
|
-
this.
|
|
648
|
-
this.touchState.gestures.tapPosition = null;
|
|
737
|
+
this.previousTouchPositions.clear();
|
|
738
|
+
this.pointerState.x = 0;
|
|
739
|
+
this.pointerState.y = 0;
|
|
740
|
+
this.pointerState.deltaX = 0;
|
|
741
|
+
this.pointerState.deltaY = 0;
|
|
742
|
+
this.pointerState.isDown = false;
|
|
743
|
+
this.pointerState.wasPressed = false;
|
|
744
|
+
this.pointerState.wasReleased = false;
|
|
745
|
+
this.pointerState.isInCanvas = false;
|
|
746
|
+
this.pointerState.type = "none";
|
|
747
|
+
this.previousPointerDown = false;
|
|
649
748
|
}
|
|
650
749
|
}
|
|
651
750
|
class CVSystem {
|
|
@@ -1982,7 +2081,7 @@ class P5WorkerAdapter {
|
|
|
1982
2081
|
addP5PropertyToImageParameters(parameterObjects) {
|
|
1983
2082
|
if (!this.p5Instance) return;
|
|
1984
2083
|
const isImageLike = (value) => {
|
|
1985
|
-
return value instanceof ImageBitmap || value
|
|
2084
|
+
return value instanceof ImageBitmap || value && typeof value === "object" && "width" in value && "height" in value;
|
|
1986
2085
|
};
|
|
1987
2086
|
for (const [name, param] of parameterObjects) {
|
|
1988
2087
|
try {
|
|
@@ -2191,6 +2290,11 @@ class ShaderParameterParser {
|
|
|
2191
2290
|
break;
|
|
2192
2291
|
case "image":
|
|
2193
2292
|
break;
|
|
2293
|
+
case "button":
|
|
2294
|
+
if (param.config.default !== void 0) {
|
|
2295
|
+
console.warn(`Parameter ${param.uniformName} of type 'button' ignores 'default' — buttons always start as false`);
|
|
2296
|
+
}
|
|
2297
|
+
break;
|
|
2194
2298
|
case "accumulator":
|
|
2195
2299
|
if (param.config.rate === void 0) {
|
|
2196
2300
|
throw new Error(`Accumulator '${param.uniformName}' requires a 'rate' config key (parameter name or numeric constant)`);
|
|
@@ -2198,9 +2302,6 @@ class ShaderParameterParser {
|
|
|
2198
2302
|
if (typeof param.config.rate !== "string" && typeof param.config.rate !== "number") {
|
|
2199
2303
|
throw new Error(`Accumulator '${param.uniformName}' rate must be a parameter name or numeric constant`);
|
|
2200
2304
|
}
|
|
2201
|
-
if (typeof param.config.rate === "string" && param.config.rate.startsWith("u_")) {
|
|
2202
|
-
throw new Error(`Accumulator '${param.uniformName}' cannot reference built-in uniform '${param.config.rate}' — use a parameter name`);
|
|
2203
|
-
}
|
|
2204
2305
|
break;
|
|
2205
2306
|
default:
|
|
2206
2307
|
console.warn(`Unknown parameter type: ${param.type}`);
|
|
@@ -2225,6 +2326,8 @@ class ShaderParameterParser {
|
|
|
2225
2326
|
return `uniform int ${param.uniformName};`;
|
|
2226
2327
|
case "image":
|
|
2227
2328
|
return `uniform sampler2D ${param.uniformName};`;
|
|
2329
|
+
case "button":
|
|
2330
|
+
return `uniform bool ${param.uniformName};`;
|
|
2228
2331
|
case "accumulator":
|
|
2229
2332
|
return `uniform float ${param.uniformName}; // accumulator (rate: ${param.config.rate})`;
|
|
2230
2333
|
default:
|
|
@@ -2244,11 +2347,15 @@ class ShaderParameterParser {
|
|
|
2244
2347
|
if (param.type !== "accumulator") continue;
|
|
2245
2348
|
const rate = param.config.rate;
|
|
2246
2349
|
if (typeof rate === "string" && !paramNames.has(rate)) {
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2350
|
+
if (rate.startsWith("u_")) {
|
|
2351
|
+
param.config._rateIsBuiltIn = true;
|
|
2352
|
+
} else {
|
|
2353
|
+
const available = [...paramNames].join(", ");
|
|
2354
|
+
console.warn(
|
|
2355
|
+
`Accumulator '${param.uniformName}' references parameter '${rate}' which does not exist.` + (available ? ` Available parameters: ${available}` : " No parameters declared.")
|
|
2356
|
+
);
|
|
2357
|
+
param.config._rateInvalid = true;
|
|
2358
|
+
}
|
|
2252
2359
|
}
|
|
2253
2360
|
}
|
|
2254
2361
|
}
|
|
@@ -2295,6 +2402,7 @@ class ShaderWorkerAdapter {
|
|
|
2295
2402
|
audioWaveformTexture = null;
|
|
2296
2403
|
videoTexture = null;
|
|
2297
2404
|
segmentationTexture = null;
|
|
2405
|
+
keyboardTexture = null;
|
|
2298
2406
|
// Multi-stream textures
|
|
2299
2407
|
streamTextures = [];
|
|
2300
2408
|
// Device video textures
|
|
@@ -2302,6 +2410,8 @@ class ShaderWorkerAdapter {
|
|
|
2302
2410
|
// Accumulator state (CPU-side phase accumulators for smooth parameter-driven animation)
|
|
2303
2411
|
accumulatorValues = /* @__PURE__ */ new Map();
|
|
2304
2412
|
accumulatorWarned = /* @__PURE__ */ new Set();
|
|
2413
|
+
// Cache of scalar uniform values for accumulator built-in rate lookups
|
|
2414
|
+
scalarUniformCache = /* @__PURE__ */ new Map();
|
|
2305
2415
|
// Backbuffer support (ping-pong framebuffers)
|
|
2306
2416
|
backbufferFramebuffer = null;
|
|
2307
2417
|
backbufferTexture = null;
|
|
@@ -2478,7 +2588,6 @@ uniform vec2 u_resolution; // Canvas width and height in pixels
|
|
|
2478
2588
|
uniform float u_time; // Elapsed time in seconds since scene start
|
|
2479
2589
|
uniform float u_deltaTime; // Time elapsed since last frame in seconds
|
|
2480
2590
|
uniform int u_frame; // Current frame number
|
|
2481
|
-
uniform float u_pixelRatio; // Device pixel ratio for high-DPI displays
|
|
2482
2591
|
uniform float u_fps; // Current frames per second
|
|
2483
2592
|
|
|
2484
2593
|
// Mouse API
|
|
@@ -2488,7 +2597,10 @@ uniform bool u_mousePressed; // True if any mouse button is pressed
|
|
|
2488
2597
|
uniform bool u_mouseLeft; // True if left mouse button is pressed
|
|
2489
2598
|
uniform bool u_mouseRight; // True if right mouse button is pressed
|
|
2490
2599
|
uniform bool u_mouseMiddle; // True if middle mouse button is pressed
|
|
2491
|
-
uniform vec2
|
|
2600
|
+
uniform vec2 u_mouseDelta; // Mouse movement delta in pixels (per frame)
|
|
2601
|
+
uniform float u_mouseWheel; // Mouse wheel scroll delta (per frame)
|
|
2602
|
+
uniform bool u_mouseWasPressed; // True on the frame a mouse button was pressed
|
|
2603
|
+
uniform bool u_mouseWasReleased; // True on the frame a mouse button was released
|
|
2492
2604
|
|
|
2493
2605
|
// Keyboard API - Common keys
|
|
2494
2606
|
uniform bool u_keySpace; // True if spacebar is pressed
|
|
@@ -2504,6 +2616,10 @@ uniform bool u_keyDown; // True if Down arrow key is pressed
|
|
|
2504
2616
|
uniform bool u_keyLeft; // True if Left arrow key is pressed
|
|
2505
2617
|
uniform bool u_keyRight; // True if Right arrow key is pressed
|
|
2506
2618
|
|
|
2619
|
+
// Keyboard Texture (Shadertoy-compatible 256x3)
|
|
2620
|
+
// Row 0: held state, Row 1: pressed this frame, Row 2: toggle
|
|
2621
|
+
uniform sampler2D u_keyboard; // Keyboard state texture (256x3, LUMINANCE)
|
|
2622
|
+
|
|
2507
2623
|
// Touch API
|
|
2508
2624
|
uniform int u_touchCount; // Number of active touch points (0-5)
|
|
2509
2625
|
uniform vec2 u_touch0; // First touch point position in pixels
|
|
@@ -2512,6 +2628,14 @@ uniform vec2 u_touch2; // Third touch point position in pixels
|
|
|
2512
2628
|
uniform vec2 u_touch3; // Fourth touch point position in pixels
|
|
2513
2629
|
uniform vec2 u_touch4; // Fifth touch point position in pixels
|
|
2514
2630
|
|
|
2631
|
+
// Pointer API (unified mouse/touch)
|
|
2632
|
+
uniform vec2 u_pointer; // Primary input position in pixels (WebGL coords)
|
|
2633
|
+
uniform vec2 u_pointerDelta; // Primary input movement delta (per frame)
|
|
2634
|
+
uniform bool u_pointerDown; // True if primary input is active (left-click or touch)
|
|
2635
|
+
uniform bool u_pointerWasPressed; // True on the frame primary input became active
|
|
2636
|
+
uniform bool u_pointerWasReleased; // True on the frame primary input was released
|
|
2637
|
+
uniform bool u_pointerInCanvas; // True if primary input is inside canvas bounds
|
|
2638
|
+
|
|
2515
2639
|
// Audio - Volume
|
|
2516
2640
|
uniform float u_audioVolume; // RMS volume level (0-1)
|
|
2517
2641
|
uniform float u_audioPeak; // Peak amplitude (0-1)
|
|
@@ -2845,6 +2969,7 @@ ${error}`);
|
|
|
2845
2969
|
this.textureUnits.set("u_audioWaveform", this.nextTextureUnit++);
|
|
2846
2970
|
this.textureUnits.set("u_video", this.nextTextureUnit++);
|
|
2847
2971
|
this.textureUnits.set("u_segmentationMask", this.nextTextureUnit++);
|
|
2972
|
+
this.textureUnits.set("u_keyboard", this.nextTextureUnit++);
|
|
2848
2973
|
for (let i = 0; i < ShaderWorkerAdapter.MAX_STREAMS; i++) {
|
|
2849
2974
|
this.textureUnits.set(`u_stream${i}`, this.nextTextureUnit++);
|
|
2850
2975
|
}
|
|
@@ -2929,7 +3054,6 @@ ${error}`);
|
|
|
2929
3054
|
this.setUniform("u_time", "float", viji.time);
|
|
2930
3055
|
this.setUniform("u_deltaTime", "float", viji.deltaTime);
|
|
2931
3056
|
this.setUniform("u_frame", "int", viji.frameCount);
|
|
2932
|
-
this.setUniform("u_pixelRatio", "float", viji.pixelRatio);
|
|
2933
3057
|
this.setUniform("u_fps", "float", viji.fps);
|
|
2934
3058
|
this.setUniform("u_mouse", "vec2", [viji.mouse.x, viji.height - viji.mouse.y]);
|
|
2935
3059
|
this.setUniform("u_mouseInCanvas", "bool", viji.mouse.isInCanvas);
|
|
@@ -2937,7 +3061,10 @@ ${error}`);
|
|
|
2937
3061
|
this.setUniform("u_mouseLeft", "bool", viji.mouse.leftButton);
|
|
2938
3062
|
this.setUniform("u_mouseRight", "bool", viji.mouse.rightButton);
|
|
2939
3063
|
this.setUniform("u_mouseMiddle", "bool", viji.mouse.middleButton);
|
|
2940
|
-
this.setUniform("
|
|
3064
|
+
this.setUniform("u_mouseDelta", "vec2", [viji.mouse.deltaX, -viji.mouse.deltaY]);
|
|
3065
|
+
this.setUniform("u_mouseWheel", "float", viji.mouse.wheelDelta);
|
|
3066
|
+
this.setUniform("u_mouseWasPressed", "bool", viji.mouse.wasPressed);
|
|
3067
|
+
this.setUniform("u_mouseWasReleased", "bool", viji.mouse.wasReleased);
|
|
2941
3068
|
this.setUniform("u_keySpace", "bool", viji.keyboard.isPressed(" ") || viji.keyboard.isPressed("space"));
|
|
2942
3069
|
this.setUniform("u_keyShift", "bool", viji.keyboard.shift);
|
|
2943
3070
|
this.setUniform("u_keyCtrl", "bool", viji.keyboard.ctrl);
|
|
@@ -2950,6 +3077,9 @@ ${error}`);
|
|
|
2950
3077
|
this.setUniform("u_keyDown", "bool", viji.keyboard.isPressed("ArrowDown"));
|
|
2951
3078
|
this.setUniform("u_keyLeft", "bool", viji.keyboard.isPressed("ArrowLeft"));
|
|
2952
3079
|
this.setUniform("u_keyRight", "bool", viji.keyboard.isPressed("ArrowRight"));
|
|
3080
|
+
if (viji.keyboard.textureData) {
|
|
3081
|
+
this.updateKeyboardTexture(viji.keyboard.textureData);
|
|
3082
|
+
}
|
|
2953
3083
|
this.setUniform("u_touchCount", "int", viji.touches.count);
|
|
2954
3084
|
for (let i = 0; i < 5; i++) {
|
|
2955
3085
|
const touch = viji.touches.points[i];
|
|
@@ -2959,6 +3089,12 @@ ${error}`);
|
|
|
2959
3089
|
this.setUniform(`u_touch${i}`, "vec2", [0, 0]);
|
|
2960
3090
|
}
|
|
2961
3091
|
}
|
|
3092
|
+
this.setUniform("u_pointer", "vec2", [viji.pointer.x, viji.height - viji.pointer.y]);
|
|
3093
|
+
this.setUniform("u_pointerDelta", "vec2", [viji.pointer.deltaX, -viji.pointer.deltaY]);
|
|
3094
|
+
this.setUniform("u_pointerDown", "bool", viji.pointer.isDown);
|
|
3095
|
+
this.setUniform("u_pointerWasPressed", "bool", viji.pointer.wasPressed);
|
|
3096
|
+
this.setUniform("u_pointerWasReleased", "bool", viji.pointer.wasReleased);
|
|
3097
|
+
this.setUniform("u_pointerInCanvas", "bool", viji.pointer.isInCanvas);
|
|
2962
3098
|
const audio = viji.audio;
|
|
2963
3099
|
this.setUniform("u_audioVolume", "float", audio.volume?.current || 0);
|
|
2964
3100
|
this.setUniform("u_audioPeak", "float", audio.volume?.peak || 0);
|
|
@@ -3294,6 +3430,8 @@ ${error}`);
|
|
|
3294
3430
|
const sourceParam = parameterObjects.get(rateConfig);
|
|
3295
3431
|
if (sourceParam !== void 0) {
|
|
3296
3432
|
rate = sourceParam.value ?? 0;
|
|
3433
|
+
} else if (this.scalarUniformCache.has(rateConfig)) {
|
|
3434
|
+
rate = this.scalarUniformCache.get(rateConfig);
|
|
3297
3435
|
} else {
|
|
3298
3436
|
if (!this.accumulatorWarned.has(param.uniformName)) {
|
|
3299
3437
|
console.warn(`Accumulator '${param.uniformName}': rate source '${rateConfig}' not found, using 0`);
|
|
@@ -3343,6 +3481,9 @@ ${error}`);
|
|
|
3343
3481
|
this.updateImageTexture(param.uniformName, value);
|
|
3344
3482
|
}
|
|
3345
3483
|
break;
|
|
3484
|
+
case "button":
|
|
3485
|
+
this.setUniform(param.uniformName, "bool", value);
|
|
3486
|
+
break;
|
|
3346
3487
|
}
|
|
3347
3488
|
}
|
|
3348
3489
|
}
|
|
@@ -3350,13 +3491,34 @@ ${error}`);
|
|
|
3350
3491
|
* Set uniform value
|
|
3351
3492
|
*/
|
|
3352
3493
|
setUniform(name, type, value) {
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3494
|
+
switch (type) {
|
|
3495
|
+
case "float":
|
|
3496
|
+
this.scalarUniformCache.set(name, value);
|
|
3497
|
+
break;
|
|
3498
|
+
case "int":
|
|
3499
|
+
this.scalarUniformCache.set(name, value);
|
|
3500
|
+
break;
|
|
3501
|
+
case "bool":
|
|
3502
|
+
this.scalarUniformCache.set(name, value ? 1 : 0);
|
|
3503
|
+
break;
|
|
3504
|
+
case "vec2":
|
|
3505
|
+
this.scalarUniformCache.set(name + ".x", value[0]);
|
|
3506
|
+
this.scalarUniformCache.set(name + ".y", value[1]);
|
|
3507
|
+
break;
|
|
3508
|
+
case "vec3":
|
|
3509
|
+
this.scalarUniformCache.set(name + ".x", value[0]);
|
|
3510
|
+
this.scalarUniformCache.set(name + ".y", value[1]);
|
|
3511
|
+
this.scalarUniformCache.set(name + ".z", value[2]);
|
|
3512
|
+
break;
|
|
3513
|
+
case "vec4":
|
|
3514
|
+
this.scalarUniformCache.set(name + ".x", value[0]);
|
|
3515
|
+
this.scalarUniformCache.set(name + ".y", value[1]);
|
|
3516
|
+
this.scalarUniformCache.set(name + ".z", value[2]);
|
|
3517
|
+
this.scalarUniformCache.set(name + ".w", value[3]);
|
|
3518
|
+
break;
|
|
3359
3519
|
}
|
|
3520
|
+
const location = this.uniformLocations.get(name);
|
|
3521
|
+
if (location === null || location === void 0) return;
|
|
3360
3522
|
const gl = this.gl;
|
|
3361
3523
|
switch (type) {
|
|
3362
3524
|
case "float":
|
|
@@ -3605,6 +3767,37 @@ ${error}`);
|
|
|
3605
3767
|
gl.uniform1i(location, unit);
|
|
3606
3768
|
}
|
|
3607
3769
|
}
|
|
3770
|
+
/**
|
|
3771
|
+
* Update keyboard state texture (Shadertoy-compatible 256x3)
|
|
3772
|
+
*/
|
|
3773
|
+
updateKeyboardTexture(data) {
|
|
3774
|
+
const gl = this.gl;
|
|
3775
|
+
const unit = this.textureUnits.get("u_keyboard");
|
|
3776
|
+
if (!this.keyboardTexture) {
|
|
3777
|
+
this.keyboardTexture = gl.createTexture();
|
|
3778
|
+
}
|
|
3779
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
3780
|
+
gl.bindTexture(gl.TEXTURE_2D, this.keyboardTexture);
|
|
3781
|
+
gl.texImage2D(
|
|
3782
|
+
gl.TEXTURE_2D,
|
|
3783
|
+
0,
|
|
3784
|
+
gl.LUMINANCE,
|
|
3785
|
+
256,
|
|
3786
|
+
3,
|
|
3787
|
+
0,
|
|
3788
|
+
gl.LUMINANCE,
|
|
3789
|
+
gl.UNSIGNED_BYTE,
|
|
3790
|
+
data
|
|
3791
|
+
);
|
|
3792
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
3793
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
3794
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
3795
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
3796
|
+
const location = this.uniformLocations.get("u_keyboard");
|
|
3797
|
+
if (location) {
|
|
3798
|
+
gl.uniform1i(location, unit);
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3608
3801
|
/**
|
|
3609
3802
|
* Update image parameter texture
|
|
3610
3803
|
*/
|
|
@@ -3806,7 +3999,6 @@ class VijiWorkerRuntime {
|
|
|
3806
3999
|
gl: null,
|
|
3807
4000
|
width: 0,
|
|
3808
4001
|
height: 0,
|
|
3809
|
-
pixelRatio: 1,
|
|
3810
4002
|
// Timing
|
|
3811
4003
|
time: 0,
|
|
3812
4004
|
deltaTime: 0,
|
|
@@ -3895,6 +4087,9 @@ class VijiWorkerRuntime {
|
|
|
3895
4087
|
image: (defaultValue, config) => {
|
|
3896
4088
|
return this.parameterSystem.createImageParameter(defaultValue, config);
|
|
3897
4089
|
},
|
|
4090
|
+
button: (config) => {
|
|
4091
|
+
return this.parameterSystem.createButtonParameter(config);
|
|
4092
|
+
},
|
|
3898
4093
|
// Context selection
|
|
3899
4094
|
useContext: (type) => {
|
|
3900
4095
|
if (type === "2d") {
|
|
@@ -4020,6 +4215,9 @@ class VijiWorkerRuntime {
|
|
|
4020
4215
|
case "image":
|
|
4021
4216
|
this.viji.image(null, paramConfig);
|
|
4022
4217
|
break;
|
|
4218
|
+
case "button":
|
|
4219
|
+
this.viji.button(paramConfig);
|
|
4220
|
+
break;
|
|
4023
4221
|
}
|
|
4024
4222
|
}
|
|
4025
4223
|
// Reset parameter state (called when loading new scene)
|
|
@@ -4572,6 +4770,7 @@ class VijiWorkerRuntime {
|
|
|
4572
4770
|
this.viji.frameCount = ++this.frameCount;
|
|
4573
4771
|
this.trackEffectiveFrameTime(currentTime);
|
|
4574
4772
|
this.lastTime = currentTime;
|
|
4773
|
+
this.interactionSystem.updatePointerState();
|
|
4575
4774
|
try {
|
|
4576
4775
|
if (this.shaderAdapter && this.rendererType === "shader") {
|
|
4577
4776
|
const parameterObjects = this.parameterSystem.getAllParameterObjects();
|
|
@@ -4593,6 +4792,7 @@ class VijiWorkerRuntime {
|
|
|
4593
4792
|
stack: error.stack
|
|
4594
4793
|
});
|
|
4595
4794
|
}
|
|
4795
|
+
this.parameterSystem.resetButtonParameters();
|
|
4596
4796
|
if (this.pendingCaptures.length > 0) {
|
|
4597
4797
|
const captures = [...this.pendingCaptures];
|
|
4598
4798
|
this.pendingCaptures = [];
|
|
@@ -26049,4 +26249,4 @@ async function setSceneCode(sceneCode) {
|
|
|
26049
26249
|
}
|
|
26050
26250
|
}
|
|
26051
26251
|
self.setSceneCode = setSceneCode;
|
|
26052
|
-
//# sourceMappingURL=viji.worker-
|
|
26252
|
+
//# sourceMappingURL=viji.worker-Zg128woJ.js.map
|