makepad/apps/vj/resources/effects/86_fractal_descent.splash
Arena Agent d8d5ea8b6a Repair integration manifests and port nigig exports onto upstream widget families
Restore missing required asset/flow packages from the pinned nigig revision; retain upstream script isolation, X11 hover, and widget-core fixes. Validated makepad-widgets data feature with Rust 1.97.1.
2026-10-26 11:23:26 +00:00

99 lines
4 KiB
Text

// FRACTAL DESCENT — a kaleidoscopic-IFS fractal orbited in the dark, its
// fold angle WANDERING with the beat count so the shape never repeats.
// Pattern taught: an iterated fold (abs + rotate + scale-out) inside a
// scene_sdf subclass — five iterations, own constants; the marcher, AO and
// palette all come free from the base shader. `p0` binds extra fold angle.
//
// THE LAW THIS DOCUMENT LEARNED THE HARD WAY: a shader that is a function
// of `beat` must be a BOUNDED one. `beat` is the host's SONG clock, not a
// stopwatch that starts when the effect loads — cue this into a deck an
// hour into a set and the first frame is at beat 6000. The original fold
// angle was `0.52 + beat * 0.010`, an unbounded creep: lovely for the ten
// seconds a gallery grab ever sees, and by beat ~120 the IFS attractor has
// escaped the marcher and the deck shows BLACK. So the wander here is two
// slow sines with an irrational-ish frequency ratio (quasi-periodic — it
// still never repeats) clamped into the band where the attractor is a
// picture, and the FOLD dial is centred on 0.5 so no knob can leave it.
{
name: "Fractal Descent"
engine: "raymarch"
steps: 84
max_dist: 30.0
cam: "orbit"
cam_speed: 0.14
cam_dist: 3.6
cam_height: 1.0
cam_fov: 62.0
shadow: 1
beat_pulse: "0.6 * (0.2 + 1.6*p2)"
beat_rate: 1.0
fog: 0.05
glow: "1.45 * (0.25 + 1.5*p1)"
// FOLD is knob-shaped: 0.5 is neutral, the binding breathes around it
// over the bar, and the scene reads it as an OFFSET from centre.
p0: "0.5 + 0.16 * sin(bar * tau)"
// PERFORMANCE DIALS — FOLD is this doc's own p0 lever (the scene hook
// reads self.user.x; touching it holds the fold open); GLOW and PUMP
// route p1/p2. Mid-knob = the stock look, exact.
p1: 0.5 p2: 0.5
dials: [
{name: "FOLD", bind: "p0", default: 0.5},
{name: "GLOW", bind: "p1", default: 0.5},
{name: "PUMP", bind: "p2", default: 0.5}
]
color_bg: #x05040f
color_a: #x8f4dff
color_b: #x0fd0c8
color_c: #xf0eaff
stages: [
{kind: "bloom", threshold: 0.5, strength: 1.35, levels: 3}
]
shader: draw.DrawVjFxRaymarch {
scene_sdf: fn(p: vec3) -> vec2 {
// Kaleidoscopic IFS: fold into the positive octant, then TWO
// rotations (xz and yz — the second is what lifts the fractal
// out of a flat extrusion), scale outward. Own constants.
let mut z = p * 0.72
let k = 1.84
let off = vec3(1.18, 0.62, 1.34)
// The fold angle WANDERS but never escapes: two slow sines of
// the beat count (periods ~153 and ~370 beats, ratio not a
// simple fraction, so the pair never quite comes back around)
// plus the FOLD dial as a signed offset from mid-knob. The
// clamp is the guarantee — outside roughly 0.2..0.9 the
// attractor either collapses or runs off past `max_dist`, and
// the picture goes black.
let b = self.time_beat.y
let wander = 0.14 * sin(b * 0.041) + 0.08 * sin(b * 0.017 + 2.3)
let fold = (self.user.x - 0.5) * 0.24
let ang = clamp(0.54 + wander + fold, 0.20, 0.90)
let mut s = 0.72
let mut trap = 10.0
let mut i = 0.0
loop {
if i > 5.5 {
break
}
z = abs(z)
let r = self.rot2(vec2(z.x, z.z), ang)
z = vec3(r.x, z.y, r.y)
let r2 = self.rot2(vec2(z.y, z.z), 0.61 - ang * 0.35)
z = vec3(z.x, r2.x, r2.y)
z = z * k - off * (k - 1.0)
s = s * k
// Orbit trap: nearest approach to the origin, in world
// scale — the palette rides the fractal's onion layers.
trap = min(trap, length(z) / s)
i = i + 1.0
}
let d = self.sd_box(z, vec3(0.90, 0.90, 0.90)) / s
return vec2(d * 0.82, fract(trap * 2.6))
}
}
}