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}