/*****************************************************************************
* Filter to convert IRIT data files to ray shade format.		     *
*									     *
* Written by:  Gershon Elber				Ver 1.0, Sep 1991    *
*****************************************************************************/

#include <stdio.h>
#include <math.h>
#include <string.h>
#include "irit_sm.h"
#include "iritprsr.h"
#include "allocate.h"
#include "attribut.h"
#include "iritgrap.h"
#include "getarg.h"
#include "genmat.h"
#include "ffcnvrt.h"
#include "ip_cnvrt.h"

#define DIST_EPSILON	2e-4
#define SIZE_EPSILON	1e-5

#define DEFAULT_POLYLINE_WIDTH  0.05

#define CONE_SIZE	5e-4

#define MIN_RATIO	0.01

#ifdef NO_CONCAT_STR
static char *VersionStr =
	"Irit2Ray		Version 4.0,	Gershon Elber,\n\
	 (C) Copyright 1989/90/91/92/93 Gershon Elber, Non commercial use only.";
#else
static char *VersionStr = "Irit2Ray	" VERSION ",	Gershon Elber,	"
	__DATE__ ",   " __TIME__ "\n" COPYRIGHT ", Non commercial use only.";
#endif /* NO_CONCAT_STR */

static char
#ifdef DOUBLE
    *CtrlStr = "irit2ray l%- 4%- G%-GridSize!d f%-FineNess!d o%-OutName!s g%- p%-Zmin|Zmax!F!F P%- M%- T%- I%-#UIso[:#VIso]!s S%-#SampPerCrv!d z%- DFiles!*s";
#else
    *CtrlStr = "irit2ray l%- 4%- G%-GridSize!d f%-FineNess!d o%-OutName!s g%- p%-Zmin|Zmax!f!f P%- M%- T%- I%-#UIso[:#VIso]!s S%-#SampPerCrv!d z%- DFiles!*s";
#endif /* DOUBLE */

static char
    *GlblStrNumOfIsolines = NULL,
    *GlblOutFileName = "irit2ray";

static int
    GlblTalkative = FALSE,
    GlblGridSize = 5,
    GlblGridFlag = FALSE,
    GlblFineNess = 5,
    GlblDrawMesh = 5,
    GlblDumpOnlyGeometry = FALSE,
    GlblDumpPolylines = FALSE,
    GlblDumpObjsAsPolylines = FALSE,
    GlblFourPerFlat = FALSE,
    GlblNumOfIsolines[2] = { IG_DEFAULT_NUM_OF_ISOLINES,
			     IG_DEFAULT_NUM_OF_ISOLINES },
    GlblSamplesPerCurve = IG_DEFAULT_SAMPLES_PER_CURVE;

static RealType
    GlblZBBox[2] = { INFINITY, -INFINITY },
    GlblPolylineDepthCue[3];

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 void DumpDataForRayShade(IPObjectStruct *PObjects);
static int DumpOneObject(FILE *FRay, FILE *FGeom, IPObjectStruct *PObject);
static int DumpOnePoly(FILE *FGeom, IPPolygonStruct *PPoly,
		       IPObjectStruct *PObj, char *Name, RealType Width);
static int DumpOnePolygon(FILE *f, IPPolygonStruct *PPolygon, char *Name);
static int DumpOnePolyline(FILE *f, IPPolygonStruct *PPolyline, char *Name,
					    RealType Width, int IsPolyline);
static void DumpCone(RealType R1, RealType X1, RealType Y1, RealType Z1,
		     RealType R2, RealType X2, RealType Y2, RealType Z2,
		     FILE *FGeom, char *Name);
static RealType *MapPoint(RealType *Pt);
static RealType *MapVector(RealType *Pt, RealType *Vec);
static void Irit2RayExit(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,
	SamplesPerCurveFlag = FALSE,
	NumOfIsolinesFlag = FALSE,
	NumFiles = 0;
    char Line[LINE_LEN_LONG], *p,
	**FileNames = NULL;
    IPObjectStruct *PObjects;

    if ((Error = GAGetArgs(argc, argv, CtrlStr, &LinearOnePolyFlag,
			   &GlblFourPerFlat, &GlblGridFlag, &GlblGridSize,
			   &FineNessFlag, &GlblFineNess,  &OutFileFlag,
			   &GlblOutFileName, &GlblDumpOnlyGeometry,
			   &GlblDumpPolylines, &GlblPolylineDepthCue[0],
			   &GlblPolylineDepthCue[1], &GlblDumpObjsAsPolylines,
			   &GlblDrawMesh, &GlblTalkative,
			   &NumOfIsolinesFlag, &GlblStrNumOfIsolines,
			   &SamplesPerCurveFlag, &GlblSamplesPerCurve,
			   &VerFlag, &NumFiles, &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	Irit2RayExit(1);
    }

    if (VerFlag) {
	fprintf(stderr, "\n%s\n\n", VersionStr);
	GAPrintHowTo(CtrlStr);
	Irit2RayExit(0);
    }

    if (GlblDumpPolylines) {
	if (APX_EQ(GlblPolylineDepthCue[0], GlblPolylineDepthCue[1])) {
	    fprintf(stderr, "Cannot compute depth cue if Zmin == Zmax\n");
	    Irit2RayExit(1);
	}
	else {
	    /* Precompute DZ. */
	    GlblPolylineDepthCue[2] =
		GlblPolylineDepthCue[1] - GlblPolylineDepthCue[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);

    if (NumOfIsolinesFlag && GlblStrNumOfIsolines != NULL) {
	if (sscanf(GlblStrNumOfIsolines, "%d:%d",
		   &GlblNumOfIsolines[0], &GlblNumOfIsolines[1]) != 2) {
	    if (sscanf(GlblStrNumOfIsolines, "%d",
		       &GlblNumOfIsolines[0]) != 1) {
		fprintf(stderr,
			"Number(s) of isolines (-I) cannot be parsed.\n");
		GAPrintHowTo(CtrlStr);
		Irit2RayExit(1);
	    }
	    else {
		GlblNumOfIsolines[1] = GlblNumOfIsolines[0];
	    }
	}
    }

    fprintf(stderr, "%s triangles per flat will be created.\n",
	    GlblFourPerFlat ? "Four" : "Two");

    if (!NumFiles) {
	fprintf(stderr, "No data file names were given, exit.\n");
	GAPrintHowTo(CtrlStr);
	Irit2RayExit(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;
	GlblOutFileName = IritStrdup(Line);
    }

    /* Get the data files: */
    if ((PObjects = IritPrsrGetDataFiles(FileNames, NumFiles, TRUE, FALSE)) ==
									NULL)
	Irit2RayExit(1);

    if (IritPrsrWasPrspMat)
	MatMultTwo4by4(CrntViewMat, IritPrsrViewMat, IritPrsrPrspMat);
    else
	GEN_COPY(CrntViewMat, IritPrsrViewMat, sizeof(MatrixType));

    DumpDataForRayShade(PObjects);

    if (GlblDumpPolylines && GlblZBBox[0] < GlblZBBox[1])
	fprintf(stderr, "Z depth cueing of polylines spans [%lf : %lf]\n",
		GlblZBBox[0], GlblZBBox[1]);

    Irit2RayExit(0);
}

/*****************************************************************************
* Routine to convert all surfaces/curves into polygons/lines as follows:     *
* Curves are converted to polylines and surfaces are converted to polygons,  *
* or to polylines if GlblDumpObjsAsPolylines is set.			     *
*****************************************************************************/
IPObjectStruct *IritPrsrProcessFreeForm(IPObjectStruct *CrvObjs,
					IPObjectStruct *SrfObjs)
{
    int LocalFourPerFlat;
    float RelativeFineNess;
    CagdCrvStruct *Crv, *Crvs;
    CagdSrfStruct *Srf, *Srfs;
    IPObjectStruct *PObj;
    IPPolygonStruct *PPolygon, *PPolygonTemp;

    if (CrvObjs == NULL && SrfObjs == NULL)
	return NULL;

    if (CrvObjs) {
	for (PObj = CrvObjs; PObj != NULL; PObj = PObj -> Pnext) {
	    if (GlblTalkative)
		fprintf(stderr, "Processing curve object \"%s\"\n",
			PObj -> Name);

	    Crvs = PObj -> U.Crvs;

	    /* Replace curves with polylines. */
	    PObj -> U.Pl = NULL;
	    PObj -> ObjType = IP_OBJ_POLY;
	    IP_SET_POLYLINE_OBJ(PObj);
	    for (Crv = Crvs; Crv != NULL; Crv = Crv -> Pnext) {
		PPolygon = PPolygonTemp =
			Curve2Polylines(Crv, TRUE, GlblDrawMesh,
							  GlblSamplesPerCurve);
		while (PPolygonTemp -> Pnext)
		    PPolygonTemp = PPolygonTemp -> Pnext;
		PPolygonTemp -> Pnext = PObj -> U.Pl;
		PObj -> U.Pl = PPolygon;
	    }
	}
    }

    if (GlblDumpObjsAsPolylines) {
	if (SrfObjs) {
	    for (PObj = SrfObjs; PObj != NULL; PObj = PObj -> Pnext) {
		if (GlblTalkative)
		    fprintf(stderr, "Processing surface object \"%s\"\n",
			    PObj -> Name);

		Srfs = PObj -> U.Srfs;

		/* Replace surfaces with polylines. */
		PObj -> U.Pl = NULL;
		PObj -> ObjType = IP_OBJ_POLY;
		IP_SET_POLYLINE_OBJ(PObj);
		for (Srf = Srfs; Srf != NULL; Srf = Srf -> Pnext) {
		    PPolygon = PPolygonTemp =
			Surface2Polylines(Srf, TRUE, GlblDrawMesh,
					  GlblNumOfIsolines,
					  GlblSamplesPerCurve);
		    while (PPolygonTemp -> Pnext)
			PPolygonTemp = PPolygonTemp -> Pnext;
		    PPolygonTemp -> Pnext = PObj -> U.Pl;
		    PObj -> U.Pl = PPolygon;
		}
	    }
	}
    }
    else {
	if (SrfObjs) {
	    for (PObj = SrfObjs; PObj != NULL; PObj = PObj -> Pnext) {
		CagdBBoxStruct BBox, TempBBox;
		char GridStr[LINE_LEN];

		if (GlblTalkative)
		    fprintf(stderr, "Processing surface object \"%s\"\n",
			    PObj -> Name);

		Srfs = PObj -> U.Srfs;
		PObj -> U.Pl = NULL;
		PObj -> ObjType = IP_OBJ_POLY;
		IP_SET_POLYGON_OBJ(PObj);

		LocalFourPerFlat = GlblFourPerFlat;
		
		if (AttrGetObjectStrAttrib(PObj, "twoperflat"))
		    LocalFourPerFlat = FALSE;
		if (AttrGetObjectStrAttrib(PObj, "fourperflat"))
		    LocalFourPerFlat = TRUE;
		
		if ((RelativeFineNess = AttrGetObjectRealAttrib(PObj,
					"resolution")) > IP_ATTR_BAD_REAL)
		    RelativeFineNess = 1.0;
		
		for (Srf = Srfs; Srf != NULL; Srf = Srf -> Pnext) {
		    if (GlblGridFlag) {
			/* Generate bounding box to 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 =
			IritSurface2Polygons(Srf, LocalFourPerFlat,
			      (int) (RelativeFineNess * GlblFineNess), FALSE);
		    while (PPolygonTemp -> Pnext)
			PPolygonTemp = PPolygonTemp -> Pnext;
		    PPolygonTemp -> Pnext = PObj -> U.Pl;
		    PObj -> U.Pl = 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);
		    AttrSetObjectStrAttrib(PObj, "GridSize", GridStr);
		}
	    }
	}
    }
    
    if (SrfObjs == NULL)
	return CrvObjs;
    else if (CrvObjs == NULL)
	    return SrfObjs;
    else {
	for (PObj = SrfObjs; PObj -> Pnext != NULL; PObj = PObj -> Pnext);
	PObj -> Pnext = CrvObjs;
	return SrfObjs;
    }
}

/*****************************************************************************
* Dumps the data for ray shade.						     *
*****************************************************************************/
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", GlblOutFileName);
    if (!GlblDumpOnlyGeometry) {
	if ((FRay = fopen(Line, "w")) == NULL) {
	    fprintf(stderr, "Failed to open \"%s\".\n", Line);
	    Irit2RayExit(2);
	}
    }
    else
	FRay = NULL;

    sprintf(Line, "%s.geom", GlblOutFileName);
    if ((FGeom = fopen(Line, "w")) == NULL) {
#	if defined(OS2GCC) || defined(__WINNT__)
	    sprintf(Line, "%s.geo", GlblOutFileName);
	    if ((FGeom = fopen(Line, "w")) == NULL)
#	endif /* OS2GCC || __WINNT__ */
	    {
		fprintf(stderr, "Failed to open \"%s\".\n", Line);
		Irit2RayExit(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;
    static char
        *StrAttribs[] = {
	"specpow",
	"reflect",
	"transp",
	"body",
	"index",
	NULL
    };
    int i, j, Color, RGBIColor[3],
        PolyCount = 0,
	HasColor = FALSE,
	HasSrfProp = FALSE;
    char *p, Name[LINE_LEN], SrfPropString[LINE_LEN_LONG];
    RealType RGBColor[3],
	Width = DEFAULT_POLYLINE_WIDTH;
    IPPolygonStruct *PList;

    if (!IP_IS_POLY_OBJ(PObject) || !IP_IS_POLYGON_OBJ(PObject))
	return 0;

    PList = PObject -> U.Pl;

    if (strlen(PObject -> Name) == 0)
	sprintf(Name, "ObjSeq%d", ObjectSeqNum);
    else
	strcpy(Name, PObject -> Name);

    SrfPropString[0] = 0;
    for (i = 0; StrAttribs[i] != NULL; i++) {
	if ((p = AttrGetObjectStrAttrib(PObject, StrAttribs[i])) != NULL) {
	    strcat(SrfPropString, StrAttribs[i]);
	    strcat(SrfPropString, " ");
	    strcat(SrfPropString, p);
	    strcat(SrfPropString, " ");
	    HasSrfProp = TRUE;
	}
    }

    if (GlblDumpPolylines) {
	if ((Width = AttrGetObjectRealAttrib(PObject, "Width")) >
							    IP_ATTR_BAD_REAL)
	    Width = DEFAULT_POLYLINE_WIDTH;
    }

    if (GlblGridFlag) {
	char
	    *GridStr = AttrGetObjectStrAttrib(PObject, "GridSize");

	if (GridStr != NULL)
	    fprintf(FGeom, "name %s%s grid %s\n",
		    GlblDumpObjsAsPolylines ? "Pl" : "", Name, GridStr);
	else
	    fprintf(FGeom, "name %s%s grid %d %d %d\n",
		    GlblDumpObjsAsPolylines ? "Pl" : "", Name,
		    GlblGridSize, GlblGridSize, GlblGridSize);
    }
    else
	fprintf(FGeom, "name %s%s list\n",
		GlblDumpObjsAsPolylines ? "Pl" : "", Name);

    while (PList) {
	PolyCount += DumpOnePoly(FGeom, PList, PObject, Name, Width);
	PList =	PList -> Pnext;
    }
    fprintf(FGeom, "end\n");

    if (GlblTalkative)
	fprintf(stderr, "Converting \"%s\" - %d triangles.\n",
		Name, PolyCount);

    if (AttrGetObjectRGBColor(PObject,
			      &RGBIColor[0], &RGBIColor[1], &RGBIColor[2])) {
	HasColor = TRUE;
	for (i = 0; i < 3; i++)
	    RGBColor[i] = RGBIColor[i];
    }
    else if ((Color = AttrGetObjectColor(PObject)) != IP_ATTR_NO_COLOR) {
	for (i = 0; TransColorTable[i][0] >= 0; i++) {
	    if (TransColorTable[i][0] == 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 %s%sSrfProp\n",
		    GlblDumpObjsAsPolylines ? "Pl" : "", 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 %s%sSrfProp %s%s",
		GlblDumpObjsAsPolylines ? "Pl" : "", Name,
		GlblDumpObjsAsPolylines ? "Pl" : "", Name);
    }
    else
	fprintf(FGeom, "object %s%s",
		GlblDumpObjsAsPolylines ? "Pl" : "", Name);

    if ((p = AttrGetObjectStrAttrib(PObject, "texture")) != NULL) {
	if (FRay != NULL)
	    fprintf(FRay, "#define %s%sTEXTURE %s\n",
		    GlblDumpObjsAsPolylines ? "Pl" : "", Name, p);
	fprintf(FGeom, " texture %s%sTEXTURE",
		GlblDumpObjsAsPolylines ? "Pl" : "", Name);
    }
    fprintf(FGeom, "\n\n");
    if (FRay != NULL)
	fprintf(FRay, "\n\n");

    ObjectSeqNum++;

    return PolyCount;
}

/*****************************************************************************
* Routine to dump one poly, using global Matrix transform CrntViewMat.       *
*****************************************************************************/
static int DumpOnePoly(FILE *FGeom, IPPolygonStruct *PPoly,
		       IPObjectStruct *PObj, char *Name, RealType Width)
{
    if (IP_IS_POLYGON_OBJ(PObj)) {
	if (GlblDumpObjsAsPolylines)
	    return DumpOnePolyline(FGeom, PPoly, Name, Width, FALSE);
	else
	    return DumpOnePolygon(FGeom, PPoly, Name);
    }
    else if (IP_IS_POLYLINE_OBJ(PObj)) {
	return DumpOnePolyline(FGeom, PPoly, Name, Width, TRUE);
    }
    else
	return 0;
}

/*****************************************************************************
* Routine to dump one polygon, using global Matrix transform CrntViewMat.    *
*****************************************************************************/
static int DumpOnePolygon(FILE *FGeom, IPPolygonStruct *PPolygon, char *Name)
{
    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 (!IritPrsrIsConvexPolygon(PPolygon)) {
	static int
	    Printed = FALSE;

	if (!Printed) {
	    fprintf(stderr,
		    "\nWARNING: Non convex polygon(s) might be in data (see CONVEX in IRIT),\n\t\t\t\toutput can be wrong as the result!\n");
	    Printed = TRUE;
	}
    }

    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_APX_EQ(MappedPoint[0], MappedPoint[1]) &&
	    !PT_APX_EQ(MappedPoint[0], MappedPoint[2]) &&
	    !PT_APX_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;
    }

    return TriCount;
}

/*****************************************************************************
* Routine to dump one polyline, using global Matrix transform CrntViewMat.   *
*****************************************************************************/
static int DumpOnePolyline(FILE *FGeom, IPPolygonStruct *PPolyline, char *Name,
						RealType Width, int IsPolyline)
{
    int DumpedSphere = FALSE;
    RealType *MappedPoint[2];
    IPVertexStruct *V1, *V2,
	*VList = PPolyline -> PVertex;

    if (!GlblDumpPolylines || VList == NULL)
	return 0;

    V1 = VList;
    V2 = V1 -> Pnext;
    
    while (V2 != NULL) {
	MappedPoint[0] = MapPoint(V1 -> Coord);
	MappedPoint[1] = MapPoint(V2 -> Coord);

	if (GlblZBBox[1] < MappedPoint[0][2])
	    GlblZBBox[1] = MappedPoint[0][2];
	if (GlblZBBox[0] > MappedPoint[0][2])
	    GlblZBBox[0] = MappedPoint[0][2];

	if (!PT_APX_EQ(MappedPoint[0], MappedPoint[1]) &&
	    (IsPolyline || !IP_IS_INTERNAL_VRTX(V1))) {
	    DumpCone(Width * MAX(MappedPoint[0][2] - GlblPolylineDepthCue[0],
				 MIN_RATIO) / GlblPolylineDepthCue[2],
		     MappedPoint[0][0],
		     MappedPoint[0][1],
		     MappedPoint[0][2],
		     Width * MAX(MappedPoint[1][2] - GlblPolylineDepthCue[0],
				 MIN_RATIO) / GlblPolylineDepthCue[2],
		     MappedPoint[1][0],
		     MappedPoint[1][1],
		     MappedPoint[1][2],
		     FGeom, Name);

	    if (!DumpedSphere)
		fprintf(FGeom,
			"sphere %s%sSrfProp %7.5lf %7.5lf %7.5lf %7.5lf\n",
			GlblDumpObjsAsPolylines ? "Pl" : "", Name,
			Width * MAX(MappedPoint[0][2] - GlblPolylineDepthCue[0],
				    MIN_RATIO) / GlblPolylineDepthCue[2],
			MappedPoint[0][0],
			MappedPoint[0][1],
			MappedPoint[0][2]);
	    fprintf(FGeom,
		    "sphere %s%sSrfProp %7.5lf %7.5lf %7.5lf %7.5lf\n",
		    GlblDumpObjsAsPolylines ? "Pl" : "", Name,
		    Width * MAX(MappedPoint[1][2] - GlblPolylineDepthCue[0],
				MIN_RATIO) / GlblPolylineDepthCue[2],
		    MappedPoint[1][0],
		    MappedPoint[1][1],
		    MappedPoint[1][2]);
	    DumpedSphere = TRUE;
	}
	else {
	    DumpedSphere = FALSE;
	}
	
	V1 = V2;
	V2 = V2 -> Pnext;
    }

    if (!IsPolyline && !IP_IS_INTERNAL_VRTX(V1)) {
	MappedPoint[0] = MapPoint(V1 -> Coord);
	MappedPoint[1] = MapPoint(VList -> Coord);

	if (!PT_APX_EQ(MappedPoint[0], MappedPoint[1])) {
	    DumpCone(Width * MAX(MappedPoint[0][2] - GlblPolylineDepthCue[0],
				 MIN_RATIO) / GlblPolylineDepthCue[2],
		     MappedPoint[0][0],
		     MappedPoint[0][1],
		     MappedPoint[0][2],
		     Width * MAX(MappedPoint[1][2] - GlblPolylineDepthCue[0],
				 MIN_RATIO) / GlblPolylineDepthCue[2],
		     MappedPoint[1][0],
		     MappedPoint[1][1],
		     MappedPoint[1][2],
		     FGeom, Name);

	    if (!DumpedSphere)
		fprintf(FGeom,
			"sphere %s%sSrfProp %7.5lf %7.5lf %7.5lf %7.5lf\n",
			GlblDumpObjsAsPolylines ? "Pl" : "", Name,
			Width * MAX(MappedPoint[0][2] - GlblPolylineDepthCue[0],
				    MIN_RATIO) / GlblPolylineDepthCue[2],
			MappedPoint[0][0],
			MappedPoint[0][1],
			MappedPoint[0][2]);
	    fprintf(FGeom,
		    "sphere %s%sSrfProp %7.5lf %7.5lf %7.5lf %7.5lf\n",
		    GlblDumpObjsAsPolylines ? "Pl" : "", Name,
		    Width * MAX(MappedPoint[1][2] - GlblPolylineDepthCue[0],
				MIN_RATIO) / GlblPolylineDepthCue[2],
		    MappedPoint[1][0],
		    MappedPoint[1][1],
		    MappedPoint[1][2]);
	}
    }

    return 1;
}

/*****************************************************************************
*   Routine to dump a cone (can degenerate to a cylinder or nothing).	     *
*****************************************************************************/
static void DumpCone(RealType R1, RealType X1, RealType Y1, RealType Z1,
		     RealType R2, RealType X2, RealType Y2, RealType Z2,
		     FILE *FGeom, char *Name)
{
    RealType
	Len = sqrt(SQR(X1 - X2) + SQR(Y1 - Y2) + SQR(Z1 - Z2));

    if (Len < CONE_SIZE)
	return;

    if (ABS(R1 - R2) < CONE_SIZE) {
	fprintf(FGeom,
		"cylinder %s%sSrfProp %7.5lf  %7.5lf %7.5lf %7.5lf  %7.5lf %7.5lf %7.5lf\n",
		GlblDumpObjsAsPolylines ? "Pl" : "", Name,
		R1, X1, Y1, Z1, X2, Y2, Z2);
    }
    else {
	fprintf(FGeom,
		"cone %s%sSrfProp %7.5lf %7.5lf %7.5lf %7.5lf  %7.5lf %7.5lf %7.5lf %7.5lf\n",
		GlblDumpObjsAsPolylines ? "Pl" : "", Name,
		R1, X1, Y1, Z1, R2, X2, Y2, Z2);
    }
}

/*****************************************************************************
* 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;

    MatMultVecby4by4(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 {
    	MatMultVecby4by4(MappedPt, Pt, CrntViewMat);

    	PT_ADD(Pt2, Pt, Vec);
    	MatMultVecby4by4(MappedPt2, Pt2, CrntViewMat);

    	PT_SUB(MappedVec, MappedPt2, MappedPt);
    	PT_NORMALIZE(MappedVec);
    }

    return MappedVec;
}

/*****************************************************************************
* Irit2Ray exit routine. 						     *
*****************************************************************************/
static void Irit2RayExit(int ExitCode)
{
    exit(ExitCode);
}
