diff --git a/art/generated/textures/materials/PROMPTS.md b/art/generated/textures/materials/PROMPTS.md new file mode 100644 index 0000000..4c48d8c --- /dev/null +++ b/art/generated/textures/materials/PROMPTS.md @@ -0,0 +1,79 @@ +# Furniture materials (2026-11-07) + +Textures for the furniture boxes of `src/props.rs` (`Mat`, `mc`): every box of a material gets the +texture on all sides, tinted by the box's own colour, so the furniture keeps its colours and gains +detail (book spines with titles, a spellbook page with script, straw dummies, wood grain, ...). + +`fal-ai/flux-2-pro`, `image_size: square_hd`, `seed: 4400`; the material's sentence followed by: + +```text +Square texture seen straight on, flat even lighting, no shadows, no vignette, no border, no text unless asked. Hand-painted pixel art style for a dark-fantasy dungeon strategy game: chunky clear shapes, crisp edges, readable at small size. Fills the whole image. +``` + +## wood + +```text +Light weathered wooden planks running vertically with clear wood grain, knots and a few nail heads, in light neutral beige tones. +``` + +## stone + +```text +A rough block of light grey stone with chisel marks, small pits and a few fine cracks, in light neutral grey tones. +``` + +## iron + +```text +Light grey forged iron with hammer dents, scratches and a few rivets near the edges, in light neutral grey tones. +``` + +## cloth + +```text +Woven fabric with a visible coarse weave, soft folds and a simple embroidered border near the edges, in light neutral off-white tones. +``` + +## straw + +```text +Bundled straw and sackcloth: coarse burlap with straw sticking out, stitched seams, in light neutral beige tones. +``` + +## fur + +```text +Thick shaggy animal fur with tufts and strands, in light neutral beige-grey tones. +``` + +## book + +```text +The spine of one old leather-bound book standing upright, filling the whole image: two raised bands near the top and the bottom, a gilded frame and a short line of golden title letters in the middle, worn leather, in light neutral tones. +``` + +## paper + +```text +An open page of an old spellbook filling the whole image: yellowed parchment with many lines of dark handwritten script, a small drawn magic circle and a red initial letter, in light neutral tones. +``` + +## Processing + +`tools/make_material.py` crops (stone and the book lay on white), turns the picture into grey +detail with a mean brightness of 0.8 (the page keeps its colours) and scales to 64x64 (high-res) +and 16x16 (pixel art): + +```bash +T=tools/make_material.py +python3 $T wood-raw.png wood +python3 $T stone-raw.png stone --crop 0.15,0.15,0.75,0.6 +python3 $T iron-raw.png iron --crop 0.1,0.1,0.9,0.9 +python3 $T cloth-raw.png cloth --crop 0.12,0.12,0.88,0.88 --mean 0.85 +python3 $T straw-raw.png straw --crop 0.05,0.05,0.95,0.95 +python3 $T fur-raw.png fur +python3 $T book-raw.png book --trim --mean 0.75 +python3 $T paper-raw.png paper --color --crop 0.04,0.04,0.96,0.96 --mean 0.8 +``` + +Copied to `assets/textures/mat/`. diff --git a/art/generated/textures/materials/batch.py b/art/generated/textures/materials/batch.py new file mode 100644 index 0000000..588c709 --- /dev/null +++ b/art/generated/textures/materials/batch.py @@ -0,0 +1,16 @@ +# Material textures for the furniture (props.rs), 2026-11-07: (name, prompt). The game uses them +# as grey detail that the furniture's own colours tint. +STYLE = ("Square texture seen straight on, flat even lighting, no shadows, no vignette, no border, no text unless asked. Hand-painted pixel art " + "style for a dark-fantasy dungeon strategy game: chunky clear shapes, crisp edges, readable at small size. Fills the whole image.") +MATS = { + "wood": "Light weathered wooden planks running vertically with clear wood grain, knots and a few nail heads, in light neutral beige tones.", + "stone": "A rough block of light grey stone with chisel marks, small pits and a few fine cracks, in light neutral grey tones.", + "iron": "Light grey forged iron with hammer dents, scratches and a few rivets near the edges, in light neutral grey tones.", + "cloth": "Woven fabric with a visible coarse weave, soft folds and a simple embroidered border near the edges, in light neutral off-white tones.", + "straw": "Bundled straw and sackcloth: coarse burlap with straw sticking out, stitched seams, in light neutral beige tones.", + "fur": "Thick shaggy animal fur with tufts and strands, in light neutral beige-grey tones.", + "book": "The spine of one old leather-bound book standing upright, filling the whole image: two raised bands near the top and the bottom, " + "a gilded frame and a short line of golden title letters in the middle, worn leather, in light neutral tones.", + "paper": "An open page of an old spellbook filling the whole image: yellowed parchment with many lines of dark handwritten script, " + "a small drawn magic circle and a red initial letter, in light neutral tones.", +} diff --git a/art/generated/textures/materials/book-16.png b/art/generated/textures/materials/book-16.png new file mode 100644 index 0000000..10dc925 Binary files /dev/null and b/art/generated/textures/materials/book-16.png differ diff --git a/art/generated/textures/materials/book-64.png b/art/generated/textures/materials/book-64.png new file mode 100644 index 0000000..1c48847 Binary files /dev/null and b/art/generated/textures/materials/book-64.png differ diff --git a/art/generated/textures/materials/book-raw.png b/art/generated/textures/materials/book-raw.png new file mode 100644 index 0000000..3ea1121 Binary files /dev/null and b/art/generated/textures/materials/book-raw.png differ diff --git a/art/generated/textures/materials/cloth-16.png b/art/generated/textures/materials/cloth-16.png new file mode 100644 index 0000000..c9192be Binary files /dev/null and b/art/generated/textures/materials/cloth-16.png differ diff --git a/art/generated/textures/materials/cloth-64.png b/art/generated/textures/materials/cloth-64.png new file mode 100644 index 0000000..ea30b34 Binary files /dev/null and b/art/generated/textures/materials/cloth-64.png differ diff --git a/art/generated/textures/materials/cloth-raw.png b/art/generated/textures/materials/cloth-raw.png new file mode 100644 index 0000000..2bc2697 Binary files /dev/null and b/art/generated/textures/materials/cloth-raw.png differ diff --git a/art/generated/textures/materials/fur-16.png b/art/generated/textures/materials/fur-16.png new file mode 100644 index 0000000..5669807 Binary files /dev/null and b/art/generated/textures/materials/fur-16.png differ diff --git a/art/generated/textures/materials/fur-64.png b/art/generated/textures/materials/fur-64.png new file mode 100644 index 0000000..f2fffca Binary files /dev/null and b/art/generated/textures/materials/fur-64.png differ diff --git a/art/generated/textures/materials/fur-raw.png b/art/generated/textures/materials/fur-raw.png new file mode 100644 index 0000000..f08192c Binary files /dev/null and b/art/generated/textures/materials/fur-raw.png differ diff --git a/art/generated/textures/materials/iron-16.png b/art/generated/textures/materials/iron-16.png new file mode 100644 index 0000000..906c518 Binary files /dev/null and b/art/generated/textures/materials/iron-16.png differ diff --git a/art/generated/textures/materials/iron-64.png b/art/generated/textures/materials/iron-64.png new file mode 100644 index 0000000..e387c23 Binary files /dev/null and b/art/generated/textures/materials/iron-64.png differ diff --git a/art/generated/textures/materials/iron-raw.png b/art/generated/textures/materials/iron-raw.png new file mode 100644 index 0000000..cc2013e Binary files /dev/null and b/art/generated/textures/materials/iron-raw.png differ diff --git a/art/generated/textures/materials/paper-16.png b/art/generated/textures/materials/paper-16.png new file mode 100644 index 0000000..abe874e Binary files /dev/null and b/art/generated/textures/materials/paper-16.png differ diff --git a/art/generated/textures/materials/paper-64.png b/art/generated/textures/materials/paper-64.png new file mode 100644 index 0000000..3ee33e0 Binary files /dev/null and b/art/generated/textures/materials/paper-64.png differ diff --git a/art/generated/textures/materials/paper-raw.png b/art/generated/textures/materials/paper-raw.png new file mode 100644 index 0000000..a53c320 Binary files /dev/null and b/art/generated/textures/materials/paper-raw.png differ diff --git a/art/generated/textures/materials/stone-16.png b/art/generated/textures/materials/stone-16.png new file mode 100644 index 0000000..e4eaee9 Binary files /dev/null and b/art/generated/textures/materials/stone-16.png differ diff --git a/art/generated/textures/materials/stone-64.png b/art/generated/textures/materials/stone-64.png new file mode 100644 index 0000000..f731bb9 Binary files /dev/null and b/art/generated/textures/materials/stone-64.png differ diff --git a/art/generated/textures/materials/stone-raw.png b/art/generated/textures/materials/stone-raw.png new file mode 100644 index 0000000..12a514e Binary files /dev/null and b/art/generated/textures/materials/stone-raw.png differ diff --git a/art/generated/textures/materials/straw-16.png b/art/generated/textures/materials/straw-16.png new file mode 100644 index 0000000..9dd063c Binary files /dev/null and b/art/generated/textures/materials/straw-16.png differ diff --git a/art/generated/textures/materials/straw-64.png b/art/generated/textures/materials/straw-64.png new file mode 100644 index 0000000..b732ae8 Binary files /dev/null and b/art/generated/textures/materials/straw-64.png differ diff --git a/art/generated/textures/materials/straw-raw.png b/art/generated/textures/materials/straw-raw.png new file mode 100644 index 0000000..84c414b Binary files /dev/null and b/art/generated/textures/materials/straw-raw.png differ diff --git a/art/generated/textures/materials/wood-16.png b/art/generated/textures/materials/wood-16.png new file mode 100644 index 0000000..c00b6fe Binary files /dev/null and b/art/generated/textures/materials/wood-16.png differ diff --git a/art/generated/textures/materials/wood-64.png b/art/generated/textures/materials/wood-64.png new file mode 100644 index 0000000..2fbcaf2 Binary files /dev/null and b/art/generated/textures/materials/wood-64.png differ diff --git a/art/generated/textures/materials/wood-raw.png b/art/generated/textures/materials/wood-raw.png new file mode 100644 index 0000000..cc3338d Binary files /dev/null and b/art/generated/textures/materials/wood-raw.png differ diff --git a/assets/textures/mat/book-16.png b/assets/textures/mat/book-16.png new file mode 100644 index 0000000..10dc925 Binary files /dev/null and b/assets/textures/mat/book-16.png differ diff --git a/assets/textures/mat/book-64.png b/assets/textures/mat/book-64.png new file mode 100644 index 0000000..1c48847 Binary files /dev/null and b/assets/textures/mat/book-64.png differ diff --git a/assets/textures/mat/cloth-16.png b/assets/textures/mat/cloth-16.png new file mode 100644 index 0000000..c9192be Binary files /dev/null and b/assets/textures/mat/cloth-16.png differ diff --git a/assets/textures/mat/cloth-64.png b/assets/textures/mat/cloth-64.png new file mode 100644 index 0000000..ea30b34 Binary files /dev/null and b/assets/textures/mat/cloth-64.png differ diff --git a/assets/textures/mat/fur-16.png b/assets/textures/mat/fur-16.png new file mode 100644 index 0000000..5669807 Binary files /dev/null and b/assets/textures/mat/fur-16.png differ diff --git a/assets/textures/mat/fur-64.png b/assets/textures/mat/fur-64.png new file mode 100644 index 0000000..f2fffca Binary files /dev/null and b/assets/textures/mat/fur-64.png differ diff --git a/assets/textures/mat/iron-16.png b/assets/textures/mat/iron-16.png new file mode 100644 index 0000000..906c518 Binary files /dev/null and b/assets/textures/mat/iron-16.png differ diff --git a/assets/textures/mat/iron-64.png b/assets/textures/mat/iron-64.png new file mode 100644 index 0000000..e387c23 Binary files /dev/null and b/assets/textures/mat/iron-64.png differ diff --git a/assets/textures/mat/paper-16.png b/assets/textures/mat/paper-16.png new file mode 100644 index 0000000..abe874e Binary files /dev/null and b/assets/textures/mat/paper-16.png differ diff --git a/assets/textures/mat/paper-64.png b/assets/textures/mat/paper-64.png new file mode 100644 index 0000000..3ee33e0 Binary files /dev/null and b/assets/textures/mat/paper-64.png differ diff --git a/assets/textures/mat/stone-16.png b/assets/textures/mat/stone-16.png new file mode 100644 index 0000000..e4eaee9 Binary files /dev/null and b/assets/textures/mat/stone-16.png differ diff --git a/assets/textures/mat/stone-64.png b/assets/textures/mat/stone-64.png new file mode 100644 index 0000000..f731bb9 Binary files /dev/null and b/assets/textures/mat/stone-64.png differ diff --git a/assets/textures/mat/straw-16.png b/assets/textures/mat/straw-16.png new file mode 100644 index 0000000..9dd063c Binary files /dev/null and b/assets/textures/mat/straw-16.png differ diff --git a/assets/textures/mat/straw-64.png b/assets/textures/mat/straw-64.png new file mode 100644 index 0000000..b732ae8 Binary files /dev/null and b/assets/textures/mat/straw-64.png differ diff --git a/assets/textures/mat/wood-16.png b/assets/textures/mat/wood-16.png new file mode 100644 index 0000000..c00b6fe Binary files /dev/null and b/assets/textures/mat/wood-16.png differ diff --git a/assets/textures/mat/wood-64.png b/assets/textures/mat/wood-64.png new file mode 100644 index 0000000..2fbcaf2 Binary files /dev/null and b/assets/textures/mat/wood-64.png differ diff --git a/src/gfx.rs b/src/gfx.rs index 21e5bfa..9cdcb4c 100644 --- a/src/gfx.rs +++ b/src/gfx.rs @@ -101,6 +101,8 @@ pub struct Gfx { pub hd_sprites: Texture2D, // detailed sprites (see HD_SPRITES, hd_cell_view) 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 materials: [Texture2D; 8], // furniture materials, high-res (see crate::props::Mat) + pub materials_px: [Texture2D; 8], // the same for the pixel-art style (16x16) 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,6 +329,24 @@ fn earth_px(x: i32, y: i32, v: u32) -> Color { shade(base, (big + fine) as i32) } +// ---- 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 { + ($name:literal) => { + (include_bytes!(concat!("../assets/textures/mat/", $name, "-64.png")) as &[u8], include_bytes!(concat!("../assets/textures/mat/", $name, "-16.png")) as &[u8]) + }; +} +const MATERIALS: [(&[u8], &[u8]); 8] = [ + material!("wood"), + material!("stone"), + material!("iron"), + material!("cloth"), + material!("straw"), + material!("fur"), + material!("book"), + material!("paper"), +]; + // ---- high-res tiles // 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 @@ -2664,7 +2684,14 @@ impl Gfx { 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, atlas_hd2, cursors, badges, font, font_map } + let mat = |b: &[u8]| { + let t = Texture2D::from_file_with_format(b, Some(ImageFormat::Png)); + t.set_filter(FilterMode::Nearest); + t + }; + let materials = MATERIALS.map(|(hd, _)| mat(hd)); + let materials_px = MATERIALS.map(|(_, px)| mat(px)); + Gfx { atlas, sprites, hd_sprites, atlas_hd, atlas_hd2, materials, materials_px, cursors, badges, font, font_map } } pub fn cell(id: u32) -> Rect { diff --git a/src/props.rs b/src/props.rs index 0aef74d..7772939 100644 --- a/src/props.rs +++ b/src/props.rs @@ -37,6 +37,37 @@ fn cube(p: Vec3, s: Vec3, c: Color) { draw_cube(grown(p), s * k, None, c); } +// the materials of the furniture: a texture of grey detail on every side of a box, tinted by its +// colour (gfx MATERIALS, in this order; the page keeps its own colours) +#[derive(Clone, Copy)] +pub enum Mat { + Wood, + Stone, + Iron, + Cloth, + Straw, + Fur, + Book, + Paper, +} + +thread_local! { + // the material textures of the current graphics style (set every frame, see set_materials) + static MATS: std::cell::RefCell> = std::cell::RefCell::new(None); +} + +pub fn set_materials(m: &[Texture2D; 8]) { + MATS.with(|t| *t.borrow_mut() = Some(m.clone())); +} + +// a box of a material; the detail is a little darker than white, the colour makes up for it +fn mc(m: Mat, p: Vec3, s: Vec3, c: Color) { + let k = GROW.with(|g| g.get()).map_or(1.0, |g| g.1); + let tex = MATS.with(|t| t.borrow().as_ref().map(|a| a[m as usize].clone())); + let c = if tex.is_some() { Color::new((c.r * 1.2).min(1.0), (c.g * 1.2).min(1.0), (c.b * 1.2).min(1.0), c.a) } else { c }; + draw_cube(grown(p), s * k, tex.as_ref(), c); +} + fn line3(a: Vec3, b: Vec3, w: f32, c: Color) { let k = GROW.with(|g| g.get()).map_or(1.0, |g| g.1); crate::render3d::line3(grown(a), grown(b), w * k, c); @@ -169,24 +200,24 @@ pub fn draw_props_solid(g: &Game, cam: &Cam, t: f32) { continue; } let post = vec3(e.disp_x.floor() + 0.5, 0.0, e.disp_y.floor() + 0.15); - cube(post + vec3(0.0, 0.35, 0.0), vec3(0.08, 0.7, 0.08), rgb(70, 70, 78, 0.6)); + mc(Mat::Iron, post + vec3(0.0, 0.35, 0.0), vec3(0.08, 0.7, 0.08), rgb(70, 70, 78, 0.6)); for n in 0..6 { let f = n as f32 / 6.0; let side = if n % 2 == 0 { 0.08 } else { -0.08 }; let p = post.lerp(vec3(e.disp_x + side, 0.25, e.disp_y), f) + vec3(0.0, 0.3 * (1.0 - f) + 0.05 * (f * 3.14).sin(), 0.0); - cube(p, vec3(0.05, 0.05, 0.05), rgb(120, 120, 130, 0.6)); + mc(Mat::Iron, p, vec3(0.05, 0.05, 0.05), rgb(120, 120, 130, 0.6)); } } } // the dwarves' iron-bound chest with the rune forge plans (the rune glows in the glow pass) fn rune_chest(c: Vec3, l: f32) { - cube(c + vec3(0.0, 0.16, 0.0), vec3(0.5, 0.3, 0.34), rgb(92, 60, 34, l)); - cube(c + vec3(0.0, 0.34, 0.0), vec3(0.52, 0.08, 0.36), rgb(110, 72, 40, l)); + mc(Mat::Wood, c + vec3(0.0, 0.16, 0.0), vec3(0.5, 0.3, 0.34), rgb(92, 60, 34, l)); + mc(Mat::Wood, c + vec3(0.0, 0.34, 0.0), vec3(0.52, 0.08, 0.36), rgb(110, 72, 40, l)); for dx in [-0.18f32, 0.18] { - cube(c + vec3(dx, 0.2, 0.0), vec3(0.05, 0.4, 0.38), rgb(176, 112, 56, l)); + mc(Mat::Wood, c + vec3(dx, 0.2, 0.0), vec3(0.05, 0.4, 0.38), rgb(176, 112, 56, l)); } - cube(c + vec3(0.0, 0.22, 0.18), vec3(0.08, 0.1, 0.03), rgb(200, 170, 80, l)); + mc(Mat::Iron, c + vec3(0.0, 0.22, 0.18), vec3(0.08, 0.1, 0.03), rgb(200, 170, 80, l)); } // prison: iron bars along the edges of the room (cell walls) @@ -201,41 +232,41 @@ fn prison_bars(g: &Game, x: i32, z: i32, c: Vec3, l: f32) { let side = vec3(dz as f32, 0.0, dx as f32); for k in 0..5 { let o = (k as f32 - 2.0) * 0.2; - cube(edge + side * o + vec3(0.0, 0.4, 0.0), vec3(0.04, 0.8, 0.04), iron); + mc(Mat::Iron, edge + side * o + vec3(0.0, 0.4, 0.0), vec3(0.04, 0.8, 0.04), iron); } - cube(edge + vec3(0.0, 0.78, 0.0), side * 1.0 + vec3(0.05, 0.04, 0.05), iron); + mc(Mat::Iron, edge + vec3(0.0, 0.78, 0.0), side * 1.0 + vec3(0.05, 0.04, 0.05), iron); } } fn tombstone(c: Vec3, h: u32, l: f32, size: f32) { let tilt = ((h >> 3) % 3) as f32 * 0.04 - 0.04; - cube(c + vec3(tilt, 0.2 * size, 0.0), vec3(0.24, 0.4, 0.08) * size, rgb(110, 108, 112, l)); - cube(c + vec3(tilt, 0.42 * size, 0.0), vec3(0.16, 0.06, 0.08) * size, rgb(120, 118, 122, l)); - cube(c + vec3(tilt, 0.28 * size, 0.045), vec3(0.06, 0.16, 0.01) * size, rgb(60, 58, 62, l)); + mc(Mat::Stone, c + vec3(tilt, 0.2 * size, 0.0), vec3(0.24, 0.4, 0.08) * size, rgb(110, 108, 112, l)); + mc(Mat::Stone, c + vec3(tilt, 0.42 * size, 0.0), vec3(0.16, 0.06, 0.08) * size, rgb(120, 118, 122, l)); + mc(Mat::Stone, c + vec3(tilt, 0.28 * size, 0.045), vec3(0.06, 0.16, 0.01) * size, rgb(60, 58, 62, l)); } // guard post: a brazier on a tripod (its fire glows in the glow pass) and a small banner fn guard_brazier(c: Vec3, l: f32) { for k in 0..3 { let a = k as f32 * 2.1; - cube(c + vec3(a.cos() * 0.12, 0.2, a.sin() * 0.12), vec3(0.04, 0.4, 0.04), rgb(60, 60, 66, l)); + mc(Mat::Iron, c + vec3(a.cos() * 0.12, 0.2, a.sin() * 0.12), vec3(0.04, 0.4, 0.04), rgb(60, 60, 66, l)); } - cube(c + vec3(0.0, 0.44, 0.0), vec3(0.3, 0.08, 0.3), rgb(70, 66, 70, l)); - cube(c + vec3(0.35, 0.5, 0.0), vec3(0.03, 1.0, 0.03), rgb(90, 64, 40, l)); - cube(c + vec3(0.45, 0.82, 0.0), vec3(0.2, 0.28, 0.02), rgb(150, 24, 30, l)); + mc(Mat::Iron, c + vec3(0.0, 0.44, 0.0), vec3(0.3, 0.08, 0.3), rgb(70, 66, 70, l)); + mc(Mat::Wood, c + vec3(0.35, 0.5, 0.0), vec3(0.03, 1.0, 0.03), rgb(90, 64, 40, l)); + mc(Mat::Cloth, c + vec3(0.45, 0.82, 0.0), vec3(0.2, 0.28, 0.02), rgb(150, 24, 30, l)); } // arena: a wooden post with a training weapon leaning against it fn arena_post(c: Vec3, h: u32, l: f32) { let o = vec3(((h >> 4) % 3) as f32 * 0.2 - 0.2, 0.0, ((h >> 6) % 3) as f32 * 0.2 - 0.2); - cube(c + o + vec3(0.0, 0.35, 0.0), vec3(0.1, 0.7, 0.1), rgb(110, 76, 44, l)); - cube(c + o + vec3(0.12, 0.35, 0.0), vec3(0.03, 0.6, 0.03), rgb(150, 150, 160, l)); + mc(Mat::Wood, c + o + vec3(0.0, 0.35, 0.0), vec3(0.1, 0.7, 0.1), rgb(110, 76, 44, l)); + mc(Mat::Iron, c + o + vec3(0.12, 0.35, 0.0), vec3(0.03, 0.6, 0.03), rgb(150, 150, 160, l)); } // sacrifice altar: a black block with a channel, candles at the corners (they glow) fn altar(c: Vec3, l: f32) { - cube(c + vec3(0.0, 0.08, 0.0), vec3(1.6, 0.16, 1.2), rgb(36, 30, 38, l)); - cube(c + vec3(0.0, 0.32, 0.0), vec3(1.0, 0.32, 0.6), rgb(28, 24, 30, l)); + mc(Mat::Stone, c + vec3(0.0, 0.08, 0.0), vec3(1.6, 0.16, 1.2), rgb(36, 30, 38, l)); + mc(Mat::Stone, c + vec3(0.0, 0.32, 0.0), vec3(1.0, 0.32, 0.6), rgb(28, 24, 30, l)); cube(c + vec3(0.0, 0.49, 0.0), vec3(0.8, 0.02, 0.1), rgb(90, 10, 20, l)); for (dx, dz) in [(-0.7f32, -0.5f32), (0.7, -0.5), (0.7, 0.5), (-0.7, 0.5)] { cube(c + vec3(dx, 0.25, dz), vec3(0.06, 0.18, 0.06), rgb(220, 210, 190, l)); @@ -245,23 +276,23 @@ fn altar(c: Vec3, l: f32) { // forge: an anvil on a block, a hammer leaning against it fn anvil(c: Vec3, h: u32, l: f32) { let o = vec3(((h >> 3) % 3) as f32 * 0.08 - 0.08, 0.0, ((h >> 5) % 3) as f32 * 0.08 - 0.08); - cube(c + o + vec3(0.0, 0.12, 0.0), vec3(0.22, 0.24, 0.22), rgb(70, 50, 36, l)); - cube(c + o + vec3(0.0, 0.28, 0.0), vec3(0.16, 0.08, 0.16), rgb(56, 56, 62, l)); - cube(c + o + vec3(0.0, 0.35, 0.0), vec3(0.38, 0.06, 0.16), rgb(76, 76, 84, l)); - cube(c + o + vec3(0.22, 0.35, 0.0), vec3(0.1, 0.04, 0.08), rgb(76, 76, 84, l)); + mc(Mat::Wood, c + o + vec3(0.0, 0.12, 0.0), vec3(0.22, 0.24, 0.22), rgb(70, 50, 36, l)); + mc(Mat::Iron, c + o + vec3(0.0, 0.28, 0.0), vec3(0.16, 0.08, 0.16), rgb(56, 56, 62, l)); + mc(Mat::Iron, c + o + vec3(0.0, 0.35, 0.0), vec3(0.38, 0.06, 0.16), rgb(76, 76, 84, l)); + mc(Mat::Iron, c + o + vec3(0.22, 0.35, 0.0), vec3(0.1, 0.04, 0.08), rgb(76, 76, 84, l)); } // forge: a squat stone furnace (its fire glows in the glow pass) fn furnace(c: Vec3, l: f32) { - cube(c + vec3(0.0, 0.25, 0.0), vec3(0.6, 0.5, 0.6), rgb(64, 52, 50, l)); - cube(c + vec3(0.0, 0.55, 0.0), vec3(0.4, 0.1, 0.4), rgb(50, 42, 40, l)); - cube(c + vec3(0.0, 0.75, 0.0), vec3(0.16, 0.32, 0.16), rgb(44, 38, 38, l)); + mc(Mat::Stone, c + vec3(0.0, 0.25, 0.0), vec3(0.6, 0.5, 0.6), rgb(64, 52, 50, l)); + mc(Mat::Stone, c + vec3(0.0, 0.55, 0.0), vec3(0.4, 0.1, 0.4), rgb(50, 42, 40, l)); + mc(Mat::Stone, c + vec3(0.0, 0.75, 0.0), vec3(0.16, 0.32, 0.16), rgb(44, 38, 38, l)); } // a stone basin around a mana spring fn spring_basin(c: Vec3, l: f32) { for (dx, dz, sx, sz) in [(0.0, -0.38, 0.8, 0.1), (0.0, 0.38, 0.8, 0.1), (-0.38, 0.0, 0.1, 0.8), (0.38, 0.0, 0.1, 0.8)] { - cube(c + vec3(dx, 0.08, dz), vec3(sx, 0.16, sz), rgb(90, 95, 110, l)); + mc(Mat::Stone, c + vec3(dx, 0.08, dz), vec3(sx, 0.16, sz), rgb(90, 95, 110, l)); } } @@ -398,18 +429,19 @@ fn chest(c: Vec3, h: u32, l: f32, fill: f32, res: u8) { let (ca, sa) = (rot.cos(), rot.sin()); let at = |dx: f32, dz: f32, y: f32| c + vec3(dx * ca - dz * sa, y, dx * sa + dz * ca); let cube = |p: Vec3, s: Vec3, col: Color| cube(p, if q % 2 == 1 { vec3(s.z, s.y, s.x) } else { s }, col); + let mcube = |m: Mat, p: Vec3, s: Vec3, col: Color| mc(m, p, if q % 2 == 1 { vec3(s.z, s.y, s.x) } else { s }, col); let wood = rgb(110, 70, 36, l); let band = rgb(170, 140, 70, l); - cube(at(0.0, 0.0, 0.16), vec3(0.56, 0.32, 0.4), wood); - cube(at(0.0, 0.0, 0.16), vec3(0.58, 0.05, 0.42), band); - cube(at(0.0, 0.21, 0.18), vec3(0.08, 0.1, 0.02), band); + mcube(Mat::Wood, at(0.0, 0.0, 0.16), vec3(0.56, 0.32, 0.4), wood); + mcube(Mat::Iron, at(0.0, 0.0, 0.16), vec3(0.58, 0.05, 0.42), band); + mcube(Mat::Iron, at(0.0, 0.21, 0.18), vec3(0.08, 0.1, 0.02), band); let fill = fill.clamp(0.0, 1.0); if fill < 0.02 { - cube(at(0.0, 0.0, 0.36), vec3(0.58, 0.08, 0.42), wood); + mcube(Mat::Wood, at(0.0, 0.0, 0.36), vec3(0.58, 0.08, 0.42), wood); return; } // open lid leaning back - cube(at(0.0, -0.24, 0.5), vec3(0.58, 0.32, 0.06), wood); + mcube(Mat::Wood, at(0.0, -0.24, 0.5), vec3(0.58, 0.32, 0.06), wood); // content: the treasure kind decides the colour, the amount the height (heaped when full) let col = match res { 1 => rgb(205, 210, 225, 1.0), @@ -451,7 +483,7 @@ fn bed(kind: EK, c: Vec3, h: u32, l: f32, t: f32) { // straw pile for n in 0..7 { let (dx, dz) = (((h >> (n * 3)) % 7) as f32 * 0.08 - 0.24, ((h >> (n * 2 + 1)) % 7) as f32 * 0.08 - 0.24); - cube(at(dx, dz, 0.04), vec3(0.28, 0.06, 0.12), rgb(200, 170, 70, l * (0.85 + (n % 3) as f32 * 0.08))); + mc(Mat::Straw, at(dx, dz, 0.04), vec3(0.28, 0.06, 0.12), rgb(200, 170, 70, l * (0.85 + (n % 3) as f32 * 0.08))); } } EK::Spider => { @@ -478,81 +510,81 @@ fn bed(kind: EK, c: Vec3, h: u32, l: f32, t: f32) { } EK::Mage => { // cushion with a starry blanket - cube(at(0.0, 0.0, 0.06), vec3(0.62, 0.12, 0.5), rgb(70, 50, 120, l)); - cube(at(0.0, 0.08, 0.13), vec3(0.6, 0.03, 0.34), rgb(40, 30, 90, l)); + mc(Mat::Cloth, at(0.0, 0.0, 0.06), vec3(0.62, 0.12, 0.5), rgb(70, 50, 120, l)); + mc(Mat::Cloth, at(0.0, 0.08, 0.13), vec3(0.6, 0.03, 0.34), rgb(40, 30, 90, l)); for n in 0..4 { let (dx, dz) = (((h >> (n * 4)) % 5) as f32 * 0.1 - 0.2, ((h >> (n * 4 + 2)) % 3) as f32 * 0.1 - 0.0); cube(at(dx, dz, 0.15), vec3(0.04, 0.02, 0.04), rgb(240, 210, 90, 1.0)); } - cube(at(0.0, -0.18, 0.15), vec3(0.3, 0.08, 0.14), rgb(200, 190, 220, l)); + mc(Mat::Cloth, at(0.0, -0.18, 0.15), vec3(0.3, 0.08, 0.14), rgb(200, 190, 220, l)); } EK::Ogre => { // heap of furs with a gnawed bone for n in 0..5 { let a = n as f32 * 1.3 + rot; - cube(c + vec3(a.cos() * 0.15, 0.05 + (n % 2) as f32 * 0.04, a.sin() * 0.15), vec3(0.38, 0.1, 0.3), rgb(110 + (n * 12) as u8, 76, 44, l)); + mc(Mat::Fur, c + vec3(a.cos() * 0.15, 0.05 + (n % 2) as f32 * 0.04, a.sin() * 0.15), vec3(0.38, 0.1, 0.3), rgb(110 + (n * 12) as u8, 76, 44, l)); } cube(at(0.32, 0.2, 0.06), vec3(0.24, 0.04, 0.05), rgb(230, 220, 200, l)); } EK::Jailer => { // plank bunk with chains - cube(at(0.0, 0.0, 0.12), vec3(0.72, 0.06, 0.4), rgb(90, 66, 44, l)); + mc(Mat::Wood, at(0.0, 0.0, 0.12), vec3(0.72, 0.06, 0.4), rgb(90, 66, 44, l)); for (dx, dz) in [(-0.3, -0.15), (0.3, -0.15), (-0.3, 0.15), (0.3, 0.15)] { - cube(at(dx, dz, 0.05), vec3(0.06, 0.1, 0.06), rgb(70, 50, 34, l)); + mc(Mat::Wood, at(dx, dz, 0.05), vec3(0.06, 0.1, 0.06), rgb(70, 50, 34, l)); } for n in 0..6 { - cube(at(-0.3 + n as f32 * 0.12, 0.24, 0.03 + (n % 2) as f32 * 0.02), vec3(0.08, 0.03, 0.04), rgb(120, 120, 130, l)); + mc(Mat::Iron, at(-0.3 + n as f32 * 0.12, 0.24, 0.03 + (n % 2) as f32 * 0.02), vec3(0.08, 0.03, 0.04), rgb(120, 120, 130, l)); } } EK::Priestess => { // ember cushion (glowing coals in the glow pass) - cube(at(0.0, 0.0, 0.05), vec3(0.6, 0.1, 0.48), rgb(150, 40, 30, l)); - cube(at(0.0, 0.0, 0.11), vec3(0.4, 0.03, 0.3), rgb(220, 110, 40, l)); + mc(Mat::Cloth, at(0.0, 0.0, 0.05), vec3(0.6, 0.1, 0.48), rgb(150, 40, 30, l)); + mc(Mat::Cloth, at(0.0, 0.0, 0.11), vec3(0.4, 0.03, 0.3), rgb(220, 110, 40, l)); } EK::Bat => { // roost: a beam between two posts to hang from for side in [-0.32f32, 0.32] { - cube(at(side, 0.0, 0.45), vec3(0.06, 0.9, 0.06), rgb(90, 64, 40, l)); + mc(Mat::Wood, at(side, 0.0, 0.45), vec3(0.06, 0.9, 0.06), rgb(90, 64, 40, l)); } - cube(at(0.0, 0.0, 0.88), vec3(0.72, 0.06, 0.06), rgb(110, 78, 48, l)); + mc(Mat::Wood, at(0.0, 0.0, 0.88), vec3(0.72, 0.06, 0.06), rgb(110, 78, 48, l)); } EK::Gargoyle => { // stone plinth to sit on - cube(at(0.0, 0.0, 0.2), vec3(0.5, 0.4, 0.5), rgb(110, 110, 118, l)); - cube(at(0.0, 0.0, 0.42), vec3(0.58, 0.06, 0.58), rgb(130, 130, 138, l)); + mc(Mat::Stone, at(0.0, 0.0, 0.2), vec3(0.5, 0.4, 0.5), rgb(110, 110, 118, l)); + mc(Mat::Stone, at(0.0, 0.0, 0.42), vec3(0.58, 0.06, 0.58), rgb(130, 130, 138, l)); } EK::Owl => { // twig nest on a post - cube(at(0.0, 0.0, 0.3), vec3(0.08, 0.6, 0.08), rgb(90, 64, 40, l)); + mc(Mat::Wood, at(0.0, 0.0, 0.3), vec3(0.08, 0.6, 0.08), rgb(90, 64, 40, l)); for n in 0..6 { let a = n as f32 * 1.05 + rot; - cube(c + vec3(a.cos() * 0.16, 0.62, a.sin() * 0.16), vec3(0.16, 0.06, 0.05), rgb(110 + (n * 6) as u8, 80, 44, l)); + mc(Mat::Wood, c + vec3(a.cos() * 0.16, 0.62, a.sin() * 0.16), vec3(0.16, 0.06, 0.05), rgb(110 + (n * 6) as u8, 80, 44, l)); } } EK::Wyrm => { // a big nest of dark rocks (the embers glow in the glow pass) for n in 0..9 { let a = n as f32 * 0.7 + rot; - cube(c + vec3(a.cos() * 0.42, 0.09, a.sin() * 0.42), vec3(0.2, 0.18 + (n % 3) as f32 * 0.04, 0.2), rgb(60, 44, 44, l)); + mc(Mat::Stone, c + vec3(a.cos() * 0.42, 0.09, a.sin() * 0.42), vec3(0.2, 0.18 + (n % 3) as f32 * 0.04, 0.2), rgb(60, 44, 44, l)); } } EK::Chainbreaker => { // a hollow of broken rocks and a burst chain for n in 0..7 { let a = n as f32 * 0.9 + rot; - cube(c + vec3(a.cos() * 0.36, 0.08, a.sin() * 0.36), vec3(0.18, 0.16 + (n % 3) as f32 * 0.05, 0.18), rgb(90, 86, 92, l * (0.8 + (n % 2) as f32 * 0.2))); + mc(Mat::Stone, c + vec3(a.cos() * 0.36, 0.08, a.sin() * 0.36), vec3(0.18, 0.16 + (n % 3) as f32 * 0.05, 0.18), rgb(90, 86, 92, l * (0.8 + (n % 2) as f32 * 0.2))); } for n in 0..3 { - cube(at(-0.1 + n as f32 * 0.1, 0.05, 0.03), vec3(0.08, 0.03, 0.04), rgb(130, 130, 140, l)); + mc(Mat::Iron, at(-0.1 + n as f32 * 0.1, 0.05, 0.03), vec3(0.08, 0.03, 0.04), rgb(130, 130, 140, l)); } } _ => { // defected heroes: a proper camp bed with a white sheet - cube(at(0.0, 0.0, 0.14), vec3(0.42, 0.05, 0.78), rgb(120, 86, 52, l)); - cube(at(0.0, 0.05, 0.18), vec3(0.38, 0.04, 0.5), rgb(220, 220, 228, l)); - cube(at(0.0, -0.28, 0.2), vec3(0.3, 0.06, 0.14), rgb(240, 240, 245, l)); + mc(Mat::Wood, at(0.0, 0.0, 0.14), vec3(0.42, 0.05, 0.78), rgb(120, 86, 52, l)); + mc(Mat::Cloth, at(0.0, 0.05, 0.18), vec3(0.38, 0.04, 0.5), rgb(220, 220, 228, l)); + mc(Mat::Cloth, at(0.0, -0.28, 0.2), vec3(0.3, 0.06, 0.14), rgb(240, 240, 245, l)); for (dx, dz) in [(-0.18, -0.35), (0.18, -0.35), (-0.18, 0.35), (0.18, 0.35)] { - cube(at(dx, dz, 0.06), vec3(0.05, 0.12, 0.05), rgb(100, 70, 44, l)); + mc(Mat::Wood, at(dx, dz, 0.06), vec3(0.05, 0.12, 0.05), rgb(100, 70, 44, l)); } } } @@ -562,8 +594,8 @@ fn bed(kind: EK, c: Vec3, h: u32, l: f32, t: f32) { // an empty perch in the aerie: post with a crossbar fn perch(c: Vec3, h: u32, l: f32) { let dx = ((h >> 4) % 3) as f32 * 0.12 - 0.12; - cube(c + vec3(dx, 0.35, 0.0), vec3(0.06, 0.7, 0.06), rgb(96, 68, 42, l)); - cube(c + vec3(dx, 0.68, 0.0), vec3(0.4, 0.05, 0.05), rgb(110, 78, 48, l)); + mc(Mat::Wood, c + vec3(dx, 0.35, 0.0), vec3(0.06, 0.7, 0.06), rgb(96, 68, 42, l)); + mc(Mat::Wood, c + vec3(dx, 0.68, 0.0), vec3(0.4, 0.05, 0.05), rgb(110, 78, 48, l)); } // ---------------------------------------------------------------- training hall @@ -573,28 +605,28 @@ fn training(c: Vec3, h: u32, l: f32, t: f32, busy: bool) { 0 => { // spinning dummy: post, straw body, two arms let spin = if busy { t * 6.0 } else { (h % 7) as f32 }; - cube(c + vec3(0.0, 0.35, 0.0), vec3(0.08, 0.7, 0.08), rgb(100, 70, 44, l)); - cube(c + vec3(0.0, 0.5, 0.0), vec3(0.2, 0.28, 0.2), rgb(200, 170, 100, l)); - cube(c + vec3(0.0, 0.72, 0.0), vec3(0.14, 0.14, 0.14), rgb(210, 180, 120, l)); + mc(Mat::Wood, c + vec3(0.0, 0.35, 0.0), vec3(0.08, 0.7, 0.08), rgb(100, 70, 44, l)); + mc(Mat::Straw, c + vec3(0.0, 0.5, 0.0), vec3(0.2, 0.28, 0.2), rgb(200, 170, 100, l)); + mc(Mat::Straw, c + vec3(0.0, 0.72, 0.0), vec3(0.14, 0.14, 0.14), rgb(210, 180, 120, l)); let (ax, az) = (spin.cos() * 0.3, spin.sin() * 0.3); line3(c + vec3(-ax, 0.58, -az), c + vec3(ax, 0.58, az), 0.06, rgb(120, 84, 50, l)); - cube(c + vec3(0.0, 0.03, 0.0), vec3(0.34, 0.06, 0.34), rgb(80, 56, 36, l)); + mc(Mat::Wood, c + vec3(0.0, 0.03, 0.0), vec3(0.34, 0.06, 0.34), rgb(80, 56, 36, l)); } 1 => { // weapon rack: frame with swords and an axe - cube(c + vec3(-0.3, 0.3, 0.0), vec3(0.06, 0.6, 0.06), rgb(100, 70, 44, l)); - cube(c + vec3(0.3, 0.3, 0.0), vec3(0.06, 0.6, 0.06), rgb(100, 70, 44, l)); - cube(c + vec3(0.0, 0.56, 0.0), vec3(0.66, 0.05, 0.08), rgb(110, 78, 48, l)); + mc(Mat::Wood, c + vec3(-0.3, 0.3, 0.0), vec3(0.06, 0.6, 0.06), rgb(100, 70, 44, l)); + mc(Mat::Wood, c + vec3(0.3, 0.3, 0.0), vec3(0.06, 0.6, 0.06), rgb(100, 70, 44, l)); + mc(Mat::Wood, c + vec3(0.0, 0.56, 0.0), vec3(0.66, 0.05, 0.08), rgb(110, 78, 48, l)); for n in 0..3 { let x = -0.15 + n as f32 * 0.15; - cube(c + vec3(x, 0.3, 0.03), vec3(0.03, 0.46, 0.02), rgb(190, 195, 205, l)); - cube(c + vec3(x, 0.12, 0.03), vec3(0.1, 0.02, 0.03), rgb(110, 80, 40, l)); + mc(Mat::Iron, c + vec3(x, 0.3, 0.03), vec3(0.03, 0.46, 0.02), rgb(190, 195, 205, l)); + mc(Mat::Wood, c + vec3(x, 0.12, 0.03), vec3(0.1, 0.02, 0.03), rgb(110, 80, 40, l)); } } _ => { // target board on a stand, wobbling when hit let wob = if busy { (t * 14.0).sin() * 0.05 } else { 0.0 }; - cube(c + vec3(0.0, 0.25, 0.1), vec3(0.06, 0.5, 0.06), rgb(100, 70, 44, l)); + mc(Mat::Wood, c + vec3(0.0, 0.25, 0.1), vec3(0.06, 0.5, 0.06), rgb(100, 70, 44, l)); let p = c + vec3(wob, 0.55, 0.0); for (k, (s, col)) in [(0.5, rgb(230, 225, 210, l)), (0.36, rgb(200, 40, 40, l)), (0.22, rgb(230, 225, 210, l)), (0.1, rgb(200, 40, 40, l))].iter().enumerate() { cube(p - vec3(0.0, 0.0, k as f32 * 0.01), vec3(*s, *s, 0.04), *col); @@ -611,8 +643,8 @@ fn torture(c: Vec3, h: u32, l: f32, t: f32, busy: bool) { // the wheel turns while a prisoner is on it let a0 = if busy { t * 1.5 } else { 0.3 }; let hub = c + vec3(0.0, 0.5, 0.0); - cube(c + vec3(-0.28, 0.25, 0.0), vec3(0.06, 0.5, 0.06), rgb(80, 56, 36, l)); - cube(c + vec3(0.28, 0.25, 0.0), vec3(0.06, 0.5, 0.06), rgb(80, 56, 36, l)); + mc(Mat::Wood, c + vec3(-0.28, 0.25, 0.0), vec3(0.06, 0.5, 0.06), rgb(80, 56, 36, l)); + mc(Mat::Wood, c + vec3(0.28, 0.25, 0.0), vec3(0.06, 0.5, 0.06), rgb(80, 56, 36, l)); let col = rgb(120, 84, 50, l); for n in 0..8 { let a = a0 + n as f32 * std::f32::consts::FRAC_PI_4; @@ -621,15 +653,15 @@ fn torture(c: Vec3, h: u32, l: f32, t: f32, busy: bool) { line3(hub, p(a), 0.03, col); line3(p(a), p(b), 0.05, col); } - cube(hub, vec3(0.62, 0.06, 0.06), rgb(90, 90, 100, l)); + mc(Mat::Iron, hub, vec3(0.62, 0.06, 0.06), rgb(90, 90, 100, l)); } 1 => { // iron maiden, slightly open - cube(c + vec3(0.0, 0.42, 0.06), vec3(0.4, 0.84, 0.2), rgb(70, 70, 80, l)); - cube(c + vec3(0.0, 0.78, 0.06), vec3(0.3, 0.12, 0.2), rgb(90, 90, 100, l)); - cube(c + vec3(-0.12, 0.42, -0.14), vec3(0.18, 0.8, 0.04), rgb(80, 80, 92, l)); + mc(Mat::Iron, c + vec3(0.0, 0.42, 0.06), vec3(0.4, 0.84, 0.2), rgb(70, 70, 80, l)); + mc(Mat::Iron, c + vec3(0.0, 0.78, 0.06), vec3(0.3, 0.12, 0.2), rgb(90, 90, 100, l)); + mc(Mat::Iron, c + vec3(-0.12, 0.42, -0.14), vec3(0.18, 0.8, 0.04), rgb(80, 80, 92, l)); for n in 0..4 { - cube(c + vec3(0.08, 0.2 + n as f32 * 0.16, -0.06), vec3(0.03, 0.03, 0.12), rgb(170, 170, 180, l)); + mc(Mat::Iron, c + vec3(0.08, 0.2 + n as f32 * 0.16, -0.06), vec3(0.03, 0.03, 0.12), rgb(170, 170, 180, l)); } cube(c + vec3(0.0, 0.5, 0.17), vec3(0.08, 0.08, 0.02), rgb(160, 40, 40, l)); } @@ -639,7 +671,7 @@ fn torture(c: Vec3, h: u32, l: f32, t: f32, busy: bool) { let a = n as f32 * 2.094; line3(c + vec3(a.cos() * 0.2, 0.0, a.sin() * 0.2), c + vec3(a.cos() * 0.08, 0.45, a.sin() * 0.08), 0.03, rgb(60, 60, 66, l)); } - cube(c + vec3(0.0, 0.5, 0.0), vec3(0.32, 0.08, 0.32), rgb(50, 46, 46, l)); + mc(Mat::Iron, c + vec3(0.0, 0.5, 0.0), vec3(0.32, 0.08, 0.32), rgb(50, 46, 46, l)); line3(c + vec3(0.05, 0.52, 0.0), c + vec3(0.3, 0.8, 0.1), 0.02, rgb(120, 120, 130, l)); } } @@ -649,39 +681,39 @@ fn torture(c: Vec3, h: u32, l: f32, t: f32, busy: bool) { fn temple(c: Vec3, h: u32, l: f32) { // altar: stone block, red cloth, two candles (flames in the glow pass), a fire bowl behind - cube(c + vec3(0.0, 0.22, -0.1), vec3(0.9, 0.44, 0.42), rgb(70, 56, 72, l)); - cube(c + vec3(0.0, 0.45, -0.1), vec3(0.94, 0.04, 0.46), rgb(90, 74, 92, l)); - cube(c + vec3(0.0, 0.3, 0.12), vec3(0.4, 0.3, 0.02), rgb(160, 40, 40, l)); + mc(Mat::Stone, c + vec3(0.0, 0.22, -0.1), vec3(0.9, 0.44, 0.42), rgb(70, 56, 72, l)); + mc(Mat::Stone, c + vec3(0.0, 0.45, -0.1), vec3(0.94, 0.04, 0.46), rgb(90, 74, 92, l)); + mc(Mat::Cloth, c + vec3(0.0, 0.3, 0.12), vec3(0.4, 0.3, 0.02), rgb(160, 40, 40, l)); cube(c + vec3(0.0, 0.3, 0.13), vec3(0.08, 0.08, 0.01), rgb(230, 190, 80, 1.0)); for n in [-0.32f32, 0.32] { cube(c + vec3(n, 0.56, -0.18), vec3(0.05, 0.18, 0.05), rgb(230, 220, 200, l)); } - cube(c + vec3(0.0, 0.3, 0.15), vec3(0.1, 0.6, 0.1), rgb(60, 50, 62, l)); - cube(c + vec3(0.0, 0.58, 0.15), vec3(0.24, 0.06, 0.24), rgb(90, 70, 40, l)); + mc(Mat::Stone, c + vec3(0.0, 0.3, 0.15), vec3(0.1, 0.6, 0.1), rgb(60, 50, 62, l)); + mc(Mat::Iron, c + vec3(0.0, 0.58, 0.15), vec3(0.24, 0.06, 0.24), rgb(90, 70, 40, l)); let _ = h; } fn candle_stand(c: Vec3, l: f32) { - cube(c + vec3(0.0, 0.2, 0.0), vec3(0.04, 0.4, 0.04), rgb(150, 120, 60, l)); - cube(c + vec3(0.0, 0.02, 0.0), vec3(0.14, 0.04, 0.14), rgb(150, 120, 60, l)); + mc(Mat::Iron, c + vec3(0.0, 0.2, 0.0), vec3(0.04, 0.4, 0.04), rgb(150, 120, 60, l)); + mc(Mat::Iron, c + vec3(0.0, 0.02, 0.0), vec3(0.14, 0.04, 0.14), rgb(150, 120, 60, l)); cube(c + vec3(0.0, 0.45, 0.0), vec3(0.05, 0.1, 0.05), rgb(230, 220, 200, l)); } // ---------------------------------------------------------------- library fn shelf(c: Vec3, h: u32, l: f32) { - cube(c + vec3(0.0, 0.45, 0.0), vec3(0.86, 0.9, 0.3), rgb(70, 44, 28, l)); + mc(Mat::Wood, c + vec3(0.0, 0.45, 0.0), vec3(0.86, 0.9, 0.3), rgb(70, 44, 28, l)); let colors = [(150, 40, 40), (40, 80, 140), (60, 120, 60), (160, 130, 50), (100, 60, 120), (180, 170, 150)]; for row in 0..3 { let y = 0.18 + row as f32 * 0.28; - cube(c + vec3(0.0, y - 0.1, 0.0), vec3(0.8, 0.03, 0.32), rgb(90, 60, 38, l)); + mc(Mat::Wood, c + vec3(0.0, y - 0.1, 0.0), vec3(0.8, 0.03, 0.32), rgb(90, 60, 38, l)); let mut x = -0.36; let mut n = 0; while x < 0.34 { let w = 0.05 + ((h >> ((n + row * 5) % 24)) % 3) as f32 * 0.02; let (r, gg, b) = colors[(hash(n, row, h) % 6) as usize]; let ht = 0.16 + ((h >> (n % 20)) % 3) as f32 * 0.025; - cube(c + vec3(x + w * 0.5, y - 0.08 + ht * 0.5, -0.02), vec3(w * 0.9, ht, 0.24), rgb(r, gg, b, l)); + mc(Mat::Book, c + vec3(x + w * 0.5, y - 0.08 + ht * 0.5, -0.02), vec3(w * 0.9, ht, 0.24), rgb(r, gg, b, l)); x += w; n += 1; } @@ -689,9 +721,9 @@ fn shelf(c: Vec3, h: u32, l: f32) { } fn lectern(c: Vec3, l: f32) { - cube(c + vec3(0.0, 0.3, 0.0), vec3(0.1, 0.6, 0.1), rgb(90, 60, 38, l)); - cube(c + vec3(0.0, 0.62, 0.0), vec3(0.42, 0.04, 0.3), rgb(110, 74, 46, l)); - cube(c + vec3(0.0, 0.66, 0.0), vec3(0.3, 0.02, 0.2), rgb(230, 222, 200, l)); + mc(Mat::Wood, c + vec3(0.0, 0.3, 0.0), vec3(0.1, 0.6, 0.1), rgb(90, 60, 38, l)); + mc(Mat::Wood, c + vec3(0.0, 0.62, 0.0), vec3(0.42, 0.04, 0.3), rgb(110, 74, 46, l)); + mc(Mat::Paper, c + vec3(0.0, 0.66, 0.0), vec3(0.3, 0.02, 0.2), rgb(230, 222, 200, l)); cube(c + vec3(0.0, 0.67, 0.0), vec3(0.01, 0.02, 0.2), rgb(120, 40, 40, l)); disc(c + vec3(0.0, 0.01, 0.0), 0.25, Color::new(0.0, 0.0, 0.0, 0.25), 12); } diff --git a/src/render3d.rs b/src/render3d.rs index 80ca4f9..b3b7f3d 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -1643,6 +1643,8 @@ impl R3d { } draw_landmarks_solid(g, t); draw_towers_solid(g); + // the furniture's materials in the graphics style's resolution + crate::props::set_materials(if self.chunks_hd { &gfx.materials } else { &gfx.materials_px }); crate::props::draw_props_solid(g, cam, t); draw_parts_solid(g); draw_traps_solid(g, t); diff --git a/tools/make_material.py b/tools/make_material.py new file mode 100644 index 0000000..af69caa --- /dev/null +++ b/tools/make_material.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Turn a generated material picture into a texture for the furniture (props.rs): cropped, +optionally turned into grey detail (the furniture's colours tint it in the game), brightened to +a common mean and scaled to the high-res size and the pixel-art size. + +Usage: + make_material.py raw.png out_prefix [--crop x0,y0,x1,y1] [--trim] [--color] [--mean 0.8] + +--crop fractions of the picture to keep +--trim crop to the picture's object (everything that is not near-white background) +--color keep the colours (else grey detail) +--mean mean brightness of the result (0..1) + +Writes out_prefix-64.png and out_prefix-16.png. Needs Pillow. +""" +import argparse + +from PIL import Image, ImageOps, ImageStat + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("raw") + ap.add_argument("out") + ap.add_argument("--crop", default=None) + ap.add_argument("--trim", action="store_true") + ap.add_argument("--color", action="store_true") + ap.add_argument("--mean", type=float, default=0.8) + a = ap.parse_args() + + img = Image.open(a.raw).convert("RGB") + if a.crop: + f = [float(v) for v in a.crop.split(",")] + img = img.crop((int(f[0] * img.width), int(f[1] * img.height), int(f[2] * img.width), int(f[3] * img.height))) + if a.trim: + # the object: everything darker than the white background, a few pixels in + mask = img.convert("L").point(lambda v: 255 if v < 235 else 0) + box = mask.getbbox() + if box: + img = img.crop((box[0] + 4, box[1] + 4, box[2] - 4, box[3] - 4)) + if not a.color: + img = ImageOps.grayscale(img).convert("RGB") + mean = ImageStat.Stat(img.convert("L")).mean[0] / 255 + k = a.mean / max(mean, 0.01) + img = img.point(lambda v: min(255, int(v * k))) + img.resize((64, 64), Image.LANCZOS).save(f"{a.out}-64.png") + small = img.resize((16, 16), Image.BOX) + small.quantize(colors=8, method=Image.Quantize.MEDIANCUT, dither=Image.Dither.NONE).convert("RGB").save(f"{a.out}-16.png") + print(f"{a.out}-64.png, {a.out}-16.png") + + +if __name__ == "__main__": + main()