High-res tile textures: floor, claimed floor, earth, rock, gold, churning water and lava
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
69a6b13006
commit
8197b07ac5
33 files changed
+256
-7
No files matched your search
+86
-1
@@ -99,6 +99,7 @@ pub struct Gfx {
|
||||
pub atlas: Texture2D,
|
||||
pub sprites: Texture2D,
|
||||
pub hd_sprites: Texture2D, // detailed sprites (see HD_SPRITES, hd_cell_view)
|
||||
pub atlas_hd: Texture2D, // the tile atlas of the high-res style (see HD_TILES)
|
||||
pub cursors: Texture2D, // mouse pointer frames, 16x16 each (see CURSOR_*)
|
||||
pub badges: Texture2D, // achievement badges, 32x32 each (see badges.rs)
|
||||
pub font: Texture2D, // Dinowyde (glyphs of 6x8 px ordered by Unicode, see font_slot) and DINOBYTE
|
||||
@@ -325,6 +326,55 @@ fn earth_px(x: i32, y: i32, v: u32) -> Color {
|
||||
shade(base, (big + fine) as i32)
|
||||
}
|
||||
|
||||
// ---- high-res tiles
|
||||
// The high-res style's atlas: the same cells as the 16x16 atlas, HD_TILE x HD_TILE texels each.
|
||||
// Cells without an own texture are the 16x16 ones scaled up. HD_TILES: generated textures (see
|
||||
// art/generated/textures/), each a seamless 2x2 block of tiles for the four variants of a kind
|
||||
// (variant k = quarter k % 2, k / 2); the world lays the quarters out by the tiles' positions
|
||||
// (see hd_tile), so a floor repeats only every two tiles and has no seams.
|
||||
pub const HD_TILE: u32 = 64;
|
||||
pub const ATLAS_HD_W: f32 = (ATLAS_COLS * HD_TILE) as f32;
|
||||
pub const ATLAS_HD_H: f32 = (ATLAS_ROWS * HD_TILE) as f32;
|
||||
const HD_TILES: [(u32, &[u8]); 7] = [
|
||||
(T_FLOOR, include_bytes!("../assets/textures/floor.png")),
|
||||
(T_CLAIM, include_bytes!("../assets/textures/claimed.png")),
|
||||
(T_EARTH, include_bytes!("../assets/textures/earth.png")),
|
||||
(T_ROCK, include_bytes!("../assets/textures/rock.png")),
|
||||
(T_GOLD, include_bytes!("../assets/textures/gold.png")),
|
||||
(T_WATER, include_bytes!("../assets/textures/water.png")),
|
||||
(T_LAVA, include_bytes!("../assets/textures/lava.png")),
|
||||
];
|
||||
|
||||
// whether a cell has a generated high-res texture (its variants are laid out by position)
|
||||
pub fn hd_tile(id: u32) -> bool {
|
||||
HD_TILES.iter().any(|&(base, _)| id / 4 * 4 == base)
|
||||
}
|
||||
|
||||
fn gen_hd_atlas() -> Image {
|
||||
let small = gen_atlas();
|
||||
let k = HD_TILE / 16;
|
||||
let mut img = Image::gen_image_color(ATLAS_HD_W as u16, ATLAS_HD_H as u16, Color::new(0.0, 0.0, 0.0, 0.0));
|
||||
for y in 0..ATLAS_HD_H as u32 {
|
||||
for x in 0..ATLAS_HD_W as u32 {
|
||||
img.set_pixel(x, y, small.get_pixel(x / k, y / k));
|
||||
}
|
||||
}
|
||||
for &(base, png) in HD_TILES.iter() {
|
||||
let src = Image::from_file_with_format(png, Some(ImageFormat::Png)).expect("hd tile");
|
||||
for v in 0..4 {
|
||||
let (sx, sy) = ((v % 2) * HD_TILE, (v / 2) * HD_TILE);
|
||||
let id = base + v;
|
||||
let (ox, oy) = ((id % ATLAS_COLS) * HD_TILE, (id / ATLAS_COLS) * HD_TILE);
|
||||
for y in 0..HD_TILE {
|
||||
for x in 0..HD_TILE {
|
||||
img.set_pixel(ox + x, oy + y, src.get_pixel(sx + x, sy + y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
img
|
||||
}
|
||||
|
||||
fn gen_atlas() -> Image {
|
||||
let mut cv = Canvas { img: Image::gen_image_color((ATLAS_COLS * 16) as u16, (ATLAS_ROWS * 16) as u16, Color::new(0.0, 0.0, 0.0, 0.0)) };
|
||||
for v in 0..4u32 {
|
||||
@@ -2360,9 +2410,11 @@ impl Gfx {
|
||||
cursors.set_filter(FilterMode::Nearest);
|
||||
let badges = Texture2D::from_image(&crate::badges::gen_badges());
|
||||
badges.set_filter(FilterMode::Nearest);
|
||||
let atlas_hd = Texture2D::from_image(&gen_hd_atlas());
|
||||
atlas_hd.set_filter(FilterMode::Nearest);
|
||||
let hd_sprites = Texture2D::from_image(&gen_hd_sprites());
|
||||
hd_sprites.set_filter(FilterMode::Nearest);
|
||||
Gfx { atlas, sprites, hd_sprites, cursors, badges, font, font_map }
|
||||
Gfx { atlas, sprites, hd_sprites, atlas_hd, cursors, badges, font, font_map }
|
||||
}
|
||||
|
||||
pub fn cell(id: u32) -> Rect {
|
||||
@@ -2632,6 +2684,39 @@ impl Gfx {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod hd_atlas_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn high_res_atlas() {
|
||||
let small = gen_atlas();
|
||||
let hd = gen_hd_atlas();
|
||||
assert_eq!((hd.width as f32, hd.height as f32), (ATLAS_HD_W, ATLAS_HD_H));
|
||||
let px = |img: &Image, x: u32, y: u32| {
|
||||
let p = img.get_pixel(x, y);
|
||||
(p.r, p.g, p.b, p.a)
|
||||
};
|
||||
// a cell without an own texture: the 16x16 one scaled up
|
||||
let (cx, cy) = ((T_TREASURY % ATLAS_COLS) * 16, (T_TREASURY / ATLAS_COLS) * 16);
|
||||
for (x, y) in [(0, 0), (5, 9), (15, 15)] {
|
||||
assert_eq!(px(&hd, (cx + x) * 4 + 2, (cy + y) * 4 + 1), px(&small, cx + x, cy + y));
|
||||
}
|
||||
// the generated ones: the quarters of their 2x2 sheet
|
||||
for &(base, png) in HD_TILES.iter() {
|
||||
let src = Image::from_file_with_format(png, Some(ImageFormat::Png)).unwrap();
|
||||
assert_eq!((src.width, src.height), (2 * HD_TILE as u16, 2 * HD_TILE as u16));
|
||||
for v in 0..4 {
|
||||
let id = base + v;
|
||||
assert!(hd_tile(id));
|
||||
let (ox, oy) = ((id % ATLAS_COLS) * HD_TILE, (id / ATLAS_COLS) * HD_TILE);
|
||||
assert_eq!(px(&hd, ox + 7, oy + 40), px(&src, (v % 2) * HD_TILE + 7, (v / 2) * HD_TILE + 40));
|
||||
}
|
||||
}
|
||||
assert!(!hd_tile(T_TREASURY));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod sprite_tests {
|
||||
use super::*;
|
||||
|
||||
+48
-6
@@ -259,6 +259,19 @@ uniform vec4 LightCol[24];
|
||||
uniform float LightCount;
|
||||
uniform highp float Time; // same precision as in the vertex shader (required for linking)
|
||||
uniform vec2 AtlasSize;
|
||||
uniform float CellSize; // texels per atlas cell (16; high-res 64)
|
||||
uniform float HdLiquid; // 1: high-res liquids (2x2 blocks of tiles, a second flowing layer)
|
||||
|
||||
// a flowing liquid's texture at world position p inside the kind's cells (uv names one of them);
|
||||
// high-res: the four variant cells form a 2x2 block that repeats every two tiles
|
||||
vec2 liquid_uv(vec2 p) {
|
||||
vec2 cell = floor(uv * AtlasSize / CellSize);
|
||||
if (HdLiquid > 0.5) {
|
||||
vec2 q = mod(floor(p), 2.0);
|
||||
cell.x = floor(cell.x / 4.0) * 4.0 + q.x + 2.0 * q.y;
|
||||
}
|
||||
return (cell + fract(p)) * CellSize / AtlasSize;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 tuv = uv;
|
||||
@@ -276,12 +289,22 @@ void main() {
|
||||
float ph2 = wpos.z * 1.9 - wpos.x * 0.5 - Time * ts * 1.3;
|
||||
vec2 slope = vec2(cos(ph1) * 1.3 * 0.6 - cos(ph2) * 0.5 * 0.4, cos(ph1) * 0.4 * 0.6 + cos(ph2) * 1.9 * 0.4);
|
||||
vec2 p = wpos.xz - dir * speed * Time + slope * (lava ? 0.05 : 0.07);
|
||||
vec2 cell = floor(uv * AtlasSize / 16.0) * 16.0;
|
||||
tuv = (cell + fract(p) * 16.0) / AtlasSize;
|
||||
tuv = liquid_uv(p);
|
||||
float ripple = sin(wpos.x * 5.0 + wpos.z * 3.0 + Time * ts * 1.4) * sin(wpos.z * 4.0 - wpos.x * 2.0 + Time * ts);
|
||||
wave = dot(slope, vec2(0.45, 0.35)) * 0.6 + ripple * 0.4;
|
||||
}
|
||||
vec4 tex = texture2D(Texture, tuv);
|
||||
if (liquid && HdLiquid > 0.5) {
|
||||
// a second, larger layer drifting slower and sideways: the surface churns instead of
|
||||
// only sliding along
|
||||
bool lava = color.a < 0.5;
|
||||
vec2 dir = wnorm.xz / max(length(wnorm.xz), 0.001);
|
||||
vec2 side = vec2(-dir.y, dir.x);
|
||||
float speed = lava ? 0.07 : 0.15;
|
||||
vec2 p2 = wpos.xz * 0.6 + vec2(0.37, 0.71) - dir * speed * Time + side * sin(Time * (lava ? 0.21 : 0.33)) * 0.3;
|
||||
vec4 tex2 = texture2D(Texture, liquid_uv(p2));
|
||||
tex = vec4(mix(tex.rgb, tex2.rgb, 0.42), 1.0);
|
||||
}
|
||||
if (tex.a < 0.5) discard;
|
||||
// see-through units (invisibility): every other pixel left out
|
||||
if (color.a < 0.37 && mod(floor(gl_FragCoord.x) + floor(gl_FragCoord.y), 2.0) < 1.0) discard;
|
||||
@@ -319,6 +342,8 @@ fn lit_material() -> Material {
|
||||
UniformDesc::new("LightCount", UniformType::Float1),
|
||||
UniformDesc::new("Time", UniformType::Float1),
|
||||
UniformDesc::new("AtlasSize", UniformType::Float2),
|
||||
UniformDesc::new("CellSize", UniformType::Float1),
|
||||
UniformDesc::new("HdLiquid", UniformType::Float1),
|
||||
],
|
||||
textures: vec![],
|
||||
},
|
||||
@@ -766,7 +791,8 @@ fn block_shell(mk: &mut MB, map: &Map, x: i32, z: i32, n: usize, fill: [u8; 4],
|
||||
}
|
||||
}
|
||||
|
||||
fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &Texture2D, wall_sub: usize) -> [Option<Mesh>; 4] {
|
||||
// `hd`: the high-res atlas; its generated textures are laid out by the tiles' positions
|
||||
fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &Texture2D, wall_sub: usize, hd: bool) -> [Option<Mesh>; 4] {
|
||||
let mut mb = MB::new();
|
||||
let ov = MB::new(); // textured overlays (currently unused)
|
||||
let mut ln = MB::new();
|
||||
@@ -784,6 +810,9 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T
|
||||
if t.kind == Kind::Mushroom && t.res & MUSH_GROVE != 0 {
|
||||
tex = crate::gfx::T_MOSS + (t.var as u32 & 3);
|
||||
}
|
||||
if hd && crate::gfx::hd_tile(tex) {
|
||||
tex = tex / 4 * 4 + (x & 1) as u32 + (z & 1) as u32 * 2;
|
||||
}
|
||||
let (ua, ub) = cell_uv(tex);
|
||||
let (xf, zf) = (x as f32, z as f32);
|
||||
if !map.shown(idx(x, z)) {
|
||||
@@ -1202,6 +1231,7 @@ pub struct R3d {
|
||||
pub pixel_rows: u32, // pixel look: the world is rendered this many rows high (0: off)
|
||||
target: Option<(RenderTarget, u32, u32)>, // low resolution render target
|
||||
lava: Vec<f32>, // lava light per tile (see lava_field)
|
||||
chunks_hd: bool, // the chunks were built with the high-res atlas
|
||||
}
|
||||
|
||||
// The 3D resolution setting refers to a window 1080 device pixels high: smaller windows use a
|
||||
@@ -1252,7 +1282,7 @@ fn chunk_sig(map: &Map, cx: i32, cz: i32) -> u64 {
|
||||
|
||||
impl R3d {
|
||||
pub fn new() -> R3d {
|
||||
R3d { lit: lit_material(), glow: glow_material(), opaque: opaque_material(), chunks: Vec::new(), ncx: 0, ncz: 0, outlines: true, wall_sub: 3, max_lights: MAX_LIGHTS, pixel_rows: crate::settings::PIXEL_ART_ROWS as u32, target: None, lava: Vec::new() }
|
||||
R3d { lit: lit_material(), glow: glow_material(), opaque: opaque_material(), chunks: Vec::new(), ncx: 0, ncz: 0, outlines: true, wall_sub: 3, max_lights: MAX_LIGHTS, pixel_rows: crate::settings::PIXEL_ART_ROWS as u32, target: None, lava: Vec::new(), chunks_hd: false }
|
||||
}
|
||||
|
||||
// Changing the wall detail rebuilds all chunks
|
||||
@@ -1292,6 +1322,14 @@ impl R3d {
|
||||
ch.sig = 0;
|
||||
}
|
||||
}
|
||||
// the graphics style changed: the chunks are built again with the other atlas
|
||||
let hd = self.pixel_rows >= crate::settings::HIGH_RES_ROWS as u32;
|
||||
if hd != self.chunks_hd {
|
||||
self.chunks_hd = hd;
|
||||
for ch in self.chunks.iter_mut() {
|
||||
ch.sig = 0;
|
||||
}
|
||||
}
|
||||
if view_side() != side {
|
||||
// another master looks: the marks change colour
|
||||
VIEW_SIDE.with(|v| v.set(side));
|
||||
@@ -1316,7 +1354,8 @@ impl R3d {
|
||||
lava_fresh = true;
|
||||
}
|
||||
let ch = &mut self.chunks[(cz * self.ncx + cx) as usize];
|
||||
let [m, o, l, k] = build_chunk(&g.map, a, &self.lava, cx, cz, &gfx.atlas, self.wall_sub);
|
||||
let atlas = if self.chunks_hd { &gfx.atlas_hd } else { &gfx.atlas };
|
||||
let [m, o, l, k] = build_chunk(&g.map, a, &self.lava, cx, cz, atlas, self.wall_sub, self.chunks_hd);
|
||||
ch.mesh = m;
|
||||
ch.overlay = o;
|
||||
ch.lines = l;
|
||||
@@ -1548,7 +1587,10 @@ impl R3d {
|
||||
self.lit.set_uniform("LightCount", count);
|
||||
// wrapped so the shader keeps its precision (the flow speeds are whole tiles per period)
|
||||
self.lit.set_uniform("Time", (time % 1000.0) as f32);
|
||||
self.lit.set_uniform("AtlasSize", vec2(ATLAS_W, ATLAS_H));
|
||||
let hd_tiles = self.chunks_hd;
|
||||
self.lit.set_uniform("AtlasSize", if hd_tiles { vec2(crate::gfx::ATLAS_HD_W, crate::gfx::ATLAS_HD_H) } else { vec2(ATLAS_W, ATLAS_H) });
|
||||
self.lit.set_uniform("CellSize", if hd_tiles { crate::gfx::HD_TILE as f32 } else { 16.0 });
|
||||
self.lit.set_uniform("HdLiquid", if hd_tiles { 1.0f32 } else { 0.0 });
|
||||
gl_use_material(&self.lit);
|
||||
for cz in 0..self.ncz {
|
||||
for cx in 0..self.ncx {
|
||||
|
||||
Reference in new issue
Block a user