wde_terrain/render/renderer.rs
1use std::collections::HashMap;
2
3use bevy::prelude::*;
4use wde_renderer::prelude::*;
5
6use crate::manager::{
7 CHUNK_COUNT, CHUNK_RENDER_SUBDIVISIONS, ChunkPos, SPLAT_MAP_COUNT, Terrain, TerrainDirtyTile
8};
9
10/// Represents a single terrain tile, containing its position and the associated heightmap, normal map, and splat maps.
11#[derive(Default, Clone)]
12pub struct TerrainRenderTile {
13 /// The position of the tile in world space (x, z)
14 pub position: ChunkPos,
15 /// The heightmap and splat maps for this tile
16 pub heightmap: Handle<Texture>,
17 pub splatmaps: Vec<Handle<Texture>>
18}
19
20/// Holds the tiles used for rendering. Note that all tiles are not necessarily rendered.
21#[derive(Component)]
22pub struct TerrainRenderer {
23 /// Pointer from tile position (x, z) to the corresponding tile datas
24 pub pos_to_tile: HashMap<ChunkPos, usize>,
25 /// A list of terrain render tiles that make up the entire terrain.
26 pub tiles: Vec<TerrainRenderTile>,
27 // List of tile maps that are dirty and need to be re-processed
28 pub dirty: Vec<Option<TerrainDirtyTile>>
29}
30impl TerrainRenderer {
31 /// Initializes the terrain renderer by creating the heightmap and splat map textures for each tile, and setting up the mapping from tile positions to their corresponding data.
32 ///
33 /// # Arguments
34 /// * `asset_server` - The Bevy asset server used to create the texture assets for the heightmaps and splat maps.
35 ///
36 /// # Returns
37 /// A `TerrainRenderer` component containing the initialized terrain render tiles with their respective heightmap and splat map textures, as well as the mapping from tile positions to their data.
38 pub fn new(asset_server: &AssetServer) -> Self {
39 let usages = TextureUsages::COPY_SRC
40 | TextureUsages::COPY_DST
41 | TextureUsages::TEXTURE_BINDING
42 | TextureUsages::STORAGE_BINDING;
43 let mut pos_to_tile = HashMap::new();
44 let mut tiles = Vec::new();
45 for i in 0..CHUNK_COUNT {
46 for j in 0..CHUNK_COUNT {
47 // Create the empty heightmap and splatmap textures for the tile
48 let heightmap = asset_server.add(Texture {
49 label: format!("heightmap_{}_{}", i, j),
50 size: (CHUNK_RENDER_SUBDIVISIONS, CHUNK_RENDER_SUBDIVISIONS),
51 format: TextureFormat::R8Unorm,
52 usages,
53 ..Default::default()
54 });
55 let mut splatmaps = Vec::new();
56 for k in 0..SPLAT_MAP_COUNT / 4 {
57 splatmaps.push(asset_server.add(Texture {
58 label: format!("splatmap_{}_{}-{}", i, j, k),
59 size: (CHUNK_RENDER_SUBDIVISIONS, CHUNK_RENDER_SUBDIVISIONS),
60 format: TextureFormat::Rgba8Unorm,
61 usages,
62 ..Default::default()
63 }));
64 }
65
66 // Calculate the world position of the tile (centered around the origin)
67 let px = i as i32 - (CHUNK_COUNT as i32 / 2);
68 let pz = j as i32 - (CHUNK_COUNT as i32 / 2);
69 let position = IVec2::new(px, pz);
70
71 // Create the tile and add it to the list
72 tiles.push(TerrainRenderTile {
73 position,
74 heightmap,
75 splatmaps
76 });
77 pos_to_tile.insert(position, tiles.len() - 1);
78 }
79 }
80 TerrainRenderer {
81 dirty: Vec::new(),
82 tiles,
83 pos_to_tile
84 }
85 }
86
87 /// Extracts the dirty tiles from the main terrain
88 pub fn extract_dirty(
89 mut renderer: Query<&mut TerrainRenderer>,
90 mut terrain: Query<&mut Terrain>
91 ) {
92 let mut terrain_renderer = match renderer.iter_mut().next() {
93 Some(terrain) => terrain,
94 None => return
95 };
96 let mut terrain = match terrain.iter_mut().next() {
97 Some(terrain) => terrain,
98 None => return
99 };
100
101 // Clear the dirty tiles list before processing
102 terrain_renderer.dirty.clear();
103
104 // Extract the dirty tiles from the main terrain and move them to the renderer resource
105 for dirty_tile in &terrain.dirty_render {
106 terrain_renderer.dirty.push(dirty_tile.clone());
107 }
108
109 // Clear the dirty tiles list after processing
110 terrain.dirty_render.clear();
111 }
112}