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}