1 module gfm.opengl.vbo;
2 
3 
4 // Describing vertex, submitting geometry.
5 // Implement so called Vertex Arrays and VBO (which are Vertex Arrays + OpenGL buffers)
6 
7 import std..string;
8 
9 import derelict.opengl3.gl3,
10        derelict.opengl3.deprecatedFunctions,
11        derelict.opengl3.deprecatedConstants;
12 
13 import gfm.core.log,
14        gfm.opengl.opengl,
15        gfm.opengl.program,
16        gfm.opengl.buffer;
17 
18 /**
19  * A VertexSpecification makes it easy to use Vertex Buffer Object with interleaved attributes.
20  * It's role is similar to the OpenGL VAO one.
21  * If the user requests it, the VertexSpecification can "take control" of a given VBO and/or IBO
22  * and automatically bind/unbind them when the VertexSpecification is used/unused.
23  * You have to specify every attribute manually for now.
24  *
25  * TODO: Extract vertex specification from a struct at compile-time.
26  */
27 class VertexSpecification
28 {
29     public
30     {
31         /// Creates a vertex specification.
32         this(OpenGL gl)
33         {
34             _vbo = null;
35             _ibo = null;
36             _gl = gl;
37             _currentOffset = 0;
38             _attributes = [];
39             _state = State.UNUSED;
40         }
41 
42         ~this()
43         {
44             assert(_state == State.UNUSED);
45         }
46 
47         /// Locates all the generic attributes who were given a name
48         void locateAttributes(GLProgram program) {
49             assert(_state == State.UNUSED);
50             for (uint i = 0; i < _attributes.length; ++i)
51                 _attributes[i].recoverLocation(_gl, program);
52         }
53 
54         /// Use this vertex specification.
55         /// Re-loads all the attribute locations before actually using it
56         /// Throws: $(D OpenGLException) on error.
57         void use(GLProgram program)
58         {
59             locateAttributes(program);
60             use();
61         }
62 
63         /// Use this vertex specification.
64         /// You should only call this function after a call on
65         /// locateAttributes() or use(GLProgram).
66         /// Throws: $(D OpenGLException) on error.
67         void use()
68         {
69             assert(_state == State.UNUSED);
70             if (_vbo !is null) // if we are "in control" of this VBO, we have to bind it to current OpenGL state
71                 _vbo.bind();
72             if (_ibo !is null)  // ditto, for the ibo
73                 _ibo.bind();
74             // for every attribute
75             for (uint i = 0; i < _attributes.length; ++i)
76                 _attributes[i].use(_gl, cast(GLsizei) _currentOffset);
77             _state = State.USED;
78         }
79 
80         /// Unuse this vertex specification.
81         /// Throws: $(D OpenGLException) on error.
82         void unuse()
83         {
84             assert(_state == State.USED);
85             // Leaving a clean state after unusing an object
86             // seems the most reasonable way of doing things.
87             if (_vbo !is null) // if we are "in control" of this VBO, we have to bind it to current OpenDL state
88                 _vbo.unbind();
89             if (_ibo !is null)  // ditto, for the ibo
90                 _ibo.unbind();
91             // unuse all the attributes
92             for (uint i = 0; i < _attributes.length; ++i)
93                 _attributes[i].unuse(_gl);
94             _state = State.UNUSED;
95         }
96 
97         /// This property allows the user to set/unset the used VBO.
98         /// You can't select another VBO while this VertexSpecification is being used.
99         @property GLBuffer VBO() pure
100         {
101             return _vbo;
102         }
103 
104         /// Ditto
105         @property GLBuffer VBO(GLBuffer vbo) pure nothrow
106         {
107             assert(_state == State.UNUSED);
108             return _vbo = vbo;
109         }
110 
111         /// This property allows the user to set/unset the used IBO.
112         /// You can't select another IBO while this VertexSpecification is being used.
113         @property GLBuffer IBO() pure nothrow
114         {
115             return _ibo;
116         }
117 
118         /// Ditto
119         @property GLBuffer IBO(GLBuffer ibo) pure nothrow
120         {
121             assert(_state == State.UNUSED);
122             return _ibo = ibo;
123         }
124 
125         /// Adds an non-generic attribute to the vertex specification.
126         /// Params: role = what is the role of this attribute;
127         /// n = 1, 2, 3 or 4, is the number of components of the attribute;
128         /// For compatibility, you should not define more than 16 attributes
129         void addLegacy(VertexAttribute.Role role, GLenum glType, int n)
130         {
131             assert(role != VertexAttribute.Role.GENERIC);
132             assert(n > 0 && n <= 4);
133             assert(_state == State.UNUSED);
134             assert(isGLTypeSuitable(glType));
135             _attributes ~= VertexAttribute(role, n, _currentOffset, glType, -1, "", GL_FALSE);
136             _currentOffset += n * glTypeSize(glType);
137         }
138 
139         /// Adds a generic attribute to the vertex specification.
140         /// Params: role = what is the role of this attribute;
141         /// n = 1, 2, 3 or 4, is the number of components of the attribute;
142         /// For compatibility, you should not define more than 16 attributes
143         void addGeneric(GLenum glType, int n, GLuint location, GLboolean normalize = GL_FALSE)
144         {
145             assert(n > 0 && n <= 4);
146             assert(_state == State.UNUSED);
147             assert(isGLTypeSuitable(glType));
148             _attributes ~= VertexAttribute(VertexAttribute.Role.GENERIC, n, _currentOffset, glType, location, "", normalize);
149             _currentOffset += n * glTypeSize(glType);
150         }
151 
152         /// Adds a generic attribute to the vertex specification.
153         /// Params: role = what is the role of this attribute;
154         /// n = 1, 2, 3 or 4, is the number of components of the attribute;
155         /// For compatibility, you should not define more than 16 attributes
156         void addGeneric(GLenum glType, int n, string name, GLboolean normalize = GL_FALSE)
157         {
158             assert(n > 0 && n <= 4);
159             assert(_state == State.UNUSED);
160             assert(isGLTypeSuitable(glType));
161             _attributes ~= VertexAttribute(VertexAttribute.Role.GENERIC, n, _currentOffset, glType, -1, name, normalize);
162             _currentOffset += n * glTypeSize(glType);
163         }
164 
165         /// Adds padding space to the vertex specification.
166         /// This is useful for alignment. TODO: clarify
167         void addDummyBytes(int nBytes)
168         {
169             assert(_state == State.UNUSED);
170             _currentOffset += nBytes;
171         }
172 
173         /// Returns the size of the Vertex; this size can be computer
174         /// after you added all your attributes
175         deprecated alias getVertexSize = vertexSize;
176         size_t vertexSize() pure const nothrow
177         {
178             return _currentOffset;
179         }
180     }
181 
182     private
183     {
184         enum State
185         {
186             UNUSED,
187             USED
188         }
189         GLBuffer _vbo;      /// The Vertex BO this VertexSpecification refers to
190         GLBuffer _ibo;      /// The Index BO this VertexSpecification refers to
191         State _state;
192         OpenGL _gl;
193         VertexAttribute[] _attributes;
194         size_t _currentOffset;
195     }
196 }
197 
198 /// Describes a single attribute in a vertex entry.
199 struct VertexAttribute
200 {
201     /// Role of this vertex attribute.
202     enum Role
203     {
204         POSITION,  /// This attribute is a position.
205         COLOR,     /// This attribute is a color.
206         TEX_COORD, /// This attribute is a texture coordinate.
207         NORMAL,    /// This attribute is a normal.
208         GENERIC    /// This attribute is a generic vertex attribute
209     }
210 
211     private
212     {
213         Role role;
214         int n;
215         size_t offset;
216         GLenum glType;
217         GLint genericLocation;
218         string genericName;
219         GLboolean normalize;
220 
221         /// If needed, recover the location of this generic attribute.
222         void recoverLocation(OpenGL gl, GLProgram program) {
223             // The attributes that require recovery of location are the one
224             // called by name;
225             if (genericName.length) {
226                 genericLocation = program.attrib(genericName).location;
227                 gl.runtimeCheck();
228             }
229         }
230 
231         /// Use this attribute.
232         /// Throws: $(D OpenGLException) on error.
233         void use(OpenGL gl, GLsizei sizeOfVertex)
234         {
235             final switch (role)
236             {
237                 case Role.POSITION:
238                     glEnableClientState(GL_VERTEX_ARRAY);
239                     glVertexPointer(n, glType, sizeOfVertex, cast(GLvoid *) offset);
240                     break;
241 
242                 case Role.COLOR:
243                     glEnableClientState(GL_COLOR_ARRAY);
244                     glColorPointer(n, glType, sizeOfVertex, cast(GLvoid *) offset);
245                     break;
246 
247                 case Role.TEX_COORD:
248                     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
249                     glTexCoordPointer(n, glType, sizeOfVertex, cast(GLvoid *) offset);
250                     break;
251 
252                 case Role.NORMAL:
253                     glEnableClientState(GL_NORMAL_ARRAY);
254                     assert(n == 3);
255                     glNormalPointer(glType, sizeOfVertex, cast(GLvoid *) offset);
256                     break;
257 
258                case Role.GENERIC:
259                     glEnableVertexAttribArray(genericLocation);
260                     glVertexAttribPointer(genericLocation, n, glType, normalize, sizeOfVertex, cast(GLvoid *) offset);
261                     break;
262             }
263 
264             gl.runtimeCheck();
265         }
266 
267         /// Unuse this attribute.
268         /// Throws: $(D OpenGLException) on error.
269         void unuse(OpenGL gl)
270         {
271             final switch (role)
272             {
273                 case Role.POSITION:
274                     glDisableClientState(GL_VERTEX_ARRAY);
275                     break;
276 
277                 case Role.COLOR:
278                     glDisableClientState(GL_COLOR_ARRAY);
279                     break;
280 
281                 case Role.TEX_COORD:
282                     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
283                     break;
284 
285                 case Role.NORMAL:
286                     glDisableClientState(GL_NORMAL_ARRAY);
287                     break;
288 
289                 case Role.GENERIC:
290                     glDisableVertexAttribArray(genericLocation);
291                     break;
292             }
293             gl.runtimeCheck();
294         }
295     }
296 }
297 
298 private
299 {
300     bool isGLTypeSuitable(GLenum t) pure nothrow
301     {
302         return (t == GL_BYTE  
303              || t == GL_UNSIGNED_BYTE 
304              || t == GL_SHORT 
305              || t == GL_UNSIGNED_SHORT
306              || t == GL_INT   
307              || t == GL_UNSIGNED_INT 
308              || t == GL_HALF_FLOAT
309              || t == GL_FLOAT 
310              || t == GL_DOUBLE);
311     }
312 
313     size_t glTypeSize(GLenum t) pure nothrow
314     {
315         switch(t)
316         {
317             case GL_BYTE:
318             case GL_UNSIGNED_BYTE: return 1;
319             case GL_HALF_FLOAT:
320             case GL_SHORT:
321             case GL_UNSIGNED_SHORT: return 2;
322             case GL_INT:
323             case GL_UNSIGNED_INT:
324             case GL_FLOAT: return 4;
325             case GL_DOUBLE: return 8;
326             default: assert(false);
327         }
328     }
329 }