From 036c13577c46749b686c8b1a2b423676074fcbb7 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Tue, 3 Nov 2026 12:13:52 +0100 Subject: [PATCH] 3D pixel look follows the window size The 3D resolution setting refers to a window 1080 device pixels high; smaller windows use a proportionally smaller divisor (1/1 stays full resolution). Settings show the divisor in effect. Co-Authored-By: Claude Opus 5.5 --- DESIGN.md | 2 +- src/menu.rs | 10 +++++++++- src/render3d.rs | 29 ++++++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 9a6734b..92d6f33 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -9,7 +9,7 @@ An original dungeon management game (genre: dungeon building & defence) that run - All UI windows use **CP437 frames** (single lines for panels, double lines for dialogs); **all bars are CP437 block characters** (█ ▌ ░) - Text only in **fixed integer sizes**: 1×, 2× and 4× the 6×8 glyph size, times an integer **interface factor** (automatic from the device resolution, or 1×–4× in the settings) - The interface is drawn in **device pixels** (high DPI) and laid out on the **character grid**, so every font pixel has exactly the same size -- **3D resolution** setting: the world can be rendered at 1/1 down to 1/16 resolution and scaled up by exactly that factor (pixel look); the interface stays sharp +- **3D resolution** setting: the world can be rendered at 1/1 down to 1/16 resolution and scaled up by exactly that factor (pixel look); the setting refers to a window 1080 device pixels high and scales with the window size, so the 3D pixels keep their size relative to the window; the interface stays sharp - Sound: event system + macroquad audio prepared (volume, mute key M); sounds will be generated later - Languages: all in-game texts live in **language files** (`lang/*.lang`, key = text); English is the reference/fallback, German included, more can be added. The language is chosen on first start and stored in the browser. - Repository language: **English** (code, comments, docs, commit messages) diff --git a/src/menu.rs b/src/menu.rs index 39aec25..fb63855 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -111,7 +111,15 @@ fn item_value(it: Item, s: &Settings) -> String { format!("{}x", s.ui_factor) } } - Item::RenderScale => format!("1/{}", s.render_scale.max(1)), + Item::RenderScale => { + // the setting is for a 1080 pixel high window; show what applies to this one + let now = crate::render3d::effective_div(s.render_scale.max(1) as u32, crate::ui::sh()); + if now as u8 == s.render_scale.max(1) { + format!("1/{}", s.render_scale.max(1)) + } else { + format!("1/{} (1/{})", s.render_scale.max(1), now) + } + } Item::GameSpeed => format!("{}x", s.game_speed as i32), Item::Hints => onoff(s.hints), Item::TouchUi => match s.touch_ui { diff --git a/src/render3d.rs b/src/render3d.rs index fdb408f..0d758d4 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -762,6 +762,18 @@ pub struct R3d { target: Option<(RenderTarget, u32, u32)>, // low resolution render target } +// The 3D resolution setting refers to a window 1080 device pixels high: smaller windows use a +// proportionally smaller divisor, so the 3D pixels keep their size relative to the window. +// 1 (full resolution) stays 1. +pub const RENDER_REF_HEIGHT: f32 = 1080.0; + +pub fn effective_div(setting: u32, height: f32) -> u32 { + if setting <= 1 { + return 1; + } + ((setting as f32 * height / RENDER_REF_HEIGHT).round() as u32).max(1) +} + fn rgba(u: u32) -> Color { Color::from_rgba(u as u8, (u >> 8) as u8, (u >> 16) as u8, (u >> 24) as u8) } @@ -950,9 +962,9 @@ impl R3d { self.update_chunks(g, gfx, &m); EYE.with(|e| e.set(cam.eye_dir())); // pixel look: render the world at 1/div resolution and scale it up by exactly div - let div = self.render_div.max(1); let d = crate::ui::dpr(); let (pw, ph) = ((screen_width() * d).round(), (screen_height() * d).round()); + let div = effective_div(self.render_div, ph); let mut cam3 = cam.camera(); if div > 1 { let (tw, th) = ((pw / div as f32).ceil() as u32, (ph / div as f32).ceil() as u32); @@ -1554,3 +1566,18 @@ pub fn minimap_color(tile: &Tile) -> [u8; 4] { Kind::Shard => [255, 60, 120, 255], } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn pixel_size_follows_the_window() { + assert_eq!(effective_div(1, 400.0), 1); + assert_eq!(effective_div(4, 1080.0), 4); + assert_eq!(effective_div(4, 540.0), 2); + assert_eq!(effective_div(4, 2160.0), 8); + assert_eq!(effective_div(16, 200.0), 3); + assert_eq!(effective_div(2, 100.0), 1); + } +}