/*-----------------------------------------------------------------------------
 * File: objrnd.c
 * Written by: Fredrik Kling & Alexander Boczar, 1997-09-30
 * Description: Rendring driver for plain objects...   very basic device...
 *
 * Updates:
 * -- Date -- | ----- Name ----- |-- Did what....
 * 1997-09-30 | Fredrik Kling    | Implementation
 *
 * Kommentar:
 *
 *   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/drv8.h"
#include "system/xmath.h"
#include "misc/spline.h"
//#include "debug/mono.h"

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


static int numobj,numpoly,numplot;
// A must for some rendering utility functions....
static CAMERA smp_cam;
extern RNDDRV objDRV;

static void setviewport (BUFF *buff, CAMERA *camera)
{
	if (camera!=NULL) objDRV.camera = camera;
		else
		{
			objDRV.camera = &smp_cam;
			smp_cam.focus = 400;
		}
	objDRV.buff = buff;
}


/******************************************************************
 * 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..*
 ******************************************************************/
static void v3o_render (V3O *obj, VECTOR *pos, MATRIX *rm)
{
	VECTOR realorigo = *pos;
	int i,flags;
  float xo,yo,s;
	float l1,l2,l3,t;
	XYZ p1,p2;
	int a,b;

	realorigo.z += 400;

	s = objDRV.scale;

  xo = objDRV.buff->xorigo;
  yo = objDRV.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 + 400;

		t = s*(400/(obj->rotvertex[i].z-400));

		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(&objDRV,&obj->projvertex[obj->surface[i].v1],10000,0) &&
					render_inspace(&objDRV,&obj->projvertex[obj->surface[i].v2],10000,0) &&
					render_inspace(&objDRV,&obj->projvertex[obj->surface[i].v3],10000,0) &&
					!render_ishidden(obj,&obj->surface[i]))
			{
				flags = obj->material[obj->surface[i].material].flags;
				switch (flags & (V3OMATERIALFLAG_TEXTURE | V3OMATERIALFLAG_FLAT | V3OMATERIALFLAG_GOURADE))
				{
					case V3OMATERIALFLAG_TEXTURE :
              texture_polygon (objDRV.buff->drv, &obj->surface[i],obj->projvertex,&obj->texture[obj->material[obj->surface[i].material].texture]);
							break;
					case	(V3OMATERIALFLAG_TEXTURE+V3OMATERIALFLAG_FLAT) :
							render_calclight (&objDRV,obj,&obj->surface[i],NULL,&l1,&l2,&l3,&realorigo);
              texture_flat_polygon (objDRV.buff->drv, &obj->surface[i],obj->projvertex,l1,&obj->texture[obj->material[obj->surface[i].material].texture]);
							break;
					case (V3OMATERIALFLAG_TEXTURE+V3OMATERIALFLAG_GOURADE) :
							//render_calclight (&objDRV,obj,&obj->surface[i],currentscene->lightlist,&l1,&l2,&l3,&realorigo);
							render_calclight (&objDRV,obj,&obj->surface[i],NULL,&l1,&l2,&l3,&realorigo);
              texture_gourade_polygon (objDRV.buff->drv, &obj->surface[i],obj->projvertex,l1,l2,l3,&obj->texture[obj->material[obj->surface[i].material].texture]);
							break;
					case V3OMATERIALFLAG_FLAT :
		        	render_calclight (&objDRV,obj,&obj->surface[i],NULL,&l1,&l2,&l3,&realorigo);
              flat_polygon (objDRV.buff->drv, &obj->surface[i],obj->projvertex,obj->material[obj->surface[i].material].ci,l1);
							break;
					case V3OMATERIALFLAG_GOURADE :
					  	render_calclight (&objDRV,obj,&obj->surface[i],NULL,&l1,&l2,&l3,&realorigo);
              gourade_polygon (objDRV.buff->drv, &obj->surface[i],obj->projvertex,obj->material[obj->surface[i].material].ci,l1,l2,l3);
							break;
				}
				numpoly++;
			}
		}
		else if( obj->surface[i].flags & V3OSURFACEFLAG_LINE)
		{
			if( render_inspace(&objDRV, &obj->projvertex[obj->surface[i].v1],10000,0) &&
					render_inspace(&objDRV, &obj->projvertex[obj->surface[i].v2],10000,0))
			{
				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;
        objDRV.buff->drv->line (p1,p2,obj->material[obj->surface[i].material].ci);
			}
		}
		else if( obj->surface[i].flags & V3OSURFACEFLAG_POINT)
		{
			if( render_inspace(&objDRV, &obj->projvertex[obj->surface[i].v1],10000,0))
			{
			  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;
        objDRV.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 po (OBJECT *obj)
{
	MATRIX rm;

	switch (obj->type)
	{
		case T_V3O :	if (render_checkspherical (&objDRV,obj))
									{
										// Fixa till vinklarna...
										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);
										// Rendrera
								  	v3o_render (obj->v3o,&obj->calcpos,&obj->rm);
									}
									break;
		case T_NULL:
									break;
		case T_VIO :
								 break;
	}

}

/******************************************************************
 * Function  : smp_obj_render                                     *
 * Input     : OBJECT *, VECTOR *, int, int, int                  *
 * Return    : nothing                                            *
 * Effect    : Rotates an object then draws in the viewport       *
 * Comments  : Simplified object rendering routine...             *
 *						                                                    *
 ******************************************************************/
static void po_p (OBJECT *obj, VECTOR *p)
{
		v3o_render (obj->v3o,p,&obj->rm);
}
static void po_a (OBJECT *obj, int xv, int yv, int zv)
{
		buildrotationmatrix((int)xv & (SINESIZE - 1),
		 										(int)yv & (SINESIZE - 1),
												(int)zv & (SINESIZE - 1),
												&obj->rm);

		v3o_render (obj->v3o,&obj->calcpos,&obj->rm);
}
static void po_m (OBJECT *obj, MATRIX *m)
{
		v3o_render (obj->v3o,&obj->calcpos,m);
}
static void po_p_a (OBJECT *obj, VECTOR *pos, int xv, int yv, int zv)
{
		buildrotationmatrix((int)xv & (SINESIZE - 1),
		 										(int)yv & (SINESIZE - 1),
												(int)zv & (SINESIZE - 1),
												&obj->rm);

		v3o_render (obj->v3o,pos,&obj->rm);
}
static void po_p_m (OBJECT *obj, VECTOR *pos, MATRIX *m)
{
		v3o_render (obj->v3o,pos,m);
}

RNDDRV objDRV =
{
	"objectplayer for objects (3d-object, bitmaps, soundfx) files, V1.0",
	NULL,		// Buffer
	NULL,		// Camera
	1.0,		// Scale value..  unused by most drivers:  1.0 refers to 320x200 or Resoloution 1 in a lws-file..
	setviewport,
	NULL,
	NULL,
	po,
	po_p,
	po_a,
	po_m,
	po_p_a,
	po_p_m
};
/*
	void (* putobject)(OBJECT *);
	void (* putobject_pos)(OBJECT *, VECTOR *);
	void (* putobject_ang)(OBJECT *, int, int, int);
	void (* putobject_mat)(OBJECT *, MATRIX *);
	void (* putobject_pos_ang) (OBJECT *, VECTOR *, int, int, int);
	void (* putobject_pos_mat) (OBJECT *, VECTOR *, MATRIX *);
 */