#![allow(unsafe_code)]
use glow::HasContext as _;
use crate::check_for_gl_error;
#[derive(Debug)]
pub(crate) struct BufferInfo {
pub location: u32, pub vector_size: i32,
pub data_type: u32, pub normalized: bool,
pub stride: i32,
pub offset: i32,
}
pub(crate) struct VertexArrayObject {
vao: Option<crate::glow::VertexArray>,
vbo: glow::Buffer,
buffer_infos: Vec<BufferInfo>,
}
impl VertexArrayObject {
#[allow(clippy::needless_pass_by_value)] pub(crate) unsafe fn new(
gl: &glow::Context,
vbo: glow::Buffer,
buffer_infos: Vec<BufferInfo>,
) -> Self {
let vao = if supports_vao(gl) {
unsafe {
let vao = gl.create_vertex_array().unwrap();
check_for_gl_error!(gl, "create_vertex_array");
gl.bind_vertex_array(Some(vao));
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
for attribute in &buffer_infos {
gl.vertex_attrib_pointer_f32(
attribute.location,
attribute.vector_size,
attribute.data_type,
attribute.normalized,
attribute.stride,
attribute.offset,
);
check_for_gl_error!(gl, "vertex_attrib_pointer_f32");
gl.enable_vertex_attrib_array(attribute.location);
check_for_gl_error!(gl, "enable_vertex_attrib_array");
}
gl.bind_vertex_array(None);
Some(vao)
}
} else {
log::debug!("VAO not supported");
None
};
Self {
vao,
vbo,
buffer_infos,
}
}
pub(crate) unsafe fn bind(&self, gl: &glow::Context) {
unsafe {
if let Some(vao) = self.vao {
gl.bind_vertex_array(Some(vao));
check_for_gl_error!(gl, "bind_vertex_array");
} else {
gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vbo));
check_for_gl_error!(gl, "bind_buffer");
for attribute in &self.buffer_infos {
gl.vertex_attrib_pointer_f32(
attribute.location,
attribute.vector_size,
attribute.data_type,
attribute.normalized,
attribute.stride,
attribute.offset,
);
check_for_gl_error!(gl, "vertex_attrib_pointer_f32");
gl.enable_vertex_attrib_array(attribute.location);
check_for_gl_error!(gl, "enable_vertex_attrib_array");
}
}
}
}
pub(crate) unsafe fn unbind(&self, gl: &glow::Context) {
unsafe {
if self.vao.is_some() {
gl.bind_vertex_array(None);
} else {
gl.bind_buffer(glow::ARRAY_BUFFER, None);
for attribute in &self.buffer_infos {
gl.disable_vertex_attrib_array(attribute.location);
}
}
}
}
}
fn supports_vao(gl: &glow::Context) -> bool {
const WEBGL_PREFIX: &str = "WebGL ";
const OPENGL_ES_PREFIX: &str = "OpenGL ES ";
let version_string = unsafe { gl.get_parameter_string(glow::VERSION) };
log::debug!("GL version: {:?}.", version_string);
if let Some(pos) = version_string.rfind(WEBGL_PREFIX) {
let version_str = &version_string[pos + WEBGL_PREFIX.len()..];
if version_str.contains("1.0") {
let supported_extensions = gl.supported_extensions();
log::debug!("Supported OpenGL extensions: {:?}", supported_extensions);
supported_extensions.contains("OES_vertex_array_object")
|| supported_extensions.contains("GL_OES_vertex_array_object")
} else {
true
}
} else if version_string.contains(OPENGL_ES_PREFIX) {
if version_string.contains("2.0") {
let supported_extensions = gl.supported_extensions();
log::debug!("Supported OpenGL extensions: {:?}", supported_extensions);
supported_extensions.contains("OES_vertex_array_object")
|| supported_extensions.contains("GL_OES_vertex_array_object")
} else {
true
}
} else {
if version_string.starts_with('2') {
let supported_extensions = gl.supported_extensions();
log::debug!("Supported OpenGL extensions: {:?}", supported_extensions);
supported_extensions.contains("ARB_vertex_array_object")
|| supported_extensions.contains("GL_ARB_vertex_array_object")
} else {
true
}
}
}