makepad/apps/vj/resources/effects/90_ridge_runner.splash
Admin cb49b032df vjfx: 104 presets, engine hooks + hold stage, the videomesh engine, the audio picture, livecodable effects, and mp4 thumbnail sheets
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vjfx: 104 new presets — the transition lane fills out and the screen family goes wide
- vjfx: three lanes land — engine hooks + hold stage, the videomesh engine, and the audio picture
- vj: the thumbnail pipeline becomes one honest machine, and effects go livecodable
- vj: thumbnails become mp4 — hardware-coded sheets at measured-4K cells, and the bake stops racing the GPU
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- vj: the console grows real transports, and the deck stops lying about reverse
- vj: reverse earns a memory, and the effects stop aging
- fab: a 3D creation shell and the viewer built on it
2026-08-26 08:49:48 +02:00

167 lines
7.7 KiB
Text

// RIDGE RUNNER — the mountain-jet flagship: an endless alpenglow range
// streaming under a fighter jet that weaves and BANKS through the valley,
// afterburner pulsing on the beat (and on the bass once audio feeds p0).
// Pattern taught: the mountainjet engine — heightmap streaming + a
// view-space primitive jet in one static vertex stream.
{
name: "Ridge Runner"
engine: "mountainjet"
res: 150
size: 36.0
height: 6.6
noise_scale: 0.15
scroll: 4.2
ridged: 0.72
cells: 44
look: "solid"
jet_size: 1.25
weave: 1.1
beat_pulse: 0.5
beat_rate: 1.0
p0: "0.35 + 1.8 * bass" // afterburner kick — bass once audio arrives
// PERFORMANCE DIALS — the engine's own p-levers: BURNER holds the
// afterburner binding above when touched, WEAVE speeds the flight
// weave, GLOW gains the terrain light.
dials: [
{name: "BURNER", bind: "p0", default: 0.5},
{name: "WEAVE", bind: "p1", default: 0.0},
{name: "GLOW", bind: "p2", default: 0.0}
]
color_bg: #x241028
color_a: #x8a4a5c
color_b: #xffb070
color_c: #xfff0dc
cam_fov: 62.0
cam_height: 4.2
cam_orbit: 0.0
fog: 0.05
glow: 1.05
stages: [
{kind: "bloom", threshold: 0.55, strength: 1.2, levels: 3}
]
// THE LOOK LIVES HERE. This block is not a reference to the
// engine's shader — it IS the pixel math this effect runs.
// Rewrite it and the effect looks different; everything else
// (geometry, motion, the content coupling, the beat) keeps
// working, because the block SUBCLASSES the engine shader.
// Inputs: t = the element look parameter, attr.x = class 0 land 1 jet 2 burner.
// Signals: self.time_beat (time, beat, phase, pulse),
// self.sig (bar, bpm, energy, dt), self.user (p0..p3),
// self.col_a/b/c/bg, self.fog (density, glow, content, -).
shader: draw.DrawVjFxJet {
fx_color: fn(t: float, attr: vec4, content: vec4, cmix: float) -> vec4 {
let fam = attr.x
let look = self.flow.y
let gain = self.fog.y * (1.0 + self.user.z)
let mut rgb = vec3(0.0, 0.0, 0.0)
if fam < 0.5 {
// ---- TERRAIN ----
let h = t
let lit = attr.z
// Same NEGATIVE sign as `height_at`: grid, drape and land
// must travel together, toward the camera (see the scroll
// sign note there — the range used to fly backwards).
// BOUNDED SCROLL: grid and drape are periodic in whole uv
// units (cells is a whole number), so the wrap is the same
// picture taken on a small number — which is what stops the
// lines shimmering once the clock has run for hours.
let scroll = vec2(0.0,
0.0 - modf(self.time_beat.x * self.flow.x * 0.06, 1.0))
let g = fract((self.v_uv + scroll) * self.shape.x)
let dg = min(min(g.x, 1.0 - g.x), min(g.y, 1.0 - g.y))
let line = 1.0 - smoothstep(0.0, 0.11, dg)
let beat_glow = 1.0 + self.time_beat.w * 0.9
// CONTENT — THE DRAPE: the channel video is PAINTED ON THE
// RANGE, sampled by the SAME scrolled grid uv, so the clip
// streams toward the viewer with the land it lies on. It
// REPLACES the slope gradient rather than tinting it (a
// tint on a dark alpenglow ramp reads as a mood, never as
// a picture) and keeps the terrain's own lighting.
// fog.z = the pre-gated `content` strength.
let cm = cmix
let ttx = content
if look < 0.5 {
// solid alpenglow: valleys col_a, peaks col_b, summits
// catch col_c; a whisper of grid keeps the retro DNA.
let grad = self.col_a.xyz.mix(self.col_b.xyz, clamp(h * 1.5 - 0.15, 0.0, 1.0))
.mix(ttx.xyz * 1.35, clamp(cm * 1.25, 0.0, 1.0))
let glow = self.col_c.xyz * pow(clamp(h, 0.0, 1.0), 3.0) * 0.85
rgb = (grad * lit + glow + grad * line * 0.10) * gain
} else {
if look < 1.5 {
// Battlezone wire: lines only over near-black fill.
// Content lights the wireframe AND washes the fill,
// so the picture is continuous between the lines.
let wl = self.col_a.xyz.mix(ttx.xyz * 1.15, clamp(cm * 1.2, 0.0, 1.0))
let fill = self.col_a.xyz * 0.035
+ ttx.xyz * (cm * (0.35 + 0.65 * lit))
rgb = (fill + wl * line * beat_glow
+ self.col_b.xyz * (line * h * 1.3)) * gain
} else {
// night vision: mono luminance ramp + grain. The
// hash is INLINED: a helper fn bound to the vertex
// stage (hash2 feeds height_at) cannot also be
// called from the pixel stage. Content ghosts the
// video's luminance through the mono ramp — the
// clip becomes the thing the scope is looking at.
let vl = dot(ttx.xyz, vec3(0.299, 0.587, 0.114))
let lum = clamp(
mix(0.14 + h * 0.9 * lit, vl * (0.35 + 0.75 * lit), clamp(cm, 0.0, 1.0))
+ line * 0.22,
0.0, 1.0
)
let gv = floor(self.v_uv * 331.0)
+ vec2(fract(modf(self.time_beat.x, 1.0) * 17.0), 0.0)
let grain = fract(sin(dot(gv, vec2(157.31, 113.97))) * 41739.613) * 0.10
rgb = self.col_a.xyz * ((lum + grain) * beat_glow) * gain
}
}
return vec4(rgb, 1.0)
}
if fam < 1.5 {
// ---- JET HULL ----
let edge_d = min(
min(self.v_uv.x, 1.0 - self.v_uv.x),
min(self.v_uv.y, 1.0 - self.v_uv.y)
)
let edge = 1.0 - smoothstep(0.0, 0.09, edge_d)
let shade = t
let part = attr.z
if look < 0.5 {
let c = self.fx_jet_color(shade, part, edge)
rgb = c.xyz * gain
} else {
if look < 1.5 {
// Battlezone: near-black silhouette body (the depth
// test cuts it into the grid) + hot vector edges,
// wider than the solid look so the outline reads
// against the busy terrain.
let we = 1.0 - smoothstep(0.0, 0.17, edge_d)
rgb = (self.col_a.xyz * (we * 2.4)
+ self.col_c.xyz * (we * 0.5)
+ self.col_a.xyz * 0.02) * gain
} else {
rgb = self.col_a.xyz * ((0.10 + shade * 0.55 + edge * 0.35)) * gain
}
}
return vec4(rgb, 1.0)
}
// ---- BURNER ----
let along = self.v_uv.y
let across = abs(self.v_uv.x - 0.5) * 2.0
let core = pow(clamp(1.0 - along, 0.0, 1.0), 1.5)
* clamp(1.0 - across, 0.0, 1.0)
let heat = self.col_c.xyz.mix(self.col_b.xyz, along)
let flick = 0.9 + 0.25 * sin(self.time_beat.x * 43.0 + attr.z * 61.0)
rgb = heat * (core * (0.8 + t * 1.6) * flick) * self.fog.y
return vec4(rgb, 1.0)
}
}
}