1 /* 2 ******************************************************************************************* 3 * Dgame (a D game framework) - Copyright (c) Randy Schütt 4 * 5 * This software is provided 'as-is', without any express or implied warranty. 6 * In no event will the authors be held liable for any damages arising from 7 * the use of this software. 8 * 9 * Permission is granted to anyone to use this software for any purpose, 10 * including commercial applications, and to alter it and redistribute it 11 * freely, subject to the following restrictions: 12 * 13 * 1. The origin of this software must not be misrepresented; you must not claim 14 * that you wrote the original software. If you use this software in a product, 15 * an acknowledgment in the product documentation would be appreciated but is 16 * not required. 17 * 18 * 2. Altered source versions must be plainly marked as such, and must not be 19 * misrepresented as being the original software. 20 * 21 * 3. This notice may not be removed or altered from any source distribution. 22 ******************************************************************************************* 23 */ 24 module Dgame.System.VertexBufferObject; 25 26 private { 27 import derelict.opengl3.gl; 28 29 import Dgame.Internal.Log; 30 import Dgame.Internal.core; 31 import Dgame.System.VertexRenderer; 32 import Dgame.Graphics.Texture; 33 import Dgame.Graphics.Shape; 34 } 35 36 public import Dgame.System.VertexRenderer : Target; 37 38 /** 39 * Usage methods 40 */ 41 final abstract class Usage { 42 public: 43 /** 44 * Stream usage 45 */ 46 enum Stream { 47 /** 48 * The contents of the data memory is determined once by the application 49 * and rarely used as the source for GL rendering command. 50 */ 51 Draw = GL_STREAM_DRAW, 52 /** 53 * The contents of the data memory is determined once for reading data 54 * and rarely queried by the application. 55 */ 56 Read = GL_STREAM_READ, 57 /** 58 * The contents of the data memory is determined once for reading data 59 * and rarely used as the source for GL rendering command. 60 */ 61 Copy = GL_STREAM_COPY, 62 } 63 64 /** 65 * Static usage. 66 */ 67 enum Static { 68 /** 69 * The contents of the data memory is determined once by the application 70 * and often used as a source for a GL rendering command. 71 */ 72 Draw = GL_STATIC_DRAW, 73 /** 74 * The contents of the data memory is determined once for reading data 75 * and often queried by the application. 76 */ 77 Read = GL_STATIC_READ, 78 /** 79 * The contents of the data memory is determined once for reading data 80 * and often used as a source for a GL rendering command. 81 */ 82 Copy = GL_STATIC_COPY, 83 } 84 85 /** 86 * Dynamic usage. 87 */ 88 enum Dynamic { 89 /** 90 * The contents of the data memory is repeatedly determined by the application 91 * and often used as a source for a GL rendering command. 92 */ 93 Draw = GL_DYNAMIC_DRAW, 94 /** 95 * The content of the data memory is repeatedly set for reading out data 96 * and frequently requested by the application. 97 */ 98 Read = GL_DYNAMIC_READ, 99 /** 100 * The contents of the data memory is set repeatedly for reading data 101 * and often used as a source for a GL rendering command. 102 */ 103 Copy = GL_DYNAMIC_COPY, 104 } 105 } 106 107 /** 108 * Buffer is a object oriented wrapper for a Vertex Buffer Object. 109 * VertexRenderer is public imported. See there for more details, like PointerTarget. 110 * 111 * Author: rschuett 112 */ 113 class VertexBufferObject { 114 /** 115 * The access type. 116 */ 117 enum Access { 118 Read = GL_READ_ONLY, /** Read only. */ 119 Write = GL_WRITE_ONLY, /** Write only. */ 120 ReadWrite = GL_READ_WRITE /** Read and write. */ 121 } 122 123 /** 124 * Declare which Buffer Type is stored. 125 */ 126 enum Type { 127 /** The currently bound buffer object stores vertex array data. */ 128 Array = GL_ARRAY_BUFFER, 129 /** The currently bound buffer object stores index values for vertex arrays. */ 130 Element = GL_ELEMENT_ARRAY_BUFFER 131 } 132 133 const Type type; 134 const Target targets; 135 const ubyte numTargets; 136 137 private: 138 GLuint[3] _vboId; 139 Target _curTarget; 140 ubyte[Target] _targetIds; 141 bool[Target] _dataAssigned; 142 143 public: 144 final: 145 /** 146 * CTor 147 */ 148 this(Target trg, Type type = Type.Array) { 149 if (trg == Target.None) 150 Log.error("Invalid PointerTarget."); 151 152 ubyte num_targets = 0; 153 if (Target.Vertex & trg) 154 this._targetIds[Target.Vertex] = num_targets++; 155 if (Target.Color & trg) 156 this._targetIds[Target.Color] = num_targets++; 157 if (Target.TexCoords & trg) 158 this._targetIds[Target.TexCoords] = num_targets++; 159 160 this.type = type; 161 this.targets = trg; 162 this.numTargets = num_targets; 163 this._curTarget = Target.None; 164 165 glGenBuffers(this.numTargets, &this._vboId[0]); 166 167 foreach (Target id, _; this._targetIds) { 168 this.bind(id); 169 170 this._dataAssigned[id] = false; 171 } 172 173 this.unbind(); 174 } 175 176 /** 177 * Binds the a specific VBO PointerTarget. 178 * If the target is invalid (because no such buffer exist) 179 * nothing happens. 180 * 181 * See: PointerTarget enum 182 */ 183 void bind(Target trg) { 184 if ((trg & this.targets) == 0) 185 return; 186 187 this._curTarget = trg; 188 189 const ubyte id = this._targetIds[trg]; 190 glBindBuffer(this.type, this._vboId[id]); 191 } 192 193 /** 194 * Unbind the current VBO. 195 */ 196 void unbind() { 197 this._curTarget = Target.None; 198 199 glBindBuffer(this.type, 0); 200 } 201 202 /** 203 * Returns the current PointerTarget 204 * 205 * See: PointerTarget enum 206 */ 207 Target getBound() const pure nothrow { 208 return this._curTarget; 209 } 210 211 /** 212 * Returns if some PointerTarget is currently bound 213 */ 214 bool isSomethingBound() const pure nothrow { 215 return this._curTarget != Target.None; 216 } 217 218 /** 219 * Checks whether the current buffer has already content, or not 220 */ 221 bool isCurrentEmpty() const pure nothrow { 222 if (!this.isSomethingBound()) 223 return false; 224 225 return this._dataAssigned[this._curTarget] == false; 226 } 227 228 /** 229 * Checks whether a specific buffer has already content, or not. 230 * If the target is invalid (because no such buffer exist) 231 * an Exception is thrown. 232 */ 233 bool isEmpty(Target trg) const { 234 if ((trg & this.targets) == 0) 235 Log.error("%s is not a valid target of this buffer.", trg); 236 237 return this._dataAssigned[trg] == false; 238 } 239 240 /** 241 * Reset the current buffer state 242 * 243 * See: isEmpty 244 */ 245 void deplete() { 246 if (!this.isSomethingBound()) 247 return; 248 249 this._dataAssigned[this._curTarget] = false; 250 } 251 252 /** 253 * Reset all buffer states 254 * 255 * See: isEmpty 256 */ 257 void depleteAll() { 258 foreach (Target id, _; this._targetIds) { 259 this.bind(id); 260 this.deplete(); 261 } 262 } 263 264 /** 265 * Stores data in the current VBO. 266 * 267 * See: glBufferData 268 */ 269 void cache(const void* ptr, size_t totalSize, uint usage = Usage.Static.Draw) { 270 this._dataAssigned[this._curTarget] = true; 271 272 glBufferData(this.type, totalSize, ptr, usage); 273 } 274 275 /** 276 * Modify existing buffer data 277 * 278 * See: glBufferSubData 279 */ 280 void modify(const void* ptr, size_t totalSize, uint offset = 0) const { 281 glBufferSubData(this.type, offset, totalSize, ptr); 282 } 283 284 /** 285 * The internal buffer memory is transferred to the memory of the client 286 * with a specific access. 287 * Before the buffer can be reused, <code>unmap</code> must be called. 288 * 289 * See: Access enum 290 * See: glMapBuffer 291 */ 292 void* map(Access access) const { 293 return glMapBuffer(this.type, access); 294 } 295 296 /** 297 * Allows other commands buffer access, in which it retrieves the memory from the client. 298 * 299 * See: map method 300 */ 301 void unmap() const { 302 glUnmapBuffer(this.type); 303 } 304 305 /** 306 * Points to the current VBO with a specific PointerTarget. 307 * 308 * See: glVertexPointer 309 * See: glColorPointer 310 * See: glTexCoordPointer 311 * See: PointerTarget enum. 312 */ 313 void pointTo(Target trg, ubyte stride = 0, ubyte offset = 0) { 314 this.bind(trg); 315 316 VertexRenderer.pointTo(trg, null, stride, offset); 317 } 318 319 /** 320 * Enable a specific client state (with glEnableClientState) 321 * like GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY 322 * with the corresponding PointerTarget. 323 */ 324 void enableState(Target trg) const { 325 VertexRenderer.enableState(trg); 326 } 327 328 /** 329 * Enable all client states 330 */ 331 void enableAllStates() const { 332 VertexRenderer.enableAllStates(); 333 } 334 335 /** 336 * Disable all client states 337 */ 338 void disableAllStates() const { 339 VertexRenderer.disableAllStates(); 340 } 341 342 /** 343 * Disable a specific client state (with glDisableClientState) 344 */ 345 void disableState(Target trg) const { 346 VertexRenderer.disableState(trg); 347 } 348 349 /** 350 * Draw shapes of the specific type from the current VBO data. 351 * It will use count vertices. 352 */ 353 void drawArrays(Shape.Type ptype, size_t count, uint start = 0) const { 354 VertexRenderer.drawArrays(ptype, count, start); 355 } 356 357 /** 358 * Draw shapes of the specific type from the current VBO data. 359 * It will use count vertices and indices for the correct index per vertex. 360 */ 361 void drawElements(Shape.Type ptype, size_t count, uint[] indices) const { 362 VertexRenderer.drawElements(ptype, count, indices); 363 } 364 365 /** 366 * Draw shapes of the specific type from the current VBO data. 367 * It will use count vertices and indices for the correct index per vertex. 368 * 369 * Note: If start or end are -1 or below, 0 and indices.length are used. 370 */ 371 void drawRangeElements(Shape.Type ptype, size_t count, uint[] indices, int start = -1, int end = -1) const { 372 VertexRenderer.drawRangeElements(ptype, count, indices, start, end); 373 } 374 375 /** 376 * Bind a texture to this Buffer. 377 * It's a shortcut for: 378 * ---- 379 * buf.pointTo(Target.TexCoords); 380 * buf.pointTo(Target.Vertex); 381 * 382 * tex.bind(); 383 * ---- 384 * 385 * Note: You should clean up with: 386 * tex.unbind(); and buf.disableAllStates(); 387 */ 388 void bindTexture(const Texture tex) { 389 this.pointTo(Target.TexCoords); 390 this.pointTo(Target.Vertex); 391 392 tex.bind(); 393 } 394 }