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