/*-----------------------------------------------------------------------------
 * File: wfrnd.c
 * Written by: Fredrik Kling & Alexander Boczar, 1997-08-15
 * Description: Wireframe Scene renderer...
 *
 * Updates:
 * -- Date -- | ----- Name ----- |-- Did what....
 * 1997-08-15 | Fredrik Kling    | Implementation
 *
 * Kommentar:
 *
 * Todo:
 *	 Z-clipper
 *
 *   Driver uppbyggnad, f”rslag till functioner:
 *
 *    setviewport (Camera, Buffer)
 *  	prepare (V3D *)
 *    render (V3D *)
 *    putobject (Object *)
 *    putobject_pos (OBJECT *,VECTOR *)
 *    putobject_ang (OBJECT *, int angle[3]);
 *    putobject_pos_ang (OBJECT *, VECTOR *, int angle[3]);
 *
 *  F”rslag till variabler:
 *   CAMERA *activecamera;
 *   BUFF *buff;
 *   V3D *scene;
 *
 *   En viss driver beh”ver ju inte utnyttja all info..
 *   Drivers som jag vill ha:
 *			scpRND	- ScenPlay_rendering
 *      rayRND  - Raytrace output..  anv„nd roberts tracer...
 *			objRND  - Ren object rendrare... som "old fashion" 3d-motorer...
 *      wfRND   - Wireframes rendrare...
 *
 *----------------------------------------------------------------------------*/
#include "formats/v3d.h"
#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 "polydraw.h"
#include "drivers/drv16.h"
#include "system/xmath.h"
#include "misc/col.h"
#include "misc/spline.h"
//#include "debug/mono.h"

#include "render/rnd_util.h"
#include "render/rnddrv.h"

//#define DBS 1

static V3D *currentscene;

static VECTOR camvec;
//static BUFF *buff;

static int numobj,numlight,numpoly,numplot;

// Varf”r extern?
extern RNDDRV wfDRV;

static int col_yellow,col_red, col_green, col_blue;


static void setviewport (BUFF *buff, CAMERA *camera)
{
	RGB y = {63<<2,63<<2,0},r = {63<<2,0,0}, g = {0,63<<2,0}, b = {0,0,63<<2};
	wfDRV.camera = camera;
	wfDRV.buff = buff;

	col_yellow = findcolor (buff->drv->palette,y);
	col_red = findcolor (buff->drv->palette,r);
	col_green = findcolor (buff->drv->palette,g);
	col_blue = findcolor (buff->drv->palette,b);
}

/******************************************************************
 * Function  : v3d_prepare                                        *
 * Input     : V3D *                                              *
 * Return    : nothing                                            *
 * Effect    : Prepares all objects in scene..  lights, v3o's     *
 ******************************************************************/
static prepare (V3D *v3d)
{
	OBJECT *lo;
	VECTOR lvd;

  numobj = numlight = numpoly = numplot = 0;

	currentscene = v3d;

	// Animate Camera
	spline_calcframe( wfDRV.camera->anim, v3d->framecounter);

	// Animate Lights
	for ( lo=v3d->lightlist; lo!=NULL; lo=lo->next)
		spline_calcframe( lo->anim, v3d->framecounter);

	// Animate Objects
	for ( lo=v3d->obj; lo!=NULL; lo=lo->next)
		spline_calcframe( lo->anim, v3d->framecounter);

	// Buildcamera vector...

	if (wfDRV.camera->flags & CAMERAFLAG_OBJTARGET)
	{
		VECTOR cupv;

		render_getvector(
			&camvec,
			wfDRV.camera->anim->value[ANIM_POS_X],
			wfDRV.camera->anim->value[ANIM_POS_Y],
			wfDRV.camera->anim->value[ANIM_POS_Z],
			wfDRV.camera->obj_target->anim->value[ANIM_POS_X],
			wfDRV.camera->obj_target->anim->value[ANIM_POS_Y],
			wfDRV.camera->obj_target->anim->value[ANIM_POS_Z]);

		// Camera Banking...
		cupv.x = sintable[((int)wfDRV.camera->anim->value[ANIM_ANG_Z] & (SINESIZE - 1))];
		cupv.y = -sintable[((int)wfDRV.camera->anim->value[ANIM_ANG_Z] & (SINESIZE - 1)) + (SINESIZE / 4)];
		cupv.z = 0;

		matrixfromvectors (&camvec, &cupv, &wfDRV.camera->rm);
	}
	else
	if (wfDRV.camera->flags & CAMERAFLAG_POSTARGET)
	{
		VECTOR cupv;
		render_getvector(
			&camvec,
			wfDRV.camera->anim->value[ANIM_POS_X],
			wfDRV.camera->anim->value[ANIM_POS_Y],
			wfDRV.camera->anim->value[ANIM_POS_Z],
			wfDRV.camera->anim->value[ANIM_TARGET_X],
			wfDRV.camera->anim->value[ANIM_TARGET_Y],
			wfDRV.camera->anim->value[ANIM_TARGET_Z]);
		// Camera Banking...
		cupv.x = sintable[((int)wfDRV.camera->anim->value[ANIM_ANG_Z] & (SINESIZE - 1))];
		cupv.y = -sintable[((int)wfDRV.camera->anim->value[ANIM_ANG_Z] & (SINESIZE - 1)) + (SINESIZE / 4)];
		cupv.z = 0;
		matrixfromvectors (&camvec, &cupv, &wfDRV.camera->rm);
	}
	else
	{
		buildrotationmatrix(
		 	(int)wfDRV.camera->anim->value[ANIM_ANG_X] & (SINESIZE - 1),
			(int)wfDRV.camera->anim->value[ANIM_ANG_Y] & (SINESIZE - 1),
			(int)wfDRV.camera->anim->value[ANIM_ANG_Z] & (SINESIZE - 1),
			&wfDRV.camera->rm);
	}

	// Prepare all lights in lightlist...

	for (lo=v3d->lightlist;lo!=NULL;lo=lo->next)
	{
		// Fixa dem i relation till världen och restan av livet...
  	lvd.x=(lo->anim->value[ANIM_POS_X] - wfDRV.camera->anim->value[ANIM_POS_X]);
  	lvd.y=(lo->anim->value[ANIM_POS_Y] - wfDRV.camera->anim->value[ANIM_POS_Y]);
	  lvd.z=(lo->anim->value[ANIM_POS_Z] - wfDRV.camera->anim->value[ANIM_POS_Z]);
		// Rotera
  	vecmul (&wfDRV.camera->rm, &lvd, &lo->calcpos);


	// Plot lightsources

		lo->calcpos.z += wfDRV.camera->focus;

		if (lo->calcpos.z>0)
		{
			XYZ p;
			float t = wfDRV.camera->focus/(lo->calcpos.z-wfDRV.camera->focus);
			p.x = lo->calcpos.x*t;
			p.y = lo->calcpos.y*t;
      p.x += wfDRV.buff->xorigo;
      p.y += wfDRV.buff->yorigo;
			p.z = lo->calcpos.z;
			p.l = 32;
      wfDRV.buff->drv->plot (p,col_yellow);
		}
		numlight++;
	}
}
/******************************************************************
 * Function  : v3o_render                                         *
 * Input     : OBJECT *, VECTOR *, MATRIX *, float                *
 * Return    : nothing                                            *
 * Effect    : Rotates an object then draws in the viewport       *
 * Comments  : This one recurses if subojects are found...        *
 *						 Uses inline asm, not checked with vtune...         *
 ******************************************************************/
//void v3o_render (V3O *obj, VECTOR *pos, MATRIX *rm, OBJECT *lightlist)
static void v3o_render (V3O *obj, VECTOR *pos, MATRIX *rm)
{
	VECTOR realorigo = *pos;
	int i;
  float xo,yo;
	float s,t;
	XYZ p1,p2;
	int a,b;

	//realorigo.z += wfDRV.camera->focus;

	s = wfDRV.scale;
  xo = wfDRV.buff->xorigo;
  yo = wfDRV.buff->yorigo;

	// Rotate and project object as wanted... no in one single loop..
	// Using inline assembler for speedups...  not checked for stalls etc..

	for (i=0;i<obj->numvertex;i++)
	{
		// Functions are inline...
		vecmul (rm,&obj->orgvertex[i],&obj->rotvertex[i]);
		vecmul (rm,&obj->orgnormal[i],&obj->rotnormal[i]);

		obj->rotvertex[i].x+=pos->x;
		obj->rotvertex[i].y+=pos->y;
		obj->rotvertex[i].z+=pos->z + wfDRV.camera->focus;
		// Convert to inline asm ala watcom 11.0 <- hmm...  kanske inte iaf...
/*
#if defined (__X86__)
{
	float focus=wfDRV.camera->focus,xp=pos->x,yp=pos->y,zp=pos->z;
extern float f_project (VECTOR *,VECTOR *);
#pragma aux f_project "*" = \
	"fld 		dword ptr [esi+8]",\
	"fld		focus",\
	"fxch 	st(1)",\
	"fdivp 	st(1),st",\
\
	"fld		dword ptr [esi]",\
	"fmul		st(0),st(1)",\
	"fld		dword ptr [esi+4]",\
	"fmul		st(0),st(2)",\
	"fstp		dword ptr [edi+4]",\
	"fstp		dword ptr [edi]",\
	parm [esi] [edi] value[8087];
	// doit it..

	f_project (&obj->rotvertex[i],&obj->projvertex[i]);
}
#else
{
*/
		t = s*(wfDRV.camera->focus/(obj->rotvertex[i].z-wfDRV.camera->focus));
		obj->projvertex[i].x = obj->rotvertex[i].x*t;
		obj->projvertex[i].y = obj->rotvertex[i].y*t;
		obj->projvertex[i].z = obj->rotvertex[i].z;
/*
}
#endif
*/
		obj->projvertex[i].x+=xo;
		obj->projvertex[i].y+=yo;

	}

// Draw the fucking(?) stuff...
	for (i=0;i<obj->numsurface;i++)
	{
		if( obj->surface[i].flags & V3OSURFACEFLAG_POLY)
		{
			if( render_inspace(&wfDRV,&obj->projvertex[obj->surface[i].v1],currentscene->zmax,currentscene->zmin) &&
					render_inspace(&wfDRV,&obj->projvertex[obj->surface[i].v2],currentscene->zmax,currentscene->zmin) &&
					render_inspace(&wfDRV,&obj->projvertex[obj->surface[i].v3],currentscene->zmax,currentscene->zmin) &&
					!render_ishidden(obj,&obj->surface[i]))
			{
				wire_polygon (wfDRV.buff->drv, &obj->surface[i],obj->projvertex);
				numpoly++;
			}
		}
		else if( obj->surface[i].flags & V3OSURFACEFLAG_LINE)
		{
			if( render_inspace(&wfDRV, &obj->projvertex[obj->surface[i].v1],currentscene->zmax,currentscene->zmin) &&
					render_inspace(&wfDRV, &obj->projvertex[obj->surface[i].v2],currentscene->zmax,currentscene->zmin))
			{
				a=obj->surface[i].v1;
				b=obj->surface[i].v2;
				p1.x=obj->projvertex[a].x;
				p1.y=obj->projvertex[a].y;
				p1.z=obj->projvertex[a].z;
				p1.l=LIGHTLEVELS - 1;
				p2.x=obj->projvertex[b].x;
				p2.y=obj->projvertex[b].y;
				p2.z=obj->projvertex[b].z;
				p2.l=LIGHTLEVELS - 1;
        wfDRV.buff->drv->line (p1,p2,obj->material[obj->surface[i].material].ci);
			}
		}
		else if( obj->surface[i].flags & V3OSURFACEFLAG_POINT)
		{
			if( render_inspace(&wfDRV, &obj->projvertex[obj->surface[i].v1],currentscene->zmax,currentscene->zmin))
			{
			  p1.x=obj->projvertex[obj->surface[i].v1].x;
			  p1.y=obj->projvertex[obj->surface[i].v1].y;
			  p1.z=obj->projvertex[obj->surface[i].v1].z;
				p1.l=LIGHTLEVELS - 1;
        wfDRV.buff->drv->plot (p1,obj->material[obj->surface[i].material].ci);
        numplot++;
      }
		}
	}
	numobj++;
}
/******************************************************************
 * Function  : object_draw                                        *
 * Input     : OBJECT *                                           *
 * Return    : nothing                                            *
 * Effect    : Draws an object in the scene..                     *
 ******************************************************************/
static void object_render (OBJECT *obj)
{
	MATRIX rm;
	VECTOR ovd;
	VECTOR rv;

	//  Check if parent object is available...
	if (obj->parent!=NULL)
	{
		ovd.x = obj->anim->value[ANIM_POS_X];
		ovd.y = obj->anim->value[ANIM_POS_Y];
		ovd.z = obj->anim->value[ANIM_POS_Z];
		vecmul (&obj->parent->rm, &ovd, &obj->calcpos);
		obj->calcpos.x += obj->parent->calcpos.x;
		obj->calcpos.y += obj->parent->calcpos.y;
		obj->calcpos.z += obj->parent->calcpos.z;
	} else
		{
			// Otherwise we have a clear root object...
			ovd.x=(obj->anim->value[ANIM_POS_X] - wfDRV.camera->anim->value[ANIM_POS_X]);
  		ovd.y=(obj->anim->value[ANIM_POS_Y] - wfDRV.camera->anim->value[ANIM_POS_Y]);
  		ovd.z=(obj->anim->value[ANIM_POS_Z] - wfDRV.camera->anim->value[ANIM_POS_Z]);
  		vecmul (&wfDRV.camera->rm, &ovd, &obj->calcpos);
		}

	switch (obj->type)
	{
		case T_V3O :	//if (render_checkspherical (&wfDRV,obj))
									{
										// Fixa till vinklarna...
										if (obj->flags & OBJFLAG_VECROT)
										{
											VECTOR cupv;

											cupv.x = 0, cupv.y = -1, cupv.z = 0;
											rv.x = obj->anim->value[ANIM_ANG_X];
											rv.y = obj->anim->value[ANIM_ANG_Y];
											rv.z = obj->anim->value[ANIM_ANG_Z];
											matrixfromvectors (&rv, &cupv, &rm);
										} else
											{
												obj->anim->value[ANIM_ANG_X]=(int)(obj->anim->value[ANIM_ANG_X] + obj->angleadd[0]) & (SINESIZE - 1);
												obj->anim->value[ANIM_ANG_Y]=(int)(obj->anim->value[ANIM_ANG_Y] + obj->angleadd[1]) & (SINESIZE - 1);
												obj->anim->value[ANIM_ANG_Z]=(int)(obj->anim->value[ANIM_ANG_Z] + obj->angleadd[2]) & (SINESIZE - 1);
												buildrotationmatrix (obj->anim->value[ANIM_ANG_X],obj->anim->value[ANIM_ANG_Y],obj->anim->value[ANIM_ANG_Z],&rm);
											}


										// Ta med kamera rotation.... eller parent object...
										if (obj->parent!=NULL) matmul (&obj->parent->rm,&rm,&obj->rm);
										  else matmul (&wfDRV.camera->rm,&rm,&obj->rm);
										// Rendrera
								  	v3o_render (obj->v3o,&obj->calcpos,&obj->rm);
									}
									break;
		case T_NULL:
									break;
		case T_VIO :
								 break;
	}

}
/******************************************************************
 * Function  : v3d_render                                         *
 * Input     : V3D *                                              *
 * Return    : nothing                                            *
 * Effect    : Renders the turbo ultra kewl scene with joy....    *
 ******************************************************************/
static void render (V3D *v3d)
{
	OBJECT *o;
	int i;

	for (i=0,o=v3d->obj;o!=NULL;o=o->next,i++)
	{
		object_render (o);
	}
  //debug_printfxy( 0, 23, "Frame: %d, Objects: %d, Lights: %d, Polygons: %d, Plots: %d     \n", (int)v3d->framecounter, numobj, numlight, numpoly, numplot);
}

RNDDRV wfDRV =
{
	"Wireframe Sceneplayer for .V3D files, V1.0",
	NULL,		// Buffer
	NULL,		// Camera
	1.0,
	setviewport,
	prepare,
	render,
	object_render,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

