/*
**      $VER: graphics3Df_i.c 10.10 (16.02.98)
**
**      Internal functions for graphics3D.library
**
**      (C) Copyright 97 Patrizio Biancalani
**      All Rights Reserved.
**
**	Note: this code is traslate from the blitzbasic 3d graphics engine 
**	      V 0.9 of Maciej R. Gorny.
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>

#include <graphics/rastport.h>
#include <graphics/clip.h>
#include <graphics/regions.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <graphics/layers.h>

#include "graphics3Dc.h"
#include "graphics3D.h"

/** prototipi locali **/
struct objectnode *resetobj(in);
struct objectnode *nextobj(in);
struct objectnode *pobj(in);

/*** funzioni esterne solo matematiche ***/
extern void matidentity4x4(struct matrix4x4 *imatrix); 
extern void matzero4x4(struct matrix4x4 imatrix);
extern void matcopy4x4(struct matrix4x4 *s_m,struct matrix4x4 *d_m);
extern void matmult4x4(struct matrix4x4 *a,struct matrix4x4 *b,
		struct matrix4x4 *r);
extern void matmult4x4s(struct matrix4x4 *a,struct matrix4x4 *b,
		struct matrix4x4 *r);
extern void matmult1x4s(struct matrix1x4 *a,struct matrix4x4 *b,
		struct matrix1x4 *r);
extern void makevector3d(struct vertex *a,struct vertex *b,
		 struct vector *result);
extern long int vectormag3d(struct vector *a);
extern void normalpol(struct vertex *v0,struct vertex *v1,
	 struct vertex *v2,struct vector *normal);
extern long int dotproduct(struct vector *u,struct vector *v);
extern long int sqri(long int v);
extern long int abs(long int val);

/************************************/
/* costruzione tavole seno e coseno */
/************************************/
void buildlookuptables(in)
struct ambient3d *in;
{
long int *sn,*cn,i,ii;
long int t[]=
{    
/** val. seno angolo (0,90) e moltiplicato per 256 **/
    0,4,9,13,17,22,26,30,35,39,43,47,52,
    56,60,65,69,73,77,81,85,89,93,98,101,105,
    109,113,117,121,125,129,132,136,140,143,
    147,151,154,157,161,164,167,171,174,177,
    180,183,186,189,192,195,198,200,203,206,
    208,211,213,216,218,220,222,225,227,229,
    230,232,234,236,238,239,241,242,243,245,
    246,247,248,249,250,251,252,253,253,254,
    254,255,255,255,256
};

sn=in->sintable;
cn=in->costable;
/** val. seno angolo (0,90) e moltiplicato per 1024 **/
/*
t:=[0,17,35,53,71,89,
    106,124,142,160,177,195,212,230,247,264,282,299,
    316,333,350,366,383,399,416,432,448,464,480,496,
    511,527,542,557,572,587,601,615,630,644,657,671,
    684,698,711,723,736,748,760,772,784,795,806,817,
    828,838,848,858,868,877,886,895,903,912,920,927,
    935,942,949,955,962,968,973,979,984,988,993,997,
    1001,1005,1008,1011,1013,1016,1018,1020,1021,1022,
    1023,1023,1024]:LONG
*/
ii=0;
for(i=0; i<360; i++)
	{
	if (i<91)
		{
		sn[i]=t[ii];
		cn[i]=t[90-ii];
		}
	else if (i<181) 
		{
		sn[i]=t[90-ii];
		cn[i]=-t[ii];
		}
	else if (i<271)
		{
		sn[i]=-t[ii];
		cn[i]=-t[90-ii];
		}
	else
		{
		sn[i]=-t[90-ii];
		cn[i]=t[ii];
		}
	ii++;
	if (ii>90) ii=1;
	}
}

/*******************************************************/
/** crea la matrice di trasformazione inversa globale **/
/** usata per passare da coordinate del mondo in      **/
/** coordinate della camera			      **/
/** Crea matrice in.global_view per capirci.	      **/
/*******************************************************/
void createworldtocamera(in)
struct ambient3d *in;
{
long int temp,active_axes;
struct matrix4x4 translate,rotate_x,rotate_y,rotate_z;
struct matrix4x4 result_1,result_2,*g_v;
struct vector *v_p;
struct dir3d *v_a;
long int *cos,*sin;

cos=in->costable;
sin=in->sintable;
v_a=&in->view_angle;
v_p=&in->view_point;
g_v=&in->global_view;

active_axes=0;

matidentity4x4(&translate);
translate.r3c0=-v_p->x;
translate.r3c1=-v_p->y;
translate.r3c2=-v_p->z;

if (v_a->angx!=NULL)
	{
	matidentity4x4(&rotate_x);
	rotate_x.r1c1=cos[v_a->angx];
	rotate_x.r1c2=sin[v_a->angx];
	rotate_x.r2c1=-sin[v_a->angx];
	rotate_x.r2c2=cos[v_a->angx];
	active_axes=active_axes+1;
	}

if (v_a->angy!=NULL)
	{
	matidentity4x4(&rotate_y);
	rotate_y.r0c0=cos[v_a->angy];
	rotate_y.r0c2=sin[v_a->angy];
	rotate_y.r2c0=-sin[v_a->angy];
	rotate_y.r2c2=cos[v_a->angy];
	active_axes=active_axes+2;
	}

if (v_a->angz!=NULL)
	{
	matidentity4x4(&rotate_z);
	rotate_z.r0c0=cos[v_a->angz];
	rotate_z.r0c1=-sin[v_a->angz];
	rotate_z.r1c0=sin[v_a->angz];
	rotate_z.r1c1=cos[v_a->angz];
	active_axes=active_axes+4;
	}

switch (active_axes)
	{
	case (0):
		matcopy4x4(&translate,g_v);
		break;

	case (1):
		matcopy4x4(&rotate_x,g_v);
		g_v->r3c0=-v_p->x;
		temp=-v_p->y*cos[v_a->angy] - v_p->z*sin[v_a->angy];
		g_v->r3c1=temp >> SFIXV;
		temp=v_p->y*sin[v_a->angy] - v_p->z*cos[v_a->angy];
		g_v->r3c2=temp >> SFIXV;
		break;

	case (2):
		matcopy4x4(&rotate_y,g_v);
		temp=-v_p->x*cos[v_a->angy]+v_p->z*sin[v_a->angy];
		g_v->r3c0=temp >> SFIXV;
		g_v->r3c1=-v_p->y;
		temp=-v_p->y*sin[v_a->angy]-v_p->z*cos[v_a->angy];
		g_v->r3c2=temp >> SFIXV;
		break;

	case (3):
		matmult4x4s(&translate,&rotate_x,&result_1);
		matmult4x4s(&result_1,&rotate_y,g_v);
		break;

	case (4):
		matcopy4x4(&rotate_z,g_v);
		temp=-v_p->x*cos[v_a->angz]-v_p->y*sin[v_a->angz];
		g_v->r3c0=temp >> SFIXV;
		temp= v_p->x*sin[v_a->angz]-v_p->y*cos[v_a->angz];
		g_v->r3c1=temp >> SFIXV;
		g_v->r3c3=-v_p->z;
		break;

	case (5):
		matmult4x4s(&translate,&rotate_x,&result_1);
		matmult4x4s(&result_1,&rotate_z,g_v);
		break;

	case (6):
		matmult4x4s(&translate,&rotate_y,&result_1);
		matmult4x4s(&result_1,&rotate_z,g_v);
		break;

	case (7):
		matmult4x4s(&translate,&rotate_x,&result_1);
		matmult4x4s(&result_1,&rotate_y,&result_2);
		matmult4x4s(&result_2,&rotate_z,g_v);
		
	}
}

/*****************************************************/
/** determina se l'intero oggetto corrente e' fuori **/
/** dal volume compreso nella visuale o inattivo.   **/
/** Se si rimosso ,ritorna un valore <>0	    **/
/*****************************************************/
long int t_removeobject(in)
struct ambient3d *in;
{
long int n_z,f_z;
long int temp,x_bsphere,y_bsphere,z_bsphere;
long int xmax,ymax,zmax,xmin,ymin,zmin,x_compare,y_compare;
struct objectnode *obj;
struct matrix4x4 *g_v;

/*
#ifdef DEBUG
char dbg[100];
#endif
*/

/* trasformo da interi a fixpoint */
f_z=in->far_z * FIXV;
n_z=in->near_z * FIXV;

obj=pobj(in);
g_v=&in->global_view;

/* test se oggetto disattivato ,quindi invisibile */
if (!obj->state) return(1);
	
/* segnalo oggetto non clippato per default */
obj->clipped=0;

xmax=obj->xmax;
ymax=obj->ymax;
zmax=obj->zmax;
xmin=obj->xmin;
ymin=obj->ymin;
zmin=obj->zmin;

temp=obj->worldposx*g_v->r0c0 + obj->worldposy*g_v->r1c0 +
	obj->worldposz*g_v->r2c0;
x_bsphere=(temp>>SFIXV) + g_v->r3c0;
temp=obj->worldposx*g_v->r0c1 + obj->worldposy*g_v->r1c1 +
      obj->worldposz*g_v->r2c1;
y_bsphere=(temp>>SFIXV) + g_v->r3c1;
temp=obj->worldposx*g_v->r0c2 + obj->worldposy*g_v->r1c2 +
      obj->worldposz*g_v->r2c2;
z_bsphere=(temp>>SFIXV) + g_v->r3c2;

/*
#ifdef DEBUG
sprintf(dbg,"r0c2=%ld r1c2=%ld r2c2=%ld r3c2=%ld\n",g_v->r0c1,g_v->r1c2,
	g_v->r2c2,g_v->r3c2);
write_dbg(dbg);
sprintf(dbg,"temp=%ld x=%ld y=%ld z=%ld\n",temp,obj->worldposx,
	obj->worldposy,obj->worldposz);
write_dbg(dbg);
sprintf(dbg,"far=%ld near=%ld zmax=%ld zmin=%ld\n",f_z,n_z,zmax,zmin);
write_dbg(dbg);
sprintf(dbg,"x=%ld y=%ld z=%ld\n",x_bsphere,y_bsphere,z_bsphere);
write_dbg(dbg);
sprintf(dbg,"clipmode ZPLANE\n");
if (in->clip_mode!=ZPLANE) sprintf(dbg,"clipmode FRUSTUM\n");
write_dbg(dbg);
#endif
*/

/* clip con bounding box */
if (in->clip_mode==ZPLANE)
	{
	if (z_bsphere+zmax>f_z OR z_bsphere+zmin<n_z) 
		{
		obj->clipped=1;
/*
#ifdef DEBUG
sprintf(dbg,"oggetto clippato\n");
write_dbg(dbg);
#endif
*/
		return(2);
		}
	else
		{
/*
#ifdef DEBUG
sprintf(dbg,"oggetto non clippato\n");
write_dbg(dbg);
#endif
*/
		return(0);
		}	
	}
else   
	{
	/* esegue un completo test su XYZ */
	if ((z_bsphere+zmax)>f_z OR (z_bsphere+zmin)<n_z)
		{
		obj->clipped=1;
		return(3);
		}
	temp=in->half_screen_width*z_bsphere*FIXV;
	x_compare=temp/in->viewing_distance;
	if ((x_bsphere+xmax)>x_compare OR (x_bsphere+xmin)<-x_compare)
		{
		obj->clipped=1;
		return(4);
		}
	temp=(in->half_screen_height*z_bsphere*FIXV)/in->viewing_distance;
	y_compare=(temp*in->inv_aspect_ratio) >> SFIXV;
	if ((y_bsphere+ymax)>y_compare OR (y_bsphere+ymin)<-y_compare)
		{
		obj->clipped=1;
		return(5);
		}
	}

return(0);
}

/*******************************************************
 ** trasla le coordinate locali dell'oggetto corrente **
 ** nelle coordinate del mondo 			      **
 *******************************************************
 *** INPUT :					       * 
 * in -> valore > 0 restituito da display3d.	       *
 *** OUTPUT:					       *
 * nessuno.					       *
 *******************************************************/
void localtoworld(in)
struct ambient3d *in;
{
struct objectnode *obj;
long int index;
struct vertex *vg,*vl;
struct polygon *pl;
long int wx,wy,wz;

obj=pobj(in);
vg=obj->vcamera;
vl=obj->vlocal;
pl=obj->polys;
wx=obj->worldposx;
wy=obj->worldposy;
wz=obj->worldposz;

for (index=0; index<obj->numverts; index++)
	{
	vg[index].x=vl[index].x+wx;
	vg[index].y=vl[index].y+wy;
	vg[index].z=vl[index].z+wz;
	}
/* resetta flag di invisibilita' */
for (index=0; index<obj->numpolys; index++)
	{
	pl[index].visible=1;
	pl[index].clipped=0;
	}
}

/***************************************************
 ** converte le coordinate nel mondo dell'oggetto **
 ** corrente nelle coordinate della camera        **
 ***************************************************
 *** INPUT :				 	   * 
 * in -> valore > 0 restituito da display3d.	   *
 *** OUTPUT:					   *
 * nessuno.					   *
 ***************************************************/
void worldtocamera(in)
struct ambient3d *in;
{
struct objectnode *obj;
long int index,temp,tempx,tempy,tempz,active_axes;
struct vertex *vc;
struct matrix4x4 gv,*g_v;
struct dir3d *vang;

obj=pobj(in);
vc=obj->vcamera;
vang=&in->view_angle;
g_v=&in->global_view;

active_axes=0;

if (vang->angx!=NULL) active_axes=active_axes+1; 
if (vang->angy!=NULL) active_axes=active_axes+2;
if (vang->angz!=NULL) active_axes=active_axes+4;

switch (active_axes)
	{
	case (0):
		for (index=0; index<obj->numverts; index++)
			{	
			vc[index].x=vc[index].x+g_v->r3c0;
			vc[index].y=vc[index].y+g_v->r3c1;
			vc[index].z=vc[index].z+g_v->r3c2;
			}
		break;
	case (1):
		for (index=0; index<obj->numverts; index++)
			{	
			vc[index].x=vc[index].x+g_v->r3c0;
			tempy=vc[index].y*g_v->r1c1+vc[index].z*g_v->r2c1;
			tempz=vc[index].y*g_v->r1c2+vc[index].z*g_v->r2c2;
			vc[index].z=(tempz >> SFIXV)+g_v->r3c2;
			vc[index].y=(tempy >> SFIXV)+g_v->r3c1;
			}
		break;

	case (2):
		for (index=0; index<obj->numverts; index++)
			{	
			tempx=vc[index].x*g_v->r0c0+vc[index].z*g_v->r2c0;
			vc[index].y=vc[index].y+g_v->r3c1;
			tempz=vc[index].x*g_v->r0c2+vc[index].z*g_v->r2c2;
			vc[index].z=(tempz >> SFIXV)+g_v->r3c2;
			vc[index].x=(tempx >> SFIXV)+g_v->r3c0;
			}
		break;

	case (4):
		for (index=0; index<obj->numverts; index++)
			{	
			tempx=vc[index].x*g_v->r0c0+vc[index].y*g_v->r1c0;
			tempy=vc[index].x*g_v->r0c1+vc[index].y*g_v->r1c1;
			vc[index].y=(tempy >> SFIXV)+g_v->r3c1;
			vc[index].x=(tempx >> SFIXV)+g_v->r3c0;
			vc[index].z=vc[index].z+g_v->r3c2;
			}
		break;

	default :
		for (index=0; index<obj->numverts; index++)
			{	
			tempx=vc[index].x*g_v->r0c0+vc[index].y*g_v->r1c0+
				vc[index].z*g_v->r2c0;
			tempy=vc[index].x*g_v->r0c1+vc[index].y*g_v->r1c1+
				vc[index].z*g_v->r2c1;		
			tempz=vc[index].x*g_v->r0c2+vc[index].y*g_v->r1c2+
				vc[index].z*g_v->r2c2;		
			vc[index].x=(tempx >> SFIXV)+g_v->r3c0;
			vc[index].y=(tempy >> SFIXV)+g_v->r3c1;
			vc[index].z=(tempz >> SFIXV)+g_v->r3c2;
			}
	}	
}

/******************************************************/
/** rimuove le facce posteriori dell'oggetto corrente**/
/** e calcola il flat shading dell'oggetto           **/
/******************************************************/
void removebackfacesandshade(in)
struct ambient3d *in;
{
struct objectnode *obj;
struct vector *vp, *ls;
struct vertex *v0,*v1,*v2;
struct polygon *pol;
long int x,ms1,ms,al,shading,curr_poly,dp,intensity;
struct vector u,v;
struct vector normal,norm,sight;
long int norml;

vp=&in->view_point;
ls=&in->light_source;
obj=pobj(in);
shading=obj->shade;

x=ls->x;

ms=in->maxintensity;
ms1=ms << SFIXV;
al=in->ambient_light >> SFIXV;
for (curr_poly=0; curr_poly<obj->numpolys; curr_poly++)
   {
   pol=&obj->polys[curr_poly];   	
   if (pol->twosided==NULL)
	{
	v0=&obj->vcamera[pol->vertexlist0];
	v1=&obj->vcamera[pol->vertexlist1];
	v2=&obj->vcamera[pol->vertexlist2];
		
	normalpol(v0,v1,v2,&normal);
	
	sight.x=vp->x - v0->x;
	sight.y=vp->y - v0->y;
	sight.z=vp->z - v0->z;

	dp=dotproduct(&normal,&sight);
	if (dp>=NULL)
/* poligono ad una faccia */
	   {
	   pol->visible=1;
	   switch(shading)
	     {
	     case(FLAT):
	/* calcolo lunghezza normale poligono */
	      dp=dotproduct(&normal,ls);
	      if (dp>NULL)
		 {
	      	 v0=&obj->vlocal[pol->vertexlist0];
	         v1=&obj->vlocal[pol->vertexlist1];
	      	 v2=&obj->vlocal[pol->vertexlist2];
	      	 normalpol(v0,v1,v2,&normal);

	      	 norml=vectormag3d(&normal);
	      	 if (norml==NULL) norml=FIXV;
	         intensity=al+(ms1*dp)/norml;
		 if (intensity>ms) intensity=ms;
       /* intensity e' compreso ora tra 0 e ms ,si usera' questo valore */
       /* per trovare l'indice di sfumatura */
		 pol->shade=pol->color+intensity;
		 }
	      else
		 {
	         pol->shade=pol->color+al;	
	         }
	      break;

	     case(SOLID):
 	      pol->shade=pol->color + (ms>>1);
	      break;

	     default:
	/* se tipo di shading <> da flat si assume solid */
 	      pol->shade=pol->color;
	     }
	   }
	else
	   {
	   pol->visible=0;		   
	   }
	}
   else
	{
/* poligono a due facce */
	pol->visible=1;
	switch (shading)
	 {
	 case(FLAT):
	   v0=&obj->vcamera[pol->vertexlist0];
	   v1=&obj->vcamera[pol->vertexlist1];
	   v2=&obj->vcamera[pol->vertexlist2];
		
	   normalpol(v0,v1,v2,&normal);
	
	   dp=dotproduct(&normal,ls);
	   if (dp>NULL)
	      {
	/* calcolo lunghezza normale poligono */
	      v0=&obj->vlocal[pol->vertexlist0];
	      v1=&obj->vlocal[pol->vertexlist1];
	      v2=&obj->vlocal[pol->vertexlist2];
	      normalpol(v0,v1,v2,&normal);

	      norml=vectormag3d(&normal);
	      if (norml==NULL) norml=FIXV;
	      intensity=al+(ms1*dp)/norml;
	      if (intensity>ms) intensity=ms;
	      pol->shade=pol->color+intensity;
	      }
	   else
	      {
	      pol->shade=pol->color+al;
	      }
	   break;

	 case(SOLID):	
	   pol->shade=pol->color + (ms>>1) ;
	   break;

	 default:
	/* se tipo di shading <> da flat si assume solid */
 	   pol->shade=pol->color + (ms>>1);
	 }	
	}
   }

}

/****************************************************/
/** clippa le coordinate della camera dell'oggetto **/
/** corrente rispetto al volume della visuale      **/
/****************************************************/
void clipobject3d(in)
struct ambient3d *in;
{
long int n_z, f_z, curr_poly;
long int x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4;
long int x1_cmp, y1_cmp, x2_cmp, y2_cmp, x3_cmp, y3_cmp, x4_cmp, y4_cmp;
long int vert0, vert1, vert2, vert3, fov_width, fov_height;
struct objectnode *obj;
struct vertex *vc;
struct polygon *pol;

obj=pobj(in);

vc=obj->vcamera;
pol=obj->polys;

/* converto in fixpoint */
n_z=in->near_z*FIXV;
f_z=in->far_z*FIXV;

if (in->clip_mode==ZPLANE)
	{
/* cerca di clippare ogni poligono con il volume della visuale */
	for (curr_poly=0 ;curr_poly<obj->numpolys ;curr_poly++)
		{
		z1=vc[pol[curr_poly].vertexlist0].z;
		z2=vc[pol[curr_poly].vertexlist1].z;
		z3=vc[pol[curr_poly].vertexlist2].z;

	/* test se quadrilatero */
		if (pol[curr_poly].numpoints==4)
			{
			z4=vc[pol[curr_poly].vertexlist3].z;
			}
		else
			{
			z4=z3;
			}
		if ((z1<n_z AND z2<n_z AND z3<n_z AND z4<n_z) OR
		   (z1>f_z AND z2>f_z AND z3>f_z AND z4>f_z))
			pol[curr_poly].clipped=1;
		}
	}
else
	{
/* FRUSTUM MODE CLIP */
	/* calcolo i campi visivi in fixpoint */
	fov_width=(in->half_screen_width/in->viewing_distance)*FIXV;
	fov_height=(in->half_screen_height/in->viewing_distance)*FIXV;
	for (curr_poly=0 ;curr_poly<obj->numpolys ;curr_poly++)		
		{
		vert0=pol[curr_poly].vertexlist0;
		vert1=pol[curr_poly].vertexlist1;
		vert2=pol[curr_poly].vertexlist2;	
		x1=vc[vert0].x;
		y1=vc[vert0].y;
		z1=vc[vert0].z;
		x2=vc[vert1].x;
		y2=vc[vert1].y;
		z2=vc[vert1].z;
		x3=vc[vert2].x;
		y3=vc[vert2].y;
		z3=vc[vert2].z;

	/* test se quadrilatero */
		if (pol[curr_poly].numpoints==4)		
			{
			vert3=pol[curr_poly].vertexlist3;
			x4=vc[vert3].x;
			y4=vc[vert3].y;
			z4=vc[vert3].z;
			}
		else
			{
			x4=x3;
			y4=y3;
			z4=z3;
			}
		if (z1<n_z AND z2<n_z AND z3<n_z AND z4<n_z) 
			{
			pol[curr_poly].clipped=1;
			continue;
			}
		if (z1>f_z AND z2>f_z AND z3>f_z AND z4>f_z)
			{
			pol[curr_poly].clipped=1;
			continue;
			}

		x1_cmp=(fov_width*z1) >> SFIXV;
		x2_cmp=(fov_width*z2) >> SFIXV;
		x3_cmp=(fov_width*z3) >> SFIXV;
		x4_cmp=(fov_width*z4) >> SFIXV;

		if (!((x1>-x1_cmp OR x2>-x2_cmp OR x3>-x3_cmp OR x4>-x4_cmp)
		   AND (x1<x1_cmp OR x2<x2_cmp OR x3<x3_cmp OR x4<x4_cmp)))
			{
			pol[curr_poly].clipped=1;
			continue;
			}
		y1_cmp=(fov_height*z1) >> SFIXV;
		y2_cmp=(fov_height*z2) >> SFIXV;
		y3_cmp=(fov_height*z3) >> SFIXV;
		y4_cmp=(fov_height*z4) >> SFIXV;

		if (!((y1>-y1_cmp OR y2>-y2_cmp OR y3>-y3_cmp OR y4>-y4_cmp)
		   AND (y1<y1_cmp OR y2<y2_cmp OR y3<y3_cmp OR y4<y4_cmp)))
			{
			pol[curr_poly].clipped=1;
			continue;
			}
		}
	}
}	

/******************************************************/
/** crea la lista di tutti i poligoni visibili nella **/
/** frame corrente e li memorizza gia' proiettati    **/
/** e' gia' stato ottimizzato il piu' possibile.     **/
/** Ora si calcola la distanza tra il punto medio del**/
/** poligono e l'osservatore per il succ. riordino.  **/
/******************************************************/
void generatepolylist(in)
struct ambient3d *in;
{
long int *iwp;
long int id,fix1,fix,curr_poly,i,ii,mz,mx,my,x,y,z;
struct pixel *pt;
struct objectnode *obj;
struct vertex *vc;
struct polygon *pol;
struct polytemp *wpl;

#ifdef DEBUG
char dbg[100];
#endif

iwp=in->iwpolys;
pt=(struct pixel *)in->temp;
obj=resetobj(in);
id=0;
curr_poly=0;
fix=(in->aspect_ratio*in->viewing_distance) >> SFIXV;
fix1=in->aspect_ratio;

#ifdef DEBUG
sprintf(dbg,"\n--- ciclo generatepolylist ---\n");
write_dbg(dbg);
#endif

do 
   {	
   if (obj->clipped==0 AND obj->state!=0) 
	{
	/** proietto tutti i punti dell'oggetto corrente **/
	switch(in->projection_type)
		{
		case(PROSP_P) :
	/* uso proiezione prospettica */
			for(i=0 ;i<obj->numverts ;i++ ) 
				{
				vc=&obj->vcamera[i];
				z=vc->z;
				if (z==NULL) z=FIXV;	
				pt[i].x=in->half_screen_width +
					 (vc->x*in->viewing_distance)/z;
				pt[i].y=in->half_screen_height -
					 (vc->y*fix)/z;
		
				}
				break;
		case(PARAL_P) :
	/* uso proiezione parallela */
			for(i=0 ;i<obj->numverts ;i++ ) 
				{
				vc=&obj->vcamera[i];
				pt[i].x=in->half_screen_width + 
					(vc->x >> SFIXV);
				pt[i].y=in->half_screen_height + 
					((vc->y*fix1) >> (2*SFIXV));
				}
				break;
		}
	/* genero lista di poligoni gia' proiettati */
	vc=obj->vcamera;
	for(curr_poly=0 ;curr_poly<obj->numpolys ;curr_poly++) 
		{
		pol=&obj->polys[curr_poly];
		if (pol->visible!=NULL AND pol->clipped==NULL)
			{
	/* reinizializzo puntatore a indice su lista poligoni */
	/* per velocizzare il successivo riordino	      */
			wpl=&in->worldpolys[id];
			iwp[id++]=(long int )wpl;
			mx=0;
			my=0;
			mz=0;
			wpl->numpoints=pol->numpoints;
			wpl->shade=pol->shade;
			wpl->vmode=obj->shade;		
			wpl->obj=obj->id;	
			wpl->npol=curr_poly;

			i=pol->vertexlist0;
			wpl->x1=pt[i].x;
			wpl->y1=pt[i].y;
			mx=mx+vc[i].x;
			my=my+vc[i].y;
			mz=mz+vc[i].z;
			
			i=pol->vertexlist1;
			wpl->x2=pt[i].x;
			wpl->y2=pt[i].y;
			mx=mx+vc[i].x;
			my=my+vc[i].y;
			mz=mz+vc[i].z;
			
			i=pol->vertexlist2;
			wpl->x3=pt[i].x;
			wpl->y3=pt[i].y;
			mx=mx+vc[i].x;
			my=my+vc[i].y;
			mz=mz+vc[i].z;
			
			wpl->x4=wpl->x1;
			wpl->y4=wpl->y1;	

			if (pol->numpoints==4) 
				{
				i=pol->vertexlist3;
				wpl->x4=pt[i].x;
				wpl->y4=pt[i].y;
				mx=mx+vc[i].x;
				my=my+vc[i].y;
				mz=mz+vc[i].z;

				wpl->x5=wpl->x1;
				wpl->y5=wpl->y1;
				}
/* calcolo ora il punto medio */	
			ii=pol->numpoints << SFIXV;
			mx=mx/ii ;
			my=my/ii ;
			mz=mz/ii ;

/* 
   ora calcolo la distanza, considerando che l'osservatore ora 
   e' nell'origine
   (non calcolo la radice quadrata, poiche' altrimenti aumento l'errore
    e il tempo di calcolo).
*/

			wpl->svalue=mx*mx + my*my + mz*mz;

#ifdef DEBUG
sprintf(dbg,"dist=%ld\n",wpl->svalue);
write_dbg(dbg);
#endif
			}
		}
	}
   obj=nextobj(in);
   }while(obj!=NULL);

in->total_polys=id;

/********************/
}

/**********************************************
 ** riporto oggetto attuale sul primo e      **
 ** resituisco puntatore a oggetto.          **
 **********************************************
 *** INPUT :				      * 
 * in -> valore > 0 restituito da display3d.  *
 *** OUTPUT:				      *
 * puntatore a struttura 1# oggetto.	      *
 **********************************************/
struct objectnode *resetobj(in)
struct ambient3d *in;
{

in->attuale=0;

return (pobj(in));
}

/**********************************************
 ** sposto oggetto attuale su prossimo se    **
 ** finiti restituisco 0 altrimenti puntore  **
 ** a oggetto.				     **
 **********************************************
 *** INPUT :				      * 
 * in -> valore > 0 restituito da display3d.  *
 *** OUTPUT:				      *
 * >0 - puntatore a oggetto successivo.       *
 * =0 - oggetti finiti.			      *
 **********************************************/
struct objectnode *nextobj(in)
struct ambient3d *in;
{

if (in->attuale==in->total_objects-1) return(0); 

in->attuale=in->attuale+1;

return (pobj(in));
} 

/*******************************************
 ** restituisce indirizzo oggetto attuale **
 *******************************************/

struct objectnode *pobj(in)
struct ambient3d *in;
{

return(&in->objects[in->attuale]);

}

/************************************************
 ** aggiorna tutti i valori precalcolati sul   **
 ** oggetto corrente.	  		       **
 ************************************************
 *** INPUT :				        * 
 * in -> valore > 0 restituito da display3d.	*
 *** OUTPUT:					*
 * nessuno.					*
 ************************************************/
void aggobj(in)
struct ambient3d *in;
{

computeobjectbox(in);

}

/**********************************************/
/** calcola il bounding box dell'oggetto at- **/
/** tuale per il controllo delle collisioni  **/
/**********************************************/
void computeobjectbox(in)
struct ambient3d *in;
{
struct objectnode *ob;
struct vertex *vt;
long int xmax,ymax,zmax,xmin,ymin,zmin;
long int x,y,z,i;

ob=pobj(in);

vt=ob->vlocal;

xmax=vt[0].x;
ymax=vt[0].y;
zmax=vt[0].z;
xmin=vt[0].x;
ymin=vt[0].y;
zmin=vt[0].z;

for(i=1 ;i<ob->numverts ; i++)
	{
	x=vt[i].x;
	y=vt[i].y;
	z=vt[i].z;
	if (x>xmax) xmax=x;
	if (x<xmin) xmin=x;
	if (y>ymax) ymax=y;
	if (y<ymin) ymin=y;
	if (z>zmax) zmax=z;
	if (z<zmin) zmin=z;
	}

ob->xmax=xmax;
ob->ymax=ymax;
ob->zmax=zmax;
ob->xmin=xmin;
ob->ymin=ymin;
ob->zmin=zmin;

}

/************************************
 ** routin richiamata da quicksort **
 ** occhio e' recursiva.	   **
 ** I parametri non possono percio'**
 ** essere passati tramite registri**
 ************************************/
void qsort(lo0,hi0,pol,count)
long int lo0;
long int hi0;
long int *pol;
long int *count;
{
long int it, lo, hi, mid, t;
struct polytemp *wpl;

lo=lo0;
hi=hi0;
it=count[0];

if (hi0>lo0)
/* stabilisco arbitrariamente il pivot point nel mezzo dell'array */
	{
	wpl=(struct polytemp *)pol[(lo0+hi0)>>1];
	mid=wpl->svalue;

/* eseguo il loop finche non incrocio indici */
	while(lo<=hi)
/* trovo il primo elemento minore o uguale a mid partendo da sinistra */
		{
		wpl=(struct polytemp *)pol[lo];
		while(lo<hi0 AND wpl->svalue>mid)
			{
			lo++;
			wpl=(struct polytemp *)pol[lo];
			it++;
			}
/* trovo il primo elemento maggiore o uguale a mid partendo da destra */
		wpl=(struct polytemp *)pol[hi];
		while(hi>lo0 AND wpl->svalue<mid)
			{	
			hi--;
			wpl=(struct polytemp *)pol[hi];
			it++;
			}
/* se gli indici non si sono incrociati scambio elementi */
		if (lo<=hi)
			{
			t=pol[hi];
			pol[hi]=pol[lo];
			pol[lo]=t;
			lo++;
			hi--;
			}
		}
	}		

count[0]=it;

if (lo0<hi) qsort(lo0,hi,pol,count);

if (lo<hi0) qsort(lo,hi0,pol,count);

}

/******************************************************
 ** visualizza un gruppo di poligoni nella rastport  **
 ** corrente(purche di 3 o 4 lati). 		     **	
 ** E' ottimizzata il piu' possibile.		     **
 ******************************************************
 **** INPUT :					     ** 
 **  graf ->valore non 0 ritornato da ini_g().	     **
 **  iwp  ->puntatore ad array di puntatori a strut- **
 **         ture polytemp.			     ** 
 **  total_polys ->n# totale elementi nell'array iwp **
 **  colb ->se >=0 allora poligoni con bordo di quel **
 **         colore.				     **
 **** OUTPUT:					     **
 ** nessuno.					     **
 ******************************************************/
void paintpol(graf,iwp,total_polys,colb)
struct grafica *graf;
long int *iwp;
long int total_polys;
long int colb;
{
struct RastPort *rast;
struct polytemp *wpl;
long int i,f,f1;
long int ob,m1,mx;
short int *pvert;

if ((long int)graf<=NULL OR (long int)(graf->rast)<=NULL) return(0); 

rast=graf->rast;
mx=graf->lb_af;
/* default bordo poligoni assente */
BNDRYOFF(rast)
if (colb>=NULL) SetOutlinePen(rast,colb);

f1=((struct polytemp *)iwp[0])->shade;
SetAPen(rast,f1);
m1=NULL;
ob=((struct polytemp *)iwp[0])->obj;
for (i=0;i<total_polys;i++)
	{
	wpl=(struct polytemp *)iwp[i];
	pvert=(short int *)wpl;
	/* visualizzo il poligono */ 
/** visualizza un poligono proiettato **/
	f=wpl->shade;
	if (f1!=f OR ob!=wpl->obj) 
		{
		if (m1>NULL) 
			{
			AreaEnd(rast);
			m1=NULL;
			}
		SetAPen(rast,f);
		f1=f;
		ob=wpl->obj;
		}	
	if (m1>mx)
		{
		AreaEnd(rast);
		m1=NULL;
		}
	if (wpl->vmode!=WIREF) 
		{
		m1=m1+5;
		AreaMove(rast,pvert[0],pvert[1]);

		AreaDraw(rast,pvert[2],pvert[3]);
		AreaDraw(rast,pvert[4],pvert[5]);

		if (wpl->numpoints>3) AreaDraw(rast,pvert[6],pvert[7]);
		}
	else
		{
		Move(rast,pvert[0],pvert[1]);
		PolyDraw(rast,wpl->numpoints+1,pvert);
		}
	}

if (m1>NULL) AreaEnd(rast);

}

/**********************************************
 ** effettuo ordinamento poligoni proiettati **
 ** via quicksort			     **
 ** mediamente 4 volte piu' veloce dello     ** 
 ** shellsort.				     **
 **********************************************
 **** INPUT :				     **
 ** len -> numero di poligoni da riordinare  **
 ** pol -> array di puntatori alle strutture **
 **        polytemp, da riordinare.          **
 **** OUTPUT:				     **
 ** n# di passi effettuati per l'ordinamento.** 
 **********************************************/
long int quicksort(len,pol)
long int len;
long int *pol;
{
long int itera;

itera=0;

if (len>NULL) qsort(0,len-1,pol,&itera);

return(itera);
}
/*******************************************************************/
