Metropoli BBS
VIEWER: irit2ray.c MODE: TEXT (ASCII)
/*****************************************************************************
* Filter to convert IRIT data files to ray shade format.		     *
*									     *
* Written by:  Gershon Elber				Ver 1.0, Sep 1991    *
*****************************************************************************/

#ifdef __MSDOS__
#include <dos.h>
#include <alloc.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#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);
}
[ RETURN TO DIRECTORY ]