
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "render.h"
#include "globals.h"

#define XS XRES
#define YS YRES
#define ONE 4096

static int  zbuf[XS*YS],
            matrix[16]={ONE,0,0,0,
                        0,ONE,0,0,
                        0,0,ONE,0,
                        0,0,0,ONE};

/* Backface culling ("takanaaman kulli" in Finnish) */
static int cull(int x1,int y1,int x2,int y2,int x3,int y3)
{
    int nx,ny;

    x2-=x1; y2-=y1;
    x3-=x1; y3-=y1;
    nx=-y3; ny=x3;

    if(nx*x2+ny*y2>0)
        return(0);
    else
        return(1);
}

static void transform(int *mtx,int *src,int *dest)
{
    dest[0]=mtx[0]*src[0]/ONE+mtx[1]*src[1]/ONE+mtx[2]*src[2]/ONE+mtx[3]*16;
    dest[1]=mtx[4]*src[0]/ONE+mtx[5]*src[1]/ONE+mtx[6]*src[2]/ONE+mtx[7]*16;
    dest[2]=mtx[8]*src[0]/ONE+mtx[9]*src[1]/ONE+mtx[10]*src[2]/ONE+mtx[11]*16;
}

/* Multiply a by b */
static void mulmat(int *a,int *b)
{
    int x,y,i,tmp[16];

    memset(tmp,0,16*sizeof(int));

    for(y=0;y<4;y++)
        for(x=0;x<4;x++)
            for(i=0;i<4;i++)
                tmp[y*4+x]+=a[y*4+i]*b[x+i*4]/ONE;

    memcpy(a,tmp,16*sizeof(int));
}

static void triangel(int *v,unsigned *dest,unsigned char *dcolor)
{
    int maxy,miny,diff,tmp,n,i, // diffz,
        x1,x2,y1,y2;//,z1,z2;
    static int right[YS],left[YS];
               //rz[YS],lz[YS];
    
    maxy=-1000000;
    miny=1000000;
    for(n=0;n<3;n++)
    {
        if(v[n*3+1]<miny)
            miny=v[n*3+1];
        if(v[n*3+1]>maxy)
            maxy=v[n*3+1];
    }
    if(maxy<0 || miny>=YS)
        return;

    if(miny<0) miny=0;
    if(maxy>=YS) maxy=YS-1;

    for(n=miny;n<=maxy;n++)
    {
        right[n]=-100000000;
        left[n]=100000000;
    }

    for(n=0;n<3;n++)
    {
        x1=v[n*3]; x2=(n==2)? v[0]:v[n*3+3];
        y1=v[n*3+1]; y2=(n==2)? v[1]:v[n*3+4];
        //z1=v[n*3+2]; z2=(n==2)? v[2]:v[n*3+5];

        if(y2==y1)
            continue;
        if(y2<y1)
        {
            tmp=x1; x1=x2; x2=tmp;
            tmp=y1; y1=y2; y2=tmp;
            //tmp=z1; z1=z2; z2=tmp;
        }

        x1*=65536;
        x2*=65536;
        diff=(x2-x1)/(y2-y1);
        //diffz=(z2-z1)/(y2-y1);

        for(i=y1;i<=y2;i++)
        {
            if(i<0)
            {
                x1+=diff;
                //z1+=diffz;
                continue;
            }
            if(i>=YS)
                break;

            if(x1<left[i])
            {
                left[i]=x1;
                //lz[i]=z1;
            }
            if(x1>right[i])
            {
                right[i]=x1;
                //rz[i]=z1;
            }
            x1+=diff;
            //z1+=diffz;
        }
    }

    for(n=miny;n<=maxy;n++)
    {
        if(right[n]<0 || left[n]>=XS*65536)
            continue;
        if(left[n]<0) left[n]=0;
        if(right[n]>=XS*65536) right[n]=(XS-1)*65536;

        /* The missing stuff was about colors & zbuf */
        //z1=lz[n];
        //z2=rz[n];
        //diff=right[n]/65536-left[n]/65536+1;
        //diffz=(z2-z1)/diff;
        for(i=left[n]/65536;i<=right[n]/65536;i++)
        {
            dest[n*XS+i]=0;
            /*if(z1>zbuf[n*XS+i])
            {
                //dest[n*XS+i]=z1*128/65536+128;
                dest[n*XS+i]=dcolor[(-z1>>10)&255];
                zbuf[n*XS+i]=z1;
            }
            z1+=diffz;*/
        }
    }
}

void bare_render(int *data,unsigned *dest,unsigned char *dcolor)
{
    int n,i,*v,*f,faces,tmp[9];
    static int trans[1000*3];

    v=&data[1];
    f=&data[2+data[0]*3];
    faces=data[1+data[0]*3];

    /* Ye transfermetions go 'ere sailor! */
    for(n=0;n<data[0]*3;n+=3)
        transform(matrix,&v[n],&trans[n]);
    v=trans;

    /* Projection & screen coordinate transformation */
    for(n=0;n<data[0]*3;n+=3)
    {
        //z=v[n+2];
        trans[n+0]=v[n+0]*3*YS/abs(v[n+2])+XS/2;
        trans[n+1]=-v[n+1]*3*YS/abs(v[n+2])+YS/2;
        trans[n+2]=v[n+2];
    }

    for(n=0;n<faces;n++)
    {
        for(i=0;i<3;i++)
        {
            tmp[i*3+0]=trans[f[n*3+i]*3+0];
            tmp[i*3+1]=trans[f[n*3+i]*3+1];
            tmp[i*3+2]=trans[f[n*3+i]*3+2];
        }
        if(cull(tmp[0],tmp[1],tmp[3],tmp[4],tmp[6],tmp[7])==0)
            triangel(tmp,dest,dcolor);
    }
}

void bare_clearzbuf(void)
{
    int n;

    for(n=0;n<XS*YS;n++)
        zbuf[n]=-100000000;
    //memset(zbuf,0,XS*YS*sizeof(int));
}

void bare_loadidentity(void)
{
    memset(matrix,0,16*sizeof(int));
    matrix[0]=matrix[5]=matrix[10]=matrix[15]=ONE;
}

/* Yes, we should multiply a translation matrix in but this is simpler */
void bare_translate(float x,float y,float z)
{
    matrix[3]+=x*ONE;
    matrix[7]+=y*ONE;
    matrix[11]+=z*ONE;
}

void bare_rotatex(float r)
{
    int mtx[16]={ONE,0,0,0,
                 0,ONE,0,0,
                 0,0,ONE,0,
                 0,0,0,ONE};

    mtx[5]=cosf(r)*ONE;
    mtx[6]=-sinf(r)*ONE;
    mtx[9]=sinf(r)*ONE;
    mtx[10]=cosf(r)*ONE;

    mulmat(matrix,mtx);
}

void bare_rotatey(float r)
{
    int mtx[16]={ONE,0,0,0,
                 0,ONE,0,0,
                 0,0,ONE,0,
                 0,0,0,ONE};

    mtx[0]=cosf(r)*ONE;
    mtx[2]=sinf(r)*ONE;
    mtx[8]=-sinf(r)*ONE;
    mtx[10]=cosf(r)*ONE;

    mulmat(matrix,mtx);
}

void bare_rotatez(float r)
{
    int mtx[16]={ONE,0,0,0,
                 0,ONE,0,0,
                 0,0,ONE,0,
                 0,0,0,ONE};

    mtx[0]=cosf(r)*ONE;
    mtx[1]=-sinf(r)*ONE;
    mtx[4]=sinf(r)*ONE;
    mtx[5]=cosf(r)*ONE;

    mulmat(matrix,mtx);
}
