Skip to main content

viewport_lib/scene/
material.rs

1/// Procedural UV visualization mode for parameterization inspection.
2///
3/// When set on a [`Material`], the mesh fragment shader ignores the albedo texture and
4/// renders a procedural pattern driven by the mesh UV coordinates instead. Useful for
5/// inspecting UV distortion, seams, and parameterization quality without needing a texture.
6///
7/// Requires the mesh to have UV coordinates. Has no effect if the mesh lacks UVs.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ParamVisMode {
10    /// Alternating black/white squares tiled in UV space.
11    Checker = 1,
12    /// Thin grid lines at UV integer boundaries.
13    Grid = 2,
14    /// Polar checkerboard centred at UV (0.5, 0.5) : reveals rotational consistency.
15    LocalChecker = 3,
16    /// Concentric rings centred at UV (0.5, 0.5) : reveals radial distortion.
17    LocalRadial = 4,
18}
19
20/// UV parameterization visualization settings.
21///
22/// Attach to [`Material::param_vis`] to enable procedural UV pattern rendering.
23/// The `scale` controls tile frequency : higher values produce more, smaller tiles.
24#[derive(Debug, Clone, Copy, PartialEq)]
25pub struct ParamVis {
26    /// Which procedural pattern to render.
27    pub mode: ParamVisMode,
28    /// Tile frequency multiplier. Default 8.0 : produces 8 checker squares per UV unit.
29    pub scale: f32,
30}
31
32impl Default for ParamVis {
33    fn default() -> Self {
34        Self {
35            mode: ParamVisMode::Checker,
36            scale: 8.0,
37        }
38    }
39}
40
41/// Controls how back faces of a mesh are rendered.
42///
43/// Use [`BackfacePolicy::Cull`] (the default) to hide back faces, [`BackfacePolicy::Identical`]
44/// to show them with the same shading as front faces, or [`BackfacePolicy::DifferentColor`]
45/// to shade back faces in a distinct color : useful for spotting mesh orientation errors or
46/// highlighting the interior of open surfaces.
47#[derive(Debug, Clone, Copy, PartialEq)]
48pub enum BackfacePolicy {
49    /// Back faces are culled (invisible). Default.
50    Cull,
51    /// Back faces are visible and shaded identically to front faces.
52    Identical,
53    /// Back faces are visible and shaded in the given RGB color (linear 0..1).
54    ///
55    /// Front faces receive normal shading; back faces receive Blinn-Phong shading
56    /// with the supplied color and the same ambient/diffuse/specular coefficients.
57    /// The normal is flipped so lighting is computed from the back-face perspective.
58    DifferentColor([f32; 3]),
59}
60
61impl Default for BackfacePolicy {
62    fn default() -> Self {
63        BackfacePolicy::Cull
64    }
65}
66
67/// Per-object material properties for Blinn-Phong and PBR shading.
68///
69/// Materials carry all shading parameters that were previously global in `LightingSettings`.
70/// Each `SceneRenderItem` now has its own `Material`, enabling per-object visual distinction.
71///
72/// This struct is `#[non_exhaustive]`: construct via [`Material::default`],
73/// [`Material::from_color`], or spread syntax (`..Default::default()`). This allows new
74/// fields to be added in future phases without breaking downstream code.
75#[non_exhaustive]
76#[derive(Debug, Clone, Copy, PartialEq)]
77pub struct Material {
78    /// Base diffuse color [r, g, b] in linear 0..1 range. Default [0.7, 0.7, 0.7].
79    pub base_color: [f32; 3],
80    /// Ambient light coefficient. Default 0.15.
81    pub ambient: f32,
82    /// Diffuse light coefficient. Default 0.75.
83    pub diffuse: f32,
84    /// Specular highlight coefficient. Default 0.4.
85    pub specular: f32,
86    /// Specular shininess exponent. Default 32.0.
87    pub shininess: f32,
88    /// Metallic factor for PBR Cook-Torrance shading. 0=dielectric, 1=metal. Default 0.0.
89    pub metallic: f32,
90    /// Roughness factor for PBR microfacet distribution. 0=mirror, 1=fully rough. Default 0.5.
91    pub roughness: f32,
92    /// Opacity (1.0 = fully opaque, 0.0 = fully transparent). Default 1.0.
93    pub opacity: f32,
94    /// Optional albedo texture identifier. None = no texture applied. Default None.
95    pub texture_id: Option<u64>,
96    /// Optional normal map texture identifier. None = no normal mapping. Default None.
97    ///
98    /// The normal map must be in tangent-space with XY encoded as RG (0..1 -> -1..+1).
99    /// Requires UVs and tangents on the mesh for correct TBN construction.
100    pub normal_map_id: Option<u64>,
101    /// Optional ambient occlusion map texture identifier. None = no AO map. Default None.
102    ///
103    /// The AO map R channel encodes cavity factor (0=fully occluded, 1=fully lit).
104    /// Applied multiplicatively to ambient and diffuse terms.
105    pub ao_map_id: Option<u64>,
106    /// Use Cook-Torrance PBR shading instead of Blinn-Phong. Default false.
107    ///
108    /// When true, `metallic` and `roughness` drive the GGX BRDF.
109    /// PBR outputs linear HDR values; enable `post_process.enabled` for correct tone mapping.
110    pub use_pbr: bool,
111    /// Optional matcap texture identifier. When set, matcap shading replaces
112    /// Blinn-Phong/PBR. Default None.
113    ///
114    /// Obtain a `MatcapId` from [`ViewportGpuResources::builtin_matcap_id`] or
115    /// [`ViewportGpuResources::upload_matcap`].  Blendable matcaps (alpha-channel)
116    /// tint the result with `base_color`; static matcaps override color entirely.
117    pub matcap_id: Option<crate::resources::MatcapId>,
118    /// UV parameterization visualization. When set, replaces albedo/lighting with a
119    /// procedural pattern in UV space : useful for inspecting parameterization quality.
120    ///
121    /// Requires UV coordinates on the mesh. Default None (standard shading).
122    pub param_vis: Option<ParamVis>,
123    /// Back-face rendering policy. Default [`BackfacePolicy::Cull`] (back faces hidden).
124    ///
125    /// Use [`BackfacePolicy::Identical`] for single-sided geometry like planes and open
126    /// surfaces. Use [`BackfacePolicy::DifferentColor`] to highlight back faces in a
127    /// distinct color : helpful for diagnosing mesh orientation errors.
128    pub backface_policy: BackfacePolicy,
129}
130
131impl Default for Material {
132    fn default() -> Self {
133        Self {
134            base_color: [0.7, 0.7, 0.7],
135            ambient: 0.15,
136            diffuse: 0.75,
137            specular: 0.4,
138            shininess: 32.0,
139            metallic: 0.0,
140            roughness: 0.5,
141            opacity: 1.0,
142            texture_id: None,
143            normal_map_id: None,
144            ao_map_id: None,
145            use_pbr: false,
146            matcap_id: None,
147            param_vis: None,
148            backface_policy: BackfacePolicy::Cull,
149        }
150    }
151}
152
153impl Material {
154    /// Returns `true` if the backface policy makes back faces visible (`Identical` or `DifferentColor`).
155    pub fn is_two_sided(&self) -> bool {
156        matches!(
157            self.backface_policy,
158            BackfacePolicy::Identical | BackfacePolicy::DifferentColor(_)
159        )
160    }
161
162    /// Construct from a plain color, all other parameters at their defaults.
163    pub fn from_color(color: [f32; 3]) -> Self {
164        Self {
165            base_color: color,
166            ..Default::default()
167        }
168    }
169
170    /// Construct a Cook-Torrance PBR material.
171    ///
172    /// - `metallic`: 0.0 = dielectric, 1.0 = full metal
173    /// - `roughness`: 0.0 = mirror, 1.0 = fully rough
174    ///
175    /// All other parameters take their defaults. Enable post-processing
176    /// (`PostProcessSettings::enabled = true`) for correct HDR tone mapping.
177    pub fn pbr(base_color: [f32; 3], metallic: f32, roughness: f32) -> Self {
178        Self {
179            base_color,
180            use_pbr: true,
181            metallic,
182            roughness,
183            ..Default::default()
184        }
185    }
186}