mod3d_gl/
webgl.rs

1//a Imports
2use std::collections::HashMap;
3
4use crate::console_log;
5use crate::webgl_log::log_gl_vao;
6use crate::{Gl, GlProgram, GlShaderType, Mat4, PipelineDesc, UniformBuffer};
7use web_sys::WebGl2RenderingContext;
8
9mod shader;
10pub use shader::Shader;
11mod program;
12pub use program::Program;
13
14mod buffer;
15
16mod texture;
17
18mod vao;
19use vao::Vao;
20
21//a Model3DWebGL
22//tp Model3DWebGL
23#[derive(Debug)]
24pub struct Model3DWebGL {
25    context: WebGl2RenderingContext,
26}
27
28//ip Model3DWebGL
29impl Model3DWebGL {
30    pub fn new(context: WebGl2RenderingContext) -> Self {
31        Self { context }
32    }
33    pub fn context(&self) -> &WebGl2RenderingContext {
34        &self.context
35    }
36
37    //fp compile_and_link_program
38    /// Create a program from a list of compiled shaders
39    pub fn compile_and_link_program(
40        &self,
41        vertex_src: String,
42        fragment_src: String,
43        named_attrs: Vec<(String, mod3d_base::VertexAttr)>,
44        named_uniforms: Vec<(String, crate::UniformId)>,
45        named_uniform_buffers: HashMap<String, usize>,
46        named_textures: Vec<(String, crate::TextureId, usize)>,
47    ) -> Result<<Self as Gl>::Program, String> {
48        let vert_shader = Shader::compile(&self.context, &vertex_src, GlShaderType::Vertex)?;
49        let frag_shader = Shader::compile(&self.context, &fragment_src, GlShaderType::Fragment)?;
50
51        let mut program = Program::link_program(&self.context, &[&vert_shader, &frag_shader])?;
52        for (name, attr) in named_attrs {
53            program.add_attr_name(self, &name, attr)?;
54        }
55        for (name, uniform) in named_uniforms {
56            program.add_uniform_name(self, &name, uniform)?;
57        }
58        for (name, uniform) in named_uniform_buffers {
59            program.add_uniform_buffer_name(self, &name, uniform)?;
60        }
61        for (name, texture_id, unit) in named_textures {
62            program.add_uniform_texture_name(self, &name, texture_id, unit)?;
63        }
64        Ok(program)
65    }
66}
67
68//ip Deref for Model3DWebGL
69impl std::ops::Deref for Model3DWebGL {
70    type Target = WebGl2RenderingContext;
71    fn deref(&self) -> &WebGl2RenderingContext {
72        &self.context
73    }
74}
75
76//ip Gl for Model3DWebGL
77impl Gl for Model3DWebGL {
78    // type Id = u32;
79    // type Shader = Shader;
80    type Program = Program;
81    type Buffer = buffer::Buffer;
82    type Vao = vao::Vao;
83    type Texture = texture::Texture;
84
85    type PipelineDesc<'a> = PipelineDesc;
86
87    fn create_pipeline<F: Fn(&str) -> Result<String, String>>(
88        &mut self,
89        read_src: &F,
90        pipeline_desc: Box<Self::PipelineDesc<'_>>,
91    ) -> Result<Self::Program, String> {
92        let compile_and_link_program = |v_src, f_src, na, nu, nub, nt| {
93            self.compile_and_link_program(v_src, f_src, na, nu, nub, nt)
94        };
95        pipeline_desc.compile(read_src, &compile_and_link_program)
96    }
97
98    //fp use_program
99    /// Use the program
100    fn use_program(&self, program: Option<&Self::Program>) {
101        if let Some(program) = program {
102            program.set_used(&self.context);
103        } else {
104            self.context.use_program(None);
105        }
106    }
107
108    //mp init_buffer_of_indices
109    fn init_buffer_of_indices(
110        &mut self,
111        buffer: &mut <Self as Gl>::Buffer,
112        view: &mod3d_base::BufferIndexAccessor<Self>,
113    ) {
114        buffer.of_indices(view, self);
115    }
116
117    //mp vao_create_from_indices
118    fn vao_create_from_indices(&mut self, indices: &crate::IndexBuffer<Self>) -> Result<Vao, ()> {
119        Vao::create_from_indices(self, indices)
120    }
121
122    //mp buffer_bind_to_vao_attr
123    fn buffer_bind_to_vao_attr(
124        &mut self,
125        buffer: &<Self as Gl>::Buffer,
126        attr_id: &<Program as GlProgram>::GlAttrId,
127        count: u32,
128        ele_type: mod3d_base::BufferElementType,
129        byte_offset: u32,
130        stride: u32,
131    ) {
132        buffer.bind_to_vao_attr(self, *attr_id, count, ele_type, byte_offset, stride);
133    }
134
135    //mp program_set_uniform_mat4
136    fn program_set_uniform_mat4(&mut self, program: &Program, id: crate::UniformId, mat4: &Mat4) {
137        console_log!("program_set_uniform_mat4: {:?} {:?}", id, mat4);
138        if let Some(u) = program.uniform(id) {
139            self.context
140                .uniform_matrix4fv_with_f32_array(Some(u), false, mat4);
141        }
142    }
143
144    //fp program_set_uniform_floats_4
145    fn program_set_uniform_floats_4(
146        &mut self,
147        program: &Self::Program,
148        id: crate::UniformId,
149        floats: &[f32],
150    ) {
151        console_log!("webgl: set uniform [vec4] {id:?} {floats:?}");
152        if let Some(u) = program.uniform(id) {
153            self.context.uniform4fv_with_f32_array(Some(u), floats);
154        }
155    }
156
157    //mp program_bind_uniform_index
158    fn program_bind_uniform_index(
159        &mut self,
160        program: &<Self as Gl>::Program,
161        uniform_buffer_id: usize,
162        gl_uindex: u32,
163    ) -> Result<(), ()> {
164        if let Some(u) = program.uniform_buffer(uniform_buffer_id) {
165            self.context
166                .uniform_block_binding(program.program(), u, gl_uindex);
167            Ok(())
168        } else {
169            Err(())
170        }
171    }
172
173    //mp program_use_texture
174    /// Requires the program to be 'used'
175    fn program_use_texture(
176        &mut self,
177        program: &<Self as Gl>::Program,
178        texture_id: crate::TextureId,
179        gl_texture: &<Self as Gl>::Texture,
180    ) {
181        console_log!("webgl: set texture {texture_id:?}");
182        if let Some((u, unit)) = program.texture_uniform(texture_id) {
183            self.context
184                .active_texture(WebGl2RenderingContext::TEXTURE0 + unit);
185            self.context
186                .bind_texture(WebGl2RenderingContext::TEXTURE_2D, gl_texture.gl_texture());
187            self.context.uniform1i(Some(u), unit as i32);
188        }
189    }
190
191    //mp draw_primitive
192    fn draw_primitive(&mut self, vaos: &[Vao], primitive: &mod3d_base::Primitive) {
193        console_log!("webgl: draw_primitive {primitive:?}");
194        use mod3d_base::PrimitiveType::*;
195        let gl_type = match primitive.primitive_type() {
196            Points => WebGl2RenderingContext::POINTS,
197            Lines => WebGl2RenderingContext::LINES,
198            LineLoop => WebGl2RenderingContext::LINE_LOOP,
199            LineStrip => WebGl2RenderingContext::LINE_STRIP,
200            Triangles => WebGl2RenderingContext::TRIANGLES,
201            TriangleFan => WebGl2RenderingContext::TRIANGLE_FAN,
202            TriangleStrip => WebGl2RenderingContext::TRIANGLE_STRIP,
203        };
204        let opt_vertices_index: Option<usize> = primitive.vertices_index().into();
205        if let Some(vertices_index) = opt_vertices_index {
206            let index_type = vaos[vertices_index].bind_vao(self);
207            self.draw_elements_with_i32(
208                gl_type,
209                primitive.index_count() as i32,
210                index_type,
211                primitive.byte_offset() as i32,
212            );
213        } else {
214            self.draw_arrays(
215                gl_type,
216                primitive.byte_offset() as i32,
217                primitive.index_count() as i32,
218            );
219        }
220    }
221
222    //mp bind_vao
223    fn bind_vao(&mut self, vao: Option<&Self::Vao>) {
224        if let Some(vao) = vao {
225            vao.bind_vao(self);
226        } else {
227            self.bind_vertex_array(None);
228            log_gl_vao(self, None, "bind_vao");
229        }
230    }
231
232    //mp uniform_buffer_create
233    fn uniform_buffer_create<F: Sized>(
234        &mut self,
235        data: &[F],
236        is_dynamic: bool,
237    ) -> Result<UniformBuffer<Self>, ()> {
238        let byte_length = std::mem::size_of_val(data);
239        let mut gl = buffer::Buffer::default();
240        gl.uniform_buffer(self, data, is_dynamic)?;
241        Ok(UniformBuffer::new(gl, byte_length))
242    }
243
244    //mp uniform_buffer_update_data
245    fn uniform_buffer_update_data<F: std::fmt::Debug>(
246        &mut self,
247        uniform_buffer: &UniformBuffer<Self>,
248        data: &[F],
249        byte_offset: u32,
250    ) {
251        uniform_buffer
252            .gl_buffer()
253            .uniform_update_data(self, data, byte_offset);
254    }
255
256    //mp uniform_index_of_range
257    fn uniform_index_of_range(
258        &mut self,
259        uniform_buffer: &UniformBuffer<Self>,
260        gl_uindex: u32,
261        byte_offset: usize,
262        byte_length: usize,
263    ) {
264        let (byte_offset, byte_length) = uniform_buffer.offset_and_length(byte_offset, byte_length);
265        uniform_buffer.gl_buffer().bind_buffer_range(
266            self,
267            WebGl2RenderingContext::UNIFORM_BUFFER,
268            gl_uindex,
269            byte_offset as i32,
270            byte_length as i32,
271        );
272    }
273}
274
275//ip mod3d_base::Renderable for Model3DWebGL
276impl mod3d_base::Renderable for Model3DWebGL {
277    type Buffer = buffer::Buffer;
278    type IndexAccessor = crate::BufferView<Self>;
279    type DataAccessor = crate::BufferView<Self>;
280    type Texture = texture::Texture;
281    type Material = crate::Material;
282    type Vertices = crate::Vertices<Self>;
283    type Descriptor = crate::Descriptor;
284
285    //mp init_buffer_desc_client
286    /// Initialize a buffer descriptor client - it will have been created using default()
287    fn init_buffer_desc_client(
288        &mut self,
289        _client: &mut Self::Descriptor,
290        _buffer_desc: &mod3d_base::BufferDescriptor<Self>,
291    ) {
292        // todo!();
293    }
294
295    //mp init_buffer_data_client
296    /// Initialize a BufferData client
297    ///
298    /// This may be called multiple times for the same [BufferData]; if the
299    /// gl buffer is 0 then create, else it already exists with the same data
300    fn init_buffer_data_client(
301        &mut self,
302        client: &mut Self::Buffer,
303        buffer_data: &mod3d_base::BufferData<Self>,
304    ) {
305        if client.is_none() {
306            client.of_data(buffer_data, self)
307        }
308    }
309
310    /// Initialize the client of an index accessor of a buffer data
311    fn init_index_accessor_client(
312        &mut self,
313        client: &mut Self::IndexAccessor,
314        buffer_view: &mod3d_base::BufferIndexAccessor<Self>,
315    ) {
316        client.init_index_accessor_client(buffer_view, self);
317    }
318
319    //mp init_buffer_view_client
320    /// Initialize a buffer view client
321    fn init_buffer_view_client(
322        &mut self,
323        client: &mut Self::DataAccessor,
324        buffer_view: &mod3d_base::BufferDataAccessor<Self>,
325        attr: mod3d_base::VertexAttr,
326    ) {
327        client.init_buffer_view_client(buffer_view, attr, self);
328    }
329
330    //mp create_vertices_client
331    fn create_vertices_client(&mut self, vertices: &mod3d_base::Vertices<Self>) -> Self::Vertices {
332        Self::Vertices::create(vertices, self)
333    }
334
335    //mp create_texture_client
336    fn create_texture_client(&mut self, texture: &mod3d_base::Texture<Self>) -> Self::Texture {
337        Self::Texture::of_texture(texture, self) // , self)
338    }
339
340    //mp init_material_client
341    fn init_material_client<M: mod3d_base::Material>(
342        &mut self,
343        _client: &mut Self::Material,
344        _material: &M,
345    ) {
346    }
347
348    fn create_material_client<M>(
349        &mut self,
350        object: &mod3d_base::Object<M, Self>,
351        material: &M,
352    ) -> crate::Material
353    where
354        M: mod3d_base::Material,
355    {
356        crate::Material::create(self, object, material).unwrap()
357    }
358
359    //zz All done
360}