#ifndef lint
static const char	RCSid[] = "$Id: mgf2inv.c,v 2.4 2014/03/06 00:40:37 greg Exp $";
#endif
/*
 * Convert MGF to Inventor file.
 *
 *	December 1995	Greg Ward
 */

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <ctype.h>

#include <string.h>

#include "mgf_parser.h"

#include "lookup.h"

#define O_INV1		1	/* Inventor 1.0 output */
#define O_INV2		2	/* Inventor 2.0 output */
#define O_VRML1		3	/* VRML 1.0 output */

#define MAXID		48	/* maximum identifier length */

#define VERTFMT		"%+16.9e %+16.9e %+16.9e\n%+6.3f %+6.3f %+6.3f"
#define VZVECT		"+0.000 +0.000 +0.000"
#define VFLEN		92	/* total vertex string length */
#define MAXVERT		10240	/* maximum cached vertices */

#define setvkey(k,v)	sprintf(k,VERTFMT,(v)->p[0],(v)->p[1],(v)->p[2],\
					(v)->n[0],(v)->n[1],(v)->n[2]);

char	vlist[MAXVERT][VFLEN];	/* our vertex cache */
int	nverts;			/* current cache size */

LUTAB	vert_tab = LU_SINIT(NULL,NULL);

struct face {
	struct face	*next;		/* next face in list */
	short		nv;		/* number of vertices */
	short		vl[3];		/* vertex index list (variable) */
}	*flist, *flast;		/* our face cache */

#define newface(n)	(struct face *)malloc(sizeof(struct face) + \
				((n) > 3 ? (n)-3 : 0)*sizeof(short))
#define freeface(f)	free(f)

#define	TABSTOP		8	/* assumed number of characters per tab */
#define	SHIFTW		2	/* nesting shift width */
#define MAXIND		15	/* maximum indent level */

char	tabs[MAXIND*SHIFTW+1];	/* current tab-in string */

#define curmatname	(c_cmname == NULL ? "mat" : to_id(c_cmname))

int	outtype = O_INV2;	/* output format */

int i_comment(int ac, char **av);
int i_object(int ac, char **av);
int i_xf(int ac, char **av);
int put_xform(register XF_SPEC *spec);
int put_material(void);
int i_face(int ac, char **av);
int i_sph(int ac, char **av);
int i_cyl(int ac, char **av);
char * to_id(register char *name);
char * to_id(register char *name);
void flush_cache(void); 


int
main(
	int	argc,
	char	*argv[]
)
{
	int	i;
				/* initialize dispatch table */
	mg_ehand[MG_E_COMMENT] = i_comment;	/* we pass comments */
	mg_ehand[MG_E_COLOR] = c_hcolor;	/* they get color */
	mg_ehand[MG_E_CMIX] = c_hcolor;		/* they mix colors */
	mg_ehand[MG_E_CSPEC] = c_hcolor;	/* they get spectra */
	mg_ehand[MG_E_CXY] = c_hcolor;		/* they get chromaticities */
	mg_ehand[MG_E_CCT] = c_hcolor;		/* they get color temp's */
	mg_ehand[MG_E_CYL] = i_cyl;		/* we do cylinders */
	mg_ehand[MG_E_ED] = c_hmaterial;	/* they get emission */
	mg_ehand[MG_E_FACE] = i_face;		/* we do faces */
	mg_ehand[MG_E_MATERIAL] = c_hmaterial;	/* they get materials */
	mg_ehand[MG_E_NORMAL] = c_hvertex;	/* they get normals */
	mg_ehand[MG_E_OBJECT] = i_object;	/* we track object names */
	mg_ehand[MG_E_POINT] = c_hvertex;	/* they get points */
	mg_ehand[MG_E_RD] = c_hmaterial;	/* they get diffuse refl. */
	mg_ehand[MG_E_RS] = c_hmaterial;	/* they get specular refl. */
	mg_ehand[MG_E_SIDES] = c_hmaterial;	/* they get # sides */
	mg_ehand[MG_E_SPH] = i_sph;		/* we do spheres */
	mg_ehand[MG_E_TD] = c_hmaterial;	/* they get diffuse trans. */
	mg_ehand[MG_E_TS] = c_hmaterial;	/* they get specular trans. */
	mg_ehand[MG_E_VERTEX] = c_hvertex;	/* they get vertices */
	mg_ehand[MG_E_XF] = i_xf;		/* we track transforms */
	mg_init();		/* initialize the parser */
				/* get options and print format line */
	for (i = 1; i < argc && argv[i][0] == '-'; i++)
		if (!strcmp(argv[i], "-vrml"))
			outtype = O_VRML1;
		else if (!strcmp(argv[i], "-1"))
			outtype = O_INV1;
		else if (!strcmp(argv[i], "-2"))
			outtype = O_INV2;
		else
			goto userr;
	switch (outtype) {
	case O_INV1:
		printf("#Inventor V1.0 ascii\n");
		break;
	case O_INV2:
		printf("#Inventor V2.0 ascii\n");
		break;
	case O_VRML1:
		printf("#VRML V1.0 ascii\n");
		break;
	}
	printf("## Translated from MGF Version %d.%d\n", MG_VMAJOR, MG_VMINOR);
	printf("Separator {\n");		/* begin root node */
						/* general properties */
	printf("MaterialBinding { value OVERALL }\n");
	printf("NormalBinding { value PER_VERTEX_INDEXED }\n");
	if (outtype != O_INV1) {
		printf("ShapeHints {\n");
		printf("\tvertexOrdering CLOCKWISE\n");
		printf("\tfaceType UNKNOWN_FACE_TYPE\n");
		printf("}\n");
	}
	if (i == argc) {	/* load standard input */
		if (mg_load(NULL) != MG_OK)
			exit(1);
		if (mg_nunknown)
			printf("## %s: %u unknown entities\n",
					argv[0], mg_nunknown);
	}
				/* load MGF files */
	for ( ; i < argc; i++) {
		printf("## %s %s ##############################\n",
				argv[0], argv[i]);
		mg_nunknown = 0;
		if (mg_load(argv[i]) != MG_OK)
			exit(1);
		if (mg_nunknown)
			printf("## %s %s: %u unknown entities\n",
					argv[0], argv[i], mg_nunknown);
	}
	flush_cache();		/* flush face cache, just in case */
	printf("}\n");		/* close root node */
	exit(0);
userr:
	fprintf(stderr, "%s: [-1|-2|-vrml] [file] ..\n", argv[0]);
	exit(1);
}


void
indent(				/* indent in or out */
	int	deeper
)
{
	static int	level;		/* current nesting level */
	register int	i;
	register char	*cp;

	if (deeper) level++;		/* in or out? */
	else if (level > 0) level--;
					/* compute actual shift */
	if ((i = level) > MAXIND) i = MAXIND;
	cp = tabs;
	for (i *= SHIFTW; i >= TABSTOP; i -= TABSTOP)
		*cp++ = '\t';
	while (i--)
		*cp++ = ' ';
	*cp = '\0';
}


int
i_comment(			/* transfer comment as is */
	int	ac,
	char	**av
)
{
	fputs(tabs, stdout);
	putchar('#');			/* Inventor comment character */
	while (--ac > 0) {
		putchar(' ');
		fputs(*++av, stdout);
	}
	putchar('\n');
	return(MG_OK);
}


int
i_object(			/* group object name */
	int	ac,
	char	**av
)
{
	static int	objnest;

	flush_cache();			/* flush cached objects */
	if (ac == 2) {				/* start group */
		printf("%sDEF %s Group {\n", tabs, to_id(av[1]));
		indent(1);
		objnest++;
		return(MG_OK);
	}
	if (ac == 1) {				/* end group */
		if (--objnest < 0)
			return(MG_ECNTXT);
		indent(0);
		fputs(tabs, stdout);
		fputs("}\n", stdout);
		return(MG_OK);
	}
	return(MG_EARGC);
}


int
i_xf(				/* transform object(s) */
	int	ac,
	char	**av
)
{
	static long	xfid;
	register XF_SPEC	*spec;

	flush_cache();			/* flush cached objects */
	if (ac == 1) {			/* end of transform */
		if ((spec = xf_context) == NULL)
			return(MG_ECNTXT);
		indent(0);			/* close original segment */
		printf("%s}\n", tabs);
		indent(0);
		printf("%s}\n", tabs);
		if (spec->xarr != NULL) {	/* check for iteration */
			register struct xf_array	*ap = spec->xarr;
			register int	n;

			ap->aarg[ap->ndim-1].i = 1;	/* iterate array */
			for ( ; ; ) {
				n = ap->ndim-1;
				while (ap->aarg[n].i < ap->aarg[n].n) {
					sprintf(ap->aarg[n].arg, "%d",
							ap->aarg[n].i);
					printf("%sSeparator {\n", tabs);
					indent(1);
					(void)put_xform(spec);
					printf("%sUSE _xf%ld\n", tabs,
							spec->xid);
					indent(0);
					printf("%s}\n", tabs);
					++ap->aarg[n].i;
				}
				ap->aarg[n].i = 0;
				(void)strcpy(ap->aarg[n].arg, "0");
				while (n-- && ++ap->aarg[n].i >= ap->aarg[n].n) {
					ap->aarg[n].i = 0;
					(void)strcpy(ap->aarg[n].arg, "0");
				}
				if (n < 0)
					break;
				sprintf(ap->aarg[n].arg, "%d", ap->aarg[n].i);
			}
		}
						/* pop transform */
		xf_context = spec->prev;
		free_xf(spec);
		return(MG_OK);
	}
					/* else allocate new transform */
	if ((spec = new_xf(ac-1, av+1)) == NULL)
		return(MG_EMEM);
	spec->xid = ++xfid;		/* assign unique ID */
	spec->prev = xf_context;	/* push onto stack */
	xf_context = spec;
					/* translate xf specification */
	printf("%sSeparator {\n", tabs);
	indent(1);
	if (put_xform(spec) < 0)
		return(MG_ETYPE);
	printf("%sDEF _xf%ld Group {\n", tabs, spec->xid);	/* begin */
	indent(1);
	return(MG_OK);
}


int
put_xform(			/* translate and print transform */
	register XF_SPEC	*spec
)
{
	register char	**av;
	register int	n;

	n = xf_ac(spec) - xf_ac(spec->prev);
	if (xf(&spec->xf, n, av=xf_av(spec)) != n)
		return(-1);
	printf("%sMatrixTransform {\n", tabs);
	indent(1);
	printf("%s# xf", tabs);		/* put out original as comment */
	while (n--) {
		putchar(' ');
		fputs(*av++, stdout);
	}
	putchar('\n');			/* put out computed matrix */
	printf("%smatrix %13.9g %13.9g %13.9g %13.9g\n", tabs,
			spec->xf.xfm[0][0], spec->xf.xfm[0][1],
			spec->xf.xfm[0][2], spec->xf.xfm[0][3]);
	for (n = 1; n < 4; n++)
		printf("%s       %13.9g %13.9g %13.9g %13.9g\n", tabs,
				spec->xf.xfm[n][0], spec->xf.xfm[n][1],
				spec->xf.xfm[n][2], spec->xf.xfm[n][3]);
	indent(0);
	printf("%s}\n", tabs);
	return(0);
}


int
put_material(void)			/* put out current material */
{
	char	*mname = curmatname;
	float	rgbval[3];

	if (!c_cmaterial->clock) {	/* current, just use it */
		printf("%sUSE %s\n", tabs, mname);
		return(0);
	}
				/* else update definition */
	printf("%sDEF %s Group {\n", tabs, mname);
	indent(1);
	printf("%sMaterial {\n", tabs);
	indent(1);
	ccy2rgb(&c_cmaterial->rd_c, c_cmaterial->rd, rgbval);
	printf("%sambientColor %.4f %.4f %.4f\n", tabs,
			rgbval[0], rgbval[1], rgbval[2]);
	printf("%sdiffuseColor %.4f %.4f %.4f\n", tabs,
			rgbval[0], rgbval[1], rgbval[2]);
	if (c_cmaterial->rs > FTINY) {
		ccy2rgb(&c_cmaterial->rs_c, c_cmaterial->rs, rgbval);
		printf("%sspecularColor %.4f %.4f %.4f\n", tabs,
				rgbval[0], rgbval[1], rgbval[2]);
		printf("%sshininess %.3f\n", tabs, 1.-sqrt(c_cmaterial->rs_a));
	}
	if (c_cmaterial->ed > FTINY) {
		ccy2rgb(&c_cmaterial->ed_c, 1.0, rgbval);
		printf("%semissiveColor %.4f %.4f %.4f\n", tabs,
				rgbval[0], rgbval[1], rgbval[2]);
	}
	if (c_cmaterial->ts > FTINY)
		printf("%stransparency %.4f\n", tabs,
				c_cmaterial->ts + c_cmaterial->td);
	indent(0);
	printf("%s}\n", tabs);
	if (outtype != O_INV1)
		printf("%sShapeHints { shapeType %s faceType UNKNOWN_FACE_TYPE }\n",
			tabs,
			c_cmaterial->sided ? "SOLID" : "UNKNOWN_SHAPE_TYPE");
	indent(0);
	printf("%s}\n", tabs);
	c_cmaterial->clock = 0;
	return(0);
}


int
i_face(			/* translate an N-sided face */
	int	ac,
	char	**av
)
{
	static char	lastmat[MAXID];
	struct face	*newf;
	register C_VERTEX	*vp;
	register LUENT	*lp;
	register int	i;

	if (ac < 4)
		return(MG_EARGC);
	if ( strcmp(lastmat, curmatname) || c_cmaterial->clock ||
			nverts == 0 || nverts+ac-1 >= MAXVERT) {
		flush_cache();			/* new cache */
		lu_init(&vert_tab, MAXVERT);
		printf("%sSeparator {\n", tabs);
		indent(1);
		if (put_material() < 0)		/* put out material */
			return(MG_EBADMAT);
		(void)strcpy(lastmat, curmatname);
	}
				/* allocate new face */
	if ((newf = newface(ac-1)) == NULL)
		return(MG_EMEM);
	newf->nv = ac-1;
				/* get vertex references */
	for (i = 0; i < newf->nv; i++) {
		if ((vp = c_getvert(av[i+1])) == NULL)
			return(MG_EUNDEF);
		setvkey(vlist[nverts], vp);
		lp = lu_find(&vert_tab, vlist[nverts]);
		if (lp == NULL)
			return(MG_EMEM);
		if (lp->key == NULL)
			lp->key = (char *)vlist[nverts++];
		newf->vl[i] = ((char (*)[VFLEN])lp->key - vlist);
	}
				/* add to face list */
	newf->next = NULL;
	if (flist == NULL)
		flist = newf;
	else
		flast->next = newf;
	flast = newf;
	return(MG_OK);		/* we'll actually put it out later */
}


int
i_sph(			/* translate sphere description */
	int	ac,
	char	**av
)
{
	register C_VERTEX	*cent;

	if (ac != 3)
		return(MG_EARGC);
	flush_cache();		/* flush vertex cache */
	printf("%sSeparator {\n", tabs);
	indent(1);
				/* put out current material */
	if (put_material() < 0)
		return(MG_EBADMAT);
				/* get center */
	if ((cent = c_getvert(av[1])) == NULL)
		return(MG_EUNDEF);
				/* get radius */
	if (!isflt(av[2]))
		return(MG_ETYPE);
	printf("%sTranslation { translation %13.9g %13.9g %13.9g }\n", tabs,
			cent->p[0], cent->p[1], cent->p[2]);
	printf("%sSphere { radius %s }\n", tabs, av[2]);
	indent(0);
	printf("%s}\n", tabs);
	return(MG_OK);
}


int
i_cyl(			/* translate a cylinder description */
	int	ac,
	char	**av
)
{
	register C_VERTEX	*v1, *v2;
	FVECT	va;
	double	length, angle;

	if (ac != 4)
		return(MG_EARGC);
	flush_cache();		/* flush vertex cache */
	printf("%sSeparator {\n", tabs);
	indent(1);
				/* put out current material */
	if (put_material() < 0)
		return(MG_EBADMAT);
				/* get endpoints */
	if (((v1 = c_getvert(av[1])) == NULL) | ((v2 = c_getvert(av[3])) == NULL))
		return(MG_EUNDEF);
				/* get radius */
	if (!isflt(av[2]))
		return(MG_ETYPE);
				/* compute transform */
	va[0] = v2->p[0] - v1->p[0];
	va[1] = v2->p[1] - v1->p[1];
	va[2] = v2->p[2] - v1->p[2];
	length = VLEN(va);
	angle = Acos(va[1]/length);
	printf("%sTranslation { translation %13.9g %13.9g %13.9g }\n", tabs,
			.5*(v1->p[0]+v2->p[0]), .5*(v1->p[1]+v2->p[1]),
			.5*(v1->p[2]+v2->p[2]));
	printf("%sRotation { rotation %.9g %.9g %.9g %.9g }\n", tabs,
			va[2], 0., -va[0], angle);
				/* open-ended */
	printf("%sCylinder { parts SIDES height %13.9g radius %s }\n", tabs,
			length, av[2]);
	indent(0);
	printf("%s}\n", tabs);
	return(MG_OK);
}


char *
to_id(			/* make sure a name is a valid Inventor ID */
	register char	*name
)
{
	static char	id[MAXID];
	register char	*cp;

	for (cp = id; *name && cp < MAXID-1+id; name++)
		if (isalnum(*name) || *name == '_')
			*cp++ = *name;
		else
			*cp++ = '_';
	*cp = '\0';
	return(id);
}


void
flush_cache(void)			/* put out cached faces */
{
	int	VFSEPPOS = strchr(vlist[0],'\n') - vlist[0];
	int	donorms = 0;
	register struct face	*f;
	register int	i;

	if (nverts == 0)
		return;
					/* put out coordinates */
	printf("%sCoordinate3 {\n", tabs);
	indent(1);
	vlist[0][VFSEPPOS] = '\0';
	printf("%spoint [ %s", tabs, vlist[0]);
	for (i = 1; i < nverts; i++) {
		vlist[i][VFSEPPOS] = '\0';
		printf(",\n%s        %s", tabs, vlist[i]);
		if (strcmp(VFSEPPOS+1+vlist[i], VZVECT))
			donorms++;
	}
	indent(0);
	printf(" ]\n%s}\n", tabs);
	if (donorms) {			/* put out normals */
		printf("%sNormal {\n", tabs);
		indent(1);
		printf("%svector [ %s", tabs, VFSEPPOS+1+vlist[0]);
		for (i = 1; i < nverts; i++)
			printf(",\n%s         %s", tabs, VFSEPPOS+1+vlist[i]);
		indent(0);
		printf(" ]\n%s}\n", tabs);
	}
					/* put out faces */
	printf("%sIndexedFaceSet {\n", tabs);
	indent(1);
	f = flist;			/* coordinate indices */
	printf("%scoordIndex [ %d", tabs, f->vl[0]);
	for (i = 1; i < f->nv; i++)
		printf(", %d", f->vl[i]);
	for (f = f->next; f != NULL; f = f->next) {
		printf(", -1,\n%s             %d", tabs, f->vl[0]);
		for (i = 1; i < f->nv; i++)
			printf(", %d", f->vl[i]);
	}
	printf(" ]\n");
	if (donorms) {
		f = flist;			/* normal indices */
		printf("%snormalIndex [ %d", tabs, f->vl[0]);
		for (i = 1; i < f->nv; i++)
			printf(", %d", f->vl[i]);
		for (f = f->next; f != NULL; f = f->next) {
			printf(", -1,\n%s              %d", tabs, f->vl[0]);
			for (i = 1; i < f->nv; i++)
				printf(", %d", f->vl[i]);
		}
		printf(" ]\n");
	}
	indent(0);			/* close IndexedFaceSet */
	printf("%s}\n", tabs);
	indent(0);			/* close face group */
	printf("%s}\n", tabs);
	while ((f = flist) != NULL) {	/* free face list */
		flist = f->next;
		freeface(f);
	}
	lu_done(&vert_tab);		/* clear lookup table */
	nverts = 0;
}
