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 }