1use 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#[derive(Debug)]
24pub struct Model3DWebGL {
25 context: WebGl2RenderingContext,
26}
27
28impl Model3DWebGL {
30 pub fn new(context: WebGl2RenderingContext) -> Self {
31 Self { context }
32 }
33 pub fn context(&self) -> &WebGl2RenderingContext {
34 &self.context
35 }
36
37 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
68impl std::ops::Deref for Model3DWebGL {
70 type Target = WebGl2RenderingContext;
71 fn deref(&self) -> &WebGl2RenderingContext {
72 &self.context
73 }
74}
75
76impl Gl for Model3DWebGL {
78 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 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 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 fn vao_create_from_indices(&mut self, indices: &crate::IndexBuffer<Self>) -> Result<Vao, ()> {
119 Vao::create_from_indices(self, indices)
120 }
121
122 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 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 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 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 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 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 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 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 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 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
275impl 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 fn init_buffer_desc_client(
288 &mut self,
289 _client: &mut Self::Descriptor,
290 _buffer_desc: &mod3d_base::BufferDescriptor<Self>,
291 ) {
292 }
294
295 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 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 fn init_data_accessor_client(
322 &mut self,
323 client: &mut Self::DataAccessor,
324 buffer_data_accessor: &mod3d_base::BufferDataAccessor<Self>,
325 ) {
326 client.init_data_accessor_client(buffer_data_accessor, self);
327 }
328
329 fn create_vertices_client(&mut self, vertices: &mod3d_base::Vertices<Self>) -> Self::Vertices {
331 Self::Vertices::create(vertices, self)
332 }
333
334 fn create_texture_client(&mut self, texture: &mod3d_base::Texture<Self>) -> Self::Texture {
336 Self::Texture::of_texture(texture, self) }
338
339 fn init_material_client<M: mod3d_base::Material>(
341 &mut self,
342 _client: &mut Self::Material,
343 _material: &M,
344 ) {
345 }
346
347 fn create_material_client<M>(
348 &mut self,
349 object: &mod3d_base::Object<M, Self>,
350 material: &M,
351 ) -> crate::Material
352 where
353 M: mod3d_base::Material,
354 {
355 crate::Material::create(self, object, material).unwrap()
356 }
357
358 }