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