/* 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 "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; GLuint list; int wedge; Dimension winW, winH; Boolean contour; } 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) { glCallList(inf -> list); } 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 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; 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); inf -> contour = !state; glPolygonMode (GL_FRONT_AND_BACK, inf -> contour ? GL_FILL : GL_LINE); 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) wpid = XtAppAddWorkProc (app_context, RotateZPos, (XtPointer) inf); else if (button == 3 && !wpid) 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 if (wpid) { 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; } /* * for safety sake we have to re-initialize the perspective * view and the viewport because another GL widget may have * been recently active and thus those transformation matrices * are actually on the stack right now ... we'd have to do this * any way on a resize, the safety part is doing it for expose */ static void RedrawFunction(Widget w, XtPointer client_data, XtPointer call_data) { info *inf = (info *) client_data; GLwDrawingAreaMakeCurrent (w, cx); glPolygonMode (GL_FRONT_AND_BACK, inf -> contour ? GL_FILL : GL_LINE); InitDisplay(inf); glDisable(GL_SCISSOR_TEST); RefreshDisplay(inf); if (inf -> wedge) DrawWedge(inf); glEnable(GL_SCISSOR_TEST); return; } /* * also make sure things get re-initialized just once whenever * we get the focus back ... this will happen before any input * callbacks (user requested rotations) so that things start * out right for the newly focused widget */ static void HandleFocusEvent(Widget w, XtPointer client_data, XEvent *event, Boolean *cont) { info *inf = (info *) client_data; static Window prev_w = 0; /* * bail if we're losing focus or if this window is the most * most recently focused window (resize or re-focus clicks * generate FocusChange events) */ if (event -> type == FocusOut || prev_w == event -> xfocus.window) return; prev_w = event -> xfocus.window; RedrawFunction(inf -> mesa, (XtPointer) inf, NULL); } static void LoadResults(inf, element, numelts, mag) info *inf; Element *element; int numelts; float mag; { int i, j, k, nd; int epn; double x, y, z; double xmax, xmin, ymax, ymin, zmax, zmin; double xctr, yctr, zctr; double xdist, ydist, zdist; double scale; GLenum modes[] = {0, 0, GL_LINES, GL_TRIANGLES, GL_QUADS}; Node n; xmin = ymin = zmin = inf -> smin = 1e12; xmax = ymax = zmax = inf -> smax = -1e12; for (i = 0 ; i < numelts ; i++) { epn = element [i+1] -> definition -> shapenodes; for (j = 0 ; j < epn ; j++) { n = element [i+1] -> node [j+1]; x = n -> x + mag*n -> dx [1]; y = n -> y + mag*n -> dx [2]; z = n -> z + mag*n -> dx [3]; if (x > xmax) xmax = x; else if (x < xmin) xmin = x; if (y > ymax) ymax = y; else if (y < ymin) ymin = y; if (z > zmax) zmax = z; else if (z < zmin) zmin = z; } } 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; glNewList(inf -> list, GL_COMPILE); glColor3f(1.0, 1.0, 1.0); for (i = 0 ; i < numelts ; i++) { epn = element [i+1] -> definition -> shapenodes; if (element [i+1] -> definition -> shape == Solid) { /* Solid */ glBegin (GL_QUADS); for (j = 0 ; j < 6 ; j++) { for (k = 0 ; k < 4 ; k++) { nd = bface[j][k]; n = element [i+1] -> node [nd+1]; x = ((n -> x + mag*n -> dx [1]) - xctr)*scale; y = ((n -> y + mag*n -> dx [2]) - yctr)*scale; z = ((n -> z + mag*n -> dx [3]) - zctr)*scale; glVertex3f (x, y, z); } } glEnd (); } else { /* Linear or Planar */ glBegin (modes [epn]); for (j = 0 ; j < epn ; j++) { n = element [i+1] -> node [j+1]; x = ((n -> x + mag*n -> dx [1]) - xctr)*scale; y = ((n -> y + mag*n -> dx [2]) - yctr)*scale; z = ((n -> z + mag*n -> dx [3]) - zctr)*scale; glVertex3f (x, y, z); } glEnd(); } } glEndList(); } static void LoadContourResults(inf, stress, comp, element, numelts) info *inf; Boolean stress; int comp; Element *element; int numelts; { int i, j, k, nd; int epn; int idx; float s; double x, y, z; double xmax, xmin, ymax, ymin, zmax, zmin; double xctr, yctr, zctr; double xdist, ydist, zdist; double scale; GLenum modes[] = {0, 0, GL_LINES, GL_TRIANGLES, GL_QUADS}; xmin = ymin = zmin = inf -> smin = 1e12; xmax = ymax = zmax = inf -> smax = -1e12; for (i = 0 ; i < numelts ; i++) { epn = element [i+1] -> definition -> shapenodes; for (j = 0 ; j < epn ; j++) { x = element [i+1] -> node [j+1] -> x; y = element [i+1] -> node [j+1] -> y; z = 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 (x > xmax) xmax = x; else if (x < xmin) xmin = x; if (y > ymax) ymax = y; else if (y < ymin) ymin = y; if (z > zmax) zmax = z; else if (z < zmin) zmin = z; } } 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; inf -> list = glGenLists(1); glNewList(inf -> list, GL_COMPILE); for (i = 0 ; i < numelts ; i++) { epn = element [i+1] -> definition -> shapenodes; if (element [i+1] -> definition -> shape == Solid) { /* Solid */ glBegin (GL_QUADS); for (j = 0 ; j < 6 ; j++) { for (k = 0 ; k < 4 ; k++) { nd = bface[j][k]; x = (element [i+1] -> node [nd+1] -> x - xctr)*scale; y = (element [i+1] -> node [nd+1] -> y - yctr)*scale; z = (element [i+1] -> node [nd+1] -> z - zctr)*scale; if (stress) s = element [i+1] -> node [nd+1] -> stress [comp]; else s = element [i+1] -> node [nd+1] -> dx [comp]; idx = (int) (253*(s - inf -> smin) /(inf -> smax - inf -> smin)); glColor3fv (colors [idx]); glVertex3f (x, y, z); } } glEnd (); } else { /* Linear or Planar */ glBegin (modes [epn]); for (j = 0 ; j < epn ; j++) { x = (element [i+1] -> node [j+1] -> x - xctr)*scale; y = (element [i+1] -> node [j+1] -> y - yctr)*scale; z = (element [i+1] -> node [j+1] -> z - zctr)*scale; if (stress) s = element [i+1] -> node [j+1] -> stress [comp]; else s = element [i+1] -> node [j+1] -> dx [comp]; idx = (int) (253*(s - inf -> smin) /(inf -> smax - inf -> smin)); glColor3fv (colors [idx]); glVertex3f (x, y, z); } glEnd(); } } glEndList(); } 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); #ifdef __GLX_MOTIF mesa = XtVaCreateManagedWidget ("mesa", glwMDrawingAreaWidgetClass, layout, #else mesa = XtVaCreateManagedWidget ("mesa", glwDrawingAreaWidgetClass, layout, #endif 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, RedrawFunction, (XtPointer) inf); XtAddCallback (mesa, GLwNexposeCallback, RedrawFunction, (XtPointer) inf); XtAddEventHandler(shell, FocusChangeMask, False, HandleFocusEvent, (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, contour ? GL_FILL : GL_LINE); inf -> contour = contour; XtSetSensitive(toggle, contour); inf -> xrot = inf -> yrot = inf -> zrot = 0.0; inf -> list = glGenLists(1); if (contour) LoadContourResults(inf, stress, comp, element, numelts); else LoadResults(inf, element, numelts, 0.0); /* 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; }