Build a scene-pipeline material bind group. Each of the four texture indices can be zero to mean 'not present' — we substitute a sensible default so the fragment shader doesn't need per-slot presence flags: • base color → textures[0] (white) • normal map → flat (0,0,1) texture — TBN becomes a no-op • MR texture → white (roughness=1, metallic=1 before factor) • emissive → white — multiplied
(
&self,
base_color_tex_idx: u32,
normal_tex_idx: u32,
metallic_roughness_tex_idx: u32,
emissive_tex_idx: u32,
occlusion_tex_idx: u32,
material_
| 8756 | /// • emissive → white — multiplied by emissive_factor, which |
| 8757 | /// is zero for non-emissive materials, giving 0. |
| 8758 | pub fn create_scene_material_bg( |
| 8759 | &self, |
| 8760 | base_color_tex_idx: u32, |
| 8761 | normal_tex_idx: u32, |
| 8762 | metallic_roughness_tex_idx: u32, |
| 8763 | emissive_tex_idx: u32, |
| 8764 | occlusion_tex_idx: u32, |
| 8765 | material_uniform: &wgpu::Buffer, |
| 8766 | ) -> wgpu::BindGroup { |
| 8767 | let view_or_white = |idx: u32| -> wgpu::TextureView { |
| 8768 | let i = idx as usize; |
| 8769 | let tex = if idx == 0 || i >= self.textures.len() { |
| 8770 | &self.textures[0] |
| 8771 | } else { |
| 8772 | &self.textures[i] |
| 8773 | }; |
| 8774 | tex.create_view(&wgpu::TextureViewDescriptor::default()) |
| 8775 | }; |
| 8776 | |
| 8777 | let base_view = view_or_white(base_color_tex_idx); |
| 8778 | let mr_view = view_or_white(metallic_roughness_tex_idx); |
| 8779 | let em_view = view_or_white(emissive_tex_idx); |
| 8780 | // Occlusion default = white texture: shader does |
| 8781 | // `mix(1.0, occlusion, strength)`, so a white sample gives |
| 8782 | // 1.0 (no occlusion) regardless of strength. |
| 8783 | let occ_view = view_or_white(occlusion_tex_idx); |
| 8784 | |
| 8785 | // Normal map uses the flat-normal default when not specified |
| 8786 | // (white here would give incorrect perturbation since it |
| 8787 | // decodes to (1, 1, 1) in tangent space instead of (0, 0, 1)). |
| 8788 | // All four view locals live until after create_bind_group, so |
| 8789 | // taking references to them is safe. |
| 8790 | let normal_view_owned = if normal_tex_idx == 0 || (normal_tex_idx as usize) >= self.textures.len() { |
| 8791 | None |
| 8792 | } else { |
| 8793 | Some(self.textures[normal_tex_idx as usize].create_view(&wgpu::TextureViewDescriptor::default())) |
| 8794 | }; |
| 8795 | let normal_view_ref: &wgpu::TextureView = normal_view_owned |
| 8796 | .as_ref() |
| 8797 | .unwrap_or(&self.default_normal_view); |
| 8798 | |
| 8799 | self.device.create_bind_group(&wgpu::BindGroupDescriptor { |
| 8800 | label: Some("scene_material_bg"), |
| 8801 | layout: &self.scene_material_layout, |
| 8802 | entries: &[ |
| 8803 | wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&base_view) }, |
| 8804 | wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&self.sampler) }, |
| 8805 | wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(normal_view_ref) }, |
| 8806 | wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::Sampler(&self.sampler) }, |
| 8807 | wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::TextureView(&mr_view) }, |
| 8808 | wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::Sampler(&self.sampler) }, |
| 8809 | wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::TextureView(&em_view) }, |
| 8810 | wgpu::BindGroupEntry { binding: 7, resource: wgpu::BindingResource::Sampler(&self.sampler) }, |
| 8811 | wgpu::BindGroupEntry { binding: 8, resource: material_uniform.as_entire_binding() }, |
| 8812 | wgpu::BindGroupEntry { binding: 9, resource: wgpu::BindingResource::TextureView(&occ_view) }, |
| 8813 | wgpu::BindGroupEntry { binding: 10, resource: wgpu::BindingResource::Sampler(&self.sampler) }, |
| 8814 | ], |
| 8815 | }) |
no outgoing calls
no test coverage detected