#pragma ident "@(#)cginterface.c	1.6 99/04/26 SMI"

/*
 * Copyright (c) 1997 by Sun Microsystems, Inc.
 * All Rights Reserved
 */

/****************************************************************************
 * cginterface.c
 *
 * Author: Mike M. Chow
 * Email: mchow@graphics.lcs.mit.edu
 * 
 * The author grants Sun Microsystems the permission to distribute this
 * file for any of its products without fee or license of any form,
 * provided this file heading and notice appears along with this file
 * in all distributions. 
 * 
 *
 ***************************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include "types.h"
#include "triStrips.h"
#include "meshify.h"
#include "meshifyLocal.h"
#include "varyQuant.h"
#include "zdebug.h"
#include "utils.h"

#include "cgpi.h"
#include "cgpmesher.h"



int zzdebug = 0;

/* Color Table for Triangle Strip Colors */
#define MAX_COLOR_TABLE_SIZE 50     /* Total colors */
static int ColorTableInitialized = 0;
static float ColorTable[MAX_COLOR_TABLE_SIZE][4];
static int VaryQuant = 0;


void meshRegionFindViewport(MeshRegion *region);
int outputCgi = 0; /* Whether to output cgp user instructions, debugging*/

/*
 * gtmesh2prims
 *
 *     Converts a general triangle mesh to Primitives (global Prims).
 */

int
gtmesh2prims(MeshInfo *minfo, CGint geomQuant, 
	     CGint normalQuant, 
	     CGint colorQuant, 
	     CGenum quantEnforcement, 
	     int sendmbrefs,
	     int restartWinding)
{
  int index; /*Index of the current triangle vertex in strips */
  int i, j;               /* Counters */
  TriStrip *strip;        /* Temp TriStrip ptr */
  TriStrip *strips;       /* Temp TriStrips ptr */
  Vertex   *vert;         /* Temp Ptr to vertex */
  HeaderInfo *headerInfo;
  GTMesh *gtmesh;
  int colorCnt;
  ModelObject *obj;
  int firstTime = 1;
  
  zdo6 printf("*********  gtmesh2prims ********** \n");

  zdo6 printf("GeomQuant %d sendmbref %d\n", (CGshort) geomQuant,
	     sendmbrefs);

  /* Init color table if needed */
  if (!ColorTableInitialized){
    ColorTableInitialized = 1;
    for (i = 0; i < MAX_COLOR_TABLE_SIZE; i++){
	ColorTable[i][0] = randNum(0.0, 1.0);
	ColorTable[i][1] = randNum(0.0, 1.0);
	ColorTable[i][2] = randNum(0.0, 1.0);
	ColorTable[i][3] = 1.0;
    }
  }
  colorCnt = 0;

  if (minfo == NULL)
    return 1;

  for (gtmesh = minfo->gtmeshes; gtmesh != NULL; gtmesh = gtmesh->next){
      for (strip = gtmesh->triStrips->strips, 
	     headerInfo = gtmesh->headers; 
	   strip != NULL; 
	   strip = strip->next, headerInfo = headerInfo->next){
	
	cgBegin(CG_TRIANGLE_STRIP);
	
	if (outputCgi) printf("cgBegin(CG_TRIANGLE_STRIP);\n");
	/* For this strip, send down strip normals and points. */
	for (j = 0; j < strip->length; j++){
	  int index;
	
	  index = strip->tri[j];
	  
	  /* Hack to check for bad vertex */
	  if (!firstTime)
	    if ((index < 0) ||
		(index > obj->vertCnt))
	      continue;

	  vert = &gtmesh->varray[index];
	  if (firstTime){
	    obj = vert->facets->current->owner->obj;
	    firstTime = 0;
	  }

	  cgCurPtSetQuant(geomQuant, normalQuant, colorQuant);
	  cgCurPtSetHeader(CGP_POINT_HDR_QUANT_CHANGE_GEOM);
	  cgCurPtSetHeader(CGP_POINT_HDR_QUANT_CHANGE_NORMAL);
	  if (0 && vert->boundaryVertex) {
	    zdo6 printf("absl vert\n");
	    cgCurPtSetHeader(CGP_POINT_HDR_GEOMETRY_ABSOLUTE);
	  }

	  if (j == 0) 
	    cgCurPtSetHeader(restartWinding);
	  else
	    cgCurPtSetHeader(CGP_POINT_HDR_REPLACE_OLDEST);

#if 0
	  /*
	   * headerInfo->header[j].repl is garbage, apparently not implemented.
	   * The meshifier does not seem to have the restart winding specifed
	   * by cgGeomFrontFace() available. GTMESH_* constants don't exist.
	   */
	  switch (headerInfo->header[j].repl) {
	  case GTMESH_REPLACE_OLDEST:
	    cgCurPtSetHeader(CGP_POINT_HDR_REPLACE_OLDEST);
	    break;
	  case GTMESH_REPLACE_MIDDLE:
	    cgCurPtSetHeader(CGP_POINT_HDR_REPLACE_MIDDLE);
	    break;
	  case GTMESH_RESTART_CW:
	    cgCurPtSetHeader(CGP_POINT_HDR_RESTART_CW);
	    break;
	  case GTMESH_RESTART_CCW:
	    cgCurPtSetHeader(CGP_POINT_HDR_RESTART_CCW);
	    break;
	  default:
	    printf("Error: cgpmesher.c found gtmesh header repl %d unknown!\n",
		  headerInfo->header[j].repl);
	  }
#endif
	  if ((sendmbrefs))  {
	    /* Mesh Buffer Push */
	    if (headerInfo->header[j].mbp)
	      cgCurPtSetHeader(CGP_POINT_HDR_PUSH);
	    
	    /* Mesh Buffer reference */
	    if (headerInfo->header[j].mbref > 0){
	      zdo6 meshBufferPrint(gtmesh->varray);
	      cgCurPtSetHeader(CGP_POINT_HDR_REF);
              /* subtract 1 from Reference - HW goes from 0..n-1 */
	      cgCurPtSetMBRef((CGint)headerInfo->header[j].mbref - 1);
	      index = mbref(headerInfo->header[j].mbref);
	      zdo6 printf("mbref index %d value %d actual %d \n",
			  headerInfo->header[j].mbref, index, strip->tri[j]);
	    }
	  }

	  if ((sendmbrefs))  {
	    if (headerInfo->header[j].mbp){
	      if (outputCgi) printf(" cgMBPush();\n");
	    }
	    if (headerInfo->header[j].mbref > 0){
	      if (outputCgi) printf(" cgMBRef(%d);\n",
				    headerInfo->header[j].mbref);		
	    }
	  }

	  if (1 || (TYPES_VTX_FLAGS_HAS (vert, TYPES_VTX_NORM))){
	    float tmp[3];

	    if (outputCgi)  fprintf(stderr, " cgNormal3f(%f, %f, %f);\n", 
			vert->normal[0], vert->normal[1], vert->normal[2]);
	    tmp[0] = 1.0;
	    tmp[1] = 1.0;
	    tmp[2] = 1.0;
	    cgNormal3fv(vert->normal);
	  }

	  if (TYPES_VTX_FLAGS_HAS(vert, TYPES_VTX_COLOR3f))
	    cgColor3fv(vert->surf->diffuse);

	  if (TYPES_VTX_FLAGS_HAS(vert, TYPES_VTX_COLOR4f))
	    cgColor4fv(vert->surf->diffuse);

#if 0
	  if ((!(TYPES_VTX_FLAGS_HAS(vert, TYPES_VTX_COLOR3f))) &&
	      (!(TYPES_VTX_FLAGS_HAS(vert, TYPES_VTX_COLOR4f)))){
	    zdo6 cgColor4f(ColorTable[colorCnt][0]+0.4,
		      ColorTable[colorCnt][1]+0.4,
		      ColorTable[colorCnt][2]+0.4, 1.0);
	    cgColor3f(1.0, 1.0, 1.0);
	    colorCnt++;
	    if (colorCnt >= MAX_COLOR_TABLE_SIZE)
	      colorCnt = 0;
	  }
#endif
	  cgVertex3fv(vert->p);

	  zdo6 fprintf(stderr, "vertex ");
	  if (outputCgi) fprintf(stderr, " cgVertex3f(%f, %f, %f);\n", 
		      vert->p[0], vert->p[1], vert->p[2]);
	  zdo6 fprintf(stderr, "n\n");
	  zdo6 printPoint( vert->normal);
	  zdo6 printPoint( vert->p);
	}
	/* For each triangle vertex */
	if (outputCgi) printf("cgEnd();\n");
	cgEnd();
      }    /* For each strip */
    } /* For each gtmesh */
  return 0;
}



/* CurrVindex keeps track of the current vertex index of the
 * temp object vertex array. Very important; make sure to reset to 0
 * beginning of each temp object. */

static int CurrVindex = 0;




/*
 * getFacetPts
 *
 *     Gets a facet once the points are 
 */

void
getFacetPts(Facet *currFptr, CGPprimitive *curPrim,
	    int startIndex, int endIndex, 
	    ModelObject *obj, 
	    Part *currPart)
{
  CGPptbuffer*   curPtBuff;
  CGPpoint*    curPt;
  Point pt;
  CGuint      i, j;

  curPtBuff = &curPrim->ptBuff;
  curPt = &curPtBuff->pts[0];
  zdo6 fprintf(stderr, "\n Number of Points: %d\n", curPrim->numPts);
  
  /* use i = 1 so points are numbered correctly */
  for (j = 0, i = startIndex; i <= endIndex; i++)
    {
      int vindex;
      float rgba[4];

      zdo6 fprintf(stderr, "  %d:\n", i);
      
      pt[0] = curPt->x;
      pt[1] = curPt->y;
      pt[2] = curPt->z;
      
      zdo6 fprintf(stderr, "\tGeom:\t%f\t%f\t%f\n",
		   curPt->x, curPt->y, curPt->z);
      
      if ((vindex = findVertex(obj, pt)) != -1) {
	zdo6 fprintf(stderr, "old vert \n");
	facetAddVindex(currFptr, obj, vindex);
      }
      else {
	gotNewVertex(obj, pt);
	zdo6 fprintf(stderr, "new vert \n");
	facetAddVindex(currFptr, obj, CurrVindex);
	vindex = CurrVindex;
	CurrVindex++;
      }
      
      if ((curPrim->primFlags & CGP_PT_TYPE_VTX_NORMAL)){
	obj->varray[vindex].flags |= TYPES_VTX_NORM;
	obj->varray[vindex].normal[0] = curPt->nX;
	obj->varray[vindex].normal[1] = curPt->nY;
	obj->varray[vindex].normal[2] = curPt->nZ;
      }
      
      if (curPrim->primFlags & CGP_PT_TYPE_VTX_COLOR)
	obj->varray[vindex].flags |= TYPES_VTX_COLOR3f;
      
      rgba[0] = curPt->r;
      rgba[1] = curPt->g;
      rgba[2] = curPt->b;
      rgba[3] = curPt->a;
      if (obj->varray[vindex].surf == NULL) {
	  obj->varray[vindex].surf = surfCreateEmpty();
	  obj->varray[vindex].surf->diffuse = rgbaCreate(rgba);
      }
      
      /* todo: Add colors too. */
      
      if (curPrim->primFlags & CGP_PT_TYPE_VTX_NORMAL)
	zdo6 fprintf(stderr, "\tNormal:\t%f\t%f\t%f\n",
		     curPt->nX, curPt->nY, curPt->nZ);
      
      if (curPrim->primFlags & CGP_PT_TYPE_VTX_COLOR)
	{
	  zdo6 fprintf(stderr, "\tColor:\t%f\t%f\t%f",
		       curPt->r, curPt->g, curPt->b);
	  
	  if (curPrim->primFlags & CGP_PT_TYPE_VTX_COLOR_ALPHA)
	    zdo6 fprintf(stderr, "\t%f", curPt->a);
	  
	  zdo6 fprintf(stderr, "\n");
	}
      
      /* see if points continue onto next ptBuffer structure */
      /* TODO: rename j */
      if (++j == CGP_PT_BUFFER_SIZE)
	{
	  curPtBuff = curPtBuff->nextPtBuffer;
	  curPt = &curPtBuff->pts[0];
	  j = 0;
	}
      else
	curPt++;
    }
}  /* End of getFacetPts */





/*
 * prims2obj
 *
 *     Converts user Prims to meshifier's own data structures. 
 */

ModelObject 
*prims2obj(CGPprimlist *prims)
{
    CGPprimlist*   CGPprimlist;
    CGPprimitive*  curPrim;
    
    ModelObject *obj;                   /* The Model object */
    Part *currPart;
    Facet *newFacet;
    int triCnt = 0;    
    char filenameNoExten[600];
    TextureMap *currTextureMap;
    int totalNumFacets = 0, 
      totalNumPts = 0,
      totalOldPts = 0,
      totalNewPts = 0;
    
    CurrVindex = 0;

    obj = objCreate();
    obj->filepath = genString("temp");
    obj->name = genString("temp");
    gotNewPart(obj, &currPart, "temp");

    zdo6 fprintf(stdout, "\n\n**** cgPrintApiBuff ****\n\n");

    CGPprimlist = prims;
    while (CGPprimlist != NULL)
    {
      Facet *currFptr;
      Point pt;
      int vindex;
      int startIndex, endIndex;

        curPrim = &CGPprimlist->primitive;

        zdo6 fprintf(stderr, "\n\nPrimitive Type (%x): ", curPrim->primType);
        switch (curPrim->primType)
        {
            case CG_TRIANGLES:
                zdo6 fprintf(stderr, "CG_TRIANGLES\n");
		for (startIndex = 1, endIndex = 3;
		     endIndex <= curPrim->numPts;
		     startIndex += 3, endIndex += 3
		     ){
		  currFptr = facetNew(currPart);
		  totalNumFacets++;
		  partAddFacet(currPart, currFptr);
		  getFacetPts(currFptr, curPrim, startIndex, endIndex,
			      obj, currPart);
		}

                break;

            case CG_TRIANGLE_STRIP:
	      fprintf(stderr, "OGL cg extension error: CG_TRIANGLE_STRIP unsupported.\n");
	      fprintf(stderr, "Please use CG_TRIANGLES, CG_QUADS, or CG_POLYGON.\n");
	      zdo6 fprintf(stderr, "CG_TRIANGLE_STRIP\n");
                break;

            case CG_TRIANGLE_FAN:
	      fprintf(stderr, "OGL cg extension error: CG_TRIANGLE_FAN unsupported.\n");
              fprintf(stderr, "Please use CG_TRIANGLES, CG_QUADS, or CG_POLYGON.\n");  
	      zdo6 fprintf(stderr, "CG_TRIANGLE_FAN\n");
                break;

            case CG_QUADS:

	      zdo6 fprintf(stderr, "CG_QUADS\n");

	      for (startIndex = 1, endIndex = 4;
		   endIndex <= curPrim->numPts;
		   startIndex += 4, endIndex += 4
		   ){
		currFptr = facetNew(currPart);
		totalNumFacets++;
		partAddFacet(currPart, currFptr);
		getFacetPts(currFptr, curPrim, startIndex, endIndex,
			    obj, currPart);
	      }
	      break;
            case CG_QUAD_STRIP:
fprintf(stderr, "OGL cg extension error: CG_QUAD_STRIP unsupported.\n");
fprintf(stderr, "Please use CG_TRIANGLES, CG_QUADS, or CG_POLYGON.\n");
                zdo6 fprintf(stderr, "CG_QUAD_STRIP\n");
                break;

            case CG_POLYGON:
                zdo6 fprintf(stderr, "CG_POLYGON\n");
		currFptr = facetNew(currPart);
		totalNumFacets++;
		partAddFacet(currPart, currFptr);
		getFacetPts(currFptr, curPrim, 1, curPrim->numPts,
			    obj, currPart);
		zdo6 printf("added %d vindices to new facet\n",
			   currFptr->numVerts);
                break;
		
	default:
                zdo6 fprintf(stderr, " ** UNKNOWN **\n");
        }

        zdo6 fprintf(stderr, "\nFront Face: ");
        if (curPrim->primFlags & CGP_FRONT_FACE_CW)
            zdo6 fprintf(stderr, "CW\n");
        else
            zdo6 fprintf(stderr, "CCW\n");

        zdo6 fprintf(stderr, "\nGlobal Normal:\n");
        if (curPrim->primFlags & CGP_GLOBAL_NORMAL_VALID)
        {
            zdo6 fprintf(stderr, "  X: %f\n", curPrim->globalNormalX);
            zdo6 fprintf(stderr, "  Y: %f\n", curPrim->globalNormalY);
            zdo6 fprintf(stderr, "  Z: %f\n", curPrim->globalNormalZ);
        }
        else
            zdo6 fprintf(stderr, "  Not Valid\n");

        zdo6 fprintf(stderr, "\nGlobal Color:\n");
        if (curPrim->primFlags & CGP_GLOBAL_COLOR_VALID)
        {
            zdo6 fprintf(stderr, "  Red:   %f\n", curPrim->globalColorR);
            zdo6 fprintf(stderr, "  Green: %f\n", curPrim->globalColorG);
            zdo6 fprintf(stderr, "  Blue:  %f\n", curPrim->globalColorB);

            if (curPrim->primFlags & CGP_GLOBAL_ALPHA_VALID)
                zdo6 fprintf(stderr, "  Alpha: %f\n", curPrim->globalAlpha);
        }
        else
            zdo6 fprintf(stderr, "  Not Valid\n");

        zdo6 fprintf(stderr, "\nPoint Type (%x): ", curPrim->primFlags & 0x7);
        if (curPrim->primFlags & CGP_PT_TYPE_VTX_NORMAL)
            zdo6 fprintf(stderr, "Normal ");
        if (curPrim->primFlags & CGP_PT_TYPE_VTX_COLOR)
            zdo6 fprintf(stderr, "Color ");
        if (curPrim->primFlags & CGP_PT_TYPE_VTX_COLOR_ALPHA)
            zdo6 fprintf(stderr, "Alpha ");
        zdo6 fprintf(stderr, "Vertex3d\n");


        CGPprimlist = CGPprimlist->nextPrimitive;

    }  /* while (curPrim != NULL) */
    
    zdo6 printf("prims2obj, totalFacets %d totalPts %d OldPts %d newPts %d sum %d",
	       totalNumFacets, obj->vertCnt+1, totalOldPts, totalNewPts,
	       totalOldPts + totalNewPts);
    findObjEdges(obj);
    return obj;
}





/*
 * cgpBuildMeshWithMeshify
 *
 *      Given a set of user primitives, convert them into meshifier's data
 * structures, and then apply meshifier on them.  Afterwards, reconvert
 * meshifier's results back into Prims replacing the original Prims.
 * 
 * Modifies: Prims
 */

void
cgpBuildMeshWithMeshify(CGPprimlist *Prims, CGint geomQuant, CGint normalQuant,
			CGint colorQuant,
			CGenum quantEnforcement)
{
  ModelObject *obj;
  CGPprimlist *prims;
  CGviewport viewport;
  TriStrip *s;
  int numStrips = 0, totalLen= 0;
  int aveStripLen;
  Part *tmpPart;
  LinkedList *lptr;
  int error;
  int restartWinding ;

  /* Convert user's Prims into meshifier's own data structures. */
  obj = prims2obj(Prims);

  /* Save the front face winding order specified in the 1st primitive. */
  if (Prims->primitive.primFlags & CGP_FRONT_FACE_CW)
    restartWinding = CGP_POINT_HDR_RESTART_CW ;
  else
    restartWinding = CGP_POINT_HDR_RESTART_CCW ;

  /* Obj consists of only 1 part. */
  tmpPart = obj->parts[0];

  /* Find average strip length of this part*/
 
  partFindTriStrips(tmpPart);
  numStrips = 0;
  totalLen = 0;
  for (s = tmpPart->triStrips->strips; s != NULL; s = s->next){
    numStrips++;
    totalLen += s->length;
  }
  aveStripLen = (int) ((float) totalLen / (float) numStrips);
  zdo6 printf("numStrip %d totallen %d\n",
	 numStrips, totalLen);
  zdo6 printf("Part ave strip len:%d .\n", aveStripLen);

  objFindPartBbox(obj);
  groupObjRegions(obj);

  meshBufferInit();

  /* Apply meshifier on this part */
  meshifyLocal(tmpPart, aveStripLen);

  /* Get rid of all user's primitives that were sent down and 
     get ready for meshifier's primitives. */
  cgResetPrims();

  /* Set current state to begin stream again! */
  cgPutBeginStream();

  for (lptr = tmpPart->regions; lptr != NULL; lptr = lptr->next){
    MeshRegion *region = (MeshRegion *) lptr->current;

    meshRegionFindViewport(region);
    if (VaryQuant) {
      zdo6 printf("Using VaryQuant\n");
      /* Convert gtmesh into primitives. */
      error = gtmesh2prims(region->meshInfo, 
		   region->quantLevel, 
		   normalQuant,
		   colorQuant,
		   quantEnforcement, 1, restartWinding);
    }
    else {
      zdo6 printf("Using static quant\n");
      /* Convert gtmesh into primitives. */
      error = gtmesh2prims(region->meshInfo, 
		   geomQuant,
		   normalQuant,
		   colorQuant,
		   quantEnforcement, 1, restartWinding);
    }
  }

  /* At this point, Prims should point to the new set of primitives (all
     triangle strips) that consists of the gtmesh */
  cgPutEndStream() ;

  /* free the object */
  objDestroy(obj);

}



void
meshRegionFindViewport(MeshRegion *region)
{
  int i;
  float curr, max;
  float offset[3];

  meshRegionFindBbox(region); 
  max = 0.0;
  for (i = 0; i < 3; i++){
    offset[i] =
      (region->bbox->min[i] + region->bbox->max[i])/2.0;
  }

  for (i = 0; i < 3; i++){
    curr = (region->bboxSize[i]);
    if (curr > max)
      max = curr;
  }

  region->viewport.xo = offset[0];
  region->viewport.yo = offset[1];
  region->viewport.zo = offset[2];

  region->viewport.scale = 1.0/max;
  zdo6 printf("region bboxsize %2.2f %2.2f %2.2f\n",
	     region->bboxSize[0], 
	     region->bboxSize[1], 
	     region->bboxSize[2]);
}




