/***************************************************************************** * Program to draw 3D object as solid objects in GIF image file format. * * * * Options: * * 1. -a ambient : Ratio of the ambient color. * * 2. -c n : Number of bit ber pixel (2^n is number of colors possible). * * 3. -l x y z : Light source direction vector. * * 4. -2 : Force 2 light sources at opposite direction of light vectors. * * 5. -m : More flag, to print more imformation on input/errors. * * 6. -s x y : Specify new dimensions to generate the resulting image. * * 7. -S SubSample : specifies dimension of pixel sub sampling (1..4). * * 8. -g : Use gouraud shading. * * 9. -b : Delete Back facing polygons. * * 10. -M Mask : Create boolean Mask (coverage) image as well. * * 11. -z : Print current version, and some helpfull data. * * 12. -f FineNess : log based 2 of the surface to polygons subdiv, fineness. * * * * Note some of those options may be permanently set to a different default * * using the configuration file "Poly3D-R.cfg" * * * * Usage: poly3d-r [-a Ambient] [-c N] [-l X Y Z] [-2] [-m] [-s Xsize Ysize] * * [-S SubSample] [-g] [-b] [-M Mask] [-z] [-f FineNess] DFiles * * * * Written by: Gershon Elber Ver 3.0, Aug 1990 * *****************************************************************************/ #ifdef __MSDOS__ #include #include #include #include #include #include #endif /* __MSDOS__ */ #include #include #include #include "program.h" #include "getarg.h" #include "genmat.h" #include "iritprsr.h" #include "config.h" #ifdef __TURBOC__ /* Malloc debug routine - only on TC++ 1.0 and above. */ #define __DEBUG_MALLOC__ #endif /* __TURBOC__ */ #ifdef __MSDOS__ /* This huge stack is mainly from second phase - the segment intersections */ /* which may cause recursive calls - a lot... */ extern unsigned int _stklen = 32766; #endif /* __MSDOS__ */ #ifdef NO_CONCAT_STR static char *VersionStr = "Poly3D-R Version 3.0, Gershon Elber,\n\ (C) Copyright 1989/90/91 Gershon Elber, Non commercial use only."; #else static char *VersionStr = "Poly3D-R " VERSION ", Gershon Elber, " __DATE__ ", " __TIME__ "\n" "(C) Copyright 1989/90/91 Gershon Elber, Non commercial use only."; #endif /* NO_CONCAT_STR */ static char *CtrlStr = #ifdef __MSDOS__ "poly3d-r a%-Ambient!f c%-N!d l%-X|Y|Z!f!f!f 2%- m%- s%-Xsize|Ysize!d!d S%-SubSample!d g%- b%- M%-Mask!s z%- f%-FineNess!d DFiles!*s"; #else "poly3d-r a%-Ambient!F c%-N!d l%-X|Y|Z!F!F!F 2%- m%- s%-Xsize|Ysize!d!d S%-SubSample!d g%- b%- M%-Mask!s z%- f%-FineNess!d DFiles!*s"; #endif /* __MSDOS__ */ static long SaveTotalTime; static GifColorType MaskColorMap[2] = { /* Boolean mask GIF file color map. */ { 0, 0, 0 }, { 255, 255, 255 } }; /* Fineness surface subdivision control. */ static int GlblFineNess = DEFAULT_FINENESS; int GlblNumOfPolys = 0; /* Total number of polygons scan converted. */ int GlblNumOfVerts = 0; /* Total number of vertices. */ MatrixType GlblViewMat; /* Current view of object. */ /* Amount scene was scaled up from normalized [-1..1] size on both X & Y: */ RealType GlblScaleUpFctr = 0.0; /* The following are setable variables (via configuration file poly3d-h.cfg).*/ int GlblMore = FALSE; ShadeInfoStruct GlblShadeInfo = { 1, /* Sub samples per pixel. */ DEFAULT_BITS_PER_PIXEL, 0, DEFAULT_COLOR, DEFAULT_BACK_GROUND_COLOR, FALSE, /* No two light sources. */ DEFAULT_SCREEN_XSIZE, DEFAULT_SCREEN_YSIZE, FALSE, /* No Gouraud shading. */ FALSE, /* No back facing deletion. */ NULL, NULL, /* No color map yet. */ DEFAULT_LIGHT_SOURCE, DEFAULT_AMBIENT, DEFAULT_NORMAL_AVG_DEGREE }; static ConfigStruct SetUp[] = { { "Ambient", (VoidPtr) &GlblShadeInfo.Ambient, SU_REAL_TYPE }, { "LightSrcX", (VoidPtr) &GlblShadeInfo.LightSource[0],SU_REAL_TYPE }, { "LightSrcY", (VoidPtr) &GlblShadeInfo.LightSource[1],SU_REAL_TYPE }, { "LightSrcZ", (VoidPtr) &GlblShadeInfo.LightSource[2],SU_REAL_TYPE }, { "AvgDegree", (VoidPtr) &GlblShadeInfo.NrmlAvgDegree, SU_REAL_TYPE }, { "TwoSources", (VoidPtr) &GlblShadeInfo.TwoSources, SU_BOOLEAN_TYPE }, { "Gouraud", (VoidPtr) &GlblShadeInfo.Gouraud, SU_BOOLEAN_TYPE }, { "backFacing", (VoidPtr) &GlblShadeInfo.BackFacing, SU_BOOLEAN_TYPE }, { "SubSample", (VoidPtr) &GlblShadeInfo.SubSamplePixel,SU_INTEGER_TYPE }, { "BitsPerPixel", (VoidPtr) &GlblShadeInfo.BitsPerPixel, SU_INTEGER_TYPE }, { "Color", (VoidPtr) &GlblShadeInfo.DefaultColor, SU_INTEGER_TYPE }, { "BackGroundColor", (VoidPtr) &GlblShadeInfo.BackGroundColor,SU_INTEGER_TYPE }, { "Xsize", (VoidPtr) &GlblShadeInfo.ScrnXSize, SU_INTEGER_TYPE }, { "Ysize", (VoidPtr) &GlblShadeInfo.ScrnYSize, SU_INTEGER_TYPE }, { "FineNess", (VoidPtr) &GlblFineNess, SU_INTEGER_TYPE }, { "More", (VoidPtr) &GlblMore, SU_BOOLEAN_TYPE } }; #define NUM_SET_UP (sizeof(SetUp) / sizeof(ConfigStruct)) /* All polygons to be scan convert will be inserted into this hash table */ /* during the preprocessing (PrepareXXXX functions). */ IPPolygonStruct **PolyHashTable; static IPObjectStruct *MainGetDataFiles(char **DataFileNames, int NumOfDataFiles); static IPPolygonStruct *Surface2Polygons(CagdSrfStruct *Srf); /***************************************************************************** * Main routine - Read Parameter line and do what you need... * *****************************************************************************/ void #ifdef __MSDOS__ cdecl /* So we can use -rp in Borland 3.0 (parameters in registers.). */ #endif /* __MSDOS__ */ main(int argc, char **argv) { int AmbientFlag = FALSE, ColorFlag = FALSE, LightSrcFlag = FALSE, GifMaskFlag = FALSE, VerFlag = FALSE, NumFiles = 0, ImageSizeFlag = FALSE, SubSampleFlag = FALSE, FineNessFlag = FALSE, Error; char *GifMaskName, **FileNames = NULL; RealType Size, Scale; MatrixType Mat; IPObjectStruct *PObjects; GifFileType *GifFile, *GifMask = NULL; SaveTotalTime = time(NULL); /* Save starting time. */ #ifdef __MSDOS__ ctrlbrk((int cdecl (*)()) MyExit); /* Kill this program if ^C... */ #endif /* __MSDOS__ */ Config("poly3d-r", SetUp, NUM_SET_UP); /* Read config. file if exists. */ if ((Error = GAGetArgs (argc, argv, CtrlStr, &AmbientFlag, &GlblShadeInfo.Ambient, &ColorFlag, &GlblShadeInfo.BitsPerPixel, &LightSrcFlag, &GlblShadeInfo.LightSource[0], &GlblShadeInfo.LightSource[1], &GlblShadeInfo.LightSource[2], &GlblShadeInfo.TwoSources, &GlblMore, &ImageSizeFlag, &GlblShadeInfo.ScrnXSize, &GlblShadeInfo.ScrnYSize, &SubSampleFlag, &GlblShadeInfo.SubSamplePixel, &GlblShadeInfo.Gouraud, &GlblShadeInfo.BackFacing, &GifMaskFlag, &GifMaskName, &VerFlag, &FineNessFlag, &GlblFineNess, &NumFiles, &FileNames)) != 0) { GAPrintErrMsg(Error); GAPrintHowTo(CtrlStr); MyExit(1); } if (GlblShadeInfo.Ambient < 0.0 || GlblShadeInfo.Ambient > 1.0) { fprintf(stderr, "Ambient light specified not in [0.0..1.0] range, %lf selected instead.\n", DEFAULT_AMBIENT); GlblShadeInfo.Ambient = DEFAULT_AMBIENT; } if (GlblShadeInfo.BitsPerPixel < 1 || GlblShadeInfo.BitsPerPixel > 8) { fprintf(stderr, "PitsPerPixel not in [1..8] range, %d selected instead.\n", DEFAULT_BITS_PER_PIXEL); GlblShadeInfo.BitsPerPixel = DEFAULT_BITS_PER_PIXEL; } Size = sqrt(SQR(GlblShadeInfo.LightSource[0]) + SQR(GlblShadeInfo.LightSource[1]) + SQR(GlblShadeInfo.LightSource[2])); if (ABS(Size) < EPSILON) { fprintf(stderr, "Light source vector is zero, Z axis selected instead.\n"); GlblShadeInfo.LightSource[0] = GlblShadeInfo.LightSource[1] = 0.0; GlblShadeInfo.LightSource[2] = 1.0; } else { GlblShadeInfo.LightSource[0] /= Size; GlblShadeInfo.LightSource[1] /= Size; GlblShadeInfo.LightSource[2] /= Size; } if (VerFlag) { fprintf(stderr, "\n%s\n\n", VersionStr); GAPrintHowTo(CtrlStr); ConfigPrint(SetUp, NUM_SET_UP); MyExit(0); } if (!NumFiles) { fprintf(stderr, "No data file names were given, exit.\n"); GAPrintHowTo(CtrlStr); MyExit(1); } if (SubSampleFlag) { if (GlblShadeInfo.SubSamplePixel < 1 || GlblShadeInfo.SubSamplePixel > 4) { fprintf(stderr, "Sub sampling can be 1 to 4 only (1x1 to 4x4).\n"); GAPrintHowTo(CtrlStr); MyExit(1); } } /* Get the data files: */ IritPrsrPolyListCirc = FALSE; PObjects = MainGetDataFiles(FileNames, NumFiles); /* Compute the viewing matrices and related data: */ if (IritPrsrWasPrspMat) MultTwo4by4(GlblViewMat, IritPrsrViewMat, IritPrsrPrspMat); else GEN_COPY(GlblViewMat, IritPrsrViewMat, sizeof(MatrixType)); /* Now its time to scale the normalized image (+/-1 on both X & Y) to */ /* size specified by the image dimensions. We scale up to the SMALLER */ /* dimension, and put the center at the image center. */ /* Also, as the GIF image starts at the top, we must flip the image */ /* along Y axis. */ GlblScaleUpFctr = Scale = MIN(GlblShadeInfo.ScrnXSize * GlblShadeInfo.SubSamplePixel, GlblShadeInfo.ScrnYSize * GlblShadeInfo.SubSamplePixel) / 2.0; GenMatScale(Scale, -Scale, Scale, Mat); MultTwo4by4(GlblViewMat, GlblViewMat, Mat); GenMatTrans(GlblShadeInfo.ScrnXSize * GlblShadeInfo.SubSamplePixel / 2.0, GlblShadeInfo.ScrnYSize * GlblShadeInfo.SubSamplePixel / 2.0, 0.0, Mat); MultTwo4by4(GlblViewMat, GlblViewMat, Mat); /* Prepare data structures of objects themselves: */ PrepareViewData(PObjects); /* Into shadingInfo global structure: */ PrepareColorTable(PObjects); EvalVrtxColors(PObjects); #ifndef DEBUG_NO_GIF /* Open stdout for the GIF image file: */ if ((GifFile = EGifOpenFileHandle(1)) == NULL || EGifPutScreenDesc(GifFile, GlblShadeInfo.ScrnXSize, GlblShadeInfo.ScrnYSize, GlblShadeInfo.BitsPerPixel, 0, GlblShadeInfo.BitsPerPixel, GlblShadeInfo.PColorMap) == GIF_ERROR || EGifPutImageDesc(GifFile, 0, 0, GlblShadeInfo.ScrnXSize, GlblShadeInfo.ScrnYSize, FALSE, GlblShadeInfo.BitsPerPixel, NULL) == GIF_ERROR) QuitGifError(); /* Open special mask file if required: */ if (GifMaskFlag && ((GifMask = EGifOpenFileName(GifMaskName, FALSE)) == NULL || EGifPutScreenDesc(GifMask, GlblShadeInfo.ScrnXSize, GlblShadeInfo.ScrnYSize, 1, 0, 1, MaskColorMap) == GIF_ERROR || EGifPutImageDesc(GifMask, 0, 0, GlblShadeInfo.ScrnXSize, GlblShadeInfo.ScrnYSize, FALSE, 1, NULL) == GIF_ERROR)) QuitGifError(); #endif /* DEBUG_NO_GIF */ ScanConvertData(GifFile, GifMask); /* Do the real interesting stuff... */ #ifndef DEBUG_NO_GIF EGifCloseFile(GifFile); if (GifMask) EGifCloseFile(GifMask); #endif /* DEBUG_NO_GIF */ 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; char *ErrorMsg; FILE *f; long SaveTime = time(NULL); IPObjectStruct *PObj, *PObjTail, *PObjHead = NULL; fprintf(stderr, "Reading data file(s).\n"); for (i = 0; i < NumOfDataFiles; i++) { if (GlblMore) fprintf(stderr, "Reading %s.\n", *DataFileNames); #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; } if (GlblMore && IritPrsrParseError(&ErrorMsg)) fprintf(stderr, "File %s, %s\n", *DataFileNames, ErrorMsg); fclose(f); /* Close the file. */ DataFileNames++; /* Skip to next file name. */ } if (PObjHead == NULL) { fprintf(stderr, "No data found.\n"); MyExit(1); } fprintf(stderr, "Done reading, %ld seconds.", time(NULL) - SaveTime); 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) { 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; if (GlblMore) 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(PObj); PObj = PObjNext; } CrvObjs = NULL; } if (SrfObjs) { for (PObj = SrfObjs; PObj != NULL; PObj = PObj -> Pnext) { Srfs = PObj -> U.PSrfs; PObj -> U.PPolygon = NULL; for (Srf = Srfs; Srf != NULL; Srf = Srf -> Pnext) { PPolygon = PPolygonTemp = Surface2Polygons(Srf); while (PPolygonTemp -> Pnext) PPolygonTemp = PPolygonTemp -> Pnext; PPolygonTemp -> Pnext = PObj -> U.PPolygon; PObj -> U.PPolygon = PPolygon; } CagdSrfFreeList(Srfs); } } return SrfObjs; } /***************************************************************************** * Routine to convert a single surface into a polygons with GlblFineNess * * samples as the subdivision fineness measure. * *****************************************************************************/ static IPPolygonStruct *Surface2Polygons(CagdSrfStruct *Srf) { int i, j; IPVertexStruct *V, *VHead; IPPolygonStruct *P, *PHead = NULL; CagdPolygonStruct *CagdPolygon, *CagdPolygonHead = NULL; CagdPolygonHead = CagdSrf2Polygons(Srf, 1 << GlblFineNess, TRUE, TRUE); for (CagdPolygon = CagdPolygonHead; CagdPolygon != NULL; CagdPolygon = CagdPolygon -> Pnext) { /* All polygons are triangles! */ for (i = 0, VHead = NULL; i < 3; i++) { /* Convert to vertices. */ if (VHead == NULL) VHead = V = IritPrsrNewVertexStruct(); else { V -> Pnext = IritPrsrNewVertexStruct(); V = V -> Pnext; } IP_SET_VRTX_NORMAL(V); /* 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]; } P = IritPrsrNewPolygonStruct(); P -> PVertex = VHead; P -> Type = IP_POLYGON; P -> Pnext = PHead; PHead = P; } CagdPolygonFreeList(CagdPolygonHead); return PHead; } #ifdef __DEBUG_MALLOC__ /***************************************************************************** * My Routine to allocate dynamic memory. All program requests must call this * * routine (no direct call to malloc). Dies if no memory. * *****************************************************************************/ static void AllocError(const char *Msg, VoidPtr *p) { fprintf(stderr, "%s, Ptr = %p\n", Msg, p); MyExit(3); } #endif /* __DEBUG_MALLOC__ */ /***************************************************************************** * My Routine to allocate dynamic memory. All program requests must call this * * routine (no direct call to malloc). Dies if no memory. * *****************************************************************************/ VoidPtr MyMalloc(unsigned size) { VoidPtr p; if ((p = malloc(size)) != NULL) return p; fprintf(stderr, "Not enough memory, exit.\n"); MyExit(2); return NULL; /* Make warnings silent. */ } /***************************************************************************** * My Routine to free dynamic memory. All program requests must call this * * routine (no direct call to free). * *****************************************************************************/ void MyFree(VoidPtr p) { #ifdef __DEBUG_MALLOC__ switch (heapchecknode(p)) { case _HEAPCORRUPT: AllocError("Heap is corrupted", p); break; case _BADNODE: AllocError("Attempt to free a bogus pointer", p); break; case _FREEENTRY: AllocError("Attempt to free an already freed pointer", p); break; case _USEDENTRY: break; default: AllocError("Allocation error", p); break; } #endif /* __DEBUG_MALLOC__ */ free(p); } /***************************************************************************** * 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(), or call * * RestoreCtrlBrk() without SetUpCtrlBrk() and it is the responsibility * * of the individual modules to do nothing in these cases. * *****************************************************************************/ void MyExit(int ExitCode) { #ifdef __MSDOS__ fprintf(stderr, "\nPoly3D-R: Total RealTime %ld seconds, Core left %ldk.\n", time(NULL) - SaveTotalTime, coreleft() / 1024); #else fprintf(stderr, "\nPoly3D-R: Total RealTime %ld seconds.\n", time(NULL) - SaveTotalTime); #endif /* __MSDOS__ */ exit(ExitCode); } /****************************************************************************** * Close output file (if open), and exit. * ******************************************************************************/ void QuitGifError(void) { PrintGifError(); MyExit('G'); }