// Hero transform tracks on the 20s timeline; bound to Position + Scale in demo.rs. // Rotation is static (set once on the entity's TransformComponent in demo.rs), not a track. ( duration: 20.0, looping: true, cues: [ (t: 14.5, event: "ScatterReset"), ], tracks: [ ( id: "hero_pos", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Smooth), (t: 10.5, v: (0.0, -0.12, 0.0), e: Step), // drop recoil dip (t: 11.2, v: (0.0, 0.0, 0.0), e: Smooth), (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( // The mech mesh is the SETUP/BUILD hero only. At SILENCE (9.5) it hands the shape over // to the scatter cloud, which is at rest in the same silhouette, and stays gone for the // rest of the cut. Returns at the 20.0 loop point. // Hidden during ShotB (t=2.0–3.0) so camera_b shows only the pill. id: "hero_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (2.0, 2.0, 2.0), e: Step), (t: 2.0, v: (0.0, 0.0, 0.0), e: Step), // hide for shot_b (t: 3.0, v: (2.0, 2.0, 2.0), e: Step), // restore for shot_a (t: 3.5, v: (0.0, 0.0, 0.0), e: Step), // hide for shot_b (second cut) (t: 4.5, v: (2.0, 2.0, 2.0), e: Step), // restore for shot_a (t: 7.0, v: (0.0, 0.0, 0.0), e: Step), // hide for shot_b (crew2 cut) (t: 9.4, v: (0.0, 0.0, 0.0), e: Step), // stays hidden through crew2 shot_b's end (t: 9.5, v: (0.0, 0.0, 0.0), e: Step), // Silence: mesh gone, the scatter cloud carries the shape (t: 19.9, v: (0.0, 0.0, 0.0), e: Step), (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( // Static light marker sphere; hidden at Mid alongside probes/scatter for the clean cut to Title. id: "light_a_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.1, 0.1, 0.1), e: Step), (t: 14.0, v: (0.0, 0.0, 0.0), e: Step), (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( // ShotA backdrop disc, on only in the ShotA windows that show the mech (3.0-3.5, // 4.5-7.0). Scale here owns its visibility — it is not in switch_scene's shot lists. id: "shot_a_disc_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), // Setup: hidden (t: 4.5, v: (0.0, 0.0, 0.0), e: Step), // Crew1 = ShotA: cross first (t: 4.75, v: (2.5, 2.5, 2.5), e: Step), // disc takes over (t: 7.0, v: (0.0, 0.0, 0.0), e: Step), // Crew2 — Build framing (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), // Silence onward: the scatter cloud owns the shot ], ), ( // Scatter cookie card in front of the head; same windows as the disc. id: "shot_a_cookie_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), // Setup: hidden (t: 4.5, v: (3.0, 3.0, 4.0), e: Step), // Crew1 = ShotA (t: 4.75, v: (4.0, 4.0, 4.0), e: Step), // Crew1: ambient tension — the card breathes ~2.5% off its rest size on an // irregular rhythm, with two quick Step ticks so it is not a clean sine. (t: 4.95, v: (4.10, 4.10, 4.10), e: Smooth), (t: 5.18, v: (3.90, 3.90, 3.90), e: Smooth), (t: 5.31, v: (4.12, 4.12, 4.12), e: Step), (t: 5.35, v: (3.96, 3.96, 3.96), e: Step), (t: 5.56, v: (4.05, 4.05, 4.05), e: Smooth), (t: 5.83, v: (3.90, 3.90, 3.90), e: Smooth), (t: 6.04, v: (4.08, 4.08, 4.08), e: Smooth), (t: 6.17, v: (3.93, 3.93, 3.93), e: Step), (t: 6.21, v: (4.06, 4.06, 4.06), e: Step), (t: 6.44, v: (3.96, 3.96, 3.96), e: Smooth), (t: 6.70, v: (4.07, 4.07, 4.07), e: Smooth), (t: 6.92, v: (3.98, 3.98, 3.98), e: Smooth), (t: 7.0, v: (0.0, 0.0, 0.0), e: Step), // Crew2 — Build framing (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), // Silence onward: the scatter cloud owns the shot ], ), ( // Backdrop variation: the "+" runs for the first 0.5s of the ShotA cut, then the disc // replaces it. Bars are unit cubes scaled into strips. id: "shot_a_cross_h_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 4.5, v: (5.0, 2.0, 0.05), e: Step), // Crew1 = ShotA: cross in (t: 4.75, v: (0.0, 0.0, 0.0), e: Step), // swap to the disc (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( id: "shot_a_cross_v_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 4.5, v: (2.0, 5.0, 0.05), e: Step), // Crew1 = ShotA: cross in (t: 4.75, v: (0.0, 0.0, 0.0), e: Step), (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( // White card behind the cloud for Silence + Drop; the ShotA disc and cookie are // parked from 7.0, so this owns the backdrop for that window. id: "drop_card_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 9.5, v: (12.0, 3.0, 0.05), e: Step), // Silence (t: 11.5, v: (0.0, 0.0, 0.0), e: Step), (t: 13.5, v: (30.0, 4.0, 0.05), e: Step), // Silence (t: 14.0, v: (0.0, 0.0, 0.0), e: Step), (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( // 6 units behind the origin along the axis to the cue-6 camera at (6.15, -6.74, -2.60); // if that cam_pos moves in cam.seq.ron, re-author this and drop_card_rot with it. id: "drop_card_pos", kind: Vec3, keyframes: [ (t: 0.0, v: (-4.2, 5.2, 1.5), e: Step), (t: 13.5, v: (-4.2, 9.0, 1.5), e: Step), (t: 20.0, v: (-3.747, 4.911, 1.584), e: Step), ], ), ( // Euler Rx*Ry*Rz (pbr_opaque_vertex.hlsl), facing the cue-6 camera. id: "drop_card_rot", kind: Vec3, keyframes: [ (t: 0.0, v: (-1.2, -0.8, 1.1), e: Step), (t: 13.5, v: (-1.2, -1.2, 1.1), e: Step), (t: 20.0, v: (-1.2, -0.6, 0.0), e: Step), ], ), ( id: "pill_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( // Static baseline — matches the pill's built-in transform, no ShotB motion authored yet. // Author keyframes inside the ShotB window (t=2.0-3.0) to animate the near-floor telephoto shot. id: "pill_pos", kind: Vec3, keyframes: [ (t: 2.0, v: (0.0, 0.1, 0.0), e: Step), (t: 2.2, v: (0.0, 0.2, 0.0), e: Smooth), (t: 3.0, v: (0.0, 1.0, 0.0), e: Smooth), ], ), ( id: "pill_rot", kind: Vec3, keyframes: [ (t: 2.0, v: (0.0, 0.6, 1.57), e: Step), (t: 3.0, v: (0.0, 0.6, 2.5), e: Smooth), ], ), ( id: "shot_b_cookie_pos", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 2.0, v: (0.0, 0.005, 0.0), e: Step), (t: 2.2, v: (0.0, 0.01, 0.0), e: Step), (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( id: "shot_b_cookie_rot", kind: Vec3, keyframes: [ (t: 0.0, v: (0.5, 0.0, -0.05), e: Step), (t: 2.2, v: (1.0, 0.0, -0.05), e: Step), (t: 20.0, v: (.2, -0.1, -0.05), e: Step), ], ), ( id: "shot_b_cookie_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 2.0, v: (0.43, 0.5, 0.5), e: Step), (t: 2.2, v: (0.4, 0.5, 0.5), e: Step), (t: 20.0, v: (0.5, 0.5, 1.0), e: Step), ], ), ( id: "shot_b_cookie_opacity", kind: F32, keyframes: [ (t: 0.0, v: 0.0, e: Step), (t: 2.0, v: 0.95, e: Step), (t: 2.2, v: 0.9, e: Smooth), (t: 3.0, v: 0.0, e: Smooth), (t: 20.0, v: 0.0, e: Step), ], ), ( // Title letter meshes render at fixed scale=20 (see demo.rs). Scale stays 0 for all // of Shot A/B and pops to full size right at this letter's own ignite time (matching // title_P_opacity's ramp-start) — opacity alone isn't enough to hide fixed-scale // geometry over the hero mech, so scale does the hiding, opacity does the reveal. id: "title_P_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 14.30, v: (20.0, 20.0, 20.0), e: Step), (t: 20.0, v: (20.0, 20.0, 20.0), e: Step), ], ), ( id: "title_I_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 14.38, v: (20.0, 20.0, 20.0), e: Step), (t: 20.0, v: (20.0, 20.0, 20.0), e: Step), ], ), ( id: "title_L1_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 14.36, v: (20.0, 20.0, 20.0), e: Step), (t: 20.0, v: (20.0, 20.0, 20.0), e: Step), ], ), ( id: "title_L2_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 14.30, v: (20.0, 20.0, 20.0), e: Step), (t: 20.0, v: (20.0, 20.0, 20.0), e: Step), ], ), ( // Left-to-right ignition sweep: P leads, I/L1/L2 pop in behind it (see title_I/L1/L2_opacity). // Decay is individually staggered, edge letters first, each a slow 1.4s dissolve — // not a hard cut and not two letters sharing one curve. "engine" ignites at 18.0, // while I and L1 are still mid-fade, so it overlays them (see engine_opacity). id: "title_P_opacity", kind: F32, keyframes: [ (t: 0.0, v: 0.0, e: Step), (t: 14.30, v: 0.0, e: Smooth), (t: 14.50, v: 1.0, e: Smooth), (t: 16.0, v: 1.0, e: Smooth), (t: 17.4, v: 0.0, e: Smooth), (t: 20.0, v: 0.0, e: Step), ], ), ( // Ignites 2nd — the glowing vertical-bar letter (title_glow_mat), sweep's visual anchor. // Inner letter: decay starts 3rd (after both edges), long overlap with engine igniting. id: "title_I_opacity", kind: F32, keyframes: [ (t: 0.0, v: 0.0, e: Step), (t: 14.38, v: 0.0, e: Smooth), (t: 14.58, v: 1.0, e: Smooth), (t: 16.9, v: 1.0, e: Smooth), (t: 18.3, v: 0.0, e: Smooth), (t: 20.0, v: 0.0, e: Step), ], ), ( // Ignites 3rd. Outer letter (title_2.obj "pill.002" x_center=-0.070, the left edge — // verified from raw obj vertex coords, not the P/I/L1/L2 name) — decays 2nd, shortly // after title_P_opacity. id: "title_L1_opacity", kind: F32, keyframes: [ (t: 0.0, v: 0.0, e: Step), (t: 14.36, v: 0.0, e: Smooth), (t: 14.66, v: 1.0, e: Smooth), (t: 16.45, v: 1.0, e: Smooth), (t: 17.85, v: 0.0, e: Smooth), (t: 20.0, v: 0.0, e: Step), ], ), ( // Ignites 4th — sweep lands. Inner letter ("pill.003" x_center=-0.000, near-center, // not an edge) — decays last, longest overlap with engine. id: "title_L2_opacity", kind: F32, keyframes: [ (t: 0.0, v: 0.0, e: Step), (t: 14.30, v: 0.0, e: Smooth), (t: 14.74, v: 1.0, e: Smooth), (t: 17.35, v: 1.0, e: Smooth), (t: 18.75, v: 0.0, e: Smooth), (t: 20.0, v: 0.0, e: Step), ], ), ( // Pops at 16.0 — matches when engine_opacity starts its ignite ramp (see below). id: "engine_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 16.0, v: (20.0, 20.0, 20.0), e: Step), (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( // Slow 2.0s ignite starting at 16.0 — title_I_opacity holds fully visible until // 16.9, so most of this ramp builds up while I is still at full opacity, not // already faded. Holds, then fades out over the shot's final 1.0s, hitting 0 // exactly at t=20.0 (Title shot end / loop point). id: "engine_opacity", kind: F32, keyframes: [ (t: 0.0, v: 0.0, e: Step), (t: 16.5, v: 0.0, e: Step), (t: 17.8, v: 1.0, e: Smooth), (t: 18.6, v: 1.0, e: Smooth), (t: 19.0, v: 0.0, e: Smooth), ], ), ( id: "poster_credits_scale", kind: Vec3, keyframes: [ (t: 0.0, v: (0.0, 0.0, 0.0), e: Step), (t: 19.0, v: (8.0, 8.0, 8.0), e: Step), (t: 20.0, v: (0.0, 0.0, 0.0), e: Step), ], ), ( id: "poster_credits_opacity", kind: F32, keyframes: [ (t: 0.0, v: 0.0, e: Step), (t: 19.2, v: 0.0, e: Step), (t: 19.3, v: 1.0, e: Smooth), (t: 19.7, v: 1.0, e: Smooth), (t: 20.0, v: 0.0, e: Smooth), ], ), ( // 0 = hidden, 1 = visible from Silence through Pulse (9.5–14.0); hidden again at Mid+Out. id: "scatter_visible", kind: F32, keyframes: [ (t: 0.0, v: 0.0, e: Step), (t: 9.5, v: 1.0, e: Step), (t: 14.0, v: 0.0, e: Step), (t: 20.0, v: 0.0, e: Step), ], ), ( // Scatter turbulence — slow peel: clusters fire in sequence along the crown→base wave. // A particle activates when turb > dot_t*CLUSTER_SPREAD + intra_hash*INTRA_CLUSTER_SPREAD, // dot_t in [0,1] across the wave axis, NUM_CLUSTERS=20. CLUSTER_SPREAD=4.5 must stay // below this track's 5.0 peak or the tail of the cloud never reaches its threshold. // turb 0→5 over 4.1s on an S-curve (EaseIn toe, EaseInOut body): the toe keeps the // impact beat at 10.0, the middle carries the peel, the tail lands the last Hold // clusters just before the 14.0 cut. The shot cuts at 14.0, so 4.1s is the longest // peel that fits — a true 2x of the old 2.5s would need the cut moved. id: "scatter_turb_strength", kind: F32, keyframes: [ (t: 0.0, v: 0.0, e: Step), (t: 9.5, v: 0.0, e: Step), // appear at rest (t: 9.7, v: 0.0, e: EaseIn), // slow toe: impact beat still reads at 10.0 (t: 12.2, v: 2.6, e: EaseInOut), // PEEL: S-curve, clusters fire in sequence (t: 13.8, v: 5.0, e: Smooth), // last holds go just before the 14.0 cut (t: 14.3, v: 5.0, e: Step), (t: 14.4, v: 0.0, e: Step), (t: 20.0, v: 0.0, e: Step), ], ), ( // Drag — high to match the higher TURB_FORCE_SCALE (terminal v ≈ 2.8 units/s). // Fast particles snap to terminal velocity quickly; angular motion preserved. id: "scatter_drag", kind: F32, keyframes: [ (t: 0.0, v: 0.5, e: Step), (t: 9.5, v: 0.2, e: Smooth), // light drag at rest (t: 10.0, v: 1.5, e: Smooth), // high drag: snaps to terminal in ~0.3s (t: 14.3, v: 1.5, e: Step), (t: 14.4, v: 0.0, e: Step), // free coast: particles keep full speed at cut (t: 14.5, v: 0.0, e: Step), (t: 20.0, v: 0.0, e: Step), ], ), ( // Attractor — held through the peel so clusters that have not fired keep the mech's // shape while their neighbours leave, then released for free flight. id: "scatter_attractor", kind: F32, keyframes: [ (t: 0.0, v: 0.8, e: Step), (t: 9.5, v: 0.25, e: Smooth), // holds shape at rest // Spring back to rest. Equilibrium offset of an activated particle is roughly // (sustained force) / attractor — with force ~1.1 from blast+vortex+curl, 0.05 // put that at 20+ units and the cloud simply left. 0.6 saturates it near 2 units, // about the mech's own width, so particles orbit the silhouette during the peel. (t: 10.0, v: 0.6, e: Smooth), // confine: loosen in place, don't fly (t: 12.5, v: 0.6, e: Smooth), // Pulse 2: cloud is at its widest (t: 13.5, v: 0.05, e: Smooth), // release: spring lets go as the clusters do (CLUSTER_RELEASE_TIME) (t: 13.9, v: 0.005,e: Smooth), // release: free flight into the 14.0 cut (t: 14.4, v: 0.005,e: Smooth), (t: 14.5, v: 0.0, e: Step), (t: 20.0, v: 0.0, e: Step), ], ), ], )