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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
//std
use std::sync::Arc;
// pbrt
use crate::core::bssrdf::SeparableBssrdfAdapter;
use crate::core::interaction::SurfaceInteraction;
use crate::core::material::{Material, TransportMode};
use crate::core::microfacet::{
    BeckmannDistribution, MicrofacetDistribution, TrowbridgeReitzDistribution,
};
use crate::core::pbrt::{Float, Spectrum};
use crate::core::reflection::{
    Bxdf, FourierBSDF, Fresnel, FresnelBlend, FresnelConductor, FresnelDielectric, FresnelNoOp,
    FresnelSpecular, LambertianReflection, LambertianTransmission, MicrofacetReflection,
    MicrofacetTransmission, OrenNayar, SpecularReflection, SpecularTransmission,
};
use crate::core::texture::Texture;
use crate::materials::disney::{
    DisneyClearCoat, DisneyDiffuse, DisneyFakeSS, DisneyMicrofacetDistribution, DisneyRetro,
    DisneySheen,
};
use crate::materials::hair::HairBSDF;

// see mixmat.h

/// The mix material takes two other materials and a texture and uses
/// the value returned by the texture to blend between the two
/// materials at the point being shaded.
pub struct MixMaterial {
    pub m1: Arc<Material>,
    pub m2: Arc<Material>,
    pub scale: Arc<dyn Texture<Spectrum> + Sync + Send>, // default: 0.5
}

impl MixMaterial {
    pub fn new(
        m1: Arc<Material>,
        m2: Arc<Material>,
        scale: Arc<dyn Texture<Spectrum> + Send + Sync>,
    ) -> Self {
        MixMaterial { m1, m2, scale }
    }
    // Material
    pub fn compute_scattering_functions(
        &self,
        si: &mut SurfaceInteraction,
        // arena: &mut Arena,
        mode: TransportMode,
        allow_multiple_lobes: bool,
        _material: Option<Arc<Material>>,
        _scale: Option<Spectrum>,
    ) {
        let s1: Spectrum = self
            .scale
            .evaluate(si)
            .clamp(0.0 as Float, std::f32::INFINITY as Float);
        let s2: Spectrum =
            (Spectrum::new(1.0 as Float) - s1).clamp(0.0 as Float, std::f32::INFINITY as Float);
        let mut si2: SurfaceInteraction = SurfaceInteraction::new(
            &si.common.p,
            &si.common.p_error,
            si.uv,
            &si.common.wo,
            &si.dpdu,
            &si.dpdv,
            &si.dndu,
            &si.dndv,
            si.common.time,
            si.shape,
        );
        self.m1
            .compute_scattering_functions(si, mode, allow_multiple_lobes, None, Some(s1));
        self.m2
            .compute_scattering_functions(&mut si2, mode, allow_multiple_lobes, None, Some(s2));
        if let Some(bsdf1) = &mut si.bsdf {
            if let Some(bsdf2) = &si2.bsdf {
                // get Bxdfs from si2 before it gets out of scope
                for bxdf2 in bsdf2.bxdfs.as_slice() {
                    match bxdf2 {
                        Bxdf::Empty(_bxdf) => break,
                        Bxdf::SpecRefl(bxdf) => {
                            let fresnel = match &bxdf.fresnel {
                                Fresnel::Conductor(fresnel) => {
                                    Fresnel::Conductor(FresnelConductor {
                                        eta_i: fresnel.eta_i,
                                        eta_t: fresnel.eta_t,
                                        k: fresnel.k,
                                    })
                                }
                                Fresnel::Dielectric(fresnel) => {
                                    Fresnel::Dielectric(FresnelDielectric {
                                        eta_i: fresnel.eta_i,
                                        eta_t: fresnel.eta_t,
                                    })
                                }
                                _ => Fresnel::NoOp(FresnelNoOp {}),
                            };
                            bsdf1.add(Bxdf::SpecRefl(SpecularReflection::new(
                                bxdf.r,
                                fresnel,
                                bxdf.sc_opt,
                            )))
                        }
                        Bxdf::SpecTrans(bxdf) => {
                            bsdf1.add(Bxdf::SpecTrans(SpecularTransmission::new(
                                bxdf.t,
                                bxdf.eta_a,
                                bxdf.eta_b,
                                bxdf.mode,
                                bxdf.sc_opt,
                            )))
                        }
                        Bxdf::FresnelSpec(bxdf) => {
                            bsdf1.add(Bxdf::FresnelSpec(FresnelSpecular::new(
                                bxdf.r,
                                bxdf.t,
                                bxdf.eta_a,
                                bxdf.eta_b,
                                bxdf.mode,
                                bxdf.sc_opt,
                            )))
                        }
                        Bxdf::LambertianRefl(bxdf) => bsdf1.add(Bxdf::LambertianRefl(
                            LambertianReflection::new(bxdf.r, bxdf.sc_opt),
                        )),
                        Bxdf::LambertianTrans(bxdf) => bsdf1.add(Bxdf::LambertianTrans(
                            LambertianTransmission::new(bxdf.t, bxdf.sc_opt),
                        )),
                        Bxdf::OrenNayarRefl(bxdf) => bsdf1.add(Bxdf::OrenNayarRefl(OrenNayar {
                            r: bxdf.r,
                            a: bxdf.a,
                            b: bxdf.b,
                            sc_opt: bxdf.sc_opt,
                        })),
                        Bxdf::MicrofacetRefl(bxdf) => {
                            let distribution = match &bxdf.distribution {
                                MicrofacetDistribution::Beckmann(distribution) => {
                                    MicrofacetDistribution::Beckmann(BeckmannDistribution {
                                        alpha_x: distribution.alpha_x,
                                        alpha_y: distribution.alpha_y,
                                        sample_visible_area: distribution.sample_visible_area,
                                    })
                                }
                                MicrofacetDistribution::TrowbridgeReitz(distribution) => {
                                    MicrofacetDistribution::TrowbridgeReitz(
                                        TrowbridgeReitzDistribution {
                                            alpha_x: distribution.alpha_x,
                                            alpha_y: distribution.alpha_y,
                                            sample_visible_area: distribution.sample_visible_area,
                                        },
                                    )
                                }
                                MicrofacetDistribution::DisneyMicrofacet(distribution) => {
                                    MicrofacetDistribution::DisneyMicrofacet(
                                        DisneyMicrofacetDistribution::new(
                                            distribution.inner.alpha_x,
                                            distribution.inner.alpha_y,
                                        ),
                                    )
                                }
                            };
                            let fresnel = match &bxdf.fresnel {
                                Fresnel::Conductor(fresnel) => {
                                    Fresnel::Conductor(FresnelConductor {
                                        eta_i: fresnel.eta_i,
                                        eta_t: fresnel.eta_t,
                                        k: fresnel.k,
                                    })
                                }
                                Fresnel::Dielectric(fresnel) => {
                                    Fresnel::Dielectric(FresnelDielectric {
                                        eta_i: fresnel.eta_i,
                                        eta_t: fresnel.eta_t,
                                    })
                                }
                                _ => Fresnel::NoOp(FresnelNoOp {}),
                            };
                            bsdf1.add(Bxdf::MicrofacetRefl(MicrofacetReflection::new(
                                bxdf.r,
                                distribution,
                                fresnel,
                                bxdf.sc_opt,
                            )))
                        }
                        Bxdf::MicrofacetTrans(bxdf) => {
                            let distribution = match &bxdf.distribution {
                                MicrofacetDistribution::Beckmann(distribution) => {
                                    MicrofacetDistribution::Beckmann(BeckmannDistribution {
                                        alpha_x: distribution.alpha_x,
                                        alpha_y: distribution.alpha_y,
                                        sample_visible_area: distribution.sample_visible_area,
                                    })
                                }
                                MicrofacetDistribution::TrowbridgeReitz(distribution) => {
                                    MicrofacetDistribution::TrowbridgeReitz(
                                        TrowbridgeReitzDistribution {
                                            alpha_x: distribution.alpha_x,
                                            alpha_y: distribution.alpha_y,
                                            sample_visible_area: distribution.sample_visible_area,
                                        },
                                    )
                                }
                                MicrofacetDistribution::DisneyMicrofacet(distribution) => {
                                    MicrofacetDistribution::DisneyMicrofacet(
                                        DisneyMicrofacetDistribution::new(
                                            distribution.inner.alpha_x,
                                            distribution.inner.alpha_y,
                                        ),
                                    )
                                }
                            };
                            bsdf1.add(Bxdf::MicrofacetTrans(MicrofacetTransmission::new(
                                bxdf.t,
                                distribution,
                                bxdf.eta_a,
                                bxdf.eta_b,
                                bxdf.mode,
                                bxdf.sc_opt,
                            )))
                        }
                        Bxdf::FresnelBlnd(bxdf) => {
                            let mut distrib: Option<MicrofacetDistribution> = None;
                            if let Some(distribution) = &bxdf.distribution {
                                distrib = match &distribution {
                                    MicrofacetDistribution::Beckmann(distribution) => Some(
                                        MicrofacetDistribution::Beckmann(BeckmannDistribution {
                                            alpha_x: distribution.alpha_x,
                                            alpha_y: distribution.alpha_y,
                                            sample_visible_area: distribution.sample_visible_area,
                                        }),
                                    ),
                                    MicrofacetDistribution::TrowbridgeReitz(distribution) => {
                                        Some(MicrofacetDistribution::TrowbridgeReitz(
                                            TrowbridgeReitzDistribution {
                                                alpha_x: distribution.alpha_x,
                                                alpha_y: distribution.alpha_y,
                                                sample_visible_area: distribution
                                                    .sample_visible_area,
                                            },
                                        ))
                                    }
                                    MicrofacetDistribution::DisneyMicrofacet(distribution) => {
                                        Some(MicrofacetDistribution::DisneyMicrofacet(
                                            DisneyMicrofacetDistribution::new(
                                                distribution.inner.alpha_x,
                                                distribution.inner.alpha_y,
                                            ),
                                        ))
                                    }
                                }
                            }
                            bsdf1.add(Bxdf::FresnelBlnd(FresnelBlend::new(
                                bxdf.rd,
                                bxdf.rs,
                                distrib,
                                bxdf.sc_opt,
                            )))
                        }
                        Bxdf::Fourier(bxdf) => bsdf1.add(Bxdf::Fourier(FourierBSDF::new(
                            bxdf.bsdf_table.clone(),
                            bxdf.mode,
                            bxdf.sc_opt,
                        ))),
                        Bxdf::Bssrdf(bxdf) => bsdf1.add(Bxdf::Bssrdf(SeparableBssrdfAdapter {
                            bssrdf: bxdf.bssrdf.clone(),
                            mode: bxdf.mode,
                            eta2: bxdf.eta2,
                        })),
                        Bxdf::DisDiff(bxdf) => {
                            bsdf1.add(Bxdf::DisDiff(DisneyDiffuse::new(bxdf.r, bxdf.sc_opt)))
                        }
                        Bxdf::DisSS(bxdf) => bsdf1.add(Bxdf::DisSS(DisneyFakeSS::new(
                            bxdf.r,
                            bxdf.roughness,
                            bxdf.sc_opt,
                        ))),
                        Bxdf::DisRetro(bxdf) => bsdf1.add(Bxdf::DisRetro(DisneyRetro::new(
                            bxdf.r,
                            bxdf.roughness,
                            bxdf.sc_opt,
                        ))),
                        Bxdf::DisSheen(bxdf) => {
                            bsdf1.add(Bxdf::DisSheen(DisneySheen::new(bxdf.r, bxdf.sc_opt)))
                        }
                        Bxdf::DisClearCoat(bxdf) => bsdf1.add(Bxdf::DisClearCoat(
                            DisneyClearCoat::new(bxdf.weight, bxdf.gloss, bxdf.sc_opt),
                        )),
                        Bxdf::Hair(bxdf) => bsdf1.add(Bxdf::Hair(HairBSDF {
                            h: bxdf.h,
                            gamma_o: bxdf.gamma_o,
                            eta: bxdf.eta,
                            sigma_a: bxdf.sigma_a,
                            beta_m: bxdf.beta_m,
                            beta_n: bxdf.beta_n,
                            v: bxdf.v,
                            s: bxdf.s,
                            sin_2k_alpha: bxdf.sin_2k_alpha,
                            cos_2k_alpha: bxdf.cos_2k_alpha,
                            sc_opt: bxdf.sc_opt,
                        })),
                    };
                }
            }
        }
    }
}