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 struct Gfx {
|
||||||
pub atlas: Texture2D,
|
pub atlas: Texture2D,
|
||||||
pub sprites: Texture2D,
|
pub sprites: Texture2D,
|
||||||
pub hd_sprites: Texture2D, // detailed sprites, 64x64 level (see HD_SPRITES, hd_picture)
|
pub hd_sprites: LazyTex, // detailed sprites, 64x64 level (see HD_SPRITES, hd_picture)
|
||||||
pub hd_sprites32: Texture2D, // the same, 32x32 level
|
pub hd_sprites32: LazyTex, // the same, 32x32 level
|
||||||
pub gear: Texture2D, // their gear parts (see GEAR_HELMET etc.)
|
pub gear: LazyTex, // 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_hd: LazyTex, // 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 atlas_hd2: LazyTex, // the same with 128x128 tiles (close up)
|
||||||
pub materials: [Texture2D; 9], // furniture materials, high-res (see crate::props::Mat)
|
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 materials_px: [Texture2D; 9], // the same for the pixel-art style (16x16)
|
||||||
pub cursors: Texture2D, // mouse pointer frames, 16x16 each (see CURSOR_*)
|
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())
|
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
|
// ---- 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/
|
// (the page keeps its colours), 64x64 and 16x16; see art/generated/textures/materials/
|
||||||
macro_rules! material {
|
macro_rules! material {
|
||||||
@@ -2939,17 +2965,30 @@ impl Gfx {
|
|||||||
cursors.set_filter(FilterMode::Nearest);
|
cursors.set_filter(FilterMode::Nearest);
|
||||||
let badges = Texture2D::from_image(&crate::badges::gen_badges());
|
let badges = Texture2D::from_image(&crate::badges::gen_badges());
|
||||||
badges.set_filter(FilterMode::Nearest);
|
badges.set_filter(FilterMode::Nearest);
|
||||||
let atlas_hd = Texture2D::from_image(&gen_hd_atlas(HD_TILE));
|
// the high-res textures are built when first used (the pixel-art style never needs them)
|
||||||
atlas_hd.set_filter(FilterMode::Nearest);
|
let atlas_hd = LazyTex::new(|| tex(&gen_hd_atlas(HD_TILE)));
|
||||||
let atlas_hd2 = Texture2D::from_image(&gen_hd_atlas(HD_TILE2));
|
let atlas_hd2 = LazyTex::new(|| tex(&gen_hd_atlas(HD_TILE2)));
|
||||||
atlas_hd2.set_filter(FilterMode::Nearest);
|
// both levels of the detailed sprites come from one pass (which also measures the gear anchors)
|
||||||
let (hd64, hd32) = gen_hd_sprites();
|
let both: std::rc::Rc<std::cell::OnceCell<(Texture2D, Texture2D)>> = Default::default();
|
||||||
let hd_sprites = Texture2D::from_image(&hd64);
|
let both2 = both.clone();
|
||||||
hd_sprites.set_filter(FilterMode::Nearest);
|
let make = move |b: &std::cell::OnceCell<(Texture2D, Texture2D)>| {
|
||||||
let hd_sprites32 = Texture2D::from_image(&hd32);
|
b.get_or_init(|| {
|
||||||
hd_sprites32.set_filter(FilterMode::Nearest);
|
let (a, c) = gen_hd_sprites();
|
||||||
let gear = Texture2D::from_image(&gen_gear());
|
(tex(&a), tex(&c))
|
||||||
gear.set_filter(FilterMode::Nearest);
|
})
|
||||||
|
.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 mat = |b: &[u8]| {
|
||||||
let t = Texture2D::from_file_with_format(b, Some(ImageFormat::Png));
|
let t = Texture2D::from_file_with_format(b, Some(ImageFormat::Png));
|
||||||
t.set_filter(FilterMode::Nearest);
|
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
|
// 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
|
// 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) {
|
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);
|
let r = Self::cell(id);
|
||||||
draw_texture_ex(
|
draw_texture_ex(
|
||||||
tex,
|
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)]
|
#[cfg(test)]
|
||||||
mod atlas_dump {
|
mod atlas_dump {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
+8
-3
@@ -1406,7 +1406,7 @@ impl R3d {
|
|||||||
lava_fresh = true;
|
lava_fresh = true;
|
||||||
}
|
}
|
||||||
let ch = &mut self.chunks[(cz * self.ncx + cx) as usize];
|
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);
|
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.mesh = m;
|
||||||
ch.overlay = o;
|
ch.overlay = o;
|
||||||
@@ -1652,8 +1652,8 @@ impl R3d {
|
|||||||
};
|
};
|
||||||
let atlas = match tile {
|
let atlas = match tile {
|
||||||
16 => &gfx.atlas,
|
16 => &gfx.atlas,
|
||||||
crate::gfx::HD_TILE => &gfx.atlas_hd,
|
crate::gfx::HD_TILE => &*gfx.atlas_hd,
|
||||||
_ => &gfx.atlas_hd2,
|
_ => &*gfx.atlas_hd2,
|
||||||
};
|
};
|
||||||
for ch in self.chunks.iter_mut() {
|
for ch in self.chunks.iter_mut() {
|
||||||
for mesh in [&mut ch.mesh, &mut ch.overlay].into_iter().flatten() {
|
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
|
// 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 hd = self.pixel_rows >= crate::settings::HIGH_RES_ROWS as u32;
|
||||||
let px_per_tile = world_rows / cam.view_h().max(0.1);
|
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 {
|
for e in &g.ents {
|
||||||
if !e.alive || e.held || unit_hidden(g, e) {
|
if !e.alive || e.held || unit_hidden(g, e) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in new issue
Block a user