wde_terrain/render/dependencies/
materials.rs1use wde_logger::prelude::*;
2
3use bevy::{
4 ecs::system::{
5 SystemParamItem,
6 lifetimeless::{SRes, SResMut}
7 },
8 prelude::*
9};
10use wde_renderer::prelude::*;
11
12use crate::render::dependencies::terrain_buffer::TERRAIN_MATERIAL_COUNT;
13
14pub(crate) struct TerrainMaterialsPlugin;
15impl Plugin for TerrainMaterialsPlugin {
16 fn build(&self, app: &mut App) {
17 app.init_resource::<TerrainMaterialTextures>().add_plugins((
18 ExtractResourcePlugin::<TerrainMaterialTextures>::default(),
19 RenderBindingRegisterPlugin::<TerrainMaterialsBinding>::default(),
20 RenderDataRegisterPlugin::<TerrainMaterials>::default()
21 ));
22 app.get_sub_app_mut(RenderApp)
23 .unwrap()
24 .add_systems(Render, fill_arrays.in_set(RenderSet::Prepare));
25 }
26}
27
28const TEX_SIZE: (u32, u32) = (1024, 1024);
29const MATERIALS: [&str; TERRAIN_MATERIAL_COUNT] = [
32 "mars_dust",
33 "mars_regolith",
34 "mars_pebbles",
35 "mars_gravel",
36 "mars_sediment",
37 "mars_cliff",
38 "mars_basalt",
39 "mars_compacted"
40];
41const TEX_TYPES: [&str; 5] = [
42 "albedo",
43 "normal",
44 "roughness",
45 "ambient_occlusion",
46 "displacement"
47];
48const TEX_FORMATS: [TextureFormat; 5] = [
49 TextureFormat::Rgba8UnormSrgb,
50 TextureFormat::Rgba8Unorm,
51 TextureFormat::R8Unorm,
52 TextureFormat::R8Unorm,
53 TextureFormat::R8Unorm
54];
55
56#[derive(Resource, Default, ExtractResource, Clone)]
57pub(crate) struct TerrainMaterialTextures {
58 albedo_textures: Vec<Option<Handle<Texture>>>,
59 normal_textures: Vec<Option<Handle<Texture>>>,
60 roughness_textures: Vec<Option<Handle<Texture>>>,
61 ao_textures: Vec<Option<Handle<Texture>>>,
62 displacement_textures: Vec<Option<Handle<Texture>>>
63}
64
65#[derive(Asset, Clone, Debug, TypePath, Default)]
66pub(crate) struct TerrainMaterials;
67impl TerrainMaterials {
68 pub const TERRAIN_ALBEDO_IDX: u32 = 0;
69 pub const TERRAIN_NORMAL_IDX: u32 = 1;
70 pub const TERRAIN_ROUGHNESS_IDX: u32 = 2;
71 pub const TERRAIN_AO_IDX: u32 = 3;
72 pub const TERRAIN_DISPLACEMENT_IDX: u32 = 4;
73}
74impl RenderData for TerrainMaterials {
75 type Params = (SResMut<TerrainMaterialTextures>, SRes<AssetServer>);
76
77 fn describe(
78 (materials, asset_server): &mut SystemParamItem<Self::Params>,
79 builder: &mut RenderDataBuilder
80 ) {
81 let usages = TextureUsages::COPY_SRC | TextureUsages::TEXTURE_BINDING;
83 for material in MATERIALS {
84 for (i, tex_type) in TEX_TYPES.iter().enumerate() {
85 let path = format!("core/models/terrain/{}/{}.png", material, tex_type);
86 let handle = asset_server.load_with_settings(
87 path,
88 move |settings: &mut TextureLoaderSettings| {
89 settings.label = format!("terrain-{}-{}", material, tex_type);
90 settings.format = TEX_FORMATS[i];
91 settings.usages = usages;
92 }
93 );
94 match i {
95 0 => materials.albedo_textures.push(Some(handle)),
96 1 => materials.normal_textures.push(Some(handle)),
97 2 => materials.roughness_textures.push(Some(handle)),
98 3 => materials.ao_textures.push(Some(handle)),
99 4 => materials.displacement_textures.push(Some(handle)),
100 _ => unreachable!()
101 }
102 }
103 }
104
105 let size = TEX_SIZE; let usages = TextureUsages::TEXTURE_BINDING
108 | TextureUsages::COPY_DST
109 | TextureUsages::RENDER_ATTACHMENT;
110 builder
111 .add_texture(
112 Self::TERRAIN_ALBEDO_IDX,
113 Texture {
114 label: "terrain_material_albedo_array".to_string(),
115 size,
116 format: TextureFormat::Rgba8UnormSrgb,
117 usages,
118 layer_count: materials.albedo_textures.len() as u32,
119 mip_level_count: 0, tiling: true,
121 ..Default::default()
122 }
123 )
124 .add_texture(
125 Self::TERRAIN_NORMAL_IDX,
126 Texture {
127 label: "terrain_material_normal_array".to_string(),
128 size,
129 format: TextureFormat::Rgba8Unorm,
130 usages,
131 layer_count: materials.normal_textures.len() as u32,
132 mip_level_count: 0, tiling: true,
134 ..Default::default()
135 }
136 )
137 .add_texture(
138 Self::TERRAIN_ROUGHNESS_IDX,
139 Texture {
140 label: "terrain_material_roughness_array".to_string(),
141 size,
142 format: TextureFormat::R8Unorm,
143 usages,
144 layer_count: materials.roughness_textures.len() as u32,
145 mip_level_count: 0, tiling: true,
147 ..Default::default()
148 }
149 )
150 .add_texture(
151 Self::TERRAIN_AO_IDX,
152 Texture {
153 label: "terrain_material_ao_array".to_string(),
154 size,
155 format: TextureFormat::R8Unorm,
156 usages,
157 layer_count: materials.ao_textures.len() as u32,
158 mip_level_count: 0, tiling: true,
160 ..Default::default()
161 }
162 )
163 .add_texture(
164 Self::TERRAIN_DISPLACEMENT_IDX,
165 Texture {
166 label: "terrain_material_displacement_array".to_string(),
167 size,
168 format: TextureFormat::R8Unorm,
169 usages,
170 layer_count: materials.displacement_textures.len() as u32,
171 mip_level_count: 0, tiling: true,
173 ..Default::default()
174 }
175 );
176 }
177}
178
179#[derive(Asset, Clone, Debug, TypePath, Default)]
180pub(crate) struct TerrainMaterialsBinding;
181impl RenderBinding for TerrainMaterialsBinding {
182 type Params = SRenderData<TerrainMaterials>;
183
184 fn describe(
185 &mut self,
186 mat: &SystemParamItem<Self::Params>,
187 builder: &mut RenderBindingBuilder
188 ) {
189 builder
190 .add_texture_array_view(mat, TerrainMaterials::TERRAIN_ALBEDO_IDX)
191 .add_texture_sampler(mat, TerrainMaterials::TERRAIN_ALBEDO_IDX)
192 .add_texture_array_view(mat, TerrainMaterials::TERRAIN_NORMAL_IDX)
193 .add_texture_sampler(mat, TerrainMaterials::TERRAIN_NORMAL_IDX)
194 .add_texture_array_view(mat, TerrainMaterials::TERRAIN_ROUGHNESS_IDX)
195 .add_texture_sampler(mat, TerrainMaterials::TERRAIN_ROUGHNESS_IDX)
196 .add_texture_array_view(mat, TerrainMaterials::TERRAIN_AO_IDX)
197 .add_texture_sampler(mat, TerrainMaterials::TERRAIN_AO_IDX)
198 .add_texture_array_view(mat, TerrainMaterials::TERRAIN_DISPLACEMENT_IDX)
199 .add_texture_sampler(mat, TerrainMaterials::TERRAIN_DISPLACEMENT_IDX);
200 }
201
202 fn label(&self) -> &str {
203 "terrain_texture_arrays"
204 }
205}
206
207fn fill_arrays(
209 render_instance: Res<RenderInstance>,
210 textures: Res<RenderAssets<GpuTexture>>,
211 materials: ResRenderData<TerrainMaterials>,
212 material_textures: Res<TerrainMaterialTextures>,
213 mut is_done: Local<bool>
214) {
215 if *is_done {
217 return;
218 }
219
220 let materials = match materials.iter().next() {
222 Some((_, materials)) => materials,
223 None => return
224 };
225 let (albedo_array, normal_array, roughness_array, ao_array, displacement_array) = match (
226 textures.get(
227 &materials
228 .get_texture(TerrainMaterials::TERRAIN_ALBEDO_IDX)
229 .unwrap()
230 ),
231 textures.get(
232 &materials
233 .get_texture(TerrainMaterials::TERRAIN_NORMAL_IDX)
234 .unwrap()
235 ),
236 textures.get(
237 &materials
238 .get_texture(TerrainMaterials::TERRAIN_ROUGHNESS_IDX)
239 .unwrap()
240 ),
241 textures.get(
242 &materials
243 .get_texture(TerrainMaterials::TERRAIN_AO_IDX)
244 .unwrap()
245 ),
246 textures.get(
247 &materials
248 .get_texture(TerrainMaterials::TERRAIN_DISPLACEMENT_IDX)
249 .unwrap()
250 )
251 ) {
252 (Some(albedo), Some(normal), Some(roughness), Some(ao), Some(displacement)) => {
253 (albedo, normal, roughness, ao, displacement)
254 }
255 _ => return
256 };
257
258 let render_instance = render_instance.0.read().unwrap();
260 let size = TEX_SIZE;
261 for i in 0..material_textures.albedo_textures.len() {
262 let albedo_tex = match textures.get(material_textures.albedo_textures[i].as_ref().unwrap())
263 {
264 Some(tex) => tex,
265 None => return
266 };
267 albedo_array.texture.copy_from_texture_layered(
268 &render_instance,
269 &albedo_tex.texture.texture,
270 i,
271 size
272 );
273
274 let normal_tex = match textures.get(material_textures.normal_textures[i].as_ref().unwrap())
275 {
276 Some(tex) => tex,
277 None => return
278 };
279 normal_array.texture.copy_from_texture_layered(
280 &render_instance,
281 &normal_tex.texture.texture,
282 i,
283 size
284 );
285
286 let roughness_tex =
287 match textures.get(material_textures.roughness_textures[i].as_ref().unwrap()) {
288 Some(tex) => tex,
289 None => return
290 };
291 roughness_array.texture.copy_from_texture_layered(
292 &render_instance,
293 &roughness_tex.texture.texture,
294 i,
295 size
296 );
297
298 let ao_tex = match textures.get(material_textures.ao_textures[i].as_ref().unwrap()) {
299 Some(tex) => tex,
300 None => return
301 };
302 ao_array.texture.copy_from_texture_layered(
303 &render_instance,
304 &ao_tex.texture.texture,
305 i,
306 size
307 );
308
309 let displacement_tex =
310 match textures.get(material_textures.displacement_textures[i].as_ref().unwrap()) {
311 Some(tex) => tex,
312 None => return
313 };
314 displacement_array.texture.copy_from_texture_layered(
315 &render_instance,
316 &displacement_tex.texture.texture,
317 i,
318 size
319 );
320 }
321
322 debug!("Generating mipmaps for terrain material arrays.");
324 albedo_array.texture.generate_mipmaps(&render_instance);
325 normal_array.texture.generate_mipmaps(&render_instance);
326 roughness_array.texture.generate_mipmaps(&render_instance);
327 ao_array.texture.generate_mipmaps(&render_instance);
328 displacement_array
329 .texture
330 .generate_mipmaps(&render_instance);
331
332 *is_done = true;
333}