1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
extern crate gl;

use std::ffi::c_void;

use gl::types::GLenum;
use crate::*;

#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Clone, Copy)]
pub enum BufferUsage {
    StaticRead,
    StaticCopy,
    StaticDraw,
    StreamRead,
    StreamCopy,
    StreamDraw,
    DynamicRead,
    DynamicCopy,
    #[default]
    DynamicDraw
}

impl GlEnum for BufferUsage {
    fn to_enum(&self) -> GLenum {
        match self {
            BufferUsage::StaticRead => { gl::STATIC_READ },
            BufferUsage::StaticCopy => { gl::STATIC_COPY },
            BufferUsage::StaticDraw => { gl::STATIC_DRAW },
            BufferUsage::StreamRead => { gl::STREAM_READ },
            BufferUsage::StreamCopy => { gl::STREAM_COPY },
            BufferUsage::StreamDraw => { gl::STREAM_DRAW },
            BufferUsage::DynamicRead => { gl::DYNAMIC_READ },
            BufferUsage::DynamicCopy => { gl::DYNAMIC_COPY },
            BufferUsage::DynamicDraw => { gl::DYNAMIC_DRAW }
        }
    }
}

pub trait GlBuffer: GlObject + GlBind + GlTarget {
    fn data<T: Sized>(&self, data: &Vec<T>, usage: BufferUsage) {
        let size = std::mem::size_of::<T>() * data.capacity();
        let ptr: *const c_void;
        if data.is_empty() { ptr = std::ptr::null(); }
        else { ptr = data.as_ptr() as *const c_void; }
        unsafe {
            gl::BufferData(Self::target(), size as isize, ptr, usage.to_enum());
        }
    }

    fn subdata<T: Sized>(&self, offset: isize, count: usize, data: &Vec<T>) {
        let size = std::mem::size_of::<T>() * count;
        let ptr: *const c_void;
        if data.is_empty() { ptr = std::ptr::null(); }
        else { ptr = data.as_ptr() as *const c_void; }
        unsafe {
            gl::BufferSubData(Self::target(), offset, size as isize, ptr);
        }
    }

    fn bind_read(&self) {
        unsafe { gl::BindBuffer(gl::COPY_READ_BUFFER, self.get_id()) };
    }

    fn bind_write(&self) {
        unsafe { gl::BindBuffer(gl::COPY_WRITE_BUFFER, self.get_id()) };
    }

    fn copy(&self, read_offset: isize, write_offset: isize, size: isize) {
        unsafe { gl::CopyBufferSubData(gl::COPY_READ_BUFFER, gl::COPY_WRITE_BUFFER, read_offset, write_offset, size) };
    }

    fn copy_from(&self, other: impl GlBuffer, read_offset: isize, write_offset: isize, size: isize) {
        other.bind_read();
        unsafe { gl::CopyBufferSubData(gl::COPY_READ_BUFFER, gl::COPY_WRITE_BUFFER, read_offset, write_offset, size) };
    }

    fn is_binded(&self) -> bool {
        Self::current_bind() == self.get_id()
    }
}

macro_rules! impl_buffer {
    ($Name: ident, $target: expr, $binding: expr) => {
        #[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
        pub struct $Name(u32);

        impl $Name {
            pub fn new() -> Self {
                let mut handle = 0;
                unsafe { gl::GenBuffers(1, &mut handle) };
                Self(handle)
            }
        }

        impl GlObject for $Name {
            fn get_id(&self) -> u32 { self.0 }
        }

        impl GlTarget for $Name {
            fn target() -> u32 { $target }
            fn binding() -> u32 { $binding }
        }

        impl GlBind for $Name {
            fn bind(&self) {
                unsafe { gl::BindBuffer(Self::target(), self.0) };
            }

            fn unbind(&self) {
                unsafe { gl::BindBuffer(Self::target(), 0) };
            }
        }

        impl GlBuffer for $Name {}

        impl Drop for $Name {
            fn drop(&mut self) {
                unsafe { gl::DeleteBuffers(1, &mut self.0) };
            }
        }
    }
}

impl_buffer!(ArrayBuffer, gl::ARRAY_BUFFER, gl::ARRAY_BUFFER_BINDING);
impl_buffer!(ElementArrayBuffer, gl::ELEMENT_ARRAY_BUFFER, gl::ELEMENT_ARRAY_BUFFER_BINDING);
impl_buffer!(UniformBuffer, gl::UNIFORM_BUFFER, gl::UNIFORM_BUFFER_BINDING);
impl_buffer!(TextureBuffer, gl::TEXTURE_BUFFER, gl::TEXTURE_BUFFER_BINDING);