1 // Written in the D programming language. 2 3 /** 4 This module contains OpenGL access layer. 5 6 To enable OpenGL support, build with version(USE_OPENGL); 7 8 Synopsis: 9 10 ---- 11 import dlangui.graphics.glsupport; 12 13 ---- 14 15 Copyright: Vadim Lopatin, 2014 16 License: Boost License 1.0 17 Authors: Vadim Lopatin, coolreader.org@gmail.com 18 */ 19 module dlangui.graphics.glsupport; 20 21 public import dlangui.core.config; 22 static if (BACKEND_GUI): 23 static if (ENABLE_OPENGL): 24 25 public import dlangui.core.math3d; 26 27 import dlangui.core.logger; 28 import dlangui.core.types; 29 import dlangui.core.math3d; 30 31 import std.conv; 32 import std.string; 33 import std.array; 34 35 version (Android) { 36 enum SUPPORT_LEGACY_OPENGL = false; 37 public import EGL.eglplatform : EGLint; 38 public import EGL.egl; 39 //public import GLES2.gl2; 40 public import GLES3.gl3; 41 42 static if (SUPPORT_LEGACY_OPENGL) { 43 public import GLES.gl : glEnableClientState, glLightfv, glColor4f, GL_ALPHA_TEST, GL_VERTEX_ARRAY, 44 GL_COLOR_ARRAY, glVertexPointer, glColorPointer, glDisableClientState, 45 GL_TEXTURE_COORD_ARRAY, glTexCoordPointer, glColorPointer, glMatrixMode, 46 glLoadMatrixf, glLoadIdentity, GL_PROJECTION, GL_MODELVIEW; 47 } 48 49 } else { 50 enum SUPPORT_LEGACY_OPENGL = false; //true; 51 public import bindbc.opengl; 52 } 53 54 import dlangui.graphics.scene.mesh; 55 import dlangui.graphics.scene.effect; 56 57 58 //extern (C) void func(int n); 59 //pragma(msg, __traits(identifier, func)); 60 61 /** 62 * Convenient wrapper around glGetError() 63 * Using: checkgl!glFunction(funcParams); 64 * TODO use one of the DEBUG extensions 65 */ 66 template checkgl(alias func) 67 { 68 debug auto checkgl(string functionName=__FUNCTION__, int line=__LINE__, Args...)(Args args) 69 { 70 scope(success) checkError(__traits(identifier, func), functionName, line); 71 return func(args); 72 } else 73 alias checkgl = func; 74 } 75 bool checkError(string context="", string functionName=__FUNCTION__, int line=__LINE__) 76 { 77 GLenum err = glGetError(); 78 if (err != GL_NO_ERROR) { 79 Log.e("OpenGL error ", glerrorToString(err), " at ", functionName, ":", line, " -- ", context); 80 return true; 81 } 82 return false; 83 } 84 85 /** 86 * Convenient wrapper around glGetError() 87 * Using: checkgl!glFunction(funcParams); 88 * TODO use one of the DEBUG extensions 89 */ 90 template assertgl(alias func) 91 { 92 auto assertgl(string functionName=__FUNCTION__, int line=__LINE__, Args...)(Args args) 93 { 94 scope(success) assertNoError(func.stringof, functionName, line); 95 return func(args); 96 }; 97 } 98 void assertNoError(string context="", string functionName=__FUNCTION__, int line=__LINE__) 99 { 100 GLenum err = glGetError(); 101 if (err != GL_NO_ERROR) { 102 Log.e("fatal OpenGL error ", glerrorToString(err), " at ", functionName, ":", line, " -- ", context); 103 assert(false); 104 } 105 } 106 107 /* For reporting OpenGL errors, it's nicer to get a human-readable symbolic name for the 108 * error instead of the numeric form. Derelict's GLenum is just an alias for uint, so we 109 * can't depend on D's nice toString() for enums. 110 */ 111 string glerrorToString(in GLenum err) pure nothrow { 112 switch(err) { 113 case 0x0500: return "GL_INVALID_ENUM"; 114 case 0x0501: return "GL_INVALID_VALUE"; 115 case 0x0502: return "GL_INVALID_OPERATION"; 116 case 0x0505: return "GL_OUT_OF_MEMORY"; 117 case 0x0506: return "GL_INVALID_FRAMEBUFFER_OPERATION"; 118 case 0x0507: return "GL_CONTEXT_LOST"; 119 case GL_NO_ERROR: return "No GL error"; 120 default: return "Unknown GL error: " ~ to!string(err); 121 } 122 } 123 124 class GLProgram : dlangui.graphics.scene.mesh.GraphicsEffect { 125 @property abstract string vertexSource(); 126 @property abstract string fragmentSource(); 127 protected GLuint program; 128 protected bool initialized; 129 protected bool error; 130 131 private GLuint vertexShader; 132 private GLuint fragmentShader; 133 private string glslversion; 134 private int glslversionInt; 135 private char[] glslversionString; 136 137 private void compatibilityFixes(ref char[] code, GLuint type) { 138 if (glslversionInt < 150) 139 code = replace(code, " texture(", " texture2D("); 140 if (glslversionInt < 140) { 141 if(type == GL_VERTEX_SHADER) { 142 code = replace(code, "in ", "attribute "); 143 code = replace(code, "out ", "varying "); 144 } else { 145 code = replace(code, "in ", "varying "); 146 code = replace(code, "out vec4 outColor;", ""); 147 code = replace(code, "outColor", "gl_FragColor"); 148 } 149 } 150 } 151 152 private GLuint compileShader(string src, GLuint type) { 153 import std.string : toStringz, fromStringz; 154 155 char[] sourceCode; 156 if (glslversionString.length) { 157 sourceCode ~= "#version "; 158 sourceCode ~= glslversionString; 159 sourceCode ~= "\n"; 160 } 161 sourceCode ~= src; 162 compatibilityFixes(sourceCode, type); 163 164 Log.d("compileShader: glslVersion = ", glslversion, ", type: ", (type == GL_VERTEX_SHADER ? "GL_VERTEX_SHADER" : (type == GL_FRAGMENT_SHADER ? "GL_FRAGMENT_SHADER" : "UNKNOWN"))); 165 //Log.v("Shader code:\n", sourceCode); 166 GLuint shader = checkgl!glCreateShader(type); 167 const char * psrc = sourceCode.toStringz; 168 checkgl!glShaderSource(shader, 1, &psrc, null); 169 checkgl!glCompileShader(shader); 170 GLint compiled; 171 checkgl!glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 172 if (compiled) { 173 // compiled successfully 174 return shader; 175 } else { 176 Log.e("Failed to compile shader source:\n", sourceCode); 177 GLint blen = 0; 178 GLsizei slen = 0; 179 checkgl!glGetShaderiv(shader, GL_INFO_LOG_LENGTH , &blen); 180 if (blen > 1) 181 { 182 GLchar[] msg = new GLchar[blen + 1]; 183 checkgl!glGetShaderInfoLog(shader, blen, &slen, msg.ptr); 184 Log.e("Shader compilation error: ", fromStringz(msg.ptr)); 185 } 186 return 0; 187 } 188 } 189 190 bool compile() { 191 glslversion = checkgl!fromStringz(cast(const char *)glGetString(GL_SHADING_LANGUAGE_VERSION)).dup; 192 glslversionString.length = 0; 193 glslversionInt = 0; 194 foreach(ch; glslversion) { 195 if (ch >= '0' && ch <= '9') { 196 glslversionString ~= ch; 197 glslversionInt = glslversionInt * 10 + (ch - '0'); 198 } else if (ch != '.') 199 break; 200 } 201 version (Android) { 202 glslversionInt = 130; 203 } 204 205 vertexShader = compileShader(vertexSource, GL_VERTEX_SHADER); 206 fragmentShader = compileShader(fragmentSource, GL_FRAGMENT_SHADER); 207 if (!vertexShader || !fragmentShader) { 208 error = true; 209 return false; 210 } 211 program = checkgl!glCreateProgram(); 212 checkgl!glAttachShader(program, vertexShader); 213 checkgl!glAttachShader(program, fragmentShader); 214 checkgl!glLinkProgram(program); 215 GLint isLinked = 0; 216 checkgl!glGetProgramiv(program, GL_LINK_STATUS, &isLinked); 217 if (!isLinked) { 218 GLint maxLength = 0; 219 checkgl!glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); 220 GLchar[] msg = new GLchar[maxLength + 1]; 221 checkgl!glGetProgramInfoLog(program, maxLength, &maxLength, msg.ptr); 222 Log.e("Error while linking program: ", fromStringz(msg.ptr)); 223 error = true; 224 return false; 225 } 226 Log.d("Program linked successfully"); 227 228 initStandardLocations(); 229 if (!initLocations()) { 230 Log.e("some of locations were not found"); 231 error = true; 232 } 233 initialized = true; 234 Log.v("Program is initialized successfully"); 235 return !error; 236 } 237 238 239 void initStandardLocations() { 240 for(DefaultUniform id = DefaultUniform.min; id <= DefaultUniform.max; id++) { 241 _uniformIdLocations[id] = getUniformLocation(to!string(id)); 242 } 243 for(DefaultAttribute id = DefaultAttribute.min; id <= DefaultAttribute.max; id++) { 244 _attribIdLocations[id] = getAttribLocation(to!string(id)); 245 } 246 } 247 248 /// override to init shader code locations 249 abstract bool initLocations(); 250 251 ~this() { 252 // TODO: cleanup 253 if (program) 254 glDeleteProgram(program); 255 if (vertexShader) 256 glDeleteShader(vertexShader); 257 if (fragmentShader) 258 glDeleteShader(fragmentShader); 259 program = vertexShader = fragmentShader = 0; 260 initialized = false; 261 } 262 263 /// returns true if program is ready for use 264 bool check() { 265 return !error && initialized; 266 } 267 268 static GLuint currentProgram; 269 /// binds program to current context 270 void bind() { 271 if(program != currentProgram) { 272 checkgl!glUseProgram(program); 273 currentProgram = program; 274 } 275 } 276 277 /// unbinds program from current context 278 static void unbind() { 279 checkgl!glUseProgram(0); 280 currentProgram = 0; 281 } 282 283 protected int[string] _uniformLocations; 284 protected int[string] _attribLocations; 285 protected int[DefaultUniform.max + 1] _uniformIdLocations; 286 protected int[DefaultAttribute.max + 1] _attribIdLocations; 287 288 /// get location for vertex attribute 289 override int getVertexElementLocation(VertexElementType type) { 290 return VERTEX_ELEMENT_NOT_FOUND; 291 } 292 293 294 /// get uniform location from program by uniform id, returns -1 if location is not found 295 int getUniformLocation(DefaultUniform uniform) { 296 return _uniformIdLocations[uniform]; 297 } 298 299 /// get uniform location from program, returns -1 if location is not found 300 int getUniformLocation(string variableName) { 301 if (auto p = variableName in _uniformLocations) 302 return *p; 303 int res = checkgl!glGetUniformLocation(program, variableName.toStringz); 304 //if (res == -1) 305 // Log.e("glGetUniformLocation failed for " ~ variableName); 306 _uniformLocations[variableName] = res; 307 return res; 308 } 309 310 /// get attribute location from program by uniform id, returns -1 if location is not found 311 int getAttribLocation(DefaultAttribute id) { 312 return _attribIdLocations[id]; 313 } 314 315 /// get attribute location from program, returns -1 if location is not found 316 int getAttribLocation(string variableName) { 317 if (auto p = variableName in _attribLocations) 318 return *p; 319 int res = checkgl!glGetAttribLocation(program, variableName.toStringz); 320 //if (res == -1) 321 // Log.e("glGetAttribLocation failed for " ~ variableName); 322 _attribLocations[variableName] = res; 323 return res; 324 } 325 326 override void setUniform(string uniformName, const vec2[] vec) { 327 checkgl!glUniform2fv(getUniformLocation(uniformName), cast(int)vec.length, cast(const(float)*)vec.ptr); 328 } 329 330 override void setUniform(DefaultUniform id, const vec2[] vec) { 331 checkgl!glUniform2fv(getUniformLocation(id), cast(int)vec.length, cast(const(float)*)vec.ptr); 332 } 333 334 override void setUniform(string uniformName, vec2 vec) { 335 checkgl!glUniform2fv(getUniformLocation(uniformName), 1, vec.vec.ptr); 336 } 337 338 override void setUniform(DefaultUniform id, vec2 vec) { 339 checkgl!glUniform2fv(getUniformLocation(id), 1, vec.vec.ptr); 340 } 341 342 override void setUniform(string uniformName, vec3 vec) { 343 checkgl!glUniform3fv(getUniformLocation(uniformName), 1, vec.vec.ptr); 344 } 345 346 override void setUniform(DefaultUniform id, vec3 vec) { 347 checkgl!glUniform3fv(getUniformLocation(id), 1, vec.vec.ptr); 348 } 349 350 override void setUniform(string uniformName, const vec3[] vec) { 351 checkgl!glUniform3fv(getUniformLocation(uniformName), cast(int)vec.length, cast(const(float)*)vec.ptr); 352 } 353 354 override void setUniform(DefaultUniform id, const vec3[] vec) { 355 checkgl!glUniform3fv(getUniformLocation(id), cast(int)vec.length, cast(const(float)*)vec.ptr); 356 } 357 358 override void setUniform(string uniformName, vec4 vec) { 359 checkgl!glUniform4fv(getUniformLocation(uniformName), 1, vec.vec.ptr); 360 } 361 362 override void setUniform(DefaultUniform id, vec4 vec) { 363 checkgl!glUniform4fv(getUniformLocation(id), 1, vec.vec.ptr); 364 } 365 366 override void setUniform(string uniformName, const vec4[] vec) { 367 checkgl!glUniform4fv(getUniformLocation(uniformName), cast(int)vec.length, cast(const(float)*)vec.ptr); 368 } 369 370 override void setUniform(DefaultUniform id, const vec4[] vec) { 371 checkgl!glUniform4fv(getUniformLocation(id), cast(int)vec.length, cast(const(float)*)vec.ptr); 372 } 373 374 override void setUniform(string uniformName, ref const(mat4) matrix) { 375 checkgl!glUniformMatrix4fv(getUniformLocation(uniformName), 1, false, matrix.m.ptr); 376 } 377 378 override void setUniform(DefaultUniform id, ref const(mat4) matrix) { 379 checkgl!glUniformMatrix4fv(getUniformLocation(id), 1, false, matrix.m.ptr); 380 } 381 382 override void setUniform(string uniformName, const(mat4)[] matrix) { 383 checkgl!glUniformMatrix4fv(getUniformLocation(uniformName), cast(int)matrix.length, false, cast(const(float)*)matrix.ptr); 384 } 385 386 override void setUniform(DefaultUniform id, const(mat4)[] matrix) { 387 checkgl!glUniformMatrix4fv(getUniformLocation(id), cast(int)matrix.length, false, cast(const(float)*)matrix.ptr); 388 } 389 390 override void setUniform(string uniformName, float v) { 391 checkgl!glUniform1f(getUniformLocation(uniformName), v); 392 } 393 394 override void setUniform(DefaultUniform id, float v) { 395 checkgl!glUniform1f(getUniformLocation(id), v); 396 } 397 398 override void setUniform(string uniformName, const float[] v) { 399 checkgl!glUniform1fv(getUniformLocation(uniformName), cast(int)v.length, cast(const(float)*)v.ptr); 400 } 401 402 override void setUniform(DefaultUniform id, const float[] v) { 403 checkgl!glUniform1fv(getUniformLocation(id), cast(int)v.length, cast(const(float)*)v.ptr); 404 } 405 406 /// returns true if effect has uniform 407 override bool hasUniform(DefaultUniform id) { 408 return getUniformLocation(id) >= 0; 409 } 410 411 /// returns true if effect has uniform 412 override bool hasUniform(string uniformName) { 413 return getUniformLocation(uniformName) >= 0; 414 } 415 416 /// draw mesh using this program (program should be bound by this time and all uniforms should be set) 417 override void draw(Mesh mesh, bool wireframe) { 418 VertexBuffer vb = mesh.vertexBuffer; 419 if (!vb) { 420 vb = new GLVertexBuffer(); 421 mesh.vertexBuffer = vb; 422 } 423 vb.draw(this, wireframe); 424 } 425 } 426 427 class SolidFillProgram : GLProgram { 428 @property override string vertexSource() { 429 return q{ 430 in vec3 a_position; 431 in vec4 a_color; 432 out vec4 col; 433 uniform mat4 u_worldViewProjectionMatrix; 434 void main(void) 435 { 436 gl_Position = u_worldViewProjectionMatrix * vec4(a_position, 1); 437 col = a_color; 438 } 439 }; 440 } 441 442 @property override string fragmentSource() { 443 return q{ 444 in vec4 col; 445 out vec4 outColor; 446 void main(void) 447 { 448 outColor = col; 449 } 450 }; 451 } 452 453 protected GLint matrixLocation; 454 protected GLint vertexLocation; 455 protected GLint colAttrLocation; 456 override bool initLocations() { 457 matrixLocation = getUniformLocation(DefaultUniform.u_worldViewProjectionMatrix); 458 vertexLocation = getAttribLocation(DefaultAttribute.a_position); 459 colAttrLocation = getAttribLocation(DefaultAttribute.a_color); 460 return matrixLocation >= 0 && vertexLocation >= 0 && colAttrLocation >= 0; 461 } 462 /// get location for vertex attribute 463 override int getVertexElementLocation(VertexElementType type) { 464 switch(type) with(VertexElementType) { 465 case POSITION: 466 return vertexLocation; 467 case COLOR: 468 return colAttrLocation; 469 default: 470 return VERTEX_ELEMENT_NOT_FOUND; 471 } 472 } 473 474 VAO vao; 475 476 protected void beforeExecute() { 477 bind(); 478 setUniform(DefaultUniform.u_worldViewProjectionMatrix, glSupport.projectionMatrix); 479 } 480 481 protected void createVAO(size_t verticesBufferLength) { 482 vao = new VAO; 483 484 glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0); 485 glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (verticesBufferLength * float.sizeof)); 486 487 glEnableVertexAttribArray(vertexLocation); 488 glEnableVertexAttribArray(colAttrLocation); 489 } 490 491 bool drawBatch(int length, int start, bool areLines = false) { 492 if(!check()) 493 return false; 494 beforeExecute(); 495 496 vao.bind(); 497 498 checkgl!glDrawElements(areLines ? GL_LINES : GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(start * 4)); 499 500 return true; 501 } 502 503 void destroyBuffers() { 504 destroy(vao); 505 vao = null; 506 } 507 } 508 509 class TextureProgram : SolidFillProgram { 510 @property override string vertexSource() { 511 return q{ 512 in vec3 a_position; 513 in vec4 a_color; 514 in vec2 a_texCoord; 515 out vec4 col; 516 out vec2 UV; 517 uniform mat4 u_worldViewProjectionMatrix; 518 void main(void) 519 { 520 gl_Position = u_worldViewProjectionMatrix * vec4(a_position, 1); 521 col = a_color; 522 UV = a_texCoord; 523 } 524 }; 525 } 526 @property override string fragmentSource() { 527 return q{ 528 uniform sampler2D tex; 529 in vec4 col; 530 in vec2 UV; 531 out vec4 outColor; 532 void main(void) 533 { 534 outColor = texture(tex, UV) * col; 535 } 536 }; 537 } 538 539 GLint texCoordLocation; 540 override bool initLocations() { 541 bool res = super.initLocations(); 542 texCoordLocation = getAttribLocation(DefaultAttribute.a_texCoord); 543 return res && texCoordLocation >= 0; 544 } 545 /// get location for vertex attribute 546 override int getVertexElementLocation(VertexElementType type) { 547 switch(type) with(VertexElementType) { 548 case TEXCOORD0: 549 return texCoordLocation; 550 default: 551 return super.getVertexElementLocation(type); 552 } 553 } 554 555 protected void createVAO(size_t verticesBufferLength, size_t colorsBufferLength) { 556 vao = new VAO; 557 558 glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0); 559 glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (verticesBufferLength * float.sizeof)); 560 glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, cast(void*) ((verticesBufferLength + colorsBufferLength) * float.sizeof)); 561 562 glEnableVertexAttribArray(vertexLocation); 563 glEnableVertexAttribArray(colAttrLocation); 564 glEnableVertexAttribArray(texCoordLocation); 565 } 566 567 bool drawBatch(Tex2D texture, bool linear, int length, int start) { 568 if(!check()) 569 return false; 570 beforeExecute(); 571 572 texture.setup(); 573 texture.setSamplerParams(linear); 574 575 vao.bind(); 576 577 checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(start * 4)); 578 579 texture.unbind(); 580 return true; 581 } 582 } 583 584 585 struct Color 586 { 587 float r, g, b, a; 588 } 589 590 // utility function to fill 4-float array of vertex colors with converted CR 32bit color 591 private void FillColor(uint color, Color[] buf_slice) { 592 float r = ((color >> 16) & 255) / 255.0; 593 float g = ((color >> 8) & 255) / 255.0; 594 float b = ((color >> 0) & 255) / 255.0; 595 float a = (((color >> 24) & 255) ^ 255) / 255.0; 596 foreach(ref col; buf_slice) { 597 col.r = r; 598 col.g = g; 599 col.b = b; 600 col.a = a; 601 } 602 } 603 604 605 import std.functional; 606 alias convertColors = memoize!(convertColorsImpl); 607 608 float[] convertColorsImpl(uint[] cols) pure nothrow { 609 float[] colors; 610 colors.length = cols.length * 4; 611 foreach(i; 0 .. cols.length) { 612 uint color = cols[i]; 613 float r = ((color >> 16) & 255) / 255.0; 614 float g = ((color >> 8) & 255) / 255.0; 615 float b = ((color >> 0) & 255) / 255.0; 616 float a = (((color >> 24) & 255) ^ 255) / 255.0; 617 colors[i * 4 + 0] = r; 618 colors[i * 4 + 1] = g; 619 colors[i * 4 + 2] = b; 620 colors[i * 4 + 3] = a; 621 } 622 return colors; 623 } 624 625 private __gshared GLSupport _glSupport; 626 @property GLSupport glSupport() { 627 if (!_glSupport) { 628 Log.f("GLSupport is not initialized"); 629 assert(false, "GLSupport is not initialized"); 630 } 631 if (!_glSupport.valid) { 632 Log.e("GLSupport programs are not initialized"); 633 } 634 return _glSupport; 635 } 636 637 __gshared bool glNoContext; 638 639 /// initialize OpenGL support helper (call when current OpenGL context is initialized) 640 bool initGLSupport(bool legacy = false) { 641 import dlangui.platforms.common.platform : setOpenglEnabled; 642 if (_glSupport && _glSupport.valid) 643 return true; 644 version(Android) { 645 Log.d("initGLSupport"); 646 } else { 647 648 auto support = loadOpenGL(); 649 if(support < bindbc.opengl.GLSupport.gl11) // No context! Error! 650 { 651 Log.e("OpenGL wasn't loaded successfully"); 652 return false; 653 } 654 legacy = false; 655 } 656 if (!_glSupport) { // TODO_GRIM: Legacy looks very broken to me. 657 Log.d("glSupport not initialized: trying to create"); 658 int major = *cast(int*)(glGetString(GL_VERSION)[0 .. 1].ptr); 659 legacy = legacy || (major < 3); 660 _glSupport = new GLSupport(legacy); 661 if (!_glSupport.valid) { 662 Log.e("Failed to compile shaders"); 663 version (Android) { 664 // do not recreate legacy mode 665 } else { 666 // try opposite legacy flag 667 if (_glSupport.legacyMode == legacy) { 668 Log.i("Trying to reinit GLSupport with legacy flag ", !legacy); 669 _glSupport = new GLSupport(!legacy); 670 } 671 // Situation when opposite legacy flag is true and GL version is 3+ with no old functions 672 if (_glSupport.legacyMode) { 673 if (major >= 3) { 674 Log.e("Try to create OpenGL context with <= 3.1 version"); 675 return false; 676 } 677 } 678 } 679 } 680 } 681 if (_glSupport.valid) { 682 setOpenglEnabled(); 683 Log.v("OpenGL is initialized ok"); 684 return true; 685 } else { 686 Log.e("Failed to compile shaders"); 687 return false; 688 } 689 } 690 691 /// OpenGL support helper 692 final class GLSupport { 693 694 private bool _legacyMode; 695 @property bool legacyMode() { return _legacyMode; } 696 @property queue() { return _queue; } 697 698 @property bool valid() { 699 return _legacyMode || _shadersAreInitialized; 700 } 701 702 this(bool legacy = false) { 703 _queue = new OpenGLQueue; 704 version (Android) { 705 Log.d("creating GLSupport"); 706 } else { 707 if (legacy /*&& !glLightfv*/) { 708 Log.w("GLSupport legacy API is not supported"); 709 legacy = false; 710 } 711 } 712 _legacyMode = legacy; 713 if (!_legacyMode) 714 _shadersAreInitialized = initShaders(); 715 } 716 717 ~this() { 718 uninitShaders(); 719 } 720 721 private OpenGLQueue _queue; 722 723 private SolidFillProgram _solidFillProgram; 724 private TextureProgram _textureProgram; 725 726 private bool _shadersAreInitialized; 727 private bool initShaders() { 728 if (_solidFillProgram is null) { 729 Log.v("Compiling solid fill program"); 730 _solidFillProgram = new SolidFillProgram(); 731 _solidFillProgram.compile(); 732 if (!_solidFillProgram.check()) 733 return false; 734 } 735 if (_textureProgram is null) { 736 Log.v("Compiling texture program"); 737 _textureProgram = new TextureProgram(); 738 _textureProgram.compile(); 739 if (!_textureProgram.check()) 740 return false; 741 } 742 Log.d("Shaders compiled successfully"); 743 return true; 744 } 745 746 private void uninitShaders() { 747 Log.d("Uniniting shaders"); 748 if (_solidFillProgram !is null) { 749 destroy(_solidFillProgram); 750 _solidFillProgram = null; 751 } 752 if (_textureProgram !is null) { 753 destroy(_textureProgram); 754 _textureProgram = null; 755 } 756 } 757 758 void beforeRenderGUI() { 759 glEnable(GL_BLEND); 760 checkgl!glDisable(GL_CULL_FACE); 761 checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 762 } 763 764 private VBO vbo; 765 private EBO ebo; 766 767 private void fillBuffers(float[] vertices, float[] colors, float[] texcoords, int[] indices) { 768 resetBindings(); 769 770 if(_legacyMode) 771 return; 772 773 vbo = new VBO; 774 ebo = new EBO; 775 776 vbo.bind(); 777 vbo.fill([vertices, colors, texcoords]); 778 779 ebo.bind(); 780 ebo.fill(indices); 781 782 // create vertex array objects and bind vertex buffers to them 783 _solidFillProgram.createVAO(vertices.length); 784 vbo.bind(); 785 ebo.bind(); 786 _textureProgram.createVAO(vertices.length, colors.length); 787 vbo.bind(); 788 ebo.bind(); 789 } 790 791 /// This function is needed to draw custom OpenGL scene correctly (especially on legacy API) 792 private void resetBindings() { 793 import std.traits : isFunction; 794 if (isFunction!glUseProgram) 795 GLProgram.unbind(); 796 if (isFunction!glBindVertexArray) 797 VAO.unbind(); 798 if (isFunction!glBindBuffer) 799 VBO.unbind(); 800 } 801 802 private void destroyBuffers() { 803 resetBindings(); 804 805 if(_legacyMode) 806 return; 807 808 if (_solidFillProgram) 809 _solidFillProgram.destroyBuffers(); 810 if (_textureProgram) 811 _textureProgram.destroyBuffers(); 812 813 destroy(vbo); 814 destroy(ebo); 815 vbo = null; 816 ebo = null; 817 } 818 819 private void drawLines(int length, int start) { 820 if (_legacyMode) { 821 static if (SUPPORT_LEGACY_OPENGL) { 822 glEnableClientState(GL_VERTEX_ARRAY); 823 glEnableClientState(GL_COLOR_ARRAY); 824 glVertexPointer(3, GL_FLOAT, 0, cast(void*)_queue._vertices.data.ptr); 825 glColorPointer(4, GL_FLOAT, 0, cast(void*)_queue._colors.data.ptr); 826 827 checkgl!glDrawElements(GL_LINES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(_queue._indices.data[start .. start + length].ptr)); 828 829 glDisableClientState(GL_COLOR_ARRAY); 830 glDisableClientState(GL_VERTEX_ARRAY); 831 } 832 } else { 833 if (_solidFillProgram !is null) { 834 _solidFillProgram.drawBatch(length, start, true); 835 } else 836 Log.e("No program"); 837 } 838 } 839 840 private void drawSolidFillTriangles(int length, int start) { 841 if (_legacyMode) { 842 static if (SUPPORT_LEGACY_OPENGL) { 843 glEnableClientState(GL_VERTEX_ARRAY); 844 glEnableClientState(GL_COLOR_ARRAY); 845 glVertexPointer(3, GL_FLOAT, 0, cast(void*)_queue._vertices.data.ptr); 846 glColorPointer(4, GL_FLOAT, 0, cast(void*)_queue._colors.data.ptr); 847 848 checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(_queue._indices.data[start .. start + length].ptr)); 849 850 glDisableClientState(GL_COLOR_ARRAY); 851 glDisableClientState(GL_VERTEX_ARRAY); 852 } 853 } else { 854 if (_solidFillProgram !is null) { 855 _solidFillProgram.drawBatch(length, start); 856 } else 857 Log.e("No program"); 858 } 859 } 860 861 private void drawColorAndTextureTriangles(Tex2D texture, bool linear, int length, int start) { 862 if (_legacyMode) { 863 static if (SUPPORT_LEGACY_OPENGL) { 864 glEnable(GL_TEXTURE_2D); 865 texture.setup(); 866 texture.setSamplerParams(linear); 867 868 glEnableClientState(GL_COLOR_ARRAY); 869 glEnableClientState(GL_VERTEX_ARRAY); 870 glEnableClientState(GL_TEXTURE_COORD_ARRAY); 871 glVertexPointer(3, GL_FLOAT, 0, cast(void*)_queue._vertices.data.ptr); 872 glTexCoordPointer(2, GL_FLOAT, 0, cast(void*)_queue._texCoords.data.ptr); 873 glColorPointer(4, GL_FLOAT, 0, cast(void*)_queue._colors.data.ptr); 874 875 checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(_queue._indices.data[start .. start + length].ptr)); 876 877 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 878 glDisableClientState(GL_VERTEX_ARRAY); 879 glDisableClientState(GL_COLOR_ARRAY); 880 glDisable(GL_TEXTURE_2D); 881 } 882 } else { 883 _textureProgram.drawBatch(texture, linear, length, start); 884 } 885 } 886 887 /// call glFlush 888 void flushGL() { 889 checkgl!glFlush(); 890 } 891 892 bool generateMipmap(int dx, int dy, ubyte * pixels, int level, ref ubyte[] dst) { 893 if ((dx & 1) || (dy & 1) || dx < 2 || dy < 2) 894 return false; // size is not even 895 int newdx = dx / 2; 896 int newdy = dy / 2; 897 int newlen = newdx * newdy * 4; 898 if (newlen > dst.length) 899 dst.length = newlen; 900 ubyte * dstptr = dst.ptr; 901 ubyte * srcptr = pixels; 902 int srcstride = dx * 4; 903 for (int y = 0; y < newdy; y++) { 904 for (int x = 0; x < newdx; x++) { 905 dstptr[0] = cast(ubyte)((srcptr[0+0] + srcptr[0+4] + srcptr[0+srcstride] + srcptr[0+srcstride + 4])>>2); 906 dstptr[1] = cast(ubyte)((srcptr[1+0] + srcptr[1+4] + srcptr[1+srcstride] + srcptr[1+srcstride + 4])>>2); 907 dstptr[2] = cast(ubyte)((srcptr[2+0] + srcptr[2+4] + srcptr[2+srcstride] + srcptr[2+srcstride + 4])>>2); 908 dstptr[3] = cast(ubyte)((srcptr[3+0] + srcptr[3+4] + srcptr[3+srcstride] + srcptr[3+srcstride + 4])>>2); 909 dstptr += 4; 910 srcptr += 8; 911 } 912 srcptr += srcstride; // skip srcline 913 } 914 checkgl!glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, newdx, newdy, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst.ptr); 915 return true; 916 } 917 918 bool setTextureImage(Tex2D texture, int dx, int dy, ubyte * pixels, int mipmapLevels = 0) { 919 checkError("before setTextureImage"); 920 texture.bind(); 921 checkgl!glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 922 texture.setSamplerParams(true, true); 923 924 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); 925 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmapLevels > 0 ? mipmapLevels - 1 : 0); 926 // ORIGINAL: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 927 checkgl!glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 928 if (checkError("updateTexture - glTexImage2D")) { 929 Log.e("Cannot set image for texture"); 930 return false; 931 } 932 if (mipmapLevels > 1) { 933 ubyte[] buffer; 934 ubyte * src = pixels; 935 int ndx = dx; 936 int ndy = dy; 937 for (int i = 1; i < mipmapLevels; i++) { 938 if (!generateMipmap(ndx, ndy, src, i, buffer)) 939 break; 940 ndx /= 2; 941 ndy /= 2; 942 src = buffer.ptr; 943 } 944 } 945 texture.unbind(); 946 return true; 947 } 948 949 bool setTextureImageAlpha(Tex2D texture, int dx, int dy, ubyte * pixels) { 950 checkError("before setTextureImageAlpha"); 951 texture.bind(); 952 checkgl!glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 953 texture.setSamplerParams(true, true); 954 955 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, dx, dy, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels); 956 if (checkError("setTextureImageAlpha - glTexImage2D")) { 957 Log.e("Cannot set image for texture"); 958 return false; 959 } 960 texture.unbind(); 961 return true; 962 } 963 964 private FBO currentFBO; 965 966 /// returns texture for buffer, null if failed 967 bool createFramebuffer(out Tex2D texture, out FBO fbo, int dx, int dy) { 968 checkError("before createFramebuffer"); 969 bool res = true; 970 texture = new Tex2D(); 971 if (!texture.ID) 972 return false; 973 checkError("glBindTexture GL_TEXTURE_2D"); 974 FBO f = new FBO(); 975 if (!f.ID) 976 return false; 977 fbo = f; 978 979 checkgl!glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dx, dy, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, null); 980 981 texture.setSamplerParams(true, true); 982 983 checkgl!glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.ID, 0); 984 // Always check that our framebuffer is ok 985 if(checkgl!glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 986 Log.e("glFramebufferTexture2D failed"); 987 res = false; 988 } 989 checkgl!glClearColor(0.5f, 0.5f, 0.5f, 1.0f); 990 checkgl!glClear(GL_COLOR_BUFFER_BIT); 991 currentFBO = fbo; 992 993 texture.unbind(); 994 fbo.unbind(); 995 996 return res; 997 } 998 999 void deleteFramebuffer(ref FBO fbo) { 1000 if (fbo.ID != 0) { 1001 destroy(fbo); 1002 } 1003 currentFBO = null; 1004 } 1005 1006 bool bindFramebuffer(FBO fbo) { 1007 fbo.bind(); 1008 currentFBO = fbo; 1009 return !checkError("glBindFramebuffer"); 1010 } 1011 1012 void clearDepthBuffer() { 1013 glClear(GL_DEPTH_BUFFER_BIT); 1014 //glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 1015 } 1016 1017 /// projection matrix 1018 /// current gl buffer width 1019 private int bufferDx; 1020 /// current gl buffer height 1021 private int bufferDy; 1022 private mat4 _projectionMatrix; 1023 1024 @property ref mat4 projectionMatrix() { 1025 return _projectionMatrix; 1026 } 1027 1028 void setOrthoProjection(Rect windowRect, Rect view) { 1029 flushGL(); 1030 bufferDx = windowRect.width; 1031 bufferDy = windowRect.height; 1032 _projectionMatrix.setOrtho(view.left, view.right, view.top, view.bottom, 0.5f, 50.0f); 1033 1034 if (_legacyMode) { 1035 static if (SUPPORT_LEGACY_OPENGL) { 1036 glMatrixMode(GL_PROJECTION); 1037 //checkgl!glPushMatrix(); 1038 //glLoadIdentity(); 1039 glLoadMatrixf(_projectionMatrix.m.ptr); 1040 //glOrthof(0, _dx, 0, _dy, -1.0f, 1.0f); 1041 glMatrixMode(GL_MODELVIEW); 1042 //checkgl!glPushMatrix(); 1043 glLoadIdentity(); 1044 } 1045 } 1046 checkgl!glViewport(view.left, currentFBO ? view.top : windowRect.height - view.bottom, view.width, view.height); 1047 } 1048 1049 void setPerspectiveProjection(Rect windowRect, Rect view, float fieldOfView, float nearPlane, float farPlane) { 1050 flushGL(); 1051 bufferDx = windowRect.width; 1052 bufferDy = windowRect.height; 1053 float aspectRatio = cast(float)view.width / cast(float)view.height; 1054 _projectionMatrix.setPerspective(fieldOfView, aspectRatio, nearPlane, farPlane); 1055 if (_legacyMode) { 1056 static if (SUPPORT_LEGACY_OPENGL) { 1057 glMatrixMode(GL_PROJECTION); 1058 //checkgl!glPushMatrix(); 1059 //glLoadIdentity(); 1060 glLoadMatrixf(_projectionMatrix.m.ptr); 1061 //glOrthof(0, _dx, 0, _dy, -1.0f, 1.0f); 1062 glMatrixMode(GL_MODELVIEW); 1063 //checkgl!glPushMatrix(); 1064 glLoadIdentity(); 1065 } 1066 } 1067 checkgl!glViewport(view.left, currentFBO ? view.top : windowRect.height - view.bottom, view.width, view.height); 1068 } 1069 } 1070 1071 enum GLObjectTypes { Buffer, VertexArray, Texture, Framebuffer }; 1072 /** RAII OpenGL object template. 1073 * Note: on construction it binds itself to the target, and it binds 0 to target on destruction. 1074 * All methods (except ctor, dtor, bind(), unbind() and setup()) does not perform binding. 1075 */ 1076 1077 1078 class GLObject(GLObjectTypes type, GLuint target = 0) { 1079 immutable GLuint ID; 1080 //alias ID this; // good, but it confuses destroy() 1081 1082 this() { 1083 GLuint handle; 1084 mixin("checkgl!glGen" ~ to!string(type) ~ "s(1, &handle);"); 1085 ID = handle; 1086 bind(); 1087 } 1088 1089 ~this() { 1090 if (!glNoContext) { 1091 unbind(); 1092 mixin("checkgl!glDelete" ~ to!string(type) ~ "s(1, &ID);"); 1093 } 1094 } 1095 1096 void bind() { 1097 static if(target != 0) 1098 mixin("glBind" ~ to!string(type) ~ "(" ~ to!string(target) ~ ", ID);"); 1099 else 1100 mixin("glBind" ~ to!string(type) ~ "(ID);"); 1101 } 1102 1103 static void unbind() { 1104 static if(target != 0) 1105 mixin("checkgl!glBind" ~ to!string(type) ~ "(" ~ to!string(target) ~ ", 0);"); 1106 else 1107 mixin("checkgl!glBind" ~ to!string(type) ~ "(0);"); 1108 } 1109 1110 static if(type == GLObjectTypes.Buffer) 1111 { 1112 void fill(float[][] buffs) { 1113 int length; 1114 foreach(b; buffs) 1115 length += b.length; 1116 checkgl!glBufferData(target, 1117 length * float.sizeof, 1118 null, 1119 GL_STATIC_DRAW); 1120 int offset; 1121 foreach(b; buffs) { 1122 checkgl!glBufferSubData(target, 1123 offset, 1124 b.length * float.sizeof, 1125 b.ptr); 1126 offset += b.length * float.sizeof; 1127 } 1128 } 1129 1130 static if (target == GL_ELEMENT_ARRAY_BUFFER) { 1131 void fill(int[] indexes) { 1132 checkgl!glBufferData(target, cast(int)(indexes.length * int.sizeof), indexes.ptr, GL_STATIC_DRAW); 1133 } 1134 } 1135 } 1136 1137 static if(type == GLObjectTypes.Texture) 1138 { 1139 void setSamplerParams(bool linear, bool clamp = false, bool mipmap = false) { 1140 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST); 1141 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, linear ? 1142 (!mipmap ? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR) : 1143 (!mipmap ? GL_NEAREST : GL_NEAREST_MIPMAP_NEAREST)); //GL_NEAREST_MIPMAP_NEAREST 1144 checkError("filtering - glTexParameteri"); 1145 if(clamp) { 1146 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 1147 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 1148 checkError("clamp - glTexParameteri"); 1149 } 1150 } 1151 1152 void setup(GLuint binding = 0) { 1153 glActiveTexture(GL_TEXTURE0 + binding); 1154 glBindTexture(target, ID); 1155 checkError("setup texture"); 1156 } 1157 } 1158 } 1159 1160 alias VAO = GLObject!(GLObjectTypes.VertexArray); 1161 alias VBO = GLObject!(GLObjectTypes.Buffer, GL_ARRAY_BUFFER); 1162 alias EBO = GLObject!(GLObjectTypes.Buffer, GL_ELEMENT_ARRAY_BUFFER); 1163 alias Tex2D = GLObject!(GLObjectTypes.Texture, GL_TEXTURE_2D); 1164 alias FBO = GLObject!(GLObjectTypes.Framebuffer, GL_FRAMEBUFFER); 1165 1166 class GLVertexBuffer : VertexBuffer { 1167 protected VertexFormat _format; 1168 protected IndexFragment[] _indexFragments; 1169 protected int _vertexCount; 1170 protected GLuint _vertexBuffer; 1171 protected GLuint _indexBuffer; 1172 protected GLuint _vao; 1173 1174 this() { 1175 version (Android) { 1176 checkgl!glGenBuffers(1, &_vertexBuffer); 1177 checkgl!glGenBuffers(1, &_indexBuffer); 1178 checkgl!glGenVertexArrays(1, &_vao); 1179 } else { 1180 assertgl!glGenBuffers(1, &_vertexBuffer); 1181 assertgl!glGenBuffers(1, &_indexBuffer); 1182 assertgl!glGenVertexArrays(1, &_vao); 1183 } 1184 } 1185 1186 ~this() { 1187 checkgl!glDeleteBuffers(1, &_vertexBuffer); 1188 checkgl!glDeleteBuffers(1, &_indexBuffer); 1189 checkgl!glDeleteVertexArrays(1, &_vao); 1190 } 1191 1192 ///// bind into current context 1193 //override void bind() { 1194 // checkgl!glBindVertexArray(_vao); 1195 // 1196 // // TODO: is it necessary to bind vertex/index buffers? 1197 // // specify vertex buffer 1198 // checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 1199 // // specify index buffer 1200 // checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 1201 //} 1202 // 1203 ///// unbind from current context 1204 //override void unbind() { 1205 // checkgl!glBindVertexArray(0); 1206 // checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0); 1207 // checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1208 //} 1209 1210 /// update vertex element locations for effect/shader program 1211 void enableAttributes(GraphicsEffect effect) { 1212 checkgl!glBindVertexArray(_vao); 1213 // specify vertex buffer 1214 checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 1215 // specify index buffer 1216 checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 1217 int offset = 0; 1218 //Log.v("=== enableAttributes for ", _format); 1219 for(int i = 0; i < _format.length; i++) { 1220 int loc = effect.getVertexElementLocation(_format[i].type); 1221 if (loc >= 0) { 1222 //Log.v("setting attrib pointer for type ", _format[i].type, " offset=", offset, " location=", loc); 1223 checkgl!glVertexAttribPointer(loc, _format[i].size, GL_FLOAT, cast(ubyte)GL_FALSE, _format.vertexSize, cast(char*)(offset)); 1224 checkgl!glEnableVertexAttribArray(loc); 1225 } else { 1226 //Log.v("Attribute location not found for ", _format[i].type); 1227 } 1228 offset += _format[i].byteSize; 1229 } 1230 } 1231 1232 void disableAttributes(GraphicsEffect effect) { 1233 for(int i = 0; i < _format.length; i++) { 1234 int loc = effect.getVertexElementLocation(_format[i].type); 1235 if (loc >= 0) { 1236 checkgl!glDisableVertexAttribArray(loc); 1237 } 1238 } 1239 checkgl!glBindVertexArray(0); 1240 checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0); 1241 checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1242 //unbind(); 1243 } 1244 1245 /// set or change data 1246 override void setData(Mesh mesh) { 1247 _format = mesh.vertexFormat; 1248 _indexFragments = mesh.indexFragments; 1249 _vertexCount = mesh.vertexCount; 1250 const(ushort[]) indexData = mesh.indexData; 1251 1252 Log.d("GLVertexBuffer.setData vertex data size=", mesh.vertexData.length, " index data size=", indexData.length, " vertex count=", _vertexCount, " indexBuffer=", _indexBuffer, " vertexBuffer=", _vertexBuffer, " vao=", _vao); 1253 1254 // vertex buffer 1255 checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 1256 checkgl!glBufferData(GL_ARRAY_BUFFER, _format.vertexSize * mesh.vertexCount, mesh.vertexData.ptr, GL_STATIC_DRAW); 1257 checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0); 1258 // index buffer 1259 checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 1260 checkgl!glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData.length * ushort.sizeof, indexData.ptr, GL_STATIC_DRAW); 1261 checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1262 // vertex layout 1263 //checkgl!glBindVertexArray(_vao); 1264 // specify vertex buffer 1265 //checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 1266 // specify index buffer 1267 //checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 1268 1269 //unbind(); 1270 } 1271 1272 /// draw mesh using specified effect 1273 override void draw(GraphicsEffect effect, bool wireframe) { 1274 //bind(); 1275 enableAttributes(effect); 1276 foreach (fragment; _indexFragments) { 1277 if (wireframe && fragment.type == PrimitiveType.triangles) { 1278 // TODO: support wireframe not only for triangles 1279 int triangleCount = fragment.end - fragment.start; 1280 //triangleCount /= 3; 1281 //checkgl!glDisable(GL_CULL_FACE); 1282 for (int i = 0; i < triangleCount; i += 3) { 1283 // GL line loop works strange; use GL_LINES instead 1284 checkgl!glDrawRangeElements(GL_LINE_LOOP, //GL_TRIANGLES, 1285 0, _vertexCount - 1, // The first to last vertex start, end 1286 3, // count of indexes used to draw elements 1287 GL_UNSIGNED_SHORT, 1288 cast(char*)((fragment.start + i) * short.sizeof) // offset from index buffer beginning to fragment start 1289 ); 1290 //checkgl!glDrawRangeElements(GL_LINES, //GL_TRIANGLES, 1291 // 0, _vertexCount - 1, // The first to last vertex start, end 1292 // 2, // count of indexes used to draw elements 1293 // GL_UNSIGNED_SHORT, 1294 // cast(char*)((fragment.start + i + 1) * short.sizeof) // offset from index buffer beginning to fragment start 1295 // ); 1296 } 1297 //checkgl!glEnable(GL_CULL_FACE); 1298 } else { 1299 checkgl!glDrawRangeElements(primitiveTypeToGL(fragment.type), 1300 0, _vertexCount - 1, // The first to last vertex 1301 fragment.end - fragment.start, // count of indexes used to draw elements 1302 GL_UNSIGNED_SHORT, 1303 cast(char*)(fragment.start * short.sizeof) // offset from index buffer beginning to fragment start 1304 ); 1305 } 1306 } 1307 disableAttributes(effect); 1308 //unbind(); 1309 } 1310 } 1311 1312 class DummyVertexBuffer : VertexBuffer { 1313 protected VertexFormat _format; 1314 protected IndexFragment[] _indexFragments; 1315 protected int _vertexCount; 1316 protected const(float)[] _vertexData; 1317 protected const(ushort)[] _indexData; 1318 1319 this() { 1320 } 1321 1322 ~this() { 1323 } 1324 1325 ///// bind into current context 1326 //override void bind() { 1327 //} 1328 // 1329 ///// unbind from current context 1330 //override void unbind() { 1331 //} 1332 1333 /// update vertex element locations for effect/shader program 1334 void enableAttributes(GraphicsEffect effect) { 1335 int offset = 0; 1336 for(int i = 0; i < _format.length; i++) { 1337 int loc = effect.getVertexElementLocation(_format[i].type); 1338 if (loc >= 0) { 1339 checkgl!glVertexAttribPointer(loc, _format[i].size, GL_FLOAT, cast(ubyte)GL_FALSE, _format.vertexSize, cast(char*)(offset)); 1340 checkgl!glEnableVertexAttribArray(loc); 1341 } else { 1342 //Log.d("Attribute location not found for ", _format[i].type); 1343 } 1344 offset += _format[i].byteSize; 1345 } 1346 } 1347 1348 void disableAttributes(GraphicsEffect effect) { 1349 for(int i = 0; i < _format.length; i++) { 1350 int loc = effect.getVertexElementLocation(_format[i].type); 1351 if (loc >= 0) { 1352 checkgl!glDisableVertexAttribArray(loc); 1353 } 1354 } 1355 } 1356 1357 /// set or change data 1358 override void setData(Mesh mesh) { 1359 _format = mesh.vertexFormat; 1360 _indexFragments = mesh.indexFragments; 1361 _vertexCount = mesh.vertexCount; 1362 _vertexData = mesh.vertexData; 1363 _indexData = mesh.indexData; 1364 } 1365 1366 /// draw mesh using specified effect 1367 override void draw(GraphicsEffect effect, bool wireframe) { 1368 //bind(); 1369 enableAttributes(effect); 1370 foreach (fragment; _indexFragments) { 1371 // TODO: support wireframe 1372 checkgl!glDrawRangeElements(primitiveTypeToGL(fragment.type), 1373 0, _vertexCount, 1374 fragment.end - fragment.start, 1375 GL_UNSIGNED_SHORT, cast(char*)(fragment.start * short.sizeof)); 1376 } 1377 disableAttributes(effect); 1378 //unbind(); 1379 } 1380 } 1381 1382 GLenum primitiveTypeToGL(PrimitiveType type) { 1383 switch(type) with (PrimitiveType) { 1384 case triangles: 1385 return GL_TRIANGLES; 1386 case triangleStripes: 1387 return GL_TRIANGLE_STRIP; 1388 case lines: 1389 return GL_LINES; 1390 case lineStripes: 1391 return GL_LINE_STRIP; 1392 case points: 1393 default: 1394 return GL_POINTS; 1395 } 1396 } 1397 1398 1399 1400 /// OpenGL GUI rendering queue. It collects gui draw calls, fills a big buffer for vertex data and draws everything 1401 private final class OpenGLQueue { 1402 1403 /// OpenGL batch structure - to draw several triangles in single OpenGL call 1404 private struct OpenGLBatch { 1405 1406 enum BatchType { Line = 0, Rect, Triangle, TexturedRect } 1407 BatchType type; 1408 1409 Tex2D texture; 1410 int textureDx; 1411 int textureDy; 1412 bool textureLinear; 1413 1414 // length of batch in indices 1415 int length; 1416 // offset in index buffer 1417 int start; 1418 } 1419 1420 import std.array: Appender; 1421 Appender!(OpenGLBatch[]) batches; 1422 // a big buffer 1423 Appender!(float[]) _vertices; 1424 Appender!(float[]) _colors; 1425 Appender!(float[]) _texCoords; 1426 Appender!(int[]) _indices; 1427 1428 /// draw all 1429 void flush() { 1430 glSupport.fillBuffers(_vertices.data, _colors.data, _texCoords.data, _indices.data); 1431 foreach(b; batches.data) { 1432 switch(b.type) with(OpenGLBatch.BatchType) 1433 { 1434 case Line: glSupport.drawLines(b.length, b.start); break; 1435 case Rect: glSupport.drawSolidFillTriangles(b.length, b.start); break; 1436 case Triangle: glSupport.drawSolidFillTriangles(b.length, b.start); break; 1437 case TexturedRect: glSupport.drawColorAndTextureTriangles(b.texture, b.textureLinear, b.length, b.start); break; 1438 default: break; 1439 } 1440 } 1441 //Log.d(batches.length, " ", _vertices.data.length, " ", _colors.data.length, " ", _texCoords.data.length, " ", _indices.data.length); 1442 glSupport.destroyBuffers(); 1443 batches.clear; 1444 _vertices.clear; 1445 _colors.clear; 1446 _texCoords.clear; 1447 _indices.clear; 1448 } 1449 1450 static immutable float Z_2D = -2.0f; 1451 1452 /// add textured rectangle to queue 1453 void addTexturedRect(Tex2D texture, int textureDx, int textureDy, uint color1, uint color2, uint color3, uint color4, Rect srcrc, Rect dstrc, bool linear) { 1454 if (batches.data.length == 0 1455 || batches.data[$-1].type != OpenGLBatch.BatchType.TexturedRect 1456 || batches.data[$-1].texture.ID != texture.ID 1457 || batches.data[$-1].textureLinear != linear) 1458 { 1459 batches ~= OpenGLBatch(); 1460 batches.data[$-1].type = OpenGLBatch.BatchType.TexturedRect; 1461 batches.data[$-1].texture = texture; 1462 batches.data[$-1].textureDx = textureDx; 1463 batches.data[$-1].textureDy = textureDy; 1464 batches.data[$-1].textureLinear = linear; 1465 if(batches.data.length > 1) 1466 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length; 1467 } 1468 1469 uint[4] colorsARGB = [color1, color2, color3, color4]; 1470 float[] colors = convertColors(colorsARGB); 1471 1472 float dstx0 = cast(float)dstrc.left; 1473 float dsty0 = cast(float)(glSupport.currentFBO ? dstrc.top : (glSupport.bufferDy - dstrc.top)); 1474 float dstx1 = cast(float)dstrc.right; 1475 float dsty1 = cast(float)(glSupport.currentFBO ? dstrc.bottom : (glSupport.bufferDy - dstrc.bottom)); 1476 1477 float srcx0 = srcrc.left / cast(float)textureDx; 1478 float srcy0 = srcrc.top / cast(float)textureDy; 1479 float srcx1 = srcrc.right / cast(float)textureDx; 1480 float srcy1 = srcrc.bottom / cast(float)textureDy; 1481 1482 float[3 * 4] vertices = [ 1483 dstx0,dsty0,Z_2D, 1484 dstx0,dsty1,Z_2D, 1485 dstx1,dsty0,Z_2D, 1486 dstx1,dsty1,Z_2D ]; 1487 1488 float[2 * 4] texCoords = [srcx0,srcy0, srcx0,srcy1, srcx1,srcy0, srcx1,srcy1]; 1489 1490 enum verts = 4; 1491 mixin(add); 1492 } 1493 1494 /// add solid rectangle to queue 1495 void addSolidRect(Rect dstRect, uint color) { 1496 addGradientRect(dstRect, color, color, color, color); 1497 } 1498 1499 /// add gradient rectangle to queue 1500 void addGradientRect(Rect rc, uint color1, uint color2, uint color3, uint color4) { 1501 if (batches.data.length == 0 || batches.data[$-1].type != OpenGLBatch.BatchType.Rect) { 1502 batches ~= OpenGLBatch(); 1503 batches.data[$-1].type = OpenGLBatch.BatchType.Rect; 1504 if(batches.data.length > 1) 1505 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length; 1506 } 1507 1508 uint[4] colorsARGB = [color1, color2, color3, color4]; 1509 float[] colors = convertColors(colorsARGB); 1510 1511 float x0 = cast(float)(rc.left); 1512 float y0 = cast(float)(glSupport.currentFBO ? rc.top : (glSupport.bufferDy - rc.top)); 1513 float x1 = cast(float)(rc.right); 1514 float y1 = cast(float)(glSupport.currentFBO ? rc.bottom : (glSupport.bufferDy - rc.bottom)); 1515 1516 float[3 * 4] vertices = [ 1517 x0,y0,Z_2D, 1518 x0,y1,Z_2D, 1519 x1,y0,Z_2D, 1520 x1,y1,Z_2D ]; 1521 // fill texture coords buffer with zeros 1522 float[2 * 4] texCoords = 0; 1523 1524 enum verts = 4; 1525 mixin(add); 1526 } 1527 1528 /// add triangle to queue 1529 void addTriangle(PointF p1, PointF p2, PointF p3, uint color1, uint color2, uint color3) { 1530 if (batches.data.length == 0 || batches.data[$-1].type != OpenGLBatch.BatchType.Triangle) { 1531 batches ~= OpenGLBatch(); 1532 batches.data[$-1].type = OpenGLBatch.BatchType.Triangle; 1533 if(batches.data.length > 1) 1534 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length; 1535 } 1536 1537 uint[3] colorsARGB = [color1, color2, color3]; 1538 float[] colors = convertColors(colorsARGB); 1539 1540 float x0 = p1.x; 1541 float y0 = glSupport.currentFBO ? p1.y : (glSupport.bufferDy - p1.y); 1542 float x1 = p2.x; 1543 float y1 = glSupport.currentFBO ? p2.y : (glSupport.bufferDy - p2.y); 1544 float x2 = p3.x; 1545 float y2 = glSupport.currentFBO ? p3.y : (glSupport.bufferDy - p3.y); 1546 1547 float[3 * 3] vertices = [ 1548 x0,y0,Z_2D, 1549 x1,y1,Z_2D, 1550 x2,y2,Z_2D ]; 1551 // fill texture coords buffer with zeros 1552 float[2 * 3] texCoords = 0; 1553 1554 enum verts = 3; 1555 mixin(add); 1556 } 1557 1558 /// add line to queue 1559 /// rc is a line (left, top) - (right, bottom) 1560 void addLine(Rect rc, uint color1, uint color2) { 1561 if (batches.data.length == 0 || batches.data[$-1].type != OpenGLBatch.BatchType.Line) { 1562 batches ~= OpenGLBatch(); 1563 batches.data[$-1].type = OpenGLBatch.BatchType.Line; 1564 if(batches.data.length > 1) 1565 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length; 1566 } 1567 1568 uint[2] colorsARGB = [color1, color2]; 1569 float[] colors = convertColors(colorsARGB); 1570 1571 float x0 = cast(float)(rc.left); 1572 float y0 = cast(float)(glSupport.currentFBO ? rc.top : (glSupport.bufferDy - rc.top)); 1573 float x1 = cast(float)(rc.right); 1574 float y1 = cast(float)(glSupport.currentFBO ? rc.bottom : (glSupport.bufferDy - rc.bottom)); 1575 1576 float[3 * 2] vertices = [ 1577 x0, y0, Z_2D, 1578 x1, y1, Z_2D ]; 1579 // fill texture coords buffer with zeros 1580 float[2 * 2] texCoords = 0; 1581 1582 enum verts = 2; 1583 mixin(add); 1584 } 1585 1586 enum add = q{ 1587 int offset = cast(int)_vertices.data.length / 3; 1588 static if(verts == 4) { 1589 // make indices for rectangle (2 triangles == 6 vertexes per rect) 1590 int[6] indices = [ 1591 offset + 0, 1592 offset + 1, 1593 offset + 2, 1594 offset + 1, 1595 offset + 2, 1596 offset + 3 ]; 1597 } else 1598 static if(verts == 3) { 1599 // make indices for triangles 1600 int[3] indices = [ 1601 offset + 0, 1602 offset + 1, 1603 offset + 2 ]; 1604 } else 1605 static if(verts == 2) { 1606 // make indices for lines 1607 int[2] indices = [ 1608 offset + 0, 1609 offset + 1 ]; 1610 } else 1611 static assert(0); 1612 1613 batches.data[$-1].length += cast(int)indices.length; 1614 1615 _vertices ~= cast(float[])vertices; 1616 _colors ~= cast(float[])colors; 1617 _texCoords ~= cast(float[])texCoords; 1618 _indices ~= cast(int[])indices; 1619 }; 1620 }