kepler_ra/techtron/webgl/
program.rs

1// MIT License
2
3// Copyright (c) 2023 Techtron-Lab
4
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23
24use std::{
25    ops::{Deref, DerefMut},
26    rc::Rc,
27};
28
29use js_sys::{Array, ArrayBuffer, Float32Array};
30use wasm_bindgen::{JsCast, JsValue};
31use web_sys::{WebGl2RenderingContext, WebGlProgram, WebGlShader};
32
33use crate::techtron::webgl::texture::LoadedTexture;
34
35use super::context::GLContext;
36use super::shader::{CompiledFragmentShader, CompiledVertexShader};
37use super::super::log::*;
38use crate::console;
39
40type GL2 = WebGl2RenderingContext;
41
42#[derive(Debug, PartialEq, Eq)]
43pub struct Program {
44    pub vertex: CompiledVertexShader,
45    pub fragment: CompiledFragmentShader,
46    pub handle: WebGlProgram,
47    pub context: GLContext,
48}
49
50impl Program {
51    pub fn new(
52        context: &GLContext,
53        vertex: CompiledVertexShader,
54        fragment: CompiledFragmentShader,
55    ) -> Program {
56        if vertex.context != fragment.context || *context != vertex.context {
57            todo!()
58        }
59        let context = context.clone();
60        let handle = link_program(&context, &*vertex, &*fragment).unwrap();
61        Program {
62            vertex,
63            fragment,
64            context,
65            handle,
66        }
67    }
68
69    pub fn use_program(&self) {
70        self.context.use_program(Some(&self.handle));
71    }
72}
73
74impl Drop for Program {
75    fn drop(&mut self) {
76        self.context.detach_shader(&self, &self.vertex);
77        self.context.detach_shader(&self, &self.fragment);
78        self.context.delete_program(Some(&self.handle))
79    }
80}
81
82impl Deref for Program {
83    type Target = WebGlProgram;
84    fn deref(&self) -> &Self::Target {
85        &self.handle
86    }
87}
88
89// impl DerefMut for Program {
90//     fn deref_mut(&mut self) -> &mut Self::Target {
91//         &mut self.handle
92//     }
93// }
94
95fn link_program(
96    context: &WebGl2RenderingContext,
97    vert_shader: &WebGlShader,
98    frag_shader: &WebGlShader,
99) -> Result<WebGlProgram, String> {
100    let program = context
101        .create_program()
102        .ok_or_else(|| String::from("Unable to create shader object"))?;
103
104    context.attach_shader(&program, vert_shader);
105    context.attach_shader(&program, frag_shader);
106    context.link_program(&program);
107
108    if context
109        .get_program_parameter(&program, WebGl2RenderingContext::LINK_STATUS)
110        .as_bool()
111        .unwrap_or(false)
112    {
113        Ok(program)
114    } else {
115        Err(context
116            .get_program_info_log(&program)
117            .unwrap_or_else(|| String::from("Unknown error creating program object")))
118    }
119}
120
121impl Program {
122    pub fn set_uniform1i(&self, uniform_name: &str, value: i32) -> Result<(), String> {
123        let gl = &self.context;
124        let u_var = gl.get_uniform_location(&self, uniform_name);
125        gl.uniform1i(u_var.as_ref(), value);
126        Ok(())
127    }
128
129    pub fn get_uniform1i(&self, uniform_name: &str) -> Result<i32, String> {
130        let gl = &self.context;
131        let u_var = gl.get_uniform_location(&self, uniform_name).ok_or(format!(
132            "Cannot retrieve uniform:{} location.",
133            uniform_name
134        ))?;
135        let value: i32 = gl
136            .get_uniform(&self, u_var.as_ref())
137            .as_f64()
138            .ok_or(format!(
139                "Cannot retrieve uniform: {} as a float.",
140                uniform_name
141            ))? as i32;
142        Ok(value)
143    }
144
145    pub fn set_uniform1f(&self, uniform_name: &str, value: f32) -> Result<(), String> {
146        let gl = &self.context;
147        let u_var = gl.get_uniform_location(&self, uniform_name);
148        gl.uniform1f(u_var.as_ref(), value);
149        Ok(())
150    }
151
152    pub fn get_uniform1f(&self, uniform_name: &str) -> Result<f32, String> {
153        let gl = &self.context;
154        let u_var = gl.get_uniform_location(&self, uniform_name).ok_or(format!(
155            "Cannot retrieve uniform:{} location.",
156            uniform_name
157        ))?;
158        let value: f32 = gl
159            .get_uniform(&self, u_var.as_ref())
160            .as_f64()
161            .ok_or(format!(
162                "Cannot retrieve uniform: {} as a float.",
163                uniform_name
164            ))? as f32;
165        Ok(value)
166    }
167
168    pub fn set_uniform3f(&self, u_name: &str, v0: f32, v1: f32, v2: f32) -> Result<(), String> {
169        let gl = &self.context;
170        let u_var = gl.get_uniform_location(&self, u_name);
171        gl.uniform3f(u_var.as_ref(), v0, v1, v2);
172        Ok(())
173    }
174
175    pub fn get_uniform3f(&self, u_name: &str) -> Result<Box<[f32]>, String> {
176        let gl = &self.context;
177        let u_var = gl
178            .get_uniform_location(&self, u_name)
179            .ok_or(format!("Cannot retrieve uniform:{} location", u_name))?;
180        let v: ArrayBuffer = gl
181            .get_uniform(&self, u_var.as_ref())
182            .dyn_into()
183            .map_err(|err| format!("cannot retrieve uniform: {:?}", err))?;
184        let ret = Float32Array::new(&v).to_vec().into_boxed_slice();
185        Ok(ret)
186    }
187
188    pub fn bind_texture_uniform(
189        &self,
190        texture: &LoadedTexture,
191        u_name: &str,
192    ) -> Result<(), String> {
193        // console!("entering bind_texture_uniform: {:?} {}", &texture, u_name);
194        let gl = &self.context;
195        let texture_unit = texture.id;
196
197        texture.activate();
198        gl.bind_texture(GL2::TEXTURE_3D, Some(&texture));
199
200        let sampler = gl.get_uniform_location(self, u_name);
201        match sampler {
202            Some(_) => gl.uniform1i(sampler.as_ref(), texture_unit as i32),
203            None => {
204                let error = gl.get_error();
205                let err_str = format!(
206                    "cannot locate uniform: {}. Error number: {} with texture {}",
207                    u_name, error, texture.id
208                );
209                console!("{}", err_str);
210                return Err(err_str);
211            }
212        }
213
214        // console!("bind texture {} with program uniform {}", texture.id, u_name);
215
216        Ok(())
217    }
218}