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 }