From 16e299d1f13fff1f6f566abc7493b3f69477d426 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Sat, 7 Nov 2026 23:49:00 +0100 Subject: [PATCH] Mossgrim's heart: a gnarled moss-grown tree rising from the throne, the heart glowing in its hollow Co-Authored-By: Claude Opus 5.5 --- src/render3d.rs | 68 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/src/render3d.rs b/src/render3d.rs index c373bf0..928955a 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -2811,34 +2811,64 @@ fn draw_rival_heart(base: Vec3, hc: Vec3, hp: f32, t: f32, ph: f32, beat: f32, r let col = |v: Vec3, a: f32| Color::new(v.x.min(1.0), v.y.min(1.0), v.z.min(1.0), a); let life = 0.45 + 0.55 * hp; // a hurt heart fades match side { - // Mossgrim: a gnarled knot of branches grown into a heart, moss cushions on it that swell - // with every beat, green spores drifting up + // Mossgrim: a gnarled, moss-grown tree without leaves growing out of the throne, roots + // clawing over its steps, bare twisted branches; in a hollow of the trunk his heart glows + // green, the moss cushions swell with every beat, green spores drift up 2 => { - let bark = Color::new(0.32, 0.22, 0.14, 1.0); + let wood = crate::props::material(crate::props::Mat::Wood); + let bark = Color::new(0.36, 0.25, 0.16, 1.0); + let y0 = base.y + 0.5; + let y1 = hc.y + 0.35; + let segs = 7; + // the trunk's middle at height f (0..1): it winds a little + let mid = |f: f32| base + vec3((f * 5.0).sin() * 0.07, y0 - base.y + f * (y1 - y0), (f * 4.0 + 1.0).cos() * 0.06); + for q in 0..segs { + let (f0, f1) = (q as f32 / segs as f32, (q + 1) as f32 / segs as f32); + let (r0, r1) = (0.24 - f0 * 0.15, 0.24 - f1 * 0.15); + let p = mid(f0); + draw_cylinder(p, r1, r0, mid(f1).y - p.y + 0.02, wood.as_ref(), bark); + } + // roots over the steps for k in 0..6 { - let a0 = k as f32 * 1.05; - let mut prev = hc + up * -0.5 + right * (a0.cos() * 0.08); - for q in 1..=7 { - let f = q as f32 / 7.0; - // two lobes: the branches part above and meet below - let side_ = if k % 2 == 0 { 1.0 } else { -1.0 }; - let x = side_ * (f * std::f32::consts::PI).sin() * 0.32 + (a0 + f * 4.0).sin() * 0.04; - let next = hc + up * (-0.5 + f * 0.85) + right * x + front * ((a0 + f * 3.0).cos() * 0.12); - line3(prev, next, 0.07 - f * 0.03, bark); + let a = k as f32 * 1.05 + 0.3; + let d = vec3(a.cos(), 0.0, a.sin()); + let mut prev = mid(0.0) + d * 0.15 + up * 0.05; + for q in 1..=4 { + let f = q as f32 / 4.0; + let next = mid(0.0) + d * (0.15 + f * 0.75) + up * (0.05 - f * 0.12) + vec3(0.0, 0.0, (a * 3.0 + f * 4.0).sin() * 0.05); + line3(prev, next, 0.09 - f * 0.06, bark); prev = next; } } - for k in 0..9 { - let a = k as f32 * 2.1; - let p = hc + right * (a.cos() * 0.26) + up * (a.sin() * 0.22 - 0.05) + front * 0.12; - let r = (0.07 + (k % 3) as f32 * 0.025) * (1.0 + beat * 0.35); - draw_sphere(p, r, None, col(vec3(0.16, 0.42, 0.14) * (life + beat * 0.5), 1.0)); + // bare branches from the upper trunk, curling up and out + for k in 0..4 { + let a = k as f32 * 1.57 + 0.4; + let d = vec3(a.cos(), 0.0, a.sin()); + let mut prev = mid(0.7 + k as f32 * 0.07); + for q in 1..=5 { + let f = q as f32 / 5.0; + let next = prev + d * (0.11 - f * 0.03) + up * (0.05 + f * 0.06) + vec3((f * 6.0 + a).sin() * 0.03, 0.0, 0.0); + line3(prev, next, 0.06 - f * 0.045, bark); + prev = next; + } } - draw_sphere(hc, 0.7 * (1.0 + beat * 0.08), None, col(mc * 0.6, 0.1 + 0.1 * beat)); + // the glowing heart in a hollow of the trunk, facing the viewer + let hollow = mid(0.62) + front * 0.13; + draw_sphere(hollow, 0.13, None, Color::new(0.05, 0.08, 0.04, 1.0)); + draw_sphere(hollow + front * 0.03, 0.08 * (1.0 + beat * 0.4), None, col(vec3(0.4, 1.0, 0.35) * (0.6 + 0.4 * life + beat * 0.4), 1.0)); + // moss cushions on the trunk, the roots and the branch forks + for k in 0..11 { + let f = 0.08 + (k as f32 * 0.37).fract() * 0.8; + let a = k as f32 * 2.3; + let p = mid(f) + vec3(a.cos(), 0.0, a.sin()) * (0.22 - f * 0.13); + let r = (0.05 + (k % 3) as f32 * 0.02) * (1.0 + beat * 0.35); + draw_sphere(p, r, None, col(vec3(0.18, 0.45, 0.14) * (life + beat * 0.4), 1.0)); + } + draw_sphere(hollow, 0.55 * (1.0 + beat * 0.08), None, col(mc * 0.6, 0.08 + 0.1 * beat)); for k in 0..12 { let e = (t * 0.22 + k as f32 * 0.083).fract(); let a = k as f32 * 2.4 + t * 0.4; - let p = hc + vec3(a.cos() * (0.3 + e * 0.6), -0.3 + e * 1.8, a.sin() * (0.3 + e * 0.6)); + let p = hollow + vec3(a.cos() * (0.3 + e * 0.6), -0.3 + e * 1.8, a.sin() * (0.3 + e * 0.6)); draw_cube(p, Vec3::splat(0.035 * (1.0 - e) + 0.012), None, col(vec3(0.5, 1.0, 0.45), (1.0 - e) * 0.9)); } }