1use std::{sync::Arc, collections::HashMap, fs};
26use vulkano::{device::Queue, sync::{self, GpuFuture}};
27use crate::{components::RGB, shaders::fs_draw};
28use super::texture::Texture;
29
30
31#[derive(Debug, Clone)]
34pub struct Material{
35 #[allow(dead_code)]
36 name: String,
37 illum: u8, kd: Option<RGB>, ka: Option<RGB>, ks: Option<RGB>, #[allow(dead_code)]
43 ke: Option<RGB>, #[allow(dead_code)]
45 km: Option<f32>, ns: Option<f32>, ni: Option<f32>, d: Option<f32>, map_kd: Option<Arc<Texture>>,
51 map_ka: Option<Arc<Texture>>,
52 map_ks: Option<Arc<Texture>>,
53 #[allow(dead_code)] map_ke: Option<Arc<Texture>>,
55 #[allow(dead_code)] map_km: Option<Arc<Texture>>,
57 map_ns: Option<Arc<Texture>>,
58 #[allow(dead_code)] map_d: Option<Arc<Texture>>,
61 #[allow(dead_code)] map_refl: Option<Arc<Texture>>,
63
64 #[allow(dead_code)] halo: bool,
66}
67
68impl Default for Material {
69 fn default() -> Self {
70 Material{
71 name: String::from("default"),
72 illum: 1,
73 kd: Some(RGB::new(1.0, 0.0, 1.0)),
74 ka: Some(RGB::new(1.0, 0.0, 1.0)),
75 ks: None,
76 ke: None,
77 km: None,
78 ns: None,
79 ni: None,
80 d: None,
81 map_kd: None,
82 map_ka: None,
83 map_ks: None,
84 map_ke: None,
85 map_km: None,
86 map_ns: None,
87 map_d: None,
88 map_refl: None,
89 halo: false
90 }
91 }
92}
93
94impl Material {
95 pub fn into_set(&self, queue: Arc<Queue>) -> (fs_draw::ty::Material, [Arc<Texture>; 4]) {
96 let default_texture = Texture::default(queue);
97
98 let (diffuse, diffuse_map): ([f32; 4], _) = match (self.kd, self.map_kd.clone()) {
99 (Some(kd), None) => ([kd.r, kd.g, kd.b, 0.0], default_texture.clone()),
100 (None, Some(map_kd)) => ([1.0, 1.0, 1.0, 1.0], map_kd),
101 (Some(kd), Some(map_kd)) => ([kd.r, kd.g, kd.b, 1.0], map_kd),
102 _ => panic!("Either Kd, map_Kd, or both must be defined")
103 };
104
105 let (ambient, ambient_map) = match (self.ka, self.map_ka.clone()) {
106 (None, None) => ([0.0, 0.0, 0.0, 0.0], default_texture.clone()),
107 (Some(ka), None) => ([ka.r, ka.g, ka.b, 1.0], default_texture.clone()),
108 (None, Some(map_ka)) => ([1.0, 1.0, 1.0, 2.0], map_ka),
109 (Some(ka), Some(map_ka)) => ([ka.r, ka.g, ka.b, 2.0], map_ka),
110 };
111
112 let (specular, specular_map, specular_highlight_focus_map) = match (self.ks, self.ns, self.map_ks.clone(), self.map_ns.clone()) { (Some(ks), Some(ns), Some(map_ks), Some(map_ns)) => ([ks.r, ks.g, ks.b, -(ns + 1001.0)], map_ks, map_ns),
114 (Some(ks), Some(ns), Some(map_ks), None) => ([ks.r, ks.g, ks.b, -ns], map_ks, default_texture),
115 (Some(ks), Some(ns), None, Some(map_ns)) => ([ks.r, ks.g, ks.b, ns + 1001.0], default_texture, map_ns),
116 (Some(ks), Some(ns), None, None) => ([ks.r, ks.g, ks.b, ns], default_texture.clone(), default_texture),
117 (None, Some(ns), Some(map_ks), Some(map_ns)) => ([1.0, 1.0, 1.0, -(ns + 1001.0)], map_ks, map_ns),
118 (None, Some(ns), Some(map_ks), None) => ([1.0, 1.0, 1.0, -ns], map_ks, default_texture),
119 (Some(ks), None, Some(map_ks), Some(map_ns)) => ([ks.r, ks.g, ks.b, 1001.0], map_ks, map_ns),
120 (Some(ks), None, None, Some(map_ns)) => ([ks.r, ks.g, ks.b, 1001.0], default_texture, map_ns),
121 (None, None, Some(map_ks), Some(map_ns)) => ([1.0, 1.0, 1.0, -1001.0], map_ks, map_ns),
122 (_, None, _, None) | (None, _, None, _) => ([0.0, 0.0, 0.0, 0.0], default_texture.clone(), default_texture),
123 };
124
125 let other = [self.d.unwrap_or(1.0), self.ni.unwrap_or(0.0), 0.0, self.illum as f32];
126
127 (
128 fs_draw::ty::Material {
129 diffuse,
130 ambient,
131 specular,
132 other
133 },
134 [
135 diffuse_map,
136 ambient_map,
137 specular_map,
138 specular_highlight_focus_map,
139 ]
140 )
141 }
142
143 pub fn from_mtllib(path: &str, gfx_queue: Arc<Queue>) -> HashMap<String, (Arc<Self>, Box<dyn GpuFuture>)> {
145 let content = fs::read_to_string(path).unwrap_or_else(|_| panic!("Something went wrong when trying to read {}.", path));
146
147 let mut mtls = content.split("newmtl ");
148 mtls.next();
149
150 mtls.into_iter().map(|block| {
151 let block = String::from("newmtl ") + block;
152 Material::from_mtlblock(block.as_str(), path, gfx_queue.clone())
153 }).collect::<HashMap<String, (Arc<Material>, Box<dyn GpuFuture>)>>()
154 }
155
156 pub fn from_mtlblock(block: &str, path: &str, gfx_queue: Arc<Queue>) -> (String, (Arc<Material>, Box<dyn GpuFuture>)) {
158 let mut lines = block.lines().filter(|s| !(*s).is_empty() && !s.starts_with('#') );
159
160 let name = lines.next().unwrap_or_else(|| panic!("formatting error in {}", path)).split_whitespace().nth(1).unwrap().to_string();
161
162 let mut ka: Option<RGB> = None;
163 let mut kd: Option<RGB> = None;
164 let mut ks: Option<RGB> = None;
165 let mut ke: Option<RGB> = None;
166 let mut km: Option<f32> = None;
167 let mut ns: Option<f32> = None;
168 let mut ni: Option<f32> = None;
169 let mut d: Option<f32> = None; let mut map_kd: Option<Arc<Texture>> = None;
172 let mut map_ka: Option<Arc<Texture>> = None;
173 let mut map_ks: Option<Arc<Texture>> = None;
174 let mut map_ke: Option<Arc<Texture>> = None;
175 let mut map_km: Option<Arc<Texture>> = None;
176 let mut map_ns: Option<Arc<Texture>> = None;
177 let mut map_d: Option<Arc<Texture>> = None;
178 let mut map_refl: Option<Arc<Texture>> = None;
179
180 let mut illum: Option<u8> = None; let mut halo: bool = false;
183
184 let mut future: Box<dyn GpuFuture> = sync::now(gfx_queue.device().clone()).boxed();
185
186 for line in lines {
187 let mut line_parts = line.split_whitespace();
188 match line_parts.next().unwrap() {
189 "Kd" => kd = Some(RGB::from_parts(line_parts).unwrap_or_else(|_| panic!("formatting error in {}", path))),
190 "Ka" => ka = Some(RGB::from_parts(line_parts).unwrap_or_else(|_| panic!("formatting error in {}", path))),
191 "Ks" => ks = Some(RGB::from_parts(line_parts).unwrap_or_else(|_| panic!("formatting error in {}", path))),
192 "Ke" => ke = Some(RGB::from_parts(line_parts).unwrap_or_else(|_| panic!("formatting error in {}", path))),
193 "Km" => km = Some(line_parts.next().unwrap_or_else(|| panic!("formatting error in {}", path)).parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path))),
194 "Ns" => ns = Some(line_parts.next().unwrap_or_else(|| panic!("formatting error in {}", path)).parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path))),
195 "Ni" => ni = Some(line_parts.next().unwrap_or_else(|| panic!("formatting error in {}", path)).parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path))),
196 "d" => d = match line_parts.clone().count() {
197 1 => Some(line_parts.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path))), 2 => {
199 line_parts.next().unwrap().to_string();
200 halo = true;
201 Some(line_parts.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)))
202 },
203 _ => panic!("formatting error in {}", path)
204 }, "map_Kd" => {
208 let (new_map_kd, tex_future) = Texture::from_mtl_line(&mut line_parts, path, gfx_queue.clone()).unwrap();
209 map_kd = Some(new_map_kd);
210 future = future.join(tex_future).boxed();
211 },
212 "map_Ka" => {
213 let (new_map_ka, tex_future) = Texture::from_mtl_line(&mut line_parts, path, gfx_queue.clone()).unwrap();
214 map_ka = Some(new_map_ka);
215 future = future.join(tex_future).boxed();
216 },
217 "map_Ks" => {
218 let (new_map_ks, tex_future) = Texture::from_mtl_line(&mut line_parts, path, gfx_queue.clone()).unwrap();
219 map_ks = Some(new_map_ks);
220 future = future.join(tex_future).boxed();
221 },
222 "map_Ke" => {
223 let (new_map_ke, tex_future) = Texture::from_mtl_line(&mut line_parts, path, gfx_queue.clone()).unwrap();
224 map_ke = Some(new_map_ke);
225 future = future.join(tex_future).boxed();
226 },
227 "map_Km" | "map_Bump" | "map_bump" => {
228 let (new_map_km, tex_future) = Texture::from_mtl_line(&mut line_parts, path, gfx_queue.clone()).unwrap();
229 map_km = Some(new_map_km);
230 future = future.join(tex_future).boxed();
231 },
232 "map_Ns" => {
233 let (new_map_ns, tex_future) = Texture::from_mtl_line(&mut line_parts, path, gfx_queue.clone()).unwrap();
234 map_ns = Some(new_map_ns);
235 future = future.join(tex_future).boxed();
236 },
237 "map_d" => {
238 let (new_map_d, tex_future) = Texture::from_mtl_line(&mut line_parts, path, gfx_queue.clone()).unwrap();
239 map_d = Some(new_map_d);
240 future = future.join(tex_future).boxed();
241 },
242 "map_refl" | "refl" => {
243 let (new_map_refl, tex_future) = Texture::from_mtl_line(&mut line_parts, path, gfx_queue.clone()).unwrap();
244 map_refl = Some(new_map_refl);
246 future = future.join(tex_future).boxed();
247 },
248
249 "illum" => illum = Some(line_parts.next().unwrap_or_else(|| panic!("formatting error in {}", path)).parse::<i8>().unwrap_or_else(|_| panic!("formatting error in {}", path)) as u8),
251
252 a => panic!("formatting error in {}. {} is not supported", path, a),
254 };
255 }
256
257 let mut material = Material{
258 name: name.clone(),
259 illum: 11,
260 kd,
261 ka,
262 ks,
263 ke,
264 km,
265 ns,
266 ni,
267 d,
268
269 map_kd,
270 map_ka,
271 map_ks,
272 map_ke,
273 map_km,
274 map_ns,
275 map_d,
276 map_refl,
277
278 halo,
279 };
280
281 material.set_illumination_model(illum).unwrap_or_else(|_| panic!("formatting error in {}", path));
282
283 (name, (Arc::new(material), future))
284 }
285
286 pub fn set_illumination_model(&mut self, illum: Option<u8>) -> Result<(), ()> {
287 match illum {
288 Some(illum) => {
289 self.illum = match illum {
290 0 => 0,
291 1 if self.ka.is_some() || self.map_ka.is_some() => 1,
292 2 if (self.ks.is_some() || self.map_ks.is_some()) && self.ns.is_some() => 2,
293 3 => 3,
294 4 => 4,
295 5 => 5,
296 6 => 6,
297 7 => 7,
298 8 => 8,
299 9 => 9,
300 10 => 10,
301 _ => return Err(())
302 };
303 Ok(())
304 },
305 None => { todo!()
307 }
308 }
309 }
310}