clay_core/material/
combine.rs

1/// Local macro for replacing tokens.
2#[macro_export]
3macro_rules! _replace {
4    ($_t:tt, $sub:expr) => { $sub };
5}
6
7/// The macro for combining materials.
8///
9/// You can read more about the technique [here](https://clay-rs.github.io/knowledge/#objects).
10#[macro_export]
11macro_rules! material_combine {
12    ($Combine:ident { $( $field:ident : $Material:ty ),+ $(,)? }) => {
13        pub struct $Combine {
14            $( pub $field: (f64, $Material), )+
15        }
16
17        impl $Combine {
18            pub fn new(
19                $( mut $field: (f64, $Material), )+
20            ) -> Self {
21                let mut sum = 0.0;
22                $(
23                    sum += $field.0;
24                    $field.0 = sum;
25                )+
26                Self {
27                    $( $field: ($field.0*sum, $field.1), )+
28                }
29            }
30
31
32            #[allow(unused_assignments)]
33            fn method_source(method: &str) -> String {
34                use $crate::{prelude::*, material::*};
35
36                let cpref = format!(
37                    "{}_{}",
38                    MaterialClass::name(),
39                    method,
40                ).to_uppercase();
41
42                let mut cases = Vec::new();
43                let (mut si, mut sf) = (0, 0);
44                $(
45                    let inst_name = <$Material as Instance<MaterialClass>>::inst_name();
46                    cases.push([
47                        format!("\tif (alpha < fbuf[{}]) {{", sf),
48                        format!(
49                            "\t\treturn {}_{}({}_ARGS_B({}, {}));",
50                            inst_name, method, cpref, si, sf + 1,
51                        ),
52                        "\t}".to_string(),
53                    ].join("\n"));
54                    si += <$Material>::size_int();
55                    sf += 1 + <$Material>::size_float();
56                )+
57                let cases_text = cases.join(" else\n");
58                [
59                    &format!(
60                        "{}_RET {}_{}({}_ARGS_DEF) {{",
61                        cpref, Self::inst_name(), method, cpref,
62                    ),
63                    "\tfloat alpha = random_uniform(seed);",
64                    &cases_text,
65                    &format!("\treturn {}_RET_BAD;", cpref),
66                    "}",
67                ].join("\n")
68            }
69        }
70
71        impl $crate::material::Material for $Combine {
72            fn brightness(&self) -> f64 {
73                $(
74                    self.$field.0*self.$field.1.brightness() +
75                )+
76                0.0
77            }
78        }
79
80        impl $crate::Instance<$crate::material::MaterialClass> for $Combine {
81            fn source(cache: &mut std::collections::HashSet<u64>) -> String {
82                use $crate::{prelude::*, material::*};
83                if !cache.insert(Self::type_hash()) {
84                    return String::new()
85                }
86                let mut ms = Vec::new();
87                for method in MaterialClass::methods().into_iter() {
88                    ms.push(Self::method_source(&method));
89                }
90                [
91                    $( <$Material as Instance<MaterialClass>>::source(cache), )+
92                    ms.join("\n"),
93                ].join("\n")
94            }
95
96            fn inst_name() -> String {
97                use $crate::TypeHash;
98                format!("__combine_{:x}", Self::type_hash())
99            }
100        }
101
102        impl $crate::Pack for $Combine {
103            fn size_int() -> usize {
104                let sizes = [
105                    $( <$Material>::size_int(), )+
106                ];
107                sizes.into_iter().sum::<usize>()
108            }
109            fn size_float() -> usize {
110                let sizes = [
111                    $( 1 + <$Material>::size_float(), )+
112                ];
113                sizes.into_iter().sum::<usize>()
114            }
115            fn pack_to(&self, buffer_int: &mut [i32], buffer_float: &mut [f32]) {
116                use $crate::pack::*;
117                Packer::new(buffer_int, buffer_float)
118                $(
119                    .pack(&self.$field.0)
120                    .pack(&self.$field.1)
121                )+;
122            }
123        }
124    };
125}
126
127#[cfg(test)]
128#[allow(dead_code)]
129mod check {
130    use crate::{
131        material::test::TestMaterial,
132        material_combine,
133    };
134
135    material_combine!(TestCombine {
136        m1: TestMaterial<i32>,
137        m2: TestMaterial<f32>,
138    });
139}