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