// -*- C++ -*- /* * logo.cc: * GtkGLExt logo demo. * * written by Naofumi Yasufuku <naofumi@users.sourceforge.net> */ #include <iostream> #include <cstdlib> #include <cstring> #include <cmath> #include <GL/gl.h> #include <GL/glu.h> #ifdef G_OS_WIN32 #define WIN32_LEAN_AND_MEAN 1 #include <windows.h> #endif #include "logo.h" // // logo_draw_*() functions. // namespace LogoModel { #include "logo-model.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; } // // Logo classes. // namespace Logo { // // View class implementation. // const float View::NEAR_CLIP = 2.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 = -30.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 = 20.0; const float View::INIT_SCALE = 1.0; const float View::SCALE_MAX = 2.0; const float View::SCALE_MIN = 0.5; View::View() : m_Scale(INIT_SCALE), m_BeginX(0.0), m_BeginY(0.0) { 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::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; } bool View::on_button_press_event(GdkEventButton* event, Scene* scene) { m_BeginX = event->x; m_BeginY = event->y; // don't block return false; } bool View::on_motion_notify_event(GdkEventMotion* event, Scene* scene) { if (scene == 0) return false; float w = scene->get_width(); float h = scene->get_height(); float x = event->x; float y = event->y; float d_quat[4]; bool redraw = false; // Rotation. if (event->state & GDK_BUTTON1_MASK) { Trackball::trackball(d_quat, (2.0 * m_BeginX - w) / w, (h - 2.0 * m_BeginY) / h, (2.0 * x - w) / w, (h - 2.0 * y) / h); Trackball::add_quats(d_quat, m_Quat, m_Quat); 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->anim_is_enabled()) scene->invalidate(); // don't block return false; } // // Model class implementation. // const float Model::MAT_SPECULAR[4] = { 0.5, 0.5, 0.5, 1.0 }; const float Model::MAT_SHININESS[1] = { 10.0 }; const float Model::MAT_BLACK[4] = { 0.0, 0.0, 0.0, 1.0 }; const float Model::MAT_RED[4] = { 1.0, 0.0, 0.0, 1.0 }; const float Model::MAT_GREEN[4] = { 0.0, 1.0, 0.0, 1.0 }; const float Model::MAT_BLUE[4] = { 0.0, 0.0, 1.0, 1.0 }; const unsigned int Model::DEFAULT_ROT_COUNT = 100; static float AXIS_X[3] = { 1.0, 0.0, 0.0 }; static float AXIS_Y[3] = { 0.0, 1.0, 0.0 }; static float AXIS_Z[3] = { 0.0, 0.0, 1.0 }; const Model::RotMode Model::ROT_MODE[] = { { AXIS_X, 1.0 }, { AXIS_Y, 1.0 }, { AXIS_X, 1.0 }, { AXIS_Z, 1.0 }, { AXIS_X, 1.0 }, { AXIS_Y, -1.0 }, { AXIS_X, 1.0 }, { AXIS_Z, -1.0 }, { 0, 0.0 } // terminator }; Model::Model(unsigned int rot_count, bool enable_anim) : m_RotCount(rot_count), m_EnableAnim(enable_anim), m_Mode(0), m_Counter(0) { reset_anim(); } Model::~Model() { } void Model::init_gl() { glEnable(GL_CULL_FACE); glPushMatrix(); glMaterialfv(GL_FRONT, GL_SPECULAR, MAT_SPECULAR); glMaterialfv(GL_FRONT, GL_SHININESS, MAT_SHININESS); /* Center black cube. */ glNewList(CUBE, GL_COMPILE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK); LogoModel::logo_draw_cube(); glEndList(); /* Forward "G". */ glNewList(G_FORWARD, GL_COMPILE); glDisable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLUE); LogoModel::logo_draw_g_plane(); glEnable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK); LogoModel::logo_draw_g(); glEndList(); /* Backward "G". */ glNewList(G_BACKWARD, GL_COMPILE); glPushMatrix(); glRotatef(180.0, 1.0, 0.0, 0.0); glDisable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLUE); LogoModel::logo_draw_g_plane(); glEnable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK); LogoModel::logo_draw_g(); glPopMatrix(); glEndList(); /* Forward "T". */ glNewList(T_FORWARD, GL_COMPILE); glDisable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_RED); LogoModel::logo_draw_t_plane(); glEnable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK); LogoModel::logo_draw_t(); glEndList(); /* Backward "T". */ glNewList(T_BACKWARD, GL_COMPILE); glPushMatrix(); glRotatef(180.0, 1.0, 0.0, 0.0); glDisable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_RED); LogoModel::logo_draw_t_plane(); glEnable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK); LogoModel::logo_draw_t(); glPopMatrix(); glEndList(); /* Forward "K". */ glNewList(K_FORWARD, GL_COMPILE); glDisable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_GREEN); LogoModel::logo_draw_k_plane(); glEnable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK); LogoModel::logo_draw_k(); glEndList(); /* Backward "K". */ glNewList(K_BACKWARD, GL_COMPILE); glPushMatrix(); glRotatef(180.0, 0.0, 0.0, 1.0); glDisable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_GREEN); LogoModel::logo_draw_k_plane(); glEnable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK); LogoModel::logo_draw_k(); glPopMatrix(); glEndList(); glPopMatrix(); glEnable(GL_NORMALIZE); } void Model::draw() { // Init GL context. static bool initialized = false; if (!initialized) { init_gl(); initialized = true; } // Animation. if (m_EnableAnim) { if (m_Counter == m_RotCount) { if (ROT_MODE[++m_Mode].axis == 0) m_Mode = 0; m_Counter = 0; } float d_quat[4]; Trackball::axis_to_quat(ROT_MODE[m_Mode].axis, ROT_MODE[m_Mode].sign * G_PI_2 / m_RotCount, d_quat); Trackball::add_quats(d_quat, m_Quat, m_Quat); ++m_Counter; } // Draw logo model. glPushMatrix(); glTranslatef(m_Pos[0], m_Pos[1], m_Pos[2]); float m[4][4]; Trackball::build_rotmatrix(m, m_Quat); glMultMatrixf(&m[0][0]); glRotatef(90.0, 1.0, 0.0, 0.0); glCallList(CUBE); glCallList(G_FORWARD); glCallList(G_BACKWARD); glCallList(T_FORWARD); glCallList(T_BACKWARD); glCallList(K_FORWARD); glCallList(K_BACKWARD); glPopMatrix(); } void Model::reset_anim() { m_Pos[0] = 0.0; m_Pos[1] = 0.0; m_Pos[2] = 0.0; m_Quat[0] = 0.0; m_Quat[1] = 0.0; m_Quat[2] = 0.0; m_Quat[3] = 1.0; m_Mode = 0; m_Counter = 0; } // // Scene class implementation. // const unsigned int Scene::TIMEOUT_INTERVAL = 10; 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, 0.0, 30.0, 0.0 }; const float Scene::LIGHT0_DIFFUSE[4] = { 1.0, 1.0, 1.0, 1.0 }; const float Scene::LIGHT0_SPECULAR[4] = { 1.0, 1.0, 1.0, 1.0 }; Scene::Scene(unsigned int rot_count, bool enable_anim) : m_Menu(0), m_Model(rot_count, enable_anim) { // // 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::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_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_DIFFUSE, LIGHT0_DIFFUSE); glLightfv(GL_LIGHT0, GL_SPECULAR, LIGHT0_SPECULAR); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); 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(); // 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_map_event(GdkEventAny* event) { if (m_Model.anim_is_enabled()) timeout_add(); return true; } bool Scene::on_unmap_event(GdkEventAny* event) { timeout_remove(); return true; } bool Scene::on_visibility_notify_event(GdkEventVisibility* event) { if (m_Model.anim_is_enabled()) { if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) timeout_remove(); else timeout_add(); } return true; } bool Scene::on_timeout() { // Invalidate whole window. invalidate(); // Update window synchronously (fast). update(); return true; } void Scene::timeout_add() { if (!m_ConnectionTimeout.connected()) m_ConnectionTimeout = Glib::signal_timeout().connect( sigc::mem_fun(*this, &Scene::on_timeout), TIMEOUT_INTERVAL); } void Scene::timeout_remove() { if (m_ConnectionTimeout.connected()) m_ConnectionTimeout.disconnect(); } void Scene::toggle_anim() { if (m_Model.anim_is_enabled()) { m_Model.disable_anim(); timeout_remove(); } else { m_Model.enable_anim(); timeout_add(); } } void Scene::init_anim() { m_View.reset(); m_Model.reset_anim(); invalidate(); } Gtk::Menu* Scene::create_popup_menu() { Gtk::Menu* menu = Gtk::manage(new Gtk::Menu()); Gtk::Menu::MenuList& menu_list = menu->items(); // Toggle animation menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Toggle Animation", sigc::mem_fun(*this, &Scene::toggle_anim))); // Init orientation menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Initialize", sigc::mem_fun(*this, &Scene::init_anim))); // 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 = "Logo"; Application::Application(unsigned int rot_count, bool enable_anim) : m_VBox(false, 0), m_Scene(rot_count, enable_anim), 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); // // Application 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_a: m_Scene.toggle_anim(); break; case GDK_i: m_Scene.init_anim(); break; case GDK_Escape: Gtk::Main::quit(); break; default: return true; } m_Scene.invalidate(); return true; } } // namespace Logo // // Main. // int main(int argc, char** argv) { Gtk::Main kit(argc, argv); // // Init gtkglextmm. // Gtk::GL::init(argc, argv); // // Parse arguments. // unsigned int rot_count = Logo::Model::DEFAULT_ROT_COUNT; bool enable_anim = true; bool arg_count = false; for (int i = 1; i < argc; ++i) { if (arg_count) rot_count = std::atoi(argv[i]); if (std::strcmp(argv[i], "--help") == 0 || std::strcmp(argv[i], "-h") == 0) { std::cerr << "Usage: " << argv[0] << " [-count num] [-noanim] [--help]\n"; std::exit(0); } if (std::strcmp(argv[i], "-count") == 0) arg_count = true; if (std::strcmp(argv[i], "-noanim") == 0) enable_anim = false; } // // 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. // Logo::Application application(rot_count, enable_anim); kit.run(application); return 0; }