mesh_tools/
material.rs

1//! # Material Creation and Management
2//!
3//! This module provides utilities for creating and configuring materials in the glTF format.
4//! Materials in glTF use the Physically Based Rendering (PBR) model which simulates how light
5//! interacts with surfaces in a physically accurate way.
6//!
7//! The core of this module is the `MaterialBuilder` struct which provides a builder pattern
8//! for creating materials with various properties:
9//!
10//! - Base color (diffuse color)
11//! - Metallic and roughness factors
12//! - Normal, occlusion, and emissive textures
13//! - Transparency settings
14//! - Double-sided rendering
15//!
16//! ## Example
17//!
18//! ```rust
19//! use mesh_tools::material;
20//!
21//! // Create a red metallic material
22//! let red_metal = material::create_metallic_material(
23//!     Some("RedMetal".to_string()),
24//!     [1.0, 0.0, 0.0, 1.0], // Red color
25//!     0.9, // High metallic
26//!     0.2  // Low roughness (shiny)
27//! );
28//! ```
29
30use crate::models::{Material, NormalTextureInfo, OcclusionTextureInfo, PbrMetallicRoughness, TextureInfo};
31
32/// Builder for creating and configuring glTF materials with PBR properties
33pub struct MaterialBuilder {
34    pub material: Material,
35}
36
37impl MaterialBuilder {
38    /// Create a new material builder
39    pub fn new(name: Option<String>) -> Self {
40        let mut material = Material::default();
41        material.name = name;
42        
43        Self { material }
44    }
45    
46    /// Set base color factor
47    pub fn with_base_color(mut self, color: [f32; 4]) -> Self {
48        if self.material.pbr_metallic_roughness.is_none() {
49            self.material.pbr_metallic_roughness = Some(PbrMetallicRoughness::default());
50        }
51        
52        if let Some(pbr) = &mut self.material.pbr_metallic_roughness {
53            pbr.base_color_factor = Some(color);
54        }
55        
56        self
57    }
58    
59    /// Set metallic factor
60    pub fn with_metallic_factor(mut self, factor: f32) -> Self {
61        if self.material.pbr_metallic_roughness.is_none() {
62            self.material.pbr_metallic_roughness = Some(PbrMetallicRoughness::default());
63        }
64        
65        if let Some(pbr) = &mut self.material.pbr_metallic_roughness {
66            pbr.metallic_factor = Some(factor);
67        }
68        
69        self
70    }
71    
72    /// Set roughness factor
73    pub fn with_roughness_factor(mut self, factor: f32) -> Self {
74        if self.material.pbr_metallic_roughness.is_none() {
75            self.material.pbr_metallic_roughness = Some(PbrMetallicRoughness::default());
76        }
77        
78        if let Some(pbr) = &mut self.material.pbr_metallic_roughness {
79            pbr.roughness_factor = Some(factor);
80        }
81        
82        self
83    }
84    
85    /// Set base color texture
86    pub fn with_base_color_texture(mut self, texture_index: usize, tex_coord: Option<usize>) -> Self {
87        if self.material.pbr_metallic_roughness.is_none() {
88            self.material.pbr_metallic_roughness = Some(PbrMetallicRoughness::default());
89        }
90        
91        if let Some(pbr) = &mut self.material.pbr_metallic_roughness {
92            let mut texture_info = TextureInfo::default();
93            texture_info.index = texture_index;
94            texture_info.tex_coord = tex_coord;
95            
96            pbr.base_color_texture = Some(texture_info);
97        }
98        
99        self
100    }
101    
102    /// Set metallic roughness texture
103    pub fn with_metallic_roughness_texture(mut self, texture_index: usize, tex_coord: Option<usize>) -> Self {
104        if self.material.pbr_metallic_roughness.is_none() {
105            self.material.pbr_metallic_roughness = Some(PbrMetallicRoughness::default());
106        }
107        
108        if let Some(pbr) = &mut self.material.pbr_metallic_roughness {
109            let mut texture_info = TextureInfo::default();
110            texture_info.index = texture_index;
111            texture_info.tex_coord = tex_coord;
112            
113            pbr.metallic_roughness_texture = Some(texture_info);
114        }
115        
116        self
117    }
118    
119    /// Set normal texture
120    pub fn with_normal_texture(mut self, texture_index: usize, tex_coord: Option<usize>, scale: Option<f32>) -> Self {
121        let mut normal_info = NormalTextureInfo::default();
122        normal_info.index = texture_index;
123        normal_info.tex_coord = tex_coord;
124        normal_info.scale = scale;
125        
126        self.material.normal_texture = Some(normal_info);
127        
128        self
129    }
130    
131    /// Set occlusion texture
132    pub fn with_occlusion_texture(mut self, texture_index: usize, tex_coord: Option<usize>, strength: Option<f32>) -> Self {
133        let mut occlusion_info = OcclusionTextureInfo::default();
134        occlusion_info.index = texture_index;
135        occlusion_info.tex_coord = tex_coord;
136        occlusion_info.strength = strength;
137        
138        self.material.occlusion_texture = Some(occlusion_info);
139        
140        self
141    }
142    
143    /// Set emissive texture
144    pub fn with_emissive_texture(mut self, texture_index: usize, tex_coord: Option<usize>) -> Self {
145        let mut texture_info = TextureInfo::default();
146        texture_info.index = texture_index;
147        texture_info.tex_coord = tex_coord;
148        
149        self.material.emissive_texture = Some(texture_info);
150        
151        self
152    }
153    
154    /// Set emissive factor
155    pub fn with_emissive_factor(mut self, factor: [f32; 3]) -> Self {
156        self.material.emissive_factor = Some(factor);
157        self
158    }
159    
160    /// Set alpha mode and cutoff
161    pub fn with_alpha_mode(mut self, mode: String, cutoff: Option<f32>) -> Self {
162        self.material.alpha_mode = Some(mode);
163        self.material.alpha_cutoff = cutoff;
164        self
165    }
166    
167    /// Set double sided flag
168    pub fn with_double_sided(mut self, double_sided: bool) -> Self {
169        self.material.double_sided = Some(double_sided);
170        self
171    }
172    
173    /// Build the material
174    pub fn build(self) -> Material {
175        self.material
176    }
177}
178
179/// Create a basic material with the specified color
180pub fn create_basic_material(name: Option<String>, color: [f32; 4]) -> Material {
181    MaterialBuilder::new(name)
182        .with_base_color(color)
183        .build()
184}
185
186/// Create a metallic material
187pub fn create_metallic_material(
188    name: Option<String>, 
189    color: [f32; 4], 
190    metallic: f32,
191    roughness: f32
192) -> Material {
193    MaterialBuilder::new(name)
194        .with_base_color(color)
195        .with_metallic_factor(metallic)
196        .with_roughness_factor(roughness)
197        .build()
198}
199
200/// Create a textured material with additional options
201pub fn create_textured_material(
202    name: Option<String>,
203    base_color_texture: Option<usize>,
204    metallic_roughness_texture: Option<usize>,
205    normal_texture: Option<usize>,
206    occlusion_texture: Option<usize>,
207    emissive_texture: Option<usize>,
208    emissive_factor: Option<[f32; 3]>,
209    metallic_factor: Option<f32>,
210    roughness_factor: Option<f32>,
211    alpha_mode: Option<String>,
212    alpha_cutoff: Option<f32>,
213    double_sided: Option<bool>
214) -> Material {
215    let mut builder = MaterialBuilder::new(name);
216    
217    if let Some(texture) = base_color_texture {
218        builder = builder.with_base_color_texture(texture, None);
219    }
220    
221    if let Some(texture) = metallic_roughness_texture {
222        builder = builder.with_metallic_roughness_texture(texture, None);
223    }
224    
225    if let Some(texture) = normal_texture {
226        builder = builder.with_normal_texture(texture, None, None);
227    }
228    
229    if let Some(texture) = occlusion_texture {
230        builder = builder.with_occlusion_texture(texture, None, None);
231    }
232    
233    if let Some(texture) = emissive_texture {
234        builder = builder.with_emissive_texture(texture, None);
235    }
236    
237    if let Some(factor) = emissive_factor {
238        builder = builder.with_emissive_factor(factor);
239    }
240    
241    if let Some(factor) = metallic_factor {
242        builder = builder.with_metallic_factor(factor);
243    }
244    
245    if let Some(factor) = roughness_factor {
246        builder = builder.with_roughness_factor(factor);
247    }
248    
249    if let Some(mode) = alpha_mode {
250        builder = builder.with_alpha_mode(mode, alpha_cutoff);
251    }
252    
253    if let Some(ds) = double_sided {
254        builder = builder.with_double_sided(ds);
255    }
256    
257    builder.build()
258}