#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "engine3d.h"
#include "generate3d.h"
#include "ZeEngineGP2X.h"

object3d object[256];

extern unsigned char beet3do[];
extern unsigned char teapot[];
extern unsigned char cow3do[];
extern unsigned char face3do[];

unsigned char addtimes[MAXDATA];

extern unsigned int maxobj;

void CalcNorms(object3d *obj, int neg)
{
    vector3d v1,v2;

    obj->normal = (vector3d*)malloc(obj->npls * sizeof(vector3d));

    int i;
    for (i=0; i<obj->npls; i++)
    {
        v1.x = obj->point[obj->poly[i].p2].x - obj->point[obj->poly[i].p1].x;
        v1.y = obj->point[obj->poly[i].p2].y - obj->point[obj->poly[i].p1].y;
        v1.z = obj->point[obj->poly[i].p2].z - obj->point[obj->poly[i].p1].z;

        v2.x = obj->point[obj->poly[i].p1].x - obj->point[obj->poly[i].p0].x;
        v2.y = obj->point[obj->poly[i].p1].y - obj->point[obj->poly[i].p0].y;
        v2.z = obj->point[obj->poly[i].p1].z - obj->point[obj->poly[i].p0].z;

        obj->normal[i] = Normalize(CrossProduct(v1,v2));
        if (neg==1) obj->normal[i] = NegVec(obj->normal[i]);
    }
}

void CalcPtNorms(object3d *obj)
{
    obj->pt_normal = (vector3d*)malloc(obj->npts * sizeof(vector3d));

    int i;
    for (i=0; i<MAXDATA; i++)
        addtimes[i] = 0;

    for (i=0; i<obj->npts; i++)
    {
        obj->pt_normal[i].x = 0;
        obj->pt_normal[i].y = 0;
        obj->pt_normal[i].z = 0;
    }

    for (i=0; i<obj->npls; i++)
    {
        obj->pt_normal[obj->poly[i].p0].x += obj->normal[i].x;
        obj->pt_normal[obj->poly[i].p0].y += obj->normal[i].y;
        obj->pt_normal[obj->poly[i].p0].z += obj->normal[i].z;
        obj->pt_normal[obj->poly[i].p1].x += obj->normal[i].x;
        obj->pt_normal[obj->poly[i].p1].y += obj->normal[i].y;
        obj->pt_normal[obj->poly[i].p1].z += obj->normal[i].z;
        obj->pt_normal[obj->poly[i].p2].x += obj->normal[i].x;
        obj->pt_normal[obj->poly[i].p2].y += obj->normal[i].y;
        obj->pt_normal[obj->poly[i].p2].z += obj->normal[i].z;
        addtimes[obj->poly[i].p0]++;
        addtimes[obj->poly[i].p1]++;
        addtimes[obj->poly[i].p2]++;
    }

    for (i=0; i<obj->npts; i++)
    {
        if (addtimes[i]!=0)
        {
            obj->pt_normal[i].x /= addtimes[i];
            obj->pt_normal[i].y /= addtimes[i]; 
            obj->pt_normal[i].z /= addtimes[i];
            obj->pt_normal[i] = Normalize(obj->pt_normal[i]);
        }
    }
}

void ReversePolygonOrder(object3d *obj)
{
    int i, a, b, c;
    for (i=0; i<obj->npls; i++)
    {
        a = obj->poly[i].p0;
        b = obj->poly[i].p1;
        c = obj->poly[i].p2;
        obj->poly[i].p0 = c;
        obj->poly[i].p1 = b;
        obj->poly[i].p2 = a;
    }
}


void Load32bit3doFile(char* filename, object3d *obj, int neg, float stdiv)
{
	FILE *obj3d;
	obj3d=fopen(filename,"rb");

	obj->npts=fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24);
	obj->nlns=fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24);
	obj->npls=fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24);

    obj->point = (point3d*)malloc(obj->npts * sizeof(point3d));
    obj->line = (line2d*)malloc(obj->nlns * sizeof(line2d));
    obj->poly = (poly2d*)malloc(obj->npls * sizeof(poly2d));

    int i;
	for (i=0; i<obj->npts; i++)
	{
		obj->point[i].x = (fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24) - 65536)/stdiv;
		obj->point[i].y = (fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24) - 65536)/stdiv;
		obj->point[i].z = (fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24) - 65536)/stdiv;
		obj->point[i].c = 0xFFFFFF;
	}

	for (i=0; i<obj->nlns; i++)
	{
        obj->line[i].p0 = fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24);
        obj->line[i].p1 = fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24);
        obj->line[i].c = 0xFFFFFF;
	}

	for (i=0; i<obj->npls; i++)
	{
        obj->poly[i].p0 = fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24);
        obj->poly[i].p1 = fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24);
        obj->poly[i].p2 = fgetc(obj3d)+(fgetc(obj3d)<<8)+(fgetc(obj3d)<<16)+(fgetc(obj3d)<<24);
	}

	fclose(obj3d);

    CalcNorms(obj, neg);
    CalcPtNorms(obj);
}


void Load16bit3do(object3d *obj, unsigned char* objdata, int neg)
{
    obj->npts = *objdata + (*(objdata+1)<<8);
    obj->nlns = *(objdata+2) + (*(objdata+3)<<8);
    obj->npls = *(objdata+4) + (*(objdata+5)<<8);

    obj->point = malloc(obj->npts * sizeof(point3d));
    obj->line = malloc(obj->nlns * sizeof(line2d));
    obj->poly = malloc(obj->npls * sizeof(poly2d));

    objdata+=6;
    int i;
    for (i=0; i<obj->npts; i++)
    {
        obj->point[i].x = (*(objdata++) - 128) >> 1;
        obj->point[i].y = (*(objdata++) - 128) >> 1;
        obj->point[i].z = (*(objdata++) - 128) >> 1;
        obj->point[i].c = 0xFFFF;
    }

    for (i=0; i<obj->nlns; i++)
    {
        obj->line[i].p0 = *objdata + (*(objdata+1)<<8);
        obj->line[i].p1 = *(objdata+2) + (*(objdata+3)<<8);
        objdata+=4;
        obj->line[i].c = 0xFFFF;
    }

    for (i=0; i<obj->npls; i++)
    {
        obj->poly[i].p0 = *objdata + (*(objdata+1)<<8);
        obj->poly[i].p1 = *(objdata+2) + (*(objdata+3)<<8);
        obj->poly[i].p2 = *(objdata+4) + (*(objdata+5)<<8);
        objdata+=6;
    }

    CalcNorms(obj, neg);
    CalcPtNorms(obj);
}

// ==========================================================

void InitObject(object3d *obj, int objn)
{
    int tl = 4, tr = 252;
    int k;

    switch(objn)
    {
        case 0:

            obj->npts = 8;
            obj->nlns = 12;
            obj->npls = 12;

            obj->point = malloc(obj->npts * sizeof(point3d));
            obj->line = malloc(obj->nlns * sizeof(line2d));
            obj->poly = malloc(obj->npls * sizeof(poly2d));

            obj->point[0].x = -64; obj->point[0].y = 64; obj->point[0].z = -64;
            obj->point[1].x = -64; obj->point[1].y = -64; obj->point[1].z = -64;
            obj->point[2].x = 64; obj->point[2].y = -64; obj->point[2].z = -64;
            obj->point[3].x = 64; obj->point[3].y = 64; obj->point[3].z = -64;
            obj->point[4].x = -64; obj->point[4].y = 64; obj->point[4].z = 64;
            obj->point[5].x = -64; obj->point[5].y = -64; obj->point[5].z = 64;
            obj->point[6].x = 64; obj->point[6].y = -64; obj->point[6].z = 64;
            obj->point[7].x = 64; obj->point[7].y = 64; obj->point[7].z = 64;

            obj->line[0].p0 = 0; obj->line[0].p1 = 1;
            obj->line[1].p0 = 1; obj->line[1].p1 = 2;
            obj->line[2].p0 = 2; obj->line[2].p1 = 3;
            obj->line[3].p0 = 3; obj->line[3].p1 = 0;
            obj->line[4].p0 = 4; obj->line[4].p1 = 5;
            obj->line[5].p0 = 5; obj->line[5].p1 = 6;
            obj->line[6].p0 = 6; obj->line[6].p1 = 7;
            obj->line[7].p0 = 7; obj->line[7].p1 = 4;
            obj->line[8].p0 = 0; obj->line[8].p1 = 4;
            obj->line[9].p0 = 1; obj->line[9].p1 = 5;
            obj->line[10].p0 = 2; obj->line[10].p1 = 6;
            obj->line[11].p0 = 3; obj->line[11].p1 = 7;

            obj->poly[0].p0 = 0; obj->poly[0].p1 = 1; obj->poly[0].p2 = 2;
            obj->poly[1].p0 = 0; obj->poly[1].p1 = 2; obj->poly[1].p2 = 3;
            obj->poly[2].p0 = 7; obj->poly[2].p1 = 6; obj->poly[2].p2 = 5;
            obj->poly[3].p0 = 7; obj->poly[3].p1 = 5; obj->poly[3].p2 = 4;
            obj->poly[4].p0 = 3; obj->poly[4].p1 = 2; obj->poly[4].p2 = 6;
            obj->poly[5].p0 = 3; obj->poly[5].p1 = 6; obj->poly[5].p2 = 7;
            obj->poly[6].p0 = 4; obj->poly[6].p1 = 5; obj->poly[6].p2 = 1;
            obj->poly[7].p0 = 4; obj->poly[7].p1 = 1; obj->poly[7].p2 = 0;
            obj->poly[8].p0 = 4; obj->poly[8].p1 = 0; obj->poly[8].p2 = 3;
            obj->poly[9].p0 = 4; obj->poly[9].p1 = 3; obj->poly[9].p2 = 7;
            obj->poly[10].p0 = 1; obj->poly[10].p1 = 5; obj->poly[10].p2 = 6;
            obj->poly[11].p0 = 1; obj->poly[11].p1 = 6; obj->poly[11].p2 = 2;

            int i;
            for (i=0; i<6; i++)
            {
                obj->poly[2*i].tc0.u = tl; obj->poly[2*i].tc0.v = tl; obj->poly[2*i].tc1.u = tl; obj->poly[2*i].tc1.v = tr; obj->poly[2*i].tc2.u = tr; obj->poly[2*i].tc2.v = tr;
                obj->poly[2*i+1].tc0.u = tl; obj->poly[2*i+1].tc0.v = tl; obj->poly[2*i+1].tc1.u = tr; obj->poly[2*i+1].tc1.v = tr; obj->poly[2*i+1].tc2.u = tr; obj->poly[2*i+1].tc2.v = tl;
            }

            for (i=0; i<obj->nlns; i++)
                obj->line[i].c = 0xFFFF;

            obj->pos.x = 0; obj->pos.y = 0; obj->pos.z = 256;
            obj->rot.x = 0; obj->rot.y = 0; obj->rot.z = 0;
            
            for (i=0; i<11; i++)
                obj->poly[i].m = i>>1;

            CalcNorms(obj, 1);
            CalcPtNorms(obj);
        break;

        case 1:
            Load16bit3do(obj, face3do,0);
            ReversePolygonOrder(obj);
            obj->pos.x = 0; obj->pos.y = -16; obj->pos.z = 96;
            obj->rot.x = 0; obj->rot.y = -PI/2; obj->rot.z = -PI/2;
        break;

        case 2:
            Load16bit3do(obj, cow3do,1);
            obj->pos.x = 0; obj->pos.y = 0; obj->pos.z = 128;
            obj->rot.x = 0; obj->rot.y = 0; obj->rot.z = 0;
        break;

        case 3:
            Load16bit3do(obj, teapot,1);
            obj->pos.x = 0; obj->pos.y = 0; obj->pos.z = 128;
            obj->rot.x = 0; obj->rot.y = 0; obj->rot.z = 0;
        break;

        case 4:
            Load16bit3do(obj, beet3do,1);
            obj->pos.x = 0; obj->pos.y = 0; obj->pos.z = 80;
            obj->rot.x = 0; obj->rot.y = 0; obj->rot.z = 0;
        break;

        case 5:
            Load32bit3doFile("./porsche.3do", obj, 1, 1024);
            obj->pos.x = 0; obj->pos.y = 180; obj->pos.z = 1024;
            obj->rot.x = 0; obj->rot.y = 0; obj->rot.z = 0;
        break;

        default:
        break;
    }    
}


void InitAllObjects()
{
    int i;
    for (i=0; i<maxobj; i++)
        InitObject(&object[i], i);
}
