mod3d_gl/
shader_instantiable.rs

1//a Imports
2use crate::{Gl, GlProgram, UniformId, Vertices};
3
4//a Shader structure
5//tp ShaderMaterialBaseData
6/// Change to u8s
7///
8/// base_color should be [u8; 4]
9///
10/// metallic should be u8
11/// roughness should be u8
12/// emissive_color should be [u8; 3]
13/// occlusion factor is from:
14///    A scalar parameter controlling the amount of occlusion applied. A value of `0.0` means no occlusion. A value of `1.0` means full occlusion. This value affects the final occlusion value as: `1.0 + strength * (<sampled occlusion texture value> - 1.0)`.
15
16#[derive(Default, Debug)]
17#[repr(C, packed)]
18pub struct ShaderMaterialBaseData {
19    base_color: [f32; 4],
20    metallic: f32,
21    roughness: f32,
22    occlusion_factor: f32,
23    emissive_factor: f32,
24}
25
26//ip ShaderMaterialBaseData
27impl ShaderMaterialBaseData {
28    pub fn of_material<M>(material: &M) -> Self
29    where
30        M: mod3d_base::Material,
31    {
32        let base_data = material.base_data();
33        let (r, g, b, a) = base_data.rgba_tuple();
34        let base_color = [
35            r as f32 / 255.0,
36            g as f32 / 255.0,
37            b as f32 / 255.0,
38            a as f32 / 255.0,
39        ];
40        let (metallic, roughness) = base_data.metallic_roughness();
41        let occlusion_factor = 0.;
42        let emissive_factor = 0.;
43        Self {
44            base_color,
45            metallic,
46            roughness,
47            occlusion_factor,
48            emissive_factor,
49        }
50    }
51    pub fn as_slice(&self) -> &[f32] {
52        unsafe {
53            std::slice::from_raw_parts(
54                self as *const ShaderMaterialBaseData as *const f32,
55                std::mem::size_of_val(self) / std::mem::size_of::<f32>(),
56            )
57        }
58    }
59}
60
61//a ShaderInstantiable
62//tp ShaderInstantiable
63/// This is a shader-specific instantiable built from the vertices of an [mod3d_base::Instantiable]
64///
65/// A shader requires a VAO that maps *some* of the vertex attribute
66/// buffers to particular attribute UIDs in the shader program
67///
68/// It requires mapping of textures to texture things
69///
70/// Possibly it will also require some particullar Uniforms
71///
72/// An [mod3d_base::Instance] can be renderd with a shader by using the RenderRecipe
73/// from the [mod3d_base::Instantiable], using the matrix and bone positions in the
74/// Instance, and using the VAOs and other data in the
75/// [ShaderInstantiable].
76///
77/// It borrows from the [mod3d_base::Instantiable] and so does not need to its own GlBuffers
78pub struct ShaderInstantiable<'a, G>
79where
80    G: Gl,
81{
82    instantiable: &'a mod3d_base::Instantiable<G>,
83    // vaos is 1-to-1 with instantiable::vertices, specific to this shader (class)
84    vaos: Vec<G::Vao>,
85    // The program NEED NOT be borrowed, if the program's uniforms
86    // required for the draw are recorded during 'new_vao'
87    program: &'a G::Program,
88}
89
90//ip ShaderInstantiable
91impl<'a, G> ShaderInstantiable<'a, G>
92where
93    G: Gl,
94{
95    //fi new_vao
96    fn new_vao(
97        context: &mut G,
98        program: &G::Program,
99        vertices: &Vertices<G>,
100    ) -> Result<G::Vao, ()> {
101        let (indices, position, attrs) = vertices.borrow();
102        let gl_vao = context.vao_create_from_indices(indices)?;
103        for (index, vertex_attr) in program.attributes() {
104            if *vertex_attr == mod3d_base::VertexAttr::Position {
105                position.bind_to_vao_attr(context, index);
106            } else {
107                for (va, buffer) in attrs {
108                    if *vertex_attr == *va {
109                        buffer.bind_to_vao_attr(context, index);
110                        // crate::opengl_utils::check_errors().unwrap();
111                    }
112                }
113            }
114        }
115        context.bind_vao(None);
116        Ok(gl_vao)
117    }
118
119    //fp new
120    /// Create a new [ShaderInstantiable]
121    pub fn new(
122        context: &mut G,
123        program: &'a G::Program,
124        instantiable: &'a mod3d_base::Instantiable<G>,
125    ) -> Result<Self, ()> {
126        let mut vaos = Vec::new();
127        for v in &instantiable.vertices {
128            vaos.push(Self::new_vao(context, program, v)?);
129        }
130        Ok(Self {
131            instantiable,
132            vaos,
133            program,
134        })
135    }
136
137    //fp gl_draw
138    /// Draw this [ShaderInstantiable] given an [mod3d_base::Instance] data
139    pub fn gl_draw(&self, context: &mut G, instance: &mod3d_base::Instance<G>) {
140        // shader camera matrix (already set?)
141        /*
142            // for bone_set_pose in instance.bone_set_poses {
143            //  bone_set_pose.update(tick)
144            // }
145                //for (t,m,b) in self.meshes:
146                //if b>=0:
147                //bma = self.bone_set_poses[b]
148                //program.set_uniform_if("uBonesMatrices",
149                //lambda u:GL.glUniformMatrix4fv(u, bma.max_index, False, bma.data))
150                //program.set_uniform_if("uBonesScale",
151                //lambda u: GL.glUniform1f(u, 1.0) )
152                //pass
153            //else:
154                //program.set_uniform_if("uBonesScale",
155                //lambda u: GL.glUniform1f(u, 0.0) )
156                //pass
157                # Provide mesh matrix and material uniforms
158                program.set_uniform_if("uMeshMatrix",
159                                       lambda u: GL.glUniformMatrix4fv(u, 1, False, t.mat4()) )
160
161        instance bone matrices
162        instance model matrix
163        for (i, p) in render_recipe.primitives.iter().enumerate() {
164         */
165        context.program_set_uniform_mat4(
166            self.program,
167            UniformId::ModelMatrix,
168            &instance.transformation.mat4(),
169        );
170        for (i, p) in self
171            .instantiable
172            .render_recipe
173            .primitives
174            .iter()
175            .enumerate()
176        {
177            let mat = p.material();
178            if mat.is_some() {
179                let mat = &self.instantiable.materials[mat.as_usize()];
180                context.program_set_uniform_floats_4(
181                    self.program,
182                    UniformId::Material,
183                    mat.base_data().as_slice(),
184                );
185                for (texture_id, ti) in mat.textures() {
186                    if !ti.is_none() {
187                        let gl_texture = &self.instantiable.textures[ti.as_usize()];
188                        context.program_use_texture(self.program, *texture_id, gl_texture);
189                    }
190                }
191            }
192
193            // set MeshMatrix (if different to last)
194            // Optimization using mesh uniform buffer
195            // Bind a mat4-sized range of the matrices arrays to the Matrix uniform binding point
196            let m = self.instantiable.render_recipe.matrix_for_primitives[i];
197            context.program_set_uniform_mat4(
198                self.program,
199                UniformId::MeshMatrix,
200                &self.instantiable.render_recipe.matrices[m],
201            );
202            context.draw_primitive(&self.vaos, p);
203        }
204    }
205
206    //zz All done
207}