kepler_ra/techtron/webgl/
program.rs1use 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
89fn 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 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 Ok(())
217 }
218}