/*
 * BB: The portable demo
 *
 * (C) 1997 by AA-group (e-mail: aa@horac.ta.jcu.cz)
 *
 * 3rd August 1997
 * version: 1.2 [final3]
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public Licences as by published
 * by the Free Software Foundation; either version 2; or (at your option)
 * any later version
 *
 * This program is distributed in the hope that it will entertaining,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Publis License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <time.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <aalib.h>
#include "bb.h"

#define DEG (M_PI/180)
#define X_s aa_imgwidth(context)
#define Y_s aa_imgheight(context)
#define data context->imagebuffer
#define X_S (X_s/2)
#define Y_S (Y_s/2)
#define Tx 127
#define Ty 127
#define XRATIO X_s*zoom/320
#define YRATIO Y_s*zoom*aa_mmwidth(context)/aa_mmheight(context)/320
#define Bufflen 768
#define VERT 3
#define ArC 12000
#define MaxFaces 500

typedef unsigned char byte;
typedef unsigned short int word;
typedef unsigned long dword;
typedef struct {
    unsigned short int x, y;
} INFO;
typedef long TBL[361];
typedef struct POL {
    long x, y, z, n_x, n_y, n_z;
} POLYS[3];
typedef struct {
    long x, y, z, nx, ny, nz;
} TRANSFORMED;
typedef TRANSFORMED TRANS[MaxFaces][3];

static TBL sinn, coss;
static int nFaces;
dword alfa, beta, gama;
int centerx, centery, centerz;
float zoom = 2.0;
static byte *envmapa;
static POLYS *obj, rot[MaxFaces];
static TRANS polyg;
static int *zbuff;

#include "patnik.h"
#include "torus.h"

static void precalculate(void)
{
    unsigned i;
    for (i = 0; i <= 360; i++) {
	sinn[i] = (long) (sin(i * DEG) * 65536);
	coss[i] = (long) (cos(i * DEG) * 65536);
    }
}
void set_zbuff()
{
    zbuff = (int *) malloc(X_s * Y_s * sizeof(int));
}

void unset_zbuff()
{
    free(zbuff);
}

static inline void clear_zbuff()
{
    memset(zbuff, 0x55, (X_s * Y_s * sizeof(long)));
}


static inline void makej(long x1, long x2, long n1z, long n2z, long ii, long n1x, long n1y, long n2x, long n2y)
{
    register byte *off, *off1;
    register int *zbptr = zbuff + (ii * X_s) + x1;
    long n3x;
    long n3y;
    long n3z;
    long n3Dx;
    long n3Dy;
    long n3Dz;
    long e, rx, ry;

    if (ii >= Y_s || ii < 0 || x2 < 0 || x1 >= X_s)
	return;
    if (x1 < 0)
	x1 = 0;
    if (x2 >= X_s)
	x2 = X_s - 1;
    e = x2 - x1;
    if (!e)
	return;
    n3Dx = (n2x - n1x) / e;
    n3x = n1x;
    n3Dy = (n2y - n1y) / e;
    n3y = n1y;
    n3Dz = (n2z - n1z) / e;
    n3z = n1z;

    off = data + (ii * X_s) + x1;
    off1 = off + x2 - x1;
    for (; off <= off1;) {

	ry = (abs(n3y) >> 7) + 64;
	rx = (abs(n3x) >> 7) + 64;
	if (((n3z >> 8) - *zbptr) < 500) {
	    *off = *(envmapa + (((unsigned int) ((ry << 7) + rx)) & (128 * 128 - 1)));
	    *zbptr = n3z >> 8;
	}
	off++;
	zbptr++;
	n3x += n3Dx;
	n3y += n3Dy;
	n3z += n3Dz;
    }
}

static void optfsp(unsigned wh)
{
    byte pivot, maxy, a, b, c;
    long ii, aa, bb;
    long x1, x2, d1, d2, m1, m2, m3;

    long n1x, n1y, n1z;
    long n2x, n2y, n2z;
    long n1Dx, n1Dy, n1Dz;
    long n2Dx, n2Dy, n2Dz;

    pivot = maxy = 0;
    if (polyg[wh][pivot].y > polyg[wh][1].y)
	pivot = 1;
    if (polyg[wh][pivot].y > polyg[wh][2].y)
	pivot = 2;
    if (polyg[wh][maxy].y < polyg[wh][1].y)
	maxy = 1;
    if (polyg[wh][maxy].y < polyg[wh][2].y)
	maxy = 2;

    a = b = pivot;
    c = maxy;
  a01:
    if (b == 2)
	b = 0;
    else
	b++;
    if ((b == pivot) || (b == maxy))
	goto a01;

    m1 = polyg[wh][c].y - polyg[wh][a].y;
    m2 = polyg[wh][b].y - polyg[wh][a].y;
    m3 = polyg[wh][c].y - polyg[wh][b].y;

    if (m1) {
	d1 = ((polyg[wh][c].x - polyg[wh][a].x) << 16) / m1;
	x1 = polyg[wh][a].x << 16;
	n1Dx = (rot[wh][c].n_x - rot[wh][a].n_x) / m1;
	n1x = rot[wh][a].n_x;
	n1Dy = (rot[wh][c].n_y - rot[wh][a].n_y) / m1;
	n1y = rot[wh][a].n_y;
	n1Dz = (polyg[wh][c].z - polyg[wh][a].z) / m1;
	n1z = polyg[wh][a].z;


	ii = 0;
	if (m2) {
	    d2 = ((polyg[wh][b].x - polyg[wh][a].x) << 16) / m2;
	    x2 = polyg[wh][a].x << 16;
	    n2Dx = (rot[wh][b].n_x - rot[wh][a].n_x) / m2;
	    n2x = rot[wh][a].n_x;
	    n2Dy = (rot[wh][b].n_y - rot[wh][a].n_y) / m2;
	    n2y = rot[wh][a].n_y;
	    n2Dz = (polyg[wh][b].z - polyg[wh][a].z) / m2;
	    n2z = polyg[wh][a].z;
	    for (ii = polyg[wh][a].y; ii <= polyg[wh][b].y; ii++) {
		aa = x1 >> 16;
		bb = x2 >> 16;
		if (aa > bb)
		    makej(bb, aa, n2z, n1z, ii, n2x, n2y, n1x, n1y);
		if (aa < bb)
		    makej(aa, bb, n1z, n2z, ii, n1x, n1y, n2x, n2y);
		x1 += d1;
		x2 += d2;
		n1x += n1Dx;
		n1y += n1Dy;
		n1z += n1Dz;
		n2x += n2Dx;
		n2y += n2Dy;
		n2z += n2Dz;
	    }
	}
	if (m3) {
	    d2 = ((polyg[wh][c].x - polyg[wh][b].x) << 16) / m3;
	    x2 = polyg[wh][b].x << 16;
	    n2Dx = (rot[wh][c].n_x - rot[wh][b].n_x) / m3;
	    n2x = rot[wh][b].n_x;
	    n2Dy = (rot[wh][c].n_y - rot[wh][b].n_y) / m3;
	    n2y = rot[wh][b].n_y;
	    n2Dz = (polyg[wh][c].z - polyg[wh][b].z) / m3;
	    n2z = polyg[wh][b].z;
	    for (ii = polyg[wh][b].y + 1; ii <= polyg[wh][c].y; ii++) {
		aa = x1 >> 16;
		bb = x2 >> 16;
		if (aa > bb)
		    makej(bb, aa, n2z, n1z, ii, n2x, n2y, n1x, n1y);
		if (aa < bb)
		    makej(aa, bb, n1z, n2z, ii, n1x, n1y, n2x, n2y);
		x1 += d1;
		x2 += d2;
		n1x += n1Dx;
		n1y += n1Dy;
		n1z += n1Dz;
		n2x += n2Dx;
		n2y += n2Dy;
		n2z += n2Dz;
	    }
	}
    }
}

static void show(void)
{
    double ZZ;
    unsigned i, j;
    for (i = 0; i < nFaces; i++)
	for (j = 0; j < 3; j++) {
	    polyg[i][j].x = ((rot[i][j].x << 8) / (256 + rot[i][j].z)) * XRATIO + X_S;
	    polyg[i][j].y = ((rot[i][j].y << 8) / (256 + rot[i][j].z)) * YRATIO + Y_S;
	    polyg[i][j].z = rot[i][j].z << 16;
	}
    for (i = 0; i < nFaces; i++) {
	ZZ = (polyg[i][2].x - polyg[i][0].x) * (polyg[i][1].y - polyg[i][0].y) -
	    (polyg[i][1].x - polyg[i][0].x) * (polyg[i][2].y - polyg[i][0].y);
	if (ZZ <= 0)
	    optfsp(i);
    }
}

void disp3d(void)
{
    int i, j;
    for (i = 0; i < nFaces; i++)
	for (j = 0; j < 3; j++) {
	    int x = obj[i][j].x + centerx, y = obj[i][j].y + centery,
	     z = obj[i][j].z + centerz;
	    rot[i][j].x = (z * coss[alfa] - x * sinn[alfa]) >> 16;
	    rot[i][j].z = (z * sinn[alfa] + x * coss[alfa]) >> 16;
	    x = (y * coss[beta] - rot[i][j].z * sinn[beta]) >> 16;
	    rot[i][j].z = (y * sinn[beta] + rot[i][j].z * coss[beta]) >> 16;
	    rot[i][j].y = x;
	    x = (rot[i][j].y * coss[gama] - rot[i][j].x * sinn[gama]) >> 16;
	    rot[i][j].x = (rot[i][j].y * sinn[gama] + rot[i][j].x * coss[gama]) >> 16;
	    rot[i][j].y = x;


	    x = obj[i][j].n_x + centerx, y = obj[i][j].n_y + centery, z = obj[i][j].n_z + centerz;
	    rot[i][j].n_x = (z * coss[alfa] - x * sinn[alfa]) >> 16;
	    rot[i][j].n_z = (z * sinn[alfa] + x * coss[alfa]) >> 16;
	    x = (y * coss[beta] - rot[i][j].z * sinn[beta]) >> 16;
	    rot[i][j].n_z = (y * sinn[beta] + rot[i][j].z * coss[beta]) >> 16;
	    rot[i][j].n_y = x;
	    x = (rot[i][j].n_y * coss[gama] - rot[i][j].n_x * sinn[gama]) >> 16;
	    rot[i][j].n_x = (rot[i][j].n_y * sinn[gama] + rot[i][j].n_x * coss[gama]) >> 16;
	    rot[i][j].n_y = x;
	}
    clear_zbuff();
    memset(data, 0, X_s * Y_s);
    show();
}

void patnikconstructor(void)
{
    long i, j;
    unsigned char table[256];
    for (i = 0; i < 256; i++) {
	table[i] = pow(i / 256.0, 2.0) * 256.0;
    }

    precalculate();
    nFaces = patniknFaces;
    obj = patnikobj;
    envmapa = (byte *) malloc(128 * 128);

    for (i = 0; i <= 127; i++)
	for (j = 0; j <= 127; j++)
	    *(envmapa + i * 128 + j) = table[(unsigned char) (0x0007A120 / ((i - 64) * (i - 64) + (j - 64) * (j - 64) + 2100))];
    memcpy(&rot, &obj, sizeof(obj));

}

void torusconstructor(void)
{
    long i, j;
    unsigned char table[256];
    for (i = 0; i < 256; i++) {
	table[i] = pow(i / 256.0, 3.0) * 200.0;
    }

    precalculate();
    nFaces = torusnFaces;
    obj = torusobj;
    envmapa = (byte *) malloc(128 * 128);

    for (i = 0; i <= 127; i++)
	for (j = 0; j <= 127; j++)
	    *(envmapa + i * 128 + j) = table[(unsigned char) (0x0007A120 / ((i - 64) * (i - 64) + (j - 64) * (j - 64) + 2100))];
    memcpy(&rot, &obj, sizeof(obj));

}

void destructor()
{
    free(envmapa);
}
