summer_garden/
summer_garden.rs

1// An example which shows that exhaustive tile sets aren't so much desired by WFC
2use {
3    simple_tiled_wfc::grid_generation::{WfcModule, WfcContext, WfcContextBuilder},
4    macroquad::prelude::{scene::{Node, RefMut}, *},
5    macroquad::miniquad::{TextureParams, TextureFormat, TextureWrap},
6    ron::de::from_reader,
7    std::{ sync::mpsc::channel }
8};
9
10mod serialization {
11    use serde::Deserialize;
12    use std::collections::HashMap;
13
14    #[derive(Copy, Clone, PartialEq, Debug, Deserialize)]
15    pub enum TerrainType {
16        Land,
17        GrassSharp,
18        GrassRound,
19        Water
20    }
21
22    #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Deserialize)]
23    pub enum TreeType {
24        None,
25        Pine,
26        Oak,
27        Bush
28    }
29
30    #[derive(Copy, Clone, PartialEq, Debug, Deserialize)]
31    pub enum TileKind {
32        Inner,
33        Outer
34    }
35
36    #[derive(Copy, Clone, PartialEq, Debug, Deserialize)]
37    pub enum NeighbourKind {
38        WangCorners,
39        RelOffset(i32)
40    }
41
42    #[derive(Copy, Clone, Debug, Deserialize)]
43    pub struct NeighbourChooseStrategy {
44        pub north: NeighbourKind,
45        pub west: NeighbourKind,
46        pub east: NeighbourKind,
47        pub south: NeighbourKind,
48    }
49
50    #[derive(Copy, Clone, Debug, Deserialize)]
51    pub struct TileSidesPattern {
52        pub north_west: TileKind,
53        pub north_east: TileKind,
54        pub south_west: TileKind,
55        pub south_east: TileKind,
56    }
57
58    #[derive(Copy, Clone, Debug, Deserialize)]
59    pub struct TileSides {
60        pub north_west: TerrainType,
61        pub north_east: TerrainType,
62        pub south_west: TerrainType,
63        pub south_east: TerrainType,
64    }
65
66    #[derive(Copy, Clone, Deserialize)]
67    pub struct TerrainTilesConfig {
68        pub x_offset: i32,
69        pub y_offset: i32,
70        pub outer_type: TerrainType,
71        pub inner_type: TerrainType,
72    }
73
74    #[derive(Copy, Clone, Debug, Deserialize)]
75    pub struct SubRect {
76        pub x: i32,
77        pub y: i32,
78        pub width: i32,
79        pub height: i32
80    }
81
82    #[derive(Clone, Deserialize)]
83    pub struct SummerGardenAtlas {
84        pub tree_sub_rects: HashMap<TreeType, SubRect>,
85
86        pub reduced_wang_patterns: Vec<TileSidesPattern>,
87        pub extended_set_1_patterns_north_west: Vec<TileSidesPattern>,
88        pub extended_set_1_patterns_north_east: Vec<TileSidesPattern>,
89        pub extended_set_1_patterns_south_west: Vec<TileSidesPattern>,
90        pub extended_set_1_patterns_south_east: Vec<TileSidesPattern>,
91        pub extended_set_2_patterns_north_west: Vec<TileSidesPattern>,
92        pub extended_set_2_patterns_north_east: Vec<TileSidesPattern>,
93        pub extended_set_2_patterns_south_west: Vec<TileSidesPattern>,
94        pub extended_set_2_patterns_south_east: Vec<TileSidesPattern>,
95
96        pub vertical_bridge_sides: Vec<TileSides>,
97        pub horizontal_bridge_sides: Vec<TileSides>,
98
99        pub reduced_wang_neighbour_strategy: Vec<NeighbourChooseStrategy>,
100        pub neighbour_strategy_2_x_2: Vec<NeighbourChooseStrategy>,
101        pub neighbour_strategy_3_x_3: Vec<NeighbourChooseStrategy>,
102
103        pub terrain_tile_configs: Vec<TerrainTilesConfig>
104    }
105}
106use serialization::*;
107
108//noinspection DuplicatedCode
109fn draw_subrect(tex: Texture2D, subrect: &SubRect, x: f32, y: f32, scale: f32) {
110    let InternalGlContext {
111        quad_context: ctx, ..
112    } = unsafe { get_internal_gl() };
113    draw_texture_ex(
114        tex,
115        x * ctx.dpi_scale(), y * ctx.dpi_scale(),
116        WHITE,
117        DrawTextureParams {
118            source: Some(Rect::new(
119                subrect.x as f32, subrect.y as f32,
120                subrect.width as f32, subrect.height as f32
121            )),
122            dest_size: Some([
123                subrect.width as f32 * ctx.dpi_scale() * scale,
124                subrect.height as f32 * ctx.dpi_scale() * scale
125            ].into()),
126            ..Default::default()
127        },
128    );
129}
130
131fn draw_subrect_pivoted(tex: Texture2D, subrect: &SubRect, x: f32, y: f32, scale: f32) {
132    let InternalGlContext {
133        quad_context: ctx, ..
134    } = unsafe { get_internal_gl() };
135    draw_texture_ex(
136        tex,
137        (x - subrect.width as f32 / (2.0 / scale)) * ctx.dpi_scale(),
138        (y - subrect.height as f32 * scale) * ctx.dpi_scale(),
139        WHITE,
140        DrawTextureParams {
141            source: Some(Rect::new(
142                subrect.x as f32, subrect.y as f32,
143                subrect.width as f32, subrect.height as f32
144            )),
145            dest_size: Some([
146                subrect.width as f32 * ctx.dpi_scale() * scale,
147                subrect.height as f32 * ctx.dpi_scale() * scale
148            ].into()),
149            ..Default::default()
150        },
151    );
152}
153
154fn window_conf() -> Conf {
155    Conf {
156        window_title: "Summer garden".to_owned(),
157        fullscreen: false,
158        window_width: 1280,
159        window_height: 800,
160        high_dpi: true,
161        ..Default::default()
162    }
163}
164
165type CustomBitSet = [u8; 32];
166
167struct WangTilemap {
168    w: usize,
169    h: usize,
170    atlas: SummerGardenAtlas,
171    draw_scale: f32,
172    tile_width: usize,
173    tile_height: usize,
174    tiles: Vec<SubRect>,
175    modules: Vec<WfcModule<CustomBitSet>>,
176    texture: Texture2D,
177    map_data: Vec<usize>,
178    corner_tree_data: Vec<TreeType>,
179    cell_tree_data: Vec<TreeType>
180}
181
182impl WangTilemap {
183    pub async fn new(w: usize, h: usize, draw_scale: f32) -> Self {
184        let InternalGlContext {
185            quad_context: ctx, ..
186        } = unsafe { get_internal_gl() };
187
188        let texture_bytes = load_file("assets/reduced_wang_scheme.png").await.unwrap();
189
190        let img = image::load_from_memory(&texture_bytes[..])
191            .unwrap_or_else(|e| panic!("{}", e))
192            .to_rgba8();
193
194        let img_w = img.width();
195        let img_h = img.height();
196
197        let texture = Texture2D::from_miniquad_texture(
198            miniquad::Texture::from_data_and_format(
199                ctx,
200                &img.into_raw(),
201                TextureParams {
202                    format: TextureFormat::RGBA8,
203                    wrap: TextureWrap::Clamp,
204                    filter: FilterMode::Nearest,
205                    width: img_w,
206                    height: img_h
207                }
208            )
209        );
210
211        let atlas_bytes = load_file("assets/reduced_wang_scheme.ron").await.unwrap();
212        let atlas: SummerGardenAtlas = from_reader(&atlas_bytes[..]).unwrap();
213
214        let mut tile_sides = Vec::new();
215        let mut neighbour_strategies = Vec::new();
216        let mut tiles = Vec::new();
217
218        {
219            for tile_cfg in atlas.terrain_tile_configs.iter() {
220                for pattern in &[
221                    &atlas.reduced_wang_patterns[..],
222                    &atlas.extended_set_1_patterns_north_west[..],
223                    &atlas.extended_set_1_patterns_north_east[..],
224                    &atlas.extended_set_1_patterns_south_west[..],
225                    &atlas.extended_set_1_patterns_south_east[..],
226                    &atlas.extended_set_2_patterns_north_west[..],
227                    &atlas.extended_set_2_patterns_north_east[..],
228                    &atlas.extended_set_2_patterns_south_west[..],
229                    &atlas.extended_set_2_patterns_south_east[..],
230                ] {
231                    tile_sides.extend(
232                        pattern.iter().map(
233                            |pattern| {
234                                TileSides {
235                                    north_west: match pattern.north_west {
236                                        TileKind::Inner => { tile_cfg.inner_type }
237                                        TileKind::Outer => { tile_cfg.outer_type }
238                                    },
239                                    north_east: match pattern.north_east {
240                                        TileKind::Inner => { tile_cfg.inner_type }
241                                        TileKind::Outer => { tile_cfg.outer_type }
242                                    },
243                                    south_west: match pattern.south_west {
244                                        TileKind::Inner => { tile_cfg.inner_type }
245                                        TileKind::Outer => { tile_cfg.outer_type }
246                                    },
247                                    south_east: match pattern.south_east {
248                                        TileKind::Inner => { tile_cfg.inner_type }
249                                        TileKind::Outer => { tile_cfg.outer_type }
250                                    }
251                                }
252                            }
253                        )
254                    );
255                }
256                for j in 0..4 {
257                    for i in 0..4 {
258                        tiles.push(SubRect{
259                            x: i * 32 + tile_cfg.x_offset,
260                            y: j * 32 + tile_cfg.y_offset,
261                            width: 32,
262                            height: 32
263                        })
264                    }
265                }
266                for jj in 0..2 {
267                    for ii in 0..2 {
268                        for j in 0..2 {
269                            for i in 0..2 {
270                                tiles.push(SubRect{
271                                    x: (i + ii * 2) * 32 + tile_cfg.x_offset + 256,
272                                    y: (j + jj * 2) * 32 + tile_cfg.y_offset,
273                                    width: 32,
274                                    height: 32
275                                })
276                            }
277                        }
278                    }
279                }
280                for jj in 0..2 {
281                    for ii in 0..2 {
282                        for j in 0..2 {
283                            for i in 0..2 {
284                                tiles.push(SubRect{
285                                    x: (i + ii * 2) * 32 + tile_cfg.x_offset,
286                                    y: (j + jj * 2) * 32 + tile_cfg.y_offset + 256,
287                                    width: 32,
288                                    height: 32
289                                })
290                            }
291                        }
292                    }
293                }
294                neighbour_strategies.extend_from_slice(&atlas.reduced_wang_neighbour_strategy[..]);
295                for _ in 0..8 {
296                    neighbour_strategies.extend_from_slice(&atlas.neighbour_strategy_2_x_2[..]);
297                }
298            }
299            tile_sides.extend_from_slice(&atlas.vertical_bridge_sides[..]);
300            tile_sides.extend_from_slice(&atlas.horizontal_bridge_sides[..]);
301            for j in 0..3 {
302                for i in 0..3 {
303                    tiles.push(SubRect{
304                        x: i * 32 + 256,
305                        y: j * 32 + 256,
306                        width: 32,
307                        height: 32
308                    })
309                }
310            }
311            for j in 0..3 {
312                for i in 0..3 {
313                    tiles.push(SubRect{
314                        x: i * 32 + 256 + 96,
315                        y: j * 32 + 256,
316                        width: 32,
317                        height: 32
318                    })
319                }
320            }
321            neighbour_strategies.extend_from_slice(&atlas.neighbour_strategy_3_x_3[..]);
322            neighbour_strategies.extend_from_slice(&atlas.neighbour_strategy_3_x_3[..]);
323
324            assert_eq!(tile_sides.len(), neighbour_strategies.len());
325        }
326
327        let mut modules = Vec::new();
328        for i in 0..tile_sides.len() {
329            let current_sides = tile_sides[i];
330            let mut module: WfcModule<CustomBitSet> = WfcModule::new();
331            match neighbour_strategies[i].north {
332                NeighbourKind::WangCorners => {
333                    for j in 0..tile_sides.len() {
334                        if neighbour_strategies[j].south != NeighbourKind::WangCorners {
335                            continue;
336                        }
337                        if tile_sides[j].south_west == current_sides.north_west &&
338                            tile_sides[j].south_east == current_sides.north_east {
339                            module.add_north_neighbour(j);
340                        }
341                    }
342                }
343                NeighbourKind::RelOffset(offset) => {
344                    let new_offset = (i as i32 + offset) as usize;
345                    module.add_north_neighbour(new_offset);
346                }
347            }
348            match neighbour_strategies[i].south {
349                NeighbourKind::WangCorners => {
350                    for j in 0..tile_sides.len() {
351                        if neighbour_strategies[j].north != NeighbourKind::WangCorners {
352                            continue;
353                        }
354                        if tile_sides[j].north_west == current_sides.south_west &&
355                            tile_sides[j].north_east == current_sides.south_east {
356                            module.add_south_neighbour(j);
357                        }
358                    }
359                }
360                NeighbourKind::RelOffset(offset) => {
361                    let new_offset = (i as i32 + offset) as usize;
362                    module.add_south_neighbour(new_offset);
363                }
364            }
365            match neighbour_strategies[i].east {
366                NeighbourKind::WangCorners => {
367                    for j in 0..tile_sides.len() {
368                        if neighbour_strategies[j].west != NeighbourKind::WangCorners {
369                            continue;
370                        }
371                        if tile_sides[j].north_west == current_sides.north_east &&
372                            tile_sides[j].south_west == current_sides.south_east {
373                            module.add_east_neighbour(j);
374                        }
375                    }
376                }
377                NeighbourKind::RelOffset(offset) => {
378                    let new_offset = (i as i32 + offset) as usize;
379                    module.add_east_neighbour(new_offset);
380                }
381            }
382            match neighbour_strategies[i].west {
383                NeighbourKind::WangCorners => {
384                    for j in 0..tile_sides.len() {
385                        if neighbour_strategies[j].east != NeighbourKind::WangCorners {
386                            continue;
387                        }
388                        if tile_sides[j].north_east == current_sides.north_west &&
389                            tile_sides[j].south_east == current_sides.south_west {
390                            module.add_west_neighbour(j);
391                        }
392                    }
393                }
394                NeighbourKind::RelOffset(offset) => {
395                    let new_offset = (i as i32 + offset) as usize;
396                    module.add_west_neighbour(new_offset);
397                }
398            }
399            modules.push(module);
400        }
401
402        Self {
403            w,
404            h,
405            draw_scale,
406            atlas,
407            tile_width: 32,
408            tile_height: 32,
409            tiles,
410            modules,
411            texture,
412            map_data: vec![0; w*h],
413            cell_tree_data: vec![TreeType::None; w*h],
414            corner_tree_data: vec![TreeType::None; (w+1)*(h+1)]
415        }
416    }
417
418    pub fn generate_new_map(&mut self) {
419        let mut wfc_context: WfcContext<CustomBitSet> = WfcContextBuilder
420        ::new(&self.modules, self.w, self.h)
421            .build();
422
423        let (tx, rc) = channel();
424
425        wfc_context.collapse(100, tx.clone());
426
427        let results = rc.recv()
428            .unwrap()
429            .unwrap_or_else(|_| vec![0; self.w * self.h]);
430
431        self.map_data.clear();
432        self.map_data.extend_from_slice(&results[..]);
433
434        self.plant_trees()
435    }
436
437    fn plant_trees(&mut self) {
438        self.cell_tree_data.fill(TreeType::None);
439        self.corner_tree_data.fill(TreeType::None);
440
441        for j in 1..self.h - 1 {
442            for i in 1..self.w - 1 {
443                let idx = j * self.w + i;
444                if !(self.map_data[idx] >= 48 && self.map_data[idx] < 96) {
445                    continue;
446                }
447
448                let lop_left_corner_idx = j * (self.w + 1) + i;
449
450                self.try_plant_cell_tree(idx);
451
452                self.try_plant_corner_tree(lop_left_corner_idx);
453
454                if j == self.h - 2 {
455                    self.try_plant_corner_tree(lop_left_corner_idx + self.w + 1);
456                }
457                if i == self.w - 2 && j == self.h - 2 {
458                    self.try_plant_corner_tree(lop_left_corner_idx + self.w + 2);
459                }
460                if i == self.w - 2 {
461                    self.try_plant_corner_tree(lop_left_corner_idx + 1);
462                }
463            }
464        }
465    }
466
467    fn try_plant_corner_tree(&mut self, idx: usize) {
468        if rand::gen_range(0, 100) < 4 {
469            self.corner_tree_data[idx] = TreeType::Bush;
470        } else if rand::gen_range(0, 100) < 12 {
471            self.corner_tree_data[idx] = TreeType::Oak;
472        } else if rand::gen_range(0, 100) < 18 {
473            self.corner_tree_data[idx] = TreeType::Pine;
474        }
475    }
476
477    fn try_plant_cell_tree(&mut self, idx: usize) {
478        if rand::gen_range(0, 100) < 4 {
479            self.cell_tree_data[idx] = TreeType::Bush;
480        } else if rand::gen_range(0, 100) < 12 {
481            self.cell_tree_data[idx] = TreeType::Oak;
482        } else if rand::gen_range(0, 100) < 18 {
483            self.cell_tree_data[idx] = TreeType::Pine;
484        }
485    }
486}
487
488impl Node for WangTilemap {
489    fn update(mut node: RefMut<Self>) {
490        if is_key_pressed(KeyCode::Space) {
491            node.generate_new_map();
492        }
493    }
494
495    fn draw(node: RefMut<Self>) {
496        let InternalGlContext {
497            quad_context: ctx, ..
498        } = unsafe { get_internal_gl() };
499
500        let start_x = screen_width() / ctx.dpi_scale();
501        let start_x = (start_x - (node.w * node.tile_width) as f32 * node.draw_scale) / 2.0;
502
503        let start_y = screen_height() / ctx.dpi_scale();
504        let start_y = (start_y - (node.h * node.tile_height) as f32 * node.draw_scale) / 2.0;
505
506        for j in 0..node.h {
507            for i in 0..node.w {
508                let idx = node.w * j + i;
509                let x = start_x + (node.tile_width * i) as f32 * node.draw_scale;
510                let y = start_y + (node.tile_height * j) as f32 * node.draw_scale;
511                let idx = node.map_data[idx];
512                draw_subrect(node.texture, &(node.tiles[idx]), x, y, node.draw_scale);
513            }
514        }
515        for j in 0..node.h+1 {
516            for i in 0..node.w+1 {
517                let idx = (node.w + 1) * j + i;
518                let x = start_x + (node.tile_width * i - 16) as f32 * node.draw_scale;
519                let y = start_y + (node.tile_height * j - 12) as f32 * node.draw_scale;
520                let cell_tree = node.corner_tree_data[idx];
521                if let Some(subrect) = node.atlas.tree_sub_rects.get(&cell_tree) {
522                    draw_subrect_pivoted(node.texture, subrect, x, y, node.draw_scale)
523                }
524            }
525
526            if j < node.h {
527                for i in 0..node.w {
528                    let idx = node.w * j + i;
529                    let x = start_x + (node.tile_width * i) as f32 * node.draw_scale;
530                    let y = start_y + (node.tile_height * j + 4) as f32 * node.draw_scale;
531                    let cell_tree = node.cell_tree_data[idx];
532                    if let Some(subrect) = node.atlas.tree_sub_rects.get(&cell_tree) {
533                        draw_subrect_pivoted(node.texture, subrect, x, y, node.draw_scale)
534                    }
535                }
536            }
537        }
538    }
539}
540
541#[macroquad::main(window_conf)]
542async fn main() {
543    scene::add_node({
544        let mut tilemap = WangTilemap::new(80, 50, 0.5).await;
545        tilemap.generate_new_map();
546        tilemap
547    });
548    loop {
549        if is_key_pressed(KeyCode::Escape) {
550            break;
551        }
552        clear_background(Color::new(0.12, 0.1, 0.15, 1.00));
553        next_frame().await;
554    }
555}