three_d/renderer/material/
normal_material.rs

1use crate::core::*;
2use crate::renderer::*;
3
4///
5/// Render the object with colors that reflect its normals which primarily is used for debug purposes.
6/// A normal with an x value of -1 yields 0.0 in the red channel and an x value of 1 yields 1.0 in the red channel.
7/// The same mapping is applied from y value to green channel and z value to blue channel.
8///
9#[derive(Clone)]
10pub struct NormalMaterial {
11    /// A scalar multiplier applied to each normal vector of the [Self::normal_texture].
12    pub normal_scale: f32,
13    /// A tangent space normal map, also known as bump map.
14    pub normal_texture: Option<Texture2DRef>,
15    /// Render states.
16    pub render_states: RenderStates,
17}
18
19impl NormalMaterial {
20    /// Constructs a new normal material from a [CpuMaterial] where only relevant information is used.
21    pub fn new(context: &Context, cpu_material: &CpuMaterial) -> Self {
22        let normal_texture = cpu_material
23            .normal_texture
24            .as_ref()
25            .map(|cpu_texture| Texture2DRef::from_cpu_texture(context, cpu_texture));
26        Self {
27            normal_scale: cpu_material.normal_scale,
28            normal_texture,
29            render_states: RenderStates::default(),
30        }
31    }
32
33    /// Creates a normal material from a [PhysicalMaterial].
34    pub fn from_physical_material(physical_material: &PhysicalMaterial) -> Self {
35        Self {
36            normal_scale: physical_material.normal_scale,
37            normal_texture: physical_material.normal_texture.clone(),
38            render_states: RenderStates {
39                write_mask: WriteMask::default(),
40                blend: Blend::Disabled,
41                ..physical_material.render_states
42            },
43        }
44    }
45}
46
47impl FromCpuMaterial for NormalMaterial {
48    fn from_cpu_material(context: &Context, cpu_material: &CpuMaterial) -> Self {
49        Self::new(context, cpu_material)
50    }
51}
52
53impl Material for NormalMaterial {
54    fn id(&self) -> EffectMaterialId {
55        EffectMaterialId::NormalMaterial(self.normal_texture.is_some())
56    }
57
58    fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
59        let mut source = String::new();
60        if self.normal_texture.is_some() {
61            source.push_str("#define USE_TEXTURE\nin vec2 uvs;\nin vec3 tang;\nin vec3 bitang;\n");
62        }
63        source.push_str(include_str!("shaders/normal_material.frag"));
64        source
65    }
66
67    fn use_uniforms(&self, program: &Program, _viewer: &dyn Viewer, _lights: &[&dyn Light]) {
68        if let Some(ref tex) = self.normal_texture {
69            program.use_uniform("normalScale", self.normal_scale);
70            program.use_uniform("textureTransformation", tex.transformation);
71            program.use_texture("normalTexture", tex);
72        }
73    }
74    fn render_states(&self) -> RenderStates {
75        self.render_states
76    }
77    fn material_type(&self) -> MaterialType {
78        MaterialType::Opaque
79    }
80}
81
82impl Default for NormalMaterial {
83    fn default() -> Self {
84        Self {
85            normal_texture: None,
86            normal_scale: 1.0,
87            render_states: RenderStates::default(),
88        }
89    }
90}