wde_terrain/render/
renderer_gpu.rs1use std::collections::HashMap;
2
3use bevy::{ecs::system::SystemParamItem, prelude::*};
4use wde_renderer::prelude::*;
5
6use crate::{
7 manager::{ChunkPos, TerrainDirtyTile},
8 prelude::TerrainExtractor,
9 render::{extractor, renderer::TerrainDrawList}
10};
11
12#[derive(Asset, Clone, TypePath, Default)]
16pub struct TerrainChunkArrayBg {
17 pub heightmap_array: Option<Handle<Texture>>,
18 pub splatmap_arrays: Vec<Handle<Texture>>
19}
20impl RenderBinding for TerrainChunkArrayBg {
21 type Params = ();
22
23 fn describe(
24 &mut self,
25 _params: &SystemParamItem<Self::Params>,
26 builder: &mut RenderBindingBuilder
27 ) {
28 builder.add_texture_array_view_from_id(self.heightmap_array.as_ref().map(|h| h.id()));
29 for splatmap in &self.splatmap_arrays {
30 builder
31 .add_texture_array_view_from_id(Some(splatmap.id()))
32 .add_texture_sampler_from_id(Some(splatmap.id()));
33 }
34 }
35
36 fn label(&self) -> &str {
37 "terrain-chunk-array-render"
38 }
39}
40
41#[derive(Asset, Clone, TypePath, Default)]
44pub struct TerrainComputeArrayBg {
45 pub heightmap_array: Option<Handle<Texture>>,
46 pub splatmap_array: Option<Handle<Texture>>
47}
48impl RenderBinding for TerrainComputeArrayBg {
49 type Params = ();
50
51 fn describe(
52 &mut self,
53 _params: &SystemParamItem<Self::Params>,
54 builder: &mut RenderBindingBuilder
55 ) {
56 builder
57 .add_storage_texture_array_from_id(self.heightmap_array.as_ref().map(|h| h.id()))
58 .add_storage_texture_array_from_id(self.splatmap_array.as_ref().map(|h| h.id()));
59 }
60
61 fn label(&self) -> &str {
62 "terrain-chunk-array-compute"
63 }
64}
65
66#[derive(Resource, Default)]
69pub struct TerrainRendererGPU {
70 pub ready: bool,
72 pub pos_to_layer: HashMap<ChunkPos, u32>,
74 pub draw_list: TerrainDrawList,
76
77 pub heightmap_array: Option<Handle<Texture>>,
78 pub splatmap_arrays: Vec<Handle<Texture>>,
79
80 pub render_bind_group: Option<Handle<TerrainChunkArrayBg>>,
81 pub compute_bind_group: Option<Handle<TerrainComputeArrayBg>>,
82
83 pub dirty: Vec<TerrainDirtyTile>
84}
85
86impl TerrainRendererGPU {
87 #[allow(clippy::too_many_arguments)]
90 pub fn extract_dirty(
91 main_terrain_extractor: &mut TerrainExtractor,
92 render_terrain_extractor: &mut TerrainExtractor,
93 heightmap_array: Option<Handle<Texture>>,
94 splatmap_arrays: Vec<Handle<Texture>>,
95 pos_to_layer: HashMap<ChunkPos, u32>,
96 dirty: Vec<TerrainDirtyTile>,
97 draw_list: TerrainDrawList,
98 gpu_terrain_renderer: &mut TerrainRendererGPU
99 ) {
100 extractor::extract_dirty(main_terrain_extractor, render_terrain_extractor);
102
103 if gpu_terrain_renderer.heightmap_array.is_none() {
105 gpu_terrain_renderer.heightmap_array = heightmap_array;
106 gpu_terrain_renderer.splatmap_arrays = splatmap_arrays;
107 gpu_terrain_renderer.pos_to_layer = pos_to_layer;
108 }
109
110 gpu_terrain_renderer.dirty.extend(dirty);
111 gpu_terrain_renderer.draw_list = draw_list;
112 }
113
114 pub fn upload_dirty(
116 mut gpu: ResMut<TerrainRendererGPU>,
117 textures: Res<RenderAssets<GpuTexture>>,
118 render_instance: Res<RenderInstance>
119 ) {
120 if gpu.dirty.is_empty() {
121 return;
122 }
123 let render_instance = render_instance.0.read().unwrap();
124 let dirty = std::mem::take(&mut gpu.dirty);
125 for (pos, map_type, splat_index, tile_data) in dirty {
126 let layer = match gpu.pos_to_layer.get(&pos) {
127 Some(&l) => l,
128 None => continue
129 };
130 match map_type {
131 0 => {
132 if let Some(handle) = &gpu.heightmap_array
133 && let Some(tex) = textures.get(handle)
134 {
135 tex.texture.copy_from_buffer_layered(
136 &render_instance,
137 tex.texture.format,
138 layer,
139 bytemuck::cast_slice(&tile_data)
140 );
141 }
142 }
143 1 => {
144 if let Some(handle) = gpu.splatmap_arrays.get(splat_index as usize)
145 && let Some(tex) = textures.get(handle)
146 {
147 tex.texture.copy_from_buffer_layered(
148 &render_instance,
149 tex.texture.format,
150 layer,
151 bytemuck::cast_slice(&tile_data)
152 );
153 }
154 }
155 _ => unreachable!()
156 }
157 }
158 }
159
160 pub fn prepare_bind_groups(
162 asset_server: Res<AssetServer>,
163 mut gpu: ResMut<TerrainRendererGPU>
164 ) {
165 if gpu.ready {
166 return;
167 }
168 if gpu.heightmap_array.is_none() || gpu.splatmap_arrays.is_empty() {
169 return;
170 }
171
172 if gpu.render_bind_group.is_none() {
173 gpu.render_bind_group = Some(asset_server.add(TerrainChunkArrayBg {
174 heightmap_array: gpu.heightmap_array.clone(),
175 splatmap_arrays: gpu.splatmap_arrays.clone()
176 }));
177 }
178 if gpu.compute_bind_group.is_none() {
180 gpu.compute_bind_group = Some(asset_server.add(TerrainComputeArrayBg {
181 heightmap_array: gpu.heightmap_array.clone(),
182 splatmap_array: gpu.splatmap_arrays.first().cloned()
183 }));
184 }
185
186 gpu.ready = gpu.render_bind_group.is_some() && gpu.compute_bind_group.is_some();
187 }
188}