/* This file is part of the FElt finite element analysis package. Copyright (C) 1993-2000 Jason I. Gobat and Darren C. Atkinson 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /************************************************************************ * File: opengl.c * * * * Description: This file contains the public and private functions * * needed to do post-processing graphics with OpenGL * ************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FileDialog.h" #include "Layout.h" #include "problem.h" #include "fe.h" #include "colormap.h" #include "globals.h" #define WEDGEWIDTH 100 static Display *dpy; static GLXContext cx; static GLboolean doubleBuffer = GL_TRUE; static GLuint fontbase; typedef struct { Widget shell; Widget mesa; double smin, smax; GLfloat xrot, yrot, zrot; GLfloat xbase, ybase, zbase; float **x, **y, **z; int **idx; int numelts; int wedge; Dimension winW, winH; } info; typedef int face[4]; face bface[] = { {0, 1, 2, 3}, {0, 3, 7, 4}, {4, 7, 6, 5}, {1, 2, 6, 5}, {2, 3, 7, 6}, {0, 1, 5, 4}, }; static void MakeFont(fwidth, fheight) int *fwidth; int *fheight; { XFontStruct *fontInfo; Font id; unsigned int first, last; fontInfo = XLoadQueryFont(dpy, "6x12"); if (fontInfo == NULL) { *fwidth = 0; *fheight = 0; return; } id = fontInfo->fid; first = fontInfo->min_char_or_byte2; last = fontInfo->max_char_or_byte2; fontbase = glGenLists((GLuint) last+1); if (fontbase == 0) { *fwidth = 0; *fheight = 0; return; } glXUseXFont(id, first, last-first+1, fontbase+first); *fheight = fontInfo -> ascent + fontInfo -> descent; *fwidth = fontInfo -> max_bounds.width; } static void PrintString(GLfloat x, GLfloat y, GLfloat z, char *s) { glRasterPos3f(x, y, z); glPushAttrib (GL_LIST_BIT); glListBase(fontbase); glCallLists(strlen(s), GL_UNSIGNED_BYTE, (GLubyte *)s); glPopAttrib (); } static void DrawElements (info *inf) { int i, j, k; glBegin (GL_QUADS); for (i = 0; i < inf -> numelts; i ++) { for (j = 0 ; j < 6 ; j++) { for (k = 0 ; k < 4 ; k++) { glColor3fv (colors [inf -> idx [i][bface[j][k]]]); glVertex3f (inf -> x [i][bface[j][k]], inf -> y [i][bface[j][k]], inf -> z [i][bface[j][k]]); } } } glEnd (); } static void InitDisplay (info *inf) { Arg args [2]; Dimension winW, winH; Widget w; w = inf -> mesa; XtSetArg(args [0], XtNwidth, &winW); XtSetArg(args [1], XtNheight, &winH); XtGetValues(w, args, 2); inf -> winW = winW; inf -> winH = winH; glClearDepth (1.0); glClearColor (0.0, 0.0, 0.0, 0.0); glViewport(0, 0, winW - inf -> wedge, winH); glScissor(0, 0, winW - inf -> wedge, winH); glMatrixMode (GL_PROJECTION); glLoadIdentity (); gluPerspective(40, (double) (winW - inf -> wedge)/(double) winH, 1.0, 100.0); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); } static void RefreshDisplay (info *inf) { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); /* push I down the stack */ glTranslatef (0.0, 0.0, -3.5); glRotatef (inf -> xrot, 1.0, 0.0, 0.0); glRotatef (inf -> yrot, 0.0, 1.0, 0.0); glRotatef (inf -> zrot, 0.0, 0.0, 1.0); DrawElements (inf); glPopMatrix(); /* pop I back to stack top */ glXSwapBuffers (dpy, XtWindow (inf -> mesa)); if (!glXIsDirect(dpy, cx)) glFinish (); } static void SaveFunction(Widget w, XtPointer client_data, XtPointer call_data) { extern FileDialog dumpd; info *inf = (info *) client_data; String save_file; String format; FILE *fp; FileDialogSetToggles (dumpd, "PPM", "EPS"); CenterOnScreen (XtNameToWidget (toplevel, "fileDialog"), True); WarpToCenter (XtNameToWidget (toplevel, "fileDialog")); FileDialogSelect (dumpd, "Save Contour plot", "Save file name:", "", &save_file, &format); if (save_file == NULL) return; if (strcmp (format, "EPS") == 0) WidgetToEPS (save_file, inf -> mesa); else if (strcmp(format, "PPM") == 0) WidgetToPPM (save_file, inf -> mesa); fp = fopen("temp.xwd", "wb"); DumpWidget(inf -> mesa, fp); return; } static void DismissFunction(Widget w, XtPointer client_data, XtPointer call_data) { int i; info *inf = (info *) client_data; for (i = 0 ; i < inf -> numelts ; i++) { free(inf -> x [i]); free(inf -> y [i]); free(inf -> z [i]); free(inf -> idx [i]); } free(inf -> x); free(inf -> y); free(inf -> z); free(inf -> idx); XtDestroyWidget(inf -> shell); free(inf); } static void ToggleFunction(Widget w, XtPointer client_data, XtPointer call_data) { Boolean state; Widget dw = (Widget) client_data; Arg args [1]; info *inf = (info *) client_data; GLwDrawingAreaMakeCurrent (inf -> mesa, cx); XtSetArg (args [0], XtNstate, &state); XtGetValues (w, args, 1); glPolygonMode (GL_FRONT_AND_BACK, state ? GL_LINE : GL_FILL); RefreshDisplay(inf); } static Boolean RotateZPos (XtPointer client_data) { info *inf = (info *) client_data; GLwDrawingAreaMakeCurrent (inf -> mesa, cx); inf -> zrot += 3; if (inf -> zrot >= 360 || inf -> zrot <= -360) inf -> zrot = 0.0; RefreshDisplay(inf); return False; } static Boolean RotateZNeg (XtPointer client_data) { info *inf = (info *) client_data; GLwDrawingAreaMakeCurrent (inf -> mesa, cx); inf -> zrot -= 3; if (inf -> zrot >= 360 || inf -> zrot <= -360) inf -> zrot = 0.0; RefreshDisplay(inf); return False; } void InputFunction (Widget w, XtPointer client_data, XtPointer call_data) { info *inf = (info *) client_data; static int prev_x, prev_y; static int rotating = 0; static XtWorkProcId wpid = 0; int button; GLwDrawingAreaCallbackStruct *gs=(GLwDrawingAreaCallbackStruct *) call_data; GLwDrawingAreaMakeCurrent (inf -> mesa, cx); switch (gs -> event -> type) { case ButtonPress: button = gs -> event -> xbutton.button; if (button == 1) { prev_x = gs -> event -> xbutton.x; prev_y = gs -> event -> xbutton.y; rotating = 1; } else if (button == 2) wpid = XtAppAddWorkProc (app_context, RotateZPos, (XtPointer) inf); else if (button == 3) wpid = XtAppAddWorkProc (app_context, RotateZNeg, (XtPointer) inf); break; case ButtonRelease: button = gs -> event -> xbutton.button; if (button == 1) { inf -> xbase = inf -> xrot; inf -> ybase = inf -> yrot; rotating = 0; } else { XtRemoveWorkProc (wpid); wpid = 0; } break; case MotionNotify: if (rotating) { inf -> xrot = inf -> xbase + gs -> event -> xmotion.y - prev_y; inf -> yrot = inf -> ybase + gs -> event -> xmotion.x - prev_x; if (inf -> xrot >= 360 || inf -> xrot <= -360) inf -> xrot = 0.0; if (inf -> yrot >= 360 || inf -> yrot <= -360) inf -> yrot = 0.0; RefreshDisplay(inf); } break; } } /* * this does both initialization and drawing for the wedge * because we need to set both projection and model view * matrices away from their normal 3D values every time * we redraw the wedge ... we also need to be sure that * we store and restore on exit all of the 3D viewport * and transformation information */ static void DrawWedge (info *inf) { static int fwidth, fheight; static int init = 0; Arg args [2]; int i; double dy; char buffer [32]; char *ptr; double fscale; GLint prev_mode; Widget w; w = inf -> mesa; if (!init) { MakeFont (&fwidth, &fheight); init = 1; } glViewport(inf -> winW - inf -> wedge, 0, inf -> wedge, inf -> winH); glMatrixMode (GL_PROJECTION); glPushMatrix(); glLoadIdentity (); gluOrtho2D(-1.0, 1.0, -1.0, 1.0); glMatrixMode (GL_MODELVIEW); glPushMatrix(); glLoadIdentity (); dy = 1.8/253; glGetIntegerv(GL_POLYGON_MODE, &prev_mode); glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); glBegin (GL_QUAD_STRIP); glColor3fv (colors [0]); glVertex3f (-0.8, -0.9, 0.0); glVertex3f (-0.2, -0.9, 0.0); for (i = 1 ; i < 254 ; i++) { glColor3fv (colors [i]); glVertex2f (-0.8, -0.9 + dy*i); glVertex2f (-0.2, -0.9 + dy*i); } glEnd (); if (fwidth) { fscale = 2.0/inf -> winH; glColor3f(1.0, 1.0, 1.0); sprintf (buffer, "%11.5g", inf -> smin); ptr = buffer; while (isspace (*ptr)) ptr++; PrintString(-0.15, -0.9, 0.0, ptr); sprintf (buffer, "%11.5g", inf -> smax); ptr = buffer; while (isspace (*ptr)) ptr++; PrintString(-0.15, 0.9 - fheight*fscale, 0.0, ptr); } glXSwapBuffers (dpy, XtWindow (w)); if (!glXIsDirect(dpy, cx)) glFinish (); glMatrixMode (GL_PROJECTION); glPopMatrix(); glMatrixMode (GL_MODELVIEW); glPopMatrix(); glPolygonMode (GL_FRONT_AND_BACK, prev_mode); glViewport(0, 0, inf -> winW - inf -> wedge, inf -> winH); return; } static void ResizeFunction(Widget w, XtPointer client_data, XtPointer call_data) { info *inf = (info *) client_data; GLwDrawingAreaMakeCurrent (w, cx); InitDisplay(inf); glDisable(GL_SCISSOR_TEST); RefreshDisplay(inf); if (inf -> wedge) DrawWedge(inf); glEnable(GL_SCISSOR_TEST); return; } static void RedrawFunction(Widget w, XtPointer client_data, XtPointer call_data) { info *inf = (info *) client_data; GLwDrawingAreaMakeCurrent (w, cx); glDisable(GL_SCISSOR_TEST); RefreshDisplay(inf); if (inf -> wedge) DrawWedge(inf); glEnable(GL_SCISSOR_TEST); return; } static void LoadResults(inf, stress, comp, element, numelts) info *inf; Boolean stress; int comp; Element *element; int numelts; { int i, j; int epn; float s; double xmax, xmin, ymax, ymin, zmax, zmin; double xctr, yctr, zctr; double xdist, ydist, zdist; double scale; inf -> numelts = numelts; inf -> x = (float **) malloc(sizeof(float *) * numelts); inf -> y = (float **) malloc(sizeof(float *) * numelts); inf -> z = (float **) malloc(sizeof(float *) * numelts); inf -> idx = (int **) malloc(sizeof(int *) * numelts); xmin = ymin = zmin = inf -> smin = 1e12; xmax = ymax = zmax = inf -> smax = -1e12; for (i = 0 ; i < numelts ; i++) { epn = element [i+1] -> definition -> shapenodes; inf -> x [i] = (float *) malloc(sizeof(float) * epn); inf -> y [i] = (float *) malloc(sizeof(float) * epn); inf -> z [i] = (float *) malloc(sizeof(float) * epn); inf -> idx [i] = (int *) malloc(sizeof(int) * epn); for (j = 0 ; j < epn ; j++) { inf -> x[i][j] = element [i+1] -> node [j+1] -> x; inf -> y[i][j] = element [i+1] -> node [j+1] -> y; inf -> z[i][j] = element [i+1] -> node [j+1] -> z; if (stress) s = element [i+1] -> node [j+1] -> stress [comp]; else s = element [i+1] -> node [j+1] -> dx [comp]; if (s > inf -> smax) inf -> smax = s; else if (s < inf -> smin) inf -> smin = s; if (inf -> x [i][j] > xmax) xmax = inf -> x [i][j]; else if (inf -> x [i][j] < xmin) xmin = inf -> x [i][j]; if (inf -> y [i][j] > ymax) ymax = inf -> y [i][j]; else if (inf -> y [i][j] < ymin) ymin = inf -> y [i][j]; if (inf -> z [i][j] > zmax) zmax = inf -> z [i][j]; else if (inf -> z [i][j] < zmin) zmin = inf -> z [i][j]; } } xctr = (xmax + xmin)/2.0; yctr = (ymax + ymin)/2.0; zctr = (zmax + zmin)/2.0; xdist = xmax - xmin; ydist = ymax - ymin; zdist = zmax - zmin; if (xdist >= ydist && xdist >= zdist && xdist) scale = 2.0/xdist; else if (ydist >= xdist && ydist >= zdist && ydist) scale = 2.0/ydist; else if (zdist) scale = 2.0/zdist; else scale = 1.0; for (i = 0 ; i < numelts ; i++) { epn = element [i+1] -> definition -> shapenodes; for (j = 0 ; j < epn ; j++) { if (stress) s = element [i+1] -> node [j+1] -> stress [comp]; else s = element [i+1] -> node [j+1] -> dx [comp]; inf -> idx [i][j] = (int) (253*(s - inf -> smin)/ (inf -> smax - inf -> smin)); inf -> x [i][j] = (inf -> x [i][j] - xctr)*scale; inf -> y [i][j] = (inf -> y [i][j] - yctr)*scale; inf -> z [i][j] = (inf -> z [i][j] - zctr)*scale; } } } static int dblBuf[] = { GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; static int *snglBuf = &dblBuf[1]; static char layout_string [ ] = "vertical { \ 8 \ horizontal { \ 8 \ mesa <+inf -100% * +inf -100%> \ 8 \ } \ 24 \ horizontal { \ 8 \ dismiss \ 8 \ save \ 8 \ toggle \ 8 <+inf -100%> \ } \ 8 \ }"; static String table = "space: AutoRepeat(off) set()\n\ Return: AutoRepeat(off) set()\n\ Return: AutoRepeat(saved) unset() ShellAction(button)\n\ space: AutoRepeat(saved) unset() ShellAction(button)"; static void Action (w, event, params, num_params) Widget w; XEvent *event; String *params; Cardinal *num_params; { if (strcmp (params [0], "delete") == 0) w = XtNameToWidget (w, "layout.dismiss"); XtCallCallbacks (w, XtNcallback, NULL); } void CreateOpenGLShell(name, title, stress, comp, element, numelts, contour) String name; String title; Boolean stress; int comp; Element *element; unsigned numelts; Boolean contour; { static XVisualInfo *vi = NULL; Widget group [3]; Widget mesa, shell, layout, dismiss, save, toggle; Cardinal n; Arg args [10]; Pixel highlight; XtTranslations translations; static XtActionsRec actions [ ] = {{"ShellAction", Action}}; info *inf; inf = (info *) malloc(sizeof(info)); inf -> wedge = contour ? WEDGEWIDTH : 0; n = 0; XtSetArg (args [n], XtNtitle, title); n++; XtSetArg (args [n], XtNiconName, title); n++; XtSetArg (args [n], XtNallowShellResize, True); n++; shell = XtCreatePopupShell (name, topLevelShellWidgetClass, toplevel, args, n); inf -> shell = shell; if (vi == NULL) { dpy = XtDisplay(toplevel); vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf); if (vi == NULL) { vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf); if (vi == NULL) XtAppError(app_context, "no RGB visual with depth buffer"); doubleBuffer = GL_FALSE; } cx = glXCreateContext(dpy, vi, None, GL_TRUE); if (cx == NULL) XtAppError(app_context, "could not create rendering context"); } n = 0; XtSetArg (args [n], XtNlayout, StringToLayout (toplevel,layout_string)); n++; layout = XtCreateManagedWidget ("layout", layoutWidgetClass, shell, args, n); mesa = XtVaCreateManagedWidget ("mesa", glwDrawingAreaWidgetClass, layout, GLwNvisualInfo, vi, GLwNinstallColormap, True, XtNheight, 500, XtNwidth, 500 + inf -> wedge, NULL); inf -> mesa = mesa; dismiss = XtVaCreateManagedWidget ("dismiss", commandWidgetClass, layout, NULL); save = XtVaCreateManagedWidget ("save", commandWidgetClass, layout, NULL); toggle = XtVaCreateManagedWidget ("toggle", toggleWidgetClass, layout, XtNlabel, "wireframe", NULL); XtAddCallback (dismiss, XtNcallback, DismissFunction, (XtPointer) inf); XtAddCallback (save, XtNcallback, SaveFunction, (XtPointer) inf); XtAddCallback (toggle,XtNcallback, ToggleFunction, (XtPointer) inf); XtAddCallback (mesa, GLwNinputCallback, InputFunction, (XtPointer) inf); XtAddCallback (mesa, GLwNresizeCallback, ResizeFunction, (XtPointer) inf); XtAddCallback (mesa, GLwNexposeCallback, RedrawFunction, (XtPointer) inf); XtRealizeWidget (shell); # ifdef SMOOTHLINES glEnable (GL_LINE_SMOOTH); glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); # endif GLwDrawingAreaMakeCurrent (mesa, cx); glEnable (GL_DEPTH_TEST); glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); inf -> xrot = inf -> yrot = inf -> zrot = 0.0; LoadResults(inf, stress, comp, element, numelts); /* group [0] = dismiss; group [1] = save; group [2] = toggle; XtSetArg (args [0], XtNborderColor, &highlight); XtGetValues (layout, args, 1); CreateTabGroup (shell, group, 3, highlight, True); */ XtAppAddActions (app_context, actions, 1); translations = XtParseTranslationTable (table); XtOverrideTranslations (save, translations); XtOverrideTranslations (dismiss, translations); AddDeleteWindowProtocol (shell, "ShellAction(delete)"); InitDisplay (inf); XtPopup(shell, XtGrabNone); return; }