1use crate::ageing::{apply, scratch_marked, scratch_marked_coat};
2use crate::kinds::{CoatWobble, Crinkle, Fibre, Grime, PlankWood, Scratch, WallMottle};
3use crate::material::{At, Material, NoiseKind, NoiseLayer};
4use crate::math::{
5 add3, clamp01, dot, fract, lerp, lerp3, mul3, normalize, scale3, smoothstep, sub3,
6};
7use crate::noise::{Hash, fbm3, flow, hash3, value_noise3, value_noise3_gradient};
8
9fn noise(p: [f32; 3]) -> f32 {
10 value_noise3(p, Hash::Xor)
11}
12
13fn fbm(p: [f32; 3], octaves: i32) -> f32 {
14 fbm3(p, octaves, Hash::Nested)
15}
16
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub struct Resolved {
19 pub material: Material,
20 pub normal: [f32; 3],
21}
22
23pub fn resolve(material: &Material, age: f32, at: &At) -> Resolved {
24 let mut material = apply(material, age, at);
25 let mut normal = if dot(at.normal, at.normal) <= 1e-12 {
26 [0.0, 0.0, 1.0]
27 } else {
28 normalize(at.normal)
29 };
30 for layer in material.layers {
31 if layer.active() {
32 sample_layer(&mut material, &mut normal, layer, at);
33 }
34 }
35 Resolved { material, normal }
36}
37
38fn sample_layer(material: &mut Material, normal: &mut [f32; 3], layer: NoiseLayer, at: &At) {
39 let amp = layer.amplitude;
40 let p = shift(at.position, layer.seed);
41 let uv = at.uv;
42 match layer.kind {
43 NoiseKind::None => {}
44 NoiseKind::Fibre => fibre(material, normal, p, amp, Fibre::from_params(&layer.params)),
45 NoiseKind::Crinkle => {
46 let c = Crinkle::from_params(&layer.params);
47 let g = add3(
48 scale3(value_noise3_gradient(p, c.frequencies[0]), c.gains[0] * amp),
49 scale3(value_noise3_gradient(p, c.frequencies[1]), c.gains[1] * amp),
50 );
51 *normal = perturb(*normal, g);
52 }
53 NoiseKind::PlankWood => {
54 if layer.active() {
55 plank_wood(material, p, amp, PlankWood::from_params(&layer.params));
56 }
57 }
58 NoiseKind::WallMottle => wall_mottle(
59 material,
60 normal,
61 p,
62 amp,
63 WallMottle::from_params(&layer.params),
64 ),
65 NoiseKind::Grime => grime(material, p, amp, Grime::from_params(&layer.params)),
66 NoiseKind::Scratch => {
67 let scales = Scratch::from_params(&layer.params);
68 if material.clearcoat > 0.0 {
69 let (base, roughness) = scratch_marked_coat(
70 material.base,
71 material.clearcoat_roughness,
72 amp,
73 p,
74 layer.seed,
75 scales,
76 );
77 material.base = base;
78 material.clearcoat_roughness = roughness;
79 } else {
80 let (base, roughness) = scratch_marked(
81 material.base,
82 material.roughness,
83 amp,
84 p,
85 layer.seed,
86 scales,
87 );
88 material.base = base;
89 material.roughness = roughness;
90 }
91 }
92 NoiseKind::CoatWobble => {
93 if layer.active() {
94 *normal = perturb(
95 *normal,
96 coat_wobble(p, amp, CoatWobble::from_params(&layer.params)),
97 );
98 }
99 }
100 NoiseKind::Leaf => {
101 let spots = fbm([uv[0] * 12.0, uv[1] * 12.0, 2.0], 4);
102 tint_factor(material, 0.7 + 0.6 * spots, amp);
103 height_relief(normal, at, amp, |uv, _| leaf_height(uv));
104 }
105 NoiseKind::Bark => {
106 let n = fbm(scale3(p, 300.0), 4);
107 tint_factor(material, 0.75 + 0.5 * n, amp);
108 height_relief(normal, at, amp, |_, p| bark_height(p));
109 }
110 NoiseKind::Flow => {
111 let n = flow([uv[0] * layer.frequency, uv[1] * layer.frequency], at.time);
112 material.thin_film = (material.thin_film + amp * (n - 0.5) * 2.0).max(0.0);
113 }
114 NoiseKind::Value => {
115 let n = noise(scale3(p, layer.frequency));
116 tint_factor(material, 1.0 + (n - 0.5) * 2.0, amp);
117 }
118 NoiseKind::Fbm => {
119 let n = fbm(scale3(p, layer.frequency), 4);
120 tint_factor(material, 1.0 + (n - 0.5) * 2.0, amp);
121 }
122 }
123}
124
125fn fibre(material: &mut Material, normal: &mut [f32; 3], p: [f32; 3], amount: f32, f: Fibre) {
126 let fibre = noise([p[0] * f.across, p[1] * f.along, p[2] * f.across]) * 0.6
127 + noise(scale3(p, f.fine)) * 0.4;
128 let mottle = noise(scale3(p, f.mottle));
129 let factor = 1.0 + ((fibre - 0.5) * f.fibre_gain + (mottle - 0.5) * f.mottle_gain) * amount;
130 material.base = scale3(material.base, factor);
131 let g = add3(
132 scale3(value_noise3_gradient(p, f.ripple), f.ripple_gain * amount),
133 scale3(value_noise3_gradient(p, f.swell), f.swell_gain * amount),
134 );
135 *normal = perturb(*normal, g);
136}
137
138fn plank_wood(material: &mut Material, p: [f32; 3], amp: f32, w: PlankWood) {
139 let plank = (p[2] / w.width).floor();
140 let tone = hash3([plank as i32, 7, 3], Hash::Xor);
141 let across = fract(p[2] / w.width);
142 let seam = smoothstep(0.0, 0.006, across) + smoothstep(0.0, 0.006, 1.0 - across) - 1.0;
143 let warp = noise([p[0] * 1.4, plank * 3.7, p[2] * 6.0]) * 0.9
144 + noise([p[0] * 6.0, plank, p[2] * 20.0]) * 0.25;
145 let rings =
146 fract((p[2] + warp * w.warp) * w.rings + noise([p[0] * 0.8, plank, 0.0]) * w.jitter);
147 let figure = smoothstep(0.55, 0.95, rings) * 0.55
148 + noise([p[0] * w.figure[0], p[2] * w.figure[1], plank]) * 0.25;
149 let factor = (0.78 + tone * w.tone)
150 * (1.08 - figure * w.figure_depth)
151 * lerp(w.seam_floor, 1.0, seam.clamp(0.0, 1.0));
152 tint_factor(material, factor, amp);
153 let alpha = material.roughness * material.roughness;
154 let next = (alpha * (0.85 + figure * w.roughness)).min(1.0).sqrt();
155 material.roughness = lerp(material.roughness, next, clamp01(amp));
156}
157
158fn wall_mottle(
159 material: &mut Material,
160 normal: &mut [f32; 3],
161 p: [f32; 3],
162 amp: f32,
163 w: WallMottle,
164) {
165 let broad = noise(scale3(p, w.broad[0])) * 0.6 + noise(scale3(p, w.broad[1])) * 0.4;
166 let fine = noise(scale3(p, w.fine[0])) * 0.6 + noise(scale3(p, w.fine[1])) * 0.4;
167 let factor = 1.0 + (broad - 0.5) * w.broad_gain + (fine - 0.5) * w.fine_gain;
168 tint_factor(material, factor, amp);
169 let g = add3(
170 scale3(value_noise3_gradient(p, w.swell[0]), w.swell_gains[0] * amp),
171 scale3(value_noise3_gradient(p, w.swell[1]), w.swell_gains[1] * amp),
172 );
173 *normal = perturb(*normal, g);
174}
175
176fn grime(material: &mut Material, p: [f32; 3], amp: f32, g: Grime) {
177 let grime = noise(scale3(p, g.frequencies[0])) * 0.6 + noise(scale3(p, g.frequencies[1])) * 0.4;
178 let wear = smoothstep(0.35, 0.75, grime);
179 let tint = lerp3(g.dirty, g.clean, wear);
180 material.base = mul3(material.base, lerp3([1.0, 1.0, 1.0], tint, clamp01(amp)));
181 let alpha = material.roughness * material.roughness;
182 let next = (alpha * lerp(g.roughness[0], g.roughness[1], wear))
183 .min(1.0)
184 .sqrt();
185 material.roughness = lerp(material.roughness, next, clamp01(amp));
186}
187
188fn leaf_height(uv: [f32; 2]) -> f32 {
189 let vein = (uv[1] * 40.0 + uv[0] * 3.0).sin().abs().powi(12);
190 0.0002 * fbm([uv[0] * 30.0, uv[1] * 30.0, 1.0], 3) - 0.00008 * vein
191}
192
193fn bark_height(p: [f32; 3]) -> f32 {
194 0.0006 * fbm([p[0] * 200.0, p[1] * 60.0, p[2] * 200.0], 4)
195}
196
197fn height_relief(
198 normal: &mut [f32; 3],
199 at: &At,
200 amp: f32,
201 height: impl Fn([f32; 2], [f32; 3]) -> f32,
202) {
203 let (t, b) = basis(*normal);
204 let e = 0.00012;
205 let h0 = height(at.uv, at.position);
206 let hu = height([at.uv[0] + e, at.uv[1]], add3(at.position, scale3(t, e)));
207 let hv = height([at.uv[0], at.uv[1] + e], add3(at.position, scale3(b, e)));
208 let bent = normalize(sub3(
209 sub3(*normal, scale3(t, (hu - h0) / e * amp)),
210 scale3(b, (hv - h0) / e * amp),
211 ));
212 if dot(bent, *normal) > 0.05 {
213 *normal = bent;
214 }
215}
216
217fn coat_wobble(p: [f32; 3], z: f32, c: CoatWobble) -> [f32; 3] {
218 add3(
219 scale3(value_noise3_gradient(p, c.frequencies[0]), z),
220 add3(
221 scale3(value_noise3_gradient(p, c.frequencies[1]), z * c.weights[0]),
222 scale3(value_noise3_gradient(p, c.frequencies[2]), z * c.weights[1]),
223 ),
224 )
225}
226
227fn tint_factor(material: &mut Material, factor: f32, amp: f32) {
228 let target = scale3(material.base, factor);
229 material.base = lerp3(material.base, target, clamp01(amp));
230}
231
232fn perturb(n: [f32; 3], g: [f32; 3]) -> [f32; 3] {
233 let along = dot(g, n);
234 normalize(sub3(n, sub3(g, scale3(n, along))))
235}
236
237fn shift(p: [f32; 3], seed: u32) -> [f32; 3] {
238 if seed == 0 {
239 p
240 } else {
241 [p[0] + seed as f32 * 0.001, p[1] + seed as f32 * 0.001, p[2]]
242 }
243}
244
245fn basis(n: [f32; 3]) -> ([f32; 3], [f32; 3]) {
246 let s = if n[2] >= 0.0 { 1.0 } else { -1.0 };
247 let a = -1.0 / (s + n[2]);
248 let b = n[0] * n[1] * a;
249 let t = [1.0 + s * n[0] * n[0] * a, s * b, -s * n[0]];
250 let bt = [b, s + n[1] * n[1] * a, -n[1]];
251 (t, bt)
252}