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)]
88 pub fn extract_dirty(
89 main_terrain_extractor: &mut TerrainExtractor,
90 render_terrain_extractor: &mut TerrainExtractor,
91 heightmap_array: Option<Handle<Texture>>,
92 splatmap_array: Option<Handle<Texture>>,
93 pos_to_layer: HashMap<ChunkPos, u32>,
94 dirty: Vec<TerrainDirtyTile>,
95 terrain_renderer_chunk_count: u32,
96 gpu_terrain_renderer: &mut TerrainRendererGPU
97 ) {
98 extractor::extract_dirty(main_terrain_extractor, render_terrain_extractor);
100
101 if gpu_terrain_renderer.heightmap_array.is_none() {
103 gpu_terrain_renderer.heightmap_array = heightmap_array;
104 gpu_terrain_renderer.splatmap_array = splatmap_array;
105 gpu_terrain_renderer.pos_to_layer = pos_to_layer;
106 gpu_terrain_renderer.chunk_count = terrain_renderer_chunk_count;
107 }
108
109 gpu_terrain_renderer.dirty.extend(dirty);
110 }
111
112 pub fn upload_dirty(
114 mut gpu: ResMut<TerrainRendererGPU>,
115 textures: Res<RenderAssets<GpuTexture>>,
116 render_instance: Res<RenderInstance>
117 ) {
118 if gpu.dirty.is_empty() {
119 return;
120 }
121 let render_instance = render_instance.0.read().unwrap();
122 let dirty = std::mem::take(&mut gpu.dirty);
123 for (pos, map_type, _, tile_data) in dirty {
124 let layer = match gpu.pos_to_layer.get(&pos) {
125 Some(&l) => l,
126 None => continue
127 };
128 match map_type {
129 0 => {
130 if let Some(handle) = &gpu.heightmap_array
131 && let Some(tex) = textures.get(handle)
132 {
133 tex.texture.copy_from_buffer_layered(
134 &render_instance,
135 tex.texture.format,
136 layer,
137 bytemuck::cast_slice(&tile_data)
138 );
139 }
140 }
141 1 => {
142 if let Some(handle) = &gpu.splatmap_array
143 && let Some(tex) = textures.get(handle)
144 {
145 tex.texture.copy_from_buffer_layered(
146 &render_instance,
147 tex.texture.format,
148 layer,
149 bytemuck::cast_slice(&tile_data)
150 );
151 }
152 }
153 _ => unreachable!()
154 }
155 }
156 }
157
158 pub fn prepare_bind_groups(
160 asset_server: Res<AssetServer>,
161 mut gpu: ResMut<TerrainRendererGPU>
162 ) {
163 if gpu.ready {
164 return;
165 }
166 if gpu.heightmap_array.is_none() || gpu.splatmap_array.is_none() {
167 return;
168 }
169
170 if gpu.render_bind_group.is_none() {
171 gpu.render_bind_group = Some(asset_server.add(TerrainChunkArrayBg {
172 heightmap_array: gpu.heightmap_array.clone(),
173 splatmap_array: gpu.splatmap_array.clone()
174 }));
175 }
176 if gpu.compute_bind_group.is_none() {
177 gpu.compute_bind_group = Some(asset_server.add(TerrainComputeArrayBg {
178 heightmap_array: gpu.heightmap_array.clone(),
179 splatmap_array: gpu.splatmap_array.clone()
180 }));
181 }
182
183 gpu.ready = gpu.render_bind_group.is_some() && gpu.compute_bind_group.is_some();
184 }
185}