1 module canvas.backend.opengl; 2 import canvas.backend.common; 3 import canvas.color; 4 import canvas.math; 5 import bindbc.opengl; 6 7 private void[] flipImage(size_t width, size_t height, const(void)[] data) { 8 void[] result = new void[4 * width * height]; 9 size_t length = 4 * width; 10 foreach (j; 0 .. height) { 11 size_t index1 = j * 4 * width; 12 size_t index2 = (height - j - 1) * 4 * width; 13 result[index1 .. index1 + length] = data[index2 .. index2 + length]; 14 } 15 return result; 16 } 17 18 private interface OpenGLResource { 19 20 void dispose(); 21 22 } 23 24 final class OpenGLException : Exception { 25 26 this(string msg, string file = __FILE__, size_t line = __LINE__) @nogc @safe pure nothrow { 27 super(msg, file, line); 28 } 29 30 } 31 32 final class OpenGLShader : Shader, OpenGLResource { 33 34 private { 35 OpenGLBackend context; 36 37 GLuint program; 38 39 GLint[string] uniformLocations; 40 } 41 42 private this(OpenGLBackend context, ShaderSource[] sources) { 43 import std.conv : to; 44 45 debug (resourceTracking) { 46 import std.stdio : writeln; 47 48 writefln!"Create OpenGL shader %p"(cast(void*) this); 49 } 50 51 this.context = context; 52 53 program = glCreateProgram(); 54 if (program == 0) { 55 throw new OpenGLException("Could not create shader program"); 56 } 57 58 GLuint[] shaders; 59 60 foreach (source; sources) { 61 GLenum type; 62 final switch (source.type) { 63 case ShaderType.Fragment: 64 type = GL_FRAGMENT_SHADER; 65 break; 66 case ShaderType.Vertex: 67 type = GL_VERTEX_SHADER; 68 break; 69 } 70 71 GLuint shader = glCreateShader(type); 72 if (shader == 0) { 73 foreach (s; shaders) { 74 glDeleteShader(s); 75 } 76 77 glDeleteProgram(program); 78 79 throw new OpenGLException("Could not create " ~ source.type.to!string ~ " shader"); 80 } 81 82 const(char)* src = source.source.ptr; 83 84 if (source.source.length > GLint.max) { 85 throw new OpenGLException("Length of " ~ source.type.to!string ~ " shader is too large"); 86 } 87 88 GLint length = cast(GLint) source.source.length; 89 90 glShaderSource(shader, 1, &src, &length); 91 92 glCompileShader(shader); 93 94 GLint success; 95 glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 96 if (!success) { 97 import std.exception : assumeUnique; 98 99 GLint size; 100 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size); 101 102 if (size <= 1) { 103 throw new OpenGLException(source.type.to!string ~ " shader compilation failed"); 104 } 105 106 char[] buf = new char[size - 1]; 107 glGetShaderInfoLog(shader, size - 1, null, buf.ptr); 108 109 throw new OpenGLException(source.type.to!string ~ " shader compilation failed: " ~ buf.assumeUnique); 110 } 111 112 shaders ~= shader; 113 } 114 115 foreach (shader; shaders) { 116 glAttachShader(program, shader); 117 } 118 119 glLinkProgram(program); 120 121 GLint success; 122 glGetProgramiv(program, GL_LINK_STATUS, &success); 123 if (!success) { 124 import std.exception : assumeUnique; 125 126 GLint size; 127 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size); 128 129 if (size <= 1) { 130 throw new OpenGLException("Shader linking failed"); 131 } 132 133 char[] buf = new char[size - 1]; 134 glGetProgramInfoLog(program, size - 1, null, buf.ptr); 135 136 throw new OpenGLException("Shader linking failed: " ~ buf.assumeUnique); 137 } 138 139 foreach (shader; shaders) { 140 glDeleteShader(shader); 141 } 142 } 143 144 override void dispose() { 145 debug (resourceTracking) { 146 import std.stdio : writeln; 147 148 writefln!"Delete OpenGL shader %p"(cast(void*) this); 149 } 150 151 glDeleteProgram(program); 152 153 context.resources.remove(this); 154 } 155 156 private void useShader() { 157 if (context.shaderInUse !is this) { 158 context.shaderInUse = this; 159 glUseProgram(program); 160 } 161 } 162 163 private GLint getUniformLocation(string name) { 164 import std..string : toStringz; 165 166 if (name in uniformLocations) { 167 return uniformLocations[name]; 168 } 169 170 GLint result = glGetUniformLocation(program, name.toStringz); 171 172 uniformLocations[name] = result; 173 return result; 174 } 175 176 override void setUniform(string name, float value) { 177 useShader(); 178 179 GLint uniformLocation = getUniformLocation(name); 180 if (uniformLocation == -1) { 181 return; 182 } 183 184 glUniform1f(uniformLocation, value); 185 if (glGetError() != GL_NO_ERROR) { 186 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to float value"); 187 } 188 } 189 190 override void setUniform(string name, int value) { 191 useShader(); 192 193 GLint uniformLocation = getUniformLocation(name); 194 if (uniformLocation == -1) { 195 return; 196 } 197 198 glUniform1i(uniformLocation, value); 199 if (glGetError() != GL_NO_ERROR) { 200 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to int value"); 201 } 202 } 203 204 override void setUniform(string name, FVec2 value) { 205 useShader(); 206 207 GLint uniformLocation = getUniformLocation(name); 208 if (uniformLocation == -1) { 209 return; 210 } 211 212 glUniform2f(uniformLocation, value.x, value.y); 213 if (glGetError() != GL_NO_ERROR) { 214 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to FVec2 value"); 215 } 216 } 217 218 override void setUniform(string name, FVec3 value) { 219 useShader(); 220 221 GLint uniformLocation = getUniformLocation(name); 222 if (uniformLocation == -1) { 223 return; 224 } 225 226 glUniform3f(uniformLocation, value.x, value.y, value.z); 227 if (glGetError() != GL_NO_ERROR) { 228 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to FVec3 value"); 229 } 230 } 231 232 override void setUniform(string name, FVec4 value) { 233 useShader(); 234 235 GLint uniformLocation = getUniformLocation(name); 236 if (uniformLocation == -1) { 237 return; 238 } 239 240 glUniform4f(uniformLocation, value.x, value.y, value.z, value.w); 241 if (glGetError() != GL_NO_ERROR) { 242 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to FVec4 value"); 243 } 244 } 245 246 override void setUniform(string name, Color value) { 247 useShader(); 248 249 GLint uniformLocation = getUniformLocation(name); 250 if (uniformLocation == -1) { 251 return; 252 } 253 254 glUniform4f(uniformLocation, value.r, value.g, value.b, value.a); 255 if (glGetError() != GL_NO_ERROR) { 256 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to Color value"); 257 } 258 } 259 260 override void setUniform(string name, IVec2 value) { 261 useShader(); 262 263 GLint uniformLocation = getUniformLocation(name); 264 if (uniformLocation == -1) { 265 return; 266 } 267 268 glUniform2i(uniformLocation, value.x, value.y); 269 if (glGetError() != GL_NO_ERROR) { 270 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to IVec2 value"); 271 } 272 } 273 274 override void setUniform(string name, IVec3 value) { 275 useShader(); 276 277 GLint uniformLocation = getUniformLocation(name); 278 if (uniformLocation == -1) { 279 return; 280 } 281 282 glUniform3i(uniformLocation, value.x, value.y, value.z); 283 if (glGetError() != GL_NO_ERROR) { 284 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to IVec3 value"); 285 } 286 } 287 288 override void setUniform(string name, IVec4 value) { 289 useShader(); 290 291 GLint uniformLocation = getUniformLocation(name); 292 if (uniformLocation == -1) { 293 return; 294 } 295 296 glUniform4i(uniformLocation, value.x, value.y, value.z, value.w); 297 if (glGetError() != GL_NO_ERROR) { 298 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to IVec4 value"); 299 } 300 } 301 302 override void setUniform(string name, FMat3 value) { 303 useShader(); 304 305 GLint uniformLocation = getUniformLocation(name); 306 if (uniformLocation == -1) { 307 return; 308 } 309 310 glUniformMatrix3fv(uniformLocation, 1, true, cast(const(GLfloat)*)&value); 311 if (glGetError() != GL_NO_ERROR) { 312 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to FMat3 value"); 313 } 314 } 315 316 private { 317 size_t[string] textureLocations; 318 Texture[] textures; 319 int currTexture; 320 321 size_t assignTexture(string name) { 322 if (name !in textureLocations) { 323 useShader(); 324 325 GLint uniformLocation = getUniformLocation(name); 326 if (uniformLocation == -1) { 327 return -1; 328 } 329 330 glUniform1i(uniformLocation, currTexture); 331 if (glGetError() != GL_NO_ERROR) { 332 throw new OpenGLException("An error occurred while setting uniform '" ~ name ~ "' to texture value"); 333 } 334 335 textureLocations[name] = currTexture; 336 textures ~= null; 337 currTexture += 1; 338 } 339 return textureLocations[name]; 340 } 341 } 342 343 override void setUniform(string name, Texture value) { 344 if (value && !cast(OpenGLFramebuffer) value && !cast(OpenGLTexture) value) { 345 throw new OpenGLException("Cannot set OpenGL uniform to non-OpenGL texture"); 346 } 347 348 size_t texture = assignTexture(name); 349 if (texture == -1) { 350 return; 351 } 352 353 textures[texture] = value; 354 } 355 356 } 357 358 final class OpenGLMesh : Mesh, OpenGLResource { 359 360 private { 361 OpenGLBackend context; 362 363 GLuint vao, buffer; 364 365 MeshUsage usage; 366 } 367 368 private this(OpenGLBackend context, VertexFormat format, MeshUsage usage) { 369 debug (resourceTracking) { 370 import std.stdio : writeln; 371 372 writefln!"Create OpenGL mesh %p"(cast(void*) this); 373 } 374 375 this.context = context; 376 this.usage = usage; 377 378 glGenVertexArrays(1, &vao); 379 glBindVertexArray(vao); 380 381 glGenBuffers(1, &buffer); 382 glBindBuffer(GL_ARRAY_BUFFER, buffer); 383 384 foreach (i, v; format.attributes) { 385 GLint size; 386 GLenum type; 387 final switch (v.type) { 388 case AttributeType.Float: 389 size = 1; 390 type = GL_FLOAT; 391 break; 392 case AttributeType.FVec2: 393 size = 2; 394 type = GL_FLOAT; 395 break; 396 case AttributeType.FVec3: 397 size = 3; 398 type = GL_FLOAT; 399 break; 400 case AttributeType.FVec4: 401 size = 4; 402 type = GL_FLOAT; 403 break; 404 case AttributeType.Int: 405 size = 1; 406 type = GL_INT; 407 break; 408 case AttributeType.IVec2: 409 size = 2; 410 type = GL_INT; 411 break; 412 case AttributeType.IVec3: 413 size = 3; 414 type = GL_INT; 415 break; 416 case AttributeType.IVec4: 417 size = 4; 418 type = GL_INT; 419 break; 420 } 421 glVertexAttribPointer(cast(GLuint) i, size, type, false, 422 cast(GLsizei) format.stride, cast(void*) v.byteOffset); 423 glEnableVertexAttribArray(cast(GLuint) i); 424 } 425 } 426 427 override void dispose() { 428 debug (resourceTracking) { 429 import std.stdio : writeln; 430 431 writefln!"Delete OpenGL mesh %p"(cast(void*) this); 432 } 433 434 glDeleteVertexArrays(1, &vao); 435 glDeleteBuffers(1, &buffer); 436 437 context.resources.remove(this); 438 } 439 440 override void upload(void[] data) { 441 glBindVertexArray(vao); 442 glBufferData(GL_ARRAY_BUFFER, cast(GLsizeiptr) data.length, data.ptr, 443 usage == MeshUsage.Dynamic ? GL_STREAM_DRAW : GL_STATIC_DRAW, 444 ); 445 } 446 447 } 448 449 final class OpenGLTexture : Texture, OpenGLResource { 450 451 private { 452 OpenGLBackend context; 453 454 GLuint texture; 455 456 IVec2 _size; 457 } 458 459 private this(OpenGLBackend context, IVec2 size, const(void)[] data) { 460 debug (resourceTracking) { 461 import std.stdio : writeln; 462 463 writefln!"Create OpenGL texture %p"(cast(void*) this); 464 } 465 466 this.context = context; 467 _size = size; 468 469 glGenTextures(1, &texture); 470 glBindTexture(GL_TEXTURE_2D, texture); 471 472 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 473 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 474 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 475 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 476 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 477 cast(GLsizei) size.x, cast(GLsizei) size.y, 478 0, GL_RGBA, GL_UNSIGNED_BYTE, flipImage(size.x, size.y, data).ptr); 479 } 480 481 override void dispose() { 482 debug (resourceTracking) { 483 import std.stdio : writeln; 484 485 writefln!"Delete OpenGL texture %p"(cast(void*) this); 486 } 487 488 glDeleteTextures(1, &texture); 489 490 context.resources.remove(this); 491 } 492 493 override IVec2 size() { 494 return _size; 495 } 496 497 } 498 499 final class OpenGLFramebuffer : Framebuffer, OpenGLResource { 500 501 private { 502 OpenGLBackend context; 503 504 GLuint fbo, rbo, texColorBuffer; 505 506 IVec2 _size; 507 } 508 509 private this(OpenGLBackend context, IVec2 size) { 510 debug (resourceTracking) { 511 import std.stdio : writeln; 512 513 writefln!"Create OpenGL framebuffer %p"(cast(void*) this); 514 } 515 516 this.context = context; 517 _size = size; 518 519 glGenFramebuffers(1, &fbo); 520 521 glBindFramebuffer(GL_FRAMEBUFFER, fbo); 522 523 glGenTextures(1, &texColorBuffer); 524 glBindTexture(GL_TEXTURE_2D, texColorBuffer); 525 526 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 527 cast(GLsizei) size.x, cast(GLsizei) size.y, 528 0, GL_RGBA, GL_UNSIGNED_BYTE, null); 529 530 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 531 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 532 glBindTexture(GL_TEXTURE_2D, 0); 533 534 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 535 GL_TEXTURE_2D, texColorBuffer, 0); 536 537 glGenRenderbuffers(1, &rbo); 538 539 glBindRenderbuffer(GL_RENDERBUFFER, rbo); 540 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 541 cast(GLsizei) size.x, cast(GLsizei) size.y); 542 glBindRenderbuffer(GL_RENDERBUFFER, 0); 543 544 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 545 GL_RENDERBUFFER, rbo); 546 547 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 548 throw new OpenGLException("An error occurred while creating a framebuffer"); 549 } 550 551 if (context._renderTarget) { 552 glBindFramebuffer(GL_FRAMEBUFFER, (cast(OpenGLFramebuffer) context._renderTarget).fbo); 553 } 554 else { 555 glBindFramebuffer(GL_FRAMEBUFFER, 0); 556 } 557 } 558 559 override void dispose() { 560 debug (resourceTracking) { 561 import std.stdio : writeln; 562 563 writefln!"Delete OpenGL framebuffer %p"(cast(void*) this); 564 } 565 566 glDeleteRenderbuffers(1, &rbo); 567 glDeleteTextures(1, &texColorBuffer); 568 glDeleteFramebuffers(1, &fbo); 569 570 context.resources.remove(this); 571 } 572 573 override IVec2 size() { 574 return _size; 575 } 576 577 } 578 579 final class OpenGLBackend : CanvasBackend { 580 581 private OpenGLShader shaderInUse; 582 583 this() { 584 GLSupport gl = .loadOpenGL(); // TODO: if you wanna do multicontext on Windows, 585 // apparently loadOpenGL needs to be called after every context switch? 586 if (gl < GLSupport.gl33) { 587 throw new Exception("Could not load OpenGL 3.3 or above"); 588 } 589 590 glDisable(GL_MULTISAMPLE); 591 } 592 593 private bool[OpenGLResource] resources; 594 595 override void dispose() { 596 import std.array : array; 597 598 foreach (resource; resources.byKey.array) { 599 resource.dispose(); 600 } 601 602 // TODO: unloadOpenGL? 603 } 604 605 override Shader shader(ShaderSource[] sources) { 606 return new OpenGLShader(this, sources); 607 } 608 609 override Mesh mesh(VertexFormat format, MeshUsage usage) { 610 return new OpenGLMesh(this, format, usage); 611 } 612 613 override Framebuffer framebuffer(IVec2 size) { 614 return new OpenGLFramebuffer(this, size); 615 } 616 617 override Texture texture(IVec2 size, const(void)[] data) { 618 return new OpenGLTexture(this, size, data); 619 } 620 621 private Framebuffer _renderTarget; 622 623 override void renderTarget(Framebuffer framebuffer) { 624 if (framebuffer && !cast(OpenGLFramebuffer) framebuffer) { 625 throw new OpenGLException("Cannot bind non-OpenGL framebuffer to OpenGL context"); 626 } 627 628 if (framebuffer !is _renderTarget) { 629 _renderTarget = framebuffer; 630 if (framebuffer) { 631 glBindFramebuffer(GL_FRAMEBUFFER, (cast(OpenGLFramebuffer) framebuffer).fbo); 632 } 633 else { 634 glBindFramebuffer(GL_FRAMEBUFFER, 0); 635 } 636 } 637 } 638 639 override Framebuffer renderTarget() { 640 return _renderTarget; 641 } 642 643 override void clearColor(Color color) { 644 glClearColor(color.r, color.g, color.b, color.a); 645 glClear(GL_COLOR_BUFFER_BIT); 646 } 647 648 override void clearStencil(ubyte value) { 649 glClearStencil(value); 650 glClear(GL_STENCIL_BUFFER_BIT); 651 } 652 653 override void stencil(Stencil value) { 654 stencilSeparate(value, value); 655 } 656 657 override void stencilSeparate(Stencil front, Stencil back) { 658 GLenum convStencilFunc(StencilFunction func) { 659 final switch (func) { 660 case StencilFunction.Always: return GL_ALWAYS; 661 case StencilFunction.Never: return GL_NEVER; 662 case StencilFunction.Lt: return GL_LESS; 663 case StencilFunction.Le: return GL_LEQUAL; 664 case StencilFunction.Gt: return GL_GREATER; 665 case StencilFunction.Ge: return GL_GEQUAL; 666 case StencilFunction.Eq: return GL_EQUAL; 667 case StencilFunction.Neq: return GL_NOTEQUAL; 668 } 669 } 670 671 GLenum convStencilOp(StencilOp op) { 672 final switch (op) { 673 case StencilOp.Nop: return GL_KEEP; 674 case StencilOp.Zero: return GL_ZERO; 675 case StencilOp.Set: return GL_REPLACE; 676 case StencilOp.Inc: return GL_INCR; 677 case StencilOp.Dec: return GL_DECR; 678 case StencilOp.IncWrap: return GL_INCR_WRAP; 679 case StencilOp.DecWrap: return GL_DECR_WRAP; 680 case StencilOp.Inv: return GL_INVERT; 681 } 682 } 683 684 if (front.func == StencilFunction.Always 685 && front.stencilFail == StencilOp.Nop 686 && front.depthFail == StencilOp.Nop 687 && front.pass == StencilOp.Nop 688 && back.func == StencilFunction.Always 689 && back.stencilFail == StencilOp.Nop 690 && back.depthFail == StencilOp.Nop 691 && back.pass == StencilOp.Nop) { 692 glDisable(GL_STENCIL_TEST); 693 return; 694 } 695 696 glEnable(GL_STENCIL_TEST); 697 698 if (front.writeMask == back.writeMask) { 699 glStencilMask(front.writeMask); 700 } 701 else { 702 glStencilMaskSeparate(GL_FRONT, front.writeMask); 703 glStencilMaskSeparate(GL_BACK, back.writeMask); 704 } 705 706 if (front.func == back.func && front.refValue == back.refValue && front.readMask == back.readMask) { 707 glStencilFunc(convStencilFunc(front.func), front.refValue, front.readMask); 708 } 709 else { 710 glStencilFuncSeparate(GL_FRONT, convStencilFunc(front.func), front.refValue, front.readMask); 711 glStencilFuncSeparate(GL_BACK, convStencilFunc(back.func), back.refValue, back.readMask); 712 } 713 714 if (front.stencilFail == back.stencilFail && front.depthFail == back.depthFail && front.pass == back.pass) { 715 glStencilOp( 716 convStencilOp(front.stencilFail), 717 convStencilOp(front.depthFail), 718 convStencilOp(front.pass), 719 ); 720 } 721 else { 722 glStencilOpSeparate(GL_FRONT, 723 convStencilOp(front.stencilFail), 724 convStencilOp(front.depthFail), 725 convStencilOp(front.pass), 726 ); 727 glStencilOpSeparate(GL_BACK, 728 convStencilOp(back.stencilFail), 729 convStencilOp(back.depthFail), 730 convStencilOp(back.pass), 731 ); 732 } 733 } 734 735 private BlendingFunction _colorBlend; 736 private BlendingFunction _alphaBlend; 737 738 override BlendingFunction colorBlend() const { return _colorBlend; } 739 override BlendingFunction alphaBlend() const { return _alphaBlend; } 740 override BlendingFunction blend() const { return _colorBlend; } 741 742 override void blend(BlendingFunction value) { 743 blend(value, value); 744 } 745 746 private GLenum convBlendingOperation(BlendingFunction.Operation op) { 747 final switch (op) { 748 case BlendingFunction.Operation.Add: return GL_FUNC_ADD; 749 case BlendingFunction.Operation.SrcMinusDst: return GL_FUNC_SUBTRACT; 750 case BlendingFunction.Operation.DstMinusSrc: return GL_FUNC_REVERSE_SUBTRACT; 751 case BlendingFunction.Operation.Min: return GL_MIN; 752 case BlendingFunction.Operation.Max: return GL_MAX; 753 } 754 } 755 756 private GLenum convBlendingFactor(BlendingFunction.Factor factor) { 757 final switch (factor) { 758 case BlendingFunction.Factor.Zero: return GL_ZERO; 759 case BlendingFunction.Factor.One: return GL_ONE; 760 case BlendingFunction.Factor.SrcColor: return GL_SRC_COLOR; 761 case BlendingFunction.Factor.DstColor: return GL_DST_COLOR; 762 case BlendingFunction.Factor.SrcAlpha: return GL_SRC_ALPHA; 763 case BlendingFunction.Factor.DstAlpha: return GL_DST_ALPHA; 764 case BlendingFunction.Factor.ConstColor: return GL_CONSTANT_COLOR; 765 case BlendingFunction.Factor.ConstAlpha: return GL_CONSTANT_ALPHA; 766 case BlendingFunction.Factor.OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR; 767 case BlendingFunction.Factor.OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR; 768 case BlendingFunction.Factor.OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; 769 case BlendingFunction.Factor.OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA; 770 case BlendingFunction.Factor.OneMinusConstColor: return GL_ONE_MINUS_CONSTANT_COLOR; 771 case BlendingFunction.Factor.OneMinusConstAlpha: return GL_ONE_MINUS_CONSTANT_ALPHA; 772 } 773 } 774 775 override void blend(BlendingFunction color, BlendingFunction alpha) { 776 _colorBlend = color; 777 _alphaBlend = alpha; 778 779 if (color == BlendingFunctions.Overwrite && alpha == color) { 780 // technically doesn't catch all functions equivalent to disabling blending 781 // but it's good enough 782 glDisable(GL_BLEND); 783 return; 784 } 785 786 glEnable(GL_BLEND); 787 788 if (color.op == alpha.op) { 789 glBlendEquation(convBlendingOperation(color.op)); 790 } 791 else { 792 glBlendEquationSeparate( 793 convBlendingOperation(color.op), 794 convBlendingOperation(alpha.op), 795 ); 796 } 797 798 if (color.sfactor == alpha.sfactor && color.dfactor == alpha.dfactor) { 799 glBlendFunc( 800 convBlendingFactor(color.sfactor), 801 convBlendingFactor(color.dfactor), 802 ); 803 } 804 else { 805 glBlendFuncSeparate( 806 convBlendingFactor(color.sfactor), 807 convBlendingFactor(color.dfactor), 808 convBlendingFactor(alpha.sfactor), 809 convBlendingFactor(alpha.dfactor), 810 ); 811 } 812 813 glBlendColor( 814 color.constant.r, 815 color.constant.g, 816 color.constant.b, 817 alpha.constant.a, 818 ); 819 } 820 821 override void viewport(IVec2 location, IVec2 size) { 822 glViewport(location.x, location.y, size.x, size.y); 823 } 824 825 override void colorWriteMask(bool r, bool g, bool b, bool a) { 826 glColorMask(r, g, b, a); 827 } 828 829 override void draw(DrawMode mode, Shader shader, Mesh mesh, size_t startVertex, size_t numVertices) { 830 if (!cast(OpenGLMesh) mesh) { 831 throw new OpenGLException("Cannot draw non-OpenGL mesh in OpenGL context"); 832 } 833 834 if (!cast(OpenGLShader) shader) { 835 throw new OpenGLException("Cannot use non-OpenGL shader in OpenGL context"); 836 } 837 838 OpenGLShader glShader = cast(OpenGLShader) shader; 839 840 glShader.useShader(); 841 842 foreach (i, texture; glShader.textures) { 843 glActiveTexture(GL_TEXTURE0 + cast(int) i); 844 if (cast(OpenGLFramebuffer) texture) { 845 glBindTexture(GL_TEXTURE_2D, (cast(OpenGLFramebuffer) texture).texColorBuffer); 846 } 847 else if (cast(OpenGLTexture) texture) { 848 glBindTexture(GL_TEXTURE_2D, (cast(OpenGLTexture) texture).texture); 849 } 850 else { 851 glBindTexture(GL_TEXTURE_2D, 0); 852 } 853 } 854 855 glBindVertexArray((cast(OpenGLMesh) mesh).vao); 856 857 GLenum glMode; 858 final switch (mode) { 859 case DrawMode.Triangles: 860 glMode = GL_TRIANGLES; 861 break; 862 case DrawMode.TriangleFan: 863 glMode = GL_TRIANGLE_FAN; 864 break; 865 case DrawMode.TriangleStrip: 866 glMode = GL_TRIANGLE_STRIP; 867 break; 868 } 869 870 glDrawArrays(glMode, cast(GLint) startVertex, cast(GLsizei) numVertices); 871 } 872 873 }