High-res effects: explosions, smoke, bursts, impact stars, healing and glowing projectiles as flipbooks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7972e8ad1b
commit
8c5afe872f
10 files changed
+218
-51
No files matched your search
@@ -1602,6 +1602,9 @@ impl R3d {
|
||||
let m = cam.matrix();
|
||||
self.update_chunks(g, gfx, &m);
|
||||
EYE.with(|e| e.set(cam.eye_dir()));
|
||||
let (_, cr, cu) = cam.basis();
|
||||
let hd_fx = self.pixel_rows >= crate::settings::HIGH_RES_ROWS as u32;
|
||||
FX.with(|f| *f.borrow_mut() = hd_fx.then(|| (gfx.fx.clone(), cr, cu)));
|
||||
// pixel look: the world is rendered at a fixed number of rows and scaled up to the screen
|
||||
// on the virtual screen (ui.rs) the world takes exactly k x k interface pixels per pixel
|
||||
let vrows = crate::ui::virtual_rows();
|
||||
@@ -2712,6 +2715,44 @@ fn draw_towers_glow(g: &Game, t: f32) {
|
||||
// Camera direction for camera-facing line quads (set once per frame)
|
||||
thread_local! {
|
||||
static EYE: std::cell::Cell<Vec3> = std::cell::Cell::new(Vec3::Y);
|
||||
// the high-res style's effect flipbooks and the camera's right and up (set once per frame;
|
||||
// None in the pixel-art style, whose effects stay simple shapes)
|
||||
static FX: std::cell::RefCell<Option<(Texture2D, Vec3, Vec3)>> = const { std::cell::RefCell::new(None) };
|
||||
}
|
||||
|
||||
// the rows of the effect atlas (8 frames of 64x64 each)
|
||||
const FX_EXPLOSION: u32 = 0;
|
||||
const FX_SMOKE: u32 = 1;
|
||||
const FX_BURST: u32 = 2; // white, tinted
|
||||
const FX_IMPACT: u32 = 3; // white, tinted
|
||||
const FX_HEAL: u32 = 4;
|
||||
const FX_ORBS: u32 = 5; // projectiles: 0-1 fireball, 2-3 white orb (tinted), 4 holy, 5 shadow, 6 rune, 7 arcane
|
||||
|
||||
// draws frame `frame` of an effect row as a camera-facing picture (`turn`: radians in the
|
||||
// screen plane); false in the pixel-art style (the caller draws its simple shapes)
|
||||
fn fx_sprite(row: u32, frame: u32, at: Vec3, size: f32, turn: f32, c: Color) -> bool {
|
||||
FX.with(|f| {
|
||||
let f = f.borrow();
|
||||
let Some((tex, r, u)) = f.as_ref() else { return false };
|
||||
let (s, co) = turn.sin_cos();
|
||||
let (rr, uu) = ((*r * co + *u * s) * size * 0.5, (*u * co - *r * s) * size * 0.5);
|
||||
let (u0, v0) = ((frame.min(7) as f32) / 8.0, row as f32 / 6.0);
|
||||
let (u1, v1) = (u0 + 1.0 / 8.0, v0 + 1.0 / 6.0);
|
||||
let col = [(c.r * 255.0) as u8, (c.g * 255.0) as u8, (c.b * 255.0) as u8, (c.a.clamp(0.0, 1.0) * 255.0) as u8];
|
||||
let v = |p: Vec3, uv: Vec2| Vertex { position: p, uv, color: col, normal: Vec4::ZERO };
|
||||
let mesh = Mesh {
|
||||
vertices: vec![v(at - rr + uu, vec2(u0, v0)), v(at + rr + uu, vec2(u1, v0)), v(at + rr - uu, vec2(u1, v1)), v(at - rr - uu, vec2(u0, v1))],
|
||||
indices: vec![0, 1, 2, 0, 2, 3],
|
||||
texture: Some(tex.clone()),
|
||||
};
|
||||
draw_mesh(&mesh);
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
// the frame of an 8-frame effect at life p (0..1)
|
||||
fn fx_frame(p: f32) -> u32 {
|
||||
((p * 8.0) as u32).min(7)
|
||||
}
|
||||
|
||||
// Thick 3D line as a camera-facing quad (GL lines don't work with custom materials)
|
||||
@@ -3509,6 +3550,23 @@ fn draw_projectile(f: &Fx, p: f32, t: f32) {
|
||||
draw_sphere(head, 0.1 * sz, None, Color::from_rgba(24, 24, 28, 255));
|
||||
draw_sphere(head, 0.14 * sz, None, Color::new(1.0, 0.6, 0.2, 0.15));
|
||||
}
|
||||
ST_FIRE | ST_HOLY | ST_SHADOW | ST_RUNE | ST_ARCANE | ST_MAGIC if {
|
||||
// the high-res style: a picture, a fireball's tail pointing back along its way
|
||||
let back = at((p - 0.05).max(0.0));
|
||||
let (_, r, u) = FX.with(|x| x.borrow().as_ref().map(|x| (0, x.1, x.2))).unwrap_or((0, Vec3::X, Vec3::Y));
|
||||
let d = head - back;
|
||||
let turn = d.dot(u).atan2(d.dot(r));
|
||||
let flick = ((t * 12.0 + f.x) as u32) % 2;
|
||||
let (frame, tint) = match f.style {
|
||||
ST_FIRE => (flick, WHITE),
|
||||
ST_HOLY => (4, WHITE),
|
||||
ST_SHADOW => (5, WHITE),
|
||||
ST_RUNE => (6, WHITE),
|
||||
ST_ARCANE => (7, WHITE),
|
||||
_ => (2 + flick, Color::new(c.r, c.g, c.b, 1.0)),
|
||||
};
|
||||
fx_sprite(FX_ORBS, frame, head, 0.42 * sz, if f.style == ST_FIRE { turn } else { 0.0 }, tint)
|
||||
} => {}
|
||||
ST_FIRE => {
|
||||
let flick = 1.0 + (t * 30.0 + f.x).sin() * 0.12;
|
||||
draw_sphere(head, 0.08 * sz * flick, None, Color::new(1.0, 0.95, 0.6, 1.0));
|
||||
@@ -3801,7 +3859,14 @@ fn draw_parts_glow(g: &Game) {
|
||||
let s = p.size * (1.0 - life * 0.6);
|
||||
draw_cube(at, Vec3::splat(s), None, Color::new(c.r, c.g, c.b, 1.0 - life * life));
|
||||
}
|
||||
SMOKE if fx_sprite(FX_SMOKE, fx_frame(life), at, p.size * 2.6, p.size * 3.0, Color::new(c.r.max(0.5), c.g.max(0.5), c.b.max(0.5), 0.55 * (1.0 - life))) => {}
|
||||
SMOKE => draw_sphere(at, p.size, None, Color::new(c.r, c.g, c.b, 0.14 * (1.0 - life))),
|
||||
FLASH if {
|
||||
// fire explodes, anything else bursts in its colour
|
||||
let fiery = c.r > 0.8 && c.g < 0.8 && c.b < 0.45;
|
||||
let (row, tint) = if fiery { (FX_EXPLOSION, WHITE) } else { (FX_BURST, Color::new(c.r, c.g, c.b, 1.0)) };
|
||||
fx_sprite(row, fx_frame(life), at, p.size * 2.6, 0.0, tint)
|
||||
} => {}
|
||||
FLASH => {
|
||||
let r = p.size * (0.3 + life * 0.9);
|
||||
draw_sphere(at, r * 0.6, None, Color::new(1.0, 0.95, 0.8, 0.95 * (1.0 - life)));
|
||||
@@ -3886,6 +3951,7 @@ fn draw_fx(f: &Fx, t: f32) {
|
||||
}
|
||||
disc(vec3(f.x, 0.05, f.y), 1.6 * (0.5 + p), Color::new(1.0, 1.0, 0.7, 0.35 * (1.0 - p)), 24);
|
||||
}
|
||||
2 if fx_sprite(FX_IMPACT, fx_frame(p), at, 0.7 * f.size, 0.0, Color::new(c.r.max(0.4), c.g.max(0.4), c.b.max(0.4), 1.0)) => {}
|
||||
2 => {
|
||||
let r = (0.1 + 0.3 * p) * f.size;
|
||||
let n = if f.size > 1.35 { 10 } else { 6 };
|
||||
@@ -3897,7 +3963,9 @@ fn draw_fx(f: &Fx, t: f32) {
|
||||
disc(vec3(f.x, 0.03, f.y), r * 1.3, a(0.3 * (1.0 - p)), 12);
|
||||
}
|
||||
}
|
||||
3 if fx_sprite(FX_BURST, fx_frame(p), vec3(f.x, 0.4, f.y), 1.4 * f.size, 0.0, Color::new(c.r, c.g, c.b, 1.0)) => {}
|
||||
3 => draw_sphere(vec3(f.x, 0.3, f.y), (0.2 + 0.5 * p) * f.size, None, a(0.4 * (1.0 - p))),
|
||||
4 if fx_sprite(FX_HEAL, fx_frame(p), at + vec3(0.0, 0.4, 0.0), 1.0, 0.0, WHITE) => {}
|
||||
4 => {
|
||||
for k in 0..3 {
|
||||
let o = vec3((k as f32 - 1.0) * 0.2, 0.3 + p * 0.9 + k as f32 * 0.1, 0.0);
|
||||
|
||||
Reference in new issue
Block a user