/***************************************************************************** * Filter to convert IRIT data files to ray shade format. * * * * Written by: Gershon Elber Ver 1.0, Sep 1991 * *****************************************************************************/ #ifdef __MSDOS__ #include #include #endif /* __MSDOS__ */ #include #include #include #include #include "irit_sm.h" #include "iritprsr.h" #include "getarg.h" #include "genmat.h" #define DIST_EPSILON 2e-4 #define SIZE_EPSILON 1e-5 #define CONVEX_EPSILON 1e-3 #ifdef __MSDOS__ extern unsigned int _stklen = 32766; /* Increase default stack size. */ #endif /* __MSDOS__ */ #ifdef NO_CONCAT_STR static char *VersionStr = "Irit2Ray Version 3.0, Gershon Elber,\n\ (C) Copyright 1989/90/91 Gershon Elber, Non commercial use only."; #else static char *VersionStr = "Irit2Ray " VERSION ", Gershon Elber, " __DATE__ ", " __TIME__ "\n" "(C) Copyright 1989/90/91 Gershon Elber, Non commercial use only."; #endif /* NO_CONCAT_STR */ static char *CtrlStr = "irit2ray l%- 4%- G%-GridSize!d f%-FineNess!d o%-OutName!s g%- z%- DFiles!*s"; static char *OutFileName = "irit2ray"; static int GlblGridSize = 5, GlblGridFlag = FALSE, GlblFineNess = 5, GlblDumpOnlyGeometry = FALSE, FourPerFlat = FALSE; static MatrixType CrntViewMat; /* This is the current view! */ static int TransColorTable[][4] = { { /* BLACK */ 0, 0, 0, 0 }, { /* BLUE */ 1, 0, 0, 255 }, { /* GREEN */ 2, 0, 255, 0 }, { /* CYAN */ 3, 0, 255, 255 }, { /* RED */ 4, 255, 0, 0 }, { /* MAGENTA */ 5, 255, 0, 255 }, { /* BROWN */ 6, 50, 0, 0 }, { /* LIGHTGRAY */ 7, 127, 127, 127 }, { /* DARKGRAY */ 8, 63, 63, 63 }, { /* LIGHTBLUE */ 9, 0, 0, 255 }, { /* LIGHTGREEN */ 10, 0, 255, 0 }, { /* LIGHTCYAN */ 11, 0, 255, 255 }, { /* LIGHTRED */ 12, 255, 0, 0 }, { /* LIGHTMAGENTA */ 13, 255, 0, 255 }, { /* YELLOW */ 14, 255, 255, 0 }, { /* WHITE */ 15, 255, 255, 255 }, { /* BROWN */ 20, 50, 0, 0 }, { /* DARKGRAY */ 56, 63, 63, 63 }, { /* LIGHTBLUE */ 57, 0, 0, 255 }, { /* LIGHTGREEN */ 58, 0, 255, 0 }, { /* LIGHTCYAN */ 59, 0, 255, 255 }, { /* LIGHTRED */ 60, 255, 0, 0 }, { /* LIGHTMAGENTA */ 61, 255, 0, 255 }, { /* YELLOW */ 62, 255, 255, 0 }, { /* WHITE */ 63, 255, 255, 255 }, { -1, 0, 0, 0 } }; static IPObjectStruct *MainGetDataFiles(char **DataFileNames, int NumOfDataFiles); static IPPolygonStruct *Surface2Polygons(CagdSrfStruct *Srf, int FourPerFlat, int FineNess); static void DumpDataForRayShade(IPObjectStruct *PObjects); static int DumpOneObject(FILE *FRay, FILE *FGeom, IPObjectStruct *PObject); static int DumpOnePolygon(FILE *f, IPPolygonStruct *PPolygon); static int IsConvexPolygon(IPPolygonStruct *Pl); static RealType *MapPoint(RealType *Pt); static RealType *MapVector(RealType *Pt, RealType *Vec); static void MyExit(int ExitCode); /***************************************************************************** * Main routine - Read Parameter line and do what you need... * *****************************************************************************/ void main(int argc, char **argv) { int Error, FineNessFlag = FALSE, LinearOnePolyFlag = FALSE, VerFlag = FALSE, OutFileFlag = FALSE, NumFiles = 0; char Line[LINE_LEN_LONG], *p, **FileNames = NULL; IPObjectStruct *PObjects; #ifdef __MSDOS__ ctrlbrk((int (*)()) MyExit); /* Kill process if ^C. */ #endif /* __MSDOS__ */ if ((Error = GAGetArgs (argc, argv, CtrlStr, &LinearOnePolyFlag, &FourPerFlat, &GlblGridFlag, &GlblGridSize, &FineNessFlag, &GlblFineNess, &OutFileFlag, &OutFileName, &GlblDumpOnlyGeometry, &VerFlag, &NumFiles, &FileNames)) != 0) { GAPrintErrMsg(Error); GAPrintHowTo(CtrlStr); MyExit(1); } if (VerFlag) { fprintf(stderr, "\n%s\n\n", VersionStr); GAPrintHowTo(CtrlStr); MyExit(0); } if (LinearOnePolyFlag) { fprintf(stderr, "Linear patch side will have a single polygon.\n"); CagdSetLinear2Poly(CAGD_ONE_POLY_PER_COLIN); } else CagdSetLinear2Poly(CAGD_REG_POLY_PER_LIN); fprintf(stderr, "%s triangles per flat will be created.\n", FourPerFlat ? "Four" : "Two"); if (!NumFiles) { fprintf(stderr, "No data file names where given, exit.\n"); GAPrintHowTo(CtrlStr); MyExit(1); } if (!OutFileFlag) { /* Pick the first input name as output name. */ strcpy(Line, FileNames[0]); if ((p = strrchr(Line, '.')) != NULL); /* Remove old file type. */ *p = 0; OutFileName = malloc(strlen(Line) + 1); strcpy(OutFileName, Line); } /* Get the data files: */ IritPrsrPolyListCirc = FALSE; PObjects = MainGetDataFiles(FileNames, NumFiles); if (IritPrsrWasPrspMat) MultTwo4by4(CrntViewMat, IritPrsrViewMat, IritPrsrPrspMat); else GEN_COPY(CrntViewMat, IritPrsrViewMat, sizeof(MatrixType)); DumpDataForRayShade(PObjects); MyExit(0); } /***************************************************************************** * Main routine to read the data description files: * * Returns pointer to pointers on FileDescription structures (one per file). * *****************************************************************************/ static IPObjectStruct *MainGetDataFiles(char **DataFileNames, int NumOfDataFiles) { int i; FILE *f; char *ErrorMsg = NULL; IPObjectStruct *PObj, *PObjTail, *PObjHead = NULL; for (i = 0; i < NumOfDataFiles; i++) { #ifdef __MSDOS__ if ((f = fopen(*DataFileNames, "rt")) == NULL) { /* Open the file. */ #else if ((f = fopen(*DataFileNames, "r")) == NULL) { /* Open the file. */ #endif /* __MSDOS__ */ fprintf(stderr, "Can't open data file %s\n", *DataFileNames); MyExit(1); } if ((PObj = IritPrsrGetObjects(f)) != NULL) { /* Get the data file. */ PObjTail = PObj; while (PObjTail -> Pnext) PObjTail = PObjTail -> Pnext; PObjTail -> Pnext = PObjHead; PObjHead = PObj; } fclose(f); /* Close the file. */ if (IritPrsrParseError(&ErrorMsg)) { fprintf(stderr, "Parse error in \"%s\":\n%s\n", *DataFileNames, ErrorMsg); MyExit(1); } DataFileNames++; /* Skip to next file name. */ } if (PObjHead == NULL) { fprintf(stderr, "No data found.\n"); MyExit(1); } return PObjHead; } /***************************************************************************** * Routine to convert all surfaces/curves into polylines as follows: * * Curves are converted to single polyline with SamplesPerCurve samples. * * Surface are converted into GlblNumOfIsolines curves in each axes, each * * handled as Curves above. The curves and surfaces are then deleted. * *****************************************************************************/ IPObjectStruct *IritPrsrProcessFreeForm(IPObjectStruct *CrvObjs, IPObjectStruct *SrfObjs) { int LocalFourPerFlat; float RelativeFineNess; CagdCrvStruct *Crvs; CagdSrfStruct *Srf, *Srfs; IPObjectStruct *PObj, *PObjNext; IPPolygonStruct *PPolygon, *PPolygonTemp; if (CrvObjs == NULL && SrfObjs == NULL) return NULL; /* Make sure requested format is something reasonable. */ if (GlblFineNess < 2) { GlblFineNess = 2; fprintf(stderr, "FineNess is less than 2, 2 picked instead.\n"); } if (CrvObjs) { /* Curves are not rendered at this time and they are ignored. */ for (PObj = CrvObjs; PObj != NULL;) { Crvs = PObj -> U.PCrvs; CagdCrvFreeList(Crvs); PObjNext = PObj -> Pnext; free((VoidPtr) PObj); PObj = PObjNext; } CrvObjs = NULL; } if (SrfObjs) { for (PObj = SrfObjs; PObj != NULL; PObj = PObj -> Pnext) { CagdBBoxStruct BBox, TempBBox; char GridStr[LINE_LEN], *p; Srfs = PObj -> U.PSrfs; PObj -> U.PPolygon = NULL; RelativeFineNess = 1.0; LocalFourPerFlat = FourPerFlat; if (IritPrsrGetStrAttrib(PObj, "twoperflat")) LocalFourPerFlat = FALSE; if (IritPrsrGetStrAttrib(PObj, "fourperflat")) LocalFourPerFlat = TRUE; if ((p = IritPrsrGetStrAttrib(PObj, "resolution")) != NULL && sscanf(p, "%f", &RelativeFineNess) != 1) RelativeFineNess = 1.0; for (Srf = Srfs; Srf != NULL; Srf = Srf -> Pnext) { if (GlblGridFlag) { /* Generate bounding box to the surfaces and estimate */ /* the grid size for it using GlblGridSize. */ if (Srf == Srfs) CagdSrfBBox(Srf, &BBox); else { CagdSrfBBox(Srf, &TempBBox); CagdMergeBBox(&BBox, &TempBBox); } } PPolygon = PPolygonTemp = Surface2Polygons(Srf, LocalFourPerFlat, (int) (RelativeFineNess * GlblFineNess)); while (PPolygonTemp -> Pnext) PPolygonTemp = PPolygonTemp -> Pnext; PPolygonTemp -> Pnext = PObj -> U.PPolygon; PObj -> U.PPolygon = PPolygon; } CagdSrfFreeList(Srfs); if (GlblGridFlag) { RealType Dx = BBox.Max[0] - BBox.Min[0], Dy = BBox.Max[1] - BBox.Min[1], Dz = BBox.Max[2] - BBox.Min[2], M = MAX(MAX(Dx, Dy), Dz); int IDx = (int) (GlblGridSize * (Dx / M)), IDy = (int) (GlblGridSize * (Dy / M)), IDz = (int) (GlblGridSize * (Dz / M)); /* Save grid information derived from the surface bbox. */ sprintf(GridStr, "%d %d %d", IDx > 0 ? IDx : 1, IDy > 0 ? IDy : 1, IDz > 0 ? IDz : 1); IritPrsrSetStrAttrib(PObj, "GridSize", GridStr); } } } return SrfObjs; } /***************************************************************************** * Routine to convert a single surface into a polylines with SamplesPerCurve * * samples, NumOfIsolines isolines into a polyline object list. * *****************************************************************************/ static IPPolygonStruct *Surface2Polygons(CagdSrfStruct *Srf, int FourPerFlat, int FineNess) { int i, j; IPVertexStruct *V, *VHead, *VTail = NULL; IPPolygonStruct *P, *PHead = NULL; CagdPolygonStruct *CagdPolygon, *CagdPolygonHead = CagdSrf2Polygons(Srf, FineNess, TRUE, FourPerFlat); for (CagdPolygon = CagdPolygonHead, VHead = NULL; CagdPolygon != NULL; CagdPolygon = CagdPolygon -> Pnext) { /* All polygons are triangles! */ for (i = 0, VHead = NULL; i < 3; i++) { /* Convert to vertices. */ V = IritPrsrNewVertexStruct(); IP_SET_VRTX_NORMAL(V); /* This vertex has normal. */ for (j = 0; j < 3; j++) /* Convert to our format. */ V -> Coord[j] = CagdPolygon -> Polygon[i].Pt[j]; for (j = 0; j < 3; j++) V -> Normal[j] = CagdPolygon -> Normal[i].Vec[j]; if (VHead) { VTail -> Pnext = V; VTail = V; } else VHead = VTail = V; } P = IritPrsrNewPolygonStruct(); P -> PVertex = VHead; P -> Type = IP_POLYGON; P -> Pnext = PHead; PHead = P; } CagdPolygonFreeList(CagdPolygonHead); return PHead; } /***************************************************************************** * Dumps the data for ray shade into stdout. * *****************************************************************************/ static void DumpDataForRayShade(IPObjectStruct *PObjects) { static char *Header1[] = { "/*", " * This file was automatically created from IRIT solid modeller data", " * using Irit2ray - IRIT to RayShade filter.", " *", " * (c) Copyright 1991/92 Gershon Elber, Non commercial use only.", " */", "", NULL }; static char *Header2[] = { "", "eyep 0 0 10", "lookp 0 0 0", "up 0 1 0", "fov 12", "", "light 1 1 1 point 10 30 10", "", NULL }; int i, TotalPolys = 0; char Line[128]; IPObjectStruct *PObj, *PObjHead = NULL; FILE *FGeom, *FRay; sprintf(Line, "%s.ray", OutFileName); if (!GlblDumpOnlyGeometry) { if ((FRay = fopen(Line, "w")) == NULL) { fprintf(stderr, "Failed to open \"%s\".\n", Line); exit(2); } } else FRay = NULL; sprintf(Line, "%s.geom", OutFileName); if ((FGeom = fopen(Line, "w")) == NULL) { fprintf(stderr, "Failed to open \"%s\".\n", Line); exit(2); } if (FRay != NULL) for (i = 0; Header1[i] != NULL; i++) fprintf(FRay, "%s\n", Header1[i]); for (i = 0; Header1[i] != NULL; i++) fprintf(FGeom, "%s\n", Header1[i]); /* Reverse object list since it was loaded in reverse by iritprsr module.*/ while (PObjects != NULL) { PObj = PObjects; PObjects = PObjects -> Pnext; PObj -> Pnext = PObjHead; PObjHead = PObj; } PObjects = PObjHead; while (PObjects) { TotalPolys += DumpOneObject(FRay, FGeom, PObjects); PObjects = PObjects -> Pnext; } if (FRay != NULL) { fprintf(FRay, "#include \"%s\"\n", Line); for (i = 0; Header2[i] != NULL; i++) fprintf(FRay, "%s\n", Header2[i]); fclose(FRay); } fclose(FGeom); fprintf(stderr, "\nTotal number of polygons - %d\n", TotalPolys); } /***************************************************************************** * Routine to dump one object PObject. * *****************************************************************************/ static int DumpOneObject(FILE *FRay, FILE *FGeom, IPObjectStruct *PObject) { static int ObjectSeqNum = 1; int i, j, PolyCount = 0, HasColor = FALSE, HasSrfProp = FALSE; char *p, Name[LINE_LEN], SrfPropString[LINE_LEN_LONG]; RealType RGBColor[3]; IPPolygonStruct *PList = PObject -> U.PPolygon; if (strlen(PObject -> Name) == 0) sprintf(Name, "ObjSeq%d", ObjectSeqNum); else strcpy(Name, PObject -> Name); SrfPropString[0] = 0; for (i = 0; i < PObject -> Attrs.NumStrAttribs; i++) { if (strcmp(PObject -> Attrs.StrAttrName[i], "specpow") == 0 || strcmp(PObject -> Attrs.StrAttrName[i], "reflect") == 0 || strcmp(PObject -> Attrs.StrAttrName[i], "transp") == 0 || strcmp(PObject -> Attrs.StrAttrName[i], "body") == 0 || strcmp(PObject -> Attrs.StrAttrName[i], "index") == 0) { strcat(SrfPropString, PObject -> Attrs.StrAttrName[i]); strcat(SrfPropString, " "); strcat(SrfPropString, PObject -> Attrs.StrAttrData[i]); strcat(SrfPropString, " "); HasSrfProp = TRUE; } } if (GlblGridFlag) { char *GridStr = IritPrsrGetStrAttrib(PObject, "GridSize"); if (GridStr != NULL) fprintf(FGeom, "name %s grid %s\n", Name, GridStr); else fprintf(FGeom, "name %s list\n", Name); } else fprintf(FGeom, "name %s list\n", Name); while (PList) { PolyCount += DumpOnePolygon(FGeom, PList); PList = PList -> Pnext; } fprintf(FGeom, "end\n"); fprintf(stderr, "Processing \"%s\" - %d triangles.\n", Name, PolyCount); if ((p = IritPrsrGetStrAttrib(PObject, "rgb")) != NULL) { # ifdef __MSDOS__ HasColor = sscanf(p, "%f,%f,%f", # else HasColor = sscanf(p, "%lf,%lf,%lf", # endif /* __MSDOS__ */ &RGBColor[0], &RGBColor[1], &RGBColor[2]) == 3; } else if (IP_HAS_OBJ_RGB(PObject)) { HasColor = TRUE; for (i = 0; i < 3; i++) RGBColor[i] = PObject -> RGB[i]; } else if (IP_HAS_OBJ_COLOR(PObject)) { for (i = 0; TransColorTable[i][0] >= 0; i++) { if (TransColorTable[i][0] == PObject -> Color) { HasColor = TRUE; for (j = 0; j < 3; j++) RGBColor[j] = TransColorTable[i][j+1]; break; } } } if (HasColor || HasSrfProp) { if (FRay != NULL) { fprintf(FRay, "surface %sSrfProp\n", Name); if (HasColor) { for (i = 0; i < 3; i++) RGBColor[i] /= 255.0; fprintf(FRay, "\tambient %7.4lf %7.4lf %7.4lf\n", 0.1 * RGBColor[0], 0.1 * RGBColor[1], 0.1 * RGBColor[2]); fprintf(FRay, "\tdiffuse %7.4lf %7.4lf %7.4lf\n", 0.7 * RGBColor[0], 0.7 * RGBColor[1], 0.7 * RGBColor[2]); fprintf(FRay, "\tspecular %7.4lf %7.4lf %7.4lf\n", 0.8, 0.8, 0.8); } if (HasSrfProp) fprintf(FRay, "\t%s\n", SrfPropString); } fprintf(FGeom, "object %sSrfProp %s", Name, Name); } else fprintf(FGeom, "object %s", Name); for (i = 0; i < PObject -> Attrs.NumStrAttribs; i++) { if ((p = IritPrsrGetStrAttrib(PObject, "texture")) != NULL) { if (FRay != NULL) fprintf(FRay, "#define %sTEXTURE %s\n", Name, p); fprintf(FGeom, " texture %sTEXTURE", Name); break; } } fprintf(FGeom, "\n\n"); if (FRay != NULL) fprintf(FRay, "\n\n"); ObjectSeqNum++; return PolyCount; } /***************************************************************************** * Routine to dump one polygon, using global Matrix transform CrntViewMat. * *****************************************************************************/ static int DumpOnePolygon(FILE *FGeom, IPPolygonStruct *PPolygon) { int i, TriCount = 0; RealType *MappedNormal[3], *MappedPoint[3], Normal[3], Vec1[3], Vec2[3]; IPVertexStruct *VFirst, *V1, *V2, *VList = PPolygon -> PVertex; if (VList == NULL) return 0; if (!IsConvexPolygon(PPolygon)) { static int Printed = FALSE; if (!Printed) { fprintf(stderr, "Non convex polygon(s) may be in data (see CONVEX in IRIT).\n"); Printed = TRUE; } } switch (PPolygon -> Type) { case IP_POLYGON: VFirst = VList; V1 = VFirst -> Pnext; V2 = V1 -> Pnext; while (V2 != NULL) { MappedPoint[0] = MapPoint(VFirst -> Coord); MappedPoint[1] = MapPoint(V1 -> Coord); MappedPoint[2] = MapPoint(V2 -> Coord); /* Test for two type of degeneracies. Make sure that no two */ /* points in the triangle are the same and that they are */ /* not colinear. */ if (!PT_EQ(MappedPoint[0], MappedPoint[1]) && !PT_EQ(MappedPoint[0], MappedPoint[2]) && !PT_EQ(MappedPoint[1], MappedPoint[2])) { PT_SUB(Vec1, MappedPoint[0], MappedPoint[1]); PT_SUB(Vec2, MappedPoint[1], MappedPoint[2]); PT_NORMALIZE(Vec1); PT_NORMALIZE(Vec2); CROSS_PROD(Normal, Vec1, Vec2); if (PT_LENGTH(Normal) > SIZE_EPSILON) { PT_NORMALIZE(Normal); MappedNormal[0] = MapVector(VFirst -> Coord, VFirst -> Normal); MappedNormal[1] = MapVector(V1 -> Coord, V1 -> Normal); MappedNormal[2] = MapVector(V2 -> Coord, V2 -> Normal); if (DOT_PROD(Normal, MappedNormal[0]) < -SIZE_EPSILON || DOT_PROD(Normal, MappedNormal[1]) < -SIZE_EPSILON || DOT_PROD(Normal, MappedNormal[2]) < -SIZE_EPSILON) { SWAP(RealType *, MappedPoint[1], MappedPoint[2]); SWAP(RealType *, MappedNormal[1], MappedNormal[2]); PT_SCALE(Normal, -1.0); } /* Make sure all normals are set properly: */ if (DOT_PROD(MappedNormal[0], MappedNormal[0]) < SIZE_EPSILON) PT_COPY(MappedNormal[0], Normal); if (DOT_PROD(MappedNormal[1], MappedNormal[1]) < SIZE_EPSILON) PT_COPY(MappedNormal[1], Normal); if (DOT_PROD(MappedNormal[2], MappedNormal[2]) < SIZE_EPSILON) PT_COPY(MappedNormal[2], Normal); TriCount++; for (i = 0; i < 3; i++) fprintf(FGeom, "%s %10.7lf %10.7lf %10.7lf %9.6lf %9.6lf %9.6lf\n", i == 0 ? " triangle" : "\t ", MappedPoint[i][0], MappedPoint[i][1], MappedPoint[i][2], MappedNormal[i][0], MappedNormal[i][1], MappedNormal[i][2]); } } V1 = V2; V2 = V2 -> Pnext; } break; } return TriCount; } /***************************************************************************** * Routine to test if the given polygon is convex or not. * * Algorithm: The polygon is convex iff the normals generated from cross * * products of two consecutive edges points to the same direction. The same * * direction is tested by a positive dot product. * *****************************************************************************/ static int IsConvexPolygon(IPPolygonStruct *Pl) { RealType Size, V1[3], V2[3], LastNormal[3], Normal[3]; IPVertexStruct *VNext, *VNextNext, *V = Pl -> PVertex; LastNormal[0] = LastNormal[1] = LastNormal[2] = 0.0; do { if ((VNext = V -> Pnext) == NULL) VNext = Pl -> PVertex; if ((VNextNext = VNext -> Pnext) == NULL) VNextNext = Pl -> PVertex; PT_SUB(V1, VNext -> Coord, V -> Coord); if ((Size = PT_LENGTH(V1)) > EPSILON) { Size = 1.0 / Size; PT_SCALE(V1, Size); } PT_SUB(V2, VNextNext -> Coord, VNext -> Coord); if ((Size = PT_LENGTH(V2)) > EPSILON) { Size = 1.0 / Size; PT_SCALE(V2, Size); } CROSS_PROD(Normal, V1, V2); if (V != Pl -> PVertex) { if (PT_LENGTH(Normal) > CONVEX_EPSILON && DOT_PROD(Normal, LastNormal) < -CONVEX_EPSILON) return FALSE; } PT_COPY(LastNormal, Normal); V = VNext; } while (V != Pl -> PVertex && V != NULL); return TRUE; } /***************************************************************************** * Maps the given E3 point using the CrntViewMat. * *****************************************************************************/ static RealType *MapPoint(RealType *Pt) { static int Count = 0; static RealType MappedPts[3][3]; RealType *MappedPt = MappedPts[Count++]; if (Count >= 3) Count = 0; MultVecby4by4(MappedPt, Pt, CrntViewMat); return MappedPt; } /***************************************************************************** * Maps the given E3 vector using the CrntViewMat. * * This routine will return a zero vector if normal is not computable. * *****************************************************************************/ static RealType *MapVector(RealType *Pt, RealType *Vec) { static int Count = 0, WasWarning = 0; static RealType MappedVecs[3][3]; RealType MappedPt[3], Pt2[3], MappedPt2[3], *MappedVec = MappedVecs[Count++]; if (Count >= 3) Count = 0; if (DOT_PROD(Vec, Vec) < SIZE_EPSILON) { MappedVec[0] = MappedVec[1] = MappedVec[2] = 0.0; if (!WasWarning) { WasWarning = 1; fprintf(stderr, "Non computable normals detected. Approximated from geometry.\n"); } } else { MultVecby4by4(MappedPt, Pt, CrntViewMat); PT_ADD(Pt2, Pt, Vec); MultVecby4by4(MappedPt2, Pt2, CrntViewMat); PT_SUB(MappedVec, MappedPt2, MappedPt); PT_NORMALIZE(MappedVec); } return MappedVec; } /***************************************************************************** * Trap Cagd_lib errors right here. * *****************************************************************************/ void CagdFatalError(CagdFatalErrorType ErrID) { char *ErrorMsg = CagdDescribeError(ErrID); fprintf(stderr, "CAGD_LIB: %s", ErrorMsg); exit(-1); } /***************************************************************************** * MyExit routine. Note it might call to CloseGraph without calling * * InitGraph(), or call MouseClose() without MouseInit() etc. and it is the * * responsibility of the individual modules to do nothing in these cases. * *****************************************************************************/ static void MyExit(int ExitCode) { #ifdef __MSDOS__ fprintf(stderr, "\nIrit2Ray: Core left %ldk.\n", coreleft() / 1024); #endif /* __MSDOS__ */ exit(ExitCode); }