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 }