1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Physically-based material.

use crate::types::Texture;
use amethyst_assets::{Asset, Handle};
use amethyst_core::ecs::prelude::DenseVecStorage;

/// Material reference this part of the texture
#[derive(Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct TextureOffset {
    /// Start and end offset for U coordinate
    pub u: (f32, f32),
    /// Start and end offset for V coordinate
    pub v: (f32, f32),
}

impl Default for TextureOffset {
    fn default() -> Self {
        TextureOffset {
            u: (0., 1.),
            v: (0., 1.),
        }
    }
}

/// A physically based Material with metallic workflow, fully utilized in PBR render pass.
#[derive(Debug, Clone, PartialEq)]
pub struct Material {
    /// Alpha cutoff: the value at which we do not draw the pixel
    pub alpha_cutoff: f32,
    /// Diffuse map.
    pub albedo: Handle<Texture>,
    /// Emission map.
    pub emission: Handle<Texture>,
    /// Normal map.
    pub normal: Handle<Texture>,
    /// Metallic-roughness map. (B channel metallic, G channel roughness)
    pub metallic_roughness: Handle<Texture>,
    /// Ambient occlusion map.
    pub ambient_occlusion: Handle<Texture>,
    /// Cavity map.
    pub cavity: Handle<Texture>,
    /// Texture offset
    pub uv_offset: TextureOffset,
}

impl Asset for Material {
    const NAME: &'static str = "renderer::Material";
    type Data = Self;
    type HandleStorage = DenseVecStorage<Handle<Self>>;
}

/// A resource providing default textures for `Material`.
/// These will be be used by the renderer in case a texture
/// handle points to a texture which is not loaded already.
/// Additionally, you can use it to fill up the fields of
/// `Material` you don't want to specify.
#[derive(Debug, Clone)]
pub struct MaterialDefaults(pub Material);

/// Trait providing generic access to a collection of texture handles
pub trait StaticTextureSet<'a>:
    Clone + Copy + std::fmt::Debug + PartialEq + Eq + std::hash::Hash + Send + Sync + 'static
{
    /// Iterator type to access this texture sets handles
    type Iter: Iterator<Item = &'a Handle<Texture>>;

    /// Returns an iterator to the textures associated with a given material.
    fn textures(mat: &'a Material) -> Self::Iter;

    /// ALWAYS RETURNS 1
    fn len() -> usize {
        1
    }
}

/// Type alias for a tuple collection of a complete PBR texture set.
pub type FullTextureSet = (
    TexAlbedo,
    TexEmission,
    TexNormal,
    TexMetallicRoughness,
    TexAmbientOcclusion,
    TexCavity,
);

macro_rules! impl_texture {
    ($name:ident, $prop:ident) => {
        #[doc = "Macro Generated Texture Type"]
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
        pub struct $name;
        impl<'a> StaticTextureSet<'a> for $name {
            type Iter = std::iter::Once<&'a Handle<Texture>>;
            #[inline(always)]
            fn textures(mat: &'a Material) -> Self::Iter {
                std::iter::once(&mat.$prop)
            }
        }
    };
}

impl_texture!(TexAlbedo, albedo);
impl_texture!(TexEmission, emission);
impl_texture!(TexNormal, normal);
impl_texture!(TexMetallicRoughness, metallic_roughness);
impl_texture!(TexAmbientOcclusion, ambient_occlusion);
impl_texture!(TexCavity, cavity);

macro_rules! recursive_iter {
    (@value $first:expr, $($rest:expr),*) => { $first.chain(recursive_iter!(@value $($rest),*)) };
    (@value $last:expr) => { $last };
    (@type $first:ty, $($rest:ty),*) => { std::iter::Chain<$first, recursive_iter!(@type $($rest),*)> };
    (@type $last:ty) => { $last };
}

macro_rules! impl_texture_set_tuple {
    ($($from:ident),*) => {
        impl<'a, $($from,)*> StaticTextureSet<'a> for ($($from),*,)
        where
            $($from: StaticTextureSet<'a>),*,
        {
            type Iter = recursive_iter!(@type $($from::Iter),*);
            #[inline(always)]
            fn textures(mat: &'a Material) -> Self::Iter {
                recursive_iter!(@value $($from::textures(mat)),*)
            }
            fn len() -> usize {
                $($from::len() + )* 0
            }
        }
    }
}

impl_texture_set_tuple!(A);
impl_texture_set_tuple!(A, B);
impl_texture_set_tuple!(A, B, C);
impl_texture_set_tuple!(A, B, C, D);
impl_texture_set_tuple!(A, B, C, D, E);
impl_texture_set_tuple!(A, B, C, D, E, F);