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