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 }