wde_terrain/render/dependencies/
terrain_buffer.rs1use bevy::{ecs::system::SystemParamItem, prelude::*};
2use wde_renderer::prelude::*;
3
4use crate::{
5 manager::{
6 CHUNK_HEIGHT, CHUNK_MAP_SIZE, CHUNK_RENDER_SUBDIVISIONS, CHUNK_SIZE, SPLAT_MAP_COUNT,
7 SPLAT_MAPS, TERRAIN_MIN_HEIGHT
8 },
9 render::renderer_gpu::TerrainRendererGPU
10};
11
12const MAX_TERRAIN_TILES: usize = 1024;
13pub const TERRAIN_MATERIAL_COUNT: usize = (SPLAT_MAPS * SPLAT_MAP_COUNT) as usize;
15
16#[derive(Resource, ExtractResource, Clone, Debug)]
18pub struct TerrainRenderSettings {
19 pub tiling: [f32; TERRAIN_MATERIAL_COUNT],
21 pub height_blend: [f32; TERRAIN_MATERIAL_COUNT]
24}
25impl Default for TerrainRenderSettings {
26 fn default() -> Self {
27 Self {
28 tiling: [10.0, 12.0, 5.0, 5.0, 12.0, 14.0, 12.0, 6.0],
30 height_blend: [0.6, 0.5, 0.8, 0.9, 0.7, 1.0, 1.0, 0.4]
31 }
32 }
33}
34
35#[repr(C)]
36#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
37pub struct TerrainDescription {
38 pub tile_size: [f32; 3],
39 pub tile_subdivisions: f32,
40 pub min_height: f32,
41 pub map_size: f32,
42 pub _padding: [f32; 2],
43 pub tiling: [[f32; 4]; SPLAT_MAPS as usize],
44 pub height_blend: [[f32; 4]; SPLAT_MAPS as usize]
45}
46impl TerrainDescription {
47 fn new(settings: &TerrainRenderSettings) -> Self {
48 let pack = |values: &[f32; TERRAIN_MATERIAL_COUNT]| {
49 std::array::from_fn(|k| std::array::from_fn(|c| values[k * 4 + c]))
50 };
51 Self {
52 tile_size: [CHUNK_SIZE, CHUNK_HEIGHT, CHUNK_SIZE],
53 tile_subdivisions: CHUNK_RENDER_SUBDIVISIONS as f32,
54 min_height: TERRAIN_MIN_HEIGHT,
55 map_size: CHUNK_MAP_SIZE as f32,
56 _padding: [0.0; 2],
57 tiling: pack(&settings.tiling),
58 height_blend: pack(&settings.height_blend)
59 }
60 }
61}
62
63#[repr(C)]
66#[derive(Clone, Copy, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)]
67pub struct TerrainTileDescription {
68 pub pos: [f32; 2],
69 pub lod: f32,
71 pub tile_layer: u32,
72 pub neighbor_lods: [f32; 4]
75}
76
77#[derive(Asset, Clone, Debug, Default, TypePath)]
78pub struct TerrainBuffer;
79impl TerrainBuffer {
80 pub const DESC_BIND: u32 = 0;
81 pub const TILES_BIND: u32 = 1;
82}
83impl RenderData for TerrainBuffer {
84 type Params = ();
85
86 fn describe(_params: &mut SystemParamItem<Self::Params>, builder: &mut RenderDataBuilder) {
87 builder
88 .add_buffer(
89 Self::DESC_BIND,
90 Buffer {
91 label: "terrain-description-buffer".to_string(),
92 size: std::mem::size_of::<TerrainDescription>(),
93 usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
94 content: Some(
95 bytemuck::cast_slice(&[TerrainDescription::new(
96 &TerrainRenderSettings::default()
97 )])
98 .into()
99 )
100 }
101 )
102 .add_buffer(
103 Self::TILES_BIND,
104 Buffer {
105 label: "terrain-tiles-buffer".to_string(),
106 size: std::mem::size_of::<TerrainTileDescription>() * MAX_TERRAIN_TILES,
107 usage: BufferUsage::STORAGE | BufferUsage::COPY_DST,
108 content: None
109 }
110 );
111 }
112}
113
114#[derive(Asset, Clone, TypePath, Default)]
115pub struct TerrainBufferBinding;
116impl RenderBinding for TerrainBufferBinding {
117 type Params = SRenderData<TerrainBuffer>;
118
119 fn describe(
120 &mut self,
121 buffer: &SystemParamItem<Self::Params>,
122 builder: &mut RenderBindingBuilder
123 ) {
124 builder
125 .add_buffer(buffer, TerrainBuffer::DESC_BIND)
126 .add_buffer(buffer, TerrainBuffer::TILES_BIND);
127 }
128
129 fn label(&self) -> &str {
130 "terrain_buffer_binding"
131 }
132}
133
134pub(crate) fn update_terrain_description_buffer(
135 render_instance: Res<RenderInstance>,
136 terrain_buffer: ResRenderData<TerrainBuffer>,
137 buffers: Res<RenderAssets<GpuBuffer>>,
138 settings: Res<TerrainRenderSettings>
139) {
140 if !settings.is_changed() {
141 return;
142 }
143 let terrain_buffer = match terrain_buffer.iter().next() {
144 Some((_, b)) => b,
145 None => return
146 };
147 let buf = match buffers.get(&terrain_buffer.get_buffer(TerrainBuffer::DESC_BIND).unwrap()) {
148 Some(b) => b,
149 None => return
150 };
151 let render_instance = render_instance.0.read().unwrap();
152 buf.buffer.write(
153 &render_instance,
154 bytemuck::cast_slice(&[TerrainDescription::new(&settings)]),
155 0
156 );
157}
158
159pub(crate) fn update_terrain_tiles_buffer(
161 render_instance: Res<RenderInstance>,
162 terrain_buffer: ResRenderData<TerrainBuffer>,
163 buffers: Res<RenderAssets<GpuBuffer>>,
164 terrain: Res<TerrainRendererGPU>
165) {
166 if !terrain.ready || terrain.draw_list.tiles.is_empty() {
167 return;
168 }
169 let terrain_buffer = match terrain_buffer.iter().next() {
170 Some((_, b)) => b,
171 None => return
172 };
173 let tile_buffer = match buffers.get(
174 &terrain_buffer
175 .get_buffer(TerrainBuffer::TILES_BIND)
176 .unwrap()
177 ) {
178 Some(b) => b,
179 None => return
180 };
181 let tiles = &terrain.draw_list.tiles[..terrain.draw_list.tiles.len().min(MAX_TERRAIN_TILES)];
182 let render_instance = render_instance.0.read().unwrap();
183 tile_buffer
184 .buffer
185 .write(&render_instance, bytemuck::cast_slice(tiles), 0);
186}