summaryrefslogtreecommitdiff
path: root/src/proc_gen/herringbone.rs
blob: 271b7a159be3b140748a07125603df7764bb71cc (plain)
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
use fxhash::hash32;
use glam::IVec2;

use crate::{
    config::TILESET_SCALING,
    proc_gen::tileset_loader::{Tile, TileOrientation, Tileset, TilesetPixelType},
};

pub fn split_position(world: IVec2, tileset: &Tileset) -> (IVec2, IVec2, TileOrientation) {
    let s = tileset.dimensions.short as i32;
    let tx = world.x.div_euclid(s);
    let ty = world.y.div_euclid(s);
    let fx = world.x.rem_euclid(s);
    let fy = world.y.rem_euclid(s);

    match (tx - ty).rem_euclid(4) {
        // left half of horizontal tile
        0 => (
            IVec2::new(tx, ty),
            IVec2::new(fx, fy),
            TileOrientation::Horizontal,
        ),
        // right half of horizontal tile
        1 => (
            IVec2::new(tx - 1, ty),
            IVec2::new(fx + s, fy),
            TileOrientation::Horizontal,
        ),
        // top half of vertical tile
        3 => (
            IVec2::new(tx, ty),
            IVec2::new(fx, fy),
            TileOrientation::Vertical,
        ),
        // bottom half of vertical tile
        2 => (
            IVec2::new(tx, ty - 1),
            IVec2::new(fx, fy + s),
            TileOrientation::Vertical,
        ),
        _ => unreachable!(),
    }
}

// better "%" for hashes since it prefers the high bits
#[inline]
fn reduce(h: u32, n: usize) -> usize {
    (((h as u128) * (n as u128)) >> 32) as usize
}

fn pick_variant(tile_pos: IVec2, orientation: TileOrientation, tileset: &Tileset) -> &Tile {
    let h = hash32(&tile_pos);
    match orientation {
        TileOrientation::Horizontal => {
            &tileset.horizontal_tiles[reduce(h, tileset.horizontal_tiles.len())]
        }
        TileOrientation::Vertical => {
            &tileset.vertical_tiles[reduce(h, tileset.vertical_tiles.len())]
        }
    }
}

// TODO: optimize
fn get_pixel_at_position(world: IVec2, tileset: &Tileset) -> TilesetPixelType {
    let scaled_world = world.div_euclid(IVec2::new(TILESET_SCALING, TILESET_SCALING));
    let (tile_pos, local, orientation) = split_position(scaled_world, tileset);
    let tile = pick_variant(tile_pos, orientation, tileset);
    let width = match orientation {
        TileOrientation::Horizontal => tileset.dimensions.long as i32,
        TileOrientation::Vertical => tileset.dimensions.short as i32,
    };
    tile.pixels[(local.x + local.y * width) as usize]
}

pub fn tiles_overlapping(
    a: IVec2,
    b: IVec2,
    tileset: &Tileset,
) -> impl Iterator<Item = (IVec2, &Tile, TileOrientation)> {
    let s = IVec2::new(
        tileset.dimensions.short as i32,
        tileset.dimensions.short as i32,
    );

    let ta = a.div_euclid(s) - 1;
    let tb = (b - 1).div_euclid(s);

    (ta.y..=tb.y)
        .flat_map(move |ty| (ta.x..=tb.x).map(move |tx| (tx, ty)))
        .filter_map(move |(tx, ty)| match (tx - ty).rem_euclid(4) {
            0 => Some((
                IVec2::new(tx, ty),
                pick_variant(IVec2::new(tx, ty), TileOrientation::Horizontal, tileset),
                TileOrientation::Horizontal,
            )),
            3 => Some((
                IVec2::new(tx, ty),
                pick_variant(IVec2::new(tx, ty), TileOrientation::Vertical, tileset),
                TileOrientation::Vertical,
            )),
            _ => None,
        })
}

// pub fn get_cell_at_position(world: IVec2, tileset: &Tileset) -> Cell {
//     let pixel = get_pixel_at_position(world, tileset);
//     match pixel {
//         TilesetPixelType::Void => Cell::void(),
//         TilesetPixelType::Terrain => Cell::from_material(MaterialId::Dirt),
//     }
// }