1 module openglexample;
2 
3 import dlangui;
4 
5 mixin APP_ENTRY_POINT;
6 
7 /// entry point for dlangui based application
8 extern (C) int UIAppMain(string[] args) {
9     // embed resources listed in views/resources.list into executable
10     embeddedResourceList.addResources(embedResourcesFromList!("resources.list")());
11 
12     // the old API example works on old context because of OpenGL deprecation model
13     // otherwise there will be GL_INVALID_OPERATION error
14     // new API functions on a modern graphics card will work even if you set context version to 1.0
15     Platform.instance.GLVersionMajor = 2;
16     Platform.instance.GLVersionMinor = 1;
17 
18     // create window
19     Window window = Platform.instance.createWindow("DlangUI OpenGL Example", null, WindowFlag.Resizable, 800, 700);
20 
21     static if (ENABLE_OPENGL) {
22         window.mainWidget = new MyOpenglWidget();
23     } else {
24         window.mainWidget = new TextWidget(null, "DlangUI is built with OpenGL support disabled"d);
25     }
26 
27     window.windowIcon = drawableCache.getImage("dlangui-logo1");
28 
29     window.show();
30     // run message loop
31     return Platform.instance.enterMessageLoop();
32 }
33 
34 static if (ENABLE_OPENGL):
35 
36 import dlangui.graphics.glsupport;
37 import dlangui.graphics.gldrawbuf;
38 
39 class MyOpenglWidget : VerticalLayout {
40     this() {
41         super("OpenGLView");
42         layoutWidth = FILL_PARENT;
43         layoutHeight = FILL_PARENT;
44         alignment = Align.Center;
45         // add some UI on top of OpenGL drawable
46         Widget w = parseML(q{
47             VerticalLayout {
48                 alignment: center
49                 layoutWidth: fill; layoutHeight: fill
50                 // background for window - tiled texture
51                 backgroundImageId: "tx_fabric.tiled"
52                 VerticalLayout {
53                     // child widget - will draw using OpenGL here
54                     id: glView
55                     margins: 20
56                     padding: 20
57                     layoutWidth: fill; layoutHeight: fill
58 
59                     TextWidget { text: "DlangUI OpenGL custom drawable example"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "Arial" }
60 
61                     TextWidget { text: "Choose OpenGL drawable:" }
62                     VerticalLayout {
63                         RadioButton { id: rbExample1; text: "Shaders based example - Cube"; checked: true }
64                         RadioButton { id: rbExample2; text: "Legacy OpenGL API example - glxGears" }
65                     }
66 
67                     TextWidget { text: "Some controls to draw on top of OpenGL scene"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "Arial" }
68 
69                     // arrange controls as form - table with two columns
70                     TableLayout {
71                         colCount: 2
72                         TextWidget { text: "param 1" }
73                         EditLine { id: edit1; text: "some text" }
74                         TextWidget { text: "param 2" }
75                         EditLine { id: edit2; text: "some text for param2" }
76                         TextWidget { text: "some radio buttons" }
77                         // arrange some radio buttons vertically
78                         VerticalLayout {
79                             RadioButton { id: rb1; text: "Item 1" }
80                             RadioButton { id: rb2; text: "Item 2" }
81                             RadioButton { id: rb3; text: "Item 3" }
82                         }
83                         TextWidget { text: "and checkboxes" }
84                         // arrange some checkboxes horizontally
85                         HorizontalLayout {
86                             CheckBox { id: cb1; text: "checkbox 1" }
87                             CheckBox { id: cb2; text: "checkbox 2" }
88                         }
89                     }
90                     VSpacer { layoutWeight: 10 }
91                     HorizontalLayout {
92                         Button { id: btnOk; text: "Ok" }
93                         Button { id: btnCancel; text: "Cancel" }
94                     }
95                 }
96             }
97         });
98         // assign OpenGL drawable to child widget background
99         w.childById("glView").backgroundDrawable = DrawableRef(new OpenGLDrawable(&doDraw));
100 
101         w.childById("rbExample1").click = delegate(Widget w) {
102             _exampleIndex = 0; // new API
103             return true;
104         };
105         w.childById("rbExample2").click = delegate(Widget w) {
106             _exampleIndex = 1; // old API
107             return true;
108         };
109 
110         addChild(w);
111     }
112 
113     int _exampleIndex = 0;
114 
115     /// returns true is widget is being animated - need to call animate() and redraw
116     @property override bool animating() { return true; }
117     /// animates window; interval is time left from previous draw, in hnsecs (1/10000000 of second)
118     override void animate(long interval) {
119         if (_exampleIndex == 1) {
120             // animate legacy API example
121             // rotate gears
122             angle += interval * 0.000002f;
123         } else {
124             // TODO: some other animation for new API example
125             angle += interval * 0.000002f;
126         }
127         invalidate();
128     }
129 
130     /// this is OpenGLDrawableDelegate implementation
131     private void doDraw(Rect windowRect, Rect rc) {
132         if (!openglEnabled) {
133             Log.v("GlGears: OpenGL is disabled");
134             return;
135         }
136         bool canUseOldApi = !!glLightfv;
137         bool canUseNewApi = !glSupport.legacyMode;
138         if (_exampleIndex == 0 || !canUseOldApi)
139             drawUsingNewAPI(windowRect, rc);
140         else if (_exampleIndex == 1 || !canUseNewApi)
141             drawUsingOldAPI(windowRect, rc);
142     }
143 
144     /// Legacy API example (glBegin/glEnd) - glxGears
145     void drawUsingOldAPI(Rect windowRect, Rect rc) {
146         static bool _initCalled;
147         if (!_initCalled) {
148             Log.d("GlGears: calling init()");
149             _initCalled = true;
150             glxgears_init();
151         }
152         glxgears_reshape(rc);
153         glEnable(GL_LIGHTING);
154         glEnable(GL_LIGHT0);
155         glEnable(GL_DEPTH_TEST);
156         glxgears_draw();
157         glDisable(GL_LIGHTING);
158         glDisable(GL_LIGHT0);
159         glDisable(GL_DEPTH_TEST);
160     }
161 
162     ~this() {
163         if (_program)
164             destroy(_program);
165         if (_vao)
166             destroy(_vao);
167         if (_vbo)
168             destroy(_vbo);
169         if (_tx)
170             destroy(_tx);
171     }
172 
173     MyGLProgram _program;
174     GLTexture _tx;
175     VAO _vao;
176     VBO _vbo;
177 
178     /// New API example (OpenGL3+, shaders)
179     void drawUsingNewAPI(Rect windowRect, Rect rc) {
180         if (!_program) {
181             _program = new MyGLProgram;
182             _program.compile();
183             createMesh();
184             auto buf = _program.createBuffers(vertices, colors, texcoords);
185             _vao = buf[0];
186             _vbo = buf[1];
187         }
188         if (!_program.check())
189             return;
190         if (!_tx.isValid) {
191             Log.e("Invalid texture");
192             return;
193         }
194 
195         checkgl!glEnable(GL_CULL_FACE);
196         checkgl!glEnable(GL_DEPTH_TEST);
197         checkgl!glCullFace(GL_BACK);
198 
199         // ======== Projection Matrix ==================
200         mat4 projectionMatrix;
201         float aspectRatio = cast(float)rc.width / cast(float)rc.height;
202         projectionMatrix.setPerspective(45.0f, aspectRatio, 0.1f, 100.0f);
203 
204         // ======== View Matrix ==================
205         mat4 viewMatrix;
206         viewMatrix.translate(0, 0, -6);
207         viewMatrix.rotatex(-15.0f);
208         //viewMatrix.lookAt(vec3(-10, 0, 0), vec3(0, 0, 0), vec3(0, 1, 0));//translation(0.0f, 0.0f, 4.0f).rotatez(angle);
209 
210         // ======== Model Matrix ==================
211         mat4 modelMatrix;
212         modelMatrix.scale(1.5f);
213         modelMatrix.rotatez(30.0f + angle * 0.3456778);
214         modelMatrix.rotatey(angle);
215         modelMatrix.rotatez(angle * 1.98765f);
216 
217         // ======= PMV matrix =====================
218         mat4 projectionViewModelMatrix = projectionMatrix * viewMatrix * modelMatrix;
219 
220         _program.execute(_vao, cast(int)vertices.length / 3, _tx.texture, true, projectionViewModelMatrix.m);
221 
222         checkgl!glDisable(GL_CULL_FACE);
223         checkgl!glDisable(GL_DEPTH_TEST);
224     }
225 
226     // Cube mesh
227     float[] vertices;
228     float[] texcoords;
229     float[4*6*6] colors;
230     void createMesh() {
231         if (!_tx)
232             _tx = new GLTexture("crate");
233 
234         // define Cube mesh
235         auto p000 = [-1.0f, -1.0f, -1.0f];
236         auto p100 = [ 1.0f, -1.0f, -1.0f];
237         auto p010 = [-1.0f,  1.0f, -1.0f];
238         auto p110 = [ 1.0f,  1.0f, -1.0f];
239         auto p001 = [-1.0f, -1.0f,  1.0f];
240         auto p101 = [ 1.0f, -1.0f,  1.0f];
241         auto p011 = [-1.0f,  1.0f,  1.0f];
242         auto p111 = [ 1.0f,  1.0f,  1.0f];
243         vertices = p000 ~ p010 ~ p110 ~  p110 ~ p100 ~ p000 // front face
244                  ~ p101 ~ p111 ~ p011 ~  p011 ~ p001 ~ p101 // back face
245                  ~ p100 ~ p110 ~ p111 ~  p111 ~ p101 ~ p100 // right face
246                  ~ p001 ~ p011 ~ p010 ~  p010 ~ p000 ~ p001 // left face
247                  ~ p010 ~ p011 ~ p111 ~  p111 ~ p110 ~ p010 // top face
248                  ~ p001 ~ p000 ~ p100 ~  p100 ~ p101 ~ p001 // bottom face
249             ;
250         // texture coordinates
251         float[2] uv = _tx.uv;
252         float tx0 = 0.0f;
253         float tx1 = uv[0];
254         float ty0 = 0.0f;
255         float ty1 = uv[1];
256         float[12] facetx = [tx1, ty1, // triangle 1
257                             tx0, ty0,
258                             tx0, ty1,
259                             tx0, ty1, // triangle 2
260                             tx1, ty0,
261                             tx1, ty1];
262         texcoords = facetx ~ facetx ~ facetx ~ facetx ~ facetx ~ facetx;
263         // init with white color (1, 1, 1, 1)
264         foreach(ref cl; colors)
265             cl = 1.0f;
266     }
267 }
268 
269 // ====================================================================================
270 // Shaders based example
271 
272 // Simple texture + color shader
273 class MyGLProgram : GLProgram {
274     @property override string vertexSource() {
275         return q{
276             in vec4 vertex;
277             in vec4 colAttr;
278             in vec4 texCoord;
279             out vec4 col;
280             out vec4 texc;
281             uniform mat4 matrix;
282             void main(void)
283             {
284                 gl_Position = matrix * vertex;
285                 col = colAttr;
286                 texc = texCoord;
287             }
288         };
289 
290     }
291     @property override string fragmentSource() {
292         return q{
293             uniform sampler2D tex;
294             in vec4 col;
295             in vec4 texc;
296             out vec4 outColor;
297             void main(void)
298             {
299                 outColor = texture(tex, texc.st) * col;
300             }
301         };
302     }
303 
304     // attribute locations
305     protected GLint matrixLocation;
306     protected GLint vertexLocation;
307     protected GLint colAttrLocation;
308     protected GLint texCoordLocation;
309 
310     override bool initLocations() {
311         matrixLocation = getUniformLocation("matrix");
312         vertexLocation = getAttribLocation("vertex");
313         colAttrLocation = getAttribLocation("colAttr");
314         texCoordLocation = getAttribLocation("texCoord");
315         return matrixLocation >= 0 && vertexLocation >= 0 && colAttrLocation >= 0 && texCoordLocation >= 0;
316     }
317 
318     import std.typecons : Tuple, tuple;
319     Tuple!(VAO, VBO) createBuffers(float[] vertices, float[] colors, float[] texcoords) {
320 
321         VBO vbo = new VBO;
322         vbo.fill([vertices, colors, texcoords]);
323 
324         VAO vao = new VAO;
325         glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0);
326         glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (vertices.length * vertices[0].sizeof));
327         glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, cast(void*) (vertices.length * vertices[0].sizeof + colors.length * colors[0].sizeof));
328 
329         glEnableVertexAttribArray(vertexLocation);
330         glEnableVertexAttribArray(colAttrLocation);
331         glEnableVertexAttribArray(texCoordLocation);
332 
333         return tuple(vao, vbo);
334     }
335 
336     void execute(VAO vao, int vertsCount, Tex2D texture, bool linear, float[16] matrix) {
337 
338         bind();
339         checkgl!glUniformMatrix4fv(matrixLocation, 1, false, matrix.ptr);
340 
341         texture.setup();
342         texture.setSamplerParams(linear);
343 
344         vao.bind();
345         checkgl!glDrawArrays(GL_TRIANGLES, 0, vertsCount);
346 
347         texture.unbind();
348         unbind();
349     }
350 }
351 
352 
353 
354 
355 //=====================================================================================
356 // Legacy OpenGL API example
357 // GlxGears
358 //=====================================================================================
359 
360 import std.math;
361 static __gshared GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0;
362 static __gshared GLint gear1, gear2, gear3;
363 static __gshared GLfloat angle = 0.0;
364 alias M_PI = std.math.PI;
365 
366 /*
367 *
368 *  Draw a gear wheel.  You'll probably want to call this function when
369 *  building a display list since we do a lot of trig here.
370 *
371 *  Input:  inner_radius - radius of hole at center
372 *          outer_radius - radius at center of teeth
373 *          width - width of gear
374 *          teeth - number of teeth
375 *          tooth_depth - depth of tooth
376 */
377 static void
378     gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
379             GLint teeth, GLfloat tooth_depth)
380 {
381     GLint i;
382     GLfloat r0, r1, r2;
383     GLfloat angle, da;
384     GLfloat u, v, len;
385 
386     r0 = inner_radius;
387     r1 = outer_radius - tooth_depth / 2.0;
388     r2 = outer_radius + tooth_depth / 2.0;
389 
390     da = 2.0 * M_PI / teeth / 4.0;
391 
392     glShadeModel(GL_FLAT);
393 
394     glNormal3f(0.0, 0.0, 1.0);
395 
396     /* draw front face */
397     glBegin(GL_QUAD_STRIP);
398     for (i = 0; i <= teeth; i++) {
399         angle = i * 2.0 * M_PI / teeth;
400         glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
401         glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
402         if (i < teeth) {
403             glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
404             glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
405                         width * 0.5);
406         }
407     }
408     glEnd();
409 
410     /* draw front sides of teeth */
411     glBegin(GL_QUADS);
412     da = 2.0 * M_PI / teeth / 4.0;
413     for (i = 0; i < teeth; i++) {
414         angle = i * 2.0 * M_PI / teeth;
415 
416         glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
417         glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
418         glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
419                     width * 0.5);
420         glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
421                     width * 0.5);
422     }
423     glEnd();
424 
425     glNormal3f(0.0, 0.0, -1.0);
426 
427     /* draw back face */
428     glBegin(GL_QUAD_STRIP);
429     for (i = 0; i <= teeth; i++) {
430         angle = i * 2.0 * M_PI / teeth;
431         glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
432         glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
433         if (i < teeth) {
434             glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
435                         -width * 0.5);
436             glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
437         }
438     }
439     glEnd();
440 
441     /* draw back sides of teeth */
442     glBegin(GL_QUADS);
443     da = 2.0 * M_PI / teeth / 4.0;
444     for (i = 0; i < teeth; i++) {
445         angle = i * 2.0 * M_PI / teeth;
446 
447         glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
448                     -width * 0.5);
449         glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
450                     -width * 0.5);
451         glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
452         glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
453     }
454     glEnd();
455 
456     /* draw outward faces of teeth */
457     glBegin(GL_QUAD_STRIP);
458     for (i = 0; i < teeth; i++) {
459         angle = i * 2.0 * M_PI / teeth;
460 
461         glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
462         glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
463         u = r2 * cos(angle + da) - r1 * cos(angle);
464         v = r2 * sin(angle + da) - r1 * sin(angle);
465         len = sqrt(u * u + v * v);
466         u /= len;
467         v /= len;
468         glNormal3f(v, -u, 0.0);
469         glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
470         glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
471         glNormal3f(cos(angle), sin(angle), 0.0);
472         glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
473                     width * 0.5);
474         glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
475                     -width * 0.5);
476         u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
477         v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
478         glNormal3f(v, -u, 0.0);
479         glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
480                     width * 0.5);
481         glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
482                     -width * 0.5);
483         glNormal3f(cos(angle), sin(angle), 0.0);
484     }
485 
486     glVertex3f(r1 * cos(0.0), r1 * sin(0.0), width * 0.5);
487     glVertex3f(r1 * cos(0.0), r1 * sin(0.0), -width * 0.5);
488 
489     glEnd();
490 
491     glShadeModel(GL_SMOOTH);
492 
493     /* draw inside radius cylinder */
494     glBegin(GL_QUAD_STRIP);
495     for (i = 0; i <= teeth; i++) {
496         angle = i * 2.0 * M_PI / teeth;
497         glNormal3f(-cos(angle), -sin(angle), 0.0);
498         glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
499         glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
500     }
501     glEnd();
502 }
503 
504 
505 static void glxgears_draw()
506 {
507     glPushMatrix();
508     glRotatef(view_rotx, 1.0, 0.0, 0.0);
509     glRotatef(view_roty, 0.0, 1.0, 0.0);
510     glRotatef(view_rotz, 0.0, 0.0, 1.0);
511 
512     glPushMatrix();
513     glTranslatef(-3.0, -2.0, 0.0);
514     glRotatef(angle, 0.0, 0.0, 1.0);
515     glCallList(gear1);
516     glPopMatrix();
517 
518     glPushMatrix();
519     glTranslatef(3.1, -2.0, 0.0);
520     glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0);
521     glCallList(gear2);
522     glPopMatrix();
523 
524     glPushMatrix();
525     glTranslatef(-3.1, 4.2, 0.0);
526     glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0);
527     glCallList(gear3);
528     glPopMatrix();
529 
530     glPopMatrix();
531 }
532 
533 
534 /* new window size or exposure */
535 static void
536     glxgears_reshape(Rect rc)
537 {
538     GLfloat h = cast(GLfloat) rc.height / cast(GLfloat) rc.width;
539     glMatrixMode(GL_PROJECTION);
540     glLoadIdentity();
541     glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
542     glMatrixMode(GL_MODELVIEW);
543     glLoadIdentity();
544     glTranslatef(0.0, 0.0, -40.0);
545 }
546 
547 
548 static void glxgears_init()
549 {
550     static GLfloat[4] pos = [ 5.0, 5.0, 10.0, 0.0 ];
551     static GLfloat[4] red = [ 0.8, 0.1, 0.0, 1.0 ];
552     static GLfloat[4] green = [ 0.0, 0.8, 0.2, 1.0 ];
553     static GLfloat[4] blue = [ 0.2, 0.2, 1.0, 1.0 ];
554 
555     glLightfv(GL_LIGHT0, GL_POSITION, pos.ptr);
556     glEnable(GL_CULL_FACE);
557     glEnable(GL_LIGHTING);
558     glEnable(GL_LIGHT0);
559     glEnable(GL_DEPTH_TEST);
560 
561     /* make the gears */
562     gear1 = glGenLists(1);
563     glNewList(gear1, GL_COMPILE);
564     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red.ptr);
565     gear(1.0, 4.0, 1.0, 20, 0.7);
566     glEndList();
567 
568     gear2 = glGenLists(1);
569     glNewList(gear2, GL_COMPILE);
570     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green.ptr);
571     gear(0.5, 2.0, 2.0, 10, 0.7);
572     glEndList();
573 
574     gear3 = glGenLists(1);
575     glNewList(gear3, GL_COMPILE);
576     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue.ptr);
577     gear(1.3, 2.0, 0.5, 10, 0.7);
578     glEndList();
579 
580     glEnable(GL_NORMALIZE);
581 }