Metropoli BBS
VIEWER: viewobj.c MODE: TEXT (ASCII)
/*****************************************************************************
*   "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 <stdio.h>
#include <math.h>
#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 <Z ... */
    if (LastCoord[2] <= 0.0 && NewCoord[2] <= 0.0) {    /* Draw the <Z part: */

	if (GlblDepthCue) GGMySetLineStyle(DOTTED_LINE);

	GGMyMove(LastCoord[0], LastCoord[1]);
	GGMyDraw(NewCoord[0], NewCoord[1]);			    /* DRAW! */
    }
    else if (LastCoord[2] >= 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 <Z part: */

	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! */
	}
    }

    GEN_COPY(LastCoord, NewCoord, 3 * sizeof(RealType));  /* Set crnt point. */
}

#ifdef __GL__

/****************************************************************************
* Routine to draw a polygon full.					    *
****************************************************************************/
static void DrawPolygonSolid(PolygonStruct *PPolygon, MatrixType Mat)
{
    int i, l = 0;
    float Normal[3], Length;
    double MappedVertex[3];
    RealType V1R[3], V2R[3], V3R[3];
    VertexStruct *PList = PPolygon -> 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__ */

[ RETURN TO DIRECTORY ]