@lovo/matter 0.6.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -314,24 +314,42 @@ declare function cursorRipple(p: TSLNode, center: TSLNode, opts?: CursorRippleOp
314
314
 
315
315
  declare const elapsedTime: ShaderNodeObject<Node>;
316
316
 
317
+ /**
318
+ * Zero the renderer's animation clock so the next rendered frame is t=0.
319
+ *
320
+ * `elapsedTime` (and three's built-in `time`) accumulate real frame deltas from
321
+ * the moment the renderer starts, which includes a nondeterministic WebGPU
322
+ * init + shader-compile warmup. Resetting the per-renderer `nodeFrame` clock
323
+ * makes every shader start from a fixed phase, so the first visible frame
324
+ * matches the deterministic poster/snapshot frame.
325
+ *
326
+ * Per-renderer by construction: each ShaderScene owns one renderer, so resetting
327
+ * here isolates scenes from one another. No-ops safely if three's internal
328
+ * shape ever changes.
329
+ */
330
+ declare function resetRendererClock(renderer: WebGPURenderer): void;
331
+
317
332
  type TSLScalar = TSLNode | number;
318
- declare function filmGrain(intensity: TSLScalar, timeOffset?: TSLScalar): ShaderNodeObject<Node>;
333
+ declare function grain(intensity: TSLScalar, timeOffset?: TSLScalar): ShaderNodeObject<Node>;
319
334
 
320
335
  /**
321
- * Add sub-LSB dither to break up 8-bit quantization banding (most visible on
322
- * wide-gamut/P3 output, where the same 256 levels span a wider gamut).
323
- *
324
- * `coord` is a per-pixel coordinate (pass `uv()`); `amount` is the noise
325
- * magnitude in the color's units (default ~1/255, roughly one 8-bit step). Uses a
326
- * triangular PDF (difference of two hashes) for flatter, less "gritty" noise than
327
- * uniform white noise.
328
- *
329
- * SPIKE NOTE: applied here in linear-sRGB working space (before the renderer's
330
- * output transfer), so the effective dither is uneven across tones (over-dithers
331
- * shadows, under-dithers highlights). The correct home is a final output pass
332
- * after color-space conversion; this is good enough to evaluate the banding fix.
336
+ * Add a sub-LSB ordered dither to break up 8-bit quantization banding (most
337
+ * visible on smooth gradients and on wide-gamut/P3 output, where the same 256
338
+ * levels span a wider gamut so each step is a coarser perceptual jump).
339
+ *
340
+ * `amount` is the noise magnitude in the color's units (default ~1/255, roughly
341
+ * one 8-bit step). The dither is an ordered Bayer 8x8 pattern keyed on the
342
+ * per-pixel `screenCoordinate`, so the grain is a crisp one device-pixel cell
343
+ * regardless of geometry or zoom, and is deterministic (no temporal shimmer —
344
+ * important for static scenes).
345
+ *
346
+ * For correctness the dither belongs in display-encoded space, immediately
347
+ * before 8-bit quantization. In a Matter-managed scene that placement is handled
348
+ * for you (`ShaderScene` applies it as a final output stage after the color
349
+ * transfer). This primitive is the entry point for Mode 2 (your own r3f canvas),
350
+ * where you apply it to your shader's output yourself.
333
351
  */
334
- declare function dither(color: TSLNode, coord: TSLNode, amount?: number): ShaderNodeObject<Node>;
352
+ declare function dither(color: TSLNode, amount?: number): ShaderNodeObject<Node>;
335
353
 
336
354
  type ReducedMotionPolicy = 'auto' | 'off' | 'slow' | 'paused';
337
355
  /**
@@ -453,4 +471,4 @@ declare class FrameScheduler {
453
471
  private readonly frame;
454
472
  }
455
473
 
456
- export { type ColorRampStop, type ColorSpace, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FractalNoiseOptions, FrameScheduler, type GpuBackend, type GpuRenderer, type HueInterpolation, type IntersectionWatcher, type OutputGamut, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vector2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, dither, elapsedTime, filmGrain, fractalNoise, getReducedMotionPolicy, getReducedMotionTimeScale, mixColor, oklabToLinearSrgb, oklchToLinearSrgb, parseColorString, quantize, setReducedMotionPolicy, signedDistanceFieldCircle, simplexNoise, srgbChannelToLinear, voronoi };
474
+ export { type ColorRampStop, type ColorSpace, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FractalNoiseOptions, FrameScheduler, type GpuBackend, type GpuRenderer, type HueInterpolation, type IntersectionWatcher, type OutputGamut, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vector2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, dither, elapsedTime, fractalNoise, getReducedMotionPolicy, getReducedMotionTimeScale, grain, mixColor, oklabToLinearSrgb, oklchToLinearSrgb, parseColorString, quantize, resetRendererClock, setReducedMotionPolicy, signedDistanceFieldCircle, simplexNoise, srgbChannelToLinear, voronoi };
package/dist/index.js CHANGED
@@ -674,25 +674,55 @@ function cursorRipple(p, center, opts = {}) {
674
674
  return wave.mul(amplitude).mul(decay);
675
675
  }
676
676
 
677
- // src/primitives/film-grain/film-grain.ts
678
- import { hash, mul as mul2, screenCoordinate } from "three/tsl";
679
- function filmGrain(intensity, timeOffset = 0) {
677
+ // src/runtime/clock/reset-clock.ts
678
+ function getNodeFrame(renderer) {
679
+ const candidate = renderer;
680
+ if (!(typeof candidate === "object" && candidate !== null && "_nodes" in candidate)) {
681
+ return void 0;
682
+ }
683
+ const nodes = candidate._nodes;
684
+ if (!(typeof nodes === "object" && nodes !== null && "nodeFrame" in nodes)) {
685
+ return void 0;
686
+ }
687
+ const frame = nodes.nodeFrame;
688
+ if (typeof frame !== "object" || frame === null) return void 0;
689
+ return frame;
690
+ }
691
+ function resetRendererClock(renderer) {
692
+ const nodeFrame = getNodeFrame(renderer);
693
+ if (!nodeFrame) return;
694
+ nodeFrame.time = 0;
695
+ nodeFrame.deltaTime = 0;
696
+ nodeFrame.lastTime = void 0;
697
+ }
698
+
699
+ // src/primitives/grain/grain.ts
700
+ import { float, hash, screenCoordinate } from "three/tsl";
701
+ function grain(intensity, timeOffset = 0) {
680
702
  const pixel = screenCoordinate.xy.floor();
681
- const seed = pixel.x.toUint().mul(1973).add(pixel.y.toUint().mul(9277)).add(mul2(timeOffset, 26699).toUint());
703
+ const column = pixel.x.toUint();
704
+ const row = pixel.y.toUint();
705
+ const frameHash = hash(float(timeOffset)).mul(16777215).toUint();
706
+ const rowHash = hash(row.add(frameHash)).mul(16777215).toUint();
707
+ const seed = column.add(rowHash);
682
708
  return hash(seed).sub(0.5).mul(intensity);
683
709
  }
684
710
 
685
711
  // src/primitives/dither/dither.ts
686
- import { dot, fract as fract3, sin as sin4, vec2 as vec23, vec3 as vec39 } from "three/tsl";
687
- function hash21(coord) {
688
- return fract3(sin4(dot(coord, vec23(12.9898, 78.233))).mul(43758.5453));
712
+ import { floor, fract as fract3, screenCoordinate as screenCoordinate2, vec2 as vec23, vec3 as vec39, vec4 as vec43 } from "three/tsl";
713
+ function bayer2(coord) {
714
+ const cell = floor(coord);
715
+ return fract3(cell.x.mul(0.5).add(cell.y.mul(cell.y).mul(0.75)));
716
+ }
717
+ function bayer4(coord) {
718
+ return bayer2(coord.mul(0.5)).mul(0.25).add(bayer2(coord));
719
+ }
720
+ function bayer8(coord) {
721
+ return bayer4(coord.mul(0.5)).mul(0.25).add(bayer2(coord));
689
722
  }
690
- function dither(color, coord, amount = 1 / 255) {
691
- const pixelCoord = vec23(coord);
692
- const firstHash = hash21(pixelCoord);
693
- const secondHash = hash21(pixelCoord.add(vec23(0.5, 0.5)));
694
- const triangularNoise = firstHash.sub(secondHash).mul(0.5);
695
- return vec39(color).add(triangularNoise.mul(amount));
723
+ function dither(color, amount = 1 / 255) {
724
+ const threshold = bayer8(vec23(screenCoordinate2.xy)).sub(0.5);
725
+ return vec43(vec39(color).add(threshold.mul(amount)), vec43(color).a);
696
726
  }
697
727
 
698
728
  // src/runtime/visibility/visibility.ts
@@ -773,7 +803,7 @@ var FrameScheduler = class {
773
803
  // Reference-counted idle voting. The scheduler is idle only when at least
774
804
  // one component has voted idle AND no component has voted animated. This
775
805
  // prevents a static component (e.g. LinearGradient speed=0) from halting
776
- // the loop while an animated overlay (e.g. FilmGrain) is still running.
806
+ // the loop while an animated overlay (e.g. Grain) is still running.
777
807
  idleVotes = 0;
778
808
  animatedVotes = 0;
779
809
  /** True when all participating components prefer idle and none need animation. */
@@ -907,15 +937,16 @@ export {
907
937
  displace,
908
938
  dither,
909
939
  elapsedTime,
910
- filmGrain,
911
940
  fractalNoise,
912
941
  getReducedMotionPolicy,
913
942
  getReducedMotionTimeScale,
943
+ grain,
914
944
  mixColor,
915
945
  oklabToLinearSrgb,
916
946
  oklchToLinearSrgb,
917
947
  parseColorString,
918
948
  quantize,
949
+ resetRendererClock,
919
950
  setReducedMotionPolicy,
920
951
  signedDistanceFieldCircle,
921
952
  simplexNoise,