/*
	Module to Scan Convert Objects on the Hemicube.
	Objects include Polygon, sphere, bubble, cylinder, tube,
	cone, cup and anular ring.
	Objects other than Polygon are scan converted by
	breaking them into rectangular parts in u,v domain.
	The breakup used is user defined if specified by the
	user or default break up.
	Default breakup for different objects are as follows:
	Sphere, Bubble : 4 x 4
	Cylinder, Tube : 4 x 1
	Cone, cup      : 4 x 1
	Anular Ring    : 4 x 1
	User may change the defaults by changing the
	appropriate #define statements below.
	The rectangluar parts in the u,v domain are assumed to
	be planar quadrilateral. No effort is made to check 
	their planarity. The normal to the quadrilateral is
	computed as the actual object object normal at the 
	center of the (u,v) rectangular part. This normal is
	used for back-face culling.
	NOTE : If the above mentioned technique is used to 
	render the objects other than Polygon then terrible
	cracks may appear. 
------
sumant
29-July-1991
*/
#include <stdio.h>
#include <math.h>

#include "GraphicsGems.h"
#include "poly.h"
#include "data_structure.h"
#include "objects.h"
extern int hemicube_resolution;
#define default_sphere_h_reso 4
#define default_sphere_v_reso 4
#define default_cylinder_h_reso 4
#define default_cylinder_v_reso 1
#define default_cone_h_reso 4
#define default_cone_v_reso 1
#define default_ring_h_reso 4
#define default_ring_v_reso 1
Poly_vert *poly_dummy;
/*
	Some Fixed assumptions for scan converting on hemicube Face
	NOTE : If the row and column resolution are same then
		the raster is assumed to be for the side face
		of hemicube.
*/
double D = 1.;
double Dmin=0.025;
double Dmax=100;
static Poly_box regular_box={-1.,1.,-1.,1.,0.,1.};

int view_transform(C,U,V,N,rows,cols,m)
Point3 *C;
Vector3 *U,*V,*N;
int rows,cols;
Matrix4 *m;
/*
        From 
	      N - ViewPlane Normal
              V - View Up Vector
	      U - Axis perpendicular to V and N
              C - the point where the new origin will lie
              D - viewplane distance from origin 
              DMin - Distance of the near plane
              Dmax - Distance of the Far place
              Umin,Umax,Vmin,Vmax - the ViewWindow extent
        Computes
              matrix - a 4x4 transformation matrix to transform the
                        viewing volume to regular clipping volume.
        Ref. : book by Slater and Salmon
*/
{
	double Wxmin,Wymin,Wxmax,Wymax;
	double pX,pY,dX,dY,dD;
	Matrix4 m1,m2;
/*
	V3Negate(U); V3Negate(V);
*/
/*
fprintf(stderr,"%g %g %g : %g %g %g : %g %g %g\n",
U->x,U->y,U->z,V->x,V->y,V->z,N->x,N->y,N->z);
*/
	/* Following two lines very special to Hemicube face */
	Wxmax = Wymax = 1.;
	Wxmin = -1.; Wymin =((rows==cols)?-1.:0.);
        m1.element[0][0]=U->x;
        m1.element[1][0]=U->y;
        m1.element[2][0]=U->z;

        m1.element[0][1]=V->x;
        m1.element[1][1]=V->y;
        m1.element[2][1]=V->z;

        m1.element[0][2]=N->x;
        m1.element[1][2]=N->y;
        m1.element[2][2]=N->z;

	m1.element[0][3] = m1.element[1][3] = m1.element[2][3] = 0.;
	m1.element[3][3] = 1.;
        m1.element[3][0] = -V3Dot(C,U);
        m1.element[3][1] = -V3Dot(C,V);
        m1.element[3][2] = -V3Dot(C,N);
        /*

                Transforming the View Volume to Regular ParalleloPiped.
                ------------------------------------------------------
                2D/dX   0       0            0
                0       2D/dY   0            0
                -pX/dX  -pY/dY  DMax/        1
                                (DMax-DMin)
                0       0       -DMax.DMin/  0
                                (DMax-DMin)

                where pX = (Umin+Umax); pY = (Vmin+Vmax)
                      dX = (Umin+Umax); dY = (Vmax-Vmin)

        */
        /* Covert to Regular Pyramid */
        pX = (Wxmin+Wxmax); pY = (Wymin+Wymax);
        dX = (Wxmax-Wxmin); dY = (Wymax-Wymin);
        dD = (Dmax-Dmin);
	m2.element[0][1] = m2.element[0][2] = m2.element[0][3] = 
	m2.element[1][0] = m2.element[1][2] = m2.element[1][3] =
	m2.element[3][0] = m2.element[3][1] = m2.element[3][3] = 0.;
        m2.element[2][3] = 1.0;

        m2.element[0][0] = 2.0*D/dX;
        m2.element[1][1] = 2.0*D/dY;
        m2.element[2][0] = -pX/dX; m2.element[2][1] = -pY/dY;
        m2.element[2][2] = Dmax/dD;
        m2.element[3][2] = -Dmax*Dmin/dD;
	V3MatMul(&m1,&m2,m);
}

static int current_patch;
static double *current_zbuffer;
static short int *current_raster;
static char *current_surface_buffer;
static void pixelproc(x, y, point)
int x, y;
Poly_vert *point;
{
        int index=y*hemicube_resolution+x;
	if (x < 0 || x >= hemicube_resolution){
		fprintf(stderr,"Beyond the raster limit.\n");
		return;
	}
        if (point->sz < current_zbuffer[index]){
		int uindex=grid_u(current_patch,point->u);
		int vindex=grid_v(current_patch,point->v);
                current_zbuffer[index] = point->sz;
		current_raster[index] = 
			object[current_patch].start_grid_index+
			gridindex(current_patch,uindex,vindex)+1;
		current_surface_buffer[index]=
			object[current_patch].surface_reflection_type+1;
        }
}
static void homogeneous_xform(p,m)
Poly_vert *p;
Matrix4 *m;
{
        p->sx = (p->x * m->element[0][0]) + (p->y * m->element[1][0]) +
                 (p->z * m->element[2][0]) + m->element[3][0];
        p->sy = (p->x * m->element[0][1]) + (p->y * m->element[1][1]) +
                 (p->z * m->element[2][1]) + m->element[3][1];
        p->sz = (p->x * m->element[0][2]) + (p->y * m->element[1][2]) +
                 (p->z * m->element[2][2]) + m->element[3][2];
        p->sw = (p->x * m->element[0][3]) + (p->y * m->element[1][3]) +
                 (p->z * m->element[2][3]) + m->element[3][3];
}
static void clip_n_scan(patchnum,p,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Poly *p;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	int i;
	for (i=0;i<p->n;i++){
		homogeneous_xform(&(p->vert[i]),m);
	}
        p->mask = (POLY_MASK(sx)|POLY_MASK(sy)|POLY_MASK(sz)
		  |POLY_MASK(sw)
		  |POLY_MASK(u)|POLY_MASK(v));
	if(poly_clip_to_box(p,&regular_box)!=POLY_CLIP_OUT){
		Window w;
                for(i=0;i<p->n;i++){
                        p->vert[i].sx/=p->vert[i].sw;
                        p->vert[i].sy/=p->vert[i].sw;
                        p->vert[i].sz/=p->vert[i].sw;
                        /* Do Window to Vp transform */
                        p->vert[i].sx=0.5*cols*(p->vert[i].sx+1.);
                        p->vert[i].sy=0.5*rows*(p->vert[i].sy+1.);
                }
		current_patch=patchnum;
		current_zbuffer=zbuffer;
		current_surface_buffer=surface_buffer;
		current_raster=raster;
		w.x0 = w.y0 = 0;
		w.x1 = cols-1; w.y1=rows-1;
                poly_scan(p,&w,pixelproc);
	}
}
void null_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
error("Scan Conversion of this geometry has not been implemented.");
}
static int back_face(v1,v2)
Vector3 *v1,*v2;
{
	double d=V3Dot(v1,v2);
	return(d>0. && d<=1.);
}
static void quadrilateral_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	Poly p;
	Quadrilateral *q=(Quadrilateral *)object[patchnum].object_specific_structure;
	if (back_face(N,&(q->plane_normal))) return;
	p.n = 4;
	p.vert[0].x = q->P00.x; p.vert[0].y = q->P00.y; p.vert[0].z = q->P00.z;
	p.vert[0].u = p.vert[0].v = 0.;
	p.vert[1].x = q->P10.x; p.vert[1].y = q->P10.y; p.vert[1].z = q->P10.z;
	p.vert[1].u = 1.0; p.vert[1].v = 0.;
	p.vert[2].x = q->P11.x; p.vert[2].y = q->P11.y; p.vert[2].z = q->P11.z;
	p.vert[2].u = p.vert[2].v = 1.;
	p.vert[3].x = q->P01.x; p.vert[3].y = q->P01.y; p.vert[3].z = q->P01.z;
	p.vert[3].u = 0.; p.vert[3].v = 1.;
	clip_n_scan(patchnum,&p,m,rows,cols,zbuffer,surface_buffer,raster);
}

static void quadric_scan_convert(otype,patchnum,nu,nv,def_nu,def_nv,
			d,N,m,rows,cols,zbuffer,surface_buffer,raster)
int otype;
int patchnum;
int nu,nv,def_nu,def_nv;
void *d;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	Point3 posn[4];
        Poly p;
	int i,j,k;
	double du,dv,u,v;
	double U[4], V[4];

	if (nv==1 && nu==1){
		nv = def_nv; nu = def_nu;
	}
	du = 1./nu; dv = 1./nv;
	for(i=0,v=0.;i<nv;i++,v+=dv){
		posn[0] = ofunc[otype].object_point(d,0.,v);
		posn[3] = ofunc[otype].object_point(d,0.,v+dv);
		U[0] = U[3] = 0.; V[0] = v; V[3] = v+dv;
		for(j=0,u=0.;j<nu;j++,u+=du){
			Vector3 normal;
			normal = ofunc[otype].get_surface_normal(
					d,u+0.5*du,v+0.5*dv
				);
        		if (back_face(N,&normal)) continue;
			posn[1] = ofunc[otype].object_point(d,u+du,v);
			U[1] = u + du; V[1] = v;
			posn[2] = ofunc[otype].object_point(d,u+du,v+dv);
			U[2] = u + du; V[2] = v+dv;
			p.n = 4;
			switch (otype){
			case SPHERE	:
			case CYLINDER	:
			case CONE	:
			case RING	:
				for(k=0;k<4;k++){
					p.vert[k].x = posn[k].x;
					p.vert[k].y = posn[k].y;
					p.vert[k].z = posn[k].z;
					p.vert[k].u = U[k];
					p.vert[k].v = V[k];
				}
				break;
			case BUBBLE	:
			case TUBE	:
			case CUP	:
				for(k=3;k>=0;k--){
					p.vert[k].x = posn[k].x;
					p.vert[k].y = posn[k].y;
					p.vert[k].z = posn[k].z;
					p.vert[k].u = U[k];
					p.vert[k].v = V[k];
				}
				break;
			default		:
				error("In scan converting qudric this case is illegal.");
			}
			posn[0] = posn[1];
				U[0] = U[1]; 
					V[0] = V[1];
			posn[3] = posn[2]; 
				U[3] = U[2]; 
					V[3] = V[2];
			clip_n_scan(patchnum,&p,m,rows,cols,zbuffer,surface_buffer,raster);
		}
	}
}
static void sphere_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	quadric_scan_convert(SPHERE,patchnum,
		object[patchnum].grid_h_reso,
		object[patchnum].grid_v_reso,
		default_sphere_h_reso,default_sphere_v_reso,
			object[patchnum].object_specific_structure,
			N,m,rows,cols,zbuffer,surface_buffer,raster);
}

static void bubble_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	quadric_scan_convert(BUBBLE,patchnum,
		object[patchnum].grid_h_reso,
		object[patchnum].grid_v_reso,
		default_sphere_h_reso,default_sphere_v_reso,
			object[patchnum].object_specific_structure,
			N,m,rows,cols,zbuffer,surface_buffer,raster);
}

static void cylinder_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	quadric_scan_convert(CYLINDER,patchnum,
		object[patchnum].grid_h_reso,
		object[patchnum].grid_v_reso,
		default_cylinder_h_reso,default_cylinder_v_reso,
			object[patchnum].object_specific_structure,
			N,m,rows,cols,zbuffer,surface_buffer,raster);
}

static void tube_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	quadric_scan_convert(TUBE,patchnum,
		object[patchnum].grid_h_reso,
		object[patchnum].grid_v_reso,
		default_cylinder_h_reso,default_cylinder_v_reso,
			object[patchnum].object_specific_structure,
			N,m,rows,cols,zbuffer,surface_buffer,raster);
}

static void cone_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	quadric_scan_convert(CONE,patchnum,
		object[patchnum].grid_h_reso,
		object[patchnum].grid_v_reso,
		default_cone_h_reso,default_cone_v_reso,
			object[patchnum].object_specific_structure,
			N,m,rows,cols,zbuffer,surface_buffer,raster);
}

static void cup_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	quadric_scan_convert(CUP,patchnum,
		object[patchnum].grid_h_reso,
		object[patchnum].grid_v_reso,
		default_cone_h_reso,default_cone_v_reso,
			object[patchnum].object_specific_structure,
			N,m,rows,cols,zbuffer,surface_buffer,raster);
}

static void ring_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
	quadric_scan_convert(RING,patchnum,
		object[patchnum].grid_h_reso,
		object[patchnum].grid_v_reso,
		default_ring_h_reso,default_ring_v_reso,
			object[patchnum].object_specific_structure,
			N,m,rows,cols,zbuffer,surface_buffer,raster);
}
static void polygon_scan_convert(patchnum,N,m,rows,cols,zbuffer,surface_buffer,raster)
int patchnum;
Vector3 *N;
Matrix4 *m;
int rows,cols;
double *zbuffer;
char *surface_buffer;
short int *raster;
{
        Poly p;
	int i;
        Polygon *poly=(Polygon *)object[patchnum].object_specific_structure;

        if (back_face(N,&(poly->plane_normal))) return;
        p.n = poly->nvertices;
	for(i=0;i<p.n;i++){
		p.vert[i].x=poly->vertex_list[i].x;
		p.vert[i].y=poly->vertex_list[i].y;
		p.vert[i].z=poly->vertex_list[i].z;
		p.vert[i].u = p.vert[i].v = 0.5; 
			/* This assignment is arbitrary.
			   Any value would do. */
	}
	clip_n_scan(patchnum,&p,m,rows,cols,zbuffer,surface_buffer,raster);
}
void (*geometry_specific_scan_convert[MAX_GEOMETRY_TYPE])()=
{
	quadrilateral_scan_convert,
	sphere_scan_convert,
	bubble_scan_convert,
	cylinder_scan_convert,
	tube_scan_convert,
	cone_scan_convert,
	cup_scan_convert,
	ring_scan_convert,
	polygon_scan_convert,
	null_scan_convert
};
