/*****************************************************************************
*   Routines to	prepare objects for the scan conversion:		     *
* At this stage, it is assumed all vertices normals has been evaluated, if   *
* is is required to interpolate them (i.e. Gouraud shading.)		     *
* This module sort the polygons into hash table of GlblShadeInfo.ScrnYSize size,	     *
* according to their lowest vertex, and update a pointer to it.		     *
*									     *
* Written by:  Gershon Elber				Ver 2.0, Mar. 1990   *
*****************************************************************************/

#include <math.h>
#include <stdio.h>
#include <time.h>
#include "program.h"
#include "genmat.h"
#include "iritprsr.h"

static void PrepareAllObjects(IPObjectStruct *PObjects);
static void PrepareOnePolygonObject(IPObjectStruct *PObject);
static void PrepareOnePolygon(IPPolygonStruct *PPolygon, int IsPolygon);
static int UpdateBBoxPolygon(IPPolygonStruct *PPolygon);
static void UpdateScanConvertData(int MinVertex, IPPolygonStruct *PPolygon);

/*****************************************************************************
* Routine to prepare NumOfObjects given in Objects from FileDescription FD   *
* according to view matrix Mat. If NumOfObjects == 0 then all the objects    *
* defined by the data sturcture are handled, and NumOfObjects is set to real *
* number of objects exists.						     *
*****************************************************************************/
void PrepareViewData(IPObjectStruct *PObjects)
{
    long
	SaveTime = time(NULL);

    fprintf(stderr, "\nPass 2, Polys        =      ");

    PrepareAllObjects(PObjects);

    fprintf(stderr, ",  %ld seconds.", time(NULL) - SaveTime);
#ifdef AMIGA
    fflush(stderr);
#endif /* AMIGA */
}

/*****************************************************************************
* Scan all objects.							     *
*****************************************************************************/
static void PrepareAllObjects(IPObjectStruct *PObjects)
{
    while (PObjects) {
	if (IP_IS_POLY_OBJ(PObjects) && IP_IS_POLYGON_OBJ(PObjects))
	    PrepareOnePolygonObject(PObjects);
	PObjects = PObjects -> Pnext;
    }
}

/*****************************************************************************
* Routine to prepare one polygon object PObject.			     *
*****************************************************************************/
static void PrepareOnePolygonObject(IPObjectStruct *PObject)
{
    struct IPPolygonStruct
	*PList = PObject -> U.Pl;

    while (PList) {
	PrepareOnePolygon(PList, IP_IS_POLYGON_OBJ(PObject));
	PList = PList -> Pnext;
    }
}

/*****************************************************************************
* Routine to prepare one polygon PPolygon.				     *
*****************************************************************************/
static void PrepareOnePolygon(IPPolygonStruct *PPolygon, int IsPolygon)
{
    static int
	PolyCount = 0;
    int	i;
    RealType CpCoord[3];
    IPVertexStruct
	*VList = PPolygon -> PVertex;

    fprintf(stderr, "\b\b\b\b\b%5d", ++PolyCount);
    GlblNumOfPolys++;

    for (; VList != NULL; VList = VList -> Pnext) {
	/* Convert the coordinate to screen space (pres.). */
	MatMultVecby4by4(CpCoord, VList -> Coord, GlblViewMat);
	for (i = 0; i < 3; i++)
	    VList -> Coord[i] = CpCoord[i];
    }

    if (!IsPolygon)
	return;

    /* Find X, Y extremum in screen space, and use the lowest vertex in Y to */
    /* initialize the scan conversion structure of the polygon:		     */
    i = UpdateBBoxPolygon(PPolygon);
    UpdateScanConvertData(i, PPolygon);

    /* Transform the polygon plane equation as well, and normalize it: */
    UpdateEqnPolygon(PPolygon, FALSE);
}

/*****************************************************************************
* Routine to update polygon boundary box in screen space:		     *
* Note this routine is called after the	polygons was checked for validity -  *
* all the list of objects was found to be vertices only.		     *
*****************************************************************************/
static int UpdateBBoxPolygon(IPPolygonStruct *PPolygon)
{
    int i,
	MinV = 0;
    RealType *Coord, Xmin, Xmax, Ymin, Ymax;
    struct IPVertexStruct
	*VList = PPolygon -> PVertex;

    Xmin = Xmax = VList -> Coord[0];
    Ymin = Ymax = VList -> Coord[1];
    for (VList = VList -> Pnext, i = 1;
	 VList != NULL;
	 VList = VList -> Pnext, i++) {
	Coord = VList -> Coord;
	if (Coord[0] > Xmax)
	    Xmax = Coord[0];
	if (Coord[0] < Xmin)
	    Xmin = Coord[0];
	if (Coord[1] > Ymax)
	    Ymax = Coord[1];
	if (Coord[1] < Ymin) {
	    Ymin = Coord[1];
	    MinV = i;
	}
    }

    PPolygon -> BBox[0][0] = (int) Xmin;
    PPolygon -> BBox[1][0] = (int) Xmax;
    PPolygon -> BBox[0][1] = (int) Ymin;
    PPolygon -> BBox[1][1] = (int) Ymax;

    return MinV;
}

/*****************************************************************************
* Routine to update polygon scan conversion information:		     *
* Each polygon (Remember they must be convex), has two boundaries we cross   *
* by the scan lines if it is active. These are LeftBndry and RightBndry we   *
* update here.								     *
*****************************************************************************/
static void UpdateScanConvertData(int MinVertex, IPPolygonStruct *PPolygon)
{
    int i;
    struct IPVertexStruct *VMinY, *VBefore, *VAfter,
	*VList = PPolygon -> PVertex;
    struct PolygonScanConvertStruct *PScan;

    /* Find the minimum location again: */
    if (MinVertex == 0) {
	/* Its the first vertex that has minimum Y value: */
	VMinY = VList;
	for (VBefore = VList;
	     VBefore -> Pnext != NULL;
	     VBefore = VBefore -> Pnext);
	VAfter = VList -> Pnext;
    }

    else {
	for (i = 1, VBefore = VList;
	     i < MinVertex;
	     i++, VBefore = VBefore -> Pnext);
	VMinY = VBefore -> Pnext;
	VAfter = VMinY -> Pnext ? VMinY -> Pnext : VList;
    }

    PPolygon -> PAux = IritMalloc(sizeof(struct PolygonScanConvertStruct));
    PScan = (PolygonScanConvertStruct *) PPolygon -> PAux;

    PScan -> Bndry1.VMinY = VMinY;
    PScan -> Bndry1.VMaxY = VBefore;
    PScan -> Bndry1.MaxEdgeY = (int) VBefore -> Coord[1];

    PScan -> Bndry2.VMinY = VMinY;
    PScan -> Bndry2.VMaxY = VAfter;
    PScan -> Bndry2.MaxEdgeY = (int) VAfter -> Coord[1];
}
