feo_oop_engine/components/
texture.rs

1//! Textures used in materials
2
3use {
4    std::{
5        sync::RwLock, 
6        collections::HashMap, 
7        fmt::{
8            self, 
9            Formatter
10        }, 
11        fs::File, 
12        io::{
13            Cursor, 
14            Read
15        }, 
16        str::SplitWhitespace, 
17        sync::Arc
18    },
19    lazy_static,
20    vulkano::{
21        sync::GpuFuture, 
22        device::Queue, 
23        format::Format, 
24        image::{
25            ImageDimensions, 
26            ImmutableImage, 
27            MipmapsCount, 
28            view::ImageView
29        }, 
30        memory::pool::{
31            PotentialDedicatedAllocation, 
32            StdMemoryPoolAlloc
33        }, 
34        sampler::{
35            Filter, 
36            MipmapMode, 
37            Sampler, 
38            SamplerAddressMode
39        }
40    },
41    image::{
42        ImageDecoder, 
43        codecs::bmp::BmpDecoder, 
44        codecs::jpeg::JpegDecoder, 
45        codecs::png::PngDecoder
46    },
47};
48lazy_static!{
49    static ref DEFAULT_TEXTURES: Arc<RwLock<HashMap<u32, Arc<Texture>>>> = Arc::new(RwLock::new(HashMap::new()));
50}
51
52/// A texture is an image that helps describe the surface of a model
53pub struct Texture {
54    #[allow(clippy::type_complexity)]
55    pub img_view: Arc<ImageView<Arc<ImmutableImage<Format, PotentialDedicatedAllocation<StdMemoryPoolAlloc>>>>>,
56    pub sampler: Arc<Sampler>,
57}
58
59impl fmt::Debug for Texture{
60    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
61        write!(f, "texture debug todo")
62    }
63}
64
65impl Texture {
66    /// Returns the default Texture <!-- TODO: use instead of undefined texture constant -->
67    pub fn default(queue: Arc<Queue>) -> Arc<Self> {
68        if let Some(texture) = DEFAULT_TEXTURES.read().unwrap().get(&queue.id_within_family()) { // TODO: other id
69            return texture.clone();
70        }
71        
72        let png_bytes = include_bytes!("./default_texture.png").to_vec();
73        let cursor = Cursor::new(png_bytes);
74        let decoder = PngDecoder::new(cursor).unwrap();
75
76        let (width, height) = decoder.dimensions();
77        
78        let mut reader = decoder.into_reader().unwrap();
79
80        let dimensions = ImageDimensions::Dim2d {
81            width,
82            height,
83            array_layers: 1,
84        };
85
86        let mut image_data = Vec::new();
87        image_data.resize((width * height * 4) as usize, 0);
88        
89        reader.read_exact(&mut image_data).unwrap();
90        
91        let (image, future) = match ImmutableImage::from_iter(
92                    image_data.iter().cloned(),
93                    dimensions,
94                    MipmapsCount::One,
95                    Format::R8G8B8A8Srgb,
96                    queue.clone(),
97                ) {
98            Ok(img) => img,
99            _ => unreachable!(),
100        };
101
102        Arc::new(future.boxed().then_signal_fence_and_flush().unwrap()).wait(None).unwrap();
103
104        let texture = Arc::new(
105                Texture {
106                    img_view: ImageView::new(image).unwrap(),
107                    sampler: Sampler::new( // TODO
108                        queue.device().clone(),
109                        Filter::Nearest,
110                        Filter::Nearest,
111                        MipmapMode::Linear, 
112                        SamplerAddressMode::Repeat, // TODO other options
113                        SamplerAddressMode::Repeat,
114                        SamplerAddressMode::Repeat,
115                        0.0,
116                        1.0,
117                        0.0,
118                        0.0
119                    ).unwrap()
120                }
121            );
122        
123        DEFAULT_TEXTURES.write().unwrap().insert(queue.id_within_family(), texture.clone());
124        
125        texture
126    }
127
128    #[allow(unused_variables, unused_assignments, clippy::unused_io_amount)]
129    pub fn from_mtl_line(params: &mut SplitWhitespace, path: &str, queue: Arc<Queue>) -> Result<(Arc<Texture>, Box<dyn GpuFuture>), &'static str> { 
130        let mut blendu = Filter::Linear;
131        let mut blendv  = Filter::Linear;
132        let mut cc = false;
133        let mut clamp = SamplerAddressMode::Repeat;
134        let mut mm = (0.0, 1.0); // base and gain
135        let mut o = (0.0, 0.0, 0.0); // TYPE UVW
136        let mut s = (1.0, 1.0, 1.0);
137        let mut t = (0.0, 0.0, 0.0);
138        let mut texres = None;
139
140        let mut incomplete = String::new();
141        for part in params.clone() {
142            let part = incomplete + part;
143            incomplete = String::new();
144            match part.as_str() {
145                _ if part.ends_with(".png") => { // color
146                    let part = part.replace("\\", "/");
147                    let texture_path = path.rsplitn(2, '/').nth(1).unwrap().to_owned() + "/" + part.as_str();
148                    let (texture, tex_future) = {
149                        let mut file = File::open(texture_path).unwrap();
150                        let mut buffer = Vec::new();
151                        file.read_to_end(&mut buffer).unwrap();
152                        let cursor = Cursor::new(buffer);
153                        let decoder = PngDecoder::new(cursor).unwrap();
154                        let (width, height) = decoder.dimensions();
155                        let mut reader = decoder.into_reader().unwrap();
156                        let dimensions = ImageDimensions::Dim2d {
157                            width,
158                            height,
159                            array_layers: 1,
160                        };
161                        let mut image_data = Vec::new();
162                        image_data.resize((width * height * 4) as usize, 0);
163                        reader.read_exact(&mut image_data).unwrap();
164                
165                        let (image, future) = ImmutableImage::from_iter(
166                            image_data.iter().cloned(),
167                            dimensions,
168                            MipmapsCount::One,
169                            Format::R8G8B8A8Srgb,
170                            queue.clone(),
171                        )
172                        .unwrap();
173            
174                        (ImageView::new(image).unwrap(), future)
175                    };
176                    
177                    return Ok((
178                        Arc::new(
179                            Texture {
180                                img_view: texture,
181                                sampler: Sampler::new(
182                                        queue.device().clone(),
183                                        blendu,
184                                        blendv,
185                                        MipmapMode::Linear, 
186                                        clamp,
187                                        clamp,
188                                        clamp,
189                                        0.0,
190                                        1.0,
191                                        0.0,
192                                        0.0
193                                    ).unwrap(),
194                                // multiplier
195                            }
196                        ), 
197                        tex_future.boxed()
198                    ));
199                },
200
201                // compiled procedural texture files
202                _ if part.ends_with(".jpg") || part.ends_with(".jpeg") => {
203                    let part = part.replace("\\", "/");
204                    let texture_path = path.rsplitn(2, '/').nth(1).unwrap().to_owned() + "/" + part.as_str();
205                    let (texture, tex_future) = {
206                        let mut file = File::open(&texture_path).unwrap_or_else(|_| panic!("The texture \"{}\" does not exist.", texture_path));
207                        let mut buffer = Vec::new();
208                        file.read_to_end(&mut buffer).unwrap();
209                        let cursor = Cursor::new(buffer);
210                        let decoder = JpegDecoder::new(cursor).unwrap();
211                        let (width, height) = decoder.dimensions();
212                        let mut reader = decoder.into_reader().unwrap();
213                        let dimensions = ImageDimensions::Dim2d {
214                            width,
215                            height,
216                            array_layers: 1,
217                        };
218                        let mut image_data = Vec::new();
219                        image_data.resize((width * height * 4) as usize, 0);
220                        reader.read(&mut image_data).unwrap();
221                
222                        let (image, future) = ImmutableImage::from_iter(
223                            image_data.iter().cloned(),
224                            dimensions,
225                            MipmapsCount::One,
226                            Format::R8G8B8Srgb,
227                            queue.clone(),
228                        )
229                        .unwrap();
230            
231                        (ImageView::new(image).unwrap(), future)
232                    };
233                    return Ok((
234                        Arc::new(
235                            Texture {
236                                img_view: texture,
237                                sampler: Sampler::new(
238                                        queue.device().clone(),
239                                        blendu,
240                                        blendv,
241                                        MipmapMode::Linear, 
242                                        clamp,
243                                        clamp,
244                                        clamp,
245                                        0.0,
246                                        1.0,
247                                        0.0,
248                                        0.0
249                                    ).unwrap(),
250                                // multiplier
251                            }
252                        ), 
253                        tex_future.boxed()
254                    ));
255                },
256                
257                // compiled procedural texture files
258                _ if part.ends_with(".bmp") => { // color
259                    let part = part.replace("\\", "/");
260                    let texture_path = path.rsplitn(2, '/').nth(1).unwrap().to_owned() + "/" + part.as_str();
261                    let (texture, tex_future) = {
262                        let mut file = File::open(texture_path).unwrap();
263                        let mut buffer = Vec::new();
264                        file.read_to_end(&mut buffer).unwrap();
265                        let cursor = Cursor::new(buffer);
266                        let decoder = BmpDecoder::new(cursor).unwrap();
267                        let (width, height) = decoder.dimensions();
268                        let mut reader = decoder.into_reader().unwrap();
269                        let dimensions = ImageDimensions::Dim2d {
270                            width,
271                            height,
272                            array_layers: 1, // * scale (s)
273                        };
274                        let mut image_data = Vec::new();
275                        image_data.resize((width * height * 4) as usize, 0);
276                        reader.read_exact(&mut image_data).unwrap();
277                
278                        let (image, future) = ImmutableImage::from_iter(
279                            image_data.iter().cloned(),
280                            dimensions,
281                            MipmapsCount::One,
282                            Format::R8G8B8Srgb,
283                            queue.clone(),
284                        ).unwrap();
285            
286                        (ImageView::new(image).unwrap(), future)
287                    };
288                    return Ok((
289                        Arc::new(
290                            Texture {
291                                img_view: texture,
292                                sampler: Sampler::new(
293                                        queue.device().clone(),
294                                        blendu,
295                                        blendv,
296                                        MipmapMode::Linear, 
297                                        clamp,
298                                        clamp,
299                                        clamp,
300                                        0.0,
301                                        1.0,
302                                        0.0,
303                                        0.0
304                                    ).unwrap(),
305                                // multiplier
306                            }
307                        ), 
308                        tex_future.boxed()
309                    ));
310                },
311
312                "-blendu" => blendu = match params.next().unwrap() {
313                    // turns texture blending in the horizontal direction
314                    "on" => Filter::Nearest,
315                    "off" => Filter::Linear,
316                    _ => panic!("formatting error in {}", path)
317                },
318                "-blendv" => blendv = match params.next().unwrap() {
319                    // turns texture blending in the vertical direction
320                    "on" => Filter::Nearest,
321                    "off" => Filter::Linear,
322                    _ => panic!("formatting error in {}", path)
323                },
324                "-cc" => cc = match params.next().unwrap() {
325                    // color correction for the texture
326                    "on" => true,
327                    "off" => false,
328                    _ => panic!("formatting error in {}", path)
329                },
330                "-clamp" => clamp = match params.next().unwrap() {
331                    // clamping on means that only one copy of the texture is mapped onto the surface
332                    // rather than repeating copies
333
334                    // When clamping is on, textures are restricted to 0-1 in the uvw range
335                    "on" => SamplerAddressMode::ClampToEdge,
336                    "off" => SamplerAddressMode::Repeat,
337                    _ => panic!("formatting error in {}", path)
338                },
339                //-mm option modifies the range over which scalar or color texture values may vary.
340                // base -> adds a base value to the texture values + increase - decrease/dim 
341                // gain -> increases range of texture values
342                "-mm" => mm = (
343                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)),
344                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path))
345                ),
346                
347                // horizontal
348                // vertical
349                // depth 
350                // for \/\/\/
351                "-o" => o = ( // offset position of texture map
352                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)),
353                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)), // TODO: optional
354                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)), // --
355                ),
356                "-s" => s = ( // scales the texture pattern
357                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)),
358                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)), // --
359                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)), // --
360                ),
361                "-t" => t = ( // turbulence for textures -> no noticeable tiling
362                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)),
363                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)), // --
364                    params.next().unwrap().parse::<f32>().unwrap_or_else(|_| panic!("formatting error in {}", path)), // --
365                ),
366
367                // the resolution of the texture
368                "-texres" => texres = Some(params.next().unwrap().parse::<u32>().unwrap_or_else(|_| panic!("formatting error in {}", path))),
369
370                _ => incomplete = format!("{} ", part),
371            }
372        }
373        Err("No image data found") // TODO clean errors
374    }
375}