/***************************************************************************** * "Irit" - the 3d polygonal solid modeller. * * * * Written by: Gershon Elber Unix - X11 Ver 0.1, Mar. 1990 * ****************************************************************************** * Module to handle viewing of objects in the ViewWindow. * *****************************************************************************/ #include #include #include "program.h" #include "attribut.h" #include "graphgen.h" #include "objects.h" #include "freeform.h" #include "geomat3d.h" #include "primitiv.h" #include "windows.h" #include "viewobj.h" #include "graphgen.h" #define MAX_ROTATE_ANGLE 45.0 /* Maximum rates used by interact mode. */ #define MAX_TRANSLATE_FACTOR 2.0 #define MAX_SCALE_FACTOR 2.0 static int ViewNormals = FALSE, ClosedObject = FALSE, SupportClosedObject = FALSE, NormalsColor = 1, /* View normals to object. */ QuitView = FALSE; static RealType LastCoord[3], /* Used to store last point we moved/draw to. */ NormalsSize = 0.1; static ObjectStruct *ActiveObjList = NULL; /* Currently displayed objects. */ /* Prototypes for the View Object module: */ static void InteractHandleInput(ObjectStruct *PObjList, MatrixType GlblViewMat, MatrixType GlblPrspMat); static MatrixType *ComputeCrntViewMatrix(void); static void ViewGeomObjectList(ObjectStruct *PObjList); static int GetInternal(void); static void ViewCagdPolyline(CagdPolylineStruct *Pl, int Color, MatrixType Mat); static void TestQuitView(void); static void ViewPolygon(PolygonStruct *Pl, int Color, int IsPolyline, MatrixType Mat, int ViewInternal); static void DepthCueMoveTo(RealType Coord[3]); static void DepthCueDrawTo(RealType NewCoord[3]); #ifdef __GL__ static void DrawPolygonSolid(PolygonStruct *PPolygon, MatrixType Mat); #endif /* __GL__ */ /***************************************************************************** * Routine to interactively display geometric object(s) PObj on the View * * window enable rotating/translating/scaling it using the Input Device. * *****************************************************************************/ void InteractPolyObject(ObjectStruct *PObj, RealType *UpdateGlblMat) { ObjectStruct *ViewMat, *PrspMat; MatrixType ViewMatCopy, PrspMatCopy; /* Save original trans. to recover. */ if (!GlblDoGraphics) return; if ((ViewMat = GetObject("VIEW_MAT")) == NULL) { WndwInputWindowPutStr( "No view transformation matrix VIEW_MAT!"); return; } else if (!IS_MAT_OBJ(ViewMat)) { WndwInputWindowPutStr( "VIEW_MAT object was modified (not matrix object)"); return; } MAT_COPY(ViewMatCopy, ViewMat -> U.Mat); if ((PrspMat = GetObject("PRSP_MAT")) == NULL) { WndwInputWindowPutStr( "No perspective transformation matrix PRSP_MAT!"); return; } else if (!IS_MAT_OBJ(PrspMat)) { WndwInputWindowPutStr( "PRSP_MAT object was modified (not matrix object)"); return; } MAT_COPY(PrspMatCopy, PrspMat -> U.Mat); /* Get data from input device, interpret it, and display interactively: */ InteractHandleInput(PObj, ViewMat -> U.Mat, PrspMat -> U.Mat); if (!APX_EQ(*UpdateGlblMat, 0.0)) { MAT_COPY(ViewMat -> U.Mat, ViewMatCopy); /* Recover. */ MAT_COPY(PrspMat -> U.Mat, PrspMatCopy); } } /***************************************************************************** * 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. * *****************************************************************************/ static void InteractHandleInput(ObjectStruct *PObjList, MatrixType GlblViewMat, MatrixType GlblPrspMat) { int UpdateView; RealType ChangeFactor; MatrixType Mat, OrigViewMat, OrigPrspMat; #if !defined(__MSDOS__) && !defined(DJGCC) long WinID, DispID, ColorMapID; #endif /* !__MSDOS__ && !DJGCC */ /* Save copy of original matrix, so we can recover if reset is required. */ GEN_COPY(OrigViewMat, GlblViewMat, sizeof(MatrixType)); GEN_COPY(OrigPrspMat, GlblPrspMat, sizeof(MatrixType)); ActiveObjList = PObjList; QuitView = FALSE; #ifdef __MSDOS__ IntrWndwPop(TransWindowID, TRUE, TRUE); #endif /* __MSDOS__ */ #if defined(__MSDOS__) || defined(DJGCC) GGClearViewArea(); /* Set the async. function for intr_lib to refresh the view window. */ IntrWndwSetRefreshFunc(ViewWindowID, (IntrIntFunc) UpdateInteractHandleInput); #endif /* __MSDOS__ || DJGCC */ UpdateInteractHandleInput(); /* Display it for the first time. */ while (TRUE) { QuitView = FALSE; UpdateView = TRUE; 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. */ MatGenUnitMat(Mat); 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; GlblPrspMat[2][2] *= ChangeFactor; GlblPrspMat[2][3] *= ChangeFactor; GlblPrspMat[3][2] *= ChangeFactor; MatGenUnitMat(Mat); break; case EVENT_ROTATE_X: /* Its rotation along the X axis. */ MatGenMatRotX1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat); break; case EVENT_ROTATE_Y: /* Its rotation along the Y axis. */ MatGenMatRotY1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat); break; case EVENT_ROTATE_Z: /* Its rotation along the Z axis. */ MatGenMatRotZ1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat); break; case EVENT_TRANSLATE_X: /* Its translation along the X axis. */ MatGenMatTrans(ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, 0.0, Mat); break; case EVENT_TRANSLATE_Y: /* Its translation along the Y axis. */ MatGenMatTrans(0.0, ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, Mat); break; case EVENT_TRANSLATE_Z: /* Its translation along the Z axis. */ MatGenMatTrans(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); MatGenMatScale(ChangeFactor, ChangeFactor, ChangeFactor, Mat); break; case EVENT_DEPTH_CUE: if (!GlblDepthCue) GGMySetLineStyle(SOLID_LINE); MatGenUnitMat(Mat); break; #ifdef __GL__ /* Only on GL library systems. */ case EVENT_DRAW_SOLID: MatGenUnitMat(Mat); break; #else case EVENT_DRAW_SOLID: #endif case EVENT_SAVE_GIF: case EVENT_SAVE_PS: case EVENT_SAVE_MATRIX: GGTone(800, 100); GGTone(400, 200); UpdateView = FALSE; break; case EVENT_RESET_MATRIX: GEN_COPY(GlblViewMat, OrigViewMat, sizeof(MatrixType)); GEN_COPY(GlblPrspMat, OrigPrspMat, sizeof(MatrixType)); MatGenUnitMat(Mat); break; case EVENT_QUIT: ActiveObjList = NULL; #ifdef __MSDOS__ IntrWndwPop(StatusWindowID, TRUE, TRUE); #endif /* __MSDOS__ */ return; /* Its Quit. */ default: FatalError("InteractHandleInput: Undefine input type, exit\n"); } if (UpdateView) { switch (GlblTransformMode) {/* Udpate the global viewing matrix. */ case TRANS_SCREEN: MatMultTwo4by4(GlblViewMat, GlblViewMat, Mat); break; case TRANS_OBJECT: MatMultTwo4by4(GlblViewMat, Mat, GlblViewMat); break; } #if defined(__MSDOS__) || defined(DJGCC) GGClearViewArea(); #endif /* __MSDOS__ || DJGCC */ UpdateInteractHandleInput(); } } } /***************************************************************************** * Compute the current view using VIEW_MAT and PRSP_MAT matrices. * *****************************************************************************/ static MatrixType *ComputeCrntViewMatrix(void) { static MatrixType CrntViewMat; ObjectStruct *ViewMat, *PrspMat; if ((ViewMat = GetObject("VIEW_MAT")) == NULL) { WndwInputWindowPutStr( "No view transformation matrix VIEW_MAT!"); return NULL; } else if (!IS_MAT_OBJ(ViewMat)) { WndwInputWindowPutStr( "VIEW_MAT object was modified (not matrix object)"); return NULL; } if ((PrspMat = GetObject("PRSP_MAT")) == NULL) { WndwInputWindowPutStr( "No perspective transformation matrix PRSP_MAT!"); return NULL; } else if (!IS_MAT_OBJ(PrspMat)) { WndwInputWindowPutStr( "PRSP_MAT object was modified (not matrix object)"); return NULL; } switch (GlblViewMode) { case VIEW_ORTHOGRAPHIC: GEN_COPY(CrntViewMat, ViewMat -> U.Mat, sizeof(MatrixType)); break; case VIEW_PERSPECTIVE: MatMultTwo4by4(CrntViewMat, ViewMat -> U.Mat, PrspMat -> U.Mat); break; } return &CrntViewMat; } /***************************************************************************** * 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). * * In MSDOS this routine is static and been called from InteractHandleInput * * above routine only. * *****************************************************************************/ void UpdateInteractHandleInput(void) { #if !defined(__MSDOS__) && !defined(DJGCC) GGClearViewArea(); #endif /* !__MSDOS__ && !DJGCC */ if (ActiveObjList != NULL) ViewGeomObject(ActiveObjList); /* And display it... */ #if !defined(__MSDOS__) && !defined(DJGCC) GGGraphicFlush(); #endif /* !__MSDOS__ && !DJGCC */ } /***************************************************************************** * Routine to display the geometric objects in PObjList, by simply calling * * ViewGeomObject on all of them. PObjList must be of type OBJ_LIST_OBJ. * *****************************************************************************/ static void ViewGeomObjectList(ObjectStruct *PObjList) { int Param = 0; ObjectStruct *PObj; while ((PObj = PObjList -> U.PObjList[Param]) != NULL && Param++ < MAX_OBJ_LIST && !QuitView) { ViewGeomObject(PObj); } } /***************************************************************************** * Routine to fetch the internal parameter from the INTERNAL object. * *****************************************************************************/ static int GetInternal(void) { int Internal; ObjectStruct *PObj = GetObject("INTERNAL"); if (PObj == NULL || !IS_NUM_OBJ(PObj)) { WndwInputWindowPutStr("No numeric object name INTERNAL is defined"); Internal = DEFAULT_INTERNAL; } else Internal = !APX_EQ((PObj -> U.R), 0.0); return Internal; } /***************************************************************************** * Routine to display the geometric object PObj on the View Window: * * Uses the global view transformation as computed by ComputeCrntViewMat. * *****************************************************************************/ void ViewGeomObject(ObjectStruct *PObj) { int Color, IsPolyline, DrawCtlPtColor = GetDrawCtlPt(), ViewInternal = GetInternal(), RealResolution = GetResolution(FALSE); CagdPolylineStruct *CagdPl; PolygonStruct *Pl; MatrixType *CrntMat; if (!GlblDoGraphics) return; QuitView = FALSE; if (IS_OLST_OBJ(PObj)) { /* Invoke the display routine on each of its componenets. */ ViewGeomObjectList(PObj); return; } if (!IS_GEOM_OBJ(PObj)) { WndwInputWindowPutStr("None displayable object ignored."); return; } Color = GetObjectColor(PObj); if ((CrntMat = ComputeCrntViewMatrix()) == NULL) return; if (IS_POLY_OBJ(PObj)) { Pl = PObj -> U.Pl.P; IsPolyline = IS_POLYLINE_OBJ(PObj); if (!IsPolyline && SupportClosedObject) ClosedObject = TRUE; while (Pl && !QuitView) { ViewPolygon(Pl, Color, IsPolyline, *CrntMat, ViewInternal); Pl = Pl -> Pnext; TestQuitView();/* if break display in the middle - Set QuitView. */ } ClosedObject = FALSE; } else if (IS_CRV_OBJ(PObj)) { if (!GlblDrawSolid) { ComputeCurveIsoLines(PObj); if (RealResolution > 0) ViewCagdPolyline(PObj -> U.Crv.PLPolys, Color, *CrntMat); if (DrawCtlPtColor) ViewCagdPolyline(PObj -> U.Crv.CtlPoly, DrawCtlPtColor, *CrntMat); } } else if (IS_SRF_OBJ(PObj)) { if (GlblDrawSolid) { ComputeSurfacePolygons(PObj); SetObjectColor(PObj -> U.Srf.Polygons, GetObjectColor(PObj)); ViewGeomObject(PObj -> U.Srf.Polygons); } else { ComputeSurfaceIsoLines(PObj); if (RealResolution > 0) { CagdPl = PObj -> U.Srf.PLPolys; while (CagdPl && !QuitView) { ViewCagdPolyline(CagdPl, Color, *CrntMat); CagdPl = CagdPl -> Pnext; TestQuitView();/* Break display in middle - Set QuitView. */ } } if (DrawCtlPtColor) { CagdPl = PObj -> U.Srf.CtlMesh; while (CagdPl && !QuitView) { ViewCagdPolyline(CagdPl, DrawCtlPtColor, *CrntMat); CagdPl = CagdPl -> Pnext; TestQuitView(); } } } } } /***************************************************************************** * Routine to display one polyline on the view window using the matrix Mat * * as a transformation matrix. * *****************************************************************************/ static void ViewCagdPolyline(CagdPolylineStruct *Pl, int Color, MatrixType Mat) { int i, j; PointType P, TempP; CagdPtStruct *Points = Pl -> Polyline; /* Since we can not guarantee that CagdRType == RealType: */ for (i = 0; i < 3; i++) TempP[i] = Points[0].Pt[i]; MatMultVecby4by4(P, TempP, Mat); /* Transform the first point. */ DepthCueMoveTo(P); GGMySetColor(Color); for (i = 1; i < Pl -> Length; i++) { for (j = 0; j < 3; j++) TempP[j] = Points[i].Pt[j]; MatMultVecby4by4(P, TempP, Mat); DepthCueDrawTo(P); }; } /***************************************************************************** * Routine to test if quit display event - occured - * * Right button was clicked on mouse. * *****************************************************************************/ static void TestQuitView(void) { QuitView = GGIsAbortKeyPressed(); } /***************************************************************************** * Routine to display one polygon on the view window using the matrix Mat as * * a transformation matrix. * *****************************************************************************/ static void ViewPolygon(PolygonStruct *Pl, int Color, int IsPolyline, MatrixType Mat, int ViewInternal) { int NumOfPoints, DontDraw, ShowNormals = ViewNormals && !IsPolyline; PointType P, CenterP; VertexStruct *V, *VStart; V = VStart = Pl -> V; if (V == NULL) FatalError("ViewPolygon: Empty polygon to view\n"); GGMySetColor(Color); #ifdef __GL__ /* Only on GL library systems. */ if (GlblDrawSolid && !IsPolyline) { DrawPolygonSolid(Pl, Mat); return; } #endif MatMultVecby4by4(P, V -> Pt, Mat); /* Transform the first point. */ DepthCueMoveTo(P); DontDraw = IS_INTERNAL_EDGE(V) && !ViewInternal; /* Draw next edge? */ if (ShowNormals) { /* If display of normal is required. */ NumOfPoints = 0; PT_CLEAR(CenterP); } do { V = V -> Pnext; if (ShowNormals) { NumOfPoints++; PT_ADD(CenterP, CenterP, V -> Pt); } MatMultVecby4by4(P, V -> Pt, Mat); /* If edge is INTERNAL (Irit.h) and not ViewInternal - dont draw: */ if (DontDraw) DepthCueMoveTo(P); else DepthCueDrawTo(P); DontDraw = IS_INTERNAL_EDGE(V) && !ViewInternal; /* Draw next edge? */ } while (V != VStart && V -> Pnext != NULL); if (ShowNormals) { PT_SCALE(CenterP, 1.0/NumOfPoints); /* Estimate for normals. */ MatMultVecby4by4(P, CenterP, Mat); /* Transform the first point. */ DepthCueMoveTo(P); PT_COPY(P, Pl -> Plane); PT_SCALE(P, NormalsSize); PT_ADD(CenterP, CenterP, P); MatMultVecby4by4(P, CenterP, Mat); /* Transform the second point. */ GGMySetColor(NormalsColor); DepthCueDrawTo(P); } } /***************************************************************************** * Routine to set the normals default values: * *****************************************************************************/ void ViewSetNormals(RealType *Active, RealType *Size, RealType *Color) { ViewNormals = !APX_EQ(*Active, 0.0); NormalsSize = *Size; NormalsColor = (int) *Color; } /***************************************************************************** * Routine to set the closed objects default. * *****************************************************************************/ void ViewSetClosed(RealType *Closed) { SupportClosedObject = !APX_EQ(*Closed, 0.0); } /***************************************************************************** * Routine to mave to 3D point given as Coord[3], using Mat transform. * *****************************************************************************/ static void DepthCueMoveTo(RealType Coord[3]) { GEN_COPY(LastCoord, Coord, 3 * sizeof(RealType)); /* Set crnt point. */ } /***************************************************************************** * Routine to draw to 3D point given as Coord[3], using Mat transform, from * * the last point we moved to. * *****************************************************************************/ static void DepthCueDrawTo(RealType NewCoord[3]) { RealType MiddleCoord[3], t; if (ClosedObject && 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 V; GGMyDrawPolygonSolid(NULL, NULL, TRUE); do { MatMultVecby4by4(V1R, PList -> Pt, Mat); for (i = 0; i < 3; i++) MappedVertex[i] = V1R[i]; /* Each vertex has different normal - specify them. */ MatMultVecby4by4(V1R, PList -> Pt, Mat); for (i = 0; i < 3; i++) V2R[i] = PList -> Pt[i] + PList -> Normal[i]; MatMultVecby4by4(V2R, V2R, Mat); for (i = 0; i < 3; i++) Normal[i] = V2R[i] - V1R[i]; Length = sqrt(SQR(Normal[0]) + SQR(Normal[1]) + SQR(Normal[2])); for (i = 0; i < 3; i++) Normal[i] /= -Length; GGMyDrawPolygonSolid(MappedVertex, Normal, TRUE); PList = PList -> Pnext; if (l++ >= 255) FatalError("GL: polygon too complex (> 256 vertices).\n"); } while (PList != NULL && PList != PPolygon -> V); GGMyDrawPolygonSolid(NULL, NULL, FALSE); } #endif /* __GL__ */