author | alfadur |
Sat, 22 Feb 2025 19:39:31 +0300 | |
changeset 16120 | 5febd2bc5372 |
parent 16113 | 36862a9ec59b |
permissions | -rw-r--r-- |
15306 | 1 |
use integral_geometry::{Rect, Size}; |
14726 | 2 |
|
15306 | 3 |
use std::{ffi, ffi::CString, mem, num::NonZeroU32, ptr, slice}; |
14723 | 4 |
|
14740 | 5 |
#[derive(Default)] |
6 |
pub struct PipelineState { |
|
7 |
blending: bool, |
|
8 |
} |
|
9 |
||
10 |
impl PipelineState { |
|
11 |
pub fn new() -> Self { |
|
12 |
Self::default() |
|
13 |
} |
|
14 |
||
15 |
pub fn with_blend(mut self) -> Self { |
|
16 |
unsafe { |
|
17 |
gl::Enable(gl::BLEND); |
|
18 |
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); |
|
19 |
} |
|
20 |
self.blending = true; |
|
21 |
self |
|
22 |
} |
|
23 |
} |
|
24 |
||
25 |
impl Drop for PipelineState { |
|
26 |
fn drop(&mut self) { |
|
27 |
if self.blending { |
|
28 |
unsafe { gl::Disable(gl::BLEND) } |
|
29 |
} |
|
30 |
} |
|
31 |
} |
|
32 |
||
14723 | 33 |
#[derive(Debug)] |
34 |
pub struct Texture2D { |
|
15782 | 35 |
handle: Option<NonZeroU32>, |
36 |
size: Size, |
|
14723 | 37 |
} |
38 |
||
39 |
impl Drop for Texture2D { |
|
40 |
fn drop(&mut self) { |
|
15306 | 41 |
if let Some(handle) = self.handle { |
14723 | 42 |
unsafe { |
15306 | 43 |
gl::DeleteTextures(1, &handle.get()); |
14723 | 44 |
} |
45 |
} |
|
46 |
} |
|
47 |
} |
|
48 |
||
15306 | 49 |
fn new_texture() -> Option<NonZeroU32> { |
50 |
let mut handle = 0; |
|
51 |
unsafe { |
|
52 |
gl::GenTextures(1, &mut handle); |
|
53 |
} |
|
54 |
NonZeroU32::new(handle) |
|
55 |
} |
|
56 |
||
15782 | 57 |
fn tex_params(filter: TextureFilter) { |
15306 | 58 |
unsafe { |
59 |
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32); |
|
60 |
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32); |
|
61 |
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, filter as i32); |
|
62 |
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, filter as i32); |
|
63 |
} |
|
64 |
} |
|
65 |
||
15782 | 66 |
#[derive(Clone, Copy, Debug)] |
67 |
pub enum TextureFormat { |
|
68 |
Rgba = gl::RGBA as isize, |
|
69 |
} |
|
70 |
||
71 |
#[derive(Clone, Copy, Debug)] |
|
72 |
pub enum TextureInternalFormat { |
|
73 |
Rgba8 = gl::RGBA as isize, |
|
74 |
} |
|
75 |
||
76 |
#[derive(Clone, Copy, Debug)] |
|
77 |
pub enum TextureDataType { |
|
78 |
UnsignedByte = gl::UNSIGNED_BYTE as isize, |
|
79 |
} |
|
80 |
||
81 |
#[derive(Clone, Copy, Debug)] |
|
82 |
pub enum TextureFilter { |
|
83 |
Nearest = gl::NEAREST as isize, |
|
84 |
Linear = gl::LINEAR as isize, |
|
85 |
} |
|
86 |
||
87 |
#[inline] |
|
88 |
fn get_u32(value: Option<NonZeroU32>) -> u32 { |
|
89 |
value.map_or(0, |v| v.get()) |
|
90 |
} |
|
91 |
||
92 |
fn is_out_of_bounds(data: &[u8], data_stride: Option<NonZeroU32>, texture_size: Size) -> bool { |
|
93 |
let data_stride = get_u32(data_stride); |
|
16113
36862a9ec59b
Update lib-hedgewars-engine to use newer versions of dependencies
unC0Rr
parents:
15786
diff
changeset
|
94 |
data_stride == 0 && texture_size.area() * 4 > data.len() as u32 |
15782 | 95 |
|| data_stride != 0 |
16113
36862a9ec59b
Update lib-hedgewars-engine to use newer versions of dependencies
unC0Rr
parents:
15786
diff
changeset
|
96 |
&& texture_size.width > data_stride |
36862a9ec59b
Update lib-hedgewars-engine to use newer versions of dependencies
unC0Rr
parents:
15786
diff
changeset
|
97 |
&& (texture_size.height * data_stride) * 4 > data.len() as u32 |
15782 | 98 |
} |
99 |
||
14723 | 100 |
impl Texture2D { |
15782 | 101 |
pub fn new(size: Size, internal_format: TextureInternalFormat, filter: TextureFilter) -> Self { |
15306 | 102 |
if let Some(handle) = new_texture() { |
103 |
unsafe { |
|
104 |
gl::BindTexture(gl::TEXTURE_2D, handle.get()); |
|
105 |
gl::TexImage2D( |
|
106 |
gl::TEXTURE_2D, |
|
107 |
0, |
|
108 |
internal_format as i32, |
|
109 |
size.width as i32, |
|
110 |
size.height as i32, |
|
111 |
0, |
|
15782 | 112 |
TextureFormat::Rgba as u32, |
113 |
TextureDataType::UnsignedByte as u32, |
|
15306 | 114 |
std::ptr::null(), |
115 |
) |
|
116 |
} |
|
117 |
||
118 |
tex_params(filter); |
|
119 |
Self { |
|
120 |
handle: Some(handle), |
|
15782 | 121 |
size, |
15306 | 122 |
} |
123 |
} else { |
|
15782 | 124 |
Self { handle: None, size } |
15306 | 125 |
} |
126 |
} |
|
127 |
||
14723 | 128 |
pub fn with_data( |
129 |
data: &[u8], |
|
15782 | 130 |
data_stride: Option<NonZeroU32>, |
15306 | 131 |
size: Size, |
15782 | 132 |
internal_format: TextureInternalFormat, |
133 |
format: TextureFormat, |
|
15783 | 134 |
data_type: TextureDataType, |
15782 | 135 |
filter: TextureFilter, |
14723 | 136 |
) -> Self { |
15782 | 137 |
if is_out_of_bounds(data, data_stride, size) { |
138 |
return Self { handle: None, size }; |
|
139 |
} |
|
140 |
||
15306 | 141 |
if let Some(handle) = new_texture() { |
142 |
unsafe { |
|
143 |
gl::BindTexture(gl::TEXTURE_2D, handle.get()); |
|
15782 | 144 |
gl::PixelStorei(gl::UNPACK_ROW_LENGTH, get_u32(data_stride) as i32); |
15306 | 145 |
gl::TexImage2D( |
146 |
gl::TEXTURE_2D, |
|
147 |
0, |
|
148 |
internal_format as i32, |
|
149 |
size.width as i32, |
|
150 |
size.height as i32, |
|
151 |
0, |
|
152 |
format as u32, |
|
15783 | 153 |
data_type as u32, |
15306 | 154 |
data.as_ptr() as *const _, |
155 |
) |
|
156 |
} |
|
14726 | 157 |
|
15306 | 158 |
tex_params(filter); |
159 |
Self { |
|
160 |
handle: Some(handle), |
|
15782 | 161 |
size, |
15306 | 162 |
} |
163 |
} else { |
|
15782 | 164 |
Self { handle: None, size } |
14723 | 165 |
} |
166 |
} |
|
167 |
||
15782 | 168 |
pub fn update( |
169 |
&self, |
|
170 |
region: Rect, |
|
171 |
data: &[u8], |
|
172 |
data_stride: Option<NonZeroU32>, |
|
173 |
format: TextureFormat, |
|
15783 | 174 |
data_type: TextureDataType, |
15782 | 175 |
) { |
15306 | 176 |
if let Some(handle) = self.handle { |
177 |
unsafe { |
|
178 |
gl::BindTexture(gl::TEXTURE_2D, handle.get()); |
|
15782 | 179 |
gl::PixelStorei(gl::UNPACK_ROW_LENGTH, get_u32(data_stride) as i32); |
15306 | 180 |
gl::TexSubImage2D( |
181 |
gl::TEXTURE_2D, |
|
15782 | 182 |
0, |
183 |
region.left(), |
|
15306 | 184 |
region.top(), |
15310 | 185 |
region.width() as i32, |
186 |
region.height() as i32, |
|
15782 | 187 |
format as u32, |
15783 | 188 |
data_type as u32, |
15782 | 189 |
data.as_ptr() as *const _, |
15306 | 190 |
); |
191 |
} |
|
14723 | 192 |
} |
193 |
} |
|
15310 | 194 |
|
195 |
pub fn retrieve(&self, data: &mut [u8]) { |
|
16113
36862a9ec59b
Update lib-hedgewars-engine to use newer versions of dependencies
unC0Rr
parents:
15786
diff
changeset
|
196 |
if self.size.area() * 4 > data.len() as u32 { |
15782 | 197 |
return; |
198 |
} |
|
199 |
||
15310 | 200 |
if let Some(handle) = self.handle { |
201 |
unsafe { |
|
202 |
gl::BindTexture(gl::TEXTURE_2D, handle.get()); |
|
203 |
gl::GetTexImage( |
|
204 |
gl::TEXTURE_2D, |
|
15782 | 205 |
0, |
206 |
TextureFormat::Rgba as u32, |
|
207 |
TextureDataType::UnsignedByte as u32, |
|
208 |
data.as_mut_ptr() as *mut _, |
|
15310 | 209 |
); |
210 |
} |
|
211 |
} |
|
212 |
} |
|
14723 | 213 |
} |
214 |
||
15783 | 215 |
#[derive(Clone, Copy, Debug)] |
216 |
#[repr(u32)] |
|
217 |
pub enum BufferType { |
|
218 |
Array = gl::ARRAY_BUFFER, |
|
219 |
ElementArray = gl::ELEMENT_ARRAY_BUFFER, |
|
220 |
} |
|
221 |
||
222 |
#[derive(Clone, Copy, Debug)] |
|
223 |
#[repr(u32)] |
|
224 |
pub enum BufferUsage { |
|
225 |
DynamicDraw = gl::DYNAMIC_DRAW, |
|
226 |
} |
|
227 |
||
14723 | 228 |
#[derive(Debug)] |
229 |
pub struct Buffer { |
|
15783 | 230 |
pub handle: Option<NonZeroU32>, |
231 |
pub buffer_type: BufferType, |
|
232 |
pub usage: BufferUsage, |
|
14723 | 233 |
} |
234 |
||
235 |
impl Buffer { |
|
15783 | 236 |
pub fn empty(buffer_type: BufferType, usage: BufferUsage) -> Buffer { |
14723 | 237 |
let mut buffer = 0; |
238 |
||
239 |
unsafe { |
|
240 |
gl::GenBuffers(1, &mut buffer); |
|
241 |
} |
|
242 |
||
243 |
Buffer { |
|
15783 | 244 |
handle: NonZeroU32::new(buffer), |
245 |
buffer_type: buffer_type, |
|
14723 | 246 |
usage, |
247 |
} |
|
248 |
} |
|
14726 | 249 |
|
15783 | 250 |
fn with_data(buffer_type: BufferType, usage: BufferUsage, data: &[u8]) -> Buffer { |
14723 | 251 |
let mut buffer = 0; |
252 |
||
253 |
unsafe { |
|
254 |
gl::GenBuffers(1, &mut buffer); |
|
15783 | 255 |
if buffer != 0 { |
256 |
gl::BindBuffer(buffer_type as u32, buffer); |
|
257 |
gl::BufferData( |
|
258 |
buffer_type as u32, |
|
259 |
data.len() as isize, |
|
260 |
data.as_ptr() as _, |
|
261 |
usage as u32, |
|
262 |
); |
|
263 |
} |
|
14723 | 264 |
} |
265 |
||
266 |
Buffer { |
|
15783 | 267 |
handle: NonZeroU32::new(buffer), |
268 |
buffer_type, |
|
14726 | 269 |
usage, |
14723 | 270 |
} |
271 |
} |
|
272 |
||
15783 | 273 |
pub fn ty(&self) -> BufferType { |
274 |
self.buffer_type |
|
14723 | 275 |
} |
276 |
||
15783 | 277 |
pub fn handle(&self) -> Option<NonZeroU32> { |
14723 | 278 |
self.handle |
279 |
} |
|
280 |
||
281 |
pub fn write_typed<T>(&self, data: &[T]) { |
|
15783 | 282 |
if let Some(handle) = self.handle { |
283 |
unsafe { |
|
284 |
gl::BindBuffer(self.buffer_type as u32, handle.get()); |
|
285 |
gl::BufferData( |
|
286 |
self.buffer_type as u32, |
|
287 |
(data.len() * mem::size_of::<T>()) as isize, |
|
288 |
data.as_ptr() as *const _, |
|
289 |
self.usage as u32, |
|
290 |
); |
|
291 |
} |
|
14723 | 292 |
} |
293 |
} |
|
14726 | 294 |
|
295 |
pub fn write(&self, data: &[u8]) { |
|
15783 | 296 |
if let Some(handle) = self.handle { |
297 |
unsafe { |
|
298 |
gl::BindBuffer(self.buffer_type as u32, handle.get()); |
|
299 |
gl::BufferData( |
|
300 |
self.buffer_type as u32, |
|
301 |
data.len() as isize, |
|
302 |
data.as_ptr() as *const _, |
|
303 |
self.usage as u32, |
|
304 |
); |
|
305 |
} |
|
14723 | 306 |
} |
307 |
} |
|
308 |
} |
|
309 |
||
310 |
impl Drop for Buffer { |
|
311 |
fn drop(&mut self) { |
|
15783 | 312 |
if let Some(handle) = self.handle { |
313 |
let handle = handle.get(); |
|
314 |
unsafe { |
|
315 |
gl::DeleteBuffers(1, &handle); |
|
316 |
} |
|
14723 | 317 |
} |
318 |
} |
|
319 |
} |
|
320 |
||
321 |
#[derive(Debug)] |
|
322 |
pub enum VariableBinding<'a> { |
|
323 |
Attribute(&'a str, u32), |
|
324 |
Uniform(&'a str, u32), |
|
325 |
UniformBlock(&'a str, u32), |
|
326 |
Sampler(&'a str, u32), |
|
327 |
} |
|
328 |
||
329 |
#[derive(Debug)] |
|
330 |
pub struct Shader { |
|
331 |
pub program: u32, |
|
332 |
} |
|
333 |
||
334 |
impl Drop for Shader { |
|
335 |
fn drop(&mut self) { |
|
336 |
unsafe { |
|
337 |
gl::DeleteProgram(self.program); |
|
338 |
} |
|
339 |
} |
|
340 |
} |
|
341 |
||
342 |
impl Shader { |
|
343 |
pub fn new<'a>( |
|
344 |
vs: &str, |
|
345 |
ps: Option<&str>, |
|
14726 | 346 |
bindings: &[VariableBinding<'a>], |
14723 | 347 |
) -> Result<Self, String> { |
15783 | 348 |
unsafe fn compile_shader(shader_type: u32, shader_code: &str) -> Result<u32, String> { |
349 |
let shader = gl::CreateShader(shader_type); |
|
350 |
let len = shader_code.len() as i32; |
|
351 |
let code_strings = shader_code.as_ptr() as *const i8; |
|
352 |
gl::ShaderSource(shader, 1, &code_strings, &len); |
|
14723 | 353 |
gl::CompileShader(shader); |
354 |
||
355 |
let mut success = 0i32; |
|
356 |
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success as _); |
|
357 |
||
358 |
if success == gl::FALSE as i32 { |
|
359 |
let mut log_size = 0i32; |
|
360 |
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut log_size as _); |
|
361 |
||
362 |
let mut log = vec![0u8; log_size as usize]; |
|
363 |
gl::GetShaderInfoLog(shader, log_size, ptr::null_mut(), log.as_mut_ptr() as _); |
|
364 |
||
365 |
gl::DeleteShader(shader); |
|
366 |
Err(String::from_utf8_unchecked(log)) |
|
367 |
} else { |
|
368 |
Ok(shader) |
|
369 |
} |
|
370 |
} |
|
14726 | 371 |
|
14723 | 372 |
let vs = unsafe { compile_shader(gl::VERTEX_SHADER, vs)? }; |
373 |
let ps = if let Some(ps) = ps { |
|
374 |
Some(unsafe { compile_shader(gl::FRAGMENT_SHADER, ps)? }) |
|
375 |
} else { |
|
376 |
None |
|
377 |
}; |
|
378 |
||
379 |
unsafe { |
|
380 |
let program = gl::CreateProgram(); |
|
14726 | 381 |
|
14723 | 382 |
gl::AttachShader(program, vs); |
383 |
if let Some(ps) = ps { |
|
384 |
gl::AttachShader(program, ps); |
|
385 |
} |
|
386 |
||
387 |
for bind in bindings { |
|
388 |
match bind { |
|
389 |
&VariableBinding::Attribute(ref name, id) => { |
|
390 |
let c_str = CString::new(name.as_bytes()).unwrap(); |
|
14726 | 391 |
gl::BindAttribLocation( |
392 |
program, |
|
393 |
id, |
|
394 |
c_str.to_bytes_with_nul().as_ptr() as *const _, |
|
395 |
); |
|
396 |
} |
|
14723 | 397 |
_ => {} |
398 |
} |
|
399 |
} |
|
400 |
||
401 |
gl::LinkProgram(program); |
|
402 |
||
403 |
let mut success = 0i32; |
|
404 |
gl::GetProgramiv(program, gl::LINK_STATUS, &mut success); |
|
405 |
if success == gl::FALSE as i32 { |
|
406 |
let mut log_size = 0i32; |
|
407 |
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut log_size as _); |
|
408 |
||
409 |
let mut log = vec![0u8; log_size as usize]; |
|
410 |
gl::GetProgramInfoLog(program, log_size, ptr::null_mut(), log.as_mut_ptr() as _); |
|
411 |
||
412 |
gl::DeleteProgram(program); |
|
413 |
return Err(String::from_utf8_unchecked(log)); |
|
414 |
} |
|
415 |
||
416 |
gl::UseProgram(program); |
|
417 |
||
418 |
for bind in bindings { |
|
419 |
match bind { |
|
420 |
VariableBinding::Uniform(name, id) => { |
|
421 |
let c_str = CString::new(name.as_bytes()).unwrap(); |
|
14726 | 422 |
let index = gl::GetUniformLocation( |
423 |
program, |
|
424 |
c_str.to_bytes_with_nul().as_ptr() as *const _, |
|
425 |
); |
|
14723 | 426 |
|
427 |
// TODO: impl for block? |
|
14726 | 428 |
} |
14723 | 429 |
VariableBinding::UniformBlock(name, id) => { |
430 |
let c_str = CString::new(name.as_bytes()).unwrap(); |
|
14726 | 431 |
let index = gl::GetUniformBlockIndex( |
432 |
program, |
|
433 |
c_str.to_bytes_with_nul().as_ptr() as *const _, |
|
434 |
); |
|
14723 | 435 |
|
436 |
gl::UniformBlockBinding(program, index, *id); |
|
437 |
} |
|
438 |
VariableBinding::Sampler(name, id) => { |
|
439 |
let c_str = CString::new(name.as_bytes()).unwrap(); |
|
14726 | 440 |
let index = gl::GetUniformLocation( |
441 |
program, |
|
442 |
c_str.to_bytes_with_nul().as_ptr() as *const _, |
|
443 |
); |
|
444 |
||
14723 | 445 |
gl::Uniform1i(index, *id as i32); |
14726 | 446 |
} |
14723 | 447 |
_ => {} |
448 |
} |
|
449 |
} |
|
450 |
||
14726 | 451 |
Ok(Shader { program }) |
14723 | 452 |
} |
453 |
} |
|
454 |
||
455 |
pub fn bind(&self) { |
|
456 |
unsafe { |
|
457 |
gl::UseProgram(self.program); |
|
458 |
} |
|
459 |
} |
|
460 |
||
461 |
pub fn set_matrix(&self, name: &str, matrix: *const f32) { |
|
462 |
unsafe { |
|
463 |
let c_str = CString::new(name).unwrap(); |
|
14726 | 464 |
let index = gl::GetUniformLocation( |
465 |
self.program, |
|
466 |
c_str.to_bytes_with_nul().as_ptr() as *const _, |
|
467 |
); |
|
468 |
||
14723 | 469 |
gl::UniformMatrix4fv(index, 1, gl::FALSE, matrix); |
470 |
} |
|
471 |
} |
|
472 |
||
473 |
pub fn bind_texture_2d(&self, index: u32, texture: &Texture2D) { |
|
474 |
self.bind(); |
|
14726 | 475 |
|
15306 | 476 |
if let Some(handle) = texture.handle { |
477 |
unsafe { |
|
478 |
gl::ActiveTexture(gl::TEXTURE0 + index); |
|
479 |
gl::BindTexture(gl::TEXTURE_2D, handle.get()); |
|
480 |
} |
|
14723 | 481 |
} |
482 |
} |
|
483 |
} |
|
484 |
||
485 |
pub enum InputFormat { |
|
486 |
Float(u32, bool), |
|
487 |
Integer(u32), |
|
488 |
} |
|
489 |
||
490 |
pub struct InputElement { |
|
491 |
pub shader_slot: u32, |
|
492 |
pub buffer_slot: u32, |
|
493 |
pub format: InputFormat, |
|
494 |
pub components: u32, |
|
495 |
pub stride: u32, |
|
496 |
pub offset: u32, |
|
497 |
} |
|
498 |
||
14726 | 499 |
// TODO: |
14723 | 500 |
pub struct InputLayout { |
501 |
pub elements: Vec<InputElement>, |
|
502 |
} |
|
503 |
||
504 |
pub struct LayoutGuard { |
|
14726 | 505 |
vao: u32, |
14723 | 506 |
} |
507 |
||
508 |
impl Drop for LayoutGuard { |
|
509 |
fn drop(&mut self) { |
|
510 |
unsafe { |
|
511 |
gl::DeleteVertexArrays(1, [self.vao].as_ptr()); |
|
512 |
gl::BindVertexArray(0); |
|
513 |
} |
|
514 |
} |
|
515 |
} |
|
516 |
||
517 |
impl InputLayout { |
|
518 |
pub fn new(elements: Vec<InputElement>) -> Self { |
|
14726 | 519 |
InputLayout { elements } |
14723 | 520 |
} |
521 |
||
14726 | 522 |
pub fn bind( |
523 |
&mut self, |
|
524 |
buffers: &[(u32, &Buffer)], |
|
525 |
index_buffer: Option<&Buffer>, |
|
526 |
) -> LayoutGuard { |
|
14723 | 527 |
let mut vao = 0; |
14726 | 528 |
|
14723 | 529 |
unsafe { |
530 |
gl::GenVertexArrays(1, &mut vao); |
|
531 |
gl::BindVertexArray(vao); |
|
532 |
} |
|
14726 | 533 |
|
14723 | 534 |
for &(slot, ref buffer) in buffers { |
15783 | 535 |
if let Some(handle) = buffer.handle() { |
536 |
unsafe { |
|
537 |
gl::BindBuffer(buffer.ty() as u32, handle.get()); |
|
538 |
} |
|
14723 | 539 |
} |
14726 | 540 |
|
14723 | 541 |
for attr in self.elements.iter().filter(|a| a.buffer_slot == slot) { |
542 |
unsafe { |
|
543 |
gl::EnableVertexAttribArray(attr.shader_slot); |
|
544 |
match attr.format { |
|
545 |
InputFormat::Float(fmt, normalized) => { |
|
546 |
gl::VertexAttribPointer( |
|
547 |
attr.shader_slot, |
|
548 |
attr.components as i32, |
|
549 |
fmt, |
|
14726 | 550 |
if normalized { gl::TRUE } else { gl::FALSE }, |
14723 | 551 |
attr.stride as i32, |
14726 | 552 |
attr.offset as *const _, |
14723 | 553 |
); |
554 |
} |
|
555 |
InputFormat::Integer(fmt) => { |
|
556 |
gl::VertexAttribIPointer( |
|
557 |
attr.shader_slot, |
|
558 |
attr.components as i32, |
|
559 |
fmt, |
|
560 |
attr.stride as i32, |
|
14726 | 561 |
attr.offset as *const _, |
14723 | 562 |
); |
563 |
} |
|
564 |
} |
|
565 |
} |
|
566 |
} |
|
567 |
} |
|
568 |
||
569 |
if let Some(buf) = index_buffer { |
|
15783 | 570 |
if let Some(handle) = buf.handle() { |
571 |
unsafe { |
|
572 |
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, handle.get()); |
|
573 |
} |
|
14723 | 574 |
} |
575 |
} |
|
576 |
||
14726 | 577 |
LayoutGuard { vao } |
14723 | 578 |
} |
579 |
} |