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 // TODO: Is this really needed? 941 // checkgl!glFlush(); 942 } 943 944 bool generateMipmap(int dx, int dy, ubyte * pixels, int level, ref ubyte[] dst) { 945 if ((dx & 1) || (dy & 1) || dx < 2 || dy < 2) 946 return false; // size is not even 947 int newdx = dx / 2; 948 int newdy = dy / 2; 949 int newlen = newdx * newdy * 4; 950 if (newlen > dst.length) 951 dst.length = newlen; 952 ubyte * dstptr = dst.ptr; 953 ubyte * srcptr = pixels; 954 int srcstride = dx * 4; 955 for (int y = 0; y < newdy; y++) { 956 for (int x = 0; x < newdx; x++) { 957 dstptr[0] = cast(ubyte)((srcptr[0+0] + srcptr[0+4] + srcptr[0+srcstride] + srcptr[0+srcstride + 4])>>2); 958 dstptr[1] = cast(ubyte)((srcptr[1+0] + srcptr[1+4] + srcptr[1+srcstride] + srcptr[1+srcstride + 4])>>2); 959 dstptr[2] = cast(ubyte)((srcptr[2+0] + srcptr[2+4] + srcptr[2+srcstride] + srcptr[2+srcstride + 4])>>2); 960 dstptr[3] = cast(ubyte)((srcptr[3+0] + srcptr[3+4] + srcptr[3+srcstride] + srcptr[3+srcstride + 4])>>2); 961 dstptr += 4; 962 srcptr += 8; 963 } 964 srcptr += srcstride; // skip srcline 965 } 966 checkgl!glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, newdx, newdy, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst.ptr); 967 return true; 968 } 969 970 bool setTextureImage(Tex2D texture, int dx, int dy, ubyte * pixels, int mipmapLevels = 0) { 971 checkError("before setTextureImage"); 972 texture.bind(); 973 checkgl!glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 974 texture.setSamplerParams(true, true); 975 976 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); 977 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmapLevels > 0 ? mipmapLevels - 1 : 0); 978 // ORIGINAL: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 979 checkgl!glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 980 if (checkError("updateTexture - glTexImage2D")) { 981 Log.e("Cannot set image for texture"); 982 return false; 983 } 984 if (mipmapLevels > 1) { 985 ubyte[] buffer; 986 ubyte * src = pixels; 987 int ndx = dx; 988 int ndy = dy; 989 for (int i = 1; i < mipmapLevels; i++) { 990 if (!generateMipmap(ndx, ndy, src, i, buffer)) 991 break; 992 ndx /= 2; 993 ndy /= 2; 994 src = buffer.ptr; 995 } 996 } 997 texture.unbind(); 998 return true; 999 } 1000 1001 bool setTextureImageAlpha(Tex2D texture, int dx, int dy, ubyte * pixels) { 1002 checkError("before setTextureImageAlpha"); 1003 texture.bind(); 1004 checkgl!glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 1005 texture.setSamplerParams(true, true); 1006 1007 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, dx, dy, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels); 1008 if (checkError("setTextureImageAlpha - glTexImage2D")) { 1009 Log.e("Cannot set image for texture"); 1010 return false; 1011 } 1012 texture.unbind(); 1013 return true; 1014 } 1015 1016 private FBO currentFBO; 1017 1018 /// returns texture for buffer, null if failed 1019 bool createFramebuffer(out Tex2D texture, out FBO fbo, int dx, int dy) { 1020 checkError("before createFramebuffer"); 1021 bool res = true; 1022 texture = new Tex2D(); 1023 if (!texture.ID) 1024 return false; 1025 checkError("glBindTexture GL_TEXTURE_2D"); 1026 FBO f = new FBO(); 1027 if (!f.ID) 1028 return false; 1029 fbo = f; 1030 1031 checkgl!glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dx, dy, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, null); 1032 1033 texture.setSamplerParams(true, true); 1034 1035 checkgl!glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.ID, 0); 1036 // Always check that our framebuffer is ok 1037 if(checkgl!glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 1038 Log.e("glFramebufferTexture2D failed"); 1039 res = false; 1040 } 1041 checkgl!glClearColor(0.5f, 0.5f, 0.5f, 1.0f); 1042 checkgl!glClear(GL_COLOR_BUFFER_BIT); 1043 currentFBO = fbo; 1044 1045 texture.unbind(); 1046 fbo.unbind(); 1047 1048 return res; 1049 } 1050 1051 void deleteFramebuffer(ref FBO fbo) { 1052 if (fbo.ID != 0) { 1053 destroy(fbo); 1054 } 1055 currentFBO = null; 1056 } 1057 1058 bool bindFramebuffer(FBO fbo) { 1059 fbo.bind(); 1060 currentFBO = fbo; 1061 return !checkError("glBindFramebuffer"); 1062 } 1063 1064 void clearDepthBuffer() { 1065 glClear(GL_DEPTH_BUFFER_BIT); 1066 //glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 1067 } 1068 1069 /// projection matrix 1070 /// current gl buffer width 1071 private int bufferDx; 1072 /// current gl buffer height 1073 private int bufferDy; 1074 private mat4 _projectionMatrix; 1075 1076 @property ref mat4 projectionMatrix() { 1077 return _projectionMatrix; 1078 } 1079 1080 void setOrthoProjection(Rect windowRect, Rect view) { 1081 flushGL(); 1082 bufferDx = windowRect.width; 1083 bufferDy = windowRect.height; 1084 _projectionMatrix.setOrtho(view.left, view.right, view.top, view.bottom, 0.5f, 50.0f); 1085 1086 if (_legacyMode) { 1087 static if (SUPPORT_LEGACY_OPENGL) { 1088 glMatrixMode(GL_PROJECTION); 1089 //checkgl!glPushMatrix(); 1090 //glLoadIdentity(); 1091 glLoadMatrixf(_projectionMatrix.m.ptr); 1092 //glOrthof(0, _dx, 0, _dy, -1.0f, 1.0f); 1093 glMatrixMode(GL_MODELVIEW); 1094 //checkgl!glPushMatrix(); 1095 glLoadIdentity(); 1096 } 1097 } 1098 checkgl!glViewport(view.left, currentFBO ? view.top : windowRect.height - view.bottom, view.width, view.height); 1099 } 1100 1101 void setPerspectiveProjection(Rect windowRect, Rect view, float fieldOfView, float nearPlane, float farPlane) { 1102 flushGL(); 1103 bufferDx = windowRect.width; 1104 bufferDy = windowRect.height; 1105 float aspectRatio = cast(float)view.width / cast(float)view.height; 1106 _projectionMatrix.setPerspective(fieldOfView, aspectRatio, nearPlane, farPlane); 1107 if (_legacyMode) { 1108 static if (SUPPORT_LEGACY_OPENGL) { 1109 glMatrixMode(GL_PROJECTION); 1110 //checkgl!glPushMatrix(); 1111 //glLoadIdentity(); 1112 glLoadMatrixf(_projectionMatrix.m.ptr); 1113 //glOrthof(0, _dx, 0, _dy, -1.0f, 1.0f); 1114 glMatrixMode(GL_MODELVIEW); 1115 //checkgl!glPushMatrix(); 1116 glLoadIdentity(); 1117 } 1118 } 1119 checkgl!glViewport(view.left, currentFBO ? view.top : windowRect.height - view.bottom, view.width, view.height); 1120 } 1121 } 1122 1123 enum GLObjectTypes { Buffer, VertexArray, Texture, Framebuffer }; 1124 /** RAII OpenGL object template. 1125 * Note: on construction it binds itself to the target, and it binds 0 to target on destruction. 1126 * All methods (except ctor, dtor, bind(), unbind() and setup()) does not perform binding. 1127 */ 1128 1129 1130 class GLObject(GLObjectTypes type, GLuint target = 0) { 1131 immutable GLuint ID; 1132 //alias ID this; // good, but it confuses destroy() 1133 1134 this() { 1135 GLuint handle; 1136 mixin("checkgl!glGen" ~ to!string(type) ~ "s(1, &handle);"); 1137 ID = handle; 1138 bind(); 1139 } 1140 1141 ~this() { 1142 if (!glNoContext) { 1143 unbind(); 1144 mixin("checkgl!glDelete" ~ to!string(type) ~ "s(1, &ID);"); 1145 } 1146 } 1147 1148 void bind() { 1149 static if(target != 0) 1150 mixin("glBind" ~ to!string(type) ~ "(" ~ to!string(target) ~ ", ID);"); 1151 else 1152 mixin("glBind" ~ to!string(type) ~ "(ID);"); 1153 } 1154 1155 static void unbind() { 1156 static if(target != 0) 1157 mixin("checkgl!glBind" ~ to!string(type) ~ "(" ~ to!string(target) ~ ", 0);"); 1158 else 1159 mixin("checkgl!glBind" ~ to!string(type) ~ "(0);"); 1160 } 1161 1162 static if(type == GLObjectTypes.Buffer) 1163 { 1164 void fill(float[][] buffs) { 1165 int length; 1166 foreach(b; buffs) 1167 length += b.length; 1168 checkgl!glBufferData(target, 1169 length * float.sizeof, 1170 null, 1171 GL_STATIC_DRAW); 1172 int offset; 1173 foreach(b; buffs) { 1174 checkgl!glBufferSubData(target, 1175 offset, 1176 b.length * float.sizeof, 1177 b.ptr); 1178 offset += b.length * float.sizeof; 1179 } 1180 } 1181 1182 static if (target == GL_ELEMENT_ARRAY_BUFFER) { 1183 void fill(int[] indexes) { 1184 checkgl!glBufferData(target, cast(int)(indexes.length * int.sizeof), indexes.ptr, GL_STATIC_DRAW); 1185 } 1186 } 1187 } 1188 1189 static if(type == GLObjectTypes.Texture) 1190 { 1191 void setSamplerParams(bool linear, bool clamp = false, bool mipmap = false) { 1192 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST); 1193 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, linear ? 1194 (!mipmap ? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR) : 1195 (!mipmap ? GL_NEAREST : GL_NEAREST_MIPMAP_NEAREST)); //GL_NEAREST_MIPMAP_NEAREST 1196 checkError("filtering - glTexParameteri"); 1197 if(clamp) { 1198 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 1199 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 1200 checkError("clamp - glTexParameteri"); 1201 } 1202 } 1203 1204 void setup(GLuint binding = 0) { 1205 glActiveTexture(GL_TEXTURE0 + binding); 1206 glBindTexture(target, ID); 1207 checkError("setup texture"); 1208 } 1209 } 1210 } 1211 1212 alias VAO = GLObject!(GLObjectTypes.VertexArray); 1213 alias VBO = GLObject!(GLObjectTypes.Buffer, GL_ARRAY_BUFFER); 1214 alias EBO = GLObject!(GLObjectTypes.Buffer, GL_ELEMENT_ARRAY_BUFFER); 1215 alias Tex2D = GLObject!(GLObjectTypes.Texture, GL_TEXTURE_2D); 1216 alias FBO = GLObject!(GLObjectTypes.Framebuffer, GL_FRAMEBUFFER); 1217 1218 class GLVertexBuffer : VertexBuffer { 1219 protected VertexFormat _format; 1220 protected IndexFragment[] _indexFragments; 1221 protected int _vertexCount; 1222 protected GLuint _vertexBuffer; 1223 protected GLuint _indexBuffer; 1224 protected GLuint _vao; 1225 1226 this() { 1227 version (Android) { 1228 checkgl!glGenBuffers(1, &_vertexBuffer); 1229 checkgl!glGenBuffers(1, &_indexBuffer); 1230 checkgl!glGenVertexArrays(1, &_vao); 1231 } else { 1232 assertgl!glGenBuffers(1, &_vertexBuffer); 1233 assertgl!glGenBuffers(1, &_indexBuffer); 1234 assertgl!glGenVertexArrays(1, &_vao); 1235 } 1236 } 1237 1238 ~this() { 1239 checkgl!glDeleteBuffers(1, &_vertexBuffer); 1240 checkgl!glDeleteBuffers(1, &_indexBuffer); 1241 checkgl!glDeleteVertexArrays(1, &_vao); 1242 } 1243 1244 ///// bind into current context 1245 //override void bind() { 1246 // checkgl!glBindVertexArray(_vao); 1247 // 1248 // // TODO: is it necessary to bind vertex/index buffers? 1249 // // specify vertex buffer 1250 // checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 1251 // // specify index buffer 1252 // checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 1253 //} 1254 // 1255 ///// unbind from current context 1256 //override void unbind() { 1257 // checkgl!glBindVertexArray(0); 1258 // checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0); 1259 // checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1260 //} 1261 1262 /// update vertex element locations for effect/shader program 1263 void enableAttributes(GraphicsEffect effect) { 1264 checkgl!glBindVertexArray(_vao); 1265 // specify vertex buffer 1266 checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 1267 // specify index buffer 1268 checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 1269 int offset = 0; 1270 //Log.v("=== enableAttributes for ", _format); 1271 for(int i = 0; i < _format.length; i++) { 1272 int loc = effect.getVertexElementLocation(_format[i].type); 1273 if (loc >= 0) { 1274 //Log.v("setting attrib pointer for type ", _format[i].type, " offset=", offset, " location=", loc); 1275 checkgl!glVertexAttribPointer(loc, _format[i].size, GL_FLOAT, cast(ubyte)GL_FALSE, _format.vertexSize, cast(char*)(offset)); 1276 checkgl!glEnableVertexAttribArray(loc); 1277 } else { 1278 //Log.v("Attribute location not found for ", _format[i].type); 1279 } 1280 offset += _format[i].byteSize; 1281 } 1282 } 1283 1284 void disableAttributes(GraphicsEffect effect) { 1285 for(int i = 0; i < _format.length; i++) { 1286 int loc = effect.getVertexElementLocation(_format[i].type); 1287 if (loc >= 0) { 1288 checkgl!glDisableVertexAttribArray(loc); 1289 } 1290 } 1291 checkgl!glBindVertexArray(0); 1292 checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0); 1293 checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1294 //unbind(); 1295 } 1296 1297 /// set or change data 1298 override void setData(Mesh mesh) { 1299 _format = mesh.vertexFormat; 1300 _indexFragments = mesh.indexFragments; 1301 _vertexCount = mesh.vertexCount; 1302 const(ushort[]) indexData = mesh.indexData; 1303 1304 Log.d("GLVertexBuffer.setData vertex data size=", mesh.vertexData.length, " index data size=", indexData.length, " vertex count=", _vertexCount, " indexBuffer=", _indexBuffer, " vertexBuffer=", _vertexBuffer, " vao=", _vao); 1305 1306 // vertex buffer 1307 checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 1308 checkgl!glBufferData(GL_ARRAY_BUFFER, _format.vertexSize * mesh.vertexCount, mesh.vertexData.ptr, GL_STATIC_DRAW); 1309 checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0); 1310 // index buffer 1311 checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 1312 checkgl!glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData.length * ushort.sizeof, indexData.ptr, GL_STATIC_DRAW); 1313 checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1314 // vertex layout 1315 //checkgl!glBindVertexArray(_vao); 1316 // specify vertex buffer 1317 //checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 1318 // specify index buffer 1319 //checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 1320 1321 //unbind(); 1322 } 1323 1324 /// draw mesh using specified effect 1325 override void draw(GraphicsEffect effect, bool wireframe) { 1326 //bind(); 1327 enableAttributes(effect); 1328 foreach (fragment; _indexFragments) { 1329 if (wireframe && fragment.type == PrimitiveType.triangles) { 1330 // TODO: support wireframe not only for triangles 1331 int triangleCount = fragment.end - fragment.start; 1332 //triangleCount /= 3; 1333 //checkgl!glDisable(GL_CULL_FACE); 1334 for (int i = 0; i < triangleCount; i += 3) { 1335 // GL line loop works strange; use GL_LINES instead 1336 checkgl!glDrawRangeElements(GL_LINE_LOOP, //GL_TRIANGLES, 1337 0, _vertexCount - 1, // The first to last vertex start, end 1338 3, // count of indexes used to draw elements 1339 GL_UNSIGNED_SHORT, 1340 cast(char*)((fragment.start + i) * short.sizeof) // offset from index buffer beginning to fragment start 1341 ); 1342 //checkgl!glDrawRangeElements(GL_LINES, //GL_TRIANGLES, 1343 // 0, _vertexCount - 1, // The first to last vertex start, end 1344 // 2, // count of indexes used to draw elements 1345 // GL_UNSIGNED_SHORT, 1346 // cast(char*)((fragment.start + i + 1) * short.sizeof) // offset from index buffer beginning to fragment start 1347 // ); 1348 } 1349 //checkgl!glEnable(GL_CULL_FACE); 1350 } else { 1351 checkgl!glDrawRangeElements(primitiveTypeToGL(fragment.type), 1352 0, _vertexCount - 1, // The first to last vertex 1353 fragment.end - fragment.start, // count of indexes used to draw elements 1354 GL_UNSIGNED_SHORT, 1355 cast(char*)(fragment.start * short.sizeof) // offset from index buffer beginning to fragment start 1356 ); 1357 } 1358 } 1359 disableAttributes(effect); 1360 //unbind(); 1361 } 1362 } 1363 1364 class DummyVertexBuffer : VertexBuffer { 1365 protected VertexFormat _format; 1366 protected IndexFragment[] _indexFragments; 1367 protected int _vertexCount; 1368 protected const(float)[] _vertexData; 1369 protected const(ushort)[] _indexData; 1370 1371 this() { 1372 } 1373 1374 ~this() { 1375 } 1376 1377 ///// bind into current context 1378 //override void bind() { 1379 //} 1380 // 1381 ///// unbind from current context 1382 //override void unbind() { 1383 //} 1384 1385 /// update vertex element locations for effect/shader program 1386 void enableAttributes(GraphicsEffect effect) { 1387 int offset = 0; 1388 for(int i = 0; i < _format.length; i++) { 1389 int loc = effect.getVertexElementLocation(_format[i].type); 1390 if (loc >= 0) { 1391 checkgl!glVertexAttribPointer(loc, _format[i].size, GL_FLOAT, cast(ubyte)GL_FALSE, _format.vertexSize, cast(char*)(offset)); 1392 checkgl!glEnableVertexAttribArray(loc); 1393 } else { 1394 //Log.d("Attribute location not found for ", _format[i].type); 1395 } 1396 offset += _format[i].byteSize; 1397 } 1398 } 1399 1400 void disableAttributes(GraphicsEffect effect) { 1401 for(int i = 0; i < _format.length; i++) { 1402 int loc = effect.getVertexElementLocation(_format[i].type); 1403 if (loc >= 0) { 1404 checkgl!glDisableVertexAttribArray(loc); 1405 } 1406 } 1407 } 1408 1409 /// set or change data 1410 override void setData(Mesh mesh) { 1411 _format = mesh.vertexFormat; 1412 _indexFragments = mesh.indexFragments; 1413 _vertexCount = mesh.vertexCount; 1414 _vertexData = mesh.vertexData; 1415 _indexData = mesh.indexData; 1416 } 1417 1418 /// draw mesh using specified effect 1419 override void draw(GraphicsEffect effect, bool wireframe) { 1420 //bind(); 1421 enableAttributes(effect); 1422 foreach (fragment; _indexFragments) { 1423 // TODO: support wireframe 1424 checkgl!glDrawRangeElements(primitiveTypeToGL(fragment.type), 1425 0, _vertexCount, 1426 fragment.end - fragment.start, 1427 GL_UNSIGNED_SHORT, cast(char*)(fragment.start * short.sizeof)); 1428 } 1429 disableAttributes(effect); 1430 //unbind(); 1431 } 1432 } 1433 1434 GLenum primitiveTypeToGL(PrimitiveType type) { 1435 switch(type) with (PrimitiveType) { 1436 case triangles: 1437 return GL_TRIANGLES; 1438 case triangleStripes: 1439 return GL_TRIANGLE_STRIP; 1440 case lines: 1441 return GL_LINES; 1442 case lineStripes: 1443 return GL_LINE_STRIP; 1444 case points: 1445 default: 1446 return GL_POINTS; 1447 } 1448 } 1449 1450 1451 1452 /// OpenGL GUI rendering queue. It collects gui draw calls, fills a big buffer for vertex data and draws everything 1453 private final class OpenGLQueue { 1454 1455 /// OpenGL batch structure - to draw several triangles in single OpenGL call 1456 private struct OpenGLBatch { 1457 1458 enum BatchType { Line = 0, Rect, Triangle, TexturedRect } 1459 BatchType type; 1460 1461 Tex2D texture; 1462 int textureDx; 1463 int textureDy; 1464 bool textureLinear; 1465 1466 // length of batch in indices 1467 int length; 1468 // offset in index buffer 1469 int start; 1470 } 1471 1472 import std.array: Appender; 1473 Appender!(OpenGLBatch[]) batches; 1474 // a big buffer 1475 Appender!(float[]) _vertices; 1476 Appender!(float[]) _colors; 1477 Appender!(float[]) _texCoords; 1478 Appender!(int[]) _indices; 1479 1480 /// draw all 1481 void flush() { 1482 glSupport.fillBuffers(_vertices.data, _colors.data, _texCoords.data, _indices.data); 1483 foreach(b; batches.data) { 1484 switch(b.type) with(OpenGLBatch.BatchType) 1485 { 1486 case Line: glSupport.drawLines(b.length, b.start); break; 1487 case Rect: glSupport.drawSolidFillTriangles(b.length, b.start); break; 1488 case Triangle: glSupport.drawSolidFillTriangles(b.length, b.start); break; 1489 case TexturedRect: glSupport.drawColorAndTextureTriangles(b.texture, b.textureLinear, b.length, b.start); break; 1490 default: break; 1491 } 1492 } 1493 //Log.d(batches.length, " ", _vertices.data.length, " ", _colors.data.length, " ", _texCoords.data.length, " ", _indices.data.length); 1494 glSupport.destroyBuffers(); 1495 batches.clear; 1496 _vertices.clear; 1497 _colors.clear; 1498 _texCoords.clear; 1499 _indices.clear; 1500 } 1501 1502 static immutable float Z_2D = -2.0f; 1503 1504 /// add textured rectangle to queue 1505 void addTexturedRect(Tex2D texture, int textureDx, int textureDy, uint color1, uint color2, uint color3, uint color4, Rect srcrc, Rect dstrc, bool linear) { 1506 if (batches.data.length == 0 1507 || batches.data[$-1].type != OpenGLBatch.BatchType.TexturedRect 1508 || batches.data[$-1].texture.ID != texture.ID 1509 || batches.data[$-1].textureLinear != linear) 1510 { 1511 batches ~= OpenGLBatch(); 1512 batches.data[$-1].type = OpenGLBatch.BatchType.TexturedRect; 1513 batches.data[$-1].texture = texture; 1514 batches.data[$-1].textureDx = textureDx; 1515 batches.data[$-1].textureDy = textureDy; 1516 batches.data[$-1].textureLinear = linear; 1517 if(batches.data.length > 1) 1518 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length; 1519 } 1520 1521 uint[4] colorsARGB = [color1, color2, color3, color4]; 1522 float[] colors = convertColors(colorsARGB); 1523 1524 float dstx0 = cast(float)dstrc.left; 1525 float dsty0 = cast(float)(glSupport.currentFBO ? dstrc.top : (glSupport.bufferDy - dstrc.top)); 1526 float dstx1 = cast(float)dstrc.right; 1527 float dsty1 = cast(float)(glSupport.currentFBO ? dstrc.bottom : (glSupport.bufferDy - dstrc.bottom)); 1528 1529 float srcx0 = srcrc.left / cast(float)textureDx; 1530 float srcy0 = srcrc.top / cast(float)textureDy; 1531 float srcx1 = srcrc.right / cast(float)textureDx; 1532 float srcy1 = srcrc.bottom / cast(float)textureDy; 1533 1534 float[3 * 4] vertices = [ 1535 dstx0,dsty0,Z_2D, 1536 dstx0,dsty1,Z_2D, 1537 dstx1,dsty0,Z_2D, 1538 dstx1,dsty1,Z_2D ]; 1539 1540 float[2 * 4] texCoords = [srcx0,srcy0, srcx0,srcy1, srcx1,srcy0, srcx1,srcy1]; 1541 1542 enum verts = 4; 1543 mixin(add); 1544 } 1545 1546 /// add solid rectangle to queue 1547 void addSolidRect(Rect dstRect, uint color) { 1548 addGradientRect(dstRect, color, color, color, color); 1549 } 1550 1551 /// add gradient rectangle to queue 1552 void addGradientRect(Rect rc, uint color1, uint color2, uint color3, uint color4) { 1553 if (batches.data.length == 0 || batches.data[$-1].type != OpenGLBatch.BatchType.Rect) { 1554 batches ~= OpenGLBatch(); 1555 batches.data[$-1].type = OpenGLBatch.BatchType.Rect; 1556 if(batches.data.length > 1) 1557 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length; 1558 } 1559 1560 uint[4] colorsARGB = [color1, color2, color3, color4]; 1561 float[] colors = convertColors(colorsARGB); 1562 1563 float x0 = cast(float)(rc.left); 1564 float y0 = cast(float)(glSupport.currentFBO ? rc.top : (glSupport.bufferDy - rc.top)); 1565 float x1 = cast(float)(rc.right); 1566 float y1 = cast(float)(glSupport.currentFBO ? rc.bottom : (glSupport.bufferDy - rc.bottom)); 1567 1568 float[3 * 4] vertices = [ 1569 x0,y0,Z_2D, 1570 x0,y1,Z_2D, 1571 x1,y0,Z_2D, 1572 x1,y1,Z_2D ]; 1573 // fill texture coords buffer with zeros 1574 float[2 * 4] texCoords = 0; 1575 1576 enum verts = 4; 1577 mixin(add); 1578 } 1579 1580 /// add triangle to queue 1581 void addTriangle(PointF p1, PointF p2, PointF p3, uint color1, uint color2, uint color3) { 1582 if (batches.data.length == 0 || batches.data[$-1].type != OpenGLBatch.BatchType.Triangle) { 1583 batches ~= OpenGLBatch(); 1584 batches.data[$-1].type = OpenGLBatch.BatchType.Triangle; 1585 if(batches.data.length > 1) 1586 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length; 1587 } 1588 1589 uint[3] colorsARGB = [color1, color2, color3]; 1590 float[] colors = convertColors(colorsARGB); 1591 1592 float x0 = p1.x; 1593 float y0 = glSupport.currentFBO ? p1.y : (glSupport.bufferDy - p1.y); 1594 float x1 = p2.x; 1595 float y1 = glSupport.currentFBO ? p2.y : (glSupport.bufferDy - p2.y); 1596 float x2 = p3.x; 1597 float y2 = glSupport.currentFBO ? p3.y : (glSupport.bufferDy - p3.y); 1598 1599 float[3 * 3] vertices = [ 1600 x0,y0,Z_2D, 1601 x1,y1,Z_2D, 1602 x2,y2,Z_2D ]; 1603 // fill texture coords buffer with zeros 1604 float[2 * 3] texCoords = 0; 1605 1606 enum verts = 3; 1607 mixin(add); 1608 } 1609 1610 /// add line to queue 1611 /// rc is a line (left, top) - (right, bottom) 1612 void addLine(Rect rc, uint color1, uint color2) { 1613 if (batches.data.length == 0 || batches.data[$-1].type != OpenGLBatch.BatchType.Line) { 1614 batches ~= OpenGLBatch(); 1615 batches.data[$-1].type = OpenGLBatch.BatchType.Line; 1616 if(batches.data.length > 1) 1617 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length; 1618 } 1619 1620 uint[2] colorsARGB = [color1, color2]; 1621 float[] colors = convertColors(colorsARGB); 1622 1623 float x0 = cast(float)(rc.left); 1624 float y0 = cast(float)(glSupport.currentFBO ? rc.top : (glSupport.bufferDy - rc.top)); 1625 float x1 = cast(float)(rc.right); 1626 float y1 = cast(float)(glSupport.currentFBO ? rc.bottom : (glSupport.bufferDy - rc.bottom)); 1627 1628 float[3 * 2] vertices = [ 1629 x0, y0, Z_2D, 1630 x1, y1, Z_2D ]; 1631 // fill texture coords buffer with zeros 1632 float[2 * 2] texCoords = 0; 1633 1634 enum verts = 2; 1635 mixin(add); 1636 } 1637 1638 enum add = q{ 1639 int offset = cast(int)_vertices.data.length / 3; 1640 static if(verts == 4) { 1641 // make indices for rectangle (2 triangles == 6 vertexes per rect) 1642 int[6] indices = [ 1643 offset + 0, 1644 offset + 1, 1645 offset + 2, 1646 offset + 1, 1647 offset + 2, 1648 offset + 3 ]; 1649 } else 1650 static if(verts == 3) { 1651 // make indices for triangles 1652 int[3] indices = [ 1653 offset + 0, 1654 offset + 1, 1655 offset + 2 ]; 1656 } else 1657 static if(verts == 2) { 1658 // make indices for lines 1659 int[2] indices = [ 1660 offset + 0, 1661 offset + 1 ]; 1662 } else 1663 static assert(0); 1664 1665 batches.data[$-1].length += cast(int)indices.length; 1666 1667 _vertices ~= cast(float[])vertices; 1668 _colors ~= cast(float[])colors; 1669 _texCoords ~= cast(float[])texCoords; 1670 _indices ~= cast(int[])indices; 1671 }; 1672 } 1673 }