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