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 }