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) = (1024, 1024);
27const MATERIALS: [&str; 4] = ["grass", "dirt", "rock", "sand"];
28const TEX_TYPES: [&str; 4] = ["albedo", "normal", "roughness", "ambient_occlusion"];
29const TEX_FORMATS: [TextureFormat; 4] = [
30 TextureFormat::Rgba8UnormSrgb,
31 TextureFormat::Rgba8Unorm,
32 TextureFormat::R8Unorm,
33 TextureFormat::R8Unorm
34];
35
36#[derive(Resource, Default, ExtractResource, Clone)]
37pub(crate) struct TerrainMaterialTextures {
38 albedo_textures: Vec<Option<Handle<Texture>>>,
39 normal_textures: Vec<Option<Handle<Texture>>>,
40 roughness_textures: Vec<Option<Handle<Texture>>>,
41 ao_textures: Vec<Option<Handle<Texture>>>
42}
43
44#[derive(Asset, Clone, Debug, TypePath, Default)]
45pub(crate) struct TerrainMaterials;
46impl TerrainMaterials {
47 pub const TERRAIN_ALBEDO_IDX: u32 = 0;
48 pub const TERRAIN_NORMAL_IDX: u32 = 1;
49 pub const TERRAIN_ROUGHNESS_IDX: u32 = 2;
50 pub const TERRAIN_AO_IDX: u32 = 3;
51}
52impl RenderData for TerrainMaterials {
53 type Params = (SResMut<TerrainMaterialTextures>, SRes<AssetServer>);
54
55 fn describe(
56 (materials, asset_server): &mut SystemParamItem<Self::Params>,
57 builder: &mut RenderDataBuilder
58 ) {
59 let usages = TextureUsages::COPY_SRC | TextureUsages::TEXTURE_BINDING;
61 for material in MATERIALS {
62 for (i, tex_type) in TEX_TYPES.iter().enumerate() {
63 let path = format!("core/models/terrain/{}/{}.png", material, tex_type);
64 let handle = asset_server.load_with_settings(
65 path,
66 move |settings: &mut TextureLoaderSettings| {
67 settings.label = format!("terrain-{}-{}", material, tex_type);
68 settings.format = TEX_FORMATS[i];
69 settings.usages = usages;
70 }
71 );
72 match i {
73 0 => materials.albedo_textures.push(Some(handle)),
74 1 => materials.normal_textures.push(Some(handle)),
75 2 => materials.roughness_textures.push(Some(handle)),
76 3 => materials.ao_textures.push(Some(handle)),
77 _ => unreachable!()
78 }
79 }
80 }
81
82 let size = TEX_SIZE; let usages = TextureUsages::TEXTURE_BINDING
85 | TextureUsages::COPY_DST
86 | TextureUsages::RENDER_ATTACHMENT;
87 builder
88 .add_texture(
89 Self::TERRAIN_ALBEDO_IDX,
90 Texture {
91 label: "terrain_material_albedo_array".to_string(),
92 size,
93 format: TextureFormat::Rgba8UnormSrgb,
94 usages,
95 layer_count: materials.albedo_textures.len() as u32,
96 mip_level_count: 0, ..Default::default()
98 }
99 )
100 .add_texture(
101 Self::TERRAIN_NORMAL_IDX,
102 Texture {
103 label: "terrain_material_normal_array".to_string(),
104 size,
105 format: TextureFormat::Rgba8Unorm,
106 usages,
107 layer_count: materials.normal_textures.len() as u32,
108 mip_level_count: 0, ..Default::default()
110 }
111 )
112 .add_texture(
113 Self::TERRAIN_ROUGHNESS_IDX,
114 Texture {
115 label: "terrain_material_roughness_array".to_string(),
116 size,
117 format: TextureFormat::R8Unorm,
118 usages,
119 layer_count: materials.roughness_textures.len() as u32,
120 mip_level_count: 0, ..Default::default()
122 }
123 )
124 .add_texture(
125 Self::TERRAIN_AO_IDX,
126 Texture {
127 label: "terrain_material_ao_array".to_string(),
128 size,
129 format: TextureFormat::R8Unorm,
130 usages,
131 layer_count: materials.ao_textures.len() as u32,
132 mip_level_count: 0, ..Default::default()
134 }
135 );
136 }
137}
138
139#[derive(Asset, Clone, Debug, TypePath, Default)]
140pub(crate) struct TerrainMaterialsBinding;
141impl RenderBinding for TerrainMaterialsBinding {
142 type Params = SRenderData<TerrainMaterials>;
143
144 fn describe(
145 &mut self,
146 mat: &SystemParamItem<Self::Params>,
147 builder: &mut RenderBindingBuilder
148 ) {
149 builder
150 .add_texture_array_view(mat, TerrainMaterials::TERRAIN_ALBEDO_IDX)
151 .add_texture_sampler(mat, TerrainMaterials::TERRAIN_ALBEDO_IDX)
152 .add_texture_array_view(mat, TerrainMaterials::TERRAIN_NORMAL_IDX)
153 .add_texture_sampler(mat, TerrainMaterials::TERRAIN_NORMAL_IDX)
154 .add_texture_array_view(mat, TerrainMaterials::TERRAIN_ROUGHNESS_IDX)
155 .add_texture_sampler(mat, TerrainMaterials::TERRAIN_ROUGHNESS_IDX)
156 .add_texture_array_view(mat, TerrainMaterials::TERRAIN_AO_IDX)
157 .add_texture_sampler(mat, TerrainMaterials::TERRAIN_AO_IDX);
158 }
159
160 fn label(&self) -> &str {
161 "terrain_texture_arrays"
162 }
163}
164
165fn fill_arrays(
167 render_instance: Res<RenderInstance>,
168 textures: Res<RenderAssets<GpuTexture>>,
169 materials: ResRenderData<TerrainMaterials>,
170 material_textures: Res<TerrainMaterialTextures>,
171 mut is_done: Local<bool>
172) {
173 if *is_done {
175 return;
176 }
177
178 let materials = match materials.iter().next() {
180 Some((_, materials)) => materials,
181 None => return
182 };
183 let (albedo_array, normal_array, roughness_array, ao_array) = match (
184 textures.get(
185 &materials
186 .get_texture(TerrainMaterials::TERRAIN_ALBEDO_IDX)
187 .unwrap()
188 ),
189 textures.get(
190 &materials
191 .get_texture(TerrainMaterials::TERRAIN_NORMAL_IDX)
192 .unwrap()
193 ),
194 textures.get(
195 &materials
196 .get_texture(TerrainMaterials::TERRAIN_ROUGHNESS_IDX)
197 .unwrap()
198 ),
199 textures.get(
200 &materials
201 .get_texture(TerrainMaterials::TERRAIN_AO_IDX)
202 .unwrap()
203 )
204 ) {
205 (Some(albedo), Some(normal), Some(roughness), Some(ao)) => (albedo, normal, roughness, ao),
206 _ => return
207 };
208
209 let render_instance = render_instance.0.read().unwrap();
211 let size = TEX_SIZE;
212 for i in 0..material_textures.albedo_textures.len() {
213 let albedo_tex = match textures.get(material_textures.albedo_textures[i].as_ref().unwrap())
214 {
215 Some(tex) => tex,
216 None => return
217 };
218 albedo_array.texture.copy_from_texture_layered(
219 &render_instance,
220 &albedo_tex.texture.texture,
221 i,
222 size
223 );
224
225 let normal_tex = match textures.get(material_textures.normal_textures[i].as_ref().unwrap())
226 {
227 Some(tex) => tex,
228 None => return
229 };
230 normal_array.texture.copy_from_texture_layered(
231 &render_instance,
232 &normal_tex.texture.texture,
233 i,
234 size
235 );
236
237 let roughness_tex =
238 match textures.get(material_textures.roughness_textures[i].as_ref().unwrap()) {
239 Some(tex) => tex,
240 None => return
241 };
242 roughness_array.texture.copy_from_texture_layered(
243 &render_instance,
244 &roughness_tex.texture.texture,
245 i,
246 size
247 );
248
249 let ao_tex = match textures.get(material_textures.ao_textures[i].as_ref().unwrap()) {
250 Some(tex) => tex,
251 None => return
252 };
253 ao_array.texture.copy_from_texture_layered(
254 &render_instance,
255 &ao_tex.texture.texture,
256 i,
257 size
258 );
259 }
260
261 debug!("Generating mipmaps for terrain material arrays.");
263 albedo_array.texture.generate_mipmaps(&render_instance);
264 normal_array.texture.generate_mipmaps(&render_instance);
265 roughness_array.texture.generate_mipmaps(&render_instance);
266 ao_array.texture.generate_mipmaps(&render_instance);
267
268 *is_done = true;
269}