/*-----------------------------------------------------------------------------
 * File: rnd_util.c
 * Written by: Fredrik Kling & Alexander Boczar, 1997-08-15 -> 1997-XX-XX
 * Description: Rendering utils for drivers...
 *
 * Updates:
 * -- Date -- | ----- Name ----- |-- Did what....
 * 1997-08-15 | Fredrik Kling    | Implementation
 *
 * Kommentar:
 *
 * Todo:
 *----------------------------------------------------------------------------*/
#include "formats/v3o.h"
#include "objects/object.h"
#include "objects/camera.h"
#include "objects/light.h"
#include "vmath/vector.h"
#include "vmath/matrix.h"
#include "vmath/vmath.h"
//#include "drivers/drv8.h"
#include "system/xmath.h"
#include "misc/spline.h"
//#include "debug/mono.h"

#include "render16/rnd_util.h"
//#include "render/polydraw.h"

//#define DBS 1


#ifdef DBS
static RGBA cl_white = {255,255,255,0};
#endif
//static CAMERA *activecamera;
//static V3D *currentscene;

//static VECTOR camvec;

//static BUFF *buff;

//static int numobj,numlight,numpoly,numplot;

/******************************************************************
 * Function  : project                                            *
 * Input     : vector *s, float xo, float yo, float zo, float d   *
 * Return    : nothing                                            *
 * Effect    : Transforms a 3d-coord to a 2d-coord                *
 * Comments  : -                                                  *
 ******************************************************************/
void render_project (RNDDRV *drv,VECTOR *v, float xo, float yo, float zo)
{
  float t;

	t = drv->camera->focus/(v->z+zo);
	v->x = (xo+v->x)*t;
	v->y = (yo+v->y)*t;

}
/******************************************************************
 * Function  : checkspherical                                     *
 * Input     : object *s, float d                                 *
 * Return    : 1 if success 0 if fail                             *
 * Effect    :                                                    *
 * Comments  : Check whatever an object is inside the space limit *
 ******************************************************************/
int render_checkspherical (RNDDRV *drv,OBJECT *obj)
{
  float r,t;
  float xo,yo,xrmin,xrmax,yrmin,yrmax;
  VECTOR v;
#ifdef DBS
	XYZ p1,p2;
#endif

  xo = drv->buff->xorigo;
  yo = drv->buff->yorigo;

	t = drv->camera->focus/(obj->calcpos.z);

	r = t * obj->radius;

  v.x=obj -> calcpos.x * t;
  v.y=obj -> calcpos.y * t;
  v.z=obj -> calcpos.z;

  xrmin=v.x+xo - r;
  xrmax=v.x+xo + r;
  yrmin=v.y+yo - r;
  yrmax=v.y+yo + r;

// Just testing...
//	if ((v.x+xo)>drv->buff->maxx) return 0;
//	if ((v.x+xo)<drv->buff->minx) return 0;


  if (xrmin>drv->buff->maxx) return 0;
  if (xrmax<drv->buff->minx) return 0;
  if (yrmin>drv->buff->maxy) return 0;
  if (yrmax<drv->buff->miny) return 0;

#ifdef DBS
		p1.l = p2.l = 48;

		p1.x = v.x+xo-r;
		p1.y = v.y+yo;
		p1.z = v.z;

		p2.x = v.x+xo+r;
		p2.y = v.y+yo;
		p2.z = v.z;

	  drv->buff->drv->line (p1,p2,cl_white);

		p1.x = v.x+xo;
		p1.y = v.y+yo+r;
		p1.z = v.z;

		p2.x = v.x+xo;
		p2.y = v.y+yo-r;
		p2.z = v.z;

    drv->buff->drv->line (p1,p2,cl_white);
#endif
  return 1;
}

void render_getvector (VECTOR *v,float x1,float y1,float z1,float x2, float y2,float z2)
{
	v->x=x2-x1;
	v->y=y2-y1;
	v->z=z2-z1;
}
/******************************************************************
 * Function  : calclight_v3o                                      *
 * Input     : v3o *, surface *, lightlist, l1,l2,l3              *
 * Return    : nothing                                            *
 * Effect    : Calcs lightsource values for a surface             *
 * Comments  : Supports all materials                             *
 ******************************************************************/
void render_calclight (RNDDRV *drv,V3O *v3o, V3OSURFACE *sf, OBJECT *lightlist,float *l1,float *l2,float *l3,VECTOR *origo)
{
	int a,b,c,j;
	VECTOR lv;
	VECTOR nv,v1,v2;
	OBJECT *l;
	float x,y,z,intens,i1,i2,i3,len;
	float ltab[3];
	int ptab[3];

	// Check shadeing type...
  if (v3o->material[sf->material].flags & V3OMATERIALFLAG_FLAT)
	{
		a=sf->v1,b=sf->v2,c=sf->v3;

		// Ska vi anv„nda origo eller skall vi r„kna mot ytan..

		if (lightlist)
		{
			// Exact calculations...
			// Tar ut mittpunkten p† ytan och anv„nder den som ber„knings punkt
			// f”r ljusvektorn...   D†liga resultat vid flatshadeina pga trianglar
			if (lightlist->vlgh->flags & VLGHFLAG_EXACT)
			{
				x=(v3o->rotvertex[a].x+v3o->rotvertex[b].x+v3o->rotvertex[c].x)*(float)(1.0/3.0);
				y=(v3o->rotvertex[a].y+v3o->rotvertex[b].y+v3o->rotvertex[c].y)*(float)(1.0/3.0);
				z=(v3o->rotvertex[a].z+v3o->rotvertex[b].z+v3o->rotvertex[c].z)*(float)(1.0/3.0);
			}
			else // if (exact)
			{
				x=origo->x;
				y=origo->y;
				z=origo->z;
			}
		}
		else	// if (lightlist)
		{
				x=origo->x;
				y=origo->y;
				z=origo->z;
		}
		// End of lightlist checker...

		// R„kna ut yt-normalen...

		v1.x=v3o->rotvertex[b].x-v3o->rotvertex[a].x;
		v1.y=v3o->rotvertex[b].y-v3o->rotvertex[a].y;
		v1.z=v3o->rotvertex[b].z-v3o->rotvertex[a].z;

		v2.x=v3o->rotvertex[c].x-v3o->rotvertex[a].x;
		v2.y=v3o->rotvertex[c].y-v3o->rotvertex[a].y;
		v2.z=v3o->rotvertex[c].z-v3o->rotvertex[a].z;
		vector_crossprod (&v2,&v1,&nv);

		if (!lightlist)
		{
			// Default light...
			lv.x=0;
			lv.y=0;
			lv.z=-500;
			*l1=(LIGHTLEVELS-1) * vector_angle (&lv,&nv);
		}
		else
		{
			intens=0;
			// Check all lights in the global lighttable...
			for (l=lightlist;l!=NULL;l=l->next)
			{
				render_getvector (&lv,x,y,z,l->calcpos.x,l->calcpos.y,l->calcpos.z);
				i1=l->vlgh->intens*vector_angle (&lv,&nv);
				if (l->vlgh->flags & VLGHFLAG_DISTANCE)
				{
					// Use the falloff constant...
					len = l->vlgh->falloff * vector_length (&lv);
					// len *= len  // Ska den fortfarande vara kvadratisk?
					i1 -= len;
				}
				// Intensiteten kan INTE vara negativ...
				if (0>i1) i1=0;
				intens+=i1;		// Addera intensitet...  linj„r funktion...
			} // for
			if (intens>(LIGHTLEVELS-1)) intens=(LIGHTLEVELS-1);
			*l1=intens;
		} // else
		if (*l1<0) *l1=0;

  } else // If flatshadeing...

// Gourade shadeing loop...
// I princip samma sak som flatshadeing fast med 3 normaler...

	if (v3o->material[sf->material].flags & V3OMATERIALFLAG_GOURADE)
	{
		a=sf->v1,b=sf->v2,c=sf->v3;

		// Har vi n†gra ljusk„llor...
		if (!lightlist)
		{
			// Use default...
			lv.x=0;
			lv.y=0;
			lv.z=-500;
			*l1=(LIGHTLEVELS-1) * vector_angle (&lv,&v3o->rotnormal[a]);
			*l2=(LIGHTLEVELS-1) * vector_angle (&lv,&v3o->rotnormal[b]);
			*l3=(LIGHTLEVELS-1) * vector_angle (&lv,&v3o->rotnormal[c]);
		}
		else
		{
			// Vi har ljusk„llor...
			ltab[0]=ltab[1]=ltab[2]=i1=i2=i3=0;
			ptab[0]=a;
			ptab[1]=b;
			ptab[2]=c;
			// Loopa igenom allihopa... och ber„kna intensitet i varje punkt....
			for (l=lightlist;l!=NULL;l=l->next)
			{
				for (j=0;j<3;j++)
				{
					render_getvector (&lv,v3o->rotvertex[ptab[j]].x,v3o->rotvertex[ptab[j]].y,v3o->rotvertex[ptab[j]].z,l->calcpos.x,l->calcpos.y,l->calcpos.z);
					intens=l->vlgh->intens * vector_angle (&lv,&v3o->rotnormal[ptab[j]]);
					if (0>intens) intens=0;
					ltab[j]+=intens;
				}
			}
			for (j=0;j<3;j++) if (ltab[j]>(LIGHTLEVELS-1)) ltab[j]=(LIGHTLEVELS-1);
		}
		for (j=0;j<3;j++) if (ltab[j]<0) ltab[j]=0;
		*l1=ltab[0];
		*l2=ltab[1];
		*l3=ltab[2];
	}
	else
	{
		*l1=(float)0.95;
		*l2=(float)0.95;
		*l3=(float)0.95;
	}
}
/******************************************************************
 * Function  : ismongo                                            *
 * Input     : v3o *, surface *                                   *
 * Return    :                                                    *
 * Effect    : no effect                                          *
 ******************************************************************/
int render_inspace (RNDDRV *drv,VECTOR *v,float zmax,float zmin)
{
	zmax+=drv->camera->focus;
	zmin+=drv->camera->focus;

 	if( v->z >= zmax) return( FALSE);
	if( v->z <= zmin) return( FALSE);


	return( TRUE);
}
/******************************************************************
 * Function  : ishidden                                           *
 * Input     : v3o *, surface *                                   *
 * Return    : 0 if hidden 1 if visable                           *
 * Effect    : no effect                                          *
 * Comments  : Checks if a surface is hidden, supports dbl-side   *
 ******************************************************************/
int render_ishidden (V3O *obj, V3OSURFACE *sf)
{
	float x1,y1;
	float x2,y2;
	float f;

	x1=obj->projvertex[sf->v2].x - obj->projvertex[sf->v1].x;
	y1=obj->projvertex[sf->v2].y - obj->projvertex[sf->v1].y;

	x2=obj->projvertex[sf->v3].x - obj->projvertex[sf->v1].x;
	y2=obj->projvertex[sf->v3].y - obj->projvertex[sf->v1].y;

	f=(x1*y2-y1*x2);

	if (f>0) return 0;

	return 1;

}
