/* :ts=8 */	/* Yes some of us still use vi ! */
/************************************************************************/
/*                                                                      */
/* This file contains code which is called from tesselate.c to generate	*/
/* a ttddd description file of the current object.			*/
/*                                                                      */
/************************************************************************/

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include "general.h"
#include "globals.h"
#include "intui.h"
#include "tesselate.h"
#include "ttddd.h"


    /* Increase these for very big objects  			*/
    /* Decrease them if you don't have enough memory		*/
    /* Note the arrays need only be big enough to contain	*/
    /* data for a single patch!					*/

#define Max_Nbr_TTDDD_Points	1000
#define Max_Nbr_TTDDD_Edges	1000
#define Max_Nbr_TTDDD_Faces	500


typedef struct {
	short		P1;
	short		P2;
} Edge_T;

typedef struct {
	short		E1;
	short		E2;
	short		E3;
} Face_T;

static Boolean_T  Phong   	 = TRUE;	/* Phong smoothing?	*/
static double	  TScale_Factor  = 1.0;		/* Scale factor		*/
static FILE	  *TTDDD_Stream	 = NULL;

static Vector_T  TTDDD_Points[Max_Nbr_TTDDD_Points]; /* Table of points	*/
static short	 Nbr_TTDDD_Points	= 0;

static Edge_T	 TTDDD_Edges[Max_Nbr_TTDDD_Edges];   /* Table of edges	*/
static short	 Nbr_TTDDD_Edges	= 0;

static Face_T	 TTDDD_Faces[Max_Nbr_TTDDD_Faces];   /* Table of faces	*/
static short	 Nbr_TTDDD_Faces 	= 0;


static
short Get_TTDDD_Point(Vector_T Point)
/************************************************************************/
/*									*/
/* Get id for the point 'Point'.					*/
/* If no such point exist, then create it and return the newly created  */
/* id.                                                                  */
/*									*/
/************************************************************************/
{
    short	i;

    for (i = Nbr_TTDDD_Points-1; i >= 0; i--) {

	if (ABS(Point[0] - TTDDD_Points[i][0]) < FTINY &&
	    ABS(Point[1] - TTDDD_Points[i][1]) < FTINY &&
	    ABS(Point[2] - TTDDD_Points[i][2]) < FTINY) return(i);

    } /* for */

    if (Nbr_TTDDD_Points >= Max_Nbr_TTDDD_Points) {
	Display_Error_Message("Too many points");
  	CloseStuff();
	exit(9);
    }

    TTDDD_Points[Nbr_TTDDD_Points][0] = Point[0];
    TTDDD_Points[Nbr_TTDDD_Points][1] = Point[1];
    TTDDD_Points[Nbr_TTDDD_Points][2] = Point[2];

    return(Nbr_TTDDD_Points++);

} /* Get_TTDDD_Point */

static
short Get_TTDDD_Edge(short P1, short P2)
/************************************************************************/
/*									*/
/* Get id for the edge that connects the points P1 and P2.              */
/* If no such edge exists, then create it and return the newly created  */
/* id.                                                                  */
/*									*/
/************************************************************************/
{
    short	i;

    for (i = Nbr_TTDDD_Edges-1; i >= 0; i--) {

	if ((TTDDD_Edges[i].P1 == P1 && TTDDD_Edges[i].P2 == P2) ||
	    (TTDDD_Edges[i].P1 == P2 && TTDDD_Edges[i].P2 == P1)) return(i);

    } /* for */

    if (Nbr_TTDDD_Edges >= Max_Nbr_TTDDD_Edges) {
	Display_Error_Message("Too many edges");
  	CloseStuff();
	exit(9);
    }

    TTDDD_Edges[Nbr_TTDDD_Edges].P1 = P1;
    TTDDD_Edges[Nbr_TTDDD_Edges].P2 = P2;

    return(Nbr_TTDDD_Edges++);

} /* Get_TTDDD_Edge */

static
void Add_TTDDD_Face(short P1, short P2, short P3)
/************************************************************************/
/*									*/
/* Add a new face connecting P1, P2 and P3 to the face table.           */
/* The corresponding edges will be created if they don't exist.         */
/*									*/
/************************************************************************/
{
    short	i;
    short E1, E2, E3;

    E1 = Get_TTDDD_Edge(P1, P2);
    E2 = Get_TTDDD_Edge(P2, P3);
    E3 = Get_TTDDD_Edge(P3, P1);

    for (i = Nbr_TTDDD_Faces-1; i >= 0; i--) {

	if (TTDDD_Faces[i].E1 == E1 && TTDDD_Faces[i].E2 == E2 &&
	   			       TTDDD_Faces[i].E3 == E3) return;

    } /* for */


    if (Nbr_TTDDD_Faces >= Max_Nbr_TTDDD_Faces) {
	Display_Error_Message("Too many faces");
  	CloseStuff();
	exit(9);
    }

    TTDDD_Faces[Nbr_TTDDD_Faces].E1      = Get_TTDDD_Edge(P1, P2);
    TTDDD_Faces[Nbr_TTDDD_Faces].E2      = Get_TTDDD_Edge(P2, P3);
    TTDDD_Faces[Nbr_TTDDD_Faces].E3      = Get_TTDDD_Edge(P3, P1);

    Nbr_TTDDD_Faces++;

} /* Add_TTDDD_Face */

static 
void Write_TTDDD_Object()
/************************************************************************/
/*									*/
/* Write the object saved in the global variables to TTDDD_Stream in 	*/
/* TTDDD format.							*/
/* If 'Phong' is TRUE, the object should be phong smoothed.		*/
/*									*/
/************************************************************************/
{
    short		i;

    if (Nbr_TTDDD_Points == 0) return;

    fprintf(TTDDD_Stream, "OBJ Begin   %% Begin Object chunk\n");
    fprintf(TTDDD_Stream, "\n");

    fprintf(TTDDD_Stream, "DESC Begin  %% Begin DESC sub-chunk\n");
    fprintf(TTDDD_Stream, "\n");

    fprintf(TTDDD_Stream, "\tNAME \"%s\"\n", "ICOONS");
    fprintf(TTDDD_Stream, "\n");
    fprintf(TTDDD_Stream, "\tSHAP Shape 2   %% Axis object\n");
    fprintf(TTDDD_Stream, "\tSHAP Lamp  0   %% Not a lamp\n");
    fprintf(TTDDD_Stream, "\n");
    fprintf(TTDDD_Stream, "\tPOSI X=0.0 Y=0.0 Z=0.0 %% Position.\n");
    fprintf(TTDDD_Stream, "\n");

    fprintf(TTDDD_Stream, "\tAXIS XAxis X=1 %% Defaults to 1 0 0\n");
    fprintf(TTDDD_Stream, "\tAXIS YAxis Y=1 %% Defaults to 0 1 0\n");
    fprintf(TTDDD_Stream, "\tAXIS ZAxis Z=1 %% Defaults to 0 0 1\n");
    fprintf(TTDDD_Stream, "\n");

    fprintf(TTDDD_Stream, "\tSIZE X=32 Y=32 Z=32 %% Defaults to 32.0\n");
    fprintf(TTDDD_Stream, "\n");

		/* POINTS */

    fprintf(TTDDD_Stream, "\tPNTS PCount %d %% Number of points\n", 
    							Nbr_TTDDD_Points);
    fprintf(TTDDD_Stream, "\n");

    for (i = 0; i < Nbr_TTDDD_Points; i++) {
        fprintf(TTDDD_Stream, "\tPNTS Point[%d] %lf %lf %lf\n", 
				i, 
				TScale_Factor * TTDDD_Points[i][0], 
				TScale_Factor * TTDDD_Points[i][1], 
				TScale_Factor * TTDDD_Points[i][2]);
    } /* for */
    fprintf(TTDDD_Stream, "\n");


		/* EDGES */
    fprintf(TTDDD_Stream, "\tEDGE ECount %d %% Number of edges\n", 
    							Nbr_TTDDD_Edges);
    fprintf(TTDDD_Stream, "\n");
    for (i = 0; i < Nbr_TTDDD_Edges; i++) {
        fprintf(TTDDD_Stream, "\tEDGE Edge[%d] %d %d\n", 
				i, TTDDD_Edges[i].P1, TTDDD_Edges[i].P2);
    } /* for */
    fprintf(TTDDD_Stream, "\n");


		/* FACES */
    fprintf(TTDDD_Stream, "\tFACE TCount %d %% Number of faces\n", 
    							Nbr_TTDDD_Faces);
    fprintf(TTDDD_Stream, "\n");
    for (i = 0; i < Nbr_TTDDD_Faces; i++) {
        fprintf(TTDDD_Stream, "\tFACE Connect[%d] %d %d %d\n", 
				i, 
				TTDDD_Faces[i].E1, 
				TTDDD_Faces[i].E2, 
				TTDDD_Faces[i].E3);
    } /* for */
    fprintf(TTDDD_Stream, "\n");


    fprintf(TTDDD_Stream, "\tMTTR Type  4   %%\n");
    fprintf(TTDDD_Stream, "\tMTTR Index 1.0 %%\n");
    fprintf(TTDDD_Stream, "\n");

    fprintf(TTDDD_Stream, "\tSPEC Specularity 0\n");
    fprintf(TTDDD_Stream, "\tSPEC Hardness    0\n");
    fprintf(TTDDD_Stream, "\n");

    fprintf(TTDDD_Stream, "\tPRP0[0] 0   %% Blending (dither) factor.\n");
    fprintf(TTDDD_Stream, "\tPRP0[1] 0   %% Roughness factor.\n");
    fprintf(TTDDD_Stream, "\tPRP0[2] 0   %% Shading on/off.\n");
    fprintf(TTDDD_Stream, "\tPRP0[3] %d  %% Phong shading flag.\n", !Phong);
    fprintf(TTDDD_Stream, "\tPRP0[4] 0   %% Glossy flag. \n");
    fprintf(TTDDD_Stream, "\tPRP0[5] 0   %% Quickdraw flag.\n");
    fprintf(TTDDD_Stream, "\n");
 
    fprintf(TTDDD_Stream, "End DESC  %% End of DESC sub-chunk\n");
    fprintf(TTDDD_Stream, "\n");

    fprintf(TTDDD_Stream, "TOBJ %% End of object hierachy.\n");
    fprintf(TTDDD_Stream, "\n");

    fprintf(TTDDD_Stream, "End OBJ  %% End of object chunk\n");
    fprintf(TTDDD_Stream, "\n");

    Nbr_TTDDD_Points = 0;
    Nbr_TTDDD_Edges  = 0;
    Nbr_TTDDD_Faces  = 0;

} /* Write_TTDDD_Object */

static
void Generate_TTDDD_Face(Vector_T Point0, Vector_T Point1, 
		   	 Vector_T Point2, Vector_T Point3)
/************************************************************************/
/*									*/
/* Add the square given by the four points to the face/edge/point lists.*/
/*									*/
/************************************************************************/
{
    Boolean_T 	Face1_Ok, Face2_Ok;
    short	Point0_Id, Point1_Id, Point2_Id, Point3_Id;

    Face1_Ok = Face_Is_Ok(Point0, Point1, Point2);
    Face2_Ok = Face_Is_Ok(Point1, Point2, Point3);

    if (Face1_Ok || Face2_Ok) {

       	if (Face1_Ok) Point0_Id = Get_TTDDD_Point(Point0);

    	Point1_Id = Get_TTDDD_Point(Point1);
    	Point2_Id = Get_TTDDD_Point(Point2);

    	if (Face2_Ok) Point3_Id = Get_TTDDD_Point(Point3);

	if (Face1_Ok) Add_TTDDD_Face(Point0_Id, Point1_Id, Point2_Id); 

	if (Face2_Ok) Add_TTDDD_Face(Point2_Id, Point1_Id, Point3_Id);

    } /* if */

} /* Generate_TTDDD_Face */

Boolean_T Generate_TTDDD_File(char *Path)
/************************************************************************/
/*									*/
/* Tesselate the current object, and generate a TTDDD description of it	*/
/* in the file with name 'Path'.					*/
/*									*/
/************************************************************************/
{
    Tesselate_Info_T	TI;
    Boolean_T		Error;


    TTDDD_Stream = fopen(Path, "w");
    if (TTDDD_Stream == NULL) {
        sprintf(Error_Msg, "Couldn't create TTDDD file '%s'", Path);
	Display_Message(Error_Msg);
	return(TRUE);
    }

    TI.Patch_Begin         = NULL;
    TI.Patch_Generate_Face = Generate_TTDDD_Face;
    TI.Patch_End   	   = Write_TTDDD_Object;

    Set_Pointer(TRUE);

    Error = Tesselate_Object(&TI);

    fclose(TTDDD_Stream);

    Set_Pointer(FALSE);

    return(Error);

} /* Generate_TTDDD_File */
