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 }