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 }