diff --git a/art/generated/textures/PROMPTS.md b/art/generated/textures/PROMPTS.md index 6312ca2..0b7f602 100644 --- a/art/generated/textures/PROMPTS.md +++ b/art/generated/textures/PROMPTS.md @@ -63,3 +63,14 @@ Molten lava seen from above, glowing orange and yellow molten rock flowing in sw Water and lava flow as before (the shader moves the texture along the river and lets the surface swell); in the high-res style a second, larger layer of the same texture drifts slower and sideways over it, so the surface churns. + +## 128x128 (2026-11-07) + +The same raw pictures also give 128x128 tiles for close views (`HD_TILE2`; the world shows them +when a tile gets nearly 128 pixels, else the 64x64 ones): + +```bash +python3 tools/make_tile.py -raw.png --size 256 --colors 48 --band 28 +``` + +`-256.png` is copied to `assets/textures/-256.png`. diff --git a/art/generated/textures/claimed-256-preview.png b/art/generated/textures/claimed-256-preview.png new file mode 100644 index 0000000..3bb935f Binary files /dev/null and b/art/generated/textures/claimed-256-preview.png differ diff --git a/art/generated/textures/claimed-256.png b/art/generated/textures/claimed-256.png new file mode 100644 index 0000000..bef1c06 Binary files /dev/null and b/art/generated/textures/claimed-256.png differ diff --git a/art/generated/textures/earth-256-preview.png b/art/generated/textures/earth-256-preview.png new file mode 100644 index 0000000..76f99db Binary files /dev/null and b/art/generated/textures/earth-256-preview.png differ diff --git a/art/generated/textures/earth-256.png b/art/generated/textures/earth-256.png new file mode 100644 index 0000000..53c2363 Binary files /dev/null and b/art/generated/textures/earth-256.png differ diff --git a/art/generated/textures/floor-256-preview.png b/art/generated/textures/floor-256-preview.png new file mode 100644 index 0000000..22fe8f6 Binary files /dev/null and b/art/generated/textures/floor-256-preview.png differ diff --git a/art/generated/textures/floor-256.png b/art/generated/textures/floor-256.png new file mode 100644 index 0000000..bf2fe93 Binary files /dev/null and b/art/generated/textures/floor-256.png differ diff --git a/art/generated/textures/gold-256-preview.png b/art/generated/textures/gold-256-preview.png new file mode 100644 index 0000000..301362f Binary files /dev/null and b/art/generated/textures/gold-256-preview.png differ diff --git a/art/generated/textures/gold-256.png b/art/generated/textures/gold-256.png new file mode 100644 index 0000000..7321cf9 Binary files /dev/null and b/art/generated/textures/gold-256.png differ diff --git a/art/generated/textures/lava-256-preview.png b/art/generated/textures/lava-256-preview.png new file mode 100644 index 0000000..aff0b5a Binary files /dev/null and b/art/generated/textures/lava-256-preview.png differ diff --git a/art/generated/textures/lava-256.png b/art/generated/textures/lava-256.png new file mode 100644 index 0000000..d05cdb6 Binary files /dev/null and b/art/generated/textures/lava-256.png differ diff --git a/art/generated/textures/rock-256-preview.png b/art/generated/textures/rock-256-preview.png new file mode 100644 index 0000000..14271d0 Binary files /dev/null and b/art/generated/textures/rock-256-preview.png differ diff --git a/art/generated/textures/rock-256.png b/art/generated/textures/rock-256.png new file mode 100644 index 0000000..1b1ec82 Binary files /dev/null and b/art/generated/textures/rock-256.png differ diff --git a/art/generated/textures/water-256-preview.png b/art/generated/textures/water-256-preview.png new file mode 100644 index 0000000..d72c977 Binary files /dev/null and b/art/generated/textures/water-256-preview.png differ diff --git a/art/generated/textures/water-256.png b/art/generated/textures/water-256.png new file mode 100644 index 0000000..b5d0aea Binary files /dev/null and b/art/generated/textures/water-256.png differ diff --git a/assets/textures/claimed-256.png b/assets/textures/claimed-256.png new file mode 100644 index 0000000..bef1c06 Binary files /dev/null and b/assets/textures/claimed-256.png differ diff --git a/assets/textures/earth-256.png b/assets/textures/earth-256.png new file mode 100644 index 0000000..53c2363 Binary files /dev/null and b/assets/textures/earth-256.png differ diff --git a/assets/textures/floor-256.png b/assets/textures/floor-256.png new file mode 100644 index 0000000..bf2fe93 Binary files /dev/null and b/assets/textures/floor-256.png differ diff --git a/assets/textures/gold-256.png b/assets/textures/gold-256.png new file mode 100644 index 0000000..7321cf9 Binary files /dev/null and b/assets/textures/gold-256.png differ diff --git a/assets/textures/lava-256.png b/assets/textures/lava-256.png new file mode 100644 index 0000000..d05cdb6 Binary files /dev/null and b/assets/textures/lava-256.png differ diff --git a/assets/textures/rock-256.png b/assets/textures/rock-256.png new file mode 100644 index 0000000..1b1ec82 Binary files /dev/null and b/assets/textures/rock-256.png differ diff --git a/assets/textures/water-256.png b/assets/textures/water-256.png new file mode 100644 index 0000000..b5d0aea Binary files /dev/null and b/assets/textures/water-256.png differ diff --git a/src/gfx.rs b/src/gfx.rs index 0fa89a5..11a7eda 100644 --- a/src/gfx.rs +++ b/src/gfx.rs @@ -5,7 +5,7 @@ use crate::map::{Kind, Owner}; use macroquad::prelude::*; pub const CELL: f32 = 16.0; -const ATLAS_COLS: u32 = 16; +pub const ATLAS_COLS: u32 = 16; pub const ATLAS_ROWS: u32 = 14; // atlas indices (4 variants each) @@ -99,7 +99,8 @@ 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 atlas_hd: Texture2D, // the tile atlas of the high-res style, 64x64 tiles (see HD_TILES) + pub atlas_hd2: Texture2D, // the same with 128x128 tiles (close up) 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 @@ -327,46 +328,49 @@ fn earth_px(x: i32, y: i32, v: u32) -> Color { } // ---- 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. +// The high-res style's atlases: the same cells as the 16x16 atlas, HD_TILE (64) or HD_TILE2 (128) +// texels each; the world shows the 128 one close up, where a tile gets that many pixels (two +// levels like the sprites: neither loses pixels nor flickers). 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")), +pub const HD_TILE2: u32 = 128; +const HD_TILES: [(u32, &[u8], &[u8]); 7] = [ + (T_FLOOR, include_bytes!("../assets/textures/floor.png"), include_bytes!("../assets/textures/floor-256.png")), + (T_CLAIM, include_bytes!("../assets/textures/claimed.png"), include_bytes!("../assets/textures/claimed-256.png")), + (T_EARTH, include_bytes!("../assets/textures/earth.png"), include_bytes!("../assets/textures/earth-256.png")), + (T_ROCK, include_bytes!("../assets/textures/rock.png"), include_bytes!("../assets/textures/rock-256.png")), + (T_GOLD, include_bytes!("../assets/textures/gold.png"), include_bytes!("../assets/textures/gold-256.png")), + (T_WATER, include_bytes!("../assets/textures/water.png"), include_bytes!("../assets/textures/water-256.png")), + (T_LAVA, include_bytes!("../assets/textures/lava.png"), include_bytes!("../assets/textures/lava-256.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) + HD_TILES.iter().any(|&(base, _, _)| id / 4 * 4 == base) } -fn gen_hd_atlas() -> Image { +// a high-res atlas with `tile` x `tile` texel cells (HD_TILE or HD_TILE2) +fn gen_hd_atlas(tile: u32) -> 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 { + let k = tile / 16; + let (w, h) = (ATLAS_COLS * tile, ATLAS_ROWS * tile); + let mut img = Image::gen_image_color(w as u16, h as u16, Color::new(0.0, 0.0, 0.0, 0.0)); + for y in 0..h { + for x in 0..w { 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 &(base, png, png2) in HD_TILES.iter() { + let src = Image::from_file_with_format(if tile == HD_TILE2 { png2 } else { png }, Some(ImageFormat::Png)).expect("hd tile"); for v in 0..4 { - let (sx, sy) = ((v % 2) * HD_TILE, (v / 2) * HD_TILE); + let (sx, sy) = ((v % 2) * tile, (v / 2) * 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 { + let (ox, oy) = ((id % ATLAS_COLS) * tile, (id / ATLAS_COLS) * tile); + for y in 0..tile { + for x in 0..tile { img.set_pixel(ox + x, oy + y, src.get_pixel(sx + x, sy + y)); } } @@ -2410,11 +2414,13 @@ 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()); + let atlas_hd = Texture2D::from_image(&gen_hd_atlas(HD_TILE)); atlas_hd.set_filter(FilterMode::Nearest); + let atlas_hd2 = Texture2D::from_image(&gen_hd_atlas(HD_TILE2)); + atlas_hd2.set_filter(FilterMode::Nearest); let hd_sprites = Texture2D::from_image(&gen_hd_sprites()); hd_sprites.set_filter(FilterMode::Nearest); - Gfx { atlas, sprites, hd_sprites, atlas_hd, cursors, badges, font, font_map } + Gfx { atlas, sprites, hd_sprites, atlas_hd, atlas_hd2, cursors, badges, font, font_map } } pub fn cell(id: u32) -> Rect { @@ -2691,8 +2697,10 @@ mod hd_atlas_tests { #[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)); + for tile in [HD_TILE, HD_TILE2] { + let hd = gen_hd_atlas(tile); + let k = tile / 16; + assert_eq!((hd.width as u32, hd.height as u32), (ATLAS_COLS * tile, ATLAS_ROWS * tile)); let px = |img: &Image, x: u32, y: u32| { let p = img.get_pixel(x, y); (p.r, p.g, p.b, p.a) @@ -2700,19 +2708,20 @@ mod hd_atlas_tests { // 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)); + assert_eq!(px(&hd, (cx + x) * k + 2, (cy + y) * k + 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 &(base, png, png2) in HD_TILES.iter() { + let src = Image::from_file_with_format(if tile == HD_TILE2 { png2 } else { png }, Some(ImageFormat::Png)).unwrap(); + assert_eq!((src.width as u32, src.height as u32), (2 * tile, 2 * tile)); 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)); + let (ox, oy) = ((id % ATLAS_COLS) * tile, (id / ATLAS_COLS) * tile); + assert_eq!(px(&hd, ox + 7, oy + 40), px(&src, (v % 2) * tile + 7, (v / 2) * tile + 40)); } } + } assert!(!hd_tile(T_TREASURY)); } } diff --git a/src/render3d.rs b/src/render3d.rs index c7fdba9..f21ec2a 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -1587,9 +1587,31 @@ 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); + // high-res: the 128x128 tiles where a tile gets nearly that many pixels, else the 64x64 + // ones (same layout: the chunks keep their uvs, only the texture changes) 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 }); + let rows = pix.map_or(screen_height() * screen_dpi_scale(), |(_, th, _)| th as f32); + let tile = if !hd_tiles { + 16 + } else if rows / cam.view_h().max(0.1) >= crate::gfx::HD_TILE2 as f32 * 0.85 { + crate::gfx::HD_TILE2 + } else { + crate::gfx::HD_TILE + }; + let atlas = match tile { + 16 => &gfx.atlas, + crate::gfx::HD_TILE => &gfx.atlas_hd, + _ => &gfx.atlas_hd2, + }; + for ch in self.chunks.iter_mut() { + for mesh in [&mut ch.mesh, &mut ch.overlay].into_iter().flatten() { + if mesh.texture.as_ref() != Some(atlas) { + mesh.texture = Some(atlas.clone()); + } + } + } + self.lit.set_uniform("AtlasSize", vec2((crate::gfx::ATLAS_COLS * tile) as f32, (crate::gfx::ATLAS_ROWS * tile) as f32)); + self.lit.set_uniform("CellSize", tile as f32); self.lit.set_uniform("HdLiquid", if hd_tiles { 1.0f32 } else { 0.0 }); gl_use_material(&self.lit); for cz in 0..self.ncz {