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/// Procedural pattern for back-face rendering.
42///
43/// Used with [`BackfacePolicy::Pattern`] to render a procedural pattern on back faces.
44/// The pattern is evaluated in world space so it remains stable as the camera moves.
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum BackfacePattern {
47 /// Alternating squares in world XY.
48 Checker = 0,
49 /// Diagonal lines (45 degrees).
50 Hatching = 1,
51 /// Two sets of diagonal lines (45 and 135 degrees).
52 Crosshatch = 2,
53 /// Horizontal stripes.
54 Stripes = 3,
55}
56
57/// Controls how back faces of a mesh are rendered.
58///
59/// Use [`BackfacePolicy::Cull`] (the default) to hide back faces, [`BackfacePolicy::Identical`]
60/// to show them with the same shading as front faces, or [`BackfacePolicy::DifferentColor`]
61/// to shade back faces in a distinct color : useful for spotting mesh orientation errors or
62/// highlighting the interior of open surfaces.
63#[derive(Debug, Clone, Copy, PartialEq)]
64pub enum BackfacePolicy {
65 /// Back faces are culled (invisible). Default.
66 Cull,
67 /// Back faces are visible and shaded identically to front faces.
68 Identical,
69 /// Back faces are visible and shaded in the given RGB color (linear 0..1).
70 ///
71 /// Front faces receive normal shading; back faces receive Blinn-Phong shading
72 /// with the supplied color and the same ambient/diffuse/specular coefficients.
73 /// The normal is flipped so lighting is computed from the back-face perspective.
74 DifferentColor([f32; 3]),
75 /// Back faces are visible and tinted darker by the given factor (0.0..1.0).
76 ///
77 /// The base color is multiplied by `(1.0 - factor)`, so `Tint(0.3)` means
78 /// back faces are 30% darker. The normal is flipped for correct lighting.
79 Tint(f32),
80 /// Back faces are visible and rendered with a procedural pattern in the given RGB color.
81 ///
82 /// The pattern alternates between the specified color and the object's base color.
83 /// The normal is flipped for correct lighting.
84 Pattern {
85 /// Which procedural pattern to use.
86 pattern: BackfacePattern,
87 /// RGB color for the pattern foreground (linear 0..1).
88 color: [f32; 3],
89 },
90}
91
92impl Default for BackfacePolicy {
93 fn default() -> Self {
94 BackfacePolicy::Cull
95 }
96}
97
98/// Per-object material properties for Blinn-Phong and PBR shading.
99///
100/// Materials carry all shading parameters that were previously global in `LightingSettings`.
101/// Each `SceneRenderItem` now has its own `Material`, enabling per-object visual distinction.
102///
103/// This struct is `#[non_exhaustive]`: construct via [`Material::default`],
104/// [`Material::from_color`], or spread syntax (`..Default::default()`). This allows new
105/// fields to be added in future phases without breaking downstream code.
106#[non_exhaustive]
107#[derive(Debug, Clone, Copy, PartialEq)]
108pub struct Material {
109 /// Base diffuse color [r, g, b] in linear 0..1 range. Default [0.7, 0.7, 0.7].
110 pub base_color: [f32; 3],
111 /// Ambient light coefficient. Default 0.15.
112 pub ambient: f32,
113 /// Diffuse light coefficient. Default 0.75.
114 pub diffuse: f32,
115 /// Specular highlight coefficient. Default 0.4.
116 pub specular: f32,
117 /// Specular shininess exponent. Default 32.0.
118 pub shininess: f32,
119 /// Metallic factor for PBR Cook-Torrance shading. 0=dielectric, 1=metal. Default 0.0.
120 pub metallic: f32,
121 /// Roughness factor for PBR microfacet distribution. 0=mirror, 1=fully rough. Default 0.5.
122 pub roughness: f32,
123 /// Opacity (1.0 = fully opaque, 0.0 = fully transparent). Default 1.0.
124 pub opacity: f32,
125 /// Optional albedo texture identifier. None = no texture applied. Default None.
126 pub texture_id: Option<u64>,
127 /// Optional normal map texture identifier. None = no normal mapping. Default None.
128 ///
129 /// The normal map must be in tangent-space with XY encoded as RG (0..1 -> -1..+1).
130 /// Requires UVs and tangents on the mesh for correct TBN construction.
131 pub normal_map_id: Option<u64>,
132 /// Optional ambient occlusion map texture identifier. None = no AO map. Default None.
133 ///
134 /// The AO map R channel encodes cavity factor (0=fully occluded, 1=fully lit).
135 /// Applied multiplicatively to ambient and diffuse terms.
136 pub ao_map_id: Option<u64>,
137 /// Use Cook-Torrance PBR shading instead of Blinn-Phong. Default false.
138 ///
139 /// When true, `metallic` and `roughness` drive the GGX BRDF.
140 /// PBR outputs linear HDR values; enable `post_process.enabled` for correct tone mapping.
141 pub use_pbr: bool,
142 /// Optional matcap texture identifier. When set, matcap shading replaces
143 /// Blinn-Phong/PBR. Default None.
144 ///
145 /// Obtain a `MatcapId` from [`ViewportGpuResources::builtin_matcap_id`] or
146 /// [`ViewportGpuResources::upload_matcap`]. Blendable matcaps (alpha-channel)
147 /// tint the result with `base_color`; static matcaps override color entirely.
148 pub matcap_id: Option<crate::resources::MatcapId>,
149 /// UV parameterization visualization. When set, replaces albedo/lighting with a
150 /// procedural pattern in UV space : useful for inspecting parameterization quality.
151 ///
152 /// Requires UV coordinates on the mesh. Default None (standard shading).
153 pub param_vis: Option<ParamVis>,
154 /// Back-face rendering policy. Default [`BackfacePolicy::Cull`] (back faces hidden).
155 ///
156 /// Use [`BackfacePolicy::Identical`] for single-sided geometry like planes and open
157 /// surfaces. Use [`BackfacePolicy::DifferentColor`] to highlight back faces in a
158 /// distinct color : helpful for diagnosing mesh orientation errors.
159 pub backface_policy: BackfacePolicy,
160}
161
162impl Default for Material {
163 fn default() -> Self {
164 Self {
165 base_color: [0.7, 0.7, 0.7],
166 ambient: 0.15,
167 diffuse: 0.75,
168 specular: 0.4,
169 shininess: 32.0,
170 metallic: 0.0,
171 roughness: 0.5,
172 opacity: 1.0,
173 texture_id: None,
174 normal_map_id: None,
175 ao_map_id: None,
176 use_pbr: false,
177 matcap_id: None,
178 param_vis: None,
179 backface_policy: BackfacePolicy::Cull,
180 }
181 }
182}
183
184impl Material {
185 /// Returns `true` if the backface policy makes back faces visible.
186 pub fn is_two_sided(&self) -> bool {
187 !matches!(self.backface_policy, BackfacePolicy::Cull)
188 }
189
190 /// Construct from a plain color, all other parameters at their defaults.
191 pub fn from_color(color: [f32; 3]) -> Self {
192 Self {
193 base_color: color,
194 ..Default::default()
195 }
196 }
197
198 /// Construct a Cook-Torrance PBR material.
199 ///
200 /// - `metallic`: 0.0 = dielectric, 1.0 = full metal
201 /// - `roughness`: 0.0 = mirror, 1.0 = fully rough
202 ///
203 /// All other parameters take their defaults. Enable post-processing
204 /// (`PostProcessSettings::enabled = true`) for correct HDR tone mapping.
205 pub fn pbr(base_color: [f32; 3], metallic: f32, roughness: f32) -> Self {
206 Self {
207 base_color,
208 use_pbr: true,
209 metallic,
210 roughness,
211 ..Default::default()
212 }
213 }
214}