use core::{ffi::CStr, num::NonZeroU32};
use bitflags::bitflags;
#[cfg(feature = "alloc")]
use alloc::string::String;
pub mod ffi;
#[derive(Copy, Clone)]
pub struct Shader(pub NonZeroU32);
#[derive(Copy, Clone)]
pub struct Program(pub NonZeroU32);
#[derive(Copy, Clone)]
pub struct Buffer(pub NonZeroU32);
#[derive(Copy, Clone)]
pub struct VertexArray(pub NonZeroU32);
#[derive(Copy, Clone)]
pub struct Texture(pub NonZeroU32);
#[derive(Copy, Clone)]
pub struct Framebuffer(pub NonZeroU32);
#[derive(Copy, Clone)]
pub struct UniformLocation(pub i32);
#[repr(u32)]
pub enum ShaderType {
Vertex = ffi::GL_VERTEX_SHADER,
Fragment = ffi::GL_FRAGMENT_SHADER,
Geometry = ffi::GL_GEOMETRY_SHADER,
Compute = ffi::GL_COMPUTE_SHADER,
}
#[repr(u32)]
pub enum DrawMode {
Points = ffi::GL_POINTS,
Lines = ffi::GL_LINES,
LineLoop = ffi::GL_LINE_LOOP,
LineStrip = ffi::GL_LINE_STRIP,
Triangles = ffi::GL_TRIANGLES,
TriangleStrip = ffi::GL_TRIANGLE_STRIP,
TriangleFan = ffi::GL_TRIANGLE_FAN,
LinesAdjacency = ffi::GL_LINES_ADJACENCY,
LineStripAdjacency = ffi::GL_LINE_STRIP_ADJACENCY,
TrianglesAdjacency = ffi::GL_TRIANGLES_ADJACENCY,
TriangleStripAdjacency = ffi::GL_TRIANGLE_STRIP_ADJACENCY,
Patches = ffi::GL_PATCHES,
}
#[repr(u32)]
pub enum BufferUsage {
StreamDraw = ffi::GL_STREAM_DRAW,
StreamRead = ffi::GL_STREAM_READ,
StreamCopy = ffi::GL_STREAM_COPY,
StaticDraw = ffi::GL_STATIC_DRAW,
StaticRead = ffi::GL_STATIC_READ,
StaticCopy = ffi::GL_STATIC_COPY,
DynamicDraw = ffi::GL_DYNAMIC_DRAW,
DynamicRead = ffi::GL_DYNAMIC_READ,
DynamicCopy = ffi::GL_DYNAMIC_COPY,
}
#[derive(Copy, Clone)]
#[repr(u32)]
pub enum VertexAttribDataType {
Byte = ffi::GL_BYTE,
Short = ffi::GL_SHORT,
Int = ffi::GL_INT,
Fixed = ffi::GL_FIXED,
Float = ffi::GL_FLOAT,
HalfFloat = ffi::GL_HALF_FLOAT,
Double = ffi::GL_DOUBLE,
UnsignedByte = ffi::GL_UNSIGNED_BYTE,
UnsignedShort = ffi::GL_UNSIGNED_SHORT,
UnsignedInt = ffi::GL_UNSIGNED_INT,
}
#[repr(u32)]
pub enum TextureTarget {
Texture1D = ffi::GL_TEXTURE_1D,
Texture2D = ffi::GL_TEXTURE_2D,
Texture3D = ffi::GL_TEXTURE_3D,
Texture1DArray = ffi::GL_TEXTURE_1D_ARRAY,
Texture2DArray = ffi::GL_TEXTURE_2D_ARRAY,
TextureRectangle = ffi::GL_TEXTURE_RECTANGLE,
TextureCubeMap = ffi::GL_TEXTURE_CUBE_MAP,
TextureCubeMapArray = ffi::GL_TEXTURE_CUBE_MAP_ARRAY,
TextureBuffer = ffi::GL_TEXTURE_BUFFER,
Texture2DMultisample = ffi::GL_TEXTURE_2D_MULTISAMPLE,
Texture2DMultisampleArray = ffi::GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
}
#[derive(Copy, Clone)]
#[repr(u32)]
pub enum TextureSizedInternalFormat {
Rgba8 = ffi::GL_RGBA8,
Depth24Stencil8 = ffi::GL_DEPTH24_STENCIL8,
}
#[derive(Copy, Clone)]
pub enum FramebufferAttachment {
ColorAttachment(u8),
DepthAttachment,
StencilAttachment,
DepthStencilAttachment,
}
impl FramebufferAttachment {
const fn to_u32(self) -> u32 {
match self {
FramebufferAttachment::ColorAttachment(i) => ffi::GL_COLOR_ATTACHMENT0 + i as u32,
FramebufferAttachment::DepthAttachment => ffi::GL_DEPTH_ATTACHMENT,
FramebufferAttachment::StencilAttachment => ffi::GL_STENCIL_ATTACHMENT,
FramebufferAttachment::DepthStencilAttachment => ffi::GL_DEPTH_STENCIL_ATTACHMENT,
}
}
}
bitflags! {
#[derive(Copy, Clone)]
pub struct ClearMask: u32 {
const COLOR_BUFFER = ffi::GL_COLOR_BUFFER_BIT;
const DEPTH_BUFFER = ffi::GL_DEPTH_BUFFER_BIT;
const STENCIL_BUFFER = ffi::GL_STENCIL_BUFFER_BIT;
}
}
pub unsafe fn create_shader(ty: ShaderType) -> Option<Shader> {
NonZeroU32::new(ffi::glCreateShader(ty as _)).map(Shader)
}
pub unsafe fn delete_shader(shader: Shader) {
ffi::glDeleteShader(shader.0.get());
}
pub unsafe fn shader_source(shader: Shader, source: &str) {
let ptr = source.as_ptr() as *const ffi::GLchar;
let length = source.len() as i32;
ffi::glShaderSource(shader.0.get(), 1, &ptr, &length);
}
pub unsafe fn compile_shader(shader: Shader) {
ffi::glCompileShader(shader.0.get());
}
pub unsafe fn get_shader_compile_status(shader: Shader) -> bool {
let mut status = 0;
ffi::glGetShaderiv(shader.0.get(), ffi::GL_COMPILE_STATUS, &mut status);
status == 1
}
#[cfg(feature = "alloc")]
pub unsafe fn get_shader_info_log(shader: Shader) -> String {
let mut length = 0;
ffi::glGetShaderiv(shader.0.get(), ffi::GL_INFO_LOG_LENGTH, &mut length);
if length > 0 {
let mut log = String::with_capacity(length as _);
ffi::glGetShaderInfoLog(shader.0.get(), length, &mut length, log.as_mut_ptr() as _);
log.truncate(length as _);
log
} else {
String::new()
}
}
pub unsafe fn create_program() -> Option<Program> {
NonZeroU32::new(ffi::glCreateProgram()).map(Program)
}
pub unsafe fn attach_shader(program: Program, shader: Shader) {
ffi::glAttachShader(program.0.get(), shader.0.get());
}
pub unsafe fn link_program(program: Program) {
ffi::glLinkProgram(program.0.get());
}
pub unsafe fn use_program(program: Option<Program>) {
let raw = program.map(|p| p.0.get()).unwrap_or(0);
ffi::glUseProgram(raw);
}
pub unsafe fn get_program_link_status(program: Program) -> bool {
let mut status = 0;
ffi::glGetProgramiv(program.0.get(), ffi::GL_LINK_STATUS, &mut status);
status == 1
}
pub unsafe fn delete_program(program: Program) {
ffi::glDeleteProgram(program.0.get());
}
#[cfg(feature = "alloc")]
pub unsafe fn get_program_info_log(program: Program) -> String {
let mut length = 0;
ffi::glGetProgramiv(program.0.get(), ffi::GL_INFO_LOG_LENGTH, &mut length);
if length > 0 {
let mut log = String::with_capacity(length as _);
ffi::glGetProgramInfoLog(program.0.get(), length, &mut length, log.as_mut_ptr() as _);
log.truncate(length as _);
log
} else {
String::new()
}
}
pub unsafe fn draw_arrays(mode: DrawMode, first: i32, count: i32) {
ffi::glDrawArrays(mode as _, first, count);
}
pub unsafe fn create_named_buffer() -> Option<Buffer> {
let mut buffer = 0;
ffi::glCreateBuffers(1, &mut buffer);
NonZeroU32::new(buffer).map(Buffer)
}
pub unsafe fn named_buffer_data_size(buffer: Buffer, size: i32, usage: BufferUsage) {
ffi::glNamedBufferData(buffer.0.get(), size as _, core::ptr::null(), usage as _);
}
pub unsafe fn named_buffer_data_u8_slice(buffer: Buffer, data: &[u8], usage: BufferUsage) {
ffi::glNamedBufferData(buffer.0.get(), data.len() as _, data.as_ptr() as _, usage as _);
}
pub unsafe fn delete_buffer(buffer: Buffer) {
ffi::glDeleteBuffers(1, &buffer.0.get());
}
pub unsafe fn create_named_vertex_array() -> Option<VertexArray> {
let mut vertex_array = 0;
ffi::glCreateVertexArrays(1, &mut vertex_array);
NonZeroU32::new(vertex_array).map(VertexArray)
}
pub unsafe fn delete_vertex_array(vertex_array: VertexArray) {
ffi::glDeleteVertexArrays(1, &vertex_array.0.get());
}
pub unsafe fn enable_vertex_array_attrib(vertex_array: VertexArray, index: u32) {
ffi::glEnableVertexArrayAttrib(vertex_array.0.get(), index);
}
pub unsafe fn vertex_array_element_buffer(vertex_array: VertexArray, buffer: Buffer) {
ffi::glVertexArrayElementBuffer(vertex_array.0.get(), buffer.0.get());
}
pub unsafe fn vertex_array_vertex_buffer(vertex_array: VertexArray, binding_index: u32, buffer: Option<Buffer>, offset: i32, stride: i32) {
let buffer = buffer.map_or(0, |b| b.0.get());
ffi::glVertexArrayVertexBuffer(vertex_array.0.get(), binding_index, buffer, offset as _, stride);
}
pub unsafe fn vertex_array_attrib_format(vertex_array: VertexArray, index: u32, size: i32, data_type: VertexAttribDataType, normalized: bool, relative_offset: u32) {
ffi::glVertexArrayAttribFormat(vertex_array.0.get(), index, size, data_type as _, normalized as _, relative_offset as _);
}
pub unsafe fn vertex_array_attrib_binding(vertex_array: VertexArray, attrib_index: u32, binding_index: u32) {
ffi::glVertexArrayAttribBinding(vertex_array.0.get(), attrib_index, binding_index);
}
pub unsafe fn create_texture(target: TextureTarget) -> Option<Texture> {
let mut texture = 0;
ffi::glCreateTextures(target as _, 1, &mut texture);
NonZeroU32::new(texture).map(Texture)
}
pub unsafe fn delete_texture(texture: Texture) {
ffi::glDeleteTextures(1, &texture.0.get());
}
pub unsafe fn texture_storage_2d(texture: Texture, levels: i32, internal_format: TextureSizedInternalFormat, width: i32, height: i32) {
ffi::glTextureStorage2D(texture.0.get(), levels as _, internal_format as _, width as _, height as _);
}
pub unsafe fn create_framebuffer() -> Option<Framebuffer> {
let mut framebuffer = 0;
ffi::glCreateFramebuffers(1, &mut framebuffer);
NonZeroU32::new(framebuffer).map(Framebuffer)
}
pub unsafe fn delete_framebuffer(framebuffer: Framebuffer) {
ffi::glDeleteFramebuffers(1, &framebuffer.0.get());
}
pub unsafe fn named_framebuffer_texture(framebuffer: Framebuffer, attachment: FramebufferAttachment, texture: Texture, level: i32) {
ffi::glNamedFramebufferTexture(framebuffer.0.get(), attachment.to_u32(), texture.0.get(), level as _);
}
pub unsafe fn bind_vertex_array(vertex_array: VertexArray) {
ffi::glBindVertexArray(vertex_array.0.get());
}
pub unsafe fn get_uniform_location(program: Program, name: &CStr) -> UniformLocation {
let raw = ffi::glGetUniformLocation(program.0.get(), name.as_ptr() as _);
UniformLocation(raw)
}
pub unsafe fn program_uniform_1_f32(program: Program, location: UniformLocation, value: f32) {
ffi::glProgramUniform1f(program.0.get(), location.0, value);
}
pub unsafe fn clear_color(red: f32, green: f32, blue: f32, alpha: f32) {
ffi::glClearColor(red, green, blue, alpha);
}
pub unsafe fn clear(mask: ClearMask) {
ffi::glClear(mask.bits());
}
pub unsafe fn viewport(x: i32, y: i32, width: i32, height: i32) {
ffi::glViewport(x, y, width, height);
}