#include "ZGo.h"



//---- ZGo3DOBJECT -----------------------------------------------------------//

//---- ZGo3DOBJECT

ZGo3DOBJECT::ZGo3DOBJECT(ZGo *zgo)
{
  this->zgo = zgo;
  objects = 0;

  trans = new ZGoMATRIX(zgo);

  points = 0;
  point = new int[4096][3];
  normal = new int[4096][3];

  faces = 0;
  face = new int[4096][3];
}



//---- clear

void ZGo3DOBJECT::clear()
{
  points = 0;
  faces = 0;
  objects = 0;
}



//---- addFace

void ZGo3DOBJECT::addFace(int p1, int p2, int p3)
{
  face[faces][0] = p1;
  face[faces][1] = p2;
  face[faces][2] = p3;
  faces ++;
}



//---- addPoint

void ZGo3DOBJECT::addPoint(double x, double y, double z)
{
  point[points][0] = x;
  point[points][1] = y;
  point[points][2] = z;
  points ++;
}



//---- addObject

void ZGo3DOBJECT::addObject(ZGo3DOBJECT *obj)
{
  object[objects] = obj;
  objects ++;
}



//---- transform

void ZGo3DOBJECT::transform(ZGoMATRIX *m)
{
  trans->setMatrix(m);
}



//---- render

void ZGo3DOBJECT::render(ZGoCAMERA *camera, ZGoIMAGE *im, ZGoIMAGE *zbuf, ZGoIMAGE *env)
{
  int i;

  double cx = im->w*0.5, cy = im->h*0.5, scale = cy < cx ? cy : cx;

  //- Transform points & calculate screen points

  for(i=0; i<points; i++)
  {
    //  Transform point

  	tpoint[i][0] = point[i][0]*trans->m00 + point[i][1]*trans->m01 + point[i][2]*trans->m02 + trans->m03;
	  tpoint[i][1] = point[i][0]*trans->m10 + point[i][1]*trans->m11 + point[i][2]*trans->m12 + trans->m13;
	 	tpoint[i][2] = point[i][0]*trans->m20 + point[i][1]*trans->m21 + point[i][2]*trans->m22 + trans->m23;

    tpoint[i][2] += 2;  //  TEMP: hard-coded camera position and orientation

    //  Transform normal

  	tnormal[i][0] = normal[i][0]*trans->m00 + normal[i][1]*trans->m01 + normal[i][2]*trans->m02;
	  tnormal[i][1] = normal[i][0]*trans->m10 + normal[i][1]*trans->m11 + normal[i][2]*trans->m12;
	 	tnormal[i][2] = normal[i][0]*trans->m20 + normal[i][1]*trans->m21 + normal[i][2]*trans->m22;

    //  Screen point

    spoint[i][2] = 1.0 / tpoint[i][2];

    spoint[i][0] = cx + scale * tpoint[i][0] * spoint[i][2];
    spoint[i][1] = cy + scale * tpoint[i][1] * spoint[i][2];
  }

  //- Render faces

  for(i=0; i<faces; i++)
  {
    int p1 = face[i][0];
    int p2 = face[i][1];
    int p3 = face[i][2];

    im->zbufEnvTri(
      zbuf,
      spoint[p1], tnormal[p1],
      spoint[p2], tnormal[p2],
      spoint[p3], tnormal[p3],
      env
    );
  }

  //- Render points

  if(!faces) for(i=0; i<points; i++)
  {
    double x = spoint[i][0];
    double y = spoint[i][1];
    im->fillRect(x-2, y-2, x+2, y+2, 0xFFFFFFFF);
  }

  //- Render sub-objects (TODO: Transformation, z-buffer etc.)

  for(i=0; i<objects; i++)
  {
    object[i]->render(camera, im, zbuf, env);
  }
}



//---- cube

void ZGo3DOBJECT::cube()
{
  clear();

  points = 8;
  faces = 12;

  point[0][0] =  1; point[0][1] =  1; point[0][2] =  1;
  point[1][0] =  1; point[1][1] =  1; point[1][2] = -1;
  point[2][0] = -1; point[2][1] =  1; point[2][2] = -1;
  point[3][0] = -1; point[3][1] =  1; point[3][2] =  1;
  point[4][0] =  1; point[4][1] = -1; point[4][2] =  1;
  point[5][0] =  1; point[5][1] = -1; point[5][2] = -1;
  point[6][0] = -1; point[6][1] = -1; point[6][2] = -1;
  point[7][0] = -1; point[7][1] = -1; point[7][2] =  1;
  
  face[ 0][0] = 0; face[ 0][1] = 1; face[ 0][2] = 2;
  face[ 1][0] = 2; face[ 1][1] = 3; face[ 1][2] = 0;
  face[ 2][0] = 5; face[ 2][1] = 6; face[ 2][2] = 2;
  face[ 3][0] = 2; face[ 3][1] = 1; face[ 3][2] = 5;
  face[ 4][0] = 6; face[ 4][1] = 7; face[ 4][2] = 3;
  face[ 5][0] = 3; face[ 5][1] = 2; face[ 5][2] = 6;
  face[ 6][0] = 7; face[ 6][1] = 6; face[ 6][2] = 5;
  face[ 7][0] = 5; face[ 7][1] = 4; face[ 7][2] = 7;
  face[ 8][0] = 0; face[ 8][1] = 3; face[ 8][2] = 7;
  face[ 9][0] = 7; face[ 9][1] = 4; face[ 9][2] = 0;
  face[10][0] = 0; face[10][1] = 4; face[10][2] = 5;
  face[11][0] = 5; face[11][1] = 1; face[11][2] = 0;
}



//---- pqtorus

void ZGo3DOBJECT::pqtorus(int p, int q, double poffset)
{
  clear();

  int asteps = 120;
  int bsteps = 12;

  double a, r;

  for(int i=0; i<asteps; i++)
  {
    //  Get previous, current and next points

    a = (i+asteps*poffset)*3.14159*2/asteps; r = 0.25 * (4 + sin(q*a));
    double x1 = r * cos(p*a);
    double y1 = 0.25 * r * cos(q*a);
    double z1 = r * sin(p*a);

    a = (i+asteps*poffset+0.1)*3.14159*2/asteps; r = 0.25 * (4 + sin(q*a));
    double x2 = r * cos(p*a);
    double y2 = 0.25 * r * cos(q*a);
    double z2 = r * sin(p*a);

    //  Radial points

    double tx = x2-x1, ty = y2-y1, tz = z2-z1;
    double nx = x2+x1, ny = y2+y1, nz = z2+z1;
    double bx = ty*nz-tz*ny, by = tz*nx-tx*nz, bz = tx*ny-ty*nx;
           nx = by*tz-bz*ty; ny = bz*tx-bx*tz; nz = bx*ty-by*tx;

    double n = 1.0 / sqrt(nx*nx + ny*ny + nz*nz);
    nx *= n; ny *= n; nz *= n;

    double b = 1.0 / sqrt(bx*bx + by*by + bz*bz);
    bx *= b; by *= b; bz *= b;

    for(int j=0; j<bsteps; j++)
    {
      double rr = 0.2;//0.15 + 0.1*sin(i*3.14159*2*q/asteps); // + 0.1*sin(j*3.14159*2*4/bsteps + i*3.14159*2*7/asteps);

      if(rr < 0.08) rr = 0.16-rr;
      double s = sin(j*3.14159*2/bsteps);
      double c = cos(j*3.14159*2/bsteps);

      double x = rr*sin(j*3.15159*2/bsteps);
      double y = rr*cos(j*3.15159*2/bsteps);

      point[points][0] = x * nx + y * bx + x1;
      point[points][1] = x * ny + y * by + y1;
      point[points][2] = x * nz + y * bz + z1;
      normal[points][0] = s * nx + c * bx;
      normal[points][1] = s * ny + c * by;
      normal[points][2] = s * nz + c * bz;
      points ++;

      face[faces][0] = i*bsteps + j;
      face[faces][1] = ((i+1)%asteps)*bsteps + j;
      face[faces][2] = ((i+1)%asteps)*bsteps + (j + 1)%bsteps;
      faces ++;

      face[faces][0] = ((i+1)%asteps)*bsteps + (j + 1)%bsteps;
      face[faces][1] = i*bsteps + (j + 1)%bsteps;
      face[faces][2] = i*bsteps + j;
      faces ++;
    }

  }
}



//---- end of file -----------------------------------------------------------//
