wde_terrain/render/dependencies/
terrain_buffer.rs

1use bevy::{ecs::system::SystemParamItem, prelude::*};
2use wde_renderer::prelude::*;
3
4use crate::{
5    manager::{CHUNK_HEIGHT, CHUNK_RENDER_SUBDIVISIONS, CHUNK_SIZE},
6    render::renderer_gpu::TerrainRendererGPU
7};
8
9// The maximum number of terrain tiles that can be rendered
10const MAX_TERRAIN_TILES: usize = 1000;
11
12#[repr(C)]
13#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
14pub struct TerrainDescription {
15    pub tile_size: [f32; 3],
16    pub tile_subdivisions: f32
17}
18#[repr(C)]
19#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
20pub struct TerrainTileDescription {
21    pub pos: [f32; 2],
22    pub lod: f32,
23    pub _padding: f32
24}
25
26/// Struct to hold the terrain uniform layout description.
27#[derive(Asset, Clone, Debug, Default, TypePath)]
28pub struct TerrainBuffer;
29impl TerrainBuffer {
30    pub const DESC_BIND: u32 = 0;
31    pub const TILES_BIND: u32 = 1;
32}
33impl RenderData for TerrainBuffer {
34    type Params = ();
35
36    fn describe(_params: &mut SystemParamItem<Self::Params>, builder: &mut RenderDataBuilder) {
37        builder.add_buffer(
38            Self::DESC_BIND,
39            Buffer {
40                label: "ssbo-terrain-description-buffer".to_string(),
41                size: std::mem::size_of::<TerrainDescription>(),
42                usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
43                content: Some(
44                    bytemuck::cast_slice(&[TerrainDescription {
45                        tile_size: [CHUNK_SIZE, CHUNK_HEIGHT, CHUNK_SIZE],
46                        tile_subdivisions: CHUNK_RENDER_SUBDIVISIONS as f32
47                    }])
48                    .into()
49                )
50            }
51        );
52        builder.add_buffer(
53            Self::TILES_BIND,
54            Buffer {
55                label: "ssbo-terrain-tiles-buffer".to_string(),
56                size: std::mem::size_of::<TerrainTileDescription>() * MAX_TERRAIN_TILES,
57                usage: BufferUsage::STORAGE | BufferUsage::COPY_DST,
58                content: None
59            }
60        );
61    }
62}
63
64#[derive(Asset, Clone, TypePath, Default)]
65pub struct TerrainBufferBinding;
66impl RenderBinding for TerrainBufferBinding {
67    type Params = SRenderData<TerrainBuffer>;
68
69    fn describe(
70        &mut self,
71        buffer: &SystemParamItem<Self::Params>,
72        builder: &mut RenderBindingBuilder
73    ) {
74        builder.add_buffer(buffer, TerrainBuffer::DESC_BIND);
75        builder.add_buffer(buffer, TerrainBuffer::TILES_BIND);
76    }
77
78    fn label(&self) -> &str {
79        "terrain_buffer_binding"
80    }
81}
82
83// System to update the terrain tiles buffer with the current visible tiles
84pub(crate) fn update_terrain_tiles_buffer(
85    render_instance: Res<RenderInstance>,
86    terrain_buffer: ResRenderData<TerrainBuffer>,
87    buffers: Res<RenderAssets<GpuBuffer>>,
88    terrain_tiles: Res<TerrainRendererGPU>,
89    mut is_set: Local<bool>
90) {
91    // Check if is ready
92    if *is_set || !terrain_tiles.ready {
93        return;
94    }
95
96    // Get the buffer
97    let terrain_buffer = match terrain_buffer.iter().next() {
98        Some((_, buffer)) => buffer,
99        None => return
100    };
101    let tile_buffer = match buffers.get(
102        &terrain_buffer
103            .get_buffer(TerrainBuffer::TILES_BIND)
104            .unwrap()
105    ) {
106        Some(buffer) => buffer,
107        None => return
108    };
109
110    // Prepare the data
111    let data: Vec<TerrainTileDescription> = terrain_tiles
112        .tiles
113        .iter()
114        .map(|tile| TerrainTileDescription {
115            pos: [tile.position.x as f32, tile.position.y as f32],
116            lod: 1.0,
117            _padding: 0.0
118        })
119        .collect();
120
121    // Update the buffer
122    let render_instance = render_instance.0.read().unwrap();
123    tile_buffer
124        .buffer
125        .write(&render_instance, bytemuck::cast_slice(&data), 0);
126
127    *is_set = true;
128}