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 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 12:13:52 +01:00
1 parent 05d9cf8824
commit 036c13577c
3 files changed
+38 -3

No files matched your search

+1 -1
View File
@@ -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** (█ ▌ ░) - 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) - 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 - 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 - 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. - 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) - Repository language: **English** (code, comments, docs, commit messages)
+9 -1
View File
@@ -111,7 +111,15 @@ fn item_value(it: Item, s: &Settings) -> String {
format!("{}x", s.ui_factor) 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::GameSpeed => format!("{}x", s.game_speed as i32),
Item::Hints => onoff(s.hints), Item::Hints => onoff(s.hints),
Item::TouchUi => match s.touch_ui { Item::TouchUi => match s.touch_ui {
+28 -1
View File
@@ -762,6 +762,18 @@ pub struct R3d {
target: Option<(RenderTarget, u32, u32)>, // low resolution render target 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 { fn rgba(u: u32) -> Color {
Color::from_rgba(u as u8, (u >> 8) as u8, (u >> 16) as u8, (u >> 24) as u8) 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); self.update_chunks(g, gfx, &m);
EYE.with(|e| e.set(cam.eye_dir())); EYE.with(|e| e.set(cam.eye_dir()));
// pixel look: render the world at 1/div resolution and scale it up by exactly div // 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 d = crate::ui::dpr();
let (pw, ph) = ((screen_width() * d).round(), (screen_height() * d).round()); let (pw, ph) = ((screen_width() * d).round(), (screen_height() * d).round());
let div = effective_div(self.render_div, ph);
let mut cam3 = cam.camera(); let mut cam3 = cam.camera();
if div > 1 { if div > 1 {
let (tw, th) = ((pw / div as f32).ceil() as u32, (ph / div as f32).ceil() as u32); 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], 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);
}
}