gtkglextmm: shapes.cc

Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Examples

# shapes.cc

Geometric object rendering example.

    // -*- C++ -*-
    /*
     * shapes.cc:
     * shapes demo.
     *
     * written by Naofumi Yasufuku  <naofumi@users.sourceforge.net>
     */

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>

    #ifdef G_OS_WIN32
    #define WIN32_LEAN_AND_MEAN 1
    #include <windows.h>
    #endif

    #include <GL/gl.h>
    #include <GL/glu.h>

    #include "shapes.h"

    //
    // Trackball utilities.
    //
    namespace Trackball {
      extern "C" {
        #include "trackball.h"
      }
    }

    #define DIG_2_RAD (G_PI / 180.0)
    #define RAD_2_DIG (180.0 / G_PI)

    //
    // OpenGL frame buffer configuration utilities.
    //

    struct GLConfigUtil
    {
      static void print_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig,
                                  const char* attrib_str,
                                  int attrib,
                                  bool is_boolean);

      static void examine_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig);
    };

    //
    // Print a configuration attribute.
    //
    void GLConfigUtil::print_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig,
                                       const char* attrib_str,
                                       int attrib,
                                       bool is_boolean)
    {
      int value;

      if (glconfig->get_attrib(attrib, value))
        {
          std::cout << attrib_str << " = ";
          if (is_boolean)
            std::cout << (value == true ? "true" : "false") << std::endl;
          else
            std::cout << value << std::endl;
        }
      else
        {
          std::cout << "*** Cannot get "
                    << attrib_str
                    << " attribute value\n";
        }
    }

    //
    // Print configuration attributes.
    //
    void GLConfigUtil::examine_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig)
    {
      std::cout << "\nOpenGL visual configurations :\n\n";

      std::cout << "glconfig->is_rgba() = "
                << (glconfig->is_rgba() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->is_double_buffered() = "
                << (glconfig->is_double_buffered() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->is_stereo() = "
                << (glconfig->is_stereo() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->has_alpha() = "
                << (glconfig->has_alpha() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->has_depth_buffer() = "
                << (glconfig->has_depth_buffer() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->has_stencil_buffer() = "
                << (glconfig->has_stencil_buffer() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->has_accum_buffer() = "
                << (glconfig->has_accum_buffer() ? "true" : "false")
                << std::endl;

      std::cout << std::endl;

      print_gl_attrib(glconfig, "Gdk::GL::USE_GL",           Gdk::GL::USE_GL,           true);
      print_gl_attrib(glconfig, "Gdk::GL::BUFFER_SIZE",      Gdk::GL::BUFFER_SIZE,      false);
      print_gl_attrib(glconfig, "Gdk::GL::LEVEL",            Gdk::GL::LEVEL,            false);
      print_gl_attrib(glconfig, "Gdk::GL::RGBA",             Gdk::GL::RGBA,             true);
      print_gl_attrib(glconfig, "Gdk::GL::DOUBLEBUFFER",     Gdk::GL::DOUBLEBUFFER,     true);
      print_gl_attrib(glconfig, "Gdk::GL::STEREO",           Gdk::GL::STEREO,           true);
      print_gl_attrib(glconfig, "Gdk::GL::AUX_BUFFERS",      Gdk::GL::AUX_BUFFERS,      false);
      print_gl_attrib(glconfig, "Gdk::GL::RED_SIZE",         Gdk::GL::RED_SIZE,         false);
      print_gl_attrib(glconfig, "Gdk::GL::GREEN_SIZE",       Gdk::GL::GREEN_SIZE,       false);
      print_gl_attrib(glconfig, "Gdk::GL::BLUE_SIZE",        Gdk::GL::BLUE_SIZE,        false);
      print_gl_attrib(glconfig, "Gdk::GL::ALPHA_SIZE",       Gdk::GL::ALPHA_SIZE,       false);
      print_gl_attrib(glconfig, "Gdk::GL::DEPTH_SIZE",       Gdk::GL::DEPTH_SIZE,       false);
      print_gl_attrib(glconfig, "Gdk::GL::STENCIL_SIZE",     Gdk::GL::STENCIL_SIZE,     false);
      print_gl_attrib(glconfig, "Gdk::GL::ACCUM_RED_SIZE",   Gdk::GL::ACCUM_RED_SIZE,   false);
      print_gl_attrib(glconfig, "Gdk::GL::ACCUM_GREEN_SIZE", Gdk::GL::ACCUM_GREEN_SIZE, false);
      print_gl_attrib(glconfig, "Gdk::GL::ACCUM_BLUE_SIZE",  Gdk::GL::ACCUM_BLUE_SIZE,  false);
      print_gl_attrib(glconfig, "Gdk::GL::ACCUM_ALPHA_SIZE", Gdk::GL::ACCUM_ALPHA_SIZE, false);

      std::cout << std::endl;
    }

    //
    // Shapes classes.
    //

    namespace Shapes
    {

      //
      // View class implementation.
      //

      const float View::NEAR_CLIP   = 5.0;
      const float View::FAR_CLIP    = 60.0;

      const float View::INIT_POS_X  = 0.0;
      const float View::INIT_POS_Y  = 0.0;
      const float View::INIT_POS_Z  = -10.0;

      const float View::INIT_AXIS_X = 1.0;
      const float View::INIT_AXIS_Y = 0.0;
      const float View::INIT_AXIS_Z = 0.0;
      const float View::INIT_ANGLE  = 0.0;

      const float View::INIT_SCALE  = 1.0;

      const float View::SCALE_MAX   = 2.0;
      const float View::SCALE_MIN   = 0.5;

      const float View::ANIMATE_THRESHOLD = 25.0;

      View::View()
        : m_Scale(INIT_SCALE),
          m_BeginX(0.0), m_BeginY(0.0),
          m_DX(0.0), m_DY(0.0),
          m_Animate(false)
      {
        reset();
      }

      View::~View()
      {
      }

      void View::frustum(int w, int h)
      {
        glViewport(0, 0, w, h);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();

        if (w > h) {
          float aspect = static_cast<float>(w) / static_cast<float>(h);
          glFrustum(-aspect, aspect, -1.0, 1.0, NEAR_CLIP, FAR_CLIP);
        } else {
          float aspect = static_cast<float>(h) / static_cast<float>(w);
          glFrustum(-1.0, 1.0, -aspect, aspect, NEAR_CLIP, FAR_CLIP);
        }

        glMatrixMode(GL_MODELVIEW);
      }

      void View::xform()
      {
        glTranslatef(m_Pos[0], m_Pos[1], m_Pos[2]);

        glScalef(m_Scale, m_Scale, m_Scale);

        float m[4][4];
        Trackball::add_quats(m_QuatDiff, m_Quat, m_Quat);
        Trackball::build_rotmatrix(m, m_Quat);
        glMultMatrixf(&m[0][0]);
      }

      void View::reset()
      {
        m_Pos[0] = INIT_POS_X;
        m_Pos[1] = INIT_POS_Y;
        m_Pos[2] = INIT_POS_Z;

        float sine = sin(0.5 * INIT_ANGLE * DIG_2_RAD);
        m_Quat[0] = INIT_AXIS_X * sine;
        m_Quat[1] = INIT_AXIS_Y * sine;
        m_Quat[2] = INIT_AXIS_Z * sine;
        m_Quat[3] = cos(0.5 * INIT_ANGLE * DIG_2_RAD);

        m_Scale = INIT_SCALE;

        m_QuatDiff[0] = 0.0;
        m_QuatDiff[1] = 0.0;
        m_QuatDiff[2] = 0.0;
        m_QuatDiff[3] = 1.0;
      }

      void View::enable_animation()
      {
        m_Animate = true;
      }

      void View::disable_animation()
      {
        m_Animate = false;

        m_QuatDiff[0] = 0.0;
        m_QuatDiff[1] = 0.0;
        m_QuatDiff[2] = 0.0;
        m_QuatDiff[3] = 1.0;
      }

      bool View::on_button_press_event(GdkEventButton* event,
                                       Scene* scene)
      {
        if (is_animate()) {
          if (event->button == 1) {
            disable_animation();
            scene->idle_remove();
            scene->invalidate();
          }
        } else {
          m_QuatDiff[0] = 0.0;
          m_QuatDiff[1] = 0.0;
          m_QuatDiff[2] = 0.0;
          m_QuatDiff[3] = 1.0;
        }

        m_BeginX = event->x;
        m_BeginY = event->y;

        // don't block
        return false;
      }

      bool View::on_button_release_event(GdkEventButton* event,
                                         Scene* scene)
      {
        if (!is_animate()) {
          if (event->button == 1 &&
              ((m_DX*m_DX + m_DY*m_DY) > ANIMATE_THRESHOLD)) {
            enable_animation();
            scene->idle_add();
          }
        }

        m_DX = 0.0;
        m_DY = 0.0;

        // don't block
        return false;
      }

      bool View::on_motion_notify_event(GdkEventMotion* event,
                                        Scene* scene)
      {
        float w = scene->get_width();
        float h = scene->get_height();
        float x = event->x;
        float y = event->y;
        bool redraw = false;

        // Rotation.
        if (event->state & GDK_BUTTON1_MASK) {
          Trackball::trackball(m_QuatDiff,
                               (2.0 * m_BeginX - w) / w,
                               (h - 2.0 * m_BeginY) / h,
                               (2.0 * x - w) / w,
                               (h - 2.0 * y) / h);

          m_DX = x - m_BeginX;
          m_DY = y - m_BeginY;

          redraw = true;
        }

        // Scaling.
        if (event->state & GDK_BUTTON2_MASK) {
          m_Scale = m_Scale * (1.0 + (y - m_BeginY) / h);
          if (m_Scale > SCALE_MAX)
            m_Scale = SCALE_MAX;
          else if (m_Scale < SCALE_MIN)
            m_Scale = SCALE_MIN;

          redraw = true;
        }

        m_BeginX = x;
        m_BeginY = y;

        if (redraw)
          scene->invalidate();

        // don't block
        return false;
      }

      //
      // Model class implementation.
      //

      const unsigned int Model::NUM_SHAPES = 9;

      const Model::ShapeType Model::SHAPE_CUBE         = CUBE;
      const Model::ShapeType Model::SHAPE_SPHERE       = SPHERE;
      const Model::ShapeType Model::SHAPE_CONE         = CONE;
      const Model::ShapeType Model::SHAPE_TORUS        = TORUS;
      const Model::ShapeType Model::SHAPE_TETRAHEDRON  = TETRAHEDRON;
      const Model::ShapeType Model::SHAPE_OCTAHEDRON   = OCTAHEDRON;
      const Model::ShapeType Model::SHAPE_DODECAHEDRON = DODECAHEDRON;
      const Model::ShapeType Model::SHAPE_ICOSAHEDRON  = ICOSAHEDRON;
      const Model::ShapeType Model::SHAPE_TEAPOT       = TEAPOT;

      const Model::MaterialProp Model::MAT_EMERALD = {
        {0.0215, 0.1745, 0.0215, 1.0},
        {0.07568, 0.61424, 0.07568, 1.0},
        {0.633, 0.727811, 0.633, 1.0},
        0.6
      };

      const Model::MaterialProp Model::MAT_JADE = {
        {0.135, 0.2225, 0.1575, 1.0},
        {0.54, 0.89, 0.63, 1.0},
        {0.316228, 0.316228, 0.316228, 1.0},
        0.1
      };

      const Model::MaterialProp Model::MAT_OBSIDIAN = {
        {0.05375, 0.05, 0.06625, 1.0},
        {0.18275, 0.17, 0.22525, 1.0},
        {0.332741, 0.328634, 0.346435, 1.0},
        0.3
      };

      const Model::MaterialProp Model::MAT_PEARL = {
        {0.25, 0.20725, 0.20725, 1.0},
        {1.0, 0.829, 0.829, 1.0},
        {0.296648, 0.296648, 0.296648, 1.0},
        0.088
      };

      const Model::MaterialProp Model::MAT_RUBY = {
        {0.1745, 0.01175, 0.01175, 1.0},
        {0.61424, 0.04136, 0.04136, 1.0},
        {0.727811, 0.626959, 0.626959, 1.0},
        0.6
      };

      const Model::MaterialProp Model::MAT_TURQUOISE = {
        {0.1, 0.18725, 0.1745, 1.0},
        {0.396, 0.74151, 0.69102, 1.0},
        {0.297254, 0.30829, 0.306678, 1.0},
        0.1
      };

      const Model::MaterialProp Model::MAT_BRASS = {
        {0.329412, 0.223529, 0.027451, 1.0},
        {0.780392, 0.568627, 0.113725, 1.0},
        {0.992157, 0.941176, 0.807843, 1.0},
        0.21794872
      };

      const Model::MaterialProp Model::MAT_BRONZE = {
        {0.2125, 0.1275, 0.054, 1.0},
        {0.714, 0.4284, 0.18144, 1.0},
        {0.393548, 0.271906, 0.166721, 1.0},
        0.2
      };

      const Model::MaterialProp Model::MAT_CHROME = {
        {0.25, 0.25, 0.25, 1.0},
        {0.4, 0.4, 0.4, 1.0},
        {0.774597, 0.774597, 0.774597, 1.0},
        0.6
      };

      const Model::MaterialProp Model::MAT_COPPER = {
        {0.19125, 0.0735, 0.0225, 1.0},
        {0.7038, 0.27048, 0.0828, 1.0},
        {0.256777, 0.137622, 0.086014, 1.0},
        0.1
      };

      const Model::MaterialProp Model::MAT_GOLD = {
        {0.24725, 0.1995, 0.0745, 1.0},
        {0.75164, 0.60648, 0.22648, 1.0},
        {0.628281, 0.555802, 0.366065, 1.0},
        0.4
      };

      const Model::MaterialProp Model::MAT_SILVER = {
        {0.19225, 0.19225, 0.19225, 1.0},
        {0.50754, 0.50754, 0.50754, 1.0},
        {0.508273, 0.508273, 0.508273, 1.0},
        0.4
      };

      Model::Model()
        : m_ListBase(0),
          m_CurrentShape(TEAPOT),
          m_CurrentMat(&MAT_SILVER)
      {
      }

      Model::~Model()
      {
      }

      void Model::init_gl(Glib::RefPtr<Gdk::GL::Drawable>& gldrawable)
      {
        /* Shape display lists */
        m_ListBase = glGenLists(NUM_SHAPES);

        /* Cube */
        glNewList(m_ListBase + CUBE, GL_COMPILE);
          gldrawable->draw_cube(true, 1.5);
        glEndList();

        /* Sphere */
        glNewList(m_ListBase + SPHERE, GL_COMPILE);
          gldrawable->draw_sphere(true, 1.0, 30, 30);
        glEndList();

        /* Cone */
        glNewList(m_ListBase + CONE, GL_COMPILE);
          glPushMatrix();
            glTranslatef(0.0, 0.0, -1.0);
            gldrawable->draw_cone(true, 1.0, 2.0, 30, 30);
          glPopMatrix();
        glEndList();

        /* Torus */
        glNewList(m_ListBase + TORUS, GL_COMPILE);
          gldrawable->draw_torus(true, 0.4, 0.8, 30, 30);
        glEndList();

        /* Tetrahedron */
        glNewList(m_ListBase + TETRAHEDRON, GL_COMPILE);
          glPushMatrix();
            glScalef(1.2, 1.2, 1.2);
            gldrawable->draw_tetrahedron(true);
          glPopMatrix();
        glEndList();

        /* Octahedron */
        glNewList(m_ListBase + OCTAHEDRON, GL_COMPILE);
          glPushMatrix();
            glScalef(1.2, 1.2, 1.2);
            gldrawable->draw_octahedron(true);
          glPopMatrix();
        glEndList();

        /* Dodecahedron */
        glNewList(m_ListBase + DODECAHEDRON, GL_COMPILE);
          glPushMatrix();
            glScalef(0.7, 0.7, 0.7);
            gldrawable->draw_dodecahedron(true);
          glPopMatrix();
        glEndList();

        /* Icosahedron */
        glNewList(m_ListBase + ICOSAHEDRON, GL_COMPILE);
          glPushMatrix();
            glScalef(1.2, 1.2, 1.2);
            gldrawable->draw_icosahedron(true);
          glPopMatrix();
        glEndList();

        /* Teapot */
        glNewList(m_ListBase + TEAPOT, GL_COMPILE);
          gldrawable->draw_teapot(true, 1.0);
        glEndList();
      }

      void Model::draw(Glib::RefPtr<Gdk::GL::Drawable>& gldrawable)
      {
        // Init GL context.
        static bool initialized = false;
        if (!initialized) {
          init_gl(gldrawable);
          initialized = true;
        }

        // Render shape
        glMaterialfv(GL_FRONT, GL_AMBIENT, m_CurrentMat->ambient);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, m_CurrentMat->diffuse);
        glMaterialfv(GL_FRONT, GL_SPECULAR, m_CurrentMat->specular);
        glMaterialf(GL_FRONT, GL_SHININESS, m_CurrentMat->shininess * 128.0);
        glCallList(m_ListBase + m_CurrentShape);
      }

      //
      // Scene class implementation.
      //

      const float Scene::CLEAR_COLOR[4] = { 0.5, 0.5, 0.8, 1.0 };
      const float Scene::CLEAR_DEPTH    = 1.0;

      const float Scene::LIGHT0_POSITION[4] = { 0.0, 3.0, 3.0, 0.0 };
      const float Scene::LIGHT0_AMBIENT[4]  = { 0.0, 0.0, 0.0, 1.0 };
      const float Scene::LIGHT0_DIFFUSE[4]  = { 1.0, 1.0, 1.0, 1.0 };

      const float Scene::LIGHT_MODEL_AMBIENT[4]       = { 0.2, 0.2, 0.2, 1.0 };
      const float Scene::LIGHT_MODEL_LOCAL_VIEWER[1]  = { 0.0 };

      Scene::Scene()
        : m_Menu(0)
      {
        //
        // Configure OpenGL-capable visual.
        //

        Glib::RefPtr<Gdk::GL::Config> glconfig;

        // Try double-buffered visual
        glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB    |
                                           Gdk::GL::MODE_DEPTH  |
                                           Gdk::GL::MODE_DOUBLE);
        if (!glconfig) {
          std::cerr << "*** Cannot find the double-buffered visual.\n"
                    << "*** Trying single-buffered visual.\n";

          // Try single-buffered visual
          glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB   |
                                             Gdk::GL::MODE_DEPTH);
          if (!glconfig) {
            std::cerr << "*** Cannot find any OpenGL-capable visual.\n";
            std::exit(1);
          }
        }

        // print frame buffer attributes.
        GLConfigUtil::examine_gl_attrib(glconfig);

        //
        // Set OpenGL-capability to the widget.
        //

        set_gl_capability(glconfig);

        //
        // Add events.
        //
        add_events(Gdk::BUTTON1_MOTION_MASK    |
                   Gdk::BUTTON2_MOTION_MASK    |
                   Gdk::BUTTON_PRESS_MASK      |
                   Gdk::BUTTON_RELEASE_MASK    |
                   Gdk::VISIBILITY_NOTIFY_MASK);

        // View transformation signals.
        signal_button_press_event().connect(
          sigc::bind(sigc::mem_fun(m_View, &View::on_button_press_event), this));
        signal_button_release_event().connect(
          sigc::bind(sigc::mem_fun(m_View, &View::on_button_release_event), this));
        signal_motion_notify_event().connect(
          sigc::bind(sigc::mem_fun(m_View, &View::on_motion_notify_event), this));

        //
        // Popup menu.
        //

        m_Menu = create_popup_menu();
      }

      Scene::~Scene()
      {
      }

      void Scene::on_realize()
      {
        // We need to call the base on_realize()
        Gtk::DrawingArea::on_realize();

        //
        // Get GL::Drawable.
        //

        Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();

        //
        // GL calls.
        //

        // *** OpenGL BEGIN ***
        if (!gldrawable->gl_begin(get_gl_context()))
          return;

        glClearColor(CLEAR_COLOR[0], CLEAR_COLOR[1], CLEAR_COLOR[2], CLEAR_COLOR[3]);
        glClearDepth(CLEAR_DEPTH);

        glLightfv(GL_LIGHT0, GL_POSITION, LIGHT0_POSITION);
        glLightfv(GL_LIGHT0, GL_AMBIENT,  LIGHT0_AMBIENT);
        glLightfv(GL_LIGHT0, GL_DIFFUSE,  LIGHT0_DIFFUSE);
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, LIGHT_MODEL_AMBIENT);
        glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, LIGHT_MODEL_LOCAL_VIEWER);

        glFrontFace(GL_CW);
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glEnable(GL_AUTO_NORMAL);
        glEnable(GL_NORMALIZE);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);

        gldrawable->gl_end();
        // *** OpenGL END ***
      }

      bool Scene::on_configure_event(GdkEventConfigure* event)
      {
        //
        // Get GL::Drawable.
        //

        Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();

        //
        // GL calls.
        //

        // *** OpenGL BEGIN ***
        if (!gldrawable->gl_begin(get_gl_context()))
          return false;

        m_View.frustum(get_width(), get_height());

        gldrawable->gl_end();
        // *** OpenGL END ***

        return true;
      }

      bool Scene::on_expose_event(GdkEventExpose* event)
      {
        //
        // Get GL::Drawable.
        //

        Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();

        //
        // GL calls.
        //

        // *** OpenGL BEGIN ***
        if (!gldrawable->gl_begin(get_gl_context()))
          return false;

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glLoadIdentity();

        // View transformation.
        m_View.xform();

        // Logo model.
        m_Model.draw(gldrawable);

        // Swap buffers.
        if (gldrawable->is_double_buffered())
          gldrawable->swap_buffers();
        else
          glFlush();

        gldrawable->gl_end();
        // *** OpenGL END ***

        return true;
      }

      bool Scene::on_button_press_event(GdkEventButton* event)
      {
        if (event->button == 3) {
          m_Menu->popup(event->button, event->time);
          return true;
        }

        // don't block
        return false;
      }

      bool Scene::on_unmap_event(GdkEventAny* event)
      {
        idle_remove();

        return true;
      }

      bool Scene::on_visibility_notify_event(GdkEventVisibility* event)
      {
        if (m_View.is_animate()) {
          if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
            idle_remove();
          else
            idle_add();
        }

        return true;
      }

      bool Scene::on_idle()
      {
        // Invalidate whole window.
        invalidate();
        // Update window synchronously (fast).
        update();

        return true;
      }

      void Scene::idle_add()
      {
        if (!m_ConnectionIdle.connected())
          m_ConnectionIdle = Glib::signal_idle().connect(
            sigc::mem_fun(*this, &Scene::on_idle), GDK_PRIORITY_REDRAW);
      }

      void Scene::idle_remove()
      {
        if (m_ConnectionIdle.connected())
          m_ConnectionIdle.disconnect();
      }

      void Scene::change_shape(Model::ShapeType shape)
      {
        m_Model.set_shape(shape);
        m_View.reset();
      }

      void Scene::change_material(const Model::MaterialProp* material)
      {
        m_Model.set_material(material);
      }

      Gtk::Menu* Scene::create_popup_menu()
      {

        // Shapes submenu
        Gtk::Menu* shapes_menu = Gtk::manage(new Gtk::Menu());
        {
          Gtk::Menu::MenuList& menu_list = shapes_menu->items();

          // Cube
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Cube",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_shape),
                       Model::SHAPE_CUBE)));

          // Sphere
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Sphere",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_shape),
                       Model::SHAPE_SPHERE)));

          // Cone
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Cone",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_shape),
                       Model::SHAPE_CONE)));

          // Torus
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Torus",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_shape),
                       Model::SHAPE_TORUS)));

          // Tetrahedron
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Tetrahedron",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_shape),
                       Model::SHAPE_TETRAHEDRON)));

          // Octahedron
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Octahedron",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_shape),
                       Model::SHAPE_OCTAHEDRON)));

          // Dodecahedron
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Dodecahedron",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_shape),
                       Model::SHAPE_DODECAHEDRON)));

          // Icosahedron
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Icosahedron",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_shape),
                       Model::SHAPE_ICOSAHEDRON)));

          // Teapot
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Teapot",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_shape),
                       Model::SHAPE_TEAPOT)));

        }

        // Materials submenu
        Gtk::Menu* materials_menu = Gtk::manage(new Gtk::Menu());
        {
          Gtk::Menu::MenuList& menu_list = materials_menu->items();

          // Emerald
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Emerald",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_EMERALD)));

          // Jade
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Jade",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_JADE)));

          // Obsidian
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Obsidian",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_OBSIDIAN)));

          // Pearl
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Pearl",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_PEARL)));

          // Ruby
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Ruby",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_RUBY)));

          // Turquoise
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Turquoise",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_TURQUOISE)));

          // Brass
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Brass",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_BRASS)));

          // Bronze
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Bronze",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_BRONZE)));

          // Chrome
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Chrome",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_CHROME)));

          // Copper
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Copper",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_COPPER)));

          // Gold
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Gold",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_GOLD)));

          // Silver
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Silver",
            sigc::bind(sigc::mem_fun(*this, &Scene::change_material),
                       &Model::MAT_SILVER)));

        }

        // Root popup menu
        Gtk::Menu* menu = Gtk::manage(new Gtk::Menu());
        {
          Gtk::Menu::MenuList& menu_list = menu->items();

          // Shapes submenu
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Shapes",
                                                          *shapes_menu));

          // Materials submenu
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Materials",
                                                          *materials_menu));

          // Quit
          menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Quit",
            sigc::ptr_fun(&Gtk::Main::quit)));
        }

        return menu;
      }

      //
      // Application class implementation.
      //

      const Glib::ustring Application::APP_NAME = "Shapes";

      Application::Application()
        : m_VBox(false, 0), m_ButtonQuit("Quit")
      {
        //
        // Top-level window.
        //

        set_title(APP_NAME);

        // Get automatically redrawn if any of their children changed allocation.
        set_reallocate_redraws(true);

        add(m_VBox);

        //
        // Scene.
        //

        m_Scene.set_size_request(300, 300);

        m_VBox.pack_start(m_Scene);

        //
        // Simple quit button.
        //

        m_ButtonQuit.signal_clicked().connect(
          sigc::mem_fun(*this, &Application::on_button_quit_clicked));

        m_VBox.pack_start(m_ButtonQuit, Gtk::PACK_SHRINK, 0);

        //
        // Show window.
        //

        show_all();
      }

      Application::~Application()
      {
      }

      void Application::on_button_quit_clicked()
      {
        Gtk::Main::quit();
      }

      bool Application::on_key_press_event(GdkEventKey* event)
      {
        switch (event->keyval) {
        case GDK_Escape:
          Gtk::Main::quit();
          break;
        default:
          return true;
        }

        m_Scene.invalidate();

        return true;
      }

    } // namespace Shapes

    //
    // Main.
    //

    int main(int argc, char** argv)
    {
      Gtk::Main kit(argc, argv);

      //
      // Init gtkglextmm.
      //

      Gtk::GL::init(argc, argv);

      //
      // Query OpenGL extension version.
      //

      int major, minor;
      Gdk::GL::query_version(major, minor);
      std::cout << "OpenGL extension version - "
                << major << "." << minor << std::endl;

      //
      // Instantiate and run the application.
      //

      Shapes::Application application;

      kit.run(application);

      return 0;
    }