1 /* 2 * button.c: 3 * Simple toggle button example. 4 * 5 * written by Naofumi Yasufuku <naofumi@users.sourceforge.net> 6 * adapted by Antonio Monteiro to the gtkD toolkit <gtkDoolkit@yahoo.ca> 7 * this example is released under GPL license 8 */ 9 /* 10 * This file is part of gtkD. 11 * 12 * gtkD is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU Lesser General Public License as published by 14 * the Free Software Foundation; either version 3 of the License, or 15 * (at your option) any later version. 16 * 17 * gtkD is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU Lesser General Public License for more details. 21 * 22 * You should have received a copy of the GNU Lesser General Public License 23 * along with gtkD; if not, write to the Free Software 24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 25 */ 26 27 module ShapesGL; 28 29 import TrackBall; 30 import DrawGL; 31 32 import cairo.Context; 33 import glib.Idle; 34 import glib.Timeout; 35 import gdk.Event; 36 import gtk.Button; 37 import gtk.DrawingArea; 38 import gtk.Layout; 39 import gtk.Main; 40 import gtk.MainWindow; 41 import gtk.Menu; 42 import gtk.MenuItem; 43 import gtk.SeparatorMenuItem; 44 import gtk.VBox; 45 import gtk.Widget; 46 import glgdk.GLConfig; 47 import glgdk.GLContext; 48 import glgdk.GLdInit; 49 import glgdk.GLWindow; 50 import glgtk.GLCapability; 51 import glgtk.GLWidget; 52 53 import gtkc.glibtypes; 54 import gtkglc.gl; 55 import gtkglc.glu; 56 import gtkglc.glgdktypes; 57 import gtkglc.glgtktypes; 58 59 import std.math; 60 import std.stdio; 61 62 const float DIG_2_RAD = (PI / 180.0); 63 const float RAD_2_DIG = (180.0 / PI); 64 65 const int ANIMATE_THRESHOLD = cast(int)25.0; 66 67 const float VIEW_SCALE_MAX = 2.0; 68 const float VIEW_SCALE_MIN = 0.5; 69 70 const int NUM_SHAPES = 9; 71 72 enum Shapes 73 { 74 cube = 0, 75 sphere = 1, 76 cone = 2, 77 torus = 3, 78 tetrahedron = 4, 79 octahedron = 5, 80 dodecahedron = 6, 81 icosahedron = 7, 82 teapot = 8 83 } 84 85 static GLuint shape_list_base = 0; 86 static GLuint shape_current = 0; 87 88 struct MaterialProp 89 { 90 GLfloat[4] ambient; 91 GLfloat[4] diffuse; 92 GLfloat[4] specular; 93 GLfloat shininess; 94 } 95 96 static MaterialProp mat_emerald = { 97 [0.0215, 0.1745, 0.0215, 1.0], 98 [0.07568, 0.61424, 0.07568, 1.0], 99 [0.633, 0.727811, 0.633, 1.0], 100 0.6 101 }; 102 103 static MaterialProp mat_jade = { 104 [0.135, 0.2225, 0.1575, 1.0], 105 [0.54, 0.89, 0.63, 1.0], 106 [0.316228, 0.316228, 0.316228, 1.0], 107 0.1 108 }; 109 110 static MaterialProp mat_obsidian = { 111 [0.05375, 0.05, 0.06625, 1.0], 112 [0.18275, 0.17, 0.22525, 1.0], 113 [0.332741, 0.328634, 0.346435, 1.0], 114 0.3 115 }; 116 117 static MaterialProp mat_pearl = { 118 [0.25, 0.20725, 0.20725, 1.0], 119 [1.0, 0.829, 0.829, 1.0], 120 [0.296648, 0.296648, 0.296648, 1.0], 121 0.088 122 }; 123 124 static MaterialProp mat_ruby = { 125 [0.1745, 0.01175, 0.01175, 1.0], 126 [0.61424, 0.04136, 0.04136, 1.0], 127 [0.727811, 0.626959, 0.626959, 1.0], 128 0.6 129 }; 130 131 static MaterialProp mat_turquoise = { 132 [0.1, 0.18725, 0.1745, 1.0], 133 [0.396, 0.74151, 0.69102, 1.0], 134 [0.297254, 0.30829, 0.306678, 1.0], 135 0.1 136 }; 137 138 static MaterialProp mat_brass = { 139 [0.329412, 0.223529, 0.027451, 1.0], 140 [0.780392, 0.568627, 0.113725, 1.0], 141 [0.992157, 0.941176, 0.807843, 1.0], 142 0.21794872 143 }; 144 145 static MaterialProp mat_bronze = { 146 [0.2125, 0.1275, 0.054, 1.0], 147 [0.714, 0.4284, 0.18144, 1.0], 148 [0.393548, 0.271906, 0.166721, 1.0], 149 0.2 150 }; 151 152 static MaterialProp mat_chrome = { 153 [0.25, 0.25, 0.25, 1.0], 154 [0.4, 0.4, 0.4, 1.0], 155 [0.774597, 0.774597, 0.774597, 1.0], 156 0.6 157 }; 158 159 static MaterialProp mat_copper = { 160 [0.19125, 0.0735, 0.0225, 1.0], 161 [0.7038, 0.27048, 0.0828, 1.0], 162 [0.256777, 0.137622, 0.086014, 1.0], 163 0.1 164 }; 165 166 static MaterialProp mat_gold = { 167 [0.24725, 0.1995, 0.0745, 1.0], 168 [0.75164, 0.60648, 0.22648, 1.0], 169 [0.628281, 0.555802, 0.366065, 1.0], 170 0.4 171 }; 172 173 static MaterialProp mat_silver = { 174 [0.19225, 0.19225, 0.19225, 1.0], 175 [0.50754, 0.50754, 0.50754, 1.0], 176 [0.508273, 0.508273, 0.508273, 1.0], 177 0.4 178 }; 179 180 static MaterialProp* mat_current; 181 182 static float[4] view_quat_diff = [ 0.0, 0.0, 0.0, 1.0 ]; 183 static float[4] view_quat = [ 0.0, 0.0, 0.0, 1.0 ]; 184 static float view_scale = 1.0; 185 186 static bool animate = false; 187 188 static void init_view() 189 { 190 view_quat[0] = view_quat_diff[0] = 0.0; 191 view_quat[1] = view_quat_diff[1] = 0.0; 192 view_quat[2] = view_quat_diff[2] = 0.0; 193 view_quat[3] = view_quat_diff[3] = 1.0; 194 view_scale = 1.0; 195 } 196 197 /** 198 * A GL toggle button 199 */ 200 class ShapesGL : DrawingArea 201 { 202 TestGL testGL; 203 bool animate = false; 204 Idle mainIdle; 205 Menu menu; 206 207 /** need to include the mixin to add GL capabilities to this widget */ 208 mixin GLCapability; 209 210 GLfloat width; 211 GLfloat height; 212 213 this(TestGL testGL) 214 { 215 mat_current = &mat_silver; 216 217 this.testGL = testGL; 218 setGlCapability(); // set the GL capabilities for this widget 219 220 setSizeRequest(400,400); 221 addOnMap(&mapCallback); // dispatcher.addMapListener(this,da); 222 addOnVisibilityNotify(&visibilityCallback); // dispatcher.addVisibilityListener(this,da); 223 addOnButtonPress(&mouseButtonPressCallback); // dispatcher.addMouseButtonListener(this,da); 224 addOnButtonRelease(&mouseButtonReleaseCallback); // dispatcher.addMouseMotionListener(this,da); 225 addOnMotionNotify(&motionNotifyCallback); 226 227 menu = createPopupMenu(); 228 } 229 230 bool visibilityCallback(Event e, Widget widget) 231 { 232 if (animate) 233 { 234 if (e.visibility.state == VisibilityState.FULLY_OBSCURED) 235 { 236 removeIdle(); 237 } 238 else 239 { 240 addIdle(); 241 } 242 } 243 244 return true; 245 } 246 247 bool idleCallback() 248 { 249 return drawFrame(); 250 } 251 252 bool drawGL() 253 { 254 float[4][4] m; 255 256 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 257 258 glLoadIdentity(); 259 260 /* View transformation. */ 261 glTranslatef(0.0, 0.0, -10.0); 262 glScalef(view_scale, view_scale, view_scale); 263 add_quats(view_quat_diff, view_quat, view_quat); 264 build_rotmatrix(m, view_quat); 265 glMultMatrixf(&m[0][0]); 266 267 /* Render shape */ 268 glMaterialfv(GL_FRONT, GL_AMBIENT, mat_current.ambient.ptr); 269 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_current.diffuse.ptr); 270 glMaterialfv(GL_FRONT, GL_SPECULAR, mat_current.specular.ptr); 271 glMaterialf(GL_FRONT, GL_SHININESS, mat_current.shininess * 128.0); 272 glCallList(shape_list_base + shape_current); 273 274 return true; 275 } 276 277 bool resizeGL(Event e) 278 { 279 GLfloat w; 280 GLfloat h; 281 282 if ( e is null || e.type != GdkEventType.CONFIGURE ) 283 { 284 w = getWidth(); 285 h = getHeight(); 286 } 287 else 288 { 289 w = e.configure.width; 290 h = e.configure.height; 291 } 292 293 width = w; 294 height = h; 295 296 GLfloat aspect; 297 298 glViewport(0, 0, cast(int)w, cast(int)h); 299 300 glMatrixMode(GL_PROJECTION); 301 glLoadIdentity(); 302 303 double x = w/h; 304 double y = 1.0; 305 if ( x > 1.0 ) 306 { 307 y = h/w; 308 x = 1.0; 309 } 310 311 glFrustum (-x, x, -y, y, 5.0, 60.0); 312 glMatrixMode(GL_MODELVIEW); 313 314 return true; 315 } 316 317 void initGL() 318 { 319 static GLfloat[] ambient = [0.0, 0.0, 0.0, 1.0]; 320 static GLfloat[] diffuse = [1.0, 1.0, 1.0, 1.0]; 321 static GLfloat[] position = [0.0, 3.0, 3.0, 0.0]; 322 323 static GLfloat[] lmodel_ambient = [0.2, 0.2, 0.2, 1.0]; 324 static GLfloat[] local_view = [0.0]; 325 326 resizeGL(null); 327 328 glClearColor(0.5, 0.5, 0.8, 1.0); 329 glClearDepth(1.0); 330 331 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient.ptr); 332 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse.ptr); 333 glLightfv(GL_LIGHT0, GL_POSITION, position.ptr); 334 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient.ptr); 335 glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view.ptr); 336 337 glFrontFace(GL_CW); 338 glEnable(GL_LIGHTING); 339 glEnable(GL_LIGHT0); 340 glEnable(GL_AUTO_NORMAL); 341 glEnable(GL_NORMALIZE); 342 glEnable(GL_DEPTH_TEST); 343 glDepthFunc(GL_LESS); 344 345 /* Shape display lists */ 346 shape_list_base = glGenLists(2); //Shapes.max); 347 348 int depth = 1; 349 GLfloat none = 0.0; 350 GLfloat step = 1.8 / depth; 351 GLfloat size = step / 1.8; 352 GLfloat back = -(depth-1)*step; 353 GLfloat start; 354 355 if ( depth % 2 == 1 ) 356 { 357 start = -(depth-1)/2*step; 358 } 359 else 360 { 361 start = -depth/2*step+step/2; 362 } 363 364 void addCube(GLfloat tx, GLfloat ty, GLfloat tz) 365 { 366 if ( tx!=none || ty!=none || tz!=none ) 367 { 368 glTranslatef(tx, ty, tz); 369 } 370 drawCube(true, size); 371 } 372 373 /* Cube */ 374 glNewList(shape_list_base + Shapes.cube, GL_COMPILE); 375 glPushMatrix(); 376 377 for ( int z=0 ; z<depth ; z++ ) 378 { 379 for ( int y=0 ; y<depth ; y++ ) 380 { 381 if ( y==0 ) 382 { 383 if ( z == 0 ) 384 { 385 addCube(start,start,start); 386 } 387 else 388 { 389 addCube(back, back, step); 390 } 391 } 392 else 393 { 394 addCube(back, step, none); 395 } 396 for ( int x=1 ; x<depth ; x++ ) 397 { 398 addCube(step, none, none); 399 } 400 } 401 } 402 403 glPopMatrix(); 404 glEndList(); 405 406 /* Sphere */ 407 glNewList(shape_list_base + Shapes.sphere, GL_COMPILE); 408 drawSphere(true, 1.0, 30, 30); 409 glEndList(); 410 411 /* Cone */ 412 glNewList(shape_list_base + Shapes.cone, GL_COMPILE); 413 glPushMatrix(); 414 glTranslatef(0.0, 0.0, -1.0); 415 drawCone(true, 1.0, 2.0, 30, 30); 416 glPopMatrix(); 417 glEndList(); 418 419 /* Torus */ 420 glNewList(shape_list_base + Shapes.torus, GL_COMPILE); 421 drawTorus(true, 0.4, 0.8, 30, 30); 422 glEndList(); 423 424 /* Tetrahedron */ 425 glNewList(shape_list_base + Shapes.tetrahedron, GL_COMPILE); 426 glPushMatrix(); 427 glScalef(1.2, 1.2, 1.2); 428 drawTetrahedron(true); 429 glPopMatrix(); 430 glEndList(); 431 432 /* Octahedron */ 433 glNewList(shape_list_base + Shapes.octahedron, GL_COMPILE); 434 glPushMatrix(); 435 glScalef(1.2, 1.2, 1.2); 436 drawOctahedron(true); 437 glPopMatrix(); 438 glEndList(); 439 440 /* Dodecahedron */ 441 glNewList(shape_list_base + Shapes.dodecahedron, GL_COMPILE); 442 glPushMatrix(); 443 glScalef(0.7, 0.7, 0.7); 444 drawDodecahedron(true); 445 glPopMatrix(); 446 glEndList(); 447 448 /* Icosahedron */ 449 glNewList(shape_list_base + Shapes.icosahedron, GL_COMPILE); 450 glPushMatrix(); 451 glScalef(1.2, 1.2, 1.2); 452 drawIcosahedron(true); 453 glPopMatrix(); 454 glEndList(); 455 456 /* Teapot */ 457 glNewList(shape_list_base + Shapes.teapot, GL_COMPILE); 458 drawTeapot(true, 1.0); 459 glEndList(); 460 } 461 462 void mapCallback(Widget drawable) 463 { 464 if (animate) 465 { 466 addIdle(); 467 } 468 } 469 470 void unmapCallback(Widget drawable) 471 { 472 removeIdle(); 473 } 474 475 void addIdle() 476 { 477 if ( mainIdle is null ) 478 { 479 mainIdle = new Idle(&idleCallback); 480 } 481 } 482 483 void removeIdle() 484 { 485 if ( mainIdle !is null ) 486 { 487 mainIdle.stop(); 488 mainIdle = null; 489 } 490 } 491 492 float begin_x = 0.0; 493 float begin_y = 0.0; 494 495 float dx = 0.0; 496 float dy = 0.0; 497 498 bool mouseButtonPressCallback(Event event, Widget widget) 499 { 500 if (event.button.button == 1) 501 { 502 if (animate) 503 { 504 toggleAnimation(); 505 } 506 } 507 508 begin_x = event.button.x; 509 begin_y = event.button.y; 510 511 return false; 512 } 513 514 bool mouseButtonReleaseCallback(Event event, Widget widget) 515 { 516 if (event.button.button == 1) 517 { 518 if (!animate && ((dx*dx + dy*dy) > ANIMATE_THRESHOLD)) 519 { 520 toggleAnimation(); 521 } 522 } 523 else if (event.button.button == 3) 524 { 525 /* Popup menu. */ 526 menu.popup(null,null,null,null,event.button.button, event.button.time); 527 return true; 528 } 529 530 dx = 0.0; 531 dy = 0.0; 532 533 return false; 534 } 535 536 bool motionNotifyCallback(Event event, Widget widget) 537 { 538 float w = width; 539 float h = height; 540 float x = event.motion.x; 541 float y = event.motion.y; 542 bool redraw = false; 543 544 /* Rotation. */ 545 if (event.motion.state == ModifierType.BUTTON1_MASK ) 546 { 547 trackball(view_quat_diff, 548 (2.0 * begin_x - w) / w, 549 (h - 2.0 * begin_y) / h, 550 (2.0 * x - w) / w, 551 (h - 2.0 * y) / h); 552 553 dx = x - begin_x; 554 dy = y - begin_y; 555 556 redraw = true; 557 } 558 559 /* Scaling. */ 560 if (event.motion.state == ModifierType.BUTTON2_MASK ) 561 { 562 view_scale = view_scale * (1.0 + (y - begin_y) / h); 563 if (view_scale > VIEW_SCALE_MAX) 564 { 565 view_scale = VIEW_SCALE_MAX; 566 } 567 else if (view_scale < VIEW_SCALE_MIN) 568 { 569 view_scale = VIEW_SCALE_MIN; 570 } 571 572 redraw = true; 573 } 574 575 begin_x = x; 576 begin_y = y; 577 578 if (redraw && !animate) 579 { 580 queueDraw(); 581 } 582 583 return true; 584 } 585 586 /* Toggle animation.*/ 587 void toggleAnimation() 588 { 589 animate = !animate; 590 591 if (animate) 592 { 593 addIdle(); 594 } 595 else 596 { 597 removeIdle(); 598 view_quat_diff[0] = 0.0; 599 view_quat_diff[1] = 0.0; 600 view_quat_diff[2] = 0.0; 601 view_quat_diff[3] = 1.0; 602 queueDraw(); 603 } 604 605 } 606 607 void activateItemCallback(MenuItem menuItem) 608 { 609 string action = menuItem.getActionName(); 610 writefln("activateItemCallback action = %s ", action); 611 612 switch(action) 613 { 614 case "shapes.Cube":shape_current = Shapes.cube; break; 615 case "shapes.Sphere":shape_current = Shapes.sphere; break; 616 case "shapes.Cone":shape_current = Shapes.cone; break; 617 case "shapes.Torus":shape_current = Shapes.torus; break; 618 case "shapes.Tetrahedron":shape_current = Shapes.tetrahedron; break; 619 case "shapes.Octahedron":shape_current = Shapes.octahedron; break; 620 case "shapes.Dodecahedron":shape_current = Shapes.dodecahedron; break; 621 case "shapes.Icosahedron":shape_current = Shapes.icosahedron; break; 622 case "shapes.Teapot":shape_current = Shapes.teapot; break; 623 624 case "materials.Emerald":mat_current = &mat_emerald;break; 625 case "materials.Jade":mat_current = &mat_jade;break; 626 case "materials.Obsidian":mat_current = &mat_obsidian;break; 627 case "materials.Pearl":mat_current = &mat_pearl;break; 628 case "materials.Ruby":mat_current = &mat_ruby;break; 629 case "materials.Turquoise":mat_current = &mat_turquoise;break; 630 case "materials.Brass":mat_current = &mat_brass;break; 631 case "materials.Bronze":mat_current = &mat_bronze;break; 632 case "materials.Chrome":mat_current = &mat_chrome;break; 633 case "materials.Copper":mat_current = &mat_copper;break; 634 case "materials.Gold":mat_current = &mat_gold;break; 635 case "materials.Silver":mat_current = &mat_silver;break; 636 637 case "reset":init_view();break; 638 case "fullScreen":testGL.fullscreen();break; 639 case "unFullScreen":testGL.unfullscreen();break; 640 default: break; 641 } 642 643 queueDraw(); 644 } 645 646 /* Creates the popup menu.*/ 647 Menu createPopupMenu() 648 { 649 Menu shapes = new Menu(); 650 Menu materials = new Menu(); 651 Menu menu = new Menu(); 652 MenuItem item; 653 654 /* 655 * Shapes submenu. 656 */ 657 658 menu.append(new MenuItem(&activateItemCallback, "Cube", "shapes.Cube")); 659 menu.append(new MenuItem("Sphere", &activateItemCallback, "shapes.Sphere")); 660 menu.append(new MenuItem("Cone", &activateItemCallback, "shapes.Cone")); 661 menu.append(new MenuItem("Torus", &activateItemCallback, "shapes.Torus")); 662 menu.append(new MenuItem("Tetrahedron", &activateItemCallback, "shapes.Tetrahedron")); 663 menu.append(new MenuItem("Octahedron", &activateItemCallback, "shapes.Octahedron")); 664 menu.append(new MenuItem("Dodecahedron", &activateItemCallback, "shapes.Dodecahedron")); 665 menu.append(new MenuItem("Icosahedron", &activateItemCallback, "shapes.Icosahedron")); 666 menu.append(new MenuItem("Teapot", &activateItemCallback, "shapes.Teapot")); 667 668 /* 669 * Materials submenu. 670 */ 671 menu.append(new SeparatorMenuItem()); 672 673 menu.append(new MenuItem("Emerald", &activateItemCallback, "materials.Emerald")); 674 menu.append(new MenuItem("Jade", &activateItemCallback, "materials.Jade")); 675 menu.append(new MenuItem("Obsidian", &activateItemCallback, "materials.Obsidian")); 676 menu.append(new MenuItem("Pearl", &activateItemCallback, "materials.Pearl")); 677 menu.append(new MenuItem("Ruby", &activateItemCallback, "materials.Ruby")); 678 menu.append(new MenuItem("Turquoise", &activateItemCallback, "materials.Turquoise")); 679 menu.append(new MenuItem("Brass", &activateItemCallback, "materials.Brass")); 680 menu.append(new MenuItem("Bronze", &activateItemCallback, "materials.Bronze")); 681 menu.append(new MenuItem("Chrome", &activateItemCallback, "materials.Chrome")); 682 menu.append(new MenuItem("Copper", &activateItemCallback, "materials.Copper")); 683 menu.append(new MenuItem("Gold", &activateItemCallback, "materials.Gold")); 684 menu.append(new MenuItem("Silver", &activateItemCallback, "materials.Silver")); 685 686 /* reset */ 687 menu.append(new SeparatorMenuItem()); 688 menu.append(new MenuItem("Reset", &activateItemCallback, "reset")); 689 menu.append(new MenuItem("Fullscreen", &activateItemCallback, "fullScreen")); 690 menu.append(new MenuItem("un-Fullscreen", &activateItemCallback, "unFullScreen")); 691 692 /* Quit */ 693 menu.append(new SeparatorMenuItem()); 694 menu.append(new MenuItem("Cancel", &activateItemCallback, "quit")); 695 696 menu.showAll(); 697 698 return menu; 699 } 700 } 701 702 class TestGL : MainWindow 703 { 704 this() 705 { 706 super("GtkD ShapesGL (right-click for menu)"); 707 setReallocateRedraws(true); 708 show(); 709 } 710 } 711 712 void main(string[] args) 713 { 714 Main.init(args); 715 716 GLdInit.init(args); 717 718 TestGL testGL = new TestGL(); 719 720 testGL.add(new ShapesGL(testGL)); 721 testGL.showAll(); 722 723 Main.run(); 724 }