nobject_rs/
material.rs

1use std::result::Result;
2
3use crate::{
4    get_on_off_from_str, get_opt_token_float_opt, get_token_float, get_token_int, get_token_string,
5    tokenizer::{Token, TokenSet},
6};
7use nom::{
8    branch::alt,
9    combinator::{map, opt},
10    error,
11    multi::many1,
12    sequence::preceded,
13    IResult, Parser,
14};
15use thiserror::Error;
16
17/// An enum for possible ways of specifying a material color
18#[derive(Debug, Clone, PartialEq)]
19pub enum ColorType {
20    /// RGB
21    Rgb(f32, f32, f32),
22    /// Reflectivity using a spectral curve.
23    /// This is specified as a filename and a multiplier (defaults to 1.0)
24    Spectral(String, f32),
25    /// CIEXYZ color space
26    CieXyz(f32, f32, f32),
27}
28
29/// Enum for the possible ways to specify the disolve
30#[derive(Debug, Clone, Copy, PartialEq)]
31pub enum DisolveType {
32    /// The amount this material dissolves into the background. 1.0 is fully
33    /// opaque
34    Alpha(f32),
35    /// Specifies that the disolve is based on the orientation of the viewer.
36    /// The value is the minimum to apply to a material.
37    Halo(f32),
38}
39
40#[derive(Clone, Debug)]
41enum OptionElement {
42    FileName(String),
43    BlendU(bool),
44    BlendV(bool),
45    Cc(bool),
46    Clamp(bool),
47    TextureRange((f32, f32)),
48    Offset((f32, Option<f32>, Option<f32>)),
49    Scale((f32, Option<f32>, Option<f32>)),
50    Turbulance((f32, Option<f32>, Option<f32>)),
51    TextureRes(i32),
52    ImfChan(String),
53    BumpMultiplier(f32),
54    ReflectionType(String),
55}
56
57/// Common settings for texture maps which can be color corrected.
58#[derive(Debug, Default, Clone, PartialEq)]
59pub struct ColorCorrectedMap {
60    /// The name of the texture map file.
61    pub file_name: String,
62    /// Enable horizontal texture blending
63    pub blend_u: Option<bool>,
64    /// Enable vertical texture blending
65    pub blend_v: Option<bool>,
66    /// Enable color correction
67    pub color_correct: Option<bool>,
68    /// Enables clamping.
69    pub clamp: Option<bool>,
70    /// Specifies the range over which scalar or color texture
71    /// values may vary. Corresponds to the `-mm` option.
72    pub texture_range: Option<(f32, f32)>,
73    /// Offset the position in the texture map.
74    pub offset: Option<(f32, Option<f32>, Option<f32>)>,
75    /// Scale the size of the texture pattern.
76    pub scale: Option<(f32, Option<f32>, Option<f32>)>,
77    /// A turbulance value to apply to the texture.
78    pub turbulance: Option<(f32, Option<f32>, Option<f32>)>,
79    /// Allows the specification of a specific resolution to use
80    /// when an image is used as a texture.
81    pub texture_res: Option<i32>,
82}
83
84impl ColorCorrectedMap {
85    fn new(o: &[OptionElement]) -> Self {
86        let mut res = Self::default();
87        for e in o {
88            match e {
89                OptionElement::FileName(n) => res.file_name = n.clone(),
90                OptionElement::BlendU(b) => {
91                    res.blend_u = Some(*b);
92                },
93                OptionElement::BlendV(b) => {
94                    res.blend_v = Some(*b);
95                },
96                OptionElement::Cc(b) => {
97                    res.color_correct = Some(*b);
98                },
99                OptionElement::Clamp(b) => {
100                    res.clamp = Some(*b);
101                },
102                OptionElement::TextureRange((base, gain)) => {
103                    res.texture_range = Some((*base, *gain));
104                },
105                OptionElement::Offset((x, y, z)) => {
106                    res.offset = Some((*x, *y, *z));
107                },
108                OptionElement::Scale((x, y, z)) => {
109                    res.scale = Some((*x, *y, *z));
110                },
111                OptionElement::Turbulance((x, y, z)) => {
112                    res.turbulance = Some((*x, *y, *z));
113                },
114                OptionElement::TextureRes(tex_res) => {
115                    res.texture_res = Some(*tex_res);
116                },
117                _ => {},
118            }
119        }
120        res
121    }
122}
123
124/// Common settings for texture maps which can not be color corrected.
125#[derive(Debug, Default, Clone, PartialEq)]
126pub struct NonColorCorrectedMap {
127    /// The name of the texture map file.
128    pub file_name: String,
129    /// Enable horizontal texture blending
130    pub blend_u: Option<bool>,
131    /// Enable vertical texture blending
132    pub blend_v: Option<bool>,
133    /// Enables clamping.
134    pub clamp: Option<bool>,
135    /// Specifies the channel used to create a scalar or
136    /// bump texture.
137    pub imf_chan: Option<String>,
138    /// Specifies the range over which scalar or color texture
139    /// values may vary. Corresponds to the `-mm` option.
140    pub texture_range: Option<(f32, f32)>,
141    /// Offset the position in the texture map.
142    pub offset: Option<(f32, Option<f32>, Option<f32>)>,
143    /// Scale the size of the texture pattern.
144    pub scale: Option<(f32, Option<f32>, Option<f32>)>,
145    /// A turbulance value to apply to the texture.
146    pub turbulance: Option<(f32, Option<f32>, Option<f32>)>,
147    /// Allows the specification of a specific resolution to use
148    /// when an image is used as a texture.
149    pub texture_res: Option<i32>,
150}
151
152impl NonColorCorrectedMap {
153    fn new(o: &[OptionElement]) -> Self {
154        let mut res = Self::default();
155        for e in o {
156            match e {
157                OptionElement::FileName(n) => res.file_name = n.clone(),
158                OptionElement::BlendU(b) => {
159                    res.blend_u = Some(*b);
160                },
161                OptionElement::BlendV(b) => {
162                    res.blend_v = Some(*b);
163                },
164                OptionElement::Clamp(b) => {
165                    res.clamp = Some(*b);
166                },
167                OptionElement::ImfChan(chan) => res.imf_chan = Some(chan.clone()),
168                OptionElement::TextureRange((base, gain)) => {
169                    res.texture_range = Some((*base, *gain));
170                },
171                OptionElement::Offset((x, y, z)) => {
172                    res.offset = Some((*x, *y, *z));
173                },
174                OptionElement::Scale((x, y, z)) => {
175                    res.scale = Some((*x, *y, *z));
176                },
177                OptionElement::Turbulance((x, y, z)) => {
178                    res.turbulance = Some((*x, *y, *z));
179                },
180                OptionElement::TextureRes(tex_res) => {
181                    res.texture_res = Some(*tex_res);
182                },
183                _ => {},
184            }
185        }
186        res
187    }
188}
189
190/// Contains information specific to bump maps.
191#[derive(Debug, Default, Clone, PartialEq)]
192pub struct BumpMap {
193    /// Specifies a bump multiplier
194    pub bump_multiplier: Option<f32>,
195    /// Additional map settings.
196    pub map_settings: Option<NonColorCorrectedMap>,
197}
198
199impl BumpMap {
200    fn new(o: &[OptionElement]) -> Self {
201        let mut res = Self {
202            map_settings: Some(NonColorCorrectedMap::new(o)),
203            ..Default::default()
204        };
205
206        for e in o {
207            if let OptionElement::BumpMultiplier(bm) = e {
208                res.bump_multiplier = Some(*bm);
209                break;
210            }
211        }
212        res
213    }
214}
215
216/// Reflection specific information.
217#[derive(Debug, Default, Clone, PartialEq)]
218pub struct ReflectionMap {
219    /// This contains the name of the type of reflection to use.
220    /// Corresponds to `-type` in the specification.
221    pub reflection_type: String,
222    /// Additional map settings.
223    pub map_settings: Option<ColorCorrectedMap>,
224}
225
226impl ReflectionMap {
227    fn new(o: &[OptionElement]) -> Self {
228        let mut res = Self {
229            map_settings: Some(ColorCorrectedMap::new(o)),
230            ..Default::default()
231        };
232
233        for e in o {
234            if let OptionElement::ReflectionType(ty) = e {
235                res.reflection_type = ty.clone();
236                break;
237            }
238        }
239        res
240    }
241}
242
243/// Defines a single material.
244#[derive(Debug, Default, Clone, PartialEq)]
245pub struct Material {
246    /// The name of the material.
247    /// Corresponds to `newmtl` in the specification.
248    pub name: String,
249    /// The ambient reflectivity value.
250    /// Corresponds to `Ka` in the specification.
251    pub ambient: Option<ColorType>,
252    /// The diffuse reflectivity value
253    /// Corresponds to `Kd` in the specification.
254    pub diffuse: Option<ColorType>,
255    /// The specular reflectivity value
256    /// Corresponds to `Ks` in the specification.
257    pub specular: Option<ColorType>,
258    /// The Emission Coefficient
259    /// Corresponds to `Ke` in the specification.
260    pub emissive_coefficient: Option<ColorType>,
261    /// The specular exponent.
262    /// Corresponds to `Ns` in the specification.
263    pub specular_exponent: Option<f32>,
264    /// The disolve.
265    /// Corresponds to `d` in the specification.
266    pub disolve: Option<DisolveType>,
267    /// Transparancy.
268    /// Corresponds to `Tr` in the specification.
269    pub transparancy: Option<f32>,
270    /// Transmission factor.
271    /// Corresponds to `Tf` in the specification.
272    pub transmission_factor: Option<ColorType>,
273    /// Corresponds to `sharpness` in the specification.
274    pub sharpness: Option<f32>,
275    /// Corresponds to `Ni` in the specification.
276    pub index_of_refraction: Option<f32>,
277    /// Corresponds to `illum` in the specification.
278    pub illumination_mode: Option<u32>,
279    /// Corresponds to `map_Ka` in the specification.
280    pub texture_map_ambient: Option<ColorCorrectedMap>,
281    /// Corresponds to `map_Kd` in the specification.
282    pub texture_map_diffuse: Option<ColorCorrectedMap>,
283    /// Corresponds to `map_Ks` in the specification.
284    pub texture_map_specular: Option<ColorCorrectedMap>,
285    /// Corresponds to `map_Ns` in the specification.
286    pub shininess_map: Option<NonColorCorrectedMap>,
287    /// Corresponds to `map_d` in the specification.
288    pub disolve_map: Option<NonColorCorrectedMap>,
289    /// Corresponds to `disp` in the specification.
290    pub displacement_map: Option<NonColorCorrectedMap>,
291    /// Corresponds to `decal` in the specification.
292    pub decal: Option<NonColorCorrectedMap>,
293    /// Corresponds to `bump` in the specification.
294    pub bump_map: Option<BumpMap>,
295    /// Corresponds to `refl` in the specification.
296    pub reflection_map: Option<ReflectionMap>,
297    /// Enables/Disables anti-aliasing of textures in THIS material only.
298    /// Corresponds to `map_aat` in the specification.
299    pub anti_alias_map: Option<bool>,
300}
301
302impl Material {
303    fn set_from_material_element(&mut self, element: &MaterialElement) {
304        match element {
305            MaterialElement::Name(n) => {
306                self.name = n.clone();
307            },
308            MaterialElement::Ambient(c) => {
309                self.ambient = Some(c.clone());
310            },
311            MaterialElement::Diffuse(c) => {
312                self.diffuse = Some(c.clone());
313            },
314            MaterialElement::Specular(c) => {
315                self.specular = Some(c.clone());
316            },
317            MaterialElement::EmissiveCoefficient(c) => {
318                self.emissive_coefficient = Some(c.clone());
319            },
320            MaterialElement::SpecularExponent(f) => {
321                self.specular_exponent = Some(*f);
322            },
323            MaterialElement::Disolve(d) => {
324                self.disolve = Some(*d);
325            },
326            MaterialElement::Transparency(f) => {
327                self.transparancy = Some(*f);
328            },
329            MaterialElement::TransmissionFactor(c) => {
330                self.transmission_factor = Some(c.clone());
331            },
332            MaterialElement::Sharpness(f) => {
333                self.sharpness = Some(*f);
334            },
335            MaterialElement::IndexOfRefraction(f) => {
336                self.index_of_refraction = Some(*f);
337            },
338            MaterialElement::IlluminationModel(u) => {
339                self.illumination_mode = Some(*u);
340            },
341            MaterialElement::TexMapAmbient(cc) => {
342                self.texture_map_ambient = Some(cc.clone());
343            },
344            MaterialElement::TexMapDiffuse(cc) => {
345                self.texture_map_diffuse = Some(cc.clone());
346            },
347            MaterialElement::TexMapSpecular(cc) => {
348                self.texture_map_specular = Some(cc.clone());
349            },
350            MaterialElement::ShininessMap(ncc) => {
351                self.shininess_map = Some(ncc.clone());
352            },
353            MaterialElement::DisolveMap(ncc) => {
354                self.disolve_map = Some(ncc.clone());
355            },
356            MaterialElement::DisplacementMap(ncc) => {
357                self.displacement_map = Some(ncc.clone());
358            },
359            MaterialElement::Decal(ncc) => {
360                self.decal = Some(ncc.clone());
361            },
362            MaterialElement::BumpMap(bm) => {
363                self.bump_map = Some(bm.clone());
364            },
365            MaterialElement::ReflectionMap(rm) => {
366                self.reflection_map = Some(rm.clone());
367            },
368            MaterialElement::AntiAliasMap(b) => {
369                self.anti_alias_map = Some(*b);
370            },
371        }
372    }
373}
374
375/// A wrapper for an underlying error which occurred
376/// while parsing the token stream.
377#[derive(Error, Debug)]
378pub enum MaterialError {
379    #[error("Parse Error: `{0}`")]
380    Parse(String),
381
382    /// The specification generally requires a newmtl statement
383    /// to come before all other statements. If this error occurs
384    /// it's because we also expect a newmtl statement first.
385    #[error("New Material expected, but not found.")]
386    NewMaterial,
387}
388
389#[derive(Clone, Debug)]
390enum MaterialElement {
391    Name(String),
392    Ambient(ColorType),
393    Diffuse(ColorType),
394    Specular(ColorType),
395    EmissiveCoefficient(ColorType),
396    SpecularExponent(f32),
397    Disolve(DisolveType),
398    Transparency(f32),
399    TransmissionFactor(ColorType),
400    Sharpness(f32),
401    IndexOfRefraction(f32),
402    IlluminationModel(u32),
403    TexMapAmbient(ColorCorrectedMap),
404    TexMapDiffuse(ColorCorrectedMap),
405    TexMapSpecular(ColorCorrectedMap),
406    ShininessMap(NonColorCorrectedMap),
407    DisolveMap(NonColorCorrectedMap),
408    DisplacementMap(NonColorCorrectedMap),
409    Decal(NonColorCorrectedMap),
410    BumpMap(BumpMap),
411    ReflectionMap(ReflectionMap),
412    AntiAliasMap(bool),
413}
414
415pub(crate) fn parse(input: TokenSet<'_>) -> Result<Vec<Material>, MaterialError> {
416    let elements: Vec<MaterialElement> = match parse_material_set().parse_complete(input) {
417        Ok((_, x)) => x,
418        Err(e) => return Err(MaterialError::Parse(e.to_string())),
419    };
420
421    let mut res = Vec::new();
422    for e in elements {
423        if let MaterialElement::Name(n) = e {
424            res.push(Material::default());
425            if let Some(l) = res.last_mut() {
426                l.name = n;
427            }
428        } else if let Some(l) = res.last_mut() {
429            l.set_from_material_element(&e);
430        } else {
431            return Err(MaterialError::NewMaterial);
432        }
433    }
434    Ok(res)
435}
436
437fn parse_material_set<'a>(
438) -> impl Parser<TokenSet<'a>, Output = Vec<MaterialElement>, Error = error::Error<TokenSet<'a>>> {
439    many1(alt((
440        alt((
441            parse_new_material(),
442            parse_ambient(),
443            parse_diffuse(),
444            parse_specular(),
445            parse_emissive_coefficient(),
446            parse_specular_exponent(),
447            parse_disolve(),
448            parse_transparency(),
449            parse_transmission_factor(),
450            parse_sharpness(),
451            parse_index_of_refraction(),
452            parse_illumination_model(),
453            parse_texture_map_ambient(),
454            parse_texture_map_diffuse(),
455            parse_texture_map_specular(),
456        )),
457        alt((
458            parse_shininess_map(),
459            parse_disolve_map(),
460            parse_displacement_map(),
461            parse_decal(),
462            parse_bump_map(),
463            parse_reflection_map(),
464            parse_anti_alias_map(),
465        )),
466    )))
467}
468
469fn parse_new_material<'a>(
470) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
471    map(
472        preceded(
473            token_match!(Token::NewMaterial),
474            token_match!(Token::String(_)),
475        ),
476        |s| {
477            let name = match get_token_string(&s) {
478                Ok(s) => s,
479                Err(e) => {
480                    log::error!("{}", e);
481                    Default::default()
482                },
483            };
484            MaterialElement::Name(name.into())
485        },
486    )
487}
488
489fn parse_ambient<'a>(
490) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
491    preceded(
492        token_match!(Token::AmbientColor),
493        map(parse_color_type(), MaterialElement::Ambient),
494    )
495}
496
497fn parse_diffuse<'a>(
498) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
499    preceded(
500        token_match!(Token::DiffuseColor),
501        map(parse_color_type(), MaterialElement::Diffuse),
502    )
503}
504
505fn parse_specular<'a>(
506) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
507    preceded(
508        token_match!(Token::SpecularColor),
509        map(parse_color_type(), MaterialElement::Specular),
510    )
511}
512
513fn parse_emissive_coefficient<'a>(
514) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
515    preceded(
516        token_match!(Token::EmissiveCoefficient),
517        map(parse_color_type(), MaterialElement::EmissiveCoefficient),
518    )
519}
520
521fn parse_specular_exponent<'a>(
522) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
523    preceded(
524        token_match!(Token::SpecularExponent),
525        map(token_match!(Token::Float(_) | Token::Int(_)), |f| {
526            let f = match get_token_float(&f) {
527                Ok(s) => s,
528                Err(e) => {
529                    log::error!("{}", e);
530                    Default::default()
531                },
532            };
533            MaterialElement::SpecularExponent(f)
534        }),
535    )
536}
537
538fn parse_disolve<'a>(
539) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
540    preceded(
541        token_match!(Token::Disolved),
542        alt((
543            map(
544                preceded(
545                    token_match!(Token::Halo),
546                    token_match!(Token::Float(_) | Token::Int(_)),
547                ),
548                |f| {
549                    let f = match get_token_float(&f) {
550                        Ok(s) => s,
551                        Err(e) => {
552                            log::error!("{}", e);
553                            Default::default()
554                        },
555                    };
556                    MaterialElement::Disolve(DisolveType::Halo(f))
557                },
558            ),
559            map(token_match!(Token::Float(_) | Token::Int(_)), |f| {
560                let f = match get_token_float(&f) {
561                    Ok(s) => s,
562                    Err(e) => {
563                        log::error!("{}", e);
564                        Default::default()
565                    },
566                };
567                MaterialElement::Disolve(DisolveType::Alpha(f))
568            }),
569        )),
570    )
571}
572
573fn parse_transparency<'a>(
574) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
575    preceded(
576        token_match!(Token::Transparancy),
577        map(token_match!(Token::Float(_) | Token::Int(_)), |f| {
578            let f = match get_token_float(&f) {
579                Ok(s) => s,
580                Err(e) => {
581                    log::error!("{}", e);
582                    Default::default()
583                },
584            };
585            MaterialElement::Transparency(f)
586        }),
587    )
588}
589
590fn parse_transmission_factor<'a>(
591) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
592    preceded(
593        token_match!(Token::TransmissionFactor),
594        map(parse_color_type(), MaterialElement::TransmissionFactor),
595    )
596}
597
598fn parse_sharpness<'a>(
599) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
600    preceded(
601        token_match!(Token::Sharpness),
602        map(token_match!(Token::Float(_) | Token::Int(_)), |f| {
603            let f = match get_token_float(&f) {
604                Ok(s) => s,
605                Err(e) => {
606                    log::error!("{}", e);
607                    Default::default()
608                },
609            };
610            MaterialElement::Sharpness(f)
611        }),
612    )
613}
614
615fn parse_index_of_refraction<'a>(
616) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
617    preceded(
618        token_match!(Token::IndexOfRefraction),
619        map(token_match!(Token::Float(_) | Token::Int(_)), |f| {
620            let f = match get_token_float(&f) {
621                Ok(s) => s,
622                Err(e) => {
623                    log::error!("{}", e);
624                    Default::default()
625                },
626            };
627            MaterialElement::IndexOfRefraction(f)
628        }),
629    )
630}
631
632fn parse_illumination_model<'a>(
633) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
634    preceded(
635        token_match!(Token::IlluminationModel),
636        map(token_match!(Token::Int(_)), |f| {
637            let f = match get_token_int(&f) {
638                Ok(s) => s,
639                Err(e) => {
640                    log::error!("{}", e);
641                    Default::default()
642                },
643            };
644            MaterialElement::IlluminationModel(f as u32)
645        }),
646    )
647}
648
649fn parse_texture_map_ambient<'a>(
650) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
651    preceded(
652        token_match!(Token::TextureMapAmbient),
653        map(parse_options(), |o| {
654            MaterialElement::TexMapAmbient(ColorCorrectedMap::new(&o))
655        }),
656    )
657}
658
659fn parse_texture_map_diffuse<'a>(
660) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
661    preceded(
662        token_match!(Token::TextureMapDiffuse),
663        map(parse_options(), |o| {
664            MaterialElement::TexMapDiffuse(ColorCorrectedMap::new(&o))
665        }),
666    )
667}
668
669fn parse_texture_map_specular<'a>(
670) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
671    preceded(
672        token_match!(Token::TextureMapSpecular),
673        map(parse_options(), |o| {
674            MaterialElement::TexMapSpecular(ColorCorrectedMap::new(&o))
675        }),
676    )
677}
678
679fn parse_shininess_map<'a>(
680) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
681    preceded(
682        token_match!(Token::TextureMapShininess),
683        map(parse_options(), |o| {
684            MaterialElement::ShininessMap(NonColorCorrectedMap::new(&o))
685        }),
686    )
687}
688
689fn parse_disolve_map<'a>(
690) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
691    preceded(
692        token_match!(Token::TextureMapDisolved),
693        map(parse_options(), |o| {
694            MaterialElement::DisolveMap(NonColorCorrectedMap::new(&o))
695        }),
696    )
697}
698
699fn parse_displacement_map<'a>(
700) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
701    preceded(
702        token_match!(Token::DisplacementMap),
703        map(parse_options(), |o| {
704            MaterialElement::DisplacementMap(NonColorCorrectedMap::new(&o))
705        }),
706    )
707}
708
709fn parse_decal<'a>(
710) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
711    preceded(
712        token_match!(Token::Decal),
713        map(parse_options(), |o| {
714            MaterialElement::Decal(NonColorCorrectedMap::new(&o))
715        }),
716    )
717}
718
719fn parse_bump_map<'a>(
720) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
721    preceded(
722        token_match!(Token::BumpMap),
723        map(parse_options(), |o| {
724            MaterialElement::BumpMap(BumpMap::new(&o))
725        }),
726    )
727}
728
729fn parse_reflection_map<'a>(
730) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
731    preceded(
732        token_match!(Token::ReflectionMap),
733        map(parse_options(), |o| {
734            MaterialElement::ReflectionMap(ReflectionMap::new(&o))
735        }),
736    )
737}
738
739fn parse_anti_alias_map<'a>(
740) -> impl Parser<TokenSet<'a>, Output = MaterialElement, Error = error::Error<TokenSet<'a>>> {
741    preceded(
742        token_match!(Token::AntiAliasMap),
743        map(token_match!(Token::String(_)), |o| {
744            let val = match get_on_off_from_str(&o) {
745                Ok(v) => v,
746                Err(e) => {
747                    log::error!("{}", e);
748                    Default::default()
749                },
750            };
751            MaterialElement::AntiAliasMap(val)
752        }),
753    )
754}
755
756fn parse_options<'a>(
757) -> impl Parser<TokenSet<'a>, Output = Vec<OptionElement>, Error = error::Error<TokenSet<'a>>> {
758    many1(alt((
759        parse_option_blend(),
760        parse_option_bm(),
761        parse_option_cc(),
762        parse_option_clamp(),
763        parse_option_texture_range(),
764        parse_option_offset(),
765        parse_option_scale(),
766        parse_option_turbulance(),
767        parse_option_texture_resolution(),
768        parse_option_imf_channel(),
769        parse_option_reflection_type(),
770        map(token_match!(Token::String(_)), |s| {
771            let name = match get_token_string(&s) {
772                Ok(s) => s,
773                Err(e) => {
774                    log::error!("{}", e);
775                    Default::default()
776                },
777            };
778            OptionElement::FileName(name.into())
779        }),
780    )))
781}
782
783fn parse_option_blend<'a>(
784) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
785    alt((
786        map(
787            preceded(
788                token_match!(Token::OptionBlendU),
789                token_match!(Token::String(_)),
790            ),
791            |s| {
792                let val = match get_on_off_from_str(&s) {
793                    Ok(s) => s,
794                    Err(e) => {
795                        log::error!("{}", e);
796                        Default::default()
797                    },
798                };
799                OptionElement::BlendU(val)
800            },
801        ),
802        map(
803            preceded(
804                token_match!(Token::OptionBlendV),
805                token_match!(Token::String(_)),
806            ),
807            |s| {
808                let val = match get_on_off_from_str(&s) {
809                    Ok(s) => s,
810                    Err(e) => {
811                        log::error!("{}", e);
812                        Default::default()
813                    },
814                };
815                OptionElement::BlendV(val)
816            },
817        ),
818    ))
819}
820
821fn parse_option_bm<'a>(
822) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
823    map(
824        preceded(
825            token_match!(Token::OptionBumpMultiplier),
826            token_match!(Token::Float(_) | Token::Int(_)),
827        ),
828        |s| {
829            let val = match get_token_float(&s) {
830                Ok(s) => s,
831                Err(e) => {
832                    log::error!("{}", e);
833                    Default::default()
834                },
835            };
836            OptionElement::BumpMultiplier(val)
837        },
838    )
839}
840
841fn parse_option_cc<'a>(
842) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
843    map(
844        preceded(
845            token_match!(Token::OptionColorCorrect),
846            token_match!(Token::String(_)),
847        ),
848        |s| {
849            let val = match get_on_off_from_str(&s) {
850                Ok(s) => s,
851                Err(e) => {
852                    log::error!("{}", e);
853                    Default::default()
854                },
855            };
856            OptionElement::Cc(val)
857        },
858    )
859}
860
861fn parse_option_clamp<'a>(
862) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
863    map(
864        preceded(
865            token_match!(Token::OptionClamp),
866            token_match!(Token::String(_)),
867        ),
868        |s| {
869            let val = match get_on_off_from_str(&s) {
870                Ok(s) => s,
871                Err(e) => {
872                    log::error!("{}", e);
873                    Default::default()
874                },
875            };
876            OptionElement::Clamp(val)
877        },
878    )
879}
880
881fn parse_option_texture_range<'a>(
882) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
883    map(
884        preceded(
885            token_match!(Token::OptionRange),
886            (
887                token_match!(Token::Float(_) | Token::Int(_)),
888                token_match!(Token::Float(_) | Token::Int(_)),
889            ),
890        ),
891        |(base, gain)| {
892            let base = match get_token_float(&base) {
893                Ok(s) => s,
894                Err(e) => {
895                    log::error!("{}", e);
896                    Default::default()
897                },
898            };
899            let gain = match get_token_float(&gain) {
900                Ok(s) => s,
901                Err(e) => {
902                    log::error!("{}", e);
903                    Default::default()
904                },
905            };
906            OptionElement::TextureRange((base, gain))
907        },
908    )
909}
910
911fn parse_option_offset<'a>(
912) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
913    map(
914        preceded(
915            token_match!(Token::OptionOffset),
916            (
917                token_match!(Token::Float(_) | Token::Int(_)),
918                opt(token_match!(Token::Float(_) | Token::Int(_))),
919                opt(token_match!(Token::Float(_) | Token::Int(_))),
920            ),
921        ),
922        |(x, y, z)| {
923            let x = match get_token_float(&x) {
924                Ok(s) => s,
925                Err(e) => {
926                    log::error!("{}", e);
927                    Default::default()
928                },
929            };
930            let y = match get_opt_token_float_opt(&y) {
931                Ok(s) => s,
932                Err(e) => {
933                    log::error!("{}", e);
934                    None
935                },
936            };
937            let z = match get_opt_token_float_opt(&z) {
938                Ok(s) => s,
939                Err(e) => {
940                    log::error!("{}", e);
941                    None
942                },
943            };
944            OptionElement::Offset((x, y, z))
945        },
946    )
947}
948
949fn parse_option_scale<'a>(
950) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
951    map(
952        preceded(
953            token_match!(Token::OptionScale),
954            (
955                token_match!(Token::Float(_) | Token::Int(_)),
956                opt(token_match!(Token::Float(_) | Token::Int(_))),
957                opt(token_match!(Token::Float(_) | Token::Int(_))),
958            ),
959        ),
960        |(x, y, z)| {
961            let x = match get_token_float(&x) {
962                Ok(s) => s,
963                Err(e) => {
964                    log::error!("{}", e);
965                    Default::default()
966                },
967            };
968            let y = match get_opt_token_float_opt(&y) {
969                Ok(s) => s,
970                Err(e) => {
971                    log::error!("{}", e);
972                    None
973                },
974            };
975            let z = match get_opt_token_float_opt(&z) {
976                Ok(s) => s,
977                Err(e) => {
978                    log::error!("{}", e);
979                    None
980                },
981            };
982            OptionElement::Scale((x, y, z))
983        },
984    )
985}
986
987fn parse_option_turbulance<'a>(
988) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
989    map(
990        preceded(
991            token_match!(Token::OptionTurbulence),
992            (
993                token_match!(Token::Float(_) | Token::Int(_)),
994                opt(token_match!(Token::Float(_) | Token::Int(_))),
995                opt(token_match!(Token::Float(_) | Token::Int(_))),
996            ),
997        ),
998        |(x, y, z)| {
999            let x = match get_token_float(&x) {
1000                Ok(s) => s,
1001                Err(e) => {
1002                    log::error!("{}", e);
1003                    Default::default()
1004                },
1005            };
1006            let y = match get_opt_token_float_opt(&y) {
1007                Ok(s) => s,
1008                Err(e) => {
1009                    log::error!("{}", e);
1010                    None
1011                },
1012            };
1013            let z = match get_opt_token_float_opt(&z) {
1014                Ok(s) => s,
1015                Err(e) => {
1016                    log::error!("{}", e);
1017                    None
1018                },
1019            };
1020            OptionElement::Turbulance((x, y, z))
1021        },
1022    )
1023}
1024
1025fn parse_option_texture_resolution<'a>(
1026) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
1027    map(
1028        preceded(
1029            token_match!(Token::OptionTextureResolution),
1030            token_match!(Token::Int(_)),
1031        ),
1032        |s| {
1033            let val = match get_token_int(&s) {
1034                Ok(s) => s,
1035                Err(e) => {
1036                    log::error!("{}", e);
1037                    Default::default()
1038                },
1039            };
1040            OptionElement::TextureRes(val)
1041        },
1042    )
1043}
1044
1045fn parse_option_imf_channel<'a>(
1046) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
1047    map(
1048        preceded(
1049            token_match!(Token::OptionIMFChan),
1050            token_match!(Token::String(_)),
1051        ),
1052        |s| {
1053            let val = match get_token_string(&s) {
1054                Ok(s) => s,
1055                Err(e) => {
1056                    log::error!("{}", e);
1057                    Default::default()
1058                },
1059            };
1060            OptionElement::ImfChan(val.into())
1061        },
1062    )
1063}
1064
1065fn parse_option_reflection_type<'a>(
1066) -> impl Parser<TokenSet<'a>, Output = OptionElement, Error = error::Error<TokenSet<'a>>> {
1067    map(
1068        preceded(
1069            token_match!(Token::ReflectionType),
1070            token_match!(Token::String(_)),
1071        ),
1072        |s| {
1073            let val = match get_token_string(&s) {
1074                Ok(s) => s,
1075                Err(e) => {
1076                    log::error!("{}", e);
1077                    Default::default()
1078                },
1079            };
1080            OptionElement::ReflectionType(val.into())
1081        },
1082    )
1083}
1084
1085fn parse_color_type<'a>(
1086) -> impl Parser<TokenSet<'a>, Output = ColorType, Error = error::Error<TokenSet<'a>>> {
1087    alt((
1088        map(
1089            (
1090                token_match!(Token::Spectral),
1091                token_match!(Token::String(_)),
1092                opt(token_match!(Token::Float(_) | Token::Int(_))),
1093            ),
1094            |(_, file, factor)| {
1095                let file_name = match get_token_string(&file) {
1096                    Ok(s) => s,
1097                    Err(e) => {
1098                        log::error!("{}", e);
1099                        Default::default()
1100                    },
1101                };
1102                let factor = match get_opt_token_float_opt(&factor) {
1103                    Ok(s) => s.unwrap_or(1.0),
1104                    Err(e) => {
1105                        log::error!("{}", e);
1106                        Default::default()
1107                    },
1108                };
1109                ColorType::Spectral(file_name.into(), factor)
1110            },
1111        ),
1112        map(
1113            (
1114                token_match!(Token::Xyz),
1115                token_match!(Token::Float(_) | Token::Int(_)),
1116                opt(token_match!(Token::Float(_) | Token::Int(_))),
1117                opt(token_match!(Token::Float(_) | Token::Int(_))),
1118            ),
1119            |(_, x_token, y_token, z_token)| {
1120                let x = match get_token_float(&x_token) {
1121                    Ok(s) => s,
1122                    Err(e) => {
1123                        log::error!("{}", e);
1124                        Default::default()
1125                    },
1126                };
1127                let y = match y_token {
1128                    Some(y) => match get_token_float(&y) {
1129                        Ok(s) => s,
1130                        Err(e) => {
1131                            log::error!("{}", e);
1132                            Default::default()
1133                        },
1134                    },
1135                    None => x,
1136                };
1137                let z = match z_token {
1138                    Some(z) => match get_token_float(&z) {
1139                        Ok(s) => s,
1140                        Err(e) => {
1141                            log::error!("{}", e);
1142                            Default::default()
1143                        },
1144                    },
1145                    None => x,
1146                };
1147
1148                ColorType::CieXyz(x, y, z)
1149            },
1150        ),
1151        map(
1152            (
1153                token_match!(Token::Float(_) | Token::Int(_)),
1154                token_match!(Token::Float(_) | Token::Int(_)),
1155                token_match!(Token::Float(_) | Token::Int(_)),
1156            ),
1157            |(r, g, b)| {
1158                let (r, g, b) = (
1159                    match get_token_float(&r) {
1160                        Ok(s) => s,
1161                        Err(e) => {
1162                            log::error!("{}", e);
1163                            Default::default()
1164                        },
1165                    },
1166                    match get_token_float(&g) {
1167                        Ok(s) => s,
1168                        Err(e) => {
1169                            log::error!("{}", e);
1170                            Default::default()
1171                        },
1172                    },
1173                    match get_token_float(&b) {
1174                        Ok(s) => s,
1175                        Err(e) => {
1176                            log::error!("{}", e);
1177                            Default::default()
1178                        },
1179                    },
1180                );
1181
1182                ColorType::Rgb(r, g, b)
1183            },
1184        ),
1185    ))
1186}