1use {
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
108fn 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}