/* Copyright (C) 2003 by Sean David Fleming sean@ivec.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The GNU GPL can also be found at http://www.gnu.org */ #include #include #include #include #include #include #ifdef __APPLE__ #include #else #include #endif #ifndef __WIN32 #include #endif #include "gdis.h" #include "coords.h" #include "edit.h" #include "file.h" #include "parse.h" #include "task.h" #include "morph.h" #include "matrix.h" #include "opengl.h" #include "render.h" #include "select.h" #include "spatial.h" #include "zone.h" #include "shortcuts.h" #include "interface.h" #include "dialog.h" #define DEBUG 0 #define DELETE_POV_FILE 0 extern struct sysenv_pak sysenv; extern struct elem_pak elements[]; extern GtkWidget *window; /* global render parameters */ struct light_pak current_light; GtkWidget *scale_spin; gdouble render_animate_frames = 100; gint render_animate_overwrite = TRUE; #define SCALE_MAG 10 #define PIX2ANG 0.03 /* this is an OpenGL limitation (ie shininess) */ #define MAX_HL 128 /****************/ /* render setup */ /****************/ void render_setup(GtkWidget *w, gpointer dummy) { /* checks */ if (!sysenv.povray_path) { gui_text_show(ERROR, "POVRay executable was not found.\n"); return; } /* single model background job */ povray_task(); } /******************************/ /* font cancel button handler */ /******************************/ void font_dialog_close(GtkWidget *w, gpointer fsd) { gtk_widget_destroy(GTK_WIDGET(fsd)); } /**************************/ /* font ok button handler */ /**************************/ void gl_font_selected(GtkWidget *w, gpointer fsd) { gchar *fn; fn = gtk_font_selection_dialog_get_font_name ((GtkFontSelectionDialog *) fsd); strcpy(sysenv.gl_fontname, fn); gl_font_free(); redraw_canvas(ALL); } /**********************/ /* drawing font selection */ /**********************/ void gl_font_dialog(void) { GtkWidget *fsd; fsd = gtk_font_selection_dialog_new("Font selection"); /* setup events for the file selection widget */ g_signal_connect(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fsd)->ok_button), "clicked", (GtkSignalFunc) gl_font_selected, fsd); g_signal_connect(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fsd)->cancel_button), "clicked", (GtkSignalFunc) font_dialog_close, fsd); gtk_widget_show(fsd); } /************************************/ /* event handler for display dialog */ /************************************/ void render_refresh(void) { redraw_canvas(ALL); } /********************/ /* Property toggles */ /********************/ void toggle_axes_type(void) { struct model_pak *model; model = sysenv.active_model; if (!model) return; if (model->periodic) { if (model->axes_type == CARTESIAN) model->axes_type = OTHER; else model->axes_type = CARTESIAN; } coords_compute(model); redraw_canvas(SINGLE); } void morph_toggle(GtkWidget *w, gpointer data) { redraw_canvas(SINGLE); } /****************/ /* unified hook */ /****************/ gint event_render_modify(GtkWidget *w, gpointer *obj) { gint id, refresh=0; const gchar *entry; struct model_pak *data; /* checks */ g_return_val_if_fail(obj != NULL, FALSE); /* ascertain type of modification required */ id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(obj), "id")); data = sysenv.active_model; sysenv.moving = FALSE; switch(id) { case ANIM_GIF: sysenv.render.animate_type = ANIM_GIF; break; case ANIM_MPEG: sysenv.render.animate_type = ANIM_MPEG; break; case ANIM_NAME: entry = gtk_entry_get_text(GTK_ENTRY(obj)); g_free(sysenv.render.animate_file); sysenv.render.animate_file = g_strdup(entry); break; case LIGHT_TYPE: entry = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(obj)->entry)); if (g_ascii_strncasecmp(entry, "Positional", 10) == 0) current_light.type = POSITIONAL; else current_light.type = DIRECTIONAL; break; case MORPH_FINISH: entry = (gchar *) gtk_entry_get_text(GTK_ENTRY(obj)); g_free(sysenv.render.morph_finish); sysenv.render.morph_finish = g_strdup(entry); refresh++; break; } if (refresh) redraw_canvas(ALL); return(FALSE); } /****************************/ /* rendering mode for model */ /****************************/ void render_mode_set(GtkWidget *w, gpointer data) { gint mode; struct model_pak *model; model = sysenv.active_model; if (!model) return; mode = GPOINTER_TO_INT(data); spatial_destroy_by_label("polyhedra", model); spatial_destroy_by_label("zones", model); /* new mode becomes default for the model */ model->default_render_mode = mode; /* selection based rendering */ if (model->selection) core_render_mode_set(mode, model->selection); else core_render_mode_set(mode, model->cores); redraw_canvas(SINGLE); } /*************************************/ /* display polyhedral representation */ /*************************************/ void render_mode_polyhedral(void) { struct model_pak *model; model = sysenv.active_model; if (!model) return; create_polyhedra(model); coords_init(REDO_COORDS, model); redraw_canvas(SINGLE); } /*************************************/ /* display zone based representation */ /*************************************/ void render_mode_zone(void) { gpointer za; struct model_pak *model; model = sysenv.active_model; if (!model) return; core_render_mode_set(ZONE, model->cores); /* CURRENT */ za = zone_make(sysenv.render.zone_size, model); zone_display_init(za, model); zone_free(za); coords_init(REDO_COORDS, model); redraw_canvas(SINGLE); } /*************************************/ /* wire-frame / solid atom rendering */ /*************************************/ void render_wire_atoms(void) { struct model_pak *model; model = sysenv.active_model; if (!model) return; if (model->selection) core_render_wire_set(TRUE, model->selection); else core_render_wire_set(TRUE, model->cores); redraw_canvas(SINGLE); } void render_solid_atoms(void) { struct model_pak *model; model = sysenv.active_model; if (!model) return; if (model->selection) core_render_wire_set(FALSE, model->selection); else core_render_wire_set(FALSE, model->cores); redraw_canvas(SINGLE); } /************************************/ /* toggle the render to file option */ /************************************/ /* void toggle_animate(GtkWidget *w, GtkWidget *a_frame) { if (sysenv.render.animate) gtk_widget_set_sensitive(GTK_WIDGET(a_frame), TRUE); else gtk_widget_set_sensitive(GTK_WIDGET(a_frame), FALSE); } */ /*****************************/ /* task orientated rendering */ /*****************************/ void povray_exec_task(gpointer ptr) { GString *cmd; g_return_if_fail(ptr != NULL); cmd = g_string_new(NULL); /* build the command line */ g_string_sprintf(cmd, "%s +I%s -Ga -P +W%d +H%d +FT", sysenv.povray_path, (gchar *) ptr, (gint) sysenv.render.width, (gint) sysenv.render.height); if (sysenv.render.antialias) g_string_sprintfa(cmd, " +A +AM2"); task_sync(cmd->str); g_string_free(cmd, TRUE); } /***************************/ /* task orientated viewing */ /***************************/ void exec_img_task(gpointer ptr) { gchar *cmd, *basename, *fullname; /* post povray command */ g_return_if_fail(ptr != NULL); /* construct image name */ basename = strdup_basename((gchar *) ptr); g_free(ptr); fullname = g_build_filename(sysenv.cwd, basename, NULL); /* remove .pov file */ if (sysenv.render.no_keep_tempfiles) { cmd = g_strdup_printf("%s.pov", fullname); unlink(cmd); g_free(cmd); } /* construct viewing task command */ cmd = g_strdup_printf("%s %s.tga", sysenv.viewer_path, fullname); g_spawn_command_line_async(cmd, NULL); /* cleanup */ g_free(basename); g_free(fullname); g_free(cmd); } /*************************/ /* foreground rendering */ /*************************/ void povray_exec(gchar *name) { GString *cmd; cmd = g_string_new(NULL); /* build the command line */ g_string_sprintf(cmd, "%s +I%s -GA -P +W%d +H%d +FT ", sysenv.povray_path, name, (gint) sysenv.render.width , (gint) sysenv.render.height); if (sysenv.render.antialias) g_string_sprintfa(cmd,"+A +AM2 "); /* after rendering delete input file, */ /* g_string_sprintfa(cmd,"; rm -rf %s ",name); */ /* execute */ system(cmd->str); printf("\n"); g_string_free(cmd, TRUE); } /************************/ /* background rendering */ /************************/ void povray_task(void) { gchar *basename, *fullname; struct model_pak *model; model = sysenv.active_model; if (!model) return; /* make an input file */ basename = gun("pov"); fullname = g_build_filename(sysenv.cwd, basename, NULL); write_povray(fullname, model); if (sysenv.render.no_povray_exec) g_free(basename); else task_new("POVRay", &povray_exec_task, basename, &exec_img_task, basename, NULL); g_free(fullname); } /*************/ /* callbacks */ /*************/ void geom_label_toggle(GtkWidget *w, gpointer dummy) { struct model_pak *model = sysenv.active_model; if (model) { model->show_geom_labels ^= 1; redraw_canvas(SINGLE); } } void update_geom_line_width(GtkWidget *w, gpointer *ptr) { sysenv.render.geom_line_width = GTK_ADJUSTMENT(w)->value; redraw_canvas(SINGLE); } /****************************************/ /* active model display property widget */ /****************************************/ void gui_display_widget(GtkWidget *box) { GtkWidget *frame, *vbox; /* left pane */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING); vbox = gtk_vbox_new(FALSE,1); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_button_x("Ball & Stick", render_mode_set, GINT_TO_POINTER(BALL_STICK), vbox); gui_button_x("CPK", render_mode_set, GINT_TO_POINTER(CPK), vbox); gui_button_x("Liquorice", render_mode_set, GINT_TO_POINTER(LIQUORICE), vbox); gui_button_x("Polyhedral", render_mode_polyhedral, NULL, vbox); gui_button_x("Stick", render_mode_set, GINT_TO_POINTER(STICK), vbox); gui_button_x("Zone based", render_mode_zone, NULL, vbox); gui_button_x("Wire frame", render_wire_atoms, NULL, vbox); gui_button_x("Solid", render_solid_atoms, NULL, vbox); } /***********************/ /* main rendering page */ /***********************/ void render_main_page(GtkWidget *box) { GtkWidget *vbox1, *vbox2, *vbox, *hbox, *frame; /*GtkWidget *spin;*/ /* left & right pane split */ hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(box), hbox); gtk_container_set_border_width(GTK_CONTAINER(hbox), PANEL_SPACING); vbox1 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, TRUE, 0); vbox2 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); /* left pane */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add (GTK_CONTAINER(frame), vbox); gui_button_x("Ball & Stick", render_mode_set, GINT_TO_POINTER(BALL_STICK), vbox); gui_button_x("CPK", render_mode_set, GINT_TO_POINTER(CPK), vbox); gui_button_x("Liquorice", render_mode_set, GINT_TO_POINTER(LIQUORICE), vbox); gui_button_x("Polyhedral", render_mode_polyhedral, NULL, vbox); gui_button_x("Stick", render_mode_set, GINT_TO_POINTER(STICK), vbox); gui_button_x("Zone based (experimental)", render_mode_zone, NULL, vbox); gui_button_x("Wire frame molecules", render_wire_atoms, NULL, vbox); gui_button_x("Solid molecules", render_solid_atoms, NULL, vbox); gui_button_x("Axes type", toggle_axes_type, NULL, vbox); /* extra toggles */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), vbox); gui_direct_check("Antialias", &sysenv.render.antialias, render_refresh, NULL, vbox); gui_direct_check("Wire frame surfaces", &sysenv.render.wire_surface, render_refresh, NULL, vbox); gui_direct_check("Show hidden surfaces", &sysenv.render.wire_show_hidden, render_refresh, NULL, vbox); /* zoom frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); /* surface transmission frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); /* ghost atoms and shells */ gui_direct_spin("Ghost atom opacity", &sysenv.render.ghost_opacity, 0.0, 1.0, 0.1, render_refresh, NULL, vbox); /* molecular surfaces */ gui_direct_spin("Surface opacity", &sysenv.render.transmit, 0.0, 1.0, 0.1, render_refresh, NULL, vbox); /* EXP - zone based display */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_direct_spin("Zone grid size ", &sysenv.render.zone_size, 1.0, 1000.0, 1.0, render_refresh, NULL, vbox); /* radii frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_direct_spin("Ball radius", &sysenv.render.ball_rad, 0.1, 0.5, 0.02, render_refresh, NULL, vbox); gui_direct_spin("Cylinder radius", &sysenv.render.stick_rad, 0.02, 0.5, 0.01, render_refresh, NULL, vbox); gui_direct_spin("Stick thickness", &sysenv.render.stick_thickness, 0.1, 5.0, 0.05, render_refresh, NULL, vbox); gui_direct_spin("Frame radius", &sysenv.render.frame_thickness, 0.1, 5.0, 0.05, render_refresh, NULL, vbox); gui_direct_spin("CPK scaling", &sysenv.render.cpk_scale, 0.1, 3.0, 0.05, render_refresh, NULL, vbox); /* highlighting frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, 5); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox)), PANEL_SPACING); gui_direct_spin("Atom highlight power", &sysenv.render.ahl_strength, 0.0, 1.0, 0.05, render_refresh, NULL, vbox); gui_direct_spin("Atom highlight focus", &sysenv.render.ahl_size, 0.0, MAX_HL, 5.0, render_refresh, NULL, vbox); gui_direct_spin("Surface highlight power", &sysenv.render.shl_strength, 0.0, 1.0, 0.05, render_refresh, NULL, vbox); gui_direct_spin("Surface highlight focus", &sysenv.render.shl_size, 0.0, MAX_HL, 5.0, render_refresh, NULL, vbox); /* ribbon control frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox)), PANEL_SPACING); gui_direct_spin("Ribbon curvature control", &sysenv.render.ribbon_curvature, 0.0, 1.0, 0.1, render_refresh, NULL, vbox); gui_direct_spin("Ribbon thickness", &sysenv.render.ribbon_thickness, 0.4, 6.0, 0.2, render_refresh, NULL, vbox); gui_direct_spin("Ribbon quality", &sysenv.render.ribbon_quality, 1.0, 30.0, 1.0, render_refresh, NULL, vbox); } /************************************/ /* colouring method change callback */ /************************************/ void set_colour_scheme(GtkWidget *w, gpointer *data) { const gchar *tmp; struct model_pak *model; tmp = gtk_entry_get_text(GTK_ENTRY(data)); model = sysenv.active_model; if (!model) return; /* default to atom selection */ if (g_ascii_strncasecmp(tmp, "element", 7) == 0) model_colour_scheme(ELEM, model); if (g_ascii_strncasecmp(tmp, "growth", 6) == 0) model_colour_scheme(GROWTH_SLICE, model); if (g_ascii_strncasecmp(tmp, "translation", 11) == 0) model_colour_scheme(TRANSLATE, model); if (g_ascii_strncasecmp(tmp, "molecule", 8) == 0) model_colour_scheme(MOL, model); if (g_ascii_strncasecmp(tmp, "occupancy", 8) == 0) model_colour_scheme(OCCUPANCY, model); if (g_ascii_strncasecmp(tmp, "region", 6) == 0) model_colour_scheme(REGION, model); if (g_ascii_strncasecmp(tmp, "temp", 4) == 0) model_colour_scheme(VELOCITY, model); redraw_canvas(SINGLE); } /**************************/ /* colour control options */ /**************************/ void render_colours_section(GtkWidget *box) { gpointer entry; GList *list=NULL; GtkWidget *vbox1, *vbox2, *vbox, *hbox, *frame; /* left & right pane split */ hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(box), hbox); vbox1 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox1, FALSE, FALSE, 0); vbox2 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); /* TODO - simplify by having a combo of names, and a single colour editing box */ /* next frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_colour_box("Background colour ", sysenv.render.bg_colour, vbox); /* gui_colour_box("Crystal colour ", sysenv.render.morph_colour, vbox); */ gui_colour_box("Re-entrant colour ", sysenv.render.rsurf_colour, vbox); gui_colour_box("Ribbon colour ", sysenv.render.ribbon_colour, vbox); /* next frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); /* colouring scheme */ hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); list = NULL; list = g_list_append(list, "element"); list = g_list_append(list, "growth slice"); list = g_list_append(list, "molecule"); list = g_list_append(list, "occupancy"); list = g_list_append(list, "region"); list = g_list_append(list, "temperature"); list = g_list_append(list, "translation"); entry = gui_pulldown_new("Colouring scheme ", list, FALSE, hbox); gui_button_x(NULL, set_colour_scheme, entry, hbox); } /******************/ /* camera globals */ /******************/ GtkListStore *camera_list=NULL; GtkWidget *camera_tree=NULL; GtkWidget *camera_mode_entry=NULL, *camera_zoom_entry=NULL, *camera_proj_entry=NULL; GtkWidget *camera_fov_entry=NULL, *camera_axis_entry=NULL; struct camera_pak *camera_current=NULL; gdouble render_animate_angle[3] = {0.0, 360.0, 5.0}; /*****************************/ /* update camera info values */ /*****************************/ void gui_camera_update(gpointer data) { gchar *text; struct model_pak *model; struct camera_pak *camera = data; /* checks */ model = sysenv.active_model; if (!model) return; if (!camera) camera = model->camera; g_assert(camera != NULL); camera_current = camera; if (GTK_IS_ENTRY(camera_proj_entry)) { if (camera->perspective) gtk_entry_set_text(GTK_ENTRY(camera_proj_entry), "perspective"); else gtk_entry_set_text(GTK_ENTRY(camera_proj_entry), "orthographic"); } if (GTK_IS_ENTRY(camera_fov_entry)) { if (camera->perspective) { text = g_strdup_printf("%.0f", camera->fov); gtk_entry_set_text(GTK_ENTRY(camera_fov_entry), text); g_free(text); gtk_widget_set_sensitive(GTK_WIDGET(camera_fov_entry), TRUE); } else { gtk_entry_set_text(GTK_ENTRY(camera_fov_entry), "180"); gtk_widget_set_sensitive(GTK_WIDGET(camera_fov_entry), FALSE); } } if (GTK_IS_ENTRY(camera_mode_entry)) { if (camera->mode == LOCKED) gtk_entry_set_text(GTK_ENTRY(camera_mode_entry), "origin"); else gtk_entry_set_text(GTK_ENTRY(camera_mode_entry), "variable"); } if (GTK_IS_ENTRY(camera_zoom_entry)) { if (camera->perspective) { gtk_entry_set_text(GTK_ENTRY(camera_zoom_entry), "not applicable"); gtk_widget_set_sensitive(GTK_WIDGET(camera_zoom_entry), FALSE); } else { text = g_strdup_printf("%.1f", 1000.0 / (model->rmax * camera->zoom)); gtk_entry_set_text(GTK_ENTRY(camera_zoom_entry), text); g_free(text); gtk_widget_set_sensitive(GTK_WIDGET(camera_zoom_entry), TRUE); } } } /*******************************/ /* manual zoom change callback */ /*******************************/ void gui_camera_zoom_changed(GtkWidget *w, gpointer data) { gdouble z; const gchar *text; struct model_pak *model; struct camera_pak *camera; /* checks */ model = sysenv.active_model; if (!model) return; camera = model->camera; if (GTK_IS_ENTRY(w) && camera) { text = gtk_entry_get_text(GTK_ENTRY(w)); z = str_to_float(text); camera->zoom = 1000.0 / (model->rmax * z); gui_refresh(GUI_CANVAS); } } /*****************************/ /* waypoint animate callback */ /*****************************/ void render_waypoint_animate(GtkWidget *w, gpointer data) { gint frames = render_animate_frames; struct model_pak *model; model = sysenv.active_model; if (model) camera_waypoint_animate(frames, render_animate_overwrite, model); } /*****************************/ /* rotation animate callback */ /*****************************/ void render_rotate_animate(GtkWidget *w, gpointer data) { gint axis=2; const gchar *text; struct model_pak *model; model = sysenv.active_model; if (model) { text = gtk_entry_get_text(GTK_ENTRY(camera_axis_entry)); if (g_strncasecmp(text, "x ", 2) == 0) axis = 0; if (g_strncasecmp(text, "y ", 2) == 0) axis = 1; if (g_strncasecmp(text, "z ", 2) == 0) axis = 2; camera_rotate_animate(axis, render_animate_angle, render_animate_overwrite, model); } } /***********************************/ /* camera waypoint delete callback */ /***********************************/ void gui_delete_waypoint(GtkWidget *w, gpointer data) { GtkTreeIter iter; GtkTreeModel *treemodel; GtkTreeSelection *selection; struct camera_pak *camera; struct model_pak *model; /* checks */ if (GTK_IS_TREE_VIEW(data)) selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data)); else return; model = sysenv.active_model; if (!model) return; /* record selection as the active folder */ if (gtk_tree_selection_get_selected(selection, &treemodel, &iter)) { gtk_tree_model_get(treemodel, &iter, 1, &camera, -1); if (camera == model->camera_default) { gui_text_show(ERROR, "You cannot delete the default camera.\n"); return; } else { if (camera == model->camera) model->camera = model->camera_default; model->waypoint_list = g_slist_remove(model->waypoint_list, camera); gui_camera_refresh(); redraw_canvas(SINGLE); } } } /**************************************/ /* fill out the list of model cameras */ /**************************************/ void gui_camera_populate(void) { gint n; gchar *text; gpointer camera; GSList *list; GtkTreeIter iter; GtkTreeSelection *selection; struct model_pak *model; gtk_list_store_clear(camera_list); model = sysenv.active_model; if (!model) return; /* default camera */ n=0; gtk_list_store_append(camera_list, &iter); gtk_list_store_set(camera_list, &iter, 0, "default", 1, model->camera_default, -1); /* select camera if it's currently active */ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(camera_tree)); if (selection) { if (model->camera == model->camera_default) { gtk_tree_selection_select_iter(selection, &iter); /* don't bother checking waypoint list (below) */ selection = NULL; } } /* waypoints */ for (list=model->waypoint_list ; list ; list=g_slist_next(list)) { camera = list->data; text = g_strdup_printf("waypoint %d", ++n); gtk_list_store_append(camera_list, &iter); gtk_list_store_set(camera_list, &iter, 0, text, 1, camera, -1); g_free(text); /* select camera if it's currently active */ if (selection) { if (model->camera == camera) gtk_tree_selection_select_iter(selection, &iter); } } } /******************************/ /* select a particular camera */ /******************************/ void gui_camera_select(GtkTreeSelection *selection, gpointer data) { GtkTreeIter iter; GtkTreeModel *treemodel; struct camera_pak *camera; struct model_pak *model; /* checks */ model = sysenv.active_model; if (!model) return; /* record selection as the active folder */ if (gtk_tree_selection_get_selected(selection, &treemodel, &iter)) { gtk_tree_model_get(treemodel, &iter, 1, &camera, -1); gui_camera_update(camera); model->camera = camera; redraw_canvas(SINGLE); } } /******************************/ /* camera display page update */ /******************************/ void gui_camera_refresh(void) { if (dialog_exists(POVRAY, NULL)) { gui_camera_populate(); gui_camera_update(NULL); } } /*************************************/ /* camera projection modify callback */ /*************************************/ void gui_camera_projection_changed(GtkWidget *w, gpointer data) { const gchar *text; /* checks */ g_assert(GTK_IS_ENTRY(w)); if (!camera_current) return; text = gtk_entry_get_text(GTK_ENTRY(w)); /* NB: sometimes an empty string is passed - don't update unless we have to */ if (g_strncasecmp(text, "ortho", 5) == 0) { camera_current->perspective = FALSE; redraw_canvas(SINGLE); gui_camera_update(camera_current); } if (g_strncasecmp(text, "persp", 5) == 0) { camera_current->perspective = TRUE; redraw_canvas(SINGLE); gui_camera_update(camera_current); } } /*******************************/ /* camera mode modify callback */ /*******************************/ void gui_camera_mode_changed(GtkWidget *w, gpointer data) { const gchar *text; /* checks */ g_assert(GTK_IS_ENTRY(w)); if (!camera_current) return; text = gtk_entry_get_text(GTK_ENTRY(w)); /* NB: sometimes an empty string is passed - don't update unless we have to */ if (g_strncasecmp(text, "orig", 4) == 0) { camera_current->mode = LOCKED; redraw_canvas(SINGLE); } if (g_strncasecmp(text, "vari", 4) == 0) { camera_current->mode = FREE; redraw_canvas(SINGLE); } } /***********************************/ /* camera waypoint create callback */ /***********************************/ void gui_create_waypoint(void) { struct model_pak *model; gpointer camera; model = sysenv.active_model; if (model) { /* allocate */ camera = camera_dup(model->camera); model->waypoint_list = g_slist_append(model->waypoint_list, camera); /* move new camera slightly along the viewing vector */ /* ARR3SET(v, camera->v); VEC3MUL(v, 0.1); ARR3ADD(camera->x, v); */ model->camera = camera; gui_camera_refresh(); } } /****************************************************/ /* camera manipulation (waypoints, projection etc.) */ /****************************************************/ void render_camera_section(GtkWidget *box) { GList *list; GtkCellRenderer *r; GtkTreeViewColumn *c; GtkTreeSelection *select; GtkWidget *vbox1, *vbox2, *vbox, *hbox, *frame; GtkWidget *swin, *label, *combo; /* left & right pane split */ hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(box), hbox); vbox1 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, TRUE, 0); vbox2 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); /* CURRENT - camera info */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), vbox); /* projection */ list = NULL; list = g_list_prepend(list, "perspective"); list = g_list_prepend(list, "orthographic"); camera_proj_entry = gui_pulldown_new("projection ", list, FALSE, vbox); /* focal point */ list = NULL; list = g_list_prepend(list, "variable"); list = g_list_prepend(list, "origin"); camera_mode_entry = gui_pulldown_new("focal point", list, FALSE, vbox); /* field of view */ camera_fov_entry = gui_text_entry("field of view", NULL, FALSE, FALSE, vbox); /* zoom */ camera_zoom_entry = gui_text_entry("zoom factor", NULL, TRUE, FALSE, vbox); g_signal_connect(GTK_OBJECT(camera_zoom_entry), "activate", GTK_SIGNAL_FUNC(gui_camera_zoom_changed), NULL); /* frame - animation from waypoints */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_button_x("Add waypoint ", gui_create_waypoint, NULL, vbox); gui_button_x("Delete waypoint ", gui_delete_waypoint, camera_tree, vbox); gui_button_x("Create animation from waypoints ", render_waypoint_animate, NULL, vbox); gui_direct_spin("Frames for each waypoint traversal ", &render_animate_frames, 1.0, 1000.0, 1, NULL, NULL, vbox); /* frame - animation from axis rotations */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); label = gtk_label_new("Rotation vector "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); list = NULL; list = g_list_prepend(list, "z axis"); list = g_list_prepend(list, "y axis"); list = g_list_prepend(list, "x axis"); combo = gtk_combo_new(); camera_axis_entry = GTK_COMBO(combo)->entry; gtk_entry_set_editable(GTK_ENTRY(camera_axis_entry), FALSE); gtk_combo_set_popdown_strings(GTK_COMBO(combo), list); gtk_box_pack_end(GTK_BOX(hbox), combo, FALSE, FALSE, 0); gui_direct_spin("Start ", &render_animate_angle[0], 0, 359, 5, NULL, NULL, vbox); gui_direct_spin("Stop", &render_animate_angle[1], 1, 360, 5, NULL, NULL, vbox); gui_direct_spin("Increment", &render_animate_angle[2], 1, 10, 1, NULL, NULL, vbox); gui_button_x("Create animated rotation ", render_rotate_animate, NULL, vbox); /* frame - animation from axis rotations */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_direct_check("Overwrite old frames ", &render_animate_overwrite, NULL, NULL, vbox); /* right frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox2), frame, TRUE, TRUE, 0); vbox = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); /* shortcut buttons for camera positioning */ /* hbox = gtk_hbox_new(TRUE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gui_button(" x ", gui_view_x, NULL, hbox, TT); gui_button(" y ", gui_view_y, NULL, hbox, TT); gui_button(" z ", gui_view_z, NULL, hbox, TT); gui_button(" a ", gui_view_a, NULL, hbox, TT); gui_button(" b ", gui_view_b, NULL, hbox, TT); gui_button(" c ", gui_view_c, NULL, hbox, TT); */ /* list of cameras & waypoints */ swin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0); /* list */ camera_list = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); camera_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(camera_list)); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swin), camera_tree); r = gtk_cell_renderer_text_new(); c = gtk_tree_view_column_new_with_attributes(" ", r, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(camera_tree), c); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(camera_tree), FALSE); /* camera selection handler */ select = gtk_tree_view_get_selection(GTK_TREE_VIEW(camera_tree)); gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE); g_signal_connect(G_OBJECT(select), "changed", G_CALLBACK(gui_camera_select), NULL); /* add/delete waypoint buttons */ /* hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gui_button("Add", gui_create_waypoint, NULL, hbox, TT); gui_button("Delete", gui_delete_waypoint, camera_tree, hbox, TT); */ /* TODO - a mechanism so that whenever a switch_model happens a */ /* function can be automatically called to update stuff */ /* AND is destroyed whenver the widget it's attached to is destroyed */ /* fill out the dialog */ gui_camera_refresh(); /* now attach modify callbacks */ g_signal_connect(GTK_OBJECT(camera_proj_entry), "changed", GTK_SIGNAL_FUNC(gui_camera_projection_changed), NULL); g_signal_connect(GTK_OBJECT(camera_mode_entry), "changed", GTK_SIGNAL_FUNC(gui_camera_mode_changed), NULL); } /**********************/ /* light list globals */ /**********************/ enum { RENDER_LIGHT_COLOUR, RENDER_LIGHT_VECTOR, RENDER_LIGHT_TYPE, RENDER_LIGHT_ATTRIBUTE, RENDER_LIGHT_NCOLS }; GtkWidget *render_light_tv; GtkListStore *render_light_ls; /****************************************************/ /* convert 3 doubles [0:1] into a hex colour string */ /****************************************************/ gchar *get_hex_colour(gdouble *colour) { guint r, g, b; gdouble rgb[3]; ARR3SET(rgb, colour); VEC3MUL(rgb, 255.0); r = rgb[0]; g = rgb[1]; b = rgb[2]; return(g_strdup_printf("#%2X%2X%2X", r, g, b)); } /**********************************/ /* get the currently selected row */ /**********************************/ gint render_light_selected(void) { gint row=0; GtkTreeModel *treemodel; GtkTreeSelection *selection; GtkTreeIter iter; treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(render_light_tv)); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(render_light_tv)); if (gtk_tree_model_get_iter_first(treemodel, &iter)) { do { if (gtk_tree_selection_iter_is_selected(selection, &iter)) return(row); row++; } while (gtk_tree_model_iter_next(treemodel, &iter)); } return(-1); } /***********************************/ /* construct the light source list */ /***********************************/ void update_light_list(void) { gint row, num_rows=0; gchar *text; GSList *list; GtkTreeIter iter; GtkTreeModel *treemodel; GtkTreeSelection *selection; struct light_pak *light; /* checks */ g_assert(render_light_ls != NULL); /* store */ row = render_light_selected(); /* re-populate */ gtk_list_store_clear(render_light_ls); for (list=sysenv.render.light_list ; list ; list=g_slist_next(list)) { light = list->data; gtk_list_store_append(render_light_ls, &iter); /* position or direction vector */ text = g_strdup_printf(" (%5.1f, %5.1f, %5.1f) ", light->x[0], light->x[1], light->x[2]); gtk_list_store_set(render_light_ls, &iter, RENDER_LIGHT_VECTOR, text, -1); g_free(text); /* type */ if (light->type == DIRECTIONAL) text = g_strdup(" Directional"); else text = g_strdup(" Positional"); gtk_list_store_set(render_light_ls, &iter, RENDER_LIGHT_TYPE, text, -1); g_free(text); /* FIXME - easier way to specify the RGB colour? */ text = get_hex_colour(light->colour); gtk_list_store_set(render_light_ls, &iter, RENDER_LIGHT_ATTRIBUTE, text, -1); g_free(text); num_rows++; } /* restore selected row, or the previous (if possible) if deleted */ if (row >= num_rows && row) row--; if (row >= 0) { treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(render_light_tv)); if (gtk_tree_model_iter_nth_child(treemodel, &iter, NULL, row)) { selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(render_light_tv)); if (selection) gtk_tree_selection_select_iter(selection, &iter); } } } /*******************************************/ /* light list manipulation, add new source */ /*******************************************/ void add_current_light(GtkWidget *w, gpointer dummy) { struct light_pak *light; /* duplicate data for the list */ light = g_malloc(sizeof(struct light_pak)); memcpy(light, ¤t_light, sizeof(struct light_pak)); /* append & update */ sysenv.render.light_list = g_slist_append(sysenv.render.light_list, light); update_light_list(); redraw_canvas(SINGLE); } /******************************************/ /* light list manipulation, modify source */ /******************************************/ void mod_selected_light(GtkWidget *w, gpointer dummy) { gint row; struct light_pak *light; row = render_light_selected(); if (row < 0) return; /* get the light's data from the list */ light = g_slist_nth_data(sysenv.render.light_list, row); /* overwrite with the current data */ memcpy(light, ¤t_light, sizeof(struct light_pak)); update_light_list(); redraw_canvas(SINGLE); } /******************************************/ /* light list manipulation, delete source */ /******************************************/ void del_selected_light(GtkWidget *w, gpointer dummy) { gint row; struct light_pak *light; row = render_light_selected(); if (row < 0) return; light = g_slist_nth_data(sysenv.render.light_list, row); sysenv.render.light_list = g_slist_remove(sysenv.render.light_list, light); update_light_list(); redraw_canvas(SINGLE); } /******************/ /* render cleanup */ /******************/ void render_cleanup(void) { gtk_list_store_clear(render_light_ls); render_light_ls = NULL; render_light_tv = NULL; } /*********************************************/ /* callback to fill dialog with light values */ /*********************************************/ void render_light_activate(GtkTreeView *treeview, GtkTreePath *treepath) { gint n; struct light_pak *light; /* get selected light */ n = render_light_selected(); if (n < 0) return; /* get the light's data from the list */ light = g_slist_nth_data(sysenv.render.light_list, n); /* transfer to current displayed values */ if (light) { /* overwrite */ memcpy(¤t_light, light, sizeof(struct light_pak)); /* update */ gui_relation_update(NULL); } } /****************************/ /* lighting control options */ /****************************/ void render_lighting_section(GtkWidget *box) { gint i; gchar *titles[] = {" Colour ", " Absolute vector ", " Type "}; GList *list=NULL; GtkWidget *swin, *vbox1, *vbox2, *vbox, *hbox, *frame; GtkWidget *label, *combo; GtkCellRenderer *renderer; GtkTreeViewColumn *column; /* left & right pane split */ hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(box), hbox); vbox1 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox1, FALSE, FALSE, 0); vbox2 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); /* box 1 - colour (and edit button) */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame),vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_colour_box("Current light source colour ", current_light.colour, vbox); /* box 2 - light components */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame),vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gui_direct_spin("Ambient component", ¤t_light.ambient, 0.0, 1.0, 0.1, NULL, NULL, vbox); gui_direct_spin("Diffuse component", ¤t_light.diffuse, 0.0, 1.0, 0.1, NULL, NULL, vbox); gui_direct_spin("Specular component", ¤t_light.specular, 0.0, 1.0, 0.1, NULL, NULL, vbox); /* left box - light type */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); /* type label */ hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); label = gtk_label_new(" Type "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); /* TODO - pulldown dir/pos */ /* NEW - combo box for the selection mode */ list = g_list_append(list, "Directional"); list = g_list_append(list, "Positional"); combo = gtk_combo_new(); gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(combo)->entry), FALSE); gtk_combo_set_popdown_strings(GTK_COMBO(combo), list); gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0); g_signal_connect(GTK_OBJECT(GTK_COMBO(combo)->entry), "changed", GTK_SIGNAL_FUNC(event_render_modify), (gpointer) combo); g_object_set_data(G_OBJECT(combo), "id", (gpointer) LIGHT_TYPE); /* position/direction vector input */ hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); /* x component */ vbox = gtk_vbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); label = gtk_label_new("X"); gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); gui_direct_spin(NULL, ¤t_light.x[0], -1000.0, 1000.0, 0.1, NULL, NULL, vbox); /* y component */ vbox = gtk_vbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); label = gtk_label_new("Y"); gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); gui_direct_spin(NULL, ¤t_light.x[1], -1000.0, 1000.0, 0.1, NULL, NULL, vbox); /* z component */ vbox = gtk_vbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); label = gtk_label_new("Z"); gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); gui_direct_spin(NULL, ¤t_light.x[2], -1000.0, 1000.0, 0.1, NULL, NULL, vbox); /* next frame - actions */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); hbox = gtk_hbox_new(TRUE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); gui_button(" Add ", add_current_light, NULL, hbox, TT); gui_button(" Modify ", mod_selected_light, NULL, hbox, TT); gui_button(" Delete ", del_selected_light, NULL, hbox, TT); /* right box 1 - light source listing */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox2), frame, TRUE, TRUE, 0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); /* scrolled win for the list */ swin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0); /* gtk_widget_set_size_request(swin, 24*sysenv.gfontsize, -1); */ /* NEW - light list in a list store */ render_light_ls = gtk_list_store_new(RENDER_LIGHT_NCOLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); render_light_tv = gtk_tree_view_new_with_model(GTK_TREE_MODEL(render_light_ls)); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swin), render_light_tv); for (i=0 ; i<=RENDER_LIGHT_TYPE ; i++) { renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(titles[i], renderer, "text", i, NULL); if (!i) gtk_tree_view_column_add_attribute(column, renderer, "background", RENDER_LIGHT_ATTRIBUTE); gtk_tree_view_append_column(GTK_TREE_VIEW(render_light_tv), column); } g_signal_connect(render_light_tv, "row_activated", G_CALLBACK(render_light_activate), NULL); } /***************************/ /* OpenGL specific options */ /***************************/ void render_opengl_section(GtkWidget *box) { GtkWidget *vbox1, *vbox2, *vbox, *hbox, *frame; /* left & right pane split */ hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(box), hbox); gtk_container_set_border_width(GTK_CONTAINER(hbox), PANEL_SPACING); vbox1 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, TRUE, 0); vbox2 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); /* depth queuing */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_direct_check("Depth queueing", &sysenv.render.fog, render_refresh, NULL, vbox); gui_direct_spin("Depth queueing strength", &sysenv.render.fog_density, 0.0, 1.0, 0.1, render_refresh, NULL, vbox); /* new frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox)), PANEL_SPACING); /* sphere quality */ gui_direct_spin("Sphere quality", &sysenv.render.sphere_quality, 0.0, 8.0, 1.0, render_refresh, NULL, vbox); /* cylinder quality */ gui_direct_spin("Cylinder quality", &sysenv.render.cylinder_quality, 4.0, 20.0, 1.0, render_refresh, NULL, vbox); /* quality */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox)), PANEL_SPACING); gui_direct_check("Automatic quality adjustment", &sysenv.render.auto_quality, NULL, NULL, vbox); gui_direct_check("Fast rotation", &sysenv.render.fast_rotation, NULL, NULL, vbox); gui_direct_check("Selection halos", &sysenv.render.halos, render_refresh, NULL, vbox); /* new frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), vbox); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, PANEL_SPACING); /* gui_auto_check("Label geometric measurements", geom_label_toggle, NULL, &model->show_geom_labels, hbox); */ gui_button_x("Label geometric measurements", geom_label_toggle, NULL, hbox); gui_direct_spin("Line width", &sysenv.render.geom_line_width, 0.5, 9.0, 0.5, render_refresh, NULL, vbox); /* next frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); /* change font options */ gui_button_x("Change graphics font", gl_font_dialog, NULL, vbox); } /***************************/ /* POVRay specific options */ /***************************/ void render_povray_section(GtkWidget *box) { GtkWidget *vbox1, *vbox2, *vbox, *hbox, *frame; /* left & right pane split */ hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(box), hbox); gtk_container_set_border_width(GTK_CONTAINER(hbox), PANEL_SPACING); vbox1 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, TRUE, 0); vbox2 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); /* new frame */ frame = gtk_frame_new("Image size"); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); /* create a vbox in the frame */ vbox = gtk_vbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(frame),vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_direct_spin("Width", &sysenv.render.width, 100, 2000, 100, NULL, NULL, vbox); gui_direct_spin("Height", &sysenv.render.height, 100, 2000, 100, NULL, NULL, vbox); /* new frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame),vbox); gui_direct_check("Shadowless", &sysenv.render.shadowless, NULL, NULL, vbox); /* misc options */ gui_direct_check("Create povray input files then stop", &sysenv.render.no_povray_exec, NULL, NULL, vbox); gui_direct_check("Delete intermediate input/image files", &sysenv.render.no_keep_tempfiles, NULL, NULL, vbox); /* FIXME - this is broken with the new spatial morphology display */ /* frame = gtk_frame_new("Glass morphology"); gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, 5); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox)), PANEL_SPACING); gui_direct_spin("Refractive index", &sysenv.render.ref_index, 1.0, 3.0, 0.1, NULL, NULL, vbox); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,TRUE,0); label = gtk_label_new("Surface finish "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0); entry = gtk_entry_new_with_max_length(LINELEN-1); gtk_box_pack_end(GTK_BOX(hbox), entry, FALSE, TRUE, 0); g_signal_connect(GTK_OBJECT(entry), "activate", GTK_SIGNAL_FUNC(event_render_modify), (gpointer) entry); g_object_set_data(G_OBJECT(entry), "id", (gpointer) MORPH_FINISH); */ /* run POVRay job in the background */ hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_end(GTK_BOX(box), hbox, TRUE, FALSE, PANEL_SPACING); gui_button(" Render ", render_setup, NULL, hbox, TF); } /***************************/ /* turn stereo display off */ /***************************/ void gui_stereo_off(GtkWidget *w, gpointer dummy) { sysenv.stereo = FALSE; sysenv.render.perspective = FALSE; stereo_close_window(); redraw_canvas(SINGLE); } /********************************/ /* activate desired stereo mode */ /********************************/ void gui_stereo_on(GtkWidget *w, gpointer mode) { switch(GPOINTER_TO_INT(mode)) { case 0: /* windowed */ if (sysenv.stereo_fullscreen) stereo_close_window(); sysenv.stereo = TRUE; sysenv.stereo_fullscreen = FALSE; sysenv.render.perspective = TRUE; break; case 1: /* fullscreen */ sysenv.stereo = TRUE; sysenv.stereo_fullscreen = TRUE; sysenv.render.perspective = TRUE; stereo_open_window(); break; } redraw_canvas(SINGLE); } /************************************/ /* update eye separation percentage */ /************************************/ void gui_stereo_refresh(GtkWidget *w, gpointer dummy) { gui_refresh(GUI_CANVAS); } /*************************/ /* stereo config options */ /*************************/ void render_stereo_section(GtkWidget *box) { GtkWidget *frame, *vbox, *label; frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); if (sysenv.render.stereo_quadbuffer) label = gtk_label_new("Quad buffered OpenGL visual"); else label = gtk_label_new("Standard OpenGL visual"); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); gui_button_x("Windowed stereo ", gui_stereo_on, GINT_TO_POINTER(0), vbox); gui_button_x("Fullscreen stereo ", gui_stereo_on, GINT_TO_POINTER(1), vbox); gui_button_x("Stereo off", gui_stereo_off, NULL, vbox); gui_direct_spin("Eye separation ", &sysenv.render.stereo_eye_offset, 0.01, 4.0, 0.01, gui_stereo_refresh, NULL, vbox); gui_direct_spin("Frustum asymmetry ", &sysenv.render.stereo_parallax, 0.01, 4.0, 0.01, gui_stereo_refresh, NULL, vbox); gui_direct_check("Left eye active ", &sysenv.render.stereo_left, gui_stereo_refresh, NULL, vbox); gui_direct_check("Right eye active ", &sysenv.render.stereo_right, gui_stereo_refresh, NULL, vbox); } /*****************/ /* misc. options */ /*****************/ void render_misc_section(GtkWidget *box) { GtkWidget *vbox1, *vbox2, *vbox, *hbox, *frame; struct model_pak *model; /* TODO - allow render dialog even when no models, but dont draw */ /* or make insensitive model specific buttons */ model = sysenv.active_model; /* FIXME - dont draw anything if no models are loaded (needs a better/dynamic fix) */ if (!model) return; /* left & right pane split */ hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(box), hbox); gtk_container_set_border_width(GTK_CONTAINER(hbox), PANEL_SPACING); vbox1 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, TRUE, 0); vbox2 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0); /* left pane */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); /* create a vbox in the frame */ vbox = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); /* relation check buttons */ gui_auto_check("Show cell", render_refresh, NULL, &model->show_cell, vbox); gui_auto_check("Show cell images", render_refresh, NULL, &model->show_cell_images, vbox); gui_auto_check("Show cell lengths", render_refresh, NULL, &model->show_cell_lengths, vbox); gui_auto_check("Show cores", render_refresh, NULL, &model->show_cores, vbox); gui_auto_check("Show shells", render_refresh, NULL, &model->show_shells, vbox); gui_auto_check("Show core index", render_refresh, NULL, &model->show_atom_index, vbox); gui_auto_check("Show core label", render_refresh, NULL, &model->show_atom_labels, vbox); gui_auto_check("Show core type", render_refresh, NULL, &model->show_atom_types, vbox); gui_auto_check("Show core charge", render_refresh, NULL, &model->show_atom_charges, vbox); /* gui_auto_check("Show normal bonds", connect_refresh_global, NULL, &model->build_molecules, vbox); */ gui_auto_check("Show hydrogen bonds", connect_refresh_global, NULL, &model->build_hydrogen, vbox); gui_auto_check("Show zeolite bonds", connect_refresh_global, NULL, &model->build_zeolite, vbox); gui_auto_check("Show total charge", render_refresh, NULL, &model->show_charge, vbox); /* next frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 0); vbox = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_auto_check("Show axes", render_refresh, NULL, &model->show_axes, vbox); gui_auto_check("Show FPS", render_refresh, NULL, &model->show_title, vbox); gui_auto_check("Show spatial text ", morph_toggle, NULL, &model->morph_label, vbox); gui_auto_check("Show camera waypoints ", morph_toggle, NULL, &model->show_waypoints, vbox); } /***************************/ /* dialog refresh function */ /***************************/ void render_dialog_refresh(struct model_pak *model) { gui_camera_refresh(); } /***********************/ /* render setup widget */ /***********************/ void gui_render_dialog() { gpointer dialog; GtkWidget *window, *frame; GtkWidget *label, *notebook, *page; struct light_pak *ldata; /* struct model_pak *model; */ /* NB: some check boxes depend on having a valid model */ /* model = sysenv.active_model; if (!model) return; */ /* request dialog slot */ dialog = dialog_request(POVRAY, "Display properties", render_dialog_refresh, render_cleanup, NULL); if (!dialog) return; window = dialog_window(dialog); /* init current light */ ldata = ((GSList *) sysenv.render.light_list)->data; if (ldata) memcpy(¤t_light, ldata, sizeof(struct light_pak)); else { gui_text_show(ERROR, "Empty light list.\n"); return; } /* notebook frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), frame, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING); /* create notebook */ notebook = gtk_notebook_new(); gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP); gtk_container_add(GTK_CONTAINER(frame), notebook); gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), TRUE); /* main page */ page = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new("Main"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); render_main_page(page); /* colour page */ page = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new("Colours"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); render_colours_section(page); /* NEW */ /* camera page */ page = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new("Camera"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); render_camera_section(page); /* lighting page */ page = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new("Lights"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); render_lighting_section(page); /* OpenGL page */ page = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new("OpenGL"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); render_opengl_section(page); /* POVRay page */ page = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new("POVRay"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); render_povray_section(page); /* Stereo config page */ page = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new("Stereo"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); render_stereo_section(page); /* misc page */ page = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new("Toggles"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label); render_misc_section(page); /* terminating button */ gui_stock_button(GTK_STOCK_CLOSE, dialog_destroy, dialog, GTK_DIALOG(window)->action_area); /* display the dialog */ gtk_widget_show_all(window); update_light_list(); }