


//---- TYPES -----------------------------------------------------------------//

pattern_type(ZGO_MATRIX, float m[4][4]);

pattern_type(ZGO_POINT3D, float x; float y; float z; float u; float v; float r; float g; float b; float a; float nx; float ny; float nz);

pattern_type(ZGO_SIMPLE3D, int numPoints; int numFaces; float point[4096][3]; int face[4096][3]);

typedef float ZGO_REAL;
typedef int ZGO_INT;



//---- FUNCTIONS -------------------------------------------------------------//


//---- ZGo_Triangle (POINT3D p1, POINT3D p2, POINT3D p3) -> (RGB buf)

void ZGo_Triangle(ZGO_POINT3D *p1, ZGO_POINT3D *p2, ZGO_POINT3D *p3, PATTERN_RGB *buf)
{
  int col = GetTickCount() * 0x7868713;

  float tri_x1, tri_y1, tri_z1, tri_r1, tri_g1, tri_b1, tri_a1, tri_u1, tri_v1, tri_d1, tri_s1;
  float tri_x2, tri_y2, tri_z2, tri_r2, tri_g2, tri_b2, tri_a2, tri_u2, tri_v2, tri_d2, tri_s2;
  float tri_x3, tri_y3, tri_z3, tri_r3, tri_g3, tri_b3, tri_a3, tri_u3, tri_v3, tri_d3, tri_s3;

  int i, j;

  bool tri_backfacecull = false;

  unsigned int clip_x1 = 0, clip_y1 = 0, clip_x2 = 256, clip_y2 = 256;

  unsigned int screen_w = 256, screen_h = 256;
  unsigned int *screen = (unsigned int *)buf->buf;

  //- Sort points

  ZGO_POINT3D *t;

  if(p2->y < p1->y) { t = p1; p1 = p2; p2 = t; }
  if(p3->y < p2->y) { t = p2; p2 = p3; p3 = t; }
  if(p2->y < p1->y) { t = p1; p1 = p2; p2 = t; }

  tri_x1 = p1->x; tri_y1 = p1->y; tri_z1 = p1->z;
  tri_x2 = p2->x; tri_y2 = p2->y; tri_z2 = p2->z;
  tri_x3 = p3->x; tri_y3 = p3->y; tri_z3 = p3->z;

  //- 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);
  float du12 = (tri_u2-tri_u1)/(tri_y2-tri_y1), du23 = (tri_u3-tri_u2)/(tri_y3-tri_y2), du13 = (tri_u3-tri_u1)/(tri_y3-tri_y1);
  float dv12 = (tri_v2-tri_v1)/(tri_y2-tri_y1), dv23 = (tri_v3-tri_v2)/(tri_y3-tri_y2), dv13 = (tri_v3-tri_v1)/(tri_y3-tri_y1);

  //- Backface cull - return if not clockwise

  if(tri_backfacecull && dx12 < dx13) return;

  //- 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 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;

  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;
	}

//  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 = (int)xa; if(xai < xa) xai++;
    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 u = ul + xai*dux;
//    float v = vl + xai*dvx;

    unsigned int *p = screen + (screen_w * j);

    for(i=xai; i<xbi; i++)
    {
      p[i] = col;
//      u += dux; v += dvx;
    }

    xa += dxa; xb += dxb;

//    ul += duy;
//    vl += dvy;
  }

  //- Draw bottom of triangle

  if(side13onleft)
  {
    xb = tri_x2 + dx23 * (y2-tri_y2);
	  dxb = dx23;
	}
  else
  {
    xa = tri_x2 + dx23 * (y2-tri_y2);
	  dxa = dx23;
	}

  for(j=y2; j<y3; j++)
  {
    int xai = (int)xa; if(xai < xa) xai++;
    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 u = ul + xai*dux;
//    float v = vl + xai*dvx;

    unsigned int *p = screen + (screen_w * j);

    for(i=xai; i<xbi; i++)
    {
      p[i] = col;
//      u += dux; v += dvx;
    }

    xa += dxa; xb += dxb;

//    ul += duy;
//    vl += dvy;
 }

}





//---- ZGo_Matrix_Create () -> (MATRIX matrix)

void ZGo_Matrix_Create(ZGO_MATRIX *matrix)
{
  int i, j;

  for(j=0; j<4; j++)
    for(i=0; i<4; i++)
      matrix->m[j][i] = i==j ? 1 : 0;
}



//---- ZGo_Matrix_Create_RotateAxis (INT a1, INT a2, REAL angle) -> (MATRIX matrix)

void ZGo_Matrix_Create_RotateX(ZGO_INT a1, ZGO_INT a2, ZGO_REAL angle, ZGO_MATRIX *matrix)
{
	float s = sin(angle), c = cos(angle);

  ZGo_Matrix_Create(matrix);

  matrix->m[a1][a1] = c;
	matrix->m[a1][a2] = -s;
	matrix->m[a2][a2] = c;
	matrix->m[a2][a1] = s;
}



//---- ZGo_Matrix_Create_RotateX (REAL angle) -> (MATRIX matrix)

void ZGo_Matrix_Create_RotateX(ZGO_REAL angle, ZGO_MATRIX *matrix)
{
  ZGo_Matrix_Create_RotateX(1, 2, angle, matrix);
}



//---- ZGo_Matrix_Create_RotateY (REAL angle) -> (MATRIX matrix)

void ZGo_Matrix_Create_RotateY(ZGO_REAL angle, ZGO_MATRIX *matrix)
{
  ZGo_Matrix_Create_RotateX(0, 2, angle, matrix);
}



//---- ZGo_Matrix_Create_RotateZ (REAL angle) -> (MATRIX matrix)

void ZGo_Matrix_Create_RotateZ(ZGO_REAL angle, ZGO_MATRIX *matrix)
{
  ZGo_Matrix_Create_RotateX(0, 1, angle, matrix);
}



//---- ZGo_Matrix_Mul (MATRIX m1, MATRIX m2) -> (MATRIX matrix)

void ZGo_Matrix_Mul(ZGO_MATRIX *m1, ZGO_MATRIX *m2, ZGO_MATRIX *matrix)
{
	int i, j, k;
	float sum;

	for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
  {
		sum = 0;

		for (k = 0; k < 4; k++)
			sum += m1->m[i][k] * m2->m[k][j];

		matrix->m[i][j] = sum;
	}

}



//---- ZGo_Simple3D_Transform (SIMPLE3D obj, MATRIX matrix) -> (SIMPLE3D obj2)

void ZGo_Simple3D_Transform(ZGO_SIMPLE3D *obj, ZGO_MATRIX *matrix, ZGO_SIMPLE3D *obj2)
{
  int i, j, k;

  float sum;

  obj2->numPoints = obj->numPoints;

  for(k=0; k<obj->numPoints; k++)
  {

		for (i = 0; i < 3; i++)
    {
			sum = 0.0;
			for (j = 0; j < 4; j++) sum += matrix->m[i][j] * obj->point[k][j];
			obj2->point[k][i] = sum;
		}

  }

  obj2->numFaces = obj->numFaces;

  memcpy(obj2->face, obj->face, sizeof(obj->face));
}



//---- ZGo_Simple3D_Rotate (SIMPLE3D obj, REAL time) -> (SIMPLE3D obj2)

void ZGo_Simple3D_Rotate(ZGO_SIMPLE3D *obj, ZGO_REAL time, ZGO_SIMPLE3D *obj2)
{
  ZGO_MATRIX mx, my, mz;
  ZGO_MATRIX m1, m2;

  ZGo_Matrix_Create_RotateX(time*3.14159/4.2376, &mx);
  ZGo_Matrix_Create_RotateY(time*3.14159/5.7457, &my);
  ZGo_Matrix_Create_RotateZ(time*3.14159/6.3251, &mz);

  ZGo_Matrix_Mul(&mx, &my, &m1);
  ZGo_Matrix_Mul(&m1, &mz, &m2);

  ZGo_Simple3D_Transform(obj, &m2, obj2);
}



//---- ZGo_Simple3D_Screen (SIMPLE3D obj) -> (SIMPLE3D obj2)

void ZGo_Simple3D_Screen(ZGO_SIMPLE3D *obj, ZGO_SIMPLE3D *obj2)
{
  int i;

  float x, y, z;

  obj2->numPoints = obj->numPoints;

  for(i=0; i<obj->numPoints; i++)
  {
    x = obj->point[i][0];
    y = obj->point[i][1];
    z = obj->point[i][2];

    obj2->point[i][0] = 128 + 128 * x / (z+3);
    obj2->point[i][1] = 128 + 128 * y / (z+3);
    obj2->point[i][2] = z;
  }

  obj2->numFaces = obj->numFaces;

  memcpy(obj2->face, obj->face, sizeof(obj->face));
}



//---- Pattern_Fn_SwirlyThing1 (RGB image, REAL time) -> (RGB result)

pattern_def( Pattern_Fn_SwirlyThing1 )
{
  //- Inputs and outputs

  pattern_input(PATTERN_REAL, time, 0);
  pattern_output(PATTERN_RGB, result, 0);

  unsigned int *buf = (unsigned int *)result->buf;
  ZGO_REAL t = time->value;

  //- Render background

  int i, j;

  int c[256];

  for(i=0; i<256; i++)
  {
    c[i] = 128
      + 32 * sin(t*3.14159 * 1.2497 + 0.8764 + 0.4782 * i)
      + 32 * sin(t*3.14159 * 2.5194 + 0.2878 + 0.2382 * i)
      + 32 * sin(t*3.14159 * 1.3823 + 0.4761 + 0.9281 * i)
      + 32 * sin(t*3.14159 * 1.3243 + 0.2429 + 0.7824 * i)
    ;
  }

  for(j=0; j<256; j++) for(i=0; i<256; i++)
  {
    result->buf[j][i][0] = c[i];
    result->buf[j][i][1] = 0;
    result->buf[j][i][2] = 0;
    result->buf[j][i][3] = 0;
  }

  //- Generate 3D object

  ZGO_SIMPLE3D obj, obj2, obj3;

  obj.numPoints = 4096;

  for(i=0; i<4096; i++)
  {
    obj.point[i][0] = 0.7*sin(3.14159 * i / 128 + 1.1422 * t + 0.2873)
                    + 0.3*sin(3.14159 * i / 341 + 1.2412 * t + 0.2422);
    obj.point[i][1] = 0.7*sin(3.14159 * i / 293 + 1.0897 * t + 0.7426)
                    + 0.3*sin(3.14159 * i / 134 + 1.4211 * t + 0.3523);
    obj.point[i][2] = 0.7*sin(4.14159 * i / 207 + 1.1097 * t + 0.5243)
                    + 0.3*sin(3.14159 * i / 491 + 2.2511 * t + 0.7627);
  }

  ZGo_Simple3D_Rotate(&obj, t, &obj2);
  ZGo_Simple3D_Screen(&obj2, &obj3);

  for(i=0; i<4096; i++)
  {
    int x = obj3.point[i][0];
    int y = obj3.point[i][1];
    int z = obj3.point[i][2];

    result->buf[y&255][x&255][0] = 255;
    result->buf[y&255][x&255][1] = 255;
    result->buf[y&255][x&255][2] = 255;
    result->buf[y&255][x&255][3] = 255;
  }

}


