1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use crate::definition::*;
use crate::io::*;
use std::collections::HashMap;
use std::path::Path;

impl<'a> Loaded<'a> {
    ///
    /// Deserialize a loaded .obj file resource and .mtl material file resource (if present) into a list of meshes and materials.
    /// It uses the [wavefront-obj](https://crates.io/crates/wavefront_obj/main.rs) crate.
    ///
    /// # Feature
    /// Only available when the `obj-io` feature is enabled.
    ///
    pub fn obj<P: AsRef<Path>>(
        &'a self,
        path: P,
    ) -> Result<(Vec<CPUMesh>, Vec<CPUMaterial>), IOError> {
        let obj_bytes = self.bytes(path.as_ref())?;
        let obj = wavefront_obj::obj::parse(String::from_utf8(obj_bytes.to_owned()).unwrap())?;
        let p = path.as_ref().parent().unwrap();

        // Parse materials
        let mut cpu_materials = Vec::new();
        if let Some(material_library) = obj.material_library {
            let bytes = self
                .bytes(p.join(material_library).to_str().unwrap())?
                .to_owned();
            let materials = wavefront_obj::mtl::parse(String::from_utf8(bytes).unwrap())?.materials;

            for material in materials {
                let color = if material.color_diffuse.r != material.color_diffuse.g
                    || material.color_diffuse.g != material.color_diffuse.b
                {
                    material.color_diffuse
                } else if material.color_specular.r != material.color_specular.g
                    || material.color_specular.g != material.color_specular.b
                {
                    material.color_specular
                } else if material.color_ambient.r != material.color_ambient.g
                    || material.color_ambient.g != material.color_ambient.b
                {
                    material.color_ambient
                } else {
                    material.color_diffuse
                };
                let diffuse_intensity = (material.color_diffuse.r as f32)
                    .max(material.color_diffuse.g as f32)
                    .max(material.color_diffuse.b as f32);
                let specular_intensity = (material.color_specular.r as f32)
                    .max(material.color_specular.g as f32)
                    .max(material.color_specular.b as f32);
                cpu_materials.push(CPUMaterial {
                    name: material.name,
                    color: Some((
                        color.r as f32,
                        color.g as f32,
                        color.b as f32,
                        material.alpha as f32,
                    )),
                    diffuse_intensity: Some(diffuse_intensity),
                    specular_intensity: Some(specular_intensity),
                    specular_power: Some(material.specular_coefficient as f32),
                    texture_image: if let Some(path) = material
                        .diffuse_map
                        .as_ref()
                        .map(|texture_name| p.join(texture_name).to_str().unwrap().to_owned())
                    {
                        Some(self.image(&path)?)
                    } else {
                        None
                    },
                });
            }
        }

        // Parse meshes
        let mut cpu_meshes = Vec::new();
        for object in obj.objects.iter() {
            // Objects consisting of several meshes with different materials
            for mesh in object.geometry.iter() {
                // All meshes with different materials
                let mut positions = Vec::new();
                let mut normals = Vec::new();
                let mut uvs = Vec::new();
                let mut indices = Vec::new();

                let mut map: HashMap<usize, usize> = HashMap::new();

                let mut process = |i: wavefront_obj::obj::VTNIndex| {
                    let mut index = map.get(&i.0).map(|v| *v);

                    let uvw = i.1.map(|tex_index| object.tex_vertices[tex_index]);
                    let normal = i.2.map(|normal_index| object.normals[normal_index]);

                    if let Some(ind) = index {
                        if let Some(tex) = uvw {
                            if ((uvs[ind * 2] - tex.u as f32) as f32).abs() > std::f32::EPSILON
                                || ((uvs[ind * 2 + 1] - tex.v as f32) as f32).abs()
                                    > std::f32::EPSILON
                            {
                                index = None;
                            }
                        }
                        if let Some(n) = normal {
                            if ((normals[ind * 3] - n.x as f32) as f32).abs() > std::f32::EPSILON
                                || ((normals[ind * 3 + 1] - n.y as f32) as f32).abs()
                                    > std::f32::EPSILON
                                || ((normals[ind * 3 + 2] - n.z as f32) as f32).abs()
                                    > std::f32::EPSILON
                            {
                                index = None;
                            }
                        }
                    }

                    if index.is_none() {
                        index = Some(positions.len() / 3);
                        map.insert(i.0, index.unwrap());
                        let position = object.vertices[i.0];
                        positions.push(position.x as f32);
                        positions.push(position.y as f32);
                        positions.push(position.z as f32);

                        if let Some(tex) = uvw {
                            uvs.push(tex.u as f32);
                            uvs.push(tex.v as f32);
                        }
                        if let Some(n) = normal {
                            normals.push(n.x as f32);
                            normals.push(n.y as f32);
                            normals.push(n.z as f32);
                        }
                    }

                    indices.push(index.unwrap() as u32);
                };
                for shape in mesh.shapes.iter() {
                    // All triangles with same material
                    match shape.primitive {
                        wavefront_obj::obj::Primitive::Triangle(i0, i1, i2) => {
                            process(i0);
                            process(i1);
                            process(i2);
                        }
                        _ => {}
                    }
                }

                cpu_meshes.push(CPUMesh {
                    name: object.name.to_string(),
                    material_name: mesh.material_name.clone(),
                    positions,
                    indices: Some(indices),
                    normals: Some(normals),
                    uvs: Some(uvs),
                    colors: None,
                });
            }
        }
        Ok((cpu_meshes, cpu_materials))
    }
}