Files
atomic-engine/engine/src/render_buffer.rs
T
nico 26d5733e1e v0.4: Acid system rework + Iron material + heat conductivity
- Acid now uses acid_resist + health-based damage (not instant delete)
- Acid dissolve speeds: Wood ~2s, Dirt ~2.8s, Stone ~4.3s, Iron immune
- New material: Iron (ID 16) - silver, static, acid-proof, heat-conductive
- Iron texture: bright highlights for metallic shine
- Iron reflects light (light_block: 150)
- Iron heat_conduct: 200 - conducts heat quickly through entire block
- Improved heat transfer formula (divisor 255→200)
- Updated AGENTS.md status
- No player code (rolled back for fresh start)
2026-07-10 10:06:34 +02:00

266 lines
9.1 KiB
Rust

use crate::grid::Grid;
use crate::material::PROPS;
pub struct RenderBuffer {
pub pixels: Vec<u8>,
pub width: u32,
pub height: u32,
light: Vec<u8>,
block: Vec<u8>,
zoom: f32,
}
impl RenderBuffer {
pub fn new(width: u32, height: u32) -> Self {
let size = (width * height * 4) as usize;
let lsize = (width * height) as usize;
Self {
pixels: vec![0; size],
width,
height,
light: vec![0; lsize],
block: vec![0; lsize],
zoom: 1.0,
}
}
pub fn render(&mut self, grid: &Grid, cam_x: i32, cam_y: i32, zoom: f32) {
let rw = self.width as i32;
let rh = self.height as i32;
let total = (rw * rh) as usize;
self.zoom = zoom;
for i in 0..total {
self.light[i] = 0;
self.block[i] = 0;
}
for py in 0..rh {
let gy = cam_y + ((py as f32 - rh as f32 / 2.0) / zoom) as i32;
for px in 0..rw {
let gx = cam_x + ((px as f32 - rw as f32 / 2.0) / zoom) as i32;
let idx = (py * rw + px) as usize;
if grid.in_bounds(gx, gy) {
let gi = grid.index(gx as u32, gy as u32);
let mat = grid.materials[gi];
if mat > 0 {
let props = &PROPS[mat as usize];
self.light[idx] = props.light_emit;
self.block[idx] = props.light_block;
}
}
}
}
let max_passes = (16.0 / zoom).max(6.0).min(24.0) as u32;
for _pass in 0..max_passes {
let dir = _pass & 1;
let mut changed = false;
if dir == 0 {
for py in 0..rh {
for px in 0..rw {
if self.spread_light(py, px, rw, rh) {
changed = true;
}
}
}
} else {
for py in (0..rh).rev() {
for px in (0..rw).rev() {
if self.spread_light(py, px, rw, rh) {
changed = true;
}
}
}
}
if !changed {
break;
}
}
for py in 0..rh {
for px in 0..rw {
let gx = cam_x + ((px as f32 - rw as f32 / 2.0) / zoom) as i32;
let gy = cam_y + ((py as f32 - rh as f32 / 2.0) / zoom) as i32;
let color = if grid.in_bounds(gx, gy) {
let gi = grid.index(gx as u32, gy as u32);
let mat = grid.materials[gi];
let li = (py * rw + px) as usize;
let lvl = self.light[li] as u32;
if mat == 0 {
let bg = [20u32, 20, 30];
let glow_r = 255u32;
let glow_g = 160u32;
let glow_b = 40u32;
let t = ((lvl * lvl) / 255).min(255);
let r = (bg[0] * (255 - t) + glow_r * t) / 255;
let g = (bg[1] * (255 - t) + glow_g * t) / 255;
let b = (bg[2] * (255 - t) + glow_b * t) / 255;
[r as u8, g as u8, b as u8, 255]
} else {
let props = &PROPS[mat as usize];
let base = props.color;
let temp = grid.temps[gi];
let t = (temp as f32 / 200.0).min(1.0);
let mut r = (base[0] as f32 + t * 60.0) as u8;
let mut g = (base[1] as f32 * (1.0 - t * 0.4)) as u8;
let mut b = (base[2] as f32 * (1.0 - t * 0.6)) as u8;
if mat == 9 || mat == 6 {
let (fr, fg, fb) = flame_variation(gx as u32, gy as u32, mat);
r = (r as i32 + fr).clamp(0, 255) as u8;
g = (g as i32 + fg).clamp(0, 255) as u8;
b = (b as i32 + fb).clamp(0, 255) as u8;
} else {
let var = texture_offset(gx as u32, gy as u32, mat);
r = (r as i32 + var).clamp(0, 255) as u8;
g = (g as i32 + var).clamp(0, 255) as u8;
b = (b as i32 + var).clamp(0, 255) as u8;
}
if props.light_emit == 0 {
let ambient = 150u32;
let add = lvl;
r = ((r as u32 * ambient >> 8) + add).min(255) as u8;
g = ((g as u32 * ambient >> 8) + add).min(255) as u8;
b = ((b as u32 * ambient >> 8) + add).min(255) as u8;
}
[r, g, b, base[3]]
}
} else {
[20, 20, 30, 255]
};
let pi = ((py * rw + px) * 4) as usize;
self.pixels[pi] = color[0];
self.pixels[pi + 1] = color[1];
self.pixels[pi + 2] = color[2];
self.pixels[pi + 3] = color[3];
}
}
}
fn spread_light(&mut self, py: i32, px: i32, rw: i32, rh: i32) -> bool {
let idx = (py * rw + px) as usize;
let cur = self.light[idx] as u32;
let blk = (self.block[idx] as u32).min(200);
let neighbors: [(i32, i32); 8] = [
(px - 1, py - 1), (px, py - 1), (px + 1, py - 1),
(px - 1, py), (px + 1, py),
(px - 1, py + 1), (px, py + 1), (px + 1, py + 1),
];
let mut best = cur;
for &(nx, ny) in &neighbors {
if nx >= 0 && nx < rw && ny >= 0 && ny < rh {
let ni = (ny * rw + nx) as usize;
let nlight = self.light[ni] as u32;
if nlight <= 1 { continue; }
let base_falloff = if nx == px || ny == py { 2.0 } else { 3.0 };
let falloff = (base_falloff / self.zoom) as u32 + blk;
if nlight > falloff {
let incoming = nlight - falloff;
if incoming > best {
best = incoming;
}
}
}
}
if blk > 0 && blk < 180 && best > cur && best > 4 {
for &(nx, ny) in &neighbors {
if nx >= 0 && nx < rw && ny >= 0 && ny < rh {
let ni = (ny * rw + nx) as usize;
let nlight = self.light[ni] as u32;
if nlight >= best { continue; }
let reflect = best >> 1;
if reflect > nlight {
self.light[ni] = reflect as u8;
}
}
}
}
if best != cur {
self.light[idx] = best as u8;
true
} else {
false
}
}
}
fn texture_offset(x: u32, y: u32, mat: u8) -> i32 {
match mat {
5 => {
let wobble = ((y.wrapping_mul(5) ^ y.wrapping_shr(2)) & 3) as i32 - 1;
let pos = (x as i32).wrapping_add(wobble);
let grain = pos % 5;
let line_id = (pos / 5) as u32;
let darkness = hash(line_id, 0) & 15;
if grain == 0 {
-12 - darkness as i32
} else if grain == 1 {
-5
} else {
let n = hash(x.wrapping_add(y >> 1), y);
((n as i32 - 128) * 7) >> 7
}
}
2 => {
let n = hash(x.wrapping_mul(3) >> 2, y.wrapping_mul(3) >> 2);
((n as i32 - 128) * 20) >> 7
}
1 | 10 => {
let n = hash(x, y);
((n as i32 - 128) * 10) >> 7
}
3 => {
let n = hash(x.wrapping_add(y), y);
((n as i32 - 128) * 6) >> 7
}
11 => {
let n = hash(x, y.wrapping_mul(y));
((n as i32 - 128) * 5) >> 7
}
16 => {
let n = hash(x ^ (y >> 2), y ^ (x >> 2));
let highlight = ((x.wrapping_mul(13) ^ y.wrapping_mul(7)) & 31) as i32;
if highlight < 3 {
25 + (highlight * 6)
} else if highlight < 6 {
14
} else {
((n as i32 - 128) * 10) >> 7
}
}
_ => 0,
}
}
fn hash(x: u32, y: u32) -> u8 {
let h = x.wrapping_mul(374761393)
.wrapping_add(y.wrapping_mul(668265263));
(h ^ h.wrapping_shr(13)).wrapping_mul(1274126177) as u8
}
fn flame_variation(x: u32, y: u32, mat: u8) -> (i32, i32, i32) {
let h = hash(x, y) as i32;
let r_var = ((h - 128) * 6) >> 7;
if mat == 9 {
let g_var = ((h.wrapping_sub(40) as i32 - 128) * 12) >> 7;
let b_var = ((h.wrapping_add(60) as i32 - 128) * 8) >> 7;
(-r_var, g_var, b_var)
} else {
let g_var = ((h - 128) * 8) >> 7;
let b_var = ((h - 128) * 5) >> 7;
(r_var, g_var, b_var)
}
}