Skip to main content

ursus_materials/strategies/
pbr.rs

1use crate::error::MaterialError;
2use crate::field::{FieldDesc, FieldType};
3use crate::material::Material;
4use crate::requirements::Requirements;
5use crate::strategies::properties::*;
6use crate::strategy::ShadingStrategy;
7use crate::value::{MaterialValue, PropertyId};
8
9/// Field order matches the `MaterialData` struct in mesh.frag /
10/// depth_prepass.frag / shadow.frag: base_color, emissive, metallic,
11/// roughness, then texture indices.
12const FIELDS: &[FieldDesc] = &[
13    FieldDesc::new("base_color", FieldType::Vec4),
14    FieldDesc::new("emissive", FieldType::Vec4),
15    FieldDesc::new("metallic", FieldType::Float),
16    FieldDesc::new("roughness", FieldType::Float),
17    FieldDesc::new("tex_indices0", FieldType::UVec4),
18    FieldDesc::new("tex_indices1", FieldType::UVec4),
19];
20
21pub struct PbrStrategy {
22    requirements: Requirements,
23}
24
25impl Default for PbrStrategy {
26    fn default() -> Self {
27        let requirements = Requirements::new()
28            .optional(BASE_COLOR, MaterialValue::Color(glam::Vec4::ONE))
29            .optional(EMISSIVE, MaterialValue::Color(glam::Vec4::new(0.0, 0.0, 0.0, 1.0)))
30            .optional(METALLIC, MaterialValue::Float(0.0))
31            .optional(ROUGHNESS, MaterialValue::Float(1.0));
32        Self { requirements }
33    }
34}
35
36impl PbrStrategy {
37    /// Texture fields fall back to bindless slot 0 (the white-texture
38    /// fallback) when a material has no value for them.
39    fn texture_slot(material: &Material, id: PropertyId) -> u32 {
40        material.get(id).and_then(MaterialValue::as_texture).map(|t| t.0).unwrap_or(0)
41    }
42}
43
44impl ShadingStrategy for PbrStrategy {
45    fn name(&self) -> &'static str {
46        "pbr"
47    }
48
49    fn requirements(&self) -> &Requirements {
50        &self.requirements
51    }
52
53    fn fields(&self) -> &[FieldDesc] {
54        FIELDS
55    }
56
57    fn resolve(&self, material: &Material) -> Result<Vec<MaterialValue>, MaterialError> {
58        let get = |id| {
59            self.requirements.get_or_default(material, id).copied().ok_or_else(|| MaterialError::MissingProperty {
60                material: material.name.clone(),
61                strategy: self.name(),
62                property: id,
63            })
64        };
65
66        let tex_indices0 = MaterialValue::UVec4([
67            Self::texture_slot(material, DIFFUSE_TEXTURE),
68            Self::texture_slot(material, NORMAL_TEXTURE),
69            Self::texture_slot(material, METALLIC_ROUGHNESS_TEXTURE),
70            Self::texture_slot(material, EMISSIVE_TEXTURE),
71        ]);
72        let tex_indices1 = MaterialValue::UVec4([Self::texture_slot(material, OCCLUSION_TEXTURE), 0, 0, 0]);
73
74        Ok(vec![
75            get(BASE_COLOR)?,
76            get(EMISSIVE)?,
77            get(METALLIC)?,
78            get(ROUGHNESS)?,
79            tex_indices0,
80            tex_indices1,
81        ])
82    }
83}