High-res textures built on first use: no cost in the pixel-art style, the 128x128 atlas only when zoomed in
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fb71009110
commit
6ff1cc5f8d
2 files changed
+91
-20
No files matched your search
+83
-17
@@ -101,11 +101,11 @@ pub const T_EGG: u32 = 222; // 2 variants: a painted egg (Easter)
|
||||
pub struct Gfx {
|
||||
pub atlas: Texture2D,
|
||||
pub sprites: Texture2D,
|
||||
pub hd_sprites: Texture2D, // detailed sprites, 64x64 level (see HD_SPRITES, hd_picture)
|
||||
pub hd_sprites32: Texture2D, // the same, 32x32 level
|
||||
pub gear: Texture2D, // their gear parts (see GEAR_HELMET etc.)
|
||||
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 hd_sprites: LazyTex, // detailed sprites, 64x64 level (see HD_SPRITES, hd_picture)
|
||||
pub hd_sprites32: LazyTex, // the same, 32x32 level
|
||||
pub gear: LazyTex, // their gear parts (see GEAR_HELMET etc.)
|
||||
pub atlas_hd: LazyTex, // the tile atlas of the high-res style, 64x64 tiles (see HD_TILES)
|
||||
pub atlas_hd2: LazyTex, // the same with 128x128 tiles (close up)
|
||||
pub materials: [Texture2D; 9], // furniture materials, high-res (see crate::props::Mat)
|
||||
pub materials_px: [Texture2D; 9], // the same for the pixel-art style (16x16)
|
||||
pub cursors: Texture2D, // mouse pointer frames, 16x16 each (see CURSOR_*)
|
||||
@@ -347,6 +347,32 @@ pub fn high_res_ui() -> bool {
|
||||
HIGH_RES_UI.with(|h| h.get())
|
||||
}
|
||||
|
||||
// a texture with nearest filtering
|
||||
fn tex(img: &Image) -> Texture2D {
|
||||
let t = Texture2D::from_image(img);
|
||||
t.set_filter(FilterMode::Nearest);
|
||||
t
|
||||
}
|
||||
|
||||
// a texture built on first use (it derefs to the texture)
|
||||
pub struct LazyTex {
|
||||
cell: std::cell::OnceCell<Texture2D>,
|
||||
make: Box<dyn Fn() -> Texture2D>,
|
||||
}
|
||||
|
||||
impl LazyTex {
|
||||
fn new(make: impl Fn() -> Texture2D + 'static) -> LazyTex {
|
||||
LazyTex { cell: std::cell::OnceCell::new(), make: Box::new(make) }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for LazyTex {
|
||||
type Target = Texture2D;
|
||||
fn deref(&self) -> &Texture2D {
|
||||
self.cell.get_or_init(|| (self.make)())
|
||||
}
|
||||
}
|
||||
|
||||
// ---- furniture materials (crate::props::Mat order): grey detail the furniture's colours tint
|
||||
// (the page keeps its colours), 64x64 and 16x16; see art/generated/textures/materials/
|
||||
macro_rules! material {
|
||||
@@ -2939,17 +2965,30 @@ 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(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 (hd64, hd32) = gen_hd_sprites();
|
||||
let hd_sprites = Texture2D::from_image(&hd64);
|
||||
hd_sprites.set_filter(FilterMode::Nearest);
|
||||
let hd_sprites32 = Texture2D::from_image(&hd32);
|
||||
hd_sprites32.set_filter(FilterMode::Nearest);
|
||||
let gear = Texture2D::from_image(&gen_gear());
|
||||
gear.set_filter(FilterMode::Nearest);
|
||||
// the high-res textures are built when first used (the pixel-art style never needs them)
|
||||
let atlas_hd = LazyTex::new(|| tex(&gen_hd_atlas(HD_TILE)));
|
||||
let atlas_hd2 = LazyTex::new(|| tex(&gen_hd_atlas(HD_TILE2)));
|
||||
// both levels of the detailed sprites come from one pass (which also measures the gear anchors)
|
||||
let both: std::rc::Rc<std::cell::OnceCell<(Texture2D, Texture2D)>> = Default::default();
|
||||
let both2 = both.clone();
|
||||
let make = move |b: &std::cell::OnceCell<(Texture2D, Texture2D)>| {
|
||||
b.get_or_init(|| {
|
||||
let (a, c) = gen_hd_sprites();
|
||||
(tex(&a), tex(&c))
|
||||
})
|
||||
.clone()
|
||||
};
|
||||
let hd_sprites = LazyTex::new(move || make(&both).0);
|
||||
let hd_sprites32 = LazyTex::new(move || {
|
||||
both2
|
||||
.get_or_init(|| {
|
||||
let (a, c) = gen_hd_sprites();
|
||||
(tex(&a), tex(&c))
|
||||
})
|
||||
.1
|
||||
.clone()
|
||||
});
|
||||
let gear = LazyTex::new(|| tex(&gen_gear()));
|
||||
let mat = |b: &[u8]| {
|
||||
let t = Texture2D::from_file_with_format(b, Some(ImageFormat::Png));
|
||||
t.set_filter(FilterMode::Nearest);
|
||||
@@ -3097,7 +3136,7 @@ impl Gfx {
|
||||
// an atlas cell in the interface (tool and room buttons); the high-res style shows the
|
||||
// high-res atlas, so the room buttons show the rooms' new floors
|
||||
pub fn draw_cell(&self, id: u32, x: f32, y: f32, w: f32, h: f32, color: Color) {
|
||||
let (tex, k) = if HIGH_RES_UI.with(|h| h.get()) { (&self.atlas_hd, (HD_TILE / 16) as f32) } else { (&self.atlas, 1.0) };
|
||||
let (tex, k): (&Texture2D, f32) = if HIGH_RES_UI.with(|h| h.get()) { (&self.atlas_hd, (HD_TILE / 16) as f32) } else { (&self.atlas, 1.0) };
|
||||
let r = Self::cell(id);
|
||||
draw_texture_ex(
|
||||
tex,
|
||||
@@ -3357,6 +3396,33 @@ mod hd_atlas_tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod startup_timing {
|
||||
use super::*;
|
||||
|
||||
// `cargo test --release --bins startup_timing -- --ignored --nocapture`: how long building
|
||||
// the textures takes at the start
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn startup_timing() {
|
||||
let t = std::time::Instant::now();
|
||||
let _ = gen_atlas();
|
||||
println!("atlas 16: {:?}", t.elapsed());
|
||||
let t = std::time::Instant::now();
|
||||
let _ = gen_sprites();
|
||||
println!("sprites: {:?}", t.elapsed());
|
||||
let t = std::time::Instant::now();
|
||||
let _ = gen_hd_atlas(HD_TILE);
|
||||
println!("hd atlas 64: {:?}", t.elapsed());
|
||||
let t = std::time::Instant::now();
|
||||
let _ = gen_hd_atlas(HD_TILE2);
|
||||
println!("hd atlas 128: {:?}", t.elapsed());
|
||||
let t = std::time::Instant::now();
|
||||
let _ = gen_hd_sprites();
|
||||
println!("hd sprites: {:?}", t.elapsed());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod atlas_dump {
|
||||
use super::*;
|
||||
|
||||
+8
-3
@@ -1406,7 +1406,7 @@ impl R3d {
|
||||
lava_fresh = true;
|
||||
}
|
||||
let ch = &mut self.chunks[(cz * self.ncx + cx) as usize];
|
||||
let atlas = if self.chunks_hd { &gfx.atlas_hd } else { &gfx.atlas };
|
||||
let atlas: &Texture2D = 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;
|
||||
@@ -1652,8 +1652,8 @@ impl R3d {
|
||||
};
|
||||
let atlas = match tile {
|
||||
16 => &gfx.atlas,
|
||||
crate::gfx::HD_TILE => &gfx.atlas_hd,
|
||||
_ => &gfx.atlas_hd2,
|
||||
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() {
|
||||
@@ -1808,6 +1808,11 @@ impl R3d {
|
||||
// high) gives every texel nearly a pixel; fewer pixels would only turn it into noise
|
||||
let hd = self.pixel_rows >= crate::settings::HIGH_RES_ROWS as u32;
|
||||
let px_per_tile = world_rows / cam.view_h().max(0.1);
|
||||
if hd {
|
||||
// the detailed sprites are built on first use; that also measures the gear anchors,
|
||||
// which the gear below needs
|
||||
let _: &Texture2D = &gfx.hd_sprites;
|
||||
}
|
||||
for e in &g.ents {
|
||||
if !e.alive || e.held || unit_hidden(g, e) {
|
||||
continue;
|
||||
|
||||
Reference in new issue
Block a user