/* * Copyright (C) 1998 Janne Löf * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Modified on 10th June 2002, for porting this program * to the 'gtkglext-0.1.0` extension of gtk-2.0. * * Alif Wahid, */ /* * Improved mouse operation. * * Naofumi Yasufuku */ #include #include #include #include #include #include #ifdef G_OS_WIN32 #define WIN32_LEAN_AND_MEAN 1 #include #endif #include #include #include "trackball.h" #include "lw.h" #define VIEW_ASPECT 1.3 #define ANIMATE_THRESHOLD 25.0 #define TIMEOUT_INTERVAL 10 /* information needed to display lightwave mesh */ typedef struct { gint do_init; /* true if initgl not yet called */ lwObject *lwobject; /* lightwave object mesh */ float beginx,beginy; /* position of mouse */ float dx,dy; float quat[4]; /* orientation of object */ float dquat[4]; float zoom; /* field of view in degrees */ gboolean animate; guint timeout_id; } mesh_info; const char help_text[] = "Usage: viewlw [OPTION]... [FILE]...\n" "View LightWave 3D objects.\n" "\n" "Options:\n" " --help display help\n" "\n" "In the program:\n" " Mouse button 1 + drag spin (virtual trackball)\n" " Mouse button 2 + drag zoom\n" " Mouse button 3 popup menu\n" "\n"; static GdkGLConfig *glconfig = NULL; static void select_lwobject(void); static gint show_lwobject(const char *lwobject_name); static void timeout_add(GtkWidget *widget); static void timeout_remove(GtkWidget *widget); static void toggle_animation(GtkWidget *widget); static void initgl(void) { GLfloat light0_pos[4] = { -50.0, 50.0, 0.0, 0.0 }; GLfloat light0_color[4] = { .6, .6, .6, 1.0 }; /* white light */ GLfloat light1_pos[4] = { 50.0, 50.0, 0.0, 0.0 }; GLfloat light1_color[4] = { .4, .4, 1, 1.0 }; /* cold blue light */ /* remove back faces */ glDisable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); /* speedups */ glEnable(GL_DITHER); glShadeModel(GL_SMOOTH); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST); /* light */ glLightfv(GL_LIGHT0, GL_POSITION, light0_pos); glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_color); glLightfv(GL_LIGHT1, GL_POSITION, light1_pos); glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_color); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); glEnable(GL_LIGHTING); glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); } static gboolean expose(GtkWidget *widget, GdkEventExpose *event) { GdkGLContext *glcontext = gtk_widget_get_gl_context(widget); GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget); GLfloat m[4][4]; mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); /* draw only last expose */ if (event->count > 0) { return TRUE; } /*** OpenGL BEGIN ***/ if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) goto NO_GL; /* basic initialization */ if (info->do_init == TRUE) { initgl(); info->do_init = FALSE; } /* view */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(info->zoom, VIEW_ASPECT, 1,100); glMatrixMode(GL_MODELVIEW); /* draw object */ glClearColor(.3,.4,.6,1); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0,0,-30); add_quats(info->dquat, info->quat, info->quat); build_rotmatrix(m,info->quat); glMultMatrixf(&m[0][0]); lw_object_show(info->lwobject); /* swap backbuffer to front */ if (gdk_gl_drawable_is_double_buffered(gldrawable)) gdk_gl_drawable_swap_buffers(gldrawable); else glFlush(); gdk_gl_drawable_gl_end(gldrawable); /*** OpenGL END ***/ NO_GL: return TRUE; } static gboolean configure(GtkWidget *widget, GdkEventConfigure *event) { GdkGLContext *glcontext; GdkGLDrawable *gldrawable; g_return_val_if_fail(widget && event, FALSE); glcontext = gtk_widget_get_gl_context(widget); gldrawable = gtk_widget_get_gl_drawable(widget); /*** OpenGL BEGIN ***/ if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) goto NO_GL; glViewport (0, 0, widget->allocation.width, widget->allocation.height); gdk_gl_drawable_gl_end(gldrawable); /*** OpenGL END ***/ NO_GL: return TRUE; } static void destroy(GtkWidget *widget) { /* delete mesh info */ mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); timeout_remove(widget); lw_object_free(info->lwobject); g_free(info); } static gboolean button_press(GtkWidget *widget, GdkEventButton *event) { mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); if (info->animate) { if (event->button == 1) toggle_animation (widget); } else { info->dquat[0] = 0.0; info->dquat[1] = 0.0; info->dquat[2] = 0.0; info->dquat[3] = 1.0; } /* beginning of drag, reset mouse position */ info->beginx = event->x; info->beginy = event->y; return FALSE; } static gboolean button_release(GtkWidget *widget, GdkEventButton *event) { mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); if (!info->animate) { if (event->button == 1 && ((info->dx*info->dx + info->dy*info->dy) > ANIMATE_THRESHOLD)) toggle_animation (widget); } info->dx = 0.0; info->dy = 0.0; return FALSE; } static gboolean motion_notify(GtkWidget *widget, GdkEventMotion *event) { int x = 0; int y = 0; GdkModifierType state = 0; float width, height; gboolean redraw = FALSE; mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); if (event->is_hint) { // fix this! #if !defined(WIN32) gdk_window_get_pointer(event->window, &x, &y, &state); #endif } else { x = event->x; y = event->y; state = event->state; } width = widget->allocation.width; height = widget->allocation.height; if (state & GDK_BUTTON1_MASK) { /* drag in progress, simulate trackball */ trackball( info->dquat, (2.0*info->beginx - width) / width, ( height - 2.0*info->beginy) / height, ( 2.0*x - width) / width, ( height - 2.0*y) / height ); info->dx = x - info->beginx; info->dy = y - info->beginy; /* orientation has changed, redraw mesh */ redraw = TRUE; } if (state & GDK_BUTTON2_MASK) { /* zooming drag */ info->zoom -= ((y - info->beginy) / height) * 40.0; if (info->zoom < 5.0) info->zoom = 5.0; if (info->zoom > 120.0) info->zoom = 120.0; /* zoom has changed, redraw mesh */ redraw = TRUE; } info->beginx = x; info->beginy = y; if (redraw && !info->animate) gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE); return TRUE; } static gboolean timeout(GtkWidget *widget) { /* Invalidate the whole window. */ gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE); /* Update synchronously. */ gdk_window_process_updates (widget->window, FALSE); return TRUE; } static void timeout_add(GtkWidget *widget) { mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); if (info->timeout_id == 0) { info->timeout_id = g_timeout_add(TIMEOUT_INTERVAL, (GSourceFunc)timeout, widget); } } static void timeout_remove(GtkWidget *widget) { mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); if (info->timeout_id != 0) { g_source_remove(info->timeout_id); info->timeout_id = 0; } } static void toggle_animation(GtkWidget *widget) { mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); info->animate = !info->animate; if (info->animate) { timeout_add(widget); } else { timeout_remove(widget); info->dquat[0] = 0.0; info->dquat[1] = 0.0; info->dquat[2] = 0.0; info->dquat[3] = 1.0; gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE); } } static gboolean map_event(GtkWidget *widget, GdkEvent *event) { mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); if (info->animate) timeout_add(widget); return TRUE; } static gboolean unmap_event(GtkWidget *widget, GdkEvent *event) { timeout_remove(widget); return TRUE; } static gboolean visibility_notify_event(GtkWidget *widget, GdkEventVisibility *event) { mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); if (info->animate) { if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) timeout_remove(widget); else timeout_add(widget); } return TRUE; } static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event) { mesh_info *info = (mesh_info*)g_object_get_data(G_OBJECT(widget), "mesh_info"); switch (event->keyval) { case GDK_plus: /* zooming drag */ info->zoom -= 2.0; if (info->zoom < 5.0) info->zoom = 5.0; if (info->zoom > 120.0) info->zoom = 120.0; /* zoom has changed, redraw mesh */ gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE); break; case GDK_minus: /* zooming drag */ info->zoom += 2.0; if (info->zoom < 5.0) info->zoom = 5.0; if (info->zoom > 120.0) info->zoom = 120.0; /* zoom has changed, redraw mesh */ gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE); break; case GDK_Escape: gtk_main_quit(); break; default: return FALSE; } return TRUE; } static gboolean popup_menu_handler(GtkWidget *widget, GdkEventButton *event) { if (event->button == 3) { gtk_menu_popup(GTK_MENU(widget), NULL, NULL, NULL, NULL, event->button, event->time); return TRUE; } return FALSE; } static void popup_menu_detacher(GtkWidget *attach_widget, GtkMenu *menu) { } static void create_popup_menu(GtkWidget *widget) { GtkWidget *menu = gtk_menu_new(); GtkWidget *open_item = gtk_menu_item_new_with_label("Open"); GtkWidget *quit_item = gtk_menu_item_new_with_label("Quit"); GtkWidget *quit_all_item = gtk_menu_item_new_with_label("Quit All"); gtk_menu_shell_append(GTK_MENU_SHELL(menu), open_item); gtk_menu_shell_append(GTK_MENU_SHELL(menu), quit_item); gtk_menu_shell_append(GTK_MENU_SHELL(menu), quit_all_item); gtk_widget_show(open_item); gtk_widget_show(quit_item); gtk_widget_show(quit_all_item); gtk_menu_attach_to_widget(GTK_MENU(menu),GTK_WIDGET(widget),popup_menu_detacher); g_signal_connect_swapped(G_OBJECT(widget), "destroy", G_CALLBACK(gtk_menu_detach), menu); g_signal_connect_swapped(G_OBJECT(widget), "button_press_event", G_CALLBACK(popup_menu_handler), menu); g_signal_connect(G_OBJECT(open_item), "activate", G_CALLBACK(select_lwobject), NULL); g_signal_connect_swapped(G_OBJECT(quit_item), "activate", G_CALLBACK(gtk_widget_destroy), widget); g_signal_connect(G_OBJECT(quit_all_item), "activate", G_CALLBACK(gtk_main_quit), NULL); } static gint window_count = 0; /* number of windows on screen */ static gint window_destroy(GtkWidget *widget) { /* if this was last window quit */ if (--window_count == 0) gtk_main_quit(); return TRUE; } static gint show_lwobject(const char *lwobject_name) { GtkWidget *window, *frame, *glarea; mesh_info *info; lwObject *lwobject; /* read lightwave object */ if (!lw_is_lwobject(lwobject_name)) { g_print("%s is not a LightWave 3D object\n", lwobject_name); return FALSE; } lwobject = lw_object_read(lwobject_name); if (lwobject == NULL) { g_print("Can't read LightWave 3D object %s\n", lwobject_name); return FALSE; } lw_object_scale(lwobject, 10.0 / lw_object_radius(lwobject)); /* create aspect frame */ frame = gtk_aspect_frame_new(NULL, 0.5,0.5, VIEW_ASPECT, FALSE); /* create new OpenGL widget */ glarea = gtk_drawing_area_new(); if (glarea == NULL) { lw_object_free(lwobject); g_print("Can't create GtkDrawingArea widget\n"); return FALSE; } /* Set OpenGL-capability to the widget. */ gtk_widget_set_gl_capability(GTK_WIDGET (glarea), glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE); /* set up events and signals for OpenGL widget */ gtk_widget_set_events(glarea, GDK_EXPOSURE_MASK| GDK_BUTTON_PRESS_MASK| GDK_BUTTON_RELEASE_MASK| GDK_POINTER_MOTION_MASK| GDK_POINTER_MOTION_HINT_MASK); g_signal_connect(G_OBJECT(glarea), "expose_event", G_CALLBACK(expose), NULL); g_signal_connect(G_OBJECT(glarea), "motion_notify_event", G_CALLBACK(motion_notify), NULL); g_signal_connect(G_OBJECT(glarea), "button_press_event", G_CALLBACK(button_press), NULL); g_signal_connect(G_OBJECT(glarea), "button_release_event", G_CALLBACK(button_release), NULL); g_signal_connect(G_OBJECT(glarea), "configure_event", G_CALLBACK(configure), NULL); g_signal_connect(G_OBJECT (glarea), "map_event", G_CALLBACK (map_event), NULL); g_signal_connect(G_OBJECT (glarea), "unmap_event", G_CALLBACK (unmap_event), NULL); g_signal_connect(G_OBJECT (glarea), "visibility_notify_event", G_CALLBACK (visibility_notify_event), NULL); g_signal_connect(G_OBJECT(glarea), "destroy", G_CALLBACK(destroy), NULL); gtk_widget_set_size_request(glarea, 200,200/VIEW_ASPECT); /* minimum size */ /* set up mesh info */ info = (mesh_info*)g_malloc(sizeof(mesh_info)); info->do_init = TRUE; info->lwobject = lwobject; info->beginx = 0; info->beginy = 0; info->dx = 0; info->dy = 0; info->quat[0] = 0; info->quat[1] = 0; info->quat[2] = 0; info->quat[3] = 1; info->dquat[0] = 0; info->dquat[1] = 0; info->dquat[2] = 0; info->dquat[3] = 1; info->zoom = 45; info->animate = FALSE; info->timeout_id = 0; trackball(info->quat , 0.0, 0.0, 0.0, 0.0); g_object_set_data(G_OBJECT(glarea), "mesh_info", info); /* create new top level window */ window = gtk_window_new( GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), lwobject_name); gtk_container_set_border_width(GTK_CONTAINER(window), 10); gtk_container_set_reallocate_redraws(GTK_CONTAINER (window), TRUE); create_popup_menu(window); /* add popup menu to window */ /* key_press_event handler for top-level window */ g_signal_connect_swapped(G_OBJECT (window), "key_press_event", G_CALLBACK (key_press_event), glarea); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(window_destroy), NULL); window_count++; /* destroy this window when exiting from gtk_main() */ gtk_quit_add_destroy(1, GTK_OBJECT(window)); /* put glarea into window and show it all */ gtk_container_add(GTK_CONTAINER(window), frame); gtk_container_add(GTK_CONTAINER(frame),glarea); gtk_widget_show(glarea); gtk_widget_show(frame); gtk_widget_show(window); return TRUE; } static gint filew_ok(GtkWidget *widget, GtkWidget *filew) { if (show_lwobject(gtk_file_selection_get_filename(GTK_FILE_SELECTION(filew))) == TRUE) gtk_widget_destroy(filew); return TRUE; } static void select_lwobject(void) { GtkWidget *filew = gtk_file_selection_new("Select LightWave 3D object"); g_signal_connect(G_OBJECT(GTK_FILE_SELECTION (filew)->ok_button), "clicked", G_CALLBACK(filew_ok), filew); g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(filew)->cancel_button), "clicked", G_CALLBACK(gtk_widget_destroy), filew); g_signal_connect(G_OBJECT(filew), "destroy", G_CALLBACK(window_destroy), NULL); window_count++; gtk_widget_show(filew); } int main(int argc, char **argv) { /* initialize gtk */ gtk_init(&argc, &argv); /* initialize gtkglext */ gtk_gl_init(&argc, &argv); /* Configure OpenGL-capable visual. */ /* Try double-buffered visual */ glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE); if (glconfig == NULL) { g_print("*** Cannot find the double-buffered visual.\n"); g_print("*** Trying single-buffered visual.\n"); /* Try single-buffered visual */ glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH); if (glconfig == NULL) { g_print("*** No appropriate OpenGL-capable visual found.\n"); exit(1); } } /* help? */ if (argc >= 2 && strcmp(argv[1],"--help")==0) { g_print("%s", help_text); return 0; } if (argc == 1) { /* no filenames, show filerequester */ select_lwobject(); } else { /* show requested files */ int i; for (i=1; i 0) gtk_main(); return 0; }