summaryrefslogtreecommitdiffstats
path: root/src/texture.rs
blob: e5720c033975a43654d318b6f768b5b4b19267bb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use rand::Rng;

pub const TEX_SIZE: usize = 64;

/// Deterministic hash for coherent procedural noise.
fn hash(x: u32, y: u32, seed: u64) -> u64 {
    let mut h = (x as u64).wrapping_mul(0x9e3779b9);
    h = h.wrapping_add((y as u64).wrapping_mul(0x517cc1b727220a95));
    h = h.wrapping_add(seed);
    h = (h ^ (h >> 33)).wrapping_mul(0xc6a4a7935bd1e995);
    h = (h ^ (h >> 29)).wrapping_mul(0x5bd1e995);
    h
}

/// Brown cracked wall texture — 64×64, brick pattern with mortar lines and fissures.
pub fn generate_wall_texture(rng: &mut impl Rng) -> Vec<u32> {
    let seed = rng.gen::<u64>();
    let mut tex = vec![0u32; TEX_SIZE * TEX_SIZE];

    for y in 0..TEX_SIZE {
        for x in 0..TEX_SIZE {
            let h = hash(x as u32, y as u32, seed);

            let r = 105u8.wrapping_add((h % 50) as u8);
            let g = 55u8.wrapping_add(((h >> 8) % 35) as u8);
            let b = 22u8.wrapping_add(((h >> 16) % 25) as u8);

            // Brick mortar every 16 pixels
            let mortar = y % 16 == 0 || y % 16 == 15;

            let (r, g, b) = if mortar {
                (42, 20, 8)
            } else {
                let crack_hash = hash(x.wrapping_add(1000) as u32, y as u32, seed);
                let crack_val = (crack_hash % 100) as u8;
                if crack_val < 8 {
                    // dark crack
                    let factor = 0.30 + (crack_val as f64 / 8.0) * 0.25;
                    (
                        (r as f64 * factor) as u8,
                        (g as f64 * factor) as u8,
                        (b as f64 * factor) as u8,
                    )
                } else if crack_val < 14 {
                    // light crack highlight
                    (
                        (r.saturating_add(25)).min(255),
                        (g.saturating_add(18)).min(255),
                        (b.saturating_add(10)).min(255),
                    )
                } else {
                    (r, g, b)
                }
            };

            tex[y * TEX_SIZE + x] =
                0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
        }
    }
    tex
}

/// Gray stone-tile floor with a dark hole in the centre — the exit staircase.
pub fn generate_exit_texture(rng: &mut impl Rng) -> Vec<u32> {
    let mut tex = generate_floor_texture(rng);

    let cx = TEX_SIZE as f64 / 2.0;
    let cy = TEX_SIZE as f64 / 2.0;
    let hole_radius = TEX_SIZE as f64 * 0.35;
    let fade_zone = TEX_SIZE as f64 * 0.08;

    for y in 0..TEX_SIZE {
        for x in 0..TEX_SIZE {
            let dx = x as f64 - cx;
            let dy = y as f64 - cy;
            let dist = (dx * dx + dy * dy).sqrt();

            if dist < hole_radius {
                // Deep black void.
                let edge = (dist / hole_radius).clamp(0.0, 1.0);
                // Slight blueish tint near the edge for depth.
                let r = (8.0 * (1.0 - edge)) as u8;
                let g = (6.0 * (1.0 - edge)) as u8;
                let b = (12.0 * edge + 4.0 * (1.0 - edge)) as u8;
                tex[y * TEX_SIZE + x] =
                    0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
            } else if dist < hole_radius + fade_zone {
                // Fade zone: blend from floor to darkness.
                let t = ((dist - hole_radius) / fade_zone).clamp(0.0, 1.0);
                let floor_color = tex[y * TEX_SIZE + x];
                let fr = ((floor_color >> 16) & 0xFF) as f64;
                let fg = ((floor_color >> 8) & 0xFF) as f64;
                let fb = (floor_color & 0xFF) as f64;

                let r = (fr * t + 8.0 * (1.0 - t)) as u8;
                let g = (fg * t + 6.0 * (1.0 - t)) as u8;
                let b = (fb * t + 12.0 * (1.0 - t)) as u8;
                tex[y * TEX_SIZE + x] =
                    0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
            }
        }
    }
    tex
}

/// Gray stone-tile floor — 64×64, checkerboard with grout and subtle colour tint.
pub fn generate_floor_texture(rng: &mut impl Rng) -> Vec<u32> {
    let seed = rng.gen::<u64>();
    let mut tex = vec![0u32; TEX_SIZE * TEX_SIZE];

    for y in 0..TEX_SIZE {
        for x in 0..TEX_SIZE {
            let h = hash(x as u32, y as u32, seed);
            let h2 = hash(
                x.wrapping_add(2000) as u32,
                y.wrapping_add(2000) as u32,
                seed,
            );

            let tile_x = x / 16;
            let tile_y = y / 16;
            let is_light_tile = (tile_x + tile_y) % 2 == 0;

            let local_x = x % 16;
            let local_y = y % 16;
            let is_grout = local_x == 0 || local_y == 0;

            let base_gray: u8 = if is_light_tile { 115 } else { 95 };
            let noise = (h % 15) as u8;

            let tint_r = (h2 % 8) as i16 - 4i16;
            let tint_b = ((h2 >> 8) % 8) as i16 - 4i16;

            let gray = if is_grout {
                base_gray.saturating_sub(30).saturating_add(noise / 3)
            } else {
                base_gray.saturating_add(noise / 2)
            };

            let r = (gray as i16 + tint_r).clamp(0, 255) as u8;
            let g = gray;
            let b = (gray as i16 + tint_b).clamp(0, 255) as u8;

            tex[y * TEX_SIZE + x] =
                0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
        }
    }
    tex
}