#include "ZGo.h"



//---- ZGo3DOBJECT -----------------------------------------------------------//

//---- ZGo3DOBJECT

ZGo3DOBJECT::ZGo3DOBJECT(ZGo *zgo)
{
  this->zgo = zgo;

  trans = new ZGoMATRIX(zgo);

  points = 0;
  point = NULL;

  faces = 0;
  face = NULL;

  objects = 0;
  object = NULL;
}



//---- ZGo3DOBJECT

ZGo3DOBJECT::~ZGo3DOBJECT()
{
  clear();
  delete trans;
}



//---- clear

void ZGo3DOBJECT::clear()
{
//  if(object) for(int i=0; i<objects; i++) delete object[i];
//  if(object) delete[] object;
  objects = 0;
  object = NULL;

//  if(point) delete[] point;
  points = 0;
  point = NULL;

//  if(faces) delete[] face;
  faces = 0;
  face = NULL;
}



//---- addFace

void ZGo3DOBJECT::addFace(int p1, int p2, int p3)
{
  if(!face) face = new int[4096][4];

  face[faces][0] = p1;
  face[faces][1] = p2;
  face[faces][2] = p3;

  face[faces][3] = faces*0x82736372 + (p1*0x07862876 ^ p2*0xF7276822) + p3*0x6526388A + 0x17863267*(int)this;

  faces ++;
}



//---- addFace

void ZGo3DOBJECT::addFace(int p1, int p2, int p3, int p4)
{
  addFace(p1, p2, p3);
  addFace(p3, p4, p1);
}



//---- addColFace

void ZGo3DOBJECT::addColFace(int p1, int p2, int p3, int col)
{
  if(!face) face = new int[4096][4];

  face[faces][0] = p1;
  face[faces][1] = p2;
  face[faces][2] = p3;

  face[faces][3] = col;

  faces ++;
}



//---- addColFace

void ZGo3DOBJECT::addColFace(int p1, int p2, int p3, int p4, int col)
{
  addColFace(p1, p2, p3, col);
  addColFace(p3, p4, p1, col);
}



//---- addPoint

void ZGo3DOBJECT::addPoint(float x, float y, float z)
{
  if(!point) point = new float[4096][3];

  point[points][0] = x;
  point[points][1] = y;
  point[points][2] = z;
  points ++;
}



//---- addObject

void ZGo3DOBJECT::addObject()
{
  addObject(new ZGo3DOBJECT(zgo));
}



//---- addObject

void ZGo3DOBJECT::addObject(ZGo3DOBJECT *obj)
{
  if(!object) object = new ZGo3DOBJECT *[256];

  object[objects] = obj;
  objects ++;
}



//---- transform

ZGo3DOBJECT *ZGo3DOBJECT::transform(ZGoMATRIX *m)
{
  trans->setMatrix(m);

  return this;
}



//---- render

void ZGo3DOBJECT::render(ZGoCAMERA *camera, ZGoIMAGE *im, ZGoIMAGE *zbuf)
{
  render(camera, NULL, im, zbuf);
}



//---- render

void ZGo3DOBJECT::render(ZGoCAMERA *camera, ZGoMATRIX *m, ZGoIMAGE *im, ZGoIMAGE *zbuf)
{
  int i;

  //- Set up matrix for this object

  ZGoMATRIX *objm = new ZGoMATRIX(zgo, trans, m);

  //- Transform points & calculate screen points

  float (*tpoint)[3] = new float[points][3];

  camera->screenSpace(im, objm, point, tpoint, points);

  //- Render faces

  for(i=0; i<faces; i++)
  {
    int p1 = face[i][0];
    int p2 = face[i][1];
    int p3 = face[i][2];

    if(tpoint[p1][2] > 0 && tpoint[p2][2] > 0 && tpoint[p3][2] > 0)
    {
      im->zbufTri(
        zbuf,
        tpoint[p1],
        tpoint[p2],
        tpoint[p3],
        face[i][3]
      );
    }
  }

  //- Render points

  for(i=0; i<points; i++)
  {
    float x = tpoint[i][0];
    float y = tpoint[i][1];
//    im->fillRect(x-1, y-2, x+1, y+2, 0x00000000);
//    im->fillRect(x-2, y-1, x+2, y+1, 0x00000000);
//    im->fillRect(x-1, y-1, x+1, y+1, 0x0000FF00);
//	im->putPixel((int)x, (int)y, 0x00FFFF00);
  }

  //- Render sub-objects

  for(i=0; i<objects; i++)
  {
    object[i]->render(camera, objm, im, zbuf);
  }

  //- Done

  delete tpoint;
  delete objm;
}



//---- cube

ZGo3DOBJECT *ZGo3DOBJECT::cube()
{
  clear();

  float s = 0.5;

  addPoint( s,  s,  s);
  addPoint( s,  s, -s);
  addPoint(-s,  s, -s);
  addPoint(-s,  s,  s);
  addPoint( s, -s,  s);
  addPoint( s, -s, -s);
  addPoint(-s, -s, -s);
  addPoint(-s, -s,  s);

  addColFace(0, 1, 2, 3, 0x336699);
  addColFace(5, 6, 2, 1, 0x003366);
  addColFace(6, 7, 3, 2, 0x6699CC);
  addColFace(7, 6, 5, 4, 0x99CCFF);
  addColFace(0, 3, 7, 4, 0x336699);
  addColFace(0, 4, 5, 1, 0x6699CC);

  return this;
}



//---- plane

ZGo3DOBJECT *ZGo3DOBJECT::plane(float scale)
{
  int i, j;

  clear();

  int size = 10;

  for(j=0; j<=size; j++) for(i=0; i<=size; i++)
  {
    int col = 0;
    
    if((i^j)&1)
    {
      col = 0xFFFFFFFF;
    }

    addPoint( (i-size/2) * scale, 0, (j-size/2) * scale);
    if(i < size && j < size)
    {
      addColFace
      (
        i+j*(size+1),
        i+j*(size+1) + 1,
        i+j*(size+1) + (size+1)+1,
        i+j*(size+1) + (size+1),
        col
      );
    }
  }

  return this;
}



//---- pqtorus

ZGo3DOBJECT *ZGo3DOBJECT::pqtorus(int p, int q, float poffset)
{
  clear();

  int asteps = 120;
  int bsteps = 12;

  float 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));
    float x1 = r * cos(p*a);
    float y1 = 0.25 * r * cos(q*a);
    float z1 = r * sin(p*a);

    a = (i+asteps*poffset+0.1)*3.14159*2/asteps; r = 0.25 * (4 + sin(q*a));
    float x2 = r * cos(p*a);
    float y2 = 0.25 * r * cos(q*a);
    float z2 = r * sin(p*a);

    //  Radial points

    float tx = x2-x1, ty = y2-y1, tz = z2-z1;
    float nx = x2+x1, ny = y2+y1, nz = z2+z1;
    float 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;

    float n = 1.0 / sqrt(nx*nx + ny*ny + nz*nz);
    nx *= n; ny *= n; nz *= n;

    float b = 1.0 / sqrt(bx*bx + by*by + bz*bz);
    bx *= b; by *= b; bz *= b;

    for(int j=0; j<bsteps; j++)
    {
      float 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;
      float s = sin(j*3.14159*2/bsteps);
      float c = cos(j*3.14159*2/bsteps);

      float x = rr*sin(j*3.15159*2/bsteps);
      float y = rr*cos(j*3.15159*2/bsteps);

      addPoint
      (
        x * nx + y * bx + x1,
        x * ny + y * by + y1,
        x * nz + y * bz + z1
      );

      addFace
      (
        i*bsteps + j,
        ((i+1)%asteps)*bsteps + j,
        ((i+1)%asteps)*bsteps + (j + 1)%bsteps,
        face[faces][1] = i*bsteps + (j + 1)%bsteps
      );
    }

  }

  return this;
}



//---- end of file -----------------------------------------------------------//
