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