kiss3d/resource/
effect.rs1use std::fs::File;
2use std::io::Read;
3use std::marker::PhantomData;
4use std::mem;
5use std::path::Path;
6use std::str;
7
8use crate::context::{Context, GLintptr, Program, Shader, UniformLocation};
9use crate::resource::{GLPrimitive, GPUVec};
10use crate::verify;
11
12pub struct Effect {
14 program: Program,
15 vshader: Shader,
16 fshader: Shader,
17}
18
19impl Effect {
20 pub fn new(vshader_path: &Path, fshader_path: &Path) -> Option<Effect> {
22 let mut vshader = String::new();
23 let mut fshader = String::new();
24
25 if File::open(vshader_path)
26 .map(|mut v| v.read_to_string(&mut vshader))
27 .is_err()
28 {
29 return None;
30 }
31
32 if File::open(fshader_path)
33 .map(|mut f| f.read_to_string(&mut fshader))
34 .is_err()
35 {
36 return None;
37 }
38
39 Some(Effect::new_from_str(&vshader[..], &fshader[..]))
40 }
41
42 pub fn new_from_str(vshader: &str, fshader: &str) -> Effect {
44 let (program, vshader, fshader) = load_shader_program(vshader, fshader);
45
46 Effect {
47 program,
48 vshader,
49 fshader,
50 }
51 }
52
53 pub fn get_uniform<T: GLPrimitive>(&self, name: &str) -> Option<ShaderUniform<T>> {
55 let ctxt = Context::get();
56 let location = ctxt.get_uniform_location(&self.program, name);
57
58 if ctxt.get_error() == 0 {
59 if let Some(id) = location {
60 let data_type = PhantomData;
61 return Some(ShaderUniform { id, data_type });
62 }
63 }
64
65 None
66 }
67
68 pub fn get_attrib<T: GLPrimitive>(&self, name: &str) -> Option<ShaderAttribute<T>> {
70 let ctxt = Context::get();
71 let location = ctxt.get_attrib_location(&self.program, name);
72
73 if ctxt.get_error() == 0 && location != -1 {
74 let id = location as u32;
75 let data_type = PhantomData;
76 return Some(ShaderAttribute { id, data_type });
77 }
78
79 None
80 }
81
82 pub fn use_program(&mut self) {
84 verify!(Context::get().use_program(Some(&self.program)));
85 }
86}
87
88impl Drop for Effect {
89 fn drop(&mut self) {
90 let ctxt = Context::get();
91 if verify!(ctxt.is_program(Some(&self.program))) {
92 verify!(ctxt.delete_program(Some(&self.program)));
93 }
94 if verify!(ctxt.is_shader(Some(&self.fshader))) {
95 verify!(ctxt.delete_shader(Some(&self.fshader)));
96 }
97 if verify!(ctxt.is_shader(Some(&self.vshader))) {
98 verify!(ctxt.delete_shader(Some(&self.vshader)));
99 }
100 }
101}
102
103pub struct ShaderUniform<T> {
105 id: UniformLocation,
106 data_type: PhantomData<T>,
107}
108
109impl<T: GLPrimitive> ShaderUniform<T> {
110 pub fn upload(&mut self, value: &T) {
112 value.upload(&self.id)
113 }
114}
115
116pub struct ShaderAttribute<T> {
118 id: u32,
119 data_type: PhantomData<T>,
120}
121
122impl<T: GLPrimitive> ShaderAttribute<T> {
123 pub fn disable(&mut self) {
125 verify!(Context::get().disable_vertex_attrib_array(self.id));
126 }
127
128 pub fn enable(&mut self) {
130 verify!(Context::get().enable_vertex_attrib_array(self.id));
131 }
132
133 pub fn id(&self) -> u32 {
135 self.id
136 }
137
138 pub fn bind(&mut self, vector: &mut GPUVec<T>) {
140 vector.bind();
141
142 verify!(Context::get().vertex_attrib_pointer(
143 self.id,
144 T::size() as i32,
145 T::GLTYPE,
146 false,
147 0,
148 0
149 ));
150 }
151
152 pub fn bind_sub_buffer(&mut self, vector: &mut GPUVec<T>, strides: usize, start_index: usize) {
154 unsafe { self.bind_sub_buffer_generic(vector, strides, start_index) }
155 }
156
157 #[allow(clippy::missing_safety_doc)]
161 pub unsafe fn bind_sub_buffer_generic<T2: GLPrimitive>(
162 &mut self,
163 vector: &mut GPUVec<T2>,
164 strides: usize,
165 start_index: usize,
166 ) {
167 vector.bind();
168
169 verify!(Context::get().vertex_attrib_pointer(
170 self.id,
171 T::size() as i32,
172 T::GLTYPE,
173 false,
174 ((strides + 1) * mem::size_of::<T2>()) as i32,
175 (start_index * mem::size_of::<T2>()) as GLintptr
176 ));
177 }
178}
179
180fn load_shader_program(vertex_shader: &str, fragment_shader: &str) -> (Program, Shader, Shader) {
184 let ctxt = Context::get();
186 let vshader = verify!(ctxt
187 .create_shader(Context::VERTEX_SHADER)
188 .expect("Could not create vertex shader."));
189
190 verify!(ctxt.shader_source(&vshader, vertex_shader));
191 verify!(ctxt.compile_shader(&vshader));
192 check_shader_error(&vshader);
193
194 let fshader = verify!(ctxt
196 .create_shader(Context::FRAGMENT_SHADER)
197 .expect("Could not create fragment shader."));
198 verify!(ctxt.shader_source(&fshader, fragment_shader));
199 verify!(ctxt.compile_shader(&fshader));
200 check_shader_error(&fshader);
201
202 let program = verify!(ctxt.create_program().expect("Could not create program."));
204 verify!(ctxt.attach_shader(&program, &vshader));
205 verify!(ctxt.attach_shader(&program, &fshader));
206 verify!(ctxt.link_program(&program));
207 (program, vshader, fshader)
208}
209
210fn check_shader_error(shader: &Shader) {
214 let ctxt = Context::get();
215 let compiles = ctxt.get_shader_parameter_int(shader, Context::COMPILE_STATUS);
216
217 if compiles == Some(0) {
218 if let Some(log) = ctxt.get_shader_info_log(shader) {
219 panic!("Shader compilation failed: {}", log);
220 } else {
221 println!("Shader compilation failed.");
222 }
223 }
224}