gltf_viewer_lib/render/
primitive.rs

1use std::mem::size_of;
2use std::os::raw::c_void;
3use std::path::Path;
4use std::ptr;
5use std::rc::Rc;
6
7use gl;
8use gl::types::GLenum;
9use gltf;
10use log::{warn, debug};
11
12use crate::render::math::*;
13use crate::render::{Material, Root};
14use crate::shader::*;
15use crate::importdata::ImportData;
16
17#[derive(Debug)]
18pub struct Vertex {
19    pub position: Vector3,
20    pub normal: Vector3,
21    pub tangent: Vector4,
22    pub tex_coord_0: Vector2,
23    pub tex_coord_1: Vector2,
24    pub color_0: Vector4,
25    pub joints_0: [u16; 4],
26    pub weights_0: Vector4,
27}
28
29impl Default for Vertex {
30    fn default() -> Self {
31        Vertex {
32            position: Vector3::zero(),
33            normal: Vector3::zero(),
34            tangent: Vector4::zero(),
35            tex_coord_0: Vector2::zero(),
36            tex_coord_1: Vector2::zero(),
37            color_0: Vector4::zero(),
38            joints_0: [0; 4],
39            weights_0: Vector4::zero(),
40        }
41    }
42}
43
44#[derive(Clone, Debug)]
45pub struct Texture {
46    pub id: u32,
47    pub type_: String,
48    pub path: String,
49}
50
51pub struct Primitive {
52    pub bounds: Aabb3,
53
54    vao: u32,
55    vbo: u32,
56    num_vertices: u32,
57
58    ebo: Option<u32>,
59    num_indices: u32,
60
61    mode: GLenum,
62
63    material: Rc<Material>,
64
65    pbr_shader: Rc<PbrShader>,
66
67    // TODO!: mode, targets
68}
69
70impl Primitive {
71    pub fn new(
72        bounds: Aabb3,
73        vertices: &[Vertex],
74        indices: Option<Vec<u32>>,
75        mode: GLenum,
76        material: Rc<Material>,
77        shader: Rc<PbrShader>,
78    ) -> Primitive {
79        let num_indices = indices.as_ref().map(|i| i.len()).unwrap_or(0);
80        let mut prim = Primitive {
81            bounds,
82            num_vertices: vertices.len() as u32,
83            num_indices: num_indices as u32,
84            vao: 0, vbo: 0, ebo: None,
85            mode,
86            material,
87            pbr_shader: shader,
88        };
89
90        // now that we have all the required data, set the vertex buffers and its attribute pointers.
91        unsafe { prim.setup_primitive(vertices, indices) }
92        prim
93    }
94
95    pub fn from_gltf(
96        g_primitive: &gltf::Primitive<'_>,
97        primitive_index: usize,
98        mesh_index: usize,
99        root: &mut Root,
100        imp: &ImportData,
101        base_path: &Path) -> Primitive
102    {
103        let buffers = &imp.buffers;
104        let reader = g_primitive.reader(|buffer| Some(&buffers[buffer.index()]));
105        let positions = {
106            let iter = reader
107                .read_positions()
108                .unwrap_or_else(||
109                    panic!("primitives must have the POSITION attribute (mesh: {}, primitive: {})",
110                        mesh_index, primitive_index)
111                );
112            iter.collect::<Vec<_>>()
113        };
114
115        let bounds = g_primitive.bounding_box();
116        let bounds = Aabb3 {
117            min: bounds.min.into(),
118            max: bounds.max.into()
119        };
120
121        let mut vertices: Vec<Vertex> = positions
122            .into_iter()
123            .map(|position| {
124                Vertex {
125                    position: Vector3::from(position),
126                    ..Vertex::default()
127                }
128            }).collect();
129
130        let mut shader_flags = ShaderFlags::empty();
131
132        // normals
133        if let Some(normals) = reader.read_normals() {
134            for (i, normal) in normals.enumerate() {
135                vertices[i].normal = Vector3::from(normal);
136            }
137            shader_flags |= ShaderFlags::HAS_NORMALS;
138        }
139        else {
140            debug!("Found no NORMALs for primitive {} of mesh {} \
141                   (flat normal calculation not implemented yet)", primitive_index, mesh_index);
142        }
143
144        // tangents
145        if let Some(tangents) = reader.read_tangents() {
146            for (i, tangent) in tangents.enumerate() {
147                vertices[i].tangent = Vector4::from(tangent);
148            }
149            shader_flags |= ShaderFlags::HAS_TANGENTS;
150        }
151        else {
152            debug!("Found no TANGENTS for primitive {} of mesh {} \
153                   (tangent calculation not implemented yet)", primitive_index, mesh_index);
154        }
155
156        // texture coordinates
157        let mut tex_coord_set = 0;
158        while let Some(tex_coords) = reader.read_tex_coords(tex_coord_set) {
159            if tex_coord_set > 1 {
160                warn!("Ignoring texture coordinate set {}, \
161                        only supporting 2 sets at the moment. (mesh: {}, primitive: {})",
162                        tex_coord_set, mesh_index, primitive_index);
163                tex_coord_set += 1;
164                continue;
165            }
166            for (i, tex_coord) in tex_coords.into_f32().enumerate() {
167                match tex_coord_set {
168                    0 => vertices[i].tex_coord_0 = Vector2::from(tex_coord),
169                    1 => vertices[i].tex_coord_1 = Vector2::from(tex_coord),
170                    _ => unreachable!()
171                }
172            }
173            shader_flags |= ShaderFlags::HAS_UV;
174            tex_coord_set += 1;
175        }
176
177        // colors
178        if let Some(colors) = reader.read_colors(0) {
179            let colors = colors.into_rgba_f32();
180            for (i, c) in colors.enumerate() {
181                vertices[i].color_0 = c.into();
182            }
183            shader_flags |= ShaderFlags::HAS_COLORS;
184        }
185        if reader.read_colors(1).is_some() {
186            warn!("Ignoring further color attributes, only supporting COLOR_0. (mesh: {}, primitive: {})",
187                mesh_index, primitive_index);
188        }
189
190        if let Some(joints) = reader.read_joints(0) {
191            for (i, joint) in joints.into_u16().enumerate() {
192                vertices[i].joints_0 = joint;
193            }
194        }
195        if reader.read_joints(1).is_some() {
196            warn!("Ignoring further joint attributes, only supporting JOINTS_0. (mesh: {}, primitive: {})",
197                mesh_index, primitive_index);
198        }
199
200        if let Some(weights) = reader.read_weights(0) {
201            for (i, weights) in weights.into_f32().enumerate() {
202                vertices[i].weights_0 = weights.into();
203            }
204        }
205        if reader.read_weights(1).is_some() {
206            warn!("Ignoring further weight attributes, only supporting WEIGHTS_0. (mesh: {}, primitive: {})",
207                mesh_index, primitive_index);
208        }
209
210        let indices = reader
211            .read_indices()
212            .map(|read_indices| {
213                read_indices.into_u32().collect::<Vec<_>>()
214            });
215
216        // TODO: spec:
217        // Implementation note: When the 'mode' property is set to a non-triangular type
218        //(such as POINTS or LINES) some additional considerations must be taken while
219        //considering the proper rendering technique:
220        //   For LINES with NORMAL and TANGENT properties can render with standard lighting including normal maps.
221        //   For all POINTS or LINES with no TANGENT property, render with standard lighting but ignore any normal maps on the material.
222        //   For POINTS or LINES with no NORMAL property, don't calculate lighting and instead output the COLOR value for each pixel drawn.
223        let mode = g_primitive.mode().as_gl_enum();
224
225        let g_material = g_primitive.material();
226
227        let mut material = None;
228        if let Some(mat) = root.materials.iter().find(|m| (***m).index == g_material.index()) {
229            material = Rc::clone(mat).into()
230        }
231
232        if material.is_none() { // no else due to borrow checker madness
233            let mat = Rc::new(Material::from_gltf(&g_material, root, imp, base_path));
234            root.materials.push(Rc::clone(&mat));
235            material = Some(mat);
236        };
237        let material = material.unwrap();
238        shader_flags |= material.shader_flags();
239
240        let mut new_shader = false; // borrow checker workaround
241        let shader =
242            if let Some(shader) = root.shaders.get(&shader_flags) {
243                Rc::clone(shader)
244            }
245            else {
246                new_shader = true;
247                PbrShader::new(shader_flags).into()
248
249            };
250        if new_shader {
251            root.shaders.insert(shader_flags, Rc::clone(&shader));
252        }
253
254        Primitive::new(bounds, &vertices, indices, mode, material, shader)
255    }
256
257    /// render the mesh
258    pub unsafe fn draw(&self, model_matrix: &Matrix4, mvp_matrix: &Matrix4, camera_position: &Vector3) {
259        // TODO!: determine if shader+material already active to reduce work...
260
261        if self.material.double_sided {
262            gl::Disable(gl::CULL_FACE);
263        } else {
264            gl::Enable(gl::CULL_FACE);
265        }
266
267        if self.mode == gl::POINTS {
268            gl::PointSize(10.0);
269        }
270
271        self.configure_shader(model_matrix, mvp_matrix, camera_position);
272
273        // draw mesh
274        gl::BindVertexArray(self.vao);
275        if self.ebo.is_some() {
276            gl::DrawElements(self.mode, self.num_indices as i32, gl::UNSIGNED_INT, ptr::null());
277        }
278        else {
279            gl::DrawArrays(self.mode, 0, self.num_vertices as i32)
280        }
281
282        gl::BindVertexArray(0);
283        gl::ActiveTexture(gl::TEXTURE0);
284
285        if self.material.alpha_mode != gltf::material::AlphaMode::Opaque {
286            let shader = &self.pbr_shader.shader;
287
288            gl::Disable(gl::BLEND);
289            shader.set_float(self.pbr_shader.uniforms.u_AlphaBlend, 0.0);
290            if self.material.alpha_mode == gltf::material::AlphaMode::Mask {
291                shader.set_float(self.pbr_shader.uniforms.u_AlphaCutoff, 0.0);
292            }
293        }
294    }
295
296    unsafe fn configure_shader(&self, model_matrix: &Matrix4,
297        mvp_matrix: &Matrix4, camera_position: &Vector3)
298    {
299        // let pbr_shader = &Rc::get_mut(&mut self.pbr_shader).unwrap();
300        let mat = &self.material;
301        let shader = &self.pbr_shader.shader;
302        let uniforms = &self.pbr_shader.uniforms;
303        self.pbr_shader.shader.use_program();
304
305        // camera params
306        shader.set_mat4(uniforms.u_ModelMatrix, model_matrix);
307        shader.set_mat4(uniforms.u_MVPMatrix, mvp_matrix);
308        shader.set_vector3(uniforms.u_Camera, camera_position);
309
310        // alpha blending
311        if mat.alpha_mode != gltf::material::AlphaMode::Opaque {
312            // BLEND + MASK
313            gl::Enable(gl::BLEND);
314            gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
315            shader.set_float(uniforms.u_AlphaBlend, 1.0);
316
317            if mat.alpha_mode == gltf::material::AlphaMode::Mask {
318                shader.set_float(uniforms.u_AlphaCutoff, mat.alpha_cutoff);
319            }
320        }
321
322        // NOTE: for sampler numbers, see also PbrShader constructor
323        shader.set_vector4(uniforms.u_BaseColorFactor, &mat.base_color_factor);
324        if let Some(ref base_color_texture) = mat.base_color_texture {
325            gl::ActiveTexture(gl::TEXTURE0);
326            gl::BindTexture(gl::TEXTURE_2D, base_color_texture.id);
327            shader.set_int(uniforms.u_BaseColorTexCoord, base_color_texture.tex_coord as i32);
328        }
329        if let Some(ref normal_texture) = mat.normal_texture {
330            gl::ActiveTexture(gl::TEXTURE1);
331            gl::BindTexture(gl::TEXTURE_2D, normal_texture.id);
332            shader.set_int(uniforms.u_NormalTexCoord, normal_texture.tex_coord as i32);
333            shader.set_float(uniforms.u_NormalScale, mat.normal_scale.unwrap_or(1.0));
334        }
335        if let Some(ref emissive_texture) = mat.emissive_texture {
336            gl::ActiveTexture(gl::TEXTURE2);
337            gl::BindTexture(gl::TEXTURE_2D, emissive_texture.id);
338            shader.set_int(uniforms.u_EmissiveTexCoord, emissive_texture.tex_coord as i32);
339            shader.set_vector3(uniforms.u_EmissiveFactor, &mat.emissive_factor);
340        }
341
342        if let Some(ref mr_texture) = mat.metallic_roughness_texture {
343            gl::ActiveTexture(gl::TEXTURE3);
344            gl::BindTexture(gl::TEXTURE_2D, mr_texture.id);
345            shader.set_int(uniforms.u_MetallicRoughnessTexCoord, mr_texture.tex_coord as i32);
346        }
347        shader.set_vec2(uniforms.u_MetallicRoughnessValues,
348            mat.metallic_factor, mat.roughness_factor);
349
350        if let Some(ref occlusion_texture) = mat.occlusion_texture {
351            gl::ActiveTexture(gl::TEXTURE4);
352            gl::BindTexture(gl::TEXTURE_2D, occlusion_texture.id);
353            shader.set_int(uniforms.u_OcclusionTexCoord, occlusion_texture.tex_coord as i32);
354            shader.set_float(uniforms.u_OcclusionStrength, mat.occlusion_strength);
355        }
356    }
357
358    unsafe fn setup_primitive(&mut self, vertices: &[Vertex], indices: Option<Vec<u32>>) {
359        // create buffers/arrays
360        gl::GenVertexArrays(1, &mut self.vao);
361        gl::GenBuffers(1, &mut self.vbo);
362        if indices.is_some() {
363            let mut ebo = 0;
364            gl::GenBuffers(1, &mut ebo);
365            self.ebo = Some(ebo);
366        }
367
368        gl::BindVertexArray(self.vao);
369        // load data into vertex buffers
370        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
371        let size = (vertices.len() * size_of::<Vertex>()) as isize;
372        let data = &vertices[0] as *const Vertex as *const c_void;
373        gl::BufferData(gl::ARRAY_BUFFER, size, data, gl::STATIC_DRAW);
374
375        if let Some(ebo) = self.ebo {
376            let indices = indices.unwrap();
377            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
378            let size = (indices.len() * size_of::<u32>()) as isize;
379            let data = &indices[0] as *const u32 as *const c_void;
380            gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, size, data, gl::STATIC_DRAW);
381        }
382
383        // set the vertex attribute pointers
384        let size = size_of::<Vertex>() as i32;
385        // POSITION
386        gl::EnableVertexAttribArray(0);
387        gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, position) as *const c_void);
388        // NORMAL
389        gl::EnableVertexAttribArray(1);
390        gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, normal) as *const c_void);
391        // TANGENT
392        gl::EnableVertexAttribArray(2);
393        gl::VertexAttribPointer(2, 4, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, tangent) as *const c_void);
394        // TEXCOORD_0
395        gl::EnableVertexAttribArray(3);
396        gl::VertexAttribPointer(3, 2, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, tex_coord_0) as *const c_void);
397        // TEXCOORD_1
398        gl::EnableVertexAttribArray(4);
399        gl::VertexAttribPointer(4, 2, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, tex_coord_1) as *const c_void);
400        // COLOR_0
401        gl::EnableVertexAttribArray(5);
402        gl::VertexAttribPointer(5, 4, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, color_0) as *const c_void);
403        // JOINTS_0
404        gl::EnableVertexAttribArray(6);
405        // TODO: normalization?
406        gl::VertexAttribPointer(6, 4, gl::UNSIGNED_SHORT, gl::FALSE, size, offset_of!(Vertex, joints_0) as *const c_void);
407        // WEIGHTS_0
408        gl::EnableVertexAttribArray(7);
409        gl::VertexAttribPointer(7, 4, gl::FLOAT, gl::FALSE, size, offset_of!(Vertex, weights_0) as *const c_void);
410
411        gl::BindVertexArray(0);
412    }
413}