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