#include "ZGo.h"



//---- ZGoIMAGE --------------------------------------------------------------//



//---- ZGoIMAGE

ZGoIMAGE::ZGoIMAGE(ZGo *zgo, int w, int h)
{
  this->zgo = zgo;
  this->w = w;
  this->h = h;

  size = w*h;
  buf = (int *)malloc(size*4);
}



//---- ZGoIMAGE

ZGoIMAGE::ZGoIMAGE(ZGo *zgo, char *fname, int w, int h)
{
  this->zgo = zgo;
  this->w = w;
  this->h = h;

  size = w*h;
  buf = (int *)malloc(size*4);

  //- TODO: Load image

  int k = GetTickCount()*836782 + ((int)fname)*82619311;

  for(int j=0; j<h; j++) for(int i=0; i<w; i++) buf[i+w*j] = ((i^j)+k)*0x01010101;
}



//---- clear

void ZGoIMAGE::clear(int col)
{
  for(int i=0; i<size; i++) buf[i] = col;
}



//---- putPixel

void ZGoIMAGE::putPixel(int x, int y, int col)
{
  if(x < 0 || y < 0 || x > w || y > h) return;
  buf[y*w+x] = col;
}



//---- fillRect

void ZGoIMAGE::fillRect(int x1, int y1, int x2, int y2, int col)
{
  for(int j=y1; j<y2; j++) for(int i=x1; i<x2; i++) putPixel(i, j, col);
}



//---- putImage

void ZGoIMAGE::putImage(ZGoIMAGE *im, int x, int y)
{
  int ww = im->w;
  int hh = im->h;

  int sx = 0, sy = 0;

  //- Clip

  if(x < 0) { sx -= x; ww -= sx; x = 0; }
  if(x+ww > w) { ww = w-x; }

  if(y < 0) { sy -= y; hh -= sy; y = 0; }
  if(y+hh > h) { hh = h-y; }

  //- Blit

  int *d = buf + w*y + x;
  int *s = im->buf + im->w*sy + sx;

  for(int j=0; j<hh; j++)
  {
    for(int i=0; i<ww; i++) *d++ = *s++;
    d += w - ww;
    s += im->w - ww;
  }
}



//---- copy

void ZGoIMAGE::copy(ZGoIMAGE *im)
{
  putImage(im, 0, 0);
}



//---- env

void ZGoIMAGE::env()
{
  for(int j=0; j<256; j++) for(int i=0; i<256; i++)
  {
    int dr = 255 - sqrt((96-i)*(96-i) + (96-j)*(96-j)) * 2.5;
    int dg = 255 - sqrt((160-i)*(160-i) + (120-j)*(120-j)) * 2.5;
    int db = 255 - sqrt((128-i)*(128-i) + (150-j)*(150-j)) * 2.5;
    int da = 255 - sqrt((128-i)*(128-i) + (128-j)*(128-j)) * 8;
//    int dz = sqrt((128-i)*(128-i) + (128-j)*(128-j)) * 6 - 128;

//    if(dz <  0) dz =  0; if(dz > 255) dz = 255;

//    da += dz;

    if(da <  0) da =  0; if(da > 255) da = 255;

    dr *= 0.5;
    dg *= 0.5;
    db *= 0.5;

    dr += da;
    dg += da;
    db += da;

//    dr -= (i^j)&64;
//    dg -= (i^j)&64;
//    db -= (i^j)&64;

    if(dr < 0) dr =- dr; if(dr > 255) dr = 255;
    if(dg < 0) dg =- dg; if(dg > 255) dg = 255;
    if(db < 0) db =- db; if(db > 255) db = 255;

    buf[j*256+i] = 0xFF000000 + (dr << 16) + (dg << 8) + (db);
  }
}



//---- TRIANGLE ROUTINES -----------------------------------------------------//

//---- zbufTri

void ZGoIMAGE::zbufTri(ZGoIMAGE *zbuffer, float *p1, float *p2, float *p3, int col)
{
  int clip_x1 = 0, clip_y1 = 0;
  int clip_x2 = w, clip_y2 = h;

  float tri_x1 = p1[0], tri_y1 = p1[1], tri_z1 = p1[2];
  float tri_x2 = p2[0], tri_y2 = p2[1], tri_z2 = p2[2];
  float tri_x3 = p3[0], tri_y3 = p3[1], tri_z3 = p3[2];

  int i, j;

  //- Sort points

  float t;

  if(tri_y2 < tri_y1)
  {
    t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
    t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
    t = tri_z1; tri_z1 = tri_z2; tri_z2 = t;
  }

  if(tri_y3 < tri_y2)
  {
    t = tri_x2; tri_x2 = tri_x3; tri_x3 = t;
    t = tri_y2; tri_y2 = tri_y3; tri_y3 = t;
    t = tri_z2; tri_z2 = tri_z3; tri_z3 = t;
  }

  if(tri_y2 < tri_y1)
  {
    t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
    t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
    t = tri_z1; tri_z1 = tri_z2; tri_z2 = t;
  }

  //- Work out deltas along edges

  float dx12 = (tri_x2-tri_x1)/(tri_y2-tri_y1), dx23 = (tri_x3-tri_x2)/(tri_y3-tri_y2), dx13 = (tri_x3-tri_x1)/(tri_y3-tri_y1);

  //- Round to pixel and clip to top & bottom of screen

  int y1 = (int)tri_y1; if(y1 < tri_y1) y1++;
  int y2 = (int)tri_y2; if(y2 < tri_y2) y2++;
  int y3 = (int)tri_y3; if(y3 < tri_y3) y3++;

  if(y1 < clip_y1) y1 = clip_y1;
  if(y2 < clip_y1) y2 = clip_y1;
  if(y3 < clip_y1) y3 = clip_y1;

  if(y1 > clip_y2) y1 = clip_y2;
  if(y2 > clip_y2) y2 = clip_y2;
  if(y3 > clip_y2) y3 = clip_y2;

  if(y3 <= y1) return;

  //- Calculate horizontal and vertical deltas for parameters

  float d = (tri_x3-tri_x1)*(tri_y2-tri_y1) - (tri_y3-tri_y1)*(tri_x2-tri_x1);

  float dzx = ( (tri_y2-tri_y1)*(tri_z3-tri_z1) + (tri_y1-tri_y3)*(tri_z2-tri_z1) ) / d;
  float dzy = ( (tri_x1-tri_x2)*(tri_z3-tri_z1) + (tri_x3-tri_x1)*(tri_z2-tri_z1) ) / d;

  //- See if we need to flip triangle horizontally

  bool side13onleft = dx13 < dx12;

  //- Draw top of triangle

  float xa, xb, dxa, dxb;
  float za, zb, dza, dzb;

  //  Swap over if necessary

  if(side13onleft)
  {
    xa = tri_x1 + dx13 * (y1-tri_y1);
    xb = tri_x1 + dx12 * (y1-tri_y1);
  	dxa = dx13;
    dxb = dx12;
  }
  else
  {
	xa = tri_x1 + dx12 * (y1-tri_y1);
    xb = tri_x1 + dx13 * (y1-tri_y1);
  	dxa = dx12;
	dxb = dx13;
  }

  int fxa  = xa * 0x10000, fxb  = xb * 0x10000;
  int fdxa = dxa * 0x10000, fdxb = dxb * 0x10000;

  float zl = tri_z1 - tri_x1*dzx + (y1-tri_y1)*dzy;
  
  for(j=y1; j<y2; j++)
  {
	int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
    int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

    if(xai < clip_x1) xai = clip_x1;
    if(xbi < clip_x1) xbi = clip_x1;
    if(xai > clip_x2) xai = clip_x2;
    if(xbi > clip_x2) xbi = clip_x2;

    float z = zl + xai*dzx;

    int *buf = this->buf + j*w + xai;
    int *zbuf = zbuffer->buf + j*w + xai;

    for(i=xai; i<xbi; i++)
    {
      int tempz = z*0x10000;
      if(tempz > *zbuf)
      {
        *buf = col;
        *zbuf = tempz;
      }
      buf++;
      zbuf++;
      z += dzx;
    }

    fxa += fdxa; fxb += fdxb;

    zl += dzy;
  }

  //- Draw bottom of triangle

  if(side13onleft)
  {
    xb = tri_x2 + dx23 * (y2-tri_y2);
    dxb = dx23;
    fxb  = xb *0x10000;
    fdxb = dxb*0x10000;
  }
  else
  {
    xa = tri_x2 + dx23 * (y2-tri_y2);
    dxa = dx23;
    fxa  = xa *0x10000;
    fdxa = dxa*0x10000;
  }

  for(j=y2; j<y3; j++)
  {
    int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
    int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

    if(xai < clip_x1) xai = clip_x1;
    if(xbi < clip_x1) xbi = clip_x1;
    if(xai > clip_x2) xai = clip_x2;
    if(xbi > clip_x2) xbi = clip_x2;

    float z = zl + xai*dzx;

    int *buf = this->buf + j*w + xai;
    int *zbuf = zbuffer->buf + j*w + xai;

    for(i=xai; i<xbi; i++)
    {
      int tempz = z*0x10000;
      if(tempz > *zbuf)
      {
        *buf = col;
        *zbuf = tempz;
      }
      buf++;
      zbuf++;
      z += dzx;
    }

    fxa += fdxa; fxb += fdxb;

    zl += dzy;
  }

}



//---- zbufEnvTri

void ZGoIMAGE::zbufEnvTri(ZGoIMAGE *zbuffer, float *p1, float *n1, float *p2, float *n2, float *p3, float *n3, ZGoIMAGE *env)
{
  int clip_x1 = 0, clip_y1 = 0;
  int clip_x2 = w, clip_y2 = h;

  float tri_x1 = p1[0], tri_y1 = p1[1], tri_z1 = p1[2];
  float tri_x2 = p2[0], tri_y2 = p2[1], tri_z2 = p2[2];
  float tri_x3 = p3[0], tri_y3 = p3[1], tri_z3 = p3[2];

  float tri_u1 = 128+127*n1[0], tri_v1 = 128+127*n1[1];
  float tri_u2 = 128+127*n2[0], tri_v2 = 128+127*n2[1];
  float tri_u3 = 128+127*n3[0], tri_v3 = 128+127*n3[1];

  int i, j;

  int *envbuf = env->buf;

  //- Backface cull

  if((tri_x2-tri_x1)*(tri_y3-tri_y1) > (tri_x3-tri_x1)*(tri_y2-tri_y1)) return;

  //- Sort points

  float t;

  if(tri_y2 < tri_y1)
  {
    t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
    t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
    t = tri_z1; tri_z1 = tri_z2; tri_z2 = t;
    t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
    t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
  }

  if(tri_y3 < tri_y2)
  {
    t = tri_x2; tri_x2 = tri_x3; tri_x3 = t;
    t = tri_y2; tri_y2 = tri_y3; tri_y3 = t;
    t = tri_z2; tri_z2 = tri_z3; tri_z3 = t;
    t = tri_u2; tri_u2 = tri_u3; tri_u3 = t;
    t = tri_v2; tri_v2 = tri_v3; tri_v3 = t;
  }

  if(tri_y2 < tri_y1)
  {
    t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
    t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
    t = tri_z1; tri_z1 = tri_z2; tri_z2 = t;
    t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
    t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
  }

  //- Work out deltas along edges

  float dx12 = (tri_x2-tri_x1)/(tri_y2-tri_y1), dx23 = (tri_x3-tri_x2)/(tri_y3-tri_y2), dx13 = (tri_x3-tri_x1)/(tri_y3-tri_y1);

  //- Round to pixel and clip to top & bottom of screen

  int y1 = (int)tri_y1; if(y1 < tri_y1) y1++;
  int y2 = (int)tri_y2; if(y2 < tri_y2) y2++;
  int y3 = (int)tri_y3; if(y3 < tri_y3) y3++;

  if(y1 < clip_y1) y1 = clip_y1;
  if(y2 < clip_y1) y2 = clip_y1;
  if(y3 < clip_y1) y3 = clip_y1;

  if(y1 > clip_y2) y1 = clip_y2;
  if(y2 > clip_y2) y2 = clip_y2;
  if(y3 > clip_y2) y3 = clip_y2;

  if(y3 <= y1) return;

  //- Calculate horizontal and vertical deltas for parameters

  float d = (tri_x3-tri_x1)*(tri_y2-tri_y1) - (tri_y3-tri_y1)*(tri_x2-tri_x1);

  float dzx = ( (tri_y2-tri_y1)*(tri_z3-tri_z1) + (tri_y1-tri_y3)*(tri_z2-tri_z1) ) / d;
  float dzy = ( (tri_x1-tri_x2)*(tri_z3-tri_z1) + (tri_x3-tri_x1)*(tri_z2-tri_z1) ) / d;
  
  float dux = ( (tri_y2-tri_y1)*(tri_u3-tri_u1) + (tri_y1-tri_y3)*(tri_u2-tri_u1) ) / d;
  float duy = ( (tri_x1-tri_x2)*(tri_u3-tri_u1) + (tri_x3-tri_x1)*(tri_u2-tri_u1) ) / d;

  float dvx = ( (tri_y2-tri_y1)*(tri_v3-tri_v1) + (tri_y1-tri_y3)*(tri_v2-tri_v1) ) / d;
  float dvy = ( (tri_x1-tri_x2)*(tri_v3-tri_v1) + (tri_x3-tri_x1)*(tri_v2-tri_v1) ) / d;

  //- See if we need to flip triangle horizontally

  bool side13onleft = dx13 < dx12;

  //- Draw top of triangle

  float xa, xb, dxa, dxb;
  float za, zb, dza, dzb;
  float ua, ub, dua, dub;
  float va, vb, dva, dvb;

  //  Swap over if necessary

  if(side13onleft)
  {
    xa = tri_x1 + dx13 * (y1-tri_y1);
    xb = tri_x1 + dx12 * (y1-tri_y1);
    dxa = dx13;
    dxb = dx12;
  }
  else
  {
    xa = tri_x1 + dx12 * (y1-tri_y1);
    xb = tri_x1 + dx13 * (y1-tri_y1);
    dxa = dx12;
    dxb = dx13;
  }

  int fxa  = xa * 0x10000, fxb  = xb * 0x10000;
  int fdxa = dxa * 0x10000, fdxb = dxb * 0x10000;

  float zl = tri_z1 - tri_x1*dzx + (y1-tri_y1)*dzy;
  float ul = tri_u1 - tri_x1*dux + (y1-tri_y1)*duy;
  float vl = tri_v1 - tri_x1*dvx + (y1-tri_y1)*dvy;

  for(j=y1; j<y2; j++)
  {
    int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
    int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

    if(xai < clip_x1) xai = clip_x1;
    if(xbi < clip_x1) xbi = clip_x1;
    if(xai > clip_x2) xai = clip_x2;
  	if(xbi > clip_x2) xbi = clip_x2;

    float z = zl + xai*dzx;
    float u = ul + xai*dux;
    float v = vl + xai*dvx;

    int *buf = this->buf + j*w + xai;
    int *zbuf = zbuffer->buf + j*w + xai;

    for(i=xai; i<xbi; i++)
    {
      int tempz = z*0x10000;
      if(tempz > *zbuf)
      {
        *buf = envbuf[ (((int)v)<<8) + ((int)u) ];
        *zbuf = tempz;
      }
      buf++;
      zbuf++;
      z += dzx;
      u += dux;
      v += dvx;
    }

    fxa += fdxa; fxb += fdxb;

    zl += dzy;
    ul += duy;
    vl += dvy;
  }

  //- Draw bottom of triangle

  if(side13onleft)
  {
    xb = tri_x2 + dx23 * (y2-tri_y2);
	  dxb = dx23;
    fxb  = xb *0x10000;
    fdxb = dxb*0x10000;
  }
  else
  {
    xa = tri_x2 + dx23 * (y2-tri_y2);
  	dxa = dx23;
    fxa  = xa *0x10000;
    fdxa = dxa*0x10000;
  }

  for(j=y2; j<y3; j++)
  {
    int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
    int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

	  if(xai < clip_x1) xai = clip_x1;
	  if(xbi < clip_x1) xbi = clip_x1;
	  if(xai > clip_x2) xai = clip_x2;
	  if(xbi > clip_x2) xbi = clip_x2;

    float z = zl + xai*dzx;
    float u = ul + xai*dux;
    float v = vl + xai*dvx;

    int *buf = this->buf + j*w + xai;
    int *zbuf = zbuffer->buf + j*w + xai;

    for(i=xai; i<xbi; i++)
    {
      int tempz = z*0x10000;
      if(tempz > *zbuf)
      {
        *buf = envbuf[ (((int)v)<<8) + ((int)u) ];
        *zbuf = tempz;
      }
      buf++;
      zbuf++;
      z += dzx;
      u += dux;
      v += dvx;
    }

    fxa += fdxa; fxb += fdxb;

    zl += dzy;
    ul += duy;
    vl += dvy;
  }

}






//---- end of file -----------------------------------------------------------//
