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