#include "../ssgl.h"

gl_vertex3d gl_getvec(float x, float y, float z)
{
	gl_vertex3d ret;
	ret.x = x;
	ret.y = y;
	ret.z = z;

	return ret;
}

void gl_rotate3D(gl_vertex3d *newvertex,gl_vertex3d oldvertex, float angle, int axis)
{
	float cosX, sinX, x, y, z;

	x = oldvertex.x;
	y = oldvertex.y;
	z = oldvertex.z;

	sinX = sin(angle);
	cosX = cos(angle);

	newvertex->x = x;
	newvertex->y = y;
	newvertex->z = z;
	
	switch(axis)
	{
		case 0:
			newvertex->y = y * cosX - z * sinX;
			newvertex->z = y * sinX + z * cosX;
			break;
		case 1:
			newvertex->x = x * cosX + z * sinX;
			newvertex->z = z * cosX - x * sinX;
			break;
		case 2:
			newvertex->x = x * cosX - y * sinX;
			newvertex->y = x * sinX + y * cosX;
			break;
	}
}

gl_vertex3d ssgl_3dto2d(gl_ctl ctl, int i)
{
	gl_vertex3d src = ctl.pos[i];
	src.x -= ctl.mvpos.x;
	src.y -= ctl.mvpos.y;
	src.z -= ctl.mvpos.z;
	gl_rotate3D(&src,src,ctl.rtpos.x,0);
	gl_rotate3D(&src,src,ctl.rtpos.y,1);
	gl_rotate3D(&src,src,ctl.rtpos.z,2);
	float fov = 1000.0f / (src.z + 1000.0f);
	return gl_getvec((src.x*fov*(ctl.pixels.w/320)+(ctl.pixels.w/2)),(src.y*fov*(ctl.pixels.h/240)+(ctl.pixels.h/2)),src.z);
}

gl_ctl gl_init(uint32 *p, int w, int h)
{
	gl_ctl ret;
	ret.nowvert = 0;
	ret.pixels.w = w;
	ret.pixels.h = h;
	ret.pixels.p = p;
	ret.pixels.zfar = (float *)gl_malloc(w*h*sizeof(float));
	ret.mvpos = gl_getvec(0,0,0);
	ret.rtpos = gl_getvec(0,0,0);
	return ret;
}

void gl_vertex(gl_ctl *ctl, float x, float y, float z)
{
	ctl->pos[ctl->nowvert].x = x;
	ctl->pos[ctl->nowvert].y = y;
	ctl->pos[ctl->nowvert].z = z;
	ctl->nowvert++;
}

void gl_color(gl_ctl *ctl, uint32 color)
{
	ctl->color = color;
}

void gl_clear(gl_ctl *ctl)
{
	memset(ctl->pixels.zfar,0,ctl->pixels.h*ctl->pixels.w*sizeof(float));
}

void gl_render(gl_ctl *ctl, int flags)
{
	gl_vertex3d cpos[256];
	int i;
	uint32 tmpcol;
	for(i = 0; i < ctl->nowvert; i++) {
		cpos[i] = ssgl_3dto2d(*ctl,i);
	}

	tmpcol = ctl->color;

	if(flags == GL_FLSHADE) {
		gl_vertex3d vv1,vv2,vv3;

		vv1.x = cpos[1].x - cpos[0].x;
		vv1.y = cpos[1].y - cpos[0].y;
		vv1.z = cpos[1].z - cpos[0].z;

		vv2.x = cpos[2].x - cpos[1].x;
		vv2.y = cpos[2].y - cpos[1].y;
		vv2.z = cpos[2].z - cpos[1].z;

		gl_vec3cross(&vv3,&vv1,&vv2);
		gl_vec3normalize(&vv3,&vv3);

		if(vv3.z < 0.0f) {
			ctl->nowvert=0;
			return;
		}

		int r, g, b;

		gl_fromcolor(ctl->color,&r,&g,&b);

		r*=vv3.z;
		g*=vv3.z;
		b*=vv3.z;

		ctl->color = gl_tocolor(r,g,b);
	}

	if(flags == GL_WFRAME) gl_renderpolywire(*ctl,cpos,ctl->nowvert);
	else gl_renderpolyfil(*ctl,cpos,ctl->nowvert);
	ctl->color=tmpcol;
	ctl->nowvert=0;
}

void gl_vec3normalize(gl_vertex3d *pout, gl_vertex3d *pv)
{
	float len;
	float x, y, z;

	x = (pv->x);
	y = (pv->y);
	z = (pv->z);
	len = sqrt(x * x + y * y + z * z);

	if(len < (1e-6)) return;

	len = 1.0 / len;
	x *= len;
	y *= len;
	z *= len;

	pout->x = x;
	pout->y = y;
	pout->z = z;

	return;
}

void gl_vec3cross(gl_vertex3d *pout, gl_vertex3d *pv1, gl_vertex3d *pv2)
{
	gl_vertex3d vec;
	float x1, y1, z1, x2, y2, z2;
	
	x1 = (pv1->x);
	y1 = (pv1->y);
	z1 = (pv1->z);
	x2 = (pv2->x);
	y2 = (pv2->y);
	z2 = (pv2->z);
 
	vec.x = (y1 * z2 - z1 * y2);
	vec.y = (z1 * x2 - x1 * z2);
	vec.z = (x1 * y2 - y1 * x2);
	*pout = vec;
}

void gl_line(uint32 *vram, int xsize, int x0, int y0, int x1, int y1, int col)
{
	int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
	int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; 
	int err = (dx>dy ? dx : -dy)/2, e2;
	int a = (dy<dx)? (dx):(dy);
 
	for(;;){
		vram[y0 * xsize + x0] = col;
		if (x0==x1 && y0==y1) break;
		e2 = err;
		if (e2 >-dx) { err -= dy; x0 += sx; }
		if (e2 < dy) { err += dx; y0 += sy; }
	}
}

void gl_renderpolywire(gl_ctl ctl,gl_vertex3d *lpVertex,int VerCou)
{
	int i;
	
	for(i = 0; i < VerCou; i++) {
		if(i == VerCou - 1) gl_line(ctl.pixels.p, ctl.pixels.w, lpVertex[i].x,  lpVertex[i].y,  lpVertex[0].x,  lpVertex[0].y, ctl.color);
		else gl_line(ctl.pixels.p, ctl.pixels.w, lpVertex[i].x,  lpVertex[i].y,  lpVertex[i+1].x,  lpVertex[i+1].y, ctl.color);
	}
}

void gl_renderpolyfil(gl_ctl ctl,gl_vertex3d *lpVertex,int VerCou)
{
	int m,anum,bnum,oldm;
	double maxy,miny;
	double x1,x2;
	double dd;
	double fy;
	int i,x,y;
	int sy,ey;
	double dxA,dxB;
	gl_vertex3d *lpVer;

	double xx1,xx2;
	int xs1,xs2;
	double tmp;

	double wid;
	double z1,z2;
	double dzA,dzB;
	double zz1,zz2;
	double dz;

	if(VerCou<3) return;

	//Y座標値で最大・最小のものを求める
	m=0;
	lpVer=lpVertex;
	maxy=miny=lpVer->y;
	lpVer++;
	for(i=1;i<VerCou;i++){
		fy=lpVer->y;
		if(miny>fy){
			m=i;
			miny=fy;
		}
		if(maxy<fy) maxy=fy;
		lpVer++;
	}
	sy=(int)(miny+0.5);
	ey=(int)(maxy+0.5);
	if(sy==ey) return;

	//左側にある頂点と増加値の計算
	anum=bnum=m;
	do{
		anum++;
		if(anum>VerCou-1) anum=0;
	} while(lpVertex[anum].y<sy+0.5);
	m=anum-1;if(m<0) m=VerCou-1;

	dd=lpVertex[anum].y-lpVertex[m].y;
	dxA=(lpVertex[anum].x-lpVertex[m].x)/dd;
	dzA=(lpVertex[anum].z-lpVertex[m].z)/dd;
	x1=lpVertex[m].x;
	z1=lpVertex[m].z;

	//右側にある頂点と増加値の計算
	do{
		bnum--;
		if(bnum<0) bnum=VerCou-1;
	} while(lpVertex[bnum].y<sy+0.5);
	m=bnum+1;if(m>VerCou-1) m=0;

	dd=lpVertex[bnum].y-lpVertex[m].y;
	dxB=(lpVertex[bnum].x-lpVertex[m].x)/dd;
	dzB=(lpVertex[bnum].z-lpVertex[m].z)/dd;
	x2=lpVertex[m].x;
	z2=lpVertex[m].z;

	for(y=sy;y<ey;y++){
		//左側稜線の処理
		if(lpVertex[anum].y<y+0.5){
			do{
				anum++;
				if(anum>VerCou-1) anum=0;
			} while(lpVertex[anum].y<y+0.5);
			oldm=anum-1;if(oldm<0) oldm=VerCou-1;

			dd=lpVertex[anum].y-lpVertex[oldm].y;
			dxA=(lpVertex[anum].x-lpVertex[oldm].x)/dd;
			dzA=(lpVertex[anum].z-lpVertex[oldm].z)/dd;
			x1=lpVertex[oldm].x;
			z1=lpVertex[oldm].z;
		}
		//右稜線の処理
		if(lpVertex[bnum].y<y+0.5){
			do{
				bnum--;
				if(bnum<0) bnum=VerCou-1;
			} while(lpVertex[bnum].y<y+0.5);
			oldm=bnum+1;if(oldm>VerCou-1) oldm=0;

			dd=lpVertex[bnum].y-lpVertex[oldm].y;
			dxB=(lpVertex[bnum].x-lpVertex[oldm].x)/dd;
			dzB=(lpVertex[bnum].z-lpVertex[oldm].z)/dd;
			x2=lpVertex[oldm].x;
			z2=lpVertex[oldm].z;
		}

		//X軸方向の描画
		xx1=x1;xx2=x2;
		zz1=z1;zz2=z2;

		if(xx2<xx1){
			tmp=xx1;xx1=xx2;xx2=tmp;
			tmp=zz1;zz1=zz2;zz2=tmp;
		}
		xs1=(int)(xx1+0.5);
		xs2=(int)(xx2+0.5);

		wid=xx2-xx1;
		if(wid>0.0){
			//Z方向の増加値
			//（Xが1増加するたびにZはどれだけ増加するか）
			dz=(zz2-zz1)/wid;
		}

		//１ライン分を描画
		for(x=xs1;x<xs2;x++){
			int col = ctl.color;
			
			if(x >= 0 && x < ctl.pixels.w && y >= 0 && y < ctl.pixels.h) {
				float z = ctl.pixels.zfar[y * ctl.pixels.w + x];
				if(z == 0) z = 10000.0f;
				if(zz1 < z) ctl.pixels.p[y * ctl.pixels.w + x] = col;
				ctl.pixels.zfar[y * ctl.pixels.w + x] = zz1;
			}

			zz1+=dz;		//Z方向の増加値の加算
		}

		x1+=dxA;x2+=dxB;	//X方向の増加値の加算
		z1+=dzA;z2+=dzB;	//Z方向の増加値の加算
	}
}
