1 module gbaid.render.gl20; 2 3 import std.stdio : writeln; 4 import std.conv : to; 5 import std.string : toStringz; 6 import std.container : RedBlackTree, make; 7 import std.variant : Variant; 8 import std.regex : ctRegex, replaceFirst; 9 import std.algorithm.comparison : min; 10 import std.algorithm.sorting : sort; 11 12 import derelict.sdl2.sdl; 13 import derelict.opengl3.gl3; 14 15 import gbaid.util; 16 17 import gbaid.render.gl; 18 19 /** 20 * An OpenGL 2.0 implementation of {@link org.spout.renderer.api.gl.Context}. 21 * 22 * @see org.spout.renderer.api.gl.Context 23 */ 24 public class GL20Context : Context { 25 private string title = ""; 26 private uint width = 100; 27 private uint height = 100; 28 private bool resizable = false; 29 private bool fullScreen = false; 30 private bool useVsync = false; 31 private SDL_Window* window; 32 private SDL_GLContext glContext; 33 34 public override void create() { 35 checkNotCreated(); 36 // Load the bindings if needed 37 if (!DerelictSDL2.isLoaded) { 38 DerelictSDL2.load(); 39 } 40 if (!DerelictGL3.isLoaded) { 41 DerelictGL3.load(); 42 } 43 // Initialize SDL video if needed 44 if (!SDL_WasInit(SDL_INIT_VIDEO)) { 45 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { 46 throw new Exception("Failed to initialize the SDL video system: " ~ toDString(SDL_GetError())); 47 } 48 } 49 // Configure the context 50 setContextAttributes(); 51 // Attempt to create the window 52 int actualWidth, actualHeight; 53 getActualWindowSize(&actualWidth, &actualHeight); 54 int flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN 55 | (resizable ? SDL_WINDOW_RESIZABLE : 0) | (fullScreen ? SDL_WINDOW_FULLSCREEN : 0); 56 window = SDL_CreateWindow(toStringz(title), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 57 actualWidth, actualHeight, flags); 58 if (!window) { 59 throw new Exception("Failed to create an SDL window: " ~ toDString(SDL_GetError())); 60 } 61 // Attempt to create the OpenGL context 62 glContext = SDL_GL_CreateContext(window); 63 if (glContext is null) { 64 throw new Exception("Failed to create OpenGL context: " ~ toDString(SDL_GetError())); 65 } 66 // Set the swap interval 67 if (SDL_GL_SetSwapInterval(useVsync ? 1 : 0) < 0) { 68 throw new Exception("Failed to enable VSYNC: " ~ toDString(SDL_GetError())); 69 } 70 // Load the GL1.1+ features if needed 71 if (DerelictGL3.loadedVersion == derelict.opengl3.types.GLVersion.GL11) { 72 DerelictGL3.reload(); 73 } 74 // Check for errors 75 checkForGLError(); 76 // Update the state 77 super.create(); 78 } 79 80 /** 81 * Created new context attributes for the version. 82 * 83 * @return The context attributes 84 */ 85 protected void setContextAttributes() { 86 if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2) < 0) { 87 throw new Exception("Failed to set OpenGL major version: " ~ toDString(SDL_GetError())); 88 } 89 if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0) < 0) { 90 throw new Exception("Failed to set OpenGL minor version: " ~ toDString(SDL_GetError())); 91 } 92 } 93 94 public override void destroy() { 95 checkCreated(); 96 // Display goes after else there's no context in which to check for an error 97 checkForGLError(); 98 SDL_GL_DeleteContext(glContext); 99 SDL_DestroyWindow(window); 100 super.destroy(); 101 } 102 103 public override FrameBuffer newFrameBuffer() { 104 return new GL20FrameBuffer(); 105 } 106 107 public override Program newProgram() { 108 return new GL20Program(); 109 } 110 111 public override RenderBuffer newRenderBuffer() { 112 return new GL20RenderBuffer(); 113 } 114 115 public override Shader newShader() { 116 return new GL20Shader(); 117 } 118 119 public override Texture newTexture() { 120 return new GL20Texture(); 121 } 122 123 public override VertexArray newVertexArray() { 124 return new GL20VertexArray(); 125 } 126 127 public override string getWindowTitle() { 128 return title; 129 } 130 131 public override void setWindowTitle(string title) { 132 this.title = title; 133 if (isCreated()) { 134 SDL_SetWindowTitle(window, toStringz(title)); 135 } 136 } 137 138 public override void setWindowSize(uint width, uint height) { 139 this.width = width; 140 this.height = height; 141 if (isCreated()) { 142 int actualWidth, actualHeight; 143 getActualWindowSize(&actualWidth, &actualHeight); 144 SDL_SetWindowSize(window, actualWidth, actualHeight); 145 } 146 } 147 148 public override void getActualWindowSize(int* width, int* height) { 149 SDL_Rect maxSize; 150 if (fullScreen) { 151 // In full screen the window size if the full display size 152 if (SDL_GetDisplayBounds(0, &maxSize) < 0) { 153 throw new Exception("Failed to get display bounds: " ~ toDString(SDL_GetError())); 154 } 155 if (width != null) { 156 *width = maxSize.w; 157 } 158 if (height != null) { 159 *height = maxSize.h; 160 } 161 } else { 162 // In window mode, we limit the size to the maximum usable area 163 if (SDL_GetDisplayUsableBounds(0, &maxSize) < 0) { 164 throw new Exception("Failed to get usable display bounds: " ~ toDString(SDL_GetError())); 165 } 166 if (width != null) { 167 *width = min(this.width, maxSize.w); 168 } 169 if (height != null) { 170 *height = min(this.height, maxSize.h); 171 } 172 } 173 } 174 175 public override void setResizable(bool resizable) { 176 this.resizable = resizable; 177 if (isCreated()) { 178 SDL_SetWindowResizable(window, resizable); 179 } 180 } 181 182 public override void setFullScreen(bool fullScreen) { 183 this.fullScreen = fullScreen; 184 if (isCreated()) { 185 // We need to read the actual size before, because full screen changes it 186 int actualWidth, actualHeight; 187 getActualWindowSize(&actualWidth, &actualHeight); 188 // Set to full screen 189 if (SDL_SetWindowFullscreen(window, fullScreen ? SDL_WINDOW_FULLSCREEN : 0) < 0) { 190 throw new Exception("Failed to set window to full screen: " ~ toDString(SDL_GetError())); 191 } 192 // Set the window size to the full screen, or restore it to the original 193 SDL_SetWindowSize(window, actualWidth, actualHeight); 194 // Update the resizable state when exiting full screen 195 if (!fullScreen) { 196 SDL_SetWindowResizable(window, resizable); 197 } 198 } 199 } 200 201 public override void getWindowSize(int* width, int* height) { 202 if (isCreated()) { 203 SDL_GetWindowSize(window, width, height); 204 } else { 205 if (width != null) { 206 *width = this.width; 207 } 208 if (height != null) { 209 *height = this.height; 210 } 211 } 212 } 213 214 public override void enableVsync(bool useVsync) { 215 this.useVsync = useVsync; 216 if (isCreated()) { 217 if (SDL_GL_SetSwapInterval(useVsync ? 1 : 0) < 0) { 218 throw new Exception("Failed to enable VSYNC: " ~ toDString(SDL_GetError())); 219 } 220 } 221 } 222 223 public override uint getWindowWidth() { 224 int w; 225 getWindowSize(&w, null); 226 return w; 227 } 228 229 public override uint getWindowHeight() { 230 int h; 231 getWindowSize(null, &h); 232 return h; 233 } 234 235 public override void updateDisplay() { 236 checkCreated(); 237 SDL_GL_SwapWindow(window); 238 } 239 240 public override void setClearColor(float red, float green, float blue, float alpha) { 241 checkCreated(); 242 glClearColor(red, green, blue, alpha); 243 // Check for errors 244 checkForGLError(); 245 } 246 247 public override void clearCurrentBuffer() { 248 checkCreated(); 249 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 250 // Check for errors 251 checkForGLError(); 252 } 253 254 public override void enableCapability(Capability capability) { 255 checkCreated(); 256 glEnable(capability.getGLConstant()); 257 // Check for errors 258 checkForGLError(); 259 } 260 261 public override void disableCapability(Capability capability) { 262 checkCreated(); 263 glDisable(capability.getGLConstant()); 264 // Check for errors 265 checkForGLError(); 266 } 267 268 public override void setDepthMask(bool enabled) { 269 checkCreated(); 270 glDepthMask(enabled); 271 // Check for errors 272 checkForGLError(); 273 } 274 275 public override void setBlendingFunctions(int bufferIndex, BlendFunction source, BlendFunction destination) { 276 checkCreated(); 277 glBlendFunc(source.getGLConstant(), destination.getGLConstant()); 278 // Check for errors 279 checkForGLError(); 280 } 281 282 public override void setViewPort(uint x, uint y, uint width, uint height) { 283 checkCreated(); 284 glViewport(x, y, width, height); 285 // Check for errors 286 checkForGLError(); 287 } 288 289 public override ubyte[] readFrame(uint x, uint y, uint width, uint height, InternalFormat format) { 290 checkCreated(); 291 // Create the image buffer 292 ubyte[] buffer = new ubyte[width * height * format.getBytes()]; 293 // Read from the front buffer 294 glReadBuffer(GL_FRONT); 295 // Use byte alignment 296 glPixelStorei(GL_PACK_ALIGNMENT, 1); 297 // Read the pixels 298 glReadPixels(x, y, width, height, format.getFormat().getGLConstant(), format.getComponentType().getGLConstant(), buffer.ptr); 299 // Check for errors 300 checkForGLError(); 301 return buffer; 302 } 303 304 public override bool isWindowCloseRequested() { 305 SDL_PumpEvents(); 306 307 SDL_Event event; 308 if (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_QUIT, SDL_QUIT) < 0) { 309 throw new Exception("Failed to peep events: " ~ toDString(SDL_GetError())); 310 } 311 312 if (event.type == SDL_QUIT) { 313 return true; 314 } 315 316 SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); 317 return false; 318 } 319 320 public gbaid.render.gl.GLVersion getGLVersion() { 321 return GL20; 322 } 323 } 324 325 /** 326 * An OpenGL 2.0 implementation of {@link FrameBuffer} using EXT. 327 * 328 * @see FrameBuffer 329 */ 330 public class GL20FrameBuffer : FrameBuffer { 331 private RedBlackTree!uint outputBuffers = make!(RedBlackTree!uint); 332 333 /** 334 * Constructs a new frame buffer for OpenGL 2.0. If no EXT extension for frame buffers is available, an exception is thrown. 335 * 336 * @throws UnsupportedOperationException If the hardware doesn't support EXT frame buffers 337 */ 338 public this() { 339 if (!EXT_framebuffer_object) { 340 throw new Exception("Frame buffers are not supported by this hardware"); 341 } 342 } 343 344 public override void create() { 345 checkNotCreated(); 346 // Generate and bind the frame buffer 347 glGenFramebuffersEXT(1, &id); 348 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); 349 // Disable input buffers 350 glReadBuffer(GL_NONE); 351 // Unbind the frame buffer 352 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 353 // Update the state 354 super.create(); 355 // Check for errors 356 checkForGLError(); 357 } 358 359 public override void destroy() { 360 checkCreated(); 361 // Delete the frame buffer 362 glDeleteFramebuffersEXT(1, &id); 363 // Clear output buffers 364 outputBuffers.clear(); 365 // Update the state 366 super.destroy(); 367 // Check for errors 368 checkForGLError(); 369 } 370 371 public override void attach(AttachmentPoint point, Texture texture) { 372 checkCreated(); 373 texture.checkCreated(); 374 // Bind the frame buffer 375 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); 376 // Attach the texture 377 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, point.getGLConstant(), GL_TEXTURE_2D, texture.getID(), 0); 378 // Add it to the color outputs if it's a color type 379 if (point.isColor()) { 380 outputBuffers.insert(point.getGLConstant()); 381 } 382 // Update the list of output buffers 383 updateOutputBuffers(); 384 // Unbind the frame buffer 385 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 386 // Check for errors 387 checkForGLError(); 388 } 389 390 public override void attach(AttachmentPoint point, RenderBuffer buffer) { 391 checkCreated(); 392 buffer.checkCreated(); 393 // Bind the frame buffer 394 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); 395 // Attach the render buffer 396 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, point.getGLConstant(), GL_RENDERBUFFER_EXT, buffer.getID()); 397 // Add it to the color outputs if it's a color type 398 if (point.isColor()) { 399 outputBuffers.insert(point.getGLConstant()); 400 } 401 // Update the list of output buffers 402 updateOutputBuffers(); 403 // Unbind the frame buffer 404 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 405 // Check for errors 406 checkForGLError(); 407 } 408 409 public override void detach(AttachmentPoint point) { 410 checkCreated(); 411 // Bind the frame buffer 412 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); 413 // Detach the render buffer or texture 414 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, point.getGLConstant(), GL_RENDERBUFFER_EXT, 0); 415 // Remove it from the color outputs if it's a color type 416 if (point.isColor()) { 417 outputBuffers.removeKey(point.getGLConstant()); 418 } 419 // Update the list of output buffers 420 updateOutputBuffers(); 421 // Unbind the frame buffer 422 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 423 // Check for errors 424 checkForGLError(); 425 } 426 427 private void updateOutputBuffers() { 428 // Set the output to the proper buffers 429 uint[] outputBuffersArray; 430 if (outputBuffers.empty) { 431 outputBuffersArray = [GL_NONE]; 432 } else { 433 // Keep track of the buffers to output 434 outputBuffersArray = new uint[outputBuffers.length]; 435 uint i = 0; 436 foreach (buffer; outputBuffers[]) { 437 outputBuffersArray[i++] = buffer; 438 } 439 // Sorting the array ensures that attachments are in order n, n + 1, n + 2... 440 // This is important! 441 sort(outputBuffersArray); 442 } 443 glDrawBuffers(cast(uint) outputBuffersArray.length, outputBuffersArray.ptr); 444 } 445 446 public override bool isComplete() { 447 checkCreated(); 448 // Bind the frame buffer 449 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); 450 // Fetch the status and compare to the complete enum value 451 bool complete = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT; 452 // Unbind the frame buffer 453 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 454 // Check for errors 455 checkForGLError(); 456 return complete; 457 } 458 459 public override void bind() { 460 checkCreated(); 461 // Bind the frame buffer 462 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); 463 // Check for errors 464 checkForGLError(); 465 } 466 467 public override void unbind() { 468 checkCreated(); 469 // Unbind the frame buffer 470 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 471 // Check for errors 472 checkForGLError(); 473 } 474 475 public gbaid.render.gl.GLVersion getGLVersion() { 476 return GL20; 477 } 478 } 479 480 /** 481 * An OpenGL 2.0 implementation of {@link Program}. 482 * 483 * @see Program 484 */ 485 public class GL20Program : Program { 486 // Represents an unset value for a uniform 487 private static immutable Object UNSET = new immutable(Object)(); 488 // Regex to remove the array notation from attribute names 489 private static auto ATTRIBUTE_ARRAY_NOTATION_PATTERN = ctRegex!("\\[\\d+\\]", "g"); 490 // Set of all shaders in this program 491 private bool[Shader] shaders; 492 // Map of the attribute names to their vao index (optional for GL30 as they can be defined in the shader instead) 493 private uint[string] attributeLayouts; 494 // Map of the texture units to their names 495 private string[uint] textureLayouts; 496 // Map of the uniform names to their locations 497 private uint[string] uniforms; 498 // Map of the uniform names to their values 499 private Variant[string] uniformValues; 500 501 public override void create() { 502 checkNotCreated(); 503 // Create program 504 id = glCreateProgram(); 505 // Update the state 506 super.create(); 507 } 508 509 public override void destroy() { 510 checkCreated(); 511 // Delete the program 512 glDeleteProgram(id); 513 // Check for errors 514 checkForGLError(); 515 // Clear the data 516 shaders = null; 517 attributeLayouts = null; 518 textureLayouts = null; 519 uniforms = null; 520 uniformValues = null; 521 // Update the state 522 super.destroy(); 523 } 524 525 public override void attachShader(Shader shader) { 526 checkCreated(); 527 // Attach the shader 528 glAttachShader(id, shader.getID()); 529 // Check for errors 530 checkForGLError(); 531 // Add the shader to the set 532 shaders[shader] = true; 533 // Add all attribute and texture layouts 534 addAll!(string, uint)(attributeLayouts, shader.getAttributeLayouts()); 535 addAll!(uint, string)(textureLayouts, shader.getTextureLayouts()); 536 } 537 538 public override void detachShader(Shader shader) { 539 checkCreated(); 540 // Attach the shader 541 glDetachShader(id, shader.getID()); 542 // Check for errors 543 checkForGLError(); 544 // Remove the shader from the set 545 shaders.remove(shader); 546 // Remove all attribute and texture layouts 547 removeAll!(string, uint)(attributeLayouts, shader.getAttributeLayouts()); 548 removeAll!(uint, string)(textureLayouts, shader.getTextureLayouts()); 549 } 550 551 public override void link() { 552 checkCreated(); 553 // Add the attribute layouts to the program state 554 foreach (layout; attributeLayouts.byKey()) { 555 // Bind the index to the name 556 glBindAttribLocation(id, attributeLayouts[layout], toStringz(layout)); 557 } 558 // Link program 559 glLinkProgram(id); 560 // Check program link status 561 int status; 562 glGetProgramiv(id, GL_LINK_STATUS, &status); 563 if (status == GL_FALSE) { 564 throw new Exception("Program could not be linked\n" ~ getInfoLog()); 565 } 566 if (DEBUG_ENABLED) { 567 // Validate program 568 glValidateProgram(id); 569 // Check program validation status 570 glGetProgramiv(id, GL_VALIDATE_STATUS, &status); 571 if (status == GL_FALSE) { 572 writeln("Program validation failed. This doesn't mean it won't work, so you maybe able to ignore it\n" ~ getInfoLog()); 573 } 574 } 575 // Load uniforms 576 uniforms = null; 577 int uniformCount; 578 glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); 579 int maxLength; 580 glGetProgramiv(id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength); 581 int length; 582 int size; 583 uint type; 584 char[] name = new char[maxLength]; 585 foreach (uint i; 0 .. uniformCount) { 586 glGetActiveUniform(id, i, maxLength, &length, &size, &type, name.ptr); 587 // Simplify array names 588 string nameString = toDString(name); 589 replaceFirst(nameString, ATTRIBUTE_ARRAY_NOTATION_PATTERN, ""); 590 uniforms[nameString] = glGetUniformLocation(id, name.ptr); 591 uniformValues[nameString] = UNSET; 592 } 593 // Check for errors 594 checkForGLError(); 595 } 596 597 private string getInfoLog() { 598 static immutable uint maxLength = 1024; 599 int length; 600 char[maxLength] log = new char[maxLength]; 601 glGetProgramInfoLog(id, cast(uint) maxLength, &length, log.ptr); 602 return gbaid.util.toDString(log); 603 } 604 605 public override void use() { 606 checkCreated(); 607 // Bind the program 608 glUseProgram(id); 609 // Check for errors 610 checkForGLError(); 611 } 612 613 public override void bindSampler(uint unit) { 614 if (unit !in textureLayouts) { 615 throw new Exception("No texture layout has been set for the unit: " ~ to!string(unit)); 616 } 617 setUniform(textureLayouts[unit], unit); 618 } 619 620 public override void setUniform(string name, bool b) { 621 checkCreated(); 622 Variant var = b; 623 if (!isDirty(name, var)) { 624 return; 625 } 626 glUniform1i(uniforms[name], b ? 1 : 0); 627 uniformValues[name] = var; 628 checkForGLError(); 629 } 630 631 public override void setUniform(string name, int i) { 632 checkCreated(); 633 Variant var = i; 634 if (!isDirty(name, var)) { 635 return; 636 } 637 glUniform1i(uniforms[name], i); 638 uniformValues[name] = var; 639 checkForGLError(); 640 } 641 642 public override void setUniform(string name, float f) { 643 checkCreated(); 644 Variant var = f; 645 if (!isDirty(name, var)) { 646 return; 647 } 648 glUniform1f(uniforms[name], f); 649 uniformValues[name] = var; 650 checkForGLError(); 651 } 652 653 public override void setUniform(string name, float[] fs) { 654 checkCreated(); 655 Variant var = fs; 656 if (!isDirty(name, var)) { 657 return; 658 } 659 glUniform1fv(uniforms[name], cast(uint) fs.length, fs.ptr); 660 uniformValues[name] = var; 661 checkForGLError(); 662 } 663 664 public override void setUniform(string name, float x, float y) { 665 checkCreated(); 666 Vector2f v; 667 v.x = x; 668 v.y = y; 669 Variant var = v; 670 if (!isDirty(name, var)) { 671 return; 672 } 673 glUniform2f(uniforms[name], x, y); 674 uniformValues[name] = var; 675 checkForGLError(); 676 } 677 678 private struct Vector2f { 679 float x, y; 680 } 681 682 public override void setUniform(string name, float x, float y, float z) { 683 checkCreated(); 684 Vector3f v; 685 v.x = x; 686 v.y = y; 687 v.z = z; 688 Variant var = v; 689 if (!isDirty(name, var)) { 690 return; 691 } 692 glUniform3f(uniforms[name], x, y, z); 693 uniformValues[name] = var; 694 checkForGLError(); 695 } 696 697 private struct Vector3f { 698 float x, y, z; 699 } 700 701 public override void setUniform(string name, float x, float y, float z, float w) { 702 checkCreated(); 703 Vector4f v; 704 v.x = x; 705 v.y = y; 706 v.z = z; 707 v.w = w; 708 Variant var = v; 709 if (!isDirty(name, var)) { 710 return; 711 } 712 glUniform4f(uniforms[name], x, y, z, w); 713 uniformValues[name] = var; 714 checkForGLError(); 715 } 716 717 private struct Vector4f { 718 float x, y, z, w; 719 } 720 721 public override void setUniform(string name, ref float[4] m) { 722 checkCreated(); 723 Variant var = m.dup; 724 if (!isDirty(name, var)) { 725 return; 726 } 727 glUniformMatrix2fv(uniforms[name], 1, false, m.ptr); 728 uniformValues[name] = var; 729 checkForGLError(); 730 } 731 732 public override void setUniform(string name, ref float[9] m) { 733 checkCreated(); 734 Variant var = m.dup; 735 if (!isDirty(name, var)) { 736 return; 737 } 738 glUniformMatrix3fv(uniforms[name], 1, false, m.ptr); 739 uniformValues[name] = var; 740 checkForGLError(); 741 } 742 743 public override void setUniform(string name, ref float[16] m) { 744 checkCreated(); 745 Variant var = m.dup; 746 if (!isDirty(name, var)) { 747 return; 748 } 749 glUniformMatrix4fv(uniforms[name], 1, false, m.ptr); 750 uniformValues[name] = var; 751 checkForGLError(); 752 } 753 754 private bool isDirty(string name, Variant newValue) { 755 return name in uniformValues && uniformValues[name] != newValue; 756 } 757 758 public override Shader[] getShaders() { 759 return shaders.keys; 760 } 761 762 public override string[] getUniformNames() { 763 return uniforms.keys; 764 } 765 766 public gbaid.render.gl.GLVersion getGLVersion() { 767 return GL20; 768 } 769 } 770 771 /** 772 * An OpenGL 2.0 implementation of {@link RenderBuffer} using EXT. 773 * 774 * @see RenderBuffer 775 */ 776 public class GL20RenderBuffer : RenderBuffer { 777 // The render buffer storage format 778 private InternalFormat format; 779 // The storage dimensions 780 private uint width = 1; 781 private uint height = 1; 782 783 /** 784 * Constructs a new render buffer for OpenGL 2.0. If no EXT extension for render buffers is available, an exception is thrown. 785 * 786 * @throws UnsupportedOperationException If the hardware doesn't support EXT render buffers. 787 */ 788 public this() { 789 if (!EXT_framebuffer_object) { 790 throw new Exception("Render buffers are not supported by this hardware"); 791 } 792 } 793 794 public override void create() { 795 checkNotCreated(); 796 // Generate the render buffer 797 glGenRenderbuffersEXT(1, &id); 798 // Update the state 799 super.create(); 800 // Check for errors 801 checkForGLError(); 802 } 803 804 public override void destroy() { 805 checkCreated(); 806 // Delete the render buffer 807 glDeleteRenderbuffersEXT(1, &id); 808 // Update state 809 super.destroy(); 810 // Check for errors 811 checkForGLError(); 812 } 813 814 public override void setStorage(InternalFormat format, uint width, uint height) { 815 checkCreated(); 816 if (format is null) { 817 throw new Exception("Format cannot be null"); 818 } 819 this.format = format; 820 this.width = width; 821 this.height = height; 822 // Bind the render buffer 823 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id); 824 // Set the storage format and size 825 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format.getGLConstant(), width, height); 826 // Unbind the render buffer 827 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); 828 // Check for errors 829 checkForGLError(); 830 } 831 832 public override InternalFormat getFormat() { 833 return format; 834 } 835 836 public override uint getWidth() { 837 return width; 838 } 839 840 public override uint getHeight() { 841 return height; 842 } 843 844 public override void bind() { 845 checkCreated(); 846 // Unbind the render buffer 847 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id); 848 // Check for errors 849 checkForGLError(); 850 } 851 852 public override void unbind() { 853 checkCreated(); 854 // Bind the render buffer 855 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); 856 // Check for errors 857 checkForGLError(); 858 } 859 860 public gbaid.render.gl.GLVersion getGLVersion() { 861 return GL20; 862 } 863 } 864 865 /** 866 * An OpenGL 2.0 implementation of {@link Shader}. 867 * 868 * @see Shader 869 */ 870 public class GL20Shader : Shader { 871 private ShaderType type; 872 // Map of the attribute names to their vao index (optional for GL30 as they can be defined in the shader instead) 873 private uint[string] attributeLayouts; 874 // Map of the texture units to their names 875 private string[uint] textureLayouts; 876 877 public override void create() { 878 checkNotCreated(); 879 // Update the state 880 super.create(); 881 } 882 883 public override void destroy() { 884 checkCreated(); 885 // Delete the shader 886 glDeleteShader(id); 887 // Clear the data 888 type = null; 889 attributeLayouts = null; 890 textureLayouts = null; 891 // Update the state 892 super.destroy(); 893 // Check for errors 894 checkForGLError(); 895 } 896 897 public override void setSource(ShaderSource source) { 898 checkCreated(); 899 if (source is null) { 900 throw new Exception("Shader source cannot be null"); 901 } 902 if (!source.isComplete()) { 903 throw new Exception("Shader source isn't complete"); 904 } 905 // If we don't have a previous shader or the type isn't the same, we need to create a new one 906 ShaderType type = source.getType(); 907 if (id == 0 || this.type != type) { 908 // Delete the old shader 909 glDeleteShader(id); 910 // Create a shader of the correct type 911 id = glCreateShader(type.getGLConstant()); 912 // Store the current type 913 this.type = type; 914 } 915 // Upload the new source 916 immutable(char)* src = toStringz(source.getSource()); 917 glShaderSource(id, 1, &src, null); 918 // Set the layouts from the source 919 attributeLayouts = source.getAttributeLayouts().dup; 920 textureLayouts = source.getTextureLayouts().dup; 921 // Check for errors 922 checkForGLError(); 923 } 924 925 public override void compile() { 926 checkCreated(); 927 // Compile the shader 928 glCompileShader(id); 929 // Get the shader compile status property, check it's false and fail if that's the case 930 int status; 931 glGetShaderiv(id, GL_COMPILE_STATUS, &status); 932 if (status == GL_FALSE) { 933 throw new Exception("OPEN GL ERROR: Could not compile shader\n" ~ getInfoLog()); 934 } 935 // Check for errors 936 checkForGLError(); 937 } 938 939 private string getInfoLog() { 940 static immutable uint maxLength = 1024; 941 int length; 942 char[maxLength] log = new char[maxLength]; 943 glGetShaderInfoLog(id, cast(uint) maxLength, &length, log.ptr); 944 return toDString(log); 945 } 946 947 public override ShaderType getType() { 948 return type; 949 } 950 951 public override uint[string] getAttributeLayouts() { 952 return attributeLayouts; 953 } 954 955 public override string[uint] getTextureLayouts() { 956 return textureLayouts; 957 } 958 959 public override void setAttributeLayout(string attribute, uint layout) { 960 attributeLayouts[attribute] = layout; 961 } 962 963 public override void setTextureLayout(uint unit, string sampler) { 964 textureLayouts[unit] = sampler; 965 } 966 967 public gbaid.render.gl.GLVersion getGLVersion() { 968 return GL20; 969 } 970 } 971 972 /** 973 * An OpenGL 2.0 implementation of {@link Texture}. 974 * 975 * @see Texture 976 */ 977 public class GL20Texture : Texture { 978 // The format 979 protected Format format = RGB; 980 protected InternalFormat internalFormat = null; 981 // The min filter, to check if we need mip maps 982 protected FilterMode minFilter = NEAREST_MIPMAP_LINEAR; 983 // Texture image dimensions 984 protected uint width = 1; 985 protected uint height = 1; 986 987 public override void create() { 988 checkNotCreated(); 989 // Generate the texture 990 glGenTextures(1, &id); 991 // Update the state 992 super.create(); 993 // Check for errors 994 checkForGLError(); 995 } 996 997 public override void destroy() { 998 checkCreated(); 999 // Delete the texture 1000 glDeleteTextures(1, &id); 1001 // Reset the data 1002 super.destroy(); 1003 // Check for errors 1004 checkForGLError(); 1005 } 1006 1007 public override void setFormat(Format format, InternalFormat internalFormat) { 1008 if (format is null) { 1009 throw new Exception("Format cannot be null"); 1010 } 1011 this.format = format; 1012 this.internalFormat = internalFormat; 1013 } 1014 1015 public override Format getFormat() { 1016 return format; 1017 } 1018 1019 public override InternalFormat getInternalFormat() { 1020 return internalFormat; 1021 } 1022 1023 public override void setAnisotropicFiltering(float value) { 1024 checkCreated(); 1025 if (value <= 0) { 1026 throw new Exception("Anisotropic filtering value must be greater than zero"); 1027 } 1028 // Bind the texture 1029 glBindTexture(GL_TEXTURE_2D, id); 1030 // Set the anisotropic filtering value 1031 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, value); 1032 // Unbind the texture 1033 glBindTexture(GL_TEXTURE_2D, 0); 1034 // Check for errors 1035 checkForGLError(); 1036 } 1037 1038 public override void setWraps(WrapMode horizontalWrap, WrapMode verticalWrap) { 1039 checkCreated(); 1040 if (horizontalWrap is null) { 1041 throw new Exception("Horizontal wrap cannot be null"); 1042 } 1043 if (verticalWrap is null) { 1044 throw new Exception("Vertical wrap cannot be null"); 1045 } 1046 // Bind the texture 1047 glBindTexture(GL_TEXTURE_2D, id); 1048 // Set the vertical and horizontal texture wraps 1049 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, horizontalWrap.getGLConstant()); 1050 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, verticalWrap.getGLConstant()); 1051 // Unbind the texture 1052 glBindTexture(GL_TEXTURE_2D, 0); 1053 // Check for errors 1054 checkForGLError(); 1055 } 1056 1057 public override void setFilters(FilterMode minFilter, FilterMode magFilter) { 1058 checkCreated(); 1059 if (minFilter is null) { 1060 throw new Exception("Min filter cannot be null"); 1061 } 1062 if (magFilter is null) { 1063 throw new Exception("Mag filter cannot be null"); 1064 } 1065 if (magFilter.needsMipMaps()) { 1066 throw new Exception("Mag filter cannot require mipmaps"); 1067 } 1068 this.minFilter = minFilter; 1069 // Bind the texture 1070 glBindTexture(GL_TEXTURE_2D, id); 1071 // Set the min and max texture filters 1072 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter.getGLConstant()); 1073 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter.getGLConstant()); 1074 // Unbind the texture 1075 glBindTexture(GL_TEXTURE_2D, 0); 1076 // Check for errors 1077 checkForGLError(); 1078 } 1079 1080 public override void setCompareMode(CompareMode compareMode) { 1081 checkCreated(); 1082 if (compareMode is null) { 1083 throw new Exception("Compare mode cannot be null"); 1084 } 1085 // Bind the texture 1086 glBindTexture(GL_TEXTURE_2D, id); 1087 // Note: GL14.GL_COMPARE_R_TO_TEXTURE and GL30.GL_COMPARE_REF_TO_TEXTURE are the same, just a different name 1088 // No need for a different call in the GL30 implementation 1089 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); 1090 // Set the compare mode 1091 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, compareMode.getGLConstant()); 1092 // Unbind the texture 1093 glBindTexture(GL_TEXTURE_2D, 0); 1094 // Check for errors 1095 checkForGLError(); 1096 } 1097 1098 public override void setBorderColor(float red, float green, float blue, float alpha) { 1099 checkCreated(); 1100 // Bind the texture 1101 glBindTexture(GL_TEXTURE_2D, id); 1102 // Set the border color 1103 float[4] color = [red, green, blue, alpha]; 1104 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color.ptr); 1105 // Unbind the texture 1106 glBindTexture(GL_TEXTURE_2D, 0); 1107 // Check for errors 1108 checkForGLError(); 1109 } 1110 1111 public override void setImageData(ubyte[] imageData, uint width, uint height) { 1112 checkCreated(); 1113 // Use byte alignment 1114 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 1115 // Bind the texture 1116 glBindTexture(GL_TEXTURE_2D, id); 1117 // back up the old values 1118 uint oldWidth = this.width; 1119 uint oldHeight = this.height; 1120 // update the texture width and height 1121 this.width = width; 1122 this.height = height; 1123 // check if we can only upload without reallocating 1124 bool hasInternalFormat = internalFormat !is null; 1125 if (width == oldWidth && height == oldHeight) { 1126 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format.getGLConstant(), 1127 hasInternalFormat ? internalFormat.getComponentType().getGLConstant() : UNSIGNED_BYTE.getGLConstant(), imageData.ptr); 1128 } else { 1129 // reallocate and upload the texture to the GPU 1130 //if (minFilter.needsMipMaps() && imageData !is null) { 1131 // Build mipmaps if using mip mapped filters 1132 //gluBuild2DMipmaps(GL_TEXTURE_2D, hasInternalFormat ? internalFormat.getGLConstant() : format.getGLConstant(), width, height, format.getGLConstant(), 1133 // hasInternalFormat ? internalFormat.getComponentType().getGLConstant() : DataType.UNSIGNED_BYTE.getGLConstant(), imageData); 1134 //} else { 1135 // Else just make it a normal texture 1136 // Upload the image 1137 glTexImage2D(GL_TEXTURE_2D, 0, hasInternalFormat ? internalFormat.getGLConstant() : format.getGLConstant(), width, height, 0, format.getGLConstant(), 1138 hasInternalFormat ? internalFormat.getComponentType().getGLConstant() : UNSIGNED_BYTE.getGLConstant(), imageData.ptr); 1139 //} 1140 } 1141 // Unbind the texture 1142 glBindTexture(GL_TEXTURE_2D, 0); 1143 // Check for errors 1144 checkForGLError(); 1145 } 1146 1147 public override ubyte[] getImageData(InternalFormat format) { 1148 checkCreated(); 1149 // Bind the texture 1150 glBindTexture(GL_TEXTURE_2D, id); 1151 // Create the image buffer 1152 bool formatNotNull = format !is null; 1153 ubyte[] imageData = new ubyte[width * height * (formatNotNull ? format.getBytes() : this.format.getComponentCount() * UNSIGNED_BYTE.getByteSize())]; 1154 // Use byte alignment 1155 glPixelStorei(GL_PACK_ALIGNMENT, 1); 1156 // Get the image data 1157 glGetTexImage(GL_TEXTURE_2D, 0, formatNotNull ? format.getFormat().getGLConstant() : this.format.getGLConstant(), 1158 formatNotNull ? format.getComponentType().getGLConstant() : UNSIGNED_BYTE.getGLConstant(), imageData.ptr); 1159 // Unbind the texture 1160 glBindTexture(GL_TEXTURE_2D, 0); 1161 // Check for errors 1162 checkForGLError(); 1163 return imageData; 1164 } 1165 1166 public override uint getWidth() { 1167 return width; 1168 } 1169 1170 public override uint getHeight() { 1171 return height; 1172 } 1173 1174 public override void bind(int unit) { 1175 checkCreated(); 1176 if (unit != -1) { 1177 // Activate the texture unit 1178 glActiveTexture(GL_TEXTURE0 + unit); 1179 } 1180 // Bind the texture 1181 glBindTexture(GL_TEXTURE_2D, id); 1182 // Check for errors 1183 checkForGLError(); 1184 } 1185 1186 public override void unbind() { 1187 checkCreated(); 1188 // Unbind the texture 1189 glBindTexture(GL_TEXTURE_2D, 0); 1190 // Check for errors 1191 checkForGLError(); 1192 } 1193 1194 public gbaid.render.gl.GLVersion getGLVersion() { 1195 return GL20; 1196 } 1197 } 1198 1199 /** 1200 * An OpenGL 2.0 implementation of {@link VertexArray}. 1201 * <p/> 1202 * Vertex arrays will be used if the ARB or APPLE extension is supported by the hardware. Else, since core OpenGL doesn't support them until 3.0, the vertex attributes will have to be redefined on 1203 * each render call. 1204 * 1205 * @see VertexArray 1206 */ 1207 public class GL20VertexArray : VertexArray { 1208 private static immutable uint[0] EMPTY_ARRAY = []; 1209 // Buffers IDs 1210 private uint indicesBufferID = 0; 1211 private uint[] attributeBufferIDs = EMPTY_ARRAY; 1212 // Size of the attribute buffers 1213 private uint[] attributeBufferSizes = EMPTY_ARRAY; 1214 // Amount of indices to render 1215 private uint indicesCount = 0; 1216 private uint indicesDrawCount = 0; 1217 // First and last index to render 1218 private uint indicesOffset = 0; 1219 // Drawing mode 1220 private DrawingMode drawingMode = TRIANGLES; 1221 // Polygon mode 1222 private PolygonMode polygonMode = FILL; 1223 // The pointers to the functions for the available vertex extension 1224 private bool hasVertexArrayExtension; 1225 private da_glGenVertexArrays genVertexArrays; 1226 private da_glBindVertexArray bindVertexArray; 1227 private da_glDeleteVertexArrays deleteVertexArrays; 1228 // Attribute properties for when we don't have a vao extension 1229 private uint[] attributeSizes; 1230 private uint[] attributeTypes; 1231 private bool[] attributeNormalizing; 1232 1233 public this() { 1234 if (ARB_vertex_array_object) { 1235 hasVertexArrayExtension = true; 1236 genVertexArrays = glGenVertexArrays; 1237 bindVertexArray = glBindVertexArray; 1238 deleteVertexArrays = glDeleteVertexArrays; 1239 } else if (APPLE_vertex_array_object) { 1240 hasVertexArrayExtension = true; 1241 genVertexArrays = glGenVertexArraysAPPLE; 1242 bindVertexArray = glBindVertexArrayAPPLE; 1243 deleteVertexArrays = glDeleteVertexArraysAPPLE; 1244 } else { 1245 hasVertexArrayExtension = false; 1246 genVertexArrays = null; 1247 bindVertexArray = null; 1248 deleteVertexArrays = null; 1249 } 1250 } 1251 1252 public override void create() { 1253 checkNotCreated(); 1254 if (hasVertexArrayExtension) { 1255 // Generate the vao 1256 genVertexArrays(1, &id); 1257 } 1258 // Update state 1259 super.create(); 1260 // Check for errors 1261 checkForGLError(); 1262 } 1263 1264 public override void destroy() { 1265 checkCreated(); 1266 // Delete the indices buffer 1267 glDeleteBuffers(1, &indicesBufferID); 1268 // Delete the attribute buffers 1269 glDeleteBuffers(cast(uint) attributeBufferIDs.length, attributeBufferIDs.ptr); 1270 if (hasVertexArrayExtension) { 1271 // Delete the vao 1272 deleteVertexArrays(1, &id); 1273 } else { 1274 // Else delete the attribute properties 1275 attributeSizes = null; 1276 attributeTypes = null; 1277 attributeNormalizing = null; 1278 } 1279 // Reset the IDs and data 1280 indicesBufferID = 0; 1281 attributeBufferIDs = EMPTY_ARRAY.dup; 1282 attributeBufferSizes = EMPTY_ARRAY.dup; 1283 // Update the state 1284 super.destroy(); 1285 // Check for errors 1286 checkForGLError(); 1287 } 1288 1289 public override void setData(VertexData vertexData) { 1290 checkCreated(); 1291 // Generate a new indices buffer if we don't have one yet 1292 if (indicesBufferID == 0) { 1293 glGenBuffers(1, &indicesBufferID); 1294 } 1295 // Bind the indices buffer 1296 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufferID); 1297 // Get the new count of indices 1298 uint newIndicesCount = vertexData.getIndicesCount(); 1299 // If the new count is greater than or 50% smaller than the old one, we'll reallocate the memory 1300 // In the first case because we need more space, in the other to save space 1301 ubyte[] indicesBuffer = vertexData.getIndicesBuffer(); 1302 if (newIndicesCount > indicesCount || newIndicesCount <= indicesCount * 0.5) { 1303 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer.length, indicesBuffer.ptr, GL_STATIC_DRAW); 1304 } else { 1305 // Else, we replace the data with the new one, but we don't resize, so some old data might be left trailing in the buffer 1306 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indicesBuffer.length, indicesBuffer.ptr); 1307 } 1308 // Unbind the indices buffer 1309 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1310 // Update the count to the new one 1311 indicesCount = newIndicesCount; 1312 indicesDrawCount = indicesCount; 1313 // Ensure that the indices offset and count fits inside the valid part of the buffer 1314 indicesOffset = min(indicesOffset, indicesCount - 1); 1315 indicesDrawCount = indicesDrawCount - indicesOffset; 1316 // Bind the vao 1317 if (hasVertexArrayExtension) { 1318 bindVertexArray(id); 1319 } 1320 // Create a new array of attribute buffers ID of the correct size 1321 uint attributeCount = vertexData.getAttributeCount(); 1322 uint[] newAttributeBufferIDs = new uint[attributeCount]; 1323 // Copy all the old buffer IDs that will fit in the new array so we can reuse them 1324 size_t length = min(attributeBufferIDs.length, newAttributeBufferIDs.length); 1325 newAttributeBufferIDs[0 .. length] = attributeBufferIDs[0 .. length]; 1326 // Delete any buffers that we don't need (new array is smaller than the previous one) 1327 int difference = cast(uint) (attributeBufferIDs.length - newAttributeBufferIDs.length); 1328 if (difference > 0) { 1329 glDeleteBuffers(difference, attributeBufferIDs[newAttributeBufferIDs.length .. attributeBufferIDs.length].ptr); 1330 } else if (difference < 0) { 1331 glGenBuffers(-difference, newAttributeBufferIDs[attributeBufferIDs.length .. newAttributeBufferIDs.length].ptr); 1332 } 1333 // Copy the old valid attribute buffer sizes 1334 uint[] newAttributeBufferSizes = new uint[attributeCount]; 1335 length = min(attributeBufferSizes.length, newAttributeBufferSizes.length); 1336 newAttributeBufferSizes[0 .. length] = attributeBufferSizes[0 .. length]; 1337 // If we don't have a vao, we have to save the properties manually 1338 if (!hasVertexArrayExtension) { 1339 attributeSizes = new uint[attributeCount]; 1340 attributeTypes = new uint[attributeCount]; 1341 attributeNormalizing = new bool[attributeCount]; 1342 } 1343 // Upload the new vertex data 1344 foreach (uint i; 0 .. attributeCount) { 1345 VertexAttribute attribute = vertexData.getAttribute(i); 1346 ubyte[] attributeData = attribute.getData(); 1347 // Get the current buffer size 1348 uint bufferSize = newAttributeBufferSizes[i]; 1349 // Get the new buffer size 1350 uint newBufferSize = cast(uint) attributeData.length; 1351 // Bind the target buffer 1352 glBindBuffer(GL_ARRAY_BUFFER, newAttributeBufferIDs[i]); 1353 // If the new count is greater than or 50% smaller than the old one, we'll reallocate the memory 1354 if (newBufferSize > bufferSize || newBufferSize <= bufferSize * 0.5) { 1355 glBufferData(GL_ARRAY_BUFFER, attributeData.length, attributeData.ptr, GL_STATIC_DRAW); 1356 } else { 1357 // Else, we replace the data with the new one, but we don't resize, so some old data might be left trailing in the buffer 1358 glBufferSubData(GL_ARRAY_BUFFER, 0, attributeData.length, attributeData.ptr); 1359 } 1360 // Update the buffer size to the new one 1361 newAttributeBufferSizes[i] = newBufferSize; 1362 // Next, we add the pointer to the data in the vao 1363 if (hasVertexArrayExtension) { 1364 // As a float, normalized or not 1365 glVertexAttribPointer(i, attribute.getSize(), attribute.getType().getGLConstant(), attribute.getUploadMode().normalize(), 0, null); 1366 // Enable the attribute 1367 glEnableVertexAttribArray(i); 1368 } else { 1369 // Else we save the properties for rendering 1370 attributeSizes[i] = attribute.getSize(); 1371 attributeTypes[i] = attribute.getType().getGLConstant(); 1372 attributeNormalizing[i] = attribute.getUploadMode().normalize(); 1373 } 1374 } 1375 // Unbind the last vbo 1376 glBindBuffer(GL_ARRAY_BUFFER, 0); 1377 // Unbind the vao 1378 if (hasVertexArrayExtension) { 1379 bindVertexArray(0); 1380 } 1381 // Update the attribute buffer IDs to the new ones 1382 attributeBufferIDs = newAttributeBufferIDs; 1383 // Update the attribute buffer sizes to the new ones 1384 attributeBufferSizes = newAttributeBufferSizes; 1385 // Check for errors 1386 checkForGLError(); 1387 } 1388 1389 public override void setDrawingMode(DrawingMode mode) { 1390 if (mode is null) { 1391 throw new Exception("Drawing mode cannot be null"); 1392 } 1393 this.drawingMode = mode; 1394 } 1395 1396 public override void setPolygonMode(PolygonMode mode) { 1397 if (mode is null) { 1398 throw new Exception("Polygon mode cannot be null"); 1399 } 1400 polygonMode = mode; 1401 } 1402 1403 public override void setIndicesOffset(uint offset) { 1404 indicesOffset = min(offset, indicesCount - 1); 1405 indicesDrawCount = min(indicesDrawCount, indicesCount - indicesOffset); 1406 } 1407 1408 public override void setIndicesCount(uint count) { 1409 if (count < 0) { 1410 indicesDrawCount = indicesCount; 1411 } else { 1412 indicesDrawCount = count; 1413 } 1414 indicesDrawCount = min(indicesDrawCount, indicesCount - indicesOffset); 1415 } 1416 1417 public override void draw() { 1418 checkCreated(); 1419 if (hasVertexArrayExtension) { 1420 // Bind the vao 1421 bindVertexArray(id); 1422 } else { 1423 // Enable the vertex attributes 1424 foreach (uint i; 0 .. cast(uint) attributeBufferIDs.length) { 1425 // Bind the buffer 1426 glBindBuffer(GL_ARRAY_BUFFER, attributeBufferIDs[i]); 1427 // Define the attribute 1428 glVertexAttribPointer(i, attributeSizes[i], attributeTypes[i], attributeNormalizing[i], 0, null); 1429 // Enable it 1430 glEnableVertexAttribArray(i); 1431 } 1432 // Unbind the last buffer 1433 glBindBuffer(GL_ARRAY_BUFFER, 0); 1434 } 1435 // Bind the index buffer 1436 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufferID); 1437 // Set the polygon mode 1438 glPolygonMode(GL_FRONT_AND_BACK, polygonMode.getGLConstant()); 1439 // Draw all indices with the provided mode 1440 glDrawElements(drawingMode.getGLConstant(), indicesDrawCount, GL_UNSIGNED_INT, cast(void*) (indicesOffset * INT.getByteSize())); 1441 // Unbind the indices buffer 1442 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1443 // Check for errors 1444 checkForGLError(); 1445 } 1446 1447 public gbaid.render.gl.GLVersion getGLVersion() { 1448 return GL20; 1449 } 1450 }