|
1 use integral_geometry::Rect; |
|
2 |
|
3 use std::{ |
|
4 mem, |
|
5 slice, |
|
6 ptr, |
|
7 ffi, |
|
8 ffi::CString, |
|
9 }; |
|
10 |
|
11 #[derive(Debug)] |
|
12 pub struct Texture2D { |
|
13 pub handle: u32, |
|
14 } |
|
15 |
|
16 impl Drop for Texture2D { |
|
17 fn drop(&mut self) { |
|
18 if self.handle != 0 { |
|
19 unsafe { |
|
20 gl::DeleteTextures(1, &self.handle); |
|
21 } |
|
22 } |
|
23 } |
|
24 } |
|
25 |
|
26 impl Texture2D { |
|
27 pub fn with_data( |
|
28 data: &[u8], |
|
29 data_stride: u32, |
|
30 width: u32, |
|
31 height: u32, |
|
32 internal_format: u32, |
|
33 format: u32, |
|
34 ty: u32, |
|
35 filter: u32 |
|
36 ) -> Self { |
|
37 let mut handle = 0; |
|
38 |
|
39 unsafe { |
|
40 gl::GenTextures(1, &mut handle); |
|
41 |
|
42 gl::BindTexture(gl::TEXTURE_2D, handle); |
|
43 gl::PixelStorei(gl::UNPACK_ROW_LENGTH, data_stride as i32); |
|
44 gl::TexImage2D( |
|
45 gl::TEXTURE_2D, |
|
46 0, |
|
47 internal_format as i32, |
|
48 width as i32, |
|
49 height as i32, |
|
50 0, |
|
51 format as u32, |
|
52 ty, |
|
53 data.as_ptr() as *const _ |
|
54 ); |
|
55 |
|
56 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32); |
|
57 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32); |
|
58 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, filter as i32); |
|
59 gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, filter as i32); |
|
60 } |
|
61 |
|
62 Texture2D { |
|
63 handle |
|
64 } |
|
65 } |
|
66 |
|
67 pub fn update(&self, region: Rect, data: &[u8], data_stride: u32, format: u32, ty: u32) { |
|
68 unsafe { |
|
69 gl::BindTexture(gl::TEXTURE_2D, self.handle); |
|
70 gl::PixelStorei(gl::UNPACK_ROW_LENGTH, data_stride as i32); |
|
71 gl::TexSubImage2D( |
|
72 gl::TEXTURE_2D, |
|
73 0, // texture level |
|
74 region.left(), // texture region |
|
75 region.top(), |
|
76 region.width() as i32 - 1, |
|
77 region.height() as i32 - 1, |
|
78 format, // data format |
|
79 ty, // data type |
|
80 data.as_ptr() as *const _, // data ptr |
|
81 ); |
|
82 } |
|
83 } |
|
84 } |
|
85 |
|
86 #[derive(Debug)] |
|
87 pub struct Buffer { |
|
88 pub handle: u32, |
|
89 pub ty: u32, |
|
90 pub usage: u32, |
|
91 } |
|
92 |
|
93 impl Buffer { |
|
94 pub fn empty( |
|
95 ty: u32, |
|
96 usage: u32, |
|
97 //size: isize |
|
98 ) -> Buffer { |
|
99 let mut buffer = 0; |
|
100 |
|
101 unsafe { |
|
102 gl::GenBuffers(1, &mut buffer); |
|
103 gl::BindBuffer(ty, buffer); |
|
104 //gl::BufferData(ty, size, ptr::null_mut(), usage); |
|
105 } |
|
106 |
|
107 Buffer { |
|
108 handle: buffer, |
|
109 ty, |
|
110 usage, |
|
111 } |
|
112 } |
|
113 |
|
114 fn with_data( |
|
115 ty: u32, |
|
116 usage: u32, |
|
117 data: &[u8] |
|
118 ) -> Buffer { |
|
119 let mut buffer = 0; |
|
120 |
|
121 unsafe { |
|
122 gl::GenBuffers(1, &mut buffer); |
|
123 gl::BindBuffer(ty, buffer); |
|
124 gl::BufferData(ty, data.len() as isize, data.as_ptr() as _, usage); |
|
125 } |
|
126 |
|
127 Buffer { |
|
128 handle: buffer, |
|
129 ty, |
|
130 usage |
|
131 } |
|
132 } |
|
133 |
|
134 pub fn ty(&self) -> u32 { |
|
135 self.ty |
|
136 } |
|
137 |
|
138 pub fn handle(&self) -> u32 { |
|
139 self.handle |
|
140 } |
|
141 |
|
142 pub fn write_typed<T>(&self, data: &[T]) { |
|
143 unsafe { |
|
144 let data = slice::from_raw_parts( |
|
145 data.as_ptr() as *const u8, |
|
146 data.len() * mem::size_of::<T>(), |
|
147 ); |
|
148 |
|
149 gl::BindBuffer(self.ty, self.handle); |
|
150 gl::BufferData(self.ty, data.len() as isize, data.as_ptr() as *const _ as *const _, self.usage); |
|
151 } |
|
152 } |
|
153 |
|
154 pub fn write(&self, data: &[u8]) { |
|
155 unsafe { |
|
156 gl::BindBuffer(self.ty, self.handle); |
|
157 gl::BufferData(self.ty, data.len() as isize, data.as_ptr() as *const _ as *const _, self.usage); |
|
158 } |
|
159 } |
|
160 } |
|
161 |
|
162 impl Drop for Buffer { |
|
163 fn drop(&mut self) { |
|
164 unsafe { |
|
165 gl::DeleteBuffers(1, &self.handle); |
|
166 } |
|
167 } |
|
168 } |
|
169 |
|
170 #[derive(Debug)] |
|
171 pub enum VariableBinding<'a> { |
|
172 Attribute(&'a str, u32), |
|
173 Uniform(&'a str, u32), |
|
174 UniformBlock(&'a str, u32), |
|
175 Sampler(&'a str, u32), |
|
176 } |
|
177 |
|
178 #[derive(Debug)] |
|
179 pub struct Shader { |
|
180 pub program: u32, |
|
181 } |
|
182 |
|
183 impl Drop for Shader { |
|
184 fn drop(&mut self) { |
|
185 unsafe { |
|
186 gl::DeleteProgram(self.program); |
|
187 } |
|
188 } |
|
189 } |
|
190 |
|
191 impl Shader { |
|
192 pub fn new<'a>( |
|
193 vs: &str, |
|
194 ps: Option<&str>, |
|
195 bindings: &[VariableBinding<'a>] |
|
196 ) -> Result<Self, String> { |
|
197 unsafe fn compile_shader(ty: u32, shdr: &str) -> Result<u32, String> { |
|
198 let shader = gl::CreateShader(ty); |
|
199 let len = shdr.len() as i32; |
|
200 let shdr = shdr.as_ptr() as *const i8; |
|
201 gl::ShaderSource(shader, 1, &shdr, &len); |
|
202 gl::CompileShader(shader); |
|
203 |
|
204 let mut success = 0i32; |
|
205 gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success as _); |
|
206 |
|
207 if success == gl::FALSE as i32 { |
|
208 let mut log_size = 0i32; |
|
209 gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut log_size as _); |
|
210 |
|
211 let mut log = vec![0u8; log_size as usize]; |
|
212 gl::GetShaderInfoLog(shader, log_size, ptr::null_mut(), log.as_mut_ptr() as _); |
|
213 |
|
214 gl::DeleteShader(shader); |
|
215 Err(String::from_utf8_unchecked(log)) |
|
216 } else { |
|
217 Ok(shader) |
|
218 } |
|
219 } |
|
220 |
|
221 let vs = unsafe { compile_shader(gl::VERTEX_SHADER, vs)? }; |
|
222 let ps = if let Some(ps) = ps { |
|
223 Some(unsafe { compile_shader(gl::FRAGMENT_SHADER, ps)? }) |
|
224 } else { |
|
225 None |
|
226 }; |
|
227 |
|
228 unsafe { |
|
229 let program = gl::CreateProgram(); |
|
230 |
|
231 gl::AttachShader(program, vs); |
|
232 if let Some(ps) = ps { |
|
233 gl::AttachShader(program, ps); |
|
234 } |
|
235 |
|
236 for bind in bindings { |
|
237 match bind { |
|
238 &VariableBinding::Attribute(ref name, id) => { |
|
239 let c_str = CString::new(name.as_bytes()).unwrap(); |
|
240 gl::BindAttribLocation(program, id, c_str.to_bytes_with_nul().as_ptr() as *const _); |
|
241 }, |
|
242 _ => {} |
|
243 } |
|
244 } |
|
245 |
|
246 gl::LinkProgram(program); |
|
247 |
|
248 let mut success = 0i32; |
|
249 gl::GetProgramiv(program, gl::LINK_STATUS, &mut success); |
|
250 if success == gl::FALSE as i32 { |
|
251 let mut log_size = 0i32; |
|
252 gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut log_size as _); |
|
253 |
|
254 let mut log = vec![0u8; log_size as usize]; |
|
255 gl::GetProgramInfoLog(program, log_size, ptr::null_mut(), log.as_mut_ptr() as _); |
|
256 |
|
257 gl::DeleteProgram(program); |
|
258 return Err(String::from_utf8_unchecked(log)); |
|
259 } |
|
260 |
|
261 //gl::DetachShader(program, vs); |
|
262 if let Some(ps) = ps { |
|
263 //gl::DetachShader(program, ps); |
|
264 } |
|
265 |
|
266 gl::UseProgram(program); |
|
267 |
|
268 // after linking we setup sampler bindings as specified in the shader |
|
269 for bind in bindings { |
|
270 match bind { |
|
271 VariableBinding::Uniform(name, id) => { |
|
272 let c_str = CString::new(name.as_bytes()).unwrap(); |
|
273 let index = gl::GetUniformLocation(program, c_str.to_bytes_with_nul().as_ptr() as *const _); |
|
274 |
|
275 // TODO: impl for block? |
|
276 }, |
|
277 VariableBinding::UniformBlock(name, id) => { |
|
278 let c_str = CString::new(name.as_bytes()).unwrap(); |
|
279 let index = gl::GetUniformBlockIndex(program, c_str.to_bytes_with_nul().as_ptr() as *const _); |
|
280 |
|
281 gl::UniformBlockBinding(program, index, *id); |
|
282 } |
|
283 VariableBinding::Sampler(name, id) => { |
|
284 let c_str = CString::new(name.as_bytes()).unwrap(); |
|
285 let index = gl::GetUniformLocation(program, c_str.to_bytes_with_nul().as_ptr() as *const _); |
|
286 |
|
287 gl::Uniform1i(index, *id as i32); |
|
288 }, |
|
289 _ => {} |
|
290 } |
|
291 } |
|
292 |
|
293 Ok(Shader { |
|
294 program |
|
295 }) |
|
296 } |
|
297 } |
|
298 |
|
299 pub fn bind(&self) { |
|
300 unsafe { |
|
301 gl::UseProgram(self.program); |
|
302 } |
|
303 } |
|
304 |
|
305 pub fn set_matrix(&self, name: &str, matrix: *const f32) { |
|
306 unsafe { |
|
307 let c_str = CString::new(name).unwrap(); |
|
308 let index = gl::GetUniformLocation(self.program, c_str.to_bytes_with_nul().as_ptr() as *const _); |
|
309 |
|
310 gl::UniformMatrix4fv(index, 1, gl::FALSE, matrix); |
|
311 } |
|
312 } |
|
313 |
|
314 pub fn bind_texture_2d(&self, index: u32, texture: &Texture2D) { |
|
315 self.bind(); |
|
316 |
|
317 unsafe { |
|
318 gl::ActiveTexture(gl::TEXTURE0 + index); |
|
319 gl::BindTexture(gl::TEXTURE_2D, texture.handle); |
|
320 } |
|
321 } |
|
322 } |
|
323 |
|
324 pub enum InputFormat { |
|
325 Float(u32, bool), |
|
326 Integer(u32), |
|
327 } |
|
328 |
|
329 pub struct InputElement { |
|
330 pub shader_slot: u32, |
|
331 pub buffer_slot: u32, |
|
332 pub format: InputFormat, |
|
333 pub components: u32, |
|
334 pub stride: u32, |
|
335 pub offset: u32, |
|
336 } |
|
337 |
|
338 // TODO: |
|
339 pub struct InputLayout { |
|
340 pub elements: Vec<InputElement>, |
|
341 } |
|
342 |
|
343 pub struct LayoutGuard { |
|
344 vao: u32 |
|
345 } |
|
346 |
|
347 impl Drop for LayoutGuard { |
|
348 fn drop(&mut self) { |
|
349 unsafe { |
|
350 gl::DeleteVertexArrays(1, [self.vao].as_ptr()); |
|
351 gl::BindVertexArray(0); |
|
352 } |
|
353 } |
|
354 } |
|
355 |
|
356 impl InputLayout { |
|
357 pub fn new(elements: Vec<InputElement>) -> Self { |
|
358 InputLayout { |
|
359 elements, |
|
360 } |
|
361 } |
|
362 |
|
363 pub fn bind(&mut self, buffers: &[(u32, &Buffer)], index_buffer: Option<&Buffer>) -> LayoutGuard { |
|
364 let mut vao = 0; |
|
365 |
|
366 unsafe { |
|
367 gl::GenVertexArrays(1, &mut vao); |
|
368 gl::BindVertexArray(vao); |
|
369 } |
|
370 |
|
371 for &(slot, ref buffer) in buffers { |
|
372 unsafe { |
|
373 gl::BindBuffer(buffer.ty(), buffer.handle()); |
|
374 } |
|
375 |
|
376 for attr in self.elements.iter().filter(|a| a.buffer_slot == slot) { |
|
377 unsafe { |
|
378 gl::EnableVertexAttribArray(attr.shader_slot); |
|
379 match attr.format { |
|
380 InputFormat::Float(fmt, normalized) => { |
|
381 gl::VertexAttribPointer( |
|
382 attr.shader_slot, |
|
383 attr.components as i32, |
|
384 fmt, |
|
385 if normalized { |
|
386 gl::TRUE |
|
387 } else { |
|
388 gl::FALSE |
|
389 }, |
|
390 attr.stride as i32, |
|
391 attr.offset as *const _ |
|
392 ); |
|
393 } |
|
394 InputFormat::Integer(fmt) => { |
|
395 gl::VertexAttribIPointer( |
|
396 attr.shader_slot, |
|
397 attr.components as i32, |
|
398 fmt, |
|
399 attr.stride as i32, |
|
400 attr.offset as *const _ |
|
401 ); |
|
402 } |
|
403 } |
|
404 |
|
405 } |
|
406 } |
|
407 } |
|
408 |
|
409 if let Some(buf) = index_buffer { |
|
410 unsafe { |
|
411 gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, buf.handle()); |
|
412 } |
|
413 } |
|
414 |
|
415 LayoutGuard { |
|
416 vao |
|
417 } |
|
418 } |
|
419 } |