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