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