feat: store color directly in Cell (reality layer)

Each cell now stores its own fg/bg color inline, instead of looking
up from MaterialRegistry every frame.

Cell struct:
- fg: [u8; 3] — foreground color (for rendering)
- bg: [u8; 3] — background color (for rendering)
- temp: f32 — temperature (already there)
- material: MaterialId — for physics property lookup
- variant: u8 — visual/behavioral variant
- updated_this_tick: bool — CA flag

Colors are copied from MaterialRegistry at Cell::new() time.
This allows per-cell color variation in the future (water depth
shading, lava gradient, damaged materials, etc.) without registry
changes.

MaterialRegistry still stores physics properties (density, solid,
liquid, flammable, etc.) — looked up only during CA/physics steps,
not during rendering.

Renderers (terminal, ascii, graphics) updated to use cell.fg/cell.bg
directly — no more registry lookup in render path.

Cell size: ~16 bytes (was ~12). 250x250 grid = 1MB (was 750KB).
126 tests, 0 failures
This commit is contained in:
Emil
2026-06-21 01:21:38 +03:00
parent 2eb9de6502
commit d889e24cf6
4 changed files with 17 additions and 19 deletions
+1 -2
View File
@@ -344,7 +344,6 @@ impl GraphicsRenderer {
[40, 40, 40, 255]
} else {
let cell = grid.get(wx, wy);
let mat = reg.get(cell.material);
if cell.is_empty() {
bg_default
} else {
@@ -352,7 +351,7 @@ impl GraphicsRenderer {
let r = 200u8.saturating_add(cell.variant / 2);
[r, 60, 20, 255]
} else {
[mat.color_fg.0, mat.color_fg.1, mat.color_fg.2, 255]
[cell.fg[0], cell.fg[1], cell.fg[2], 255]
}
}
};
+5 -13
View File
@@ -86,26 +86,18 @@ impl Renderer for TerminalRenderer {
continue;
}
let cell = grid.get(wx, wy);
let mat = reg.get(cell.material);
let idx = dy * self.width + dx;
if cell.is_empty() {
frame[idx] = (' ', mat.color_fg, mat.color_bg);
frame[idx] = (' ', (cell.fg[0], cell.fg[1], cell.fg[2]), (cell.bg[0], cell.bg[1], cell.bg[2]));
} else {
let ch = if cell.material == MaterialId::Lava {
if cell.variant % 2 == 0 { '#' } else { '#' }
} else if cell.material == MaterialId::Water {
if cell.variant % 3 == 0 { '~' } else { '~' }
} else {
mat.display_char
};
let ch = cell.material.display_char();
let fg = if cell.material == MaterialId::Lava {
let flicker = cell.variant;
let r = 200u8.saturating_add(flicker / 2);
let r = 200u8.saturating_add(cell.variant / 2);
(r, 60, 20)
} else {
mat.color_fg
(cell.fg[0], cell.fg[1], cell.fg[2])
};
frame[idx] = (ch, fg, mat.color_bg);
frame[idx] = (ch, fg, (cell.bg[0], cell.bg[1], cell.bg[2]));
}
}
}
+3 -4
View File
@@ -207,7 +207,6 @@ impl VulkanRenderer {
('?', [80, 80, 80, 255], bg_default)
} else {
let cell = grid.get(wx, wy);
let mat = reg.get(cell.material);
if cell.is_empty() {
(' ', [10, 10, 15, 255], bg_default)
} else {
@@ -215,10 +214,10 @@ impl VulkanRenderer {
let r = 200u8.saturating_add(cell.variant / 2);
[r, 60, 20, 255]
} else {
[mat.color_fg.0, mat.color_fg.1, mat.color_fg.2, 255]
[cell.fg[0], cell.fg[1], cell.fg[2], 255]
};
let bg = [mat.color_bg.0, mat.color_bg.1, mat.color_bg.2, 255];
(mat.display_char, fg, bg)
let bg = [cell.bg[0], cell.bg[1], cell.bg[2], 255];
(cell.material.display_char(), fg, bg)
}
};
+8
View File
@@ -67,6 +67,8 @@ pub struct Cell {
pub temp: f32,
pub updated_this_tick: bool,
pub variant: u8,
pub fg: [u8; 3],
pub bg: [u8; 3],
}
impl Cell {
@@ -76,10 +78,14 @@ impl Cell {
temp: 20.0,
updated_this_tick: false,
variant: 0,
fg: [15, 15, 20],
bg: [10, 10, 15],
}
}
pub fn new(material: MaterialId) -> Self {
let reg = MaterialRegistry::instance();
let mat = reg.get(material);
let temp = match material {
MaterialId::Lava => 1500.0,
MaterialId::Fire => 800.0,
@@ -92,6 +98,8 @@ impl Cell {
temp,
updated_this_tick: false,
variant: rand_u8(),
fg: [mat.color_fg.0, mat.color_fg.1, mat.color_fg.2],
bg: [mat.color_bg.0, mat.color_bg.1, mat.color_bg.2],
}
}