/***************************************************************************** * General routines to handle the graphic calls. * * currently supported devices: * * Input: Keyboard, mouse. * * Output: SGI 4D using gl library device. * * * * Note no transformation is using the hardware. All transformation is * * performed in xviewobj module in software. * * * * Written by: Gershon Elber Ver 0.1, Jul 1990. * *****************************************************************************/ #include #include #include #include #include #include #include "irit_sm.h" #include "genmat.h" #include "graphgen.h" /* Interactive menu setup structure: */ #define INTERACT_NUM_OF_STRINGS 3 #define INTERACT_NUM_OF_SUB_WNDWS_FULL 17 #define INTERACT_NUM_OF_SUB_WNDWS_PART 14 typedef struct InteractString { RealType X, Y; int Color; char *Str; } InteractString; typedef struct InteractSubWindow { RealType X, Y; /* Center points. */ int Color; GraphicEventType Event; int TextInside; /* If TRUE, Str will be in window, otherwise left to it. */ char *Str; } InteractSubWindow; typedef struct InteractWindowStruct { /* The interactive menu structures. */ /* Rotate, Translate, Scale strings: */ InteractString Strings[INTERACT_NUM_OF_STRINGS]; InteractSubWindow SubWindows[INTERACT_NUM_OF_SUB_WNDWS_FULL]; } InteractWindowStruct; #define INTERACT_SUB_WINDOW_WIDTH 0.8 /* Relative to window size. */ #define INTERACT_SUB_WINDOW_HEIGHT 0.04 static int InteractNumOfSubWndws = INTERACT_NUM_OF_SUB_WNDWS_FULL, PopTransWndwAlways = TRUE, TransWindowDisplayed = FALSE, DoDoubleBuffer = TRUE; /* Interactive mode menu set up structure is define below: */ static InteractWindowStruct InteractMenuFull = { { { 0.5, 0.81, RED, "Rotate" }, { 0.5, 0.65, GREEN, "Translate" }, { 0.5, 0.49, CYAN, "Scale" }, }, { { 0.5, 0.93, YELLOW, EVENT_SCR_OBJ_TGL, TRUE, "Screen Coords." }, { 0.5, 0.87, BLUE, EVENT_PERS_ORTHO_TGL,TRUE, "Perspective" }, { 0.5, 0.83, BLUE, EVENT_PERS_ORTHO_Z, FALSE, "Z" }, { 0.5, 0.75, RED, EVENT_ROTATE_X, FALSE, "X" }, /* Rot */ { 0.5, 0.71, RED, EVENT_ROTATE_Y, FALSE, "Y" }, { 0.5, 0.67, RED, EVENT_ROTATE_Z, FALSE, "Z" }, { 0.5, 0.59, GREEN, EVENT_TRANSLATE_X, FALSE, "X" }, /* Trans */ { 0.5, 0.55, GREEN, EVENT_TRANSLATE_Y, FALSE, "Y" }, { 0.5, 0.51, GREEN, EVENT_TRANSLATE_Z, FALSE, "Z" }, { 0.5, 0.43, CYAN, EVENT_SCALE, FALSE, "" }, /* Scale */ { 0.5, 0.35, MAGENTA,EVENT_DEPTH_CUE, TRUE, "Depth cue" }, { 0.5, 0.31, MAGENTA,EVENT_DRAW_SOLID, TRUE, "Wireframe" }, { 0.5, 0.25, YELLOW, EVENT_SAVE_GIF, TRUE, "Save GIF" }, { 0.5, 0.21, YELLOW, EVENT_SAVE_PS, TRUE, "Save PS" }, { 0.5, 0.15, YELLOW, EVENT_SAVE_MATRIX, TRUE, "Save Matrix" }, { 0.5, 0.11, YELLOW, EVENT_RESET_MATRIX, TRUE, "Reset Matrix" }, { 0.5, 0.03, WHITE, EVENT_QUIT, TRUE, "Quit" }, } }; static InteractWindowStruct InteractMenuPartial = { { { 0.5, 0.77, RED, "Rotate" }, { 0.5, 0.59, GREEN, "Translate" }, { 0.5, 0.41, CYAN, "Scale" }, }, { { 0.5, 0.92, YELLOW, EVENT_SCR_OBJ_TGL, TRUE, "Screen Coords." }, { 0.5, 0.85, BLUE, EVENT_PERS_ORTHO_TGL,TRUE, "Perspective" }, { 0.5, 0.81, BLUE, EVENT_PERS_ORTHO_Z, FALSE, "Z" }, { 0.5, 0.71, RED, EVENT_ROTATE_X, FALSE, "X" }, /* Rot */ { 0.5, 0.67, RED, EVENT_ROTATE_Y, FALSE, "Y" }, { 0.5, 0.63, RED, EVENT_ROTATE_Z, FALSE, "Z" }, { 0.5, 0.53, GREEN, EVENT_TRANSLATE_X, FALSE, "X" }, /* Trans */ { 0.5, 0.49, GREEN, EVENT_TRANSLATE_Y, FALSE, "Y" }, { 0.5, 0.45, GREEN, EVENT_TRANSLATE_Z, FALSE, "Z" }, { 0.5, 0.35, CYAN, EVENT_SCALE, FALSE, "" }, /* Scale */ { 0.5, 0.27, MAGENTA,EVENT_DEPTH_CUE, TRUE, "Depth cue" }, { 0.5, 0.19, MAGENTA,EVENT_DRAW_SOLID, TRUE, "Wireframe" }, { 0.5, 0.11, YELLOW, EVENT_RESET_MATRIX, TRUE, "Reset Matrix" }, { 0.5, 0.03, WHITE, EVENT_QUIT, TRUE, "Quit" }, } }; static InteractWindowStruct *InteractMenu = &InteractMenuFull; static short Colors[MAX_COLOR + 1][3] = { { 0, 0, 0 }, /* 0. BLACK */ { 0, 0, 170 }, /* 1. BLUE */ { 0, 170, 0 }, /* 2. GREEN */ { 0, 170, 170 }, /* 3. CYAN */ { 170, 0, 0 }, /* 4. RED */ { 170, 0, 170 }, /* 5. MAGENTA */ { 170, 170, 0 }, /* 6. BROWN */ { 170, 170, 170 }, /* 7. LIGHTGREY */ { 85, 85, 85 }, /* 8. DARKGRAY */ { 85, 85, 255 }, /* 9. LIGHTBLUE */ { 85, 255, 85 }, /* 10. LIGHTGREEN */ { 85, 255, 255 }, /* 11. LIGHTCYAN */ { 255, 85, 85 }, /* 12. LIGHTRED */ { 255, 85, 255 }, /* 13. LIGHTMAGENTA */ { 255, 255, 85 }, /* 14. YELLOW */ { 255, 255, 255 } /* 15. WHITE */ }; static long TransWinID = 0, TransWinWidth = 100, TransWinWidth2 = 50, TransWinHeight = 100, TransWinLow = 0, TransWinLeft = 0, ViewWinID = 0, ViewWinWidth = 100, ViewWinWidth2 = 50, ViewWinHeight = 100, ViewWinHeight2 = 50, ViewWinLow = 0, ViewWinLeft = 0; static int InGraphicMode = FALSE; static Matrix ViewStartMatrix; static long GGMapX(RealType x); static long GGMapY(RealType y); static void SetTransformWindow(void); static void RedrawTransformWindow(void); static void SetViewWindow(void); static void DrawText(char *Str, long PosX, long PosY); /**************************************************************************** * Routine to map real -1..1 normalized coordinates to screen X size. * ****************************************************************************/ static long GGMapX(RealType x) { return ViewWinWidth2 + ((long) (x * ViewWinWidth2)); } /**************************************************************************** * Routine to map real -1..1 normalized coordinates to screen Y size. * ****************************************************************************/ static long GGMapY(RealType y) { return ViewWinHeight2 + ((long) (y * ViewWinHeight2)); } /**************************************************************************** * Routine to move to a normalized point between -1..1 on both axes : * ****************************************************************************/ void GGMyMove(RealType x, RealType y) { move2i(GGMapX(x), GGMapY(y)); } /**************************************************************************** * Routine to draw to a normalized point between -1..1 on both axes : * ****************************************************************************/ void GGMyDraw(RealType x, RealType y) { draw2i(GGMapX(x), GGMapY(y)); } /**************************************************************************** * Routine to draw a polygon full. * * Polygon is started by NewPoly = TRUE and Vertex == NULL, Normal == NULL. * * Polygon is terminated NewPoly = FALSE and Vertex == NULL, Normal == NULL. * ****************************************************************************/ void GGMyDrawPolygonSolid(double *Vertex, float *Normal, int NewPoly) { if (Vertex == NULL && Normal == NULL) { if (NewPoly) bgnpolygon(); else endpolygon(); } else { n3f(Normal); v3d(Vertex); } } /**************************************************************************** * Routine to set line style. * ****************************************************************************/ void GGMySetLineStyle(int Style) { setlinestyle((short) Style); } /**************************************************************************** * Routine to draw to a normelized point between -1..1 on both axes : * ****************************************************************************/ void GGMySetColor(int c) { static float Ambient = 0.25, Diffuse = 0.75, Specular = 1.0; static float Material[] = { AMBIENT, 0.25, 0.25, 0.25, DIFFUSE, 0.75, 0.75, 0.75, SPECULAR, 1.00, 1.00, 1.00, SHININESS, 50, LMNULL }; static float Light1[] = { AMBIENT, 0.25, 0.25, 0.25, POSITION, 0.0, 0.0, 1.0, 0.0, LMNULL }; static float Light2[] = { AMBIENT, 0.25, 0.25, 0.25, POSITION, 0.0, 0.0, -1.0, 0.0, LMNULL }; int i; if (c < 0 || c > MAX_COLOR) c = WHITE; RGBcolor(Colors[c][0], Colors[c][1], Colors[c][2]); /* Prepare matrial structure in this color and select it. */ for (i = 0; i < 3; i++) { Material[1 + i] = Ambient * Colors[c][i] / 255.0; Material[5 + i] = Diffuse * Colors[c][i] / 255.0; Material[9 + i] = Specular * Colors[c][i] / 255.0; } lmdef(DEFMATERIAL, 1, sizeof(Material) / sizeof(float), Material); lmbind(MATERIAL, 1); lmdef(DEFLIGHT, 1, sizeof(Light1) / sizeof(float), Light1); lmbind(LIGHT1, 1); lmdef(DEFLIGHT, 2, sizeof(Light2) / sizeof(float), Light2); lmbind(LIGHT2, 2); } /**************************************************************************** * Routine to reset all the system to starting condition : * ****************************************************************************/ void GGInitGraph(int argc, char **argv, int FullTransMode, int TransAlways) { int i; if (InGraphicMode) return; if (FullTransMode) { InteractNumOfSubWndws = INTERACT_NUM_OF_SUB_WNDWS_FULL; InteractMenu = &InteractMenuFull; DoDoubleBuffer = TRUE; } else { InteractNumOfSubWndws = INTERACT_NUM_OF_SUB_WNDWS_PART; InteractMenu = &InteractMenuPartial; DoDoubleBuffer = FALSE; } PopTransWndwAlways = TransAlways; SetViewWindow(); qdevice(LEFTMOUSE); qdevice(MIDDLEMOUSE); qdevice(RIGHTMOUSE); /* The default drawing window is the view window. */ winset(ViewWinID); deflinestyle((short) DOTTED_LINE, (Linestyle) 0x3333); setbell(1); /* Make it short. */ InGraphicMode = TRUE; } /**************************************************************************** * Routine to close and shutdown graphic mode : * ****************************************************************************/ void GGCloseGraph(void) { if (!InGraphicMode) return; InGraphicMode = FALSE; } /***************************************************************************** * Routine to print a message on the given location: * *****************************************************************************/ void GGPutMsgXY(char *s, RealType x, RealType y) { int Width = strwidth(s); move2s((short) (GGMapX(x) - Width / 2), (short) (GGMapY(y) - getheight() / 2)); charstr(s); } /***************************************************************************** * Routine to clear the viewing area. * *****************************************************************************/ void GGClearViewArea(void) { static Matrix IDMat = { 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 }; GGMySetColor(BLACK); clear(); if (winget() == ViewWinID) { /* activate zbuffer only if we are in solid drawing mode. */ if (GlblDrawSolid) { mmode(MVIEWING); ortho(-1.0, 1.0, -1.0, 1.0, -2.0, 2.0); loadmatrix(IDMat); /* Define necessary staff for Lighting. */ lmbind(MATERIAL, 1); lmbind(LIGHT1, 1); lmbind(LIGHT2, 2); lmbind(LMODEL, 1); zbuffer(TRUE); zclear(); } else { mmode(MSINGLE); loadmatrix(ViewStartMatrix); zbuffer(FALSE); } } } /***************************************************************************** * Routine to make some sound with given Frequency, Time milliseconds: * *****************************************************************************/ void GGTone(int Frequency, int Duration) { if (GlblDoGraphics) ringbell(); } /***************************************************************************** * Set up and draw a transformation window. * *****************************************************************************/ static void SetTransformWindow(void) { long PrefPos[4]; #ifndef _AIX foreground(); #endif if (sscanf(GlblTransPrefPos, "%ld, %ld, %ld, %ld", &PrefPos[0], &PrefPos[1], &PrefPos[2], &PrefPos[3]) == 4) prefposition(PrefPos[0], PrefPos[1], PrefPos[2], PrefPos[3]); else if (sscanf(GlblTransPrefPos, "%ld, %ld", &PrefPos[0], &PrefPos[1]) == 2) prefsize(PrefPos[0], PrefPos[1]); winopen("Poly3dTrans"); if (DoDoubleBuffer) doublebuffer(); RGBmode(); gconfig(); getorigin(&TransWinLeft, &TransWinLow); getsize(&TransWinWidth, &TransWinHeight); TransWinWidth2 = TransWinWidth / 2; TransWinID = winget(); GGMySetColor(BLACK); clear(); if (DoDoubleBuffer) swapbuffers(); /* This is wierd. without the sleep the gl get mixed up between the two */ /* windows. If you have any idea why, let me know... */ sleep(1); } /***************************************************************************** * Set up and draw a transformation window. * *****************************************************************************/ static void RedrawTransformWindow(void) { int i; long SubTransPosX, SubTransPosY, SubTransWidth, SubTransHeight; /* Make sure the menu is consistent with internatal data. */ InteractMenu -> SubWindows[0].Str = GlblTransformMode == TRANS_OBJECT ? "Object Coords." : "Screen Coords."; InteractMenu -> SubWindows[1].Str = GlblViewMode == VIEW_PERSPECTIVE ? "Perspective" : "Orthographic"; InteractMenu -> SubWindows[10].Str = GlblDepthCue ? "Depth cue" : "No depth cue"; InteractMenu -> SubWindows[11].Str = GlblDrawSolid ? "Solid" : "Wireframe"; winset(TransWinID); /* Draw in the transformation window. */ SubTransWidth = (int) (TransWinWidth * INTERACT_SUB_WINDOW_WIDTH); SubTransHeight = (int) (TransWinHeight * INTERACT_SUB_WINDOW_HEIGHT); SubTransPosX = (TransWinWidth - SubTransWidth) / 2; GGMySetColor(BLACK); clear(); for (i = 0; i < InteractNumOfSubWndws; i++) { GGMySetColor(InteractMenu -> SubWindows[i].Color); SubTransPosY = (int) (TransWinHeight * InteractMenu -> SubWindows[i].Y); move2i(SubTransPosX, SubTransPosY); draw2i(SubTransPosX + SubTransWidth, SubTransPosY); draw2i(SubTransPosX + SubTransWidth, SubTransPosY + SubTransHeight); draw2i(SubTransPosX, SubTransPosY + SubTransHeight); draw2i(SubTransPosX, SubTransPosY); if (InteractMenu -> SubWindows[i].TextInside) { DrawText(InteractMenu -> SubWindows[i].Str, TransWinWidth / 2, SubTransPosY + SubTransHeight / 2); } else { DrawText(InteractMenu -> SubWindows[i].Str, (TransWinWidth - SubTransWidth) / 3, SubTransPosY + SubTransHeight / 2); move2i(SubTransPosX + SubTransWidth / 2, SubTransPosY); draw2i(SubTransPosX + SubTransWidth / 2, SubTransPosY + SubTransHeight); } } for (i = 0; i < INTERACT_NUM_OF_STRINGS; i++) { GGMySetColor(InteractMenu -> Strings[i].Color); DrawText(InteractMenu -> Strings[i].Str, (int) (InteractMenu -> Strings[i].X * TransWinWidth), (int) (InteractMenu -> Strings[i].Y * TransWinHeight)); } if (DoDoubleBuffer) swapbuffers(); winset(ViewWinID); /* Go back to the default drawing window. */ } /***************************************************************************** * Set up and draw a view window. * *****************************************************************************/ static void SetViewWindow(void) { long PrefPos[4]; #ifndef _AIX foreground(); #endif if (sscanf(GlblViewPrefPos, "%ld, %ld, %ld, %ld", &PrefPos[0], &PrefPos[1], &PrefPos[2], &PrefPos[3]) == 4) prefposition(PrefPos[0], PrefPos[1], PrefPos[2], PrefPos[3]); else if (sscanf(GlblViewPrefPos, "%ld, %ld", &PrefPos[0], &PrefPos[1]) == 2) prefsize(PrefPos[0], PrefPos[1]); winopen("Poly3dView"); if (DoDoubleBuffer) doublebuffer(); RGBmode(); gconfig(); getorigin(&ViewWinLeft, &ViewWinLow); getsize(&ViewWinWidth, &ViewWinHeight); ViewWinWidth2 = ViewWinWidth / 2; ViewWinHeight2 = ViewWinHeight / 2; getmatrix(ViewStartMatrix); ViewWinID = winget(); GGClearViewArea(); if (DoDoubleBuffer) swapbuffers(); concave(TRUE); /* Define necessary staff for Lighting. */ lmdef(DEFMATERIAL, 1, 0, NULL); lmdef(DEFLIGHT, 1, 0, NULL); lmdef(DEFLMODEL, 1, 0, NULL); /* This is wierd. without the sleep the gl get mixed up between the two */ /* windows. If you have any idea why, let me know... */ sleep(1); } /***************************************************************************** * Return View window ID. * *****************************************************************************/ long GetViewWindowID() { return ViewWinID; } /****************************************************************************** * Returns status of abort key if pressed, and reset it. * ******************************************************************************/ int GGIsAbortKeyPressed(void) { return getbutton(RIGHTMOUSE) == 1; } /****************************************************************************** * Handle gl events * ******************************************************************************/ GraphicEventType GGGetGraphicEvent(RealType *ChangeFactor) { static GraphicEventType LastEvent = EVENT_NONE; static long LastX = -1; int i, LeftButtonIsPressed = getbutton(LEFTMOUSE) == 1; GraphicEventType RetVal = EVENT_NONE; short data; long x, y, dev; RealType XPos, YPos; if (!TransWindowDisplayed) { /* Pop up the transformation window. */ SetTransformWindow(); RedrawTransformWindow(); TransWindowDisplayed = TRUE; } if (GlblWasCtrlBrk) { GlblWasCtrlBrk = FALSE; if (!PopTransWndwAlways) { winclose(TransWinID); TransWindowDisplayed = FALSE; } return EVENT_QUIT; } /* Allow continuous drag on following events only: */ if (LeftButtonIsPressed && !(LastEvent == EVENT_PERS_ORTHO_Z || LastEvent == EVENT_ROTATE_X || LastEvent == EVENT_ROTATE_Y || LastEvent == EVENT_ROTATE_Z || LastEvent == EVENT_TRANSLATE_X || LastEvent == EVENT_TRANSLATE_Y || LastEvent == EVENT_TRANSLATE_Z || LastEvent == EVENT_SCALE)) { while (getbutton(LEFTMOUSE) == 1); LeftButtonIsPressed = FALSE; } if (LeftButtonIsPressed) { /* Allow leaving the Trans window if still pressed, and use last */ /* event as the returned event. Note we wait until current position */ /* is different from last one to make sure we do something. */ while((x = getvaluator(MOUSEX) - TransWinLeft) == LastX && getbutton(LEFTMOUSE) == 1); if (x != LastX) { *ChangeFactor = (((RealType) x) - LastX) / TransWinWidth2; LastX = x; return LastEvent; } else LeftButtonIsPressed = FALSE; } LastEvent = EVENT_NONE; while (RetVal == EVENT_NONE) { /* Wait for left button to be pressed in the Trans window. Note this */ /* is the loop we are going to cycle in idle time. */ while (getbutton(LEFTMOUSE) != 1 || (x = getvaluator(MOUSEX)) < TransWinLeft || x > TransWinLeft + TransWinWidth || (y = getvaluator(MOUSEY)) < TransWinLow || y > TransWinLow + TransWinHeight) { if (GlblWasCtrlBrk) { GlblWasCtrlBrk = FALSE; if (!PopTransWndwAlways) { winclose(TransWinID); TransWindowDisplayed = FALSE; } return EVENT_QUIT; } if (qtest()) { /* Any external event occured? */ switch (dev = qread(&data)) { case REDRAW: if (data == ViewWinID) { getorigin(&ViewWinLeft, &ViewWinLow); getsize(&ViewWinWidth, &ViewWinHeight); ViewWinWidth2 = ViewWinWidth / 2; ViewWinHeight2 = ViewWinHeight / 2; reshapeviewport(); ortho2(-0.5, ViewWinWidth - 0.5, -0.5, ViewWinHeight - 0.5); getmatrix(ViewStartMatrix); UpdateInteractHandleInput(); } else if (data == TransWinID) { winset(TransWinID); getorigin(&TransWinLeft, &TransWinLow); getsize(&TransWinWidth, &TransWinHeight); reshapeviewport(); ortho2(-0.5, TransWinWidth - 0.5, -0.5, TransWinHeight - 0.5); TransWinWidth2 = TransWinWidth / 2; RedrawTransformWindow(); winset(ViewWinID); } break; } } } x -= TransWinLeft; y -= TransWinLow; XPos = ((RealType) x) / TransWinWidth; YPos = ((RealType) y) / TransWinHeight; /* Make sure we are in bound in the X direction. */ if (XPos < (1.0 - INTERACT_SUB_WINDOW_WIDTH) / 2.0 || XPos > 1.0 - (1.0 - INTERACT_SUB_WINDOW_WIDTH) / 2.0) { GGTone(1000, 100); continue; } /* Now search the sub window the event occured in. */ for (i = 0; i < InteractNumOfSubWndws; i++) { if (InteractMenu -> SubWindows[i].Y <= YPos && InteractMenu -> SubWindows[i].Y + INTERACT_SUB_WINDOW_HEIGHT >= YPos) { RetVal = InteractMenu -> SubWindows[i].Event; break; } } if (i == InteractNumOfSubWndws) { GGTone(1000, 100); continue; } /* Take care of special cases in which the window should be updated. */ switch (RetVal) { case EVENT_SCR_OBJ_TGL: GlblTransformMode = GlblTransformMode == TRANS_OBJECT ? TRANS_SCREEN : TRANS_OBJECT; RedrawTransformWindow(); break; case EVENT_PERS_ORTHO_TGL: GlblViewMode = GlblViewMode == VIEW_PERSPECTIVE ? VIEW_ORTHOGRAPHIC : VIEW_PERSPECTIVE; RedrawTransformWindow(); break; case EVENT_DEPTH_CUE: GlblDepthCue = !GlblDepthCue; RedrawTransformWindow(); break; case EVENT_DRAW_SOLID: GlblDrawSolid = !GlblDrawSolid; RedrawTransformWindow(); break; case EVENT_QUIT: if (!PopTransWndwAlways) { winclose(TransWinID); TransWindowDisplayed = FALSE; } break; } *ChangeFactor = (((RealType) x) - TransWinWidth2) / TransWinWidth2; } LastEvent = RetVal; LastX = x; return RetVal; } /****************************************************************************** * Flush output of graphic command. * ******************************************************************************/ void GGGraphicFlush(void) { /* We are using double buffering - flip buffers. */ if (DoDoubleBuffer) swapbuffers(); } /****************************************************************************** * Draw text centered at the given position. * ******************************************************************************/ static void DrawText(char *Str, long PosX, long PosY) { long Width = strwidth(Str); cmov2s(PosX - Width / 2, PosY - (getheight() / 2 - getdescender())); charstr(Str); }