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