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}