1 module glamour.vbo;
2 
3 private {
4     import glamour.gl : GLenum, GLint, GLsizei, GLuint, GLboolean, GLintptr,
5                         GL_FALSE, glDisableVertexAttribArray,
6                         glEnableVertexAttribArray, glVertexAttribPointer,
7                         GL_STATIC_DRAW, GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
8                         glBindBuffer, glBufferData, glBufferSubData,
9                         glGenBuffers, glDeleteBuffers;
10     import glamour.shader : Shader;
11     import glamour.util : checkgl;
12                         
13     import std.traits : isArray, isPointer;
14     import std.range : ElementType;
15 
16     debug import std.stdio : stderr;
17 }
18 
19 /// Interface every buffer implements
20 interface IBuffer {
21     void bind(); ///
22     void unbind(); ///
23     void set_data(T)(const ref T data, GLenum hint = GL_STATIC_DRAW); ///
24     void update(T)(const T data, GLintptr offset); ///
25     void update(T)(const T ptr, size_t size, GLintptr offset); /// should the interface have this overload member? set_date() have no overload version.
26 }
27 
28 /// Represents an OpenGL element buffer.
29 /// The constructor must be used to avoid segmentation faults.
30 class ElementBuffer : IBuffer {
31     /// The OpenGL buffer name.
32     GLuint buffer;
33     /// Alias this to buffer.
34     alias buffer this;
35 
36     /// Specifies the expected usage pattern of the data store.
37     GLenum hint;
38     /// Length of the passed data, note it's the length of a void[] array.
39     size_t length = 0;
40 
41     /// Initializes the buffer.
42     this()() {
43         checkgl!glGenBuffers(1, &buffer);
44     }
45 
46     /// Initualizes the buffer and sets data.
47     this(T)(const auto ref T data, GLenum hint = GL_STATIC_DRAW) if(isArray!T) {
48         checkgl!glGenBuffers(1, &buffer);
49         set_data(data, hint);
50     }
51 
52     /// ditto
53     this(T)(const T ptr, size_t size, GLenum hint = GL_STATIC_DRAW) if(isPointer!T) {
54         checkgl!glGenBuffers(1, &buffer);
55         set_data(ptr, size, hint);
56     }
57 
58     ~this() {
59         debug if(buffer != 0) stderr.writefln("OpenGL: ElementBuffer resources not released.");
60     }
61     
62     /// Deletes the buffer.
63     void remove() {
64         checkgl!glDeleteBuffers(1, &buffer);
65         buffer = 0;
66     }
67 
68     /// Binds the buffer.
69     void bind() {
70         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
71     }
72 
73     /// Unbinds the buffer.
74     void unbind() {
75         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
76     }
77 
78     /// Uploads data to the GPU.
79     void set_data(T)(const auto ref T data, GLenum hint = GL_STATIC_DRAW) if(isArray!T) {
80         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); // or bind()
81         checkgl!glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.length*(ElementType!T).sizeof, data.ptr, hint);
82         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //or unbind()
83 
84         length = data.length*(ElementType!T).sizeof;
85         this.hint = hint;
86     }
87 
88     /// ditto
89     void set_data(T)(const T ptr, size_t size, GLenum hint = GL_STATIC_DRAW) if(isPointer!T) {
90         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); // or bind()
91         checkgl!glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, ptr, hint);
92         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //or unbind()
93 
94         length = size;
95         this.hint = hint;
96     }
97 
98     /// Updates the Buffer, using glBufferSubData.
99     void update(T)(const T data, GLintptr offset) if(isArray!T) {
100         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
101         checkgl!glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, data.length*(ElementType!T).sizeof, data.ptr);
102         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
103     }
104 
105     /// ditto
106     void update(T)(const T ptr, size_t size, GLintptr offset) if(isPointer!T) {
107         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
108         checkgl!glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, size, ptr);
109         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
110     }
111 }
112 
113 
114 /// Represents an OpenGL buffer.
115 /// The constructor must be used to avoid segmentation faults.
116 class Buffer : IBuffer {
117     /// The OpenGL buffer name.
118     GLuint buffer;
119     /// Alias this to buffer.
120     alias buffer this;
121 
122     /// Specifies the expected usage pattern of the data store.
123     GLenum hint;
124     /// Length of the passed data, note it's the length of a void[] array.
125     size_t length = 0;
126 
127     // Initializes the buffer.
128     this()() {
129         checkgl!glGenBuffers(1, &buffer);
130     }
131 
132     /// Initualizes the buffer and sets data.
133     /// Params:
134     /// data = any kind of data.
135     /// type = OpenGL type of the data (e.g. GL_FLOAT)
136     /// hint = Specifies the expected usage pattern of the data store.
137     this(T)(const auto ref T data, GLenum hint = GL_STATIC_DRAW) if(isArray!T) {
138         checkgl!glGenBuffers(1, &buffer);
139         set_data(data, hint);
140     }
141 
142     /// ditto
143     this(T)(const T ptr, size_t size, GLenum hint = GL_STATIC_DRAW) if(isPointer!T) {
144         checkgl!glGenBuffers(1, &buffer);
145         set_data(ptr, size, hint);
146     }
147 
148     ~this() {
149         debug if(buffer != 0) stderr.writefln("OpenGL: Buffer resources not released.");
150     }
151     
152     /// Deletes the buffer.
153     void remove() {
154         checkgl!glDeleteBuffers(1, &buffer);
155         buffer = 0;
156     }
157 
158     /// Binds the buffer.
159     void bind() {
160         checkgl!glBindBuffer(GL_ARRAY_BUFFER, buffer);
161     }
162 
163     /// Binds the buffer and sets the vertex attrib pointer.
164     /// Params:
165     /// type = Specifies the data type of each component in the array.
166     /// size = Specifies the number of components per generic vertex attribute.
167     /// offset = Specifies a offset of the first component of the first generic vertex attribute in the array in the data store of the buffer.
168     /// stride = Specifies the byte offset between consecutive generic vertex attributes.
169     /// normalized = Specifies whether fixed-point data values should be normalized (GL_TRUE) or
170     ///                converted directly as fixed-point values (GL_FALSE = default) when they are accessed.
171     void bind(GLint attrib_location, GLenum type, GLint size, GLsizei offset,
172               GLsizei stride, GLboolean normalized=GL_FALSE) {
173         checkgl!glBindBuffer(GL_ARRAY_BUFFER, buffer);
174         checkgl!glEnableVertexAttribArray(attrib_location);
175         checkgl!glVertexAttribPointer(attrib_location, size, type, normalized, stride, cast(void *)offset);
176     }
177 
178     /// ditto
179     void bind(Shader shader, string location, GLenum type, GLint size, GLsizei offset,
180               GLsizei stride, GLboolean normalized=GL_FALSE) {
181         bind(shader.get_attrib_location(location), type, size, offset, stride, normalized);
182     }
183 
184     /// Unbinds the buffer.
185     void unbind() {
186         checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0);
187     }
188 
189     /// Uploads data to the GPU.
190     void set_data(T)(const auto ref T data, GLenum hint = GL_STATIC_DRAW) if(isArray!T) {
191         checkgl!glBindBuffer(GL_ARRAY_BUFFER, buffer); // or bind()
192         checkgl!glBufferData(GL_ARRAY_BUFFER, data.length*(ElementType!T).sizeof, data.ptr, hint);
193         checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0); //or unbind()
194 
195         length = data.length*(ElementType!T).sizeof;
196         this.hint = hint;
197     }
198 
199     /// ditto
200     void set_data(T)(const T ptr, size_t size, GLenum hint = GL_STATIC_DRAW) if(isPointer!T) {
201         checkgl!glBindBuffer(GL_ARRAY_BUFFER, buffer); // or bind()
202         checkgl!glBufferData(GL_ARRAY_BUFFER, size, ptr, hint);
203         checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0); //or unbind()
204 
205         length = size;
206         this.hint = hint;
207     }
208 
209     /// Updates the Buffer, using glBufferSubData.
210     void update(T)(const ref T data, GLintptr offset) if(isArray!T) {
211         checkgl!glBindBuffer(GL_ARRAY_BUFFER, buffer);
212         checkgl!glBufferSubData(GL_ARRAY_BUFFER, offset, data.length*(ElementType!T).sizeof, data.ptr);
213         checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0);
214     }
215 
216     /// ditto
217     void update(T)(const T ptr, size_t size, GLintptr offset) if(isPointer!T) {
218         checkgl!glBindBuffer(GL_ARRAY_BUFFER, buffer);
219         checkgl!glBufferSubData(GL_ARRAY_BUFFER, offset, size, ptr);
220         checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0);
221     }
222 }