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