/***************************************************************************** * Module to handle viewing of objects in the ViewWindow. * * * * Written by: Gershon Elber Unix - X11 Ver 0.1, Mar. 1990 * *****************************************************************************/ #ifdef __MSDOS__ #include #include #endif /* __MSDOS__ */ #include #include #include #include #include "program.h" #include "interact.h" #include "iritprsr.h" #include "graphgen.h" #ifdef SUPPORT_GIF_SAVE #include "gif_lib.h" #endif /* SUPPORT_GIF_SAVE */ #define MAX_ROTATE_ANGLE 45.0 /* Max. rates used by interactive mode. */ #define MAX_TRANSLATE_FACTOR 2.0 #define MAX_SCALE_FACTOR 2.0 #define MAX_PERSP_FACTOR 2.0 #define PERSPECTIVE_DEFAULT -5 /* We support depth cues iff DepthCueFlag is TRUE: */ #define IS_DOTTED_LINE (GlblDepthCue ? DOTTED_LINE : SOLID_LINE) static RealType LastCoord[3]; /* Used to store last point we moved/draw to. */ void (* MoveToPtr)(RealType Coord[3]); void (* DrawToPtr)(RealType Coord[3]); /* Save the current displayed object information in local variables. */ static IPObjectStruct *LocalObjects; static jmp_buf LongJumpBuffer; /* Used in breaking. */ static void ViewGeomObjectList(IPObjectStruct *Objects); static void SaveCurrentMat(void); static void DrawOneObject(IPObjectStruct *PObject); static void DrawOnePolygon(IPPolygonStruct *PPolygon); static void TestQuitView(void); #ifdef __GL__ static void DrawPolygonSolid(IPPolygonStruct *PPolygon); #endif /* __GL__ */ /***************************************************************************** * Routine to handle data from the input device (keyboard, mouse etc.) - * * clip it against the sub windows of the interactive menu and perform the * * required transfomation, by updating the global view matrix object VIEW_MAT * * The input data in the Rotation/Translation/Scaling sub windows is used * * (horizontal distance from sub window center) to set amount of change. * *****************************************************************************/ void InteractGeomObject(IPObjectStruct *Objects) { #ifdef SUPPORT_GIF_SAVE static char GifFileCount = '0'; char GifFileName[PATH_NAME_LEN]; #endif /* SUPPORT_GIF_SAVE */ char *p; int UpdateView; RealType ChangeFactor; MatrixType Mat, OrigViewMat, OrigPrspMat; #ifndef __MSDOS__ long WinID, DispID, ColorMapID; #endif /* __MSDOS__ */ /* Save copy of original matrix, so we can recover if reset is required. */ GEN_COPY(OrigViewMat, IritPrsrViewMat, sizeof(MatrixType)); GEN_COPY(OrigPrspMat, IritPrsrPrspMat, sizeof(MatrixType)); /* Update the drawing routine pointers: */ MoveToPtr = ScrnMoveTo; DrawToPtr = ScrnDrawTo; switch (GlblViewMode) { /* Update the current view. */ case VIEW_ORTHOGRAPHIC: GEN_COPY(CrntViewMat, IritPrsrViewMat, sizeof(MatrixType)); break; case VIEW_PERSPECTIVE: MultTwo4by4(CrntViewMat, IritPrsrViewMat, IritPrsrPrspMat); break; } LocalObjects = Objects; GGMySetLineStyle(SOLID_LINE); GGClearViewArea(); /* Clear the view window. */ ViewGeomObjectList(Objects); /* Display it for first time. */ #if defined(__MSDOS__) || defined(DJGCC) /* Set the async. function for intr_lib to refresh the view window. */ IntrWndwSetRefreshFunc(ViewWindowID, (IntrIntFunc) UpdateInteractHandleInput); #endif /* __MSDOS__ || DJGCC */ while (TRUE) { UpdateView = TRUE; GenUnitMat(Mat); /* No transformation by default! */ switch (GGGetGraphicEvent(&ChangeFactor)) { case EVENT_SCR_OBJ_TGL: /* Its Coordinate system - toggle it. */ UpdateView = FALSE; break; case EVENT_PERS_ORTHO_TGL: /* Its View mode - toggle it. */ break; case EVENT_PERS_ORTHO_Z: /* Its Perspective Z focal point modif. */ if (GlblViewMode != VIEW_PERSPECTIVE) { GGTone(1000, 100); /* Do some noise! */ UpdateView = FALSE; break; } /* Make it between 0.5 and 1.5: */ ChangeFactor = ChangeFactor / 2.0 + 1.0; IritPrsrPrspMat[2][2] *= ChangeFactor; IritPrsrPrspMat[2][3] *= ChangeFactor; IritPrsrPrspMat[3][2] *= ChangeFactor; break; case EVENT_ROTATE_X: /* Its rotation along the X axis. */ GenMatRotX1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat); break; case EVENT_ROTATE_Y: /* Its rotation along the Y axis. */ GenMatRotY1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat); break; case EVENT_ROTATE_Z: /* Its rotation along the Z axis. */ GenMatRotZ1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat); break; case EVENT_TRANSLATE_X: /* Its translation along the X axis. */ GenMatTrans(ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, 0.0, Mat); break; case EVENT_TRANSLATE_Y: /* Its translation along the Y axis. */ GenMatTrans(0.0, ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, Mat); break; case EVENT_TRANSLATE_Z: /* Its translation along the Z axis. */ GenMatTrans(0.0, 0.0, ChangeFactor * MAX_TRANSLATE_FACTOR, Mat); break; case EVENT_SCALE: /* Its scaling along all axes. */ if (ChangeFactor > 0.0) /* Make it around 1... */ ChangeFactor = ChangeFactor * MAX_SCALE_FACTOR + 1.0; else ChangeFactor = 1.0 / (-ChangeFactor * MAX_SCALE_FACTOR + 1.0); GenMatScale(ChangeFactor, ChangeFactor, ChangeFactor, Mat); break; case EVENT_DEPTH_CUE: if (!GlblDepthCue) GGMySetLineStyle(SOLID_LINE); break; #ifdef __GL__ case EVENT_DRAW_SOLID: break; #endif #if defined(__GL__) || defined(__X11__) || defined(__MSDOS__) || defined(DJGCC) case EVENT_SAVE_GIF: #ifdef SUPPORT_GIF_SAVE strcpy(GifFileName, GENERIC_GIF_FILE); if ((p = strchr(GifFileName, '#')) != NULL) { *p = GifFileCount; if (GifFileCount++ == '9') GifFileCount = '0'; } #ifdef __GL__ if (DumpScreen2Gif(GifFileName, (int) GIF_DUMP_SGI_WINDOW, GetViewWindowID(), 0, 0) != 0) { #endif #ifdef __X11__ WinID = GetViewWindowID(&DispID, &ColorMapID); if (DumpScreen2Gif(GifFileName, (int) GIF_DUMP_X_WINDOW, WinID, DispID, ColorMapID) != 0) { #endif #ifdef __MSDOS__ if (DumpScreen2Gif(GifFileName, GRGraphDriver, GRGraphMode, 0, 0) != 0) { #endif #ifdef DJGCC if (DumpScreen2Gif(GifFileName, 0, 0, GRScreenMaxX, GRScreenMaxY) != 0) { #endif /* Something went wrong - let the user know about it. */ GGTone(400, 300); GGTone(100, 300); } else GGTone(1000, 100); UpdateView = FALSE; #else GGTone(400, 300); GGTone(100, 300); #endif /* SUPPORT_GIF_SAVE */ break; #endif /* __GL__ || __X11__ || __MSDOS__ || DJGCC */ case EVENT_SAVE_PS: SavePostScript(Objects); UpdateView = FALSE; break; case EVENT_SAVE_MATRIX: SaveCurrentMat(); UpdateView = FALSE; break; case EVENT_RESET_MATRIX: GEN_COPY(IritPrsrViewMat, OrigViewMat, sizeof(MatrixType)); GEN_COPY(IritPrsrPrspMat, OrigPrspMat, sizeof(MatrixType)); break; case EVENT_QUIT: LocalObjects = NULL; return; /* Its Quit. */ default: GGTone(1000, 100); /* Do some noise! */ UpdateView = FALSE; } if (UpdateView) { GGClearViewArea(); /* Clear the view window. */ switch (GlblTransformMode) {/* Udpate the global viewing matrix. */ case TRANS_SCREEN: MultTwo4by4(IritPrsrViewMat, IritPrsrViewMat, Mat); break; case TRANS_OBJECT: MultTwo4by4(IritPrsrViewMat, Mat, IritPrsrViewMat); break; } switch (GlblViewMode) { /* Update the current view. */ case VIEW_ORTHOGRAPHIC: GEN_COPY(CrntViewMat, IritPrsrViewMat, sizeof(MatrixType)); break; case VIEW_PERSPECTIVE: MultTwo4by4(CrntViewMat, IritPrsrViewMat, IritPrsrPrspMat); break; } ViewGeomObjectList(Objects); /* And display it... */ } } } /***************************************************************************** * Routine to update the viewing screen. On unix systems this routine may * * be invoked when X sends expose event to this program (see xgrphgen.c). * *****************************************************************************/ void UpdateInteractHandleInput(void) { #if !defined(__MSDOS__) && !defined(DJGCC) GGClearViewArea(); #endif /* !__MSDOS__ && !DJGCC */ if (LocalObjects) ViewGeomObjectList(LocalObjects); } /***************************************************************************** * A wrap to trap escapes in the drawing stage using long jump. * *****************************************************************************/ static void ViewGeomObjectList(IPObjectStruct *Objects) { if (setjmp(LongJumpBuffer) == 0) { /* Its the setjmp itself call! */ DrawAllObjects(Objects); #if !defined(__MSDOS__) && !defined(DJGCC) GGGraphicFlush(); #endif /* !__MSDOS__ && !DJGCC */ } } /***************************************************************************** * Routine to save current view trans. IritPrsrViewMat to a generic mat file * *****************************************************************************/ static void SaveCurrentMat(void) { int i, j; FILE *f; char *p, FileName[PATH_NAME_LEN]; static char FileCount = '0'; strcpy(FileName, GENERIC_MAT_FILE); if ((p = strchr(FileName, '#')) != NULL) { *p = FileCount; if (FileCount++ == '9') FileCount = '0'; } if ((f = fopen(FileName, "wt")) == NULL) { GGTone(700, 200); return; } fprintf(f, "[OBJECT MATRICES\n [OBJECT VIEW_MAT\n\t[MATRIX"); for (i = 0; i < 4; i++) { fprintf(f, "\n\t "); for (j = 0; j < 4; j++) fprintf(f, "%12.9lf ", IritPrsrViewMat[i][j]); } fprintf(f, "\n\t]\n ]\n"); if (GlblViewMode == VIEW_PERSPECTIVE) { fprintf(f, " [OBJECT PRSP_MAT\n\t[MATRIX"); for (i = 0; i < 4; i++) { fprintf(f, "\n\t "); for (j = 0; j < 4; j++) fprintf(f, "%12.9lf ", IritPrsrPrspMat[i][j]); } fprintf(f, "\n\t]\n ]\n"); } fprintf(f, "]\n"); fclose(f); } /***************************************************************************** * Routine to draw all the objects in object list Objects. * *****************************************************************************/ void DrawAllObjects(IPObjectStruct *Objects) { while (Objects) { DrawOneObject(Objects); Objects = Objects -> Pnext; } } /***************************************************************************** * Routine to draw one object Object. * *****************************************************************************/ static void DrawOneObject(IPObjectStruct *PObject) { IPPolygonStruct *PList; switch (PObject -> Type) { case IP_OBJ_POLY: PList = PObject -> U.PPolygon; GGMySetColor(PObject -> Color); while (PList) { DrawOnePolygon(PList); PList = PList -> Pnext; } break; case IP_OBJ_SURFACE: #ifdef __GL__ if (GlblDrawSolid) { CagdSrfStruct *Srf; IPObjectStruct *PPolyObj; IPPolygonStruct *PPolygonTemp, *PPolygon; if (PObject -> FFPolygons == NULL) { PObject -> FFPolygons = PPolyObj = IritPrsrNewObjectStruct(); *PPolyObj = *PObject; /* Copy all its attributes. */ PPolyObj -> U.PPolygon = NULL; PPolyObj -> Type = IP_OBJ_POLY; for (Srf = PObject -> U.PSrfs; Srf != NULL; Srf = Srf -> Pnext) { PPolygon = PPolygonTemp = Surface2Polygons(Srf); while (PPolygonTemp -> Pnext) PPolygonTemp = PPolygonTemp -> Pnext; PPolygonTemp -> Pnext = PPolyObj -> U.PPolygon; PPolyObj -> U.PPolygon = PPolygon; } } DrawOneObject(PObject -> FFPolygons); } else #endif /* __GL__ */ DrawOneObject(PObject -> FFPolylines); break; case IP_OBJ_CURVE: DrawOneObject(PObject -> FFPolylines); break; } } /***************************************************************************** * Routine to draw one polygon, using global Matrix transform Mat. * * Note this is the routine that makes the real drawing... * *****************************************************************************/ static void DrawOnePolygon(IPPolygonStruct *PPolygon) { int i, j, DrawNextEdge, NumOfVertices; RealType MappedNormal[3], PolyNormal[3]; IPVertexStruct *VList = PPolygon -> PVertex, *VHead = VList; if (VList == NULL) return; TestQuitView(); switch (PPolygon -> Type) { case IP_POINTLIST: MoveToPtr(VList -> Coord); DrawToPtr(VList -> Coord); VList = VList -> Pnext; for (i = 0; i < GlblNumEdges && VList != NULL && VList != VHead; i++) { MoveToPtr(VList -> Coord); DrawToPtr(VList -> Coord); VList = VList -> Pnext; } break; case IP_POLYLINE: MoveToPtr(VList -> Coord); DrawNextEdge = !IP_IS_VRTX_INTERNAL(VList); VList = VList -> Pnext; for (i = 1; i < GlblNumEdges && VList != NULL && VList != VHead; i++) { if (DrawNextEdge || GlblInternal) DrawToPtr(VList -> Coord); else MoveToPtr(VList -> Coord); DrawNextEdge = !IP_IS_VRTX_INTERNAL(VList); VList = VList -> Pnext; } break; case IP_POLYGON: #ifdef __GL__ if (GlblDrawSolid) { DrawPolygonSolid(PPolygon); break; } #endif /* __GL__ */ if (GlblDrawPNormal && IP_HAS_POLY_PLANE(PPolygon)) { /* Sum all points to be averaged for normal position. */ for (i = 0; i < 3; i++) PolyNormal[i] = VList -> Coord[i]; NumOfVertices = 1; } MoveToPtr(VList -> Coord); DrawNextEdge = !IP_IS_VRTX_INTERNAL(VList); VList = VList -> Pnext; for (i = 1; i < GlblNumEdges && VList != NULL; i++) { if (DrawNextEdge || GlblInternal) DrawToPtr(VList -> Coord); else MoveToPtr(VList -> Coord); if (GlblDrawVNormal && IP_HAS_VRTX_NORMAL(VList)) { for (j = 0; j < 3; j++) MappedNormal[j] = VList -> Coord[j] + VList -> Normal[j] * GlblNormalLen; j = GlblClosedObject; GlblClosedObject = FALSE; DrawToPtr(MappedNormal); MoveToPtr(VList -> Coord); GlblClosedObject = j; } if (GlblDrawPNormal && IP_HAS_POLY_PLANE(PPolygon)) { for (j = 0; j < 3; j++) PolyNormal[j] += VList -> Coord[j]; NumOfVertices++; } DrawNextEdge = !IP_IS_VRTX_INTERNAL(VList); VList = VList -> Pnext; } if (GlblNumEdges > i) { /* Close polygon by drawing a line to first vertex. */ if (DrawNextEdge || GlblInternal) DrawToPtr(VHead -> Coord); else MoveToPtr(VHead -> Coord); if (GlblDrawVNormal && IP_HAS_VRTX_NORMAL(VList)) { for (j = 0; j < 3; j++) MappedNormal[j] = VList -> Coord[j] + VList -> Normal[j] * GlblNormalLen; j = GlblClosedObject; GlblClosedObject = FALSE; DrawToPtr(MappedNormal); MoveToPtr(VList -> Coord); GlblClosedObject = j; } } if (GlblDrawPNormal && IP_HAS_POLY_PLANE(PPolygon)) { for (i = 0; i < 3; i++) PolyNormal[i] /= NumOfVertices; MoveToPtr(PolyNormal); for (i = 0; i < 3; i++) PolyNormal[i] += PPolygon -> Plane[i] * GlblNormalLen; i = GlblClosedObject; GlblClosedObject = FALSE; DrawToPtr(PolyNormal); GlblClosedObject = i; } break; } } /***************************************************************************** * Routine to test if quit display event - occured - SPACE was hit on * * keyboard or right button was clicked on mouse. * *****************************************************************************/ static void TestQuitView(void) { if (GGIsAbortKeyPressed()) longjmp(LongJumpBuffer, 1); /* Jump to... */ } /***************************************************************************** * Routine to mave to 3D point given as Coord[3], using Mat transform. * *****************************************************************************/ void ScrnMoveTo(RealType Coords[3]) { MultVecby4by4(LastCoord, Coords, CrntViewMat); /* Set last point coord. */ } /***************************************************************************** * Routine to draw to 3D point given as Coord[3], using Mat transform, from * * the last point we moved to. * *****************************************************************************/ void ScrnDrawTo(RealType Coords[3]) { RealType NewCoord[3], MiddleCoord[3], t; MultVecby4by4(NewCoord, Coords, CrntViewMat); /* Set last point coord. */ if (GlblClosedObject && NewCoord[2] < LastCoord[2]) { GEN_COPY(LastCoord, NewCoord, 3 * sizeof(RealType)); return; } /* Implementation of simple depth cue - if line is >Z or = 0.0 && NewCoord[2] >= 0.0 || ABS(LastCoord[2] - NewCoord[2]) < EPSILON) { if (GlblDepthCue) GGMySetLineStyle(SOLID_LINE); GGMyMove(LastCoord[0], LastCoord[1]); GGMyDraw(NewCoord[0], NewCoord[1]); /* DRAW! */ } else { /* Line intersect Z = 0 plane. */ t = LastCoord[2] / (LastCoord[2] - NewCoord[2]); MiddleCoord[0] = LastCoord[0] * (1.0 - t) + NewCoord[0] * t; MiddleCoord[1] = LastCoord[1] * (1.0 - t) + NewCoord[1] * t; if (GlblDepthCue) GGMySetLineStyle(SOLID_LINE); if (LastCoord[2] > 0.0) { GGMyMove(LastCoord[0], LastCoord[1]); GGMyDraw(MiddleCoord[0], MiddleCoord[1]); /* DRAW! */ } else { GGMyMove(MiddleCoord[0], MiddleCoord[1]); GGMyDraw(NewCoord[0], NewCoord[1]); /* DRAW! */ } if (GlblDepthCue) GGMySetLineStyle(DOTTED_LINE);/* Draw the PVertex; if (IP_HAS_VRTX_NORMAL(VList)) UseVertexNormal = TRUE; else if (!IP_HAS_POLY_PLANE(PPolygon)) { /* Compute normal to polygon by cross prod. of 3 consecutive points. */ MultVecby4by4(V1, VList -> Coord, IritPrsrViewMat); MultVecby4by4(V2, VList -> Pnext-> Coord, IritPrsrViewMat); MultVecby4by4(V3, VList -> Pnext -> Pnext -> Coord, IritPrsrViewMat); for (i = 0; i < 3; i++) V1[i] -= V2[i]; for (i = 0; i < 3; i++) V2[i] -= V3[i]; Normal[0] = V1[1] * V2[2] - V1[2] * V2[1]; Normal[1] = V1[2] * V2[0] - V1[0] * V2[2]; Normal[2] = V1[0] * V2[1] - V1[1] * V2[0]; Length = sqrt(SQR(Normal[0]) + SQR(Normal[1]) + SQR(Normal[2])); for (i = 0; i < 3; i++) Normal[i] /= -Length; } else { for (i = 0; i < 3; i++) Normal[i] = VList -> Coord[i] + PPolygon -> Plane[i]; MultVecby4by4(V1, VList -> Coord, IritPrsrViewMat); MultVecby4by4(V2, Normal, IritPrsrViewMat); for (i = 0; i < 3; i++) Normal[i] = V2[i] - V1[i]; Length = sqrt(SQR(Normal[0]) + SQR(Normal[1]) + SQR(Normal[2])); for (i = 0; i < 3; i++) Normal[i] /= -Length; } GGMyDrawPolygonSolid(NULL, NULL, TRUE); for (; VList != NULL; VList = VList -> Pnext) { MultVecby4by4(MappedVertex, VList -> Coord, CrntViewMat); /* Each vertex has different normal - specify them. */ if (UseVertexNormal) { MultVecby4by4(V1, VList -> Coord, IritPrsrViewMat); for (i = 0; i < 3; i++) Normal[i] = VList -> Coord[i] + VList -> Normal[i]; MultVecby4by4(V2, Normal, IritPrsrViewMat); for (i = 0; i < 3; i++) Normal[i] = V2[i] - V1[i]; Length = sqrt(SQR(Normal[0]) + SQR(Normal[1]) + SQR(Normal[2])); for (i = 0; i < 3; i++) Normal[i] /= -Length; } for (i = 0; i < 3; i++) FloatNormal[i] = Normal[i]; GGMyDrawPolygonSolid(MappedVertex, FloatNormal, TRUE); if (l >= 255) break; /* GL limit. */ } GGMyDrawPolygonSolid(NULL, NULL, FALSE); } #endif /* __GL__ */