Edit

IABSD.fr/xenocara/lib/libGLU/src/libnurbs/interface/insurfeval.cc

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2013-09-01 03:51:12
    Hash : 729f7da4
    Message : Update to GLU 9.0.0, GLU was previously part of Mesa but is now seperate. tested in a ports bulk build by landry@, ok matthieu@

  • lib/libGLU/src/libnurbs/interface/insurfeval.cc
  • /*
    ** License Applicability. Except to the extent portions of this file are
    ** made subject to an alternative license as permitted in the SGI Free
    ** Software License B, Version 1.1 (the "License"), the contents of this
    ** file are subject only to the provisions of the License. You may not use
    ** this file except in compliance with the License. You may obtain a copy
    ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
    ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
    ** 
    ** http://oss.sgi.com/projects/FreeB
    ** 
    ** Note that, as provided in the License, the Software is distributed on an
    ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
    ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
    ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
    ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
    ** 
    ** Original Code. The Original Code is: OpenGL Sample Implementation,
    ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
    ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
    ** Copyright in any portions created by third parties is as indicated
    ** elsewhere herein. All Rights Reserved.
    ** 
    ** Additional Notice Provisions: The application programming interfaces
    ** established by SGI in conjunction with the Original Code are The
    ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
    ** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
    ** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
    ** Window System(R) (Version 1.3), released October 19, 1998. This software
    ** was created using the OpenGL(R) version 1.2.1 Sample Implementation
    ** published by SGI, but has not been independently verified as being
    ** compliant with the OpenGL(R) version 1.2.1 Specification.
    **
    */
    /*
    */
    
    #include "gluos.h"
    #include <stdlib.h>
    #include <stdio.h>
    #include <GL/gl.h>
    #include <math.h>
    #include <assert.h>
    
    #include "glsurfeval.h"
    
    //extern int surfcount;
    
    //#define CRACK_TEST
    
    #define AVOID_ZERO_NORMAL
    
    #ifdef AVOID_ZERO_NORMAL
    #define myabs(x)  ((x>0)? x: (-x))
    #define MYZERO 0.000001
    #define MYDELTA 0.001
    #endif
    
    //#define USE_LOD
    #ifdef USE_LOD
    //#define LOD_EVAL_COORD(u,v) inDoEvalCoord2EM(u,v)
    #define LOD_EVAL_COORD(u,v) glEvalCoord2f(u,v)
    
    static void LOD_interpolate(REAL A[2], REAL B[2], REAL C[2], int j, int k, int pow2_level,
    			    REAL& u, REAL& v)
    {
      REAL a,a1,b,b1;
    
      a = ((REAL) j) / ((REAL) pow2_level);
      a1 = 1-a;
    
      if(j != 0)
        {
          b = ((REAL) k) / ((REAL)j);
          b1 = 1-b;
        }
      REAL x,y,z;
      x = a1;
      if(j==0)
        {
          y=0; z=0;
        }
      else{
        y = b1*a;
        z = b *a;
      }
    
      u = x*A[0] + y*B[0] + z*C[0];
      v = x*A[1] + y*B[1] + z*C[1];
    }
    
    void OpenGLSurfaceEvaluator::LOD_triangle(REAL A[2], REAL B[2], REAL C[2], 
    			 int level)     
    {
      int k,j;
      int pow2_level;
      /*compute 2^level*/
      pow2_level = 1;
    
      for(j=0; j<level; j++)
        pow2_level *= 2;
      for(j=0; j<=pow2_level-1; j++)
        {
          REAL u,v;
    
    /*      beginCallBack(GL_TRIANGLE_STRIP);*/
    glBegin(GL_TRIANGLE_STRIP);
          LOD_interpolate(A,B,C, j+1, j+1, pow2_level, u,v);
    #ifdef USE_LOD
          LOD_EVAL_COORD(u,v);
    //      glEvalCoord2f(u,v);
    #else
          inDoEvalCoord2EM(u,v);
    #endif
    
          for(k=0; k<=j; k++)
    	{
    	  LOD_interpolate(A,B,C,j,j-k,pow2_level, u,v);
    #ifdef USE_LOD
              LOD_EVAL_COORD(u,v);
    //	  glEvalCoord2f(u,v);
    #else
    	  inDoEvalCoord2EM(u,v);
    #endif
    
    	  LOD_interpolate(A,B,C,j+1,j-k,pow2_level, u,v);
    
    #ifdef USE_LOD
    	  LOD_EVAL_COORD(u,v);
    //	  glEvalCoord2f(u,v);
    #else
    	  inDoEvalCoord2EM(u,v);
    #endif
    	}
    //      endCallBack();	
    glEnd();
        }
    }
    
    void OpenGLSurfaceEvaluator::LOD_eval(int num_vert, REAL* verts, int type,
    		     int level
    		     )
    {
      int i,k;
      switch(type){
      case GL_TRIANGLE_STRIP:
      case GL_QUAD_STRIP:
        for(i=2, k=4; i<=num_vert-2; i+=2, k+=4)
          {
    	LOD_triangle(verts+k-4, verts+k-2, verts+k,
    		     level
    		     );
    	LOD_triangle(verts+k-2, verts+k+2, verts+k,
    		     level
    		     );
          }
        if(num_vert % 2 ==1) 
          {
    	LOD_triangle(verts+2*(num_vert-3), verts+2*(num_vert-2), verts+2*(num_vert-1),
    		     level
    		     );
          }
        break;      
      case GL_TRIANGLE_FAN:
        for(i=1, k=2; i<=num_vert-2; i++, k+=2)
          {
    	LOD_triangle(verts,verts+k, verts+k+2,
    		     level
    		     );
          }
        break;
      
      default:
        fprintf(stderr, "typy not supported in LOD_\n");
      }
    }
    	
    
    #endif //USE_LOD
    
    //#define  GENERIC_TEST
    #ifdef GENERIC_TEST
    extern float xmin, xmax, ymin, ymax, zmin, zmax; /*bounding box*/
    extern int temp_signal;
    
    static void gTessVertexSphere(float u, float v, float temp_normal[3], float temp_vertex[3])
    {
      float r=2.0;
      float Ox = 0.5*(xmin+xmax);
      float Oy = 0.5*(ymin+ymax);
      float Oz = 0.5*(zmin+zmax);
      float nx = cos(v) * sin(u);
      float ny = sin(v) * sin(u);
      float nz = cos(u);
      float x= Ox+r * nx;
      float y= Oy+r * ny;
      float z= Oz+r * nz;
    
      temp_normal[0] = nx;
      temp_normal[1] = ny;
      temp_normal[2] =  nz;
      temp_vertex[0] = x;
      temp_vertex[1] = y;
      temp_vertex[2] = z;
    
    //  glNormal3f(nx,ny,nz);
    //  glVertex3f(x,y,z);
    }
    
    static void gTessVertexCyl(float u, float v, float temp_normal[3], float temp_vertex[3])
    {
       float r=2.0;
      float Ox = 0.5*(xmin+xmax);
      float Oy = 0.5*(ymin+ymax);
      float Oz = 0.5*(zmin+zmax);
      float nx = cos(v);
      float ny = sin(v);
      float nz = 0;
      float x= Ox+r * nx;
      float y= Oy+r * ny;
      float z= Oz - 2*u;
    
      temp_normal[0] = nx;
      temp_normal[1] = ny;
      temp_normal[2] =  nz;
      temp_vertex[0] = x;
      temp_vertex[1] = y;
      temp_vertex[2] = z;
    
    /*  
      glNormal3f(nx,ny,nz);
      glVertex3f(x,y,z);
    */
    }
    
    #endif //GENERIC_TEST
    
    void OpenGLSurfaceEvaluator::inBPMListEval(bezierPatchMesh* list)
    {
      bezierPatchMesh* temp;
      for(temp = list; temp != NULL; temp = temp->next)
        {
          inBPMEval(temp);
        }
    }
    
    void OpenGLSurfaceEvaluator::inBPMEval(bezierPatchMesh* bpm)
    {
      int i,j,k,l;
      float u,v;
    
      int ustride = bpm->bpatch->dimension * bpm->bpatch->vorder;
      int vstride = bpm->bpatch->dimension;
      inMap2f( 
    	  (bpm->bpatch->dimension == 3)? GL_MAP2_VERTEX_3 : GL_MAP2_VERTEX_4,
    	  bpm->bpatch->umin,
    	  bpm->bpatch->umax,
    	  ustride,
    	  bpm->bpatch->uorder,
    	  bpm->bpatch->vmin,
    	  bpm->bpatch->vmax,
    	  vstride,
    	  bpm->bpatch->vorder,
    	  bpm->bpatch->ctlpoints);
      
      bpm->vertex_array = (float*) malloc(sizeof(float)* (bpm->index_UVarray/2) * 3+1); /*in case the origional dimenion is 4, then we need 4 space to pass to evaluator.*/
      assert(bpm->vertex_array);
      bpm->normal_array = (float*) malloc(sizeof(float)* (bpm->index_UVarray/2) * 3);
      assert(bpm->normal_array);
    #ifdef CRACK_TEST
    if(  global_ev_u1 ==2 &&   global_ev_u2 == 3
      && global_ev_v1 ==2 &&   global_ev_v2 == 3)
    {
    REAL vertex[4];
    REAL normal[4];
    #ifdef DEBUG
    printf("***number 1\n");
    #endif
    
    beginCallBack(GL_QUAD_STRIP, NULL);
    inEvalCoord2f(3.0, 3.0);
    inEvalCoord2f(2.0, 3.0);
    inEvalCoord2f(3.0, 2.7);
    inEvalCoord2f(2.0, 2.7);
    inEvalCoord2f(3.0, 2.0);
    inEvalCoord2f(2.0, 2.0);
    endCallBack(NULL);
    
    
    beginCallBack(GL_TRIANGLE_STRIP, NULL);
    inEvalCoord2f(2.0, 3.0);
    inEvalCoord2f(2.0, 2.0);
    inEvalCoord2f(2.0, 2.7);
    endCallBack(NULL);
    
    }
    
    /*
    if(  global_ev_u1 ==2 &&   global_ev_u2 == 3
      && global_ev_v1 ==1 &&   global_ev_v2 == 2)
    {
    #ifdef DEBUG
    printf("***number 2\n");
    #endif
    beginCallBack(GL_QUAD_STRIP);
    inEvalCoord2f(2.0, 2.0);
    inEvalCoord2f(2.0, 1.0);
    inEvalCoord2f(3.0, 2.0);
    inEvalCoord2f(3.0, 1.0);
    endCallBack();
    }
    */
    if(  global_ev_u1 ==1 &&   global_ev_u2 == 2
      && global_ev_v1 ==2 &&   global_ev_v2 == 3)
    {
    #ifdef DEBUG
    printf("***number 3\n");
    #endif
    beginCallBack(GL_QUAD_STRIP, NULL);
    inEvalCoord2f(2.0, 3.0);
    inEvalCoord2f(1.0, 3.0);
    inEvalCoord2f(2.0, 2.3);
    inEvalCoord2f(1.0, 2.3);
    inEvalCoord2f(2.0, 2.0);
    inEvalCoord2f(1.0, 2.0);
    endCallBack(NULL);
    
    beginCallBack(GL_TRIANGLE_STRIP, NULL);
    inEvalCoord2f(2.0, 2.3);
    inEvalCoord2f(2.0, 2.0);
    inEvalCoord2f(2.0, 3.0);
    endCallBack(NULL);
    
    }
    return;
    #endif
    
      k=0;
      l=0;
    
      for(i=0; i<bpm->index_length_array; i++)
        {
          beginCallBack(bpm->type_array[i], userData);
          for(j=0; j<bpm->length_array[i]; j++)
    	{
    	  u = bpm->UVarray[k];
    	  v = bpm->UVarray[k+1];
    	  inDoEvalCoord2NOGE(u,v,
    			     bpm->vertex_array+l,
    			     bpm->normal_array+l);
    
    	  normalCallBack(bpm->normal_array+l, userData);
    	  vertexCallBack(bpm->vertex_array+l, userData);
    
    	  k += 2;
    	  l += 3;
    	}
          endCallBack(userData);
        }
    }
    
    void OpenGLSurfaceEvaluator::inEvalPoint2(int i, int j)
    {
      REAL du, dv;
      REAL point[4];
      REAL normal[3];
      REAL u,v;
      du = (global_grid_u1 - global_grid_u0) / (REAL)global_grid_nu;
      dv = (global_grid_v1 - global_grid_v0) / (REAL)global_grid_nv;
      u = (i==global_grid_nu)? global_grid_u1:(global_grid_u0 + i*du);
      v = (j == global_grid_nv)? global_grid_v1: (global_grid_v0 +j*dv);
      inDoEvalCoord2(u,v,point,normal);
    }
    
    void OpenGLSurfaceEvaluator::inEvalCoord2f(REAL u, REAL v)
    {
    
      REAL point[4];
      REAL normal[3];
      inDoEvalCoord2(u,v,point, normal);
    }
    
    
    
    /*define a grid. store the values into the global variabls:
     * global_grid_*
     *These values will be used later by evaluating functions
     */
    void OpenGLSurfaceEvaluator::inMapGrid2f(int nu, REAL u0, REAL u1,
    		 int nv, REAL v0, REAL v1)
    {
     global_grid_u0 = u0;
     global_grid_u1 = u1;
     global_grid_nu = nu;
     global_grid_v0 = v0;
     global_grid_v1 = v1;
     global_grid_nv = nv;
    }
    
    void OpenGLSurfaceEvaluator::inEvalMesh2(int lowU, int lowV, int highU, int highV)
    {
      REAL du, dv;
      int i,j;
      REAL point[4];
      REAL normal[3];
      if(global_grid_nu == 0 || global_grid_nv == 0)
        return; /*no points need to be output*/
      du = (global_grid_u1 - global_grid_u0) / (REAL)global_grid_nu;
      dv = (global_grid_v1 - global_grid_v0) / (REAL)global_grid_nv;  
      
      if(global_grid_nu >= global_grid_nv){
        for(i=lowU; i<highU; i++){
          REAL u1 = (i==global_grid_nu)? global_grid_u1:(global_grid_u0 + i*du);
          REAL u2 = ((i+1) == global_grid_nu)? global_grid_u1: (global_grid_u0+(i+1)*du);
          
          bgnqstrip();
          for(j=highV; j>=lowV; j--){
    	REAL v1 = (j == global_grid_nv)? global_grid_v1: (global_grid_v0 +j*dv);
    	
    	inDoEvalCoord2(u1, v1, point, normal);
    	inDoEvalCoord2(u2, v1, point, normal);
          }
          endqstrip();
        }
      }
      
      else{
        for(i=lowV; i<highV; i++){
          REAL v1 = (i==global_grid_nv)? global_grid_v1:(global_grid_v0 + i*dv);
          REAL v2 = ((i+1) == global_grid_nv)? global_grid_v1: (global_grid_v0+(i+1)*dv);
          
          bgnqstrip();
          for(j=highU; j>=lowU; j--){
    	REAL u1 = (j == global_grid_nu)? global_grid_u1: (global_grid_u0 +j*du);	
    	inDoEvalCoord2(u1, v2, point, normal);
    	inDoEvalCoord2(u1, v1, point, normal);
          }
          endqstrip();
        }
      }
        
    }
    
    void OpenGLSurfaceEvaluator::inMap2f(int k,
    	     REAL ulower,
    	     REAL uupper,
    	     int ustride,
    	     int uorder,
    	     REAL vlower,
    	     REAL vupper,
    	     int vstride,
    	     int vorder,
    	     REAL *ctlPoints)
    {
      int i,j,x;
      REAL *data = global_ev_ctlPoints;
      
    
    
      if(k == GL_MAP2_VERTEX_3) k=3;
      else if (k==GL_MAP2_VERTEX_4) k =4;
      else {
        printf("error in inMap2f, maptype=%i is wrong, k,map is not updated\n", k);
        return;
      }
      
      global_ev_k = k;
      global_ev_u1 = ulower;
      global_ev_u2 = uupper;
      global_ev_ustride = ustride;
      global_ev_uorder = uorder;
      global_ev_v1 = vlower;
      global_ev_v2 = vupper;
      global_ev_vstride = vstride;
      global_ev_vorder = vorder;
    
      /*copy the contrl points from ctlPoints to global_ev_ctlPoints*/
      for (i=0; i<uorder; i++) {
        for (j=0; j<vorder; j++) {
          for (x=0; x<k; x++) {
    	data[x] = ctlPoints[x];
          }
          ctlPoints += vstride;
          data += k;
        }
        ctlPoints += ustride - vstride * vorder;
      }
    
    }
    
    
    /*
     *given a point p with homegeneous coordiante (x,y,z,w), 
     *let pu(x,y,z,w) be its partial derivative vector with
     *respect to u
     *and pv(x,y,z,w) be its partial derivative vector with repect to v.
     *This function returns the partial derivative vectors of the
     *inhomegensous coordinates, i.e., 
     * (x/w, y/w, z/w) with respect to u and v.
     */
    void OpenGLSurfaceEvaluator::inComputeFirstPartials(REAL *p, REAL *pu, REAL *pv)
    {
        pu[0] = pu[0]*p[3] - pu[3]*p[0];
        pu[1] = pu[1]*p[3] - pu[3]*p[1];
        pu[2] = pu[2]*p[3] - pu[3]*p[2];
    
        pv[0] = pv[0]*p[3] - pv[3]*p[0];
        pv[1] = pv[1]*p[3] - pv[3]*p[1];
        pv[2] = pv[2]*p[3] - pv[3]*p[2];
    }
    
    /*compute the cross product of pu and pv and normalize.
     *the normal is returned in retNormal
     * pu: dimension 3
     * pv: dimension 3
     * n: return normal, of dimension 3
     */
    void OpenGLSurfaceEvaluator::inComputeNormal2(REAL *pu, REAL *pv, REAL *n)
    {
      REAL mag; 
    
      n[0] = pu[1]*pv[2] - pu[2]*pv[1];
      n[1] = pu[2]*pv[0] - pu[0]*pv[2];
      n[2] = pu[0]*pv[1] - pu[1]*pv[0];  
    
      mag = sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
    
      if (mag > 0.0) {
         n[0] /= mag; 
         n[1] /= mag;
         n[2] /= mag;
      }
    }
     
    
    
    /*Compute point and normal
     *see the head of inDoDomain2WithDerivs
     *for the meaning of the arguments
     */
    void OpenGLSurfaceEvaluator::inDoEvalCoord2(REAL u, REAL v,
    			   REAL *retPoint, REAL *retNormal)
    {
    
      REAL du[4];
      REAL dv[4];
    
     
      assert(global_ev_k>=3 && global_ev_k <= 4);
      /*compute homegeneous point and partial derivatives*/
      inDoDomain2WithDerivs(global_ev_k, u, v, global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, retPoint, du, dv);
    
    #ifdef AVOID_ZERO_NORMAL
    
      if(myabs(dv[0]) <= MYZERO && myabs(dv[1]) <= MYZERO && myabs(dv[2]) <= MYZERO)
        {
    
          REAL tempdu[4];
          REAL tempdata[4];
          REAL u1 = global_ev_u1;
          REAL u2 = global_ev_u2;
          if(u-MYDELTA*(u2-u1) < u1)
    	u = u+ MYDELTA*(u2-u1);
          else
    	u = u-MYDELTA*(u2-u1);
          inDoDomain2WithDerivs(global_ev_k, u,v,global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, tempdata, tempdu, dv);
        }
      if(myabs(du[0]) <= MYZERO && myabs(du[1]) <= MYZERO && myabs(du[2]) <= MYZERO)
        {
          REAL tempdv[4];
          REAL tempdata[4];
          REAL v1 = global_ev_v1;
          REAL v2 = global_ev_v2;
          if(v-MYDELTA*(v2-v1) < v1)
    	v = v+ MYDELTA*(v2-v1);
          else
    	v = v-MYDELTA*(v2-v1);
          inDoDomain2WithDerivs(global_ev_k, u,v,global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, tempdata, du, tempdv);
        }
    #endif
    
    
      /*compute normal*/
      switch(global_ev_k){
      case 3:
        inComputeNormal2(du, dv, retNormal);
    
        break;
      case 4:
        inComputeFirstPartials(retPoint, du, dv);
        inComputeNormal2(du, dv, retNormal);
        /*transform the homegeneous coordinate of retPoint into inhomogenous one*/
        retPoint[0] /= retPoint[3];
        retPoint[1] /= retPoint[3];
        retPoint[2] /= retPoint[3];
        break;
      }
      /*output this vertex*/
    /*  inMeshStreamInsert(global_ms, retPoint, retNormal);*/
    
    
    
      glNormal3fv(retNormal);
      glVertex3fv(retPoint);
    
    
    
    
      #ifdef DEBUG
      printf("vertex(%f,%f,%f)\n", retPoint[0],retPoint[1],retPoint[2]);
      #endif
      
    
    
    }
    
    /*Compute point and normal
     *see the head of inDoDomain2WithDerivs
     *for the meaning of the arguments
     */
    void OpenGLSurfaceEvaluator::inDoEvalCoord2NOGE_BU(REAL u, REAL v,
    			   REAL *retPoint, REAL *retNormal)
    {
    
      REAL du[4];
      REAL dv[4];
    
     
      assert(global_ev_k>=3 && global_ev_k <= 4);
      /*compute homegeneous point and partial derivatives*/
    //   inPreEvaluateBU(global_ev_k, global_ev_uorder, global_ev_vorder, (u-global_ev_u1)/(global_ev_u2-global_ev_u1), global_ev_ctlPoints);
      inDoDomain2WithDerivsBU(global_ev_k, u, v, global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, retPoint, du, dv);
    
    
    #ifdef AVOID_ZERO_NORMAL
    
      if(myabs(dv[0]) <= MYZERO && myabs(dv[1]) <= MYZERO && myabs(dv[2]) <= MYZERO)
        {
    
          REAL tempdu[4];
          REAL tempdata[4];
          REAL u1 = global_ev_u1;
          REAL u2 = global_ev_u2;
          if(u-MYDELTA*(u2-u1) < u1)
    	u = u+ MYDELTA*(u2-u1);
          else
    	u = u-MYDELTA*(u2-u1);
          inDoDomain2WithDerivs(global_ev_k, u,v,global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, tempdata, tempdu, dv);
        }
      if(myabs(du[0]) <= MYZERO && myabs(du[1]) <= MYZERO && myabs(du[2]) <= MYZERO)
        {
          REAL tempdv[4];
          REAL tempdata[4];
          REAL v1 = global_ev_v1;
          REAL v2 = global_ev_v2;
          if(v-MYDELTA*(v2-v1) < v1)
    	v = v+ MYDELTA*(v2-v1);
          else
    	v = v-MYDELTA*(v2-v1);
          inDoDomain2WithDerivs(global_ev_k, u,v,global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, tempdata, du, tempdv);
        }
    #endif
    
      /*compute normal*/
      switch(global_ev_k){
      case 3:
        inComputeNormal2(du, dv, retNormal);
        break;
      case 4:
        inComputeFirstPartials(retPoint, du, dv);
        inComputeNormal2(du, dv, retNormal);
        /*transform the homegeneous coordinate of retPoint into inhomogenous one*/
        retPoint[0] /= retPoint[3];
        retPoint[1] /= retPoint[3];
        retPoint[2] /= retPoint[3];
        break;
      }
    }
    
    /*Compute point and normal
     *see the head of inDoDomain2WithDerivs
     *for the meaning of the arguments
     */
    void OpenGLSurfaceEvaluator::inDoEvalCoord2NOGE_BV(REAL u, REAL v,
    			   REAL *retPoint, REAL *retNormal)
    {
    
      REAL du[4];
      REAL dv[4];
    
     
      assert(global_ev_k>=3 && global_ev_k <= 4);
      /*compute homegeneous point and partial derivatives*/
    //   inPreEvaluateBV(global_ev_k, global_ev_uorder, global_ev_vorder, (v-global_ev_v1)/(global_ev_v2-global_ev_v1), global_ev_ctlPoints);
    
      inDoDomain2WithDerivsBV(global_ev_k, u, v, global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, retPoint, du, dv);
    
    
    #ifdef AVOID_ZERO_NORMAL
    
      if(myabs(dv[0]) <= MYZERO && myabs(dv[1]) <= MYZERO && myabs(dv[2]) <= MYZERO)
        {
    
          REAL tempdu[4];
          REAL tempdata[4];
          REAL u1 = global_ev_u1;
          REAL u2 = global_ev_u2;
          if(u-MYDELTA*(u2-u1) < u1)
    	u = u+ MYDELTA*(u2-u1);
          else
    	u = u-MYDELTA*(u2-u1);
          inDoDomain2WithDerivs(global_ev_k, u,v,global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, tempdata, tempdu, dv);
        }
      if(myabs(du[0]) <= MYZERO && myabs(du[1]) <= MYZERO && myabs(du[2]) <= MYZERO)
        {
          REAL tempdv[4];
          REAL tempdata[4];
          REAL v1 = global_ev_v1;
          REAL v2 = global_ev_v2;
          if(v-MYDELTA*(v2-v1) < v1)
    	v = v+ MYDELTA*(v2-v1);
          else
    	v = v-MYDELTA*(v2-v1);
          inDoDomain2WithDerivs(global_ev_k, u,v,global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, tempdata, du, tempdv);
        }
    #endif
    
      /*compute normal*/
      switch(global_ev_k){
      case 3:
        inComputeNormal2(du, dv, retNormal);
        break;
      case 4:
        inComputeFirstPartials(retPoint, du, dv);
        inComputeNormal2(du, dv, retNormal);
        /*transform the homegeneous coordinate of retPoint into inhomogenous one*/
        retPoint[0] /= retPoint[3];
        retPoint[1] /= retPoint[3];
        retPoint[2] /= retPoint[3];
        break;
      }
    }
     
    
    /*Compute point and normal
     *see the head of inDoDomain2WithDerivs
     *for the meaning of the arguments
     */
    void OpenGLSurfaceEvaluator::inDoEvalCoord2NOGE(REAL u, REAL v,
    			   REAL *retPoint, REAL *retNormal)
    {
    
      REAL du[4];
      REAL dv[4];
    
     
      assert(global_ev_k>=3 && global_ev_k <= 4);
      /*compute homegeneous point and partial derivatives*/
      inDoDomain2WithDerivs(global_ev_k, u, v, global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, retPoint, du, dv);
    
    
    #ifdef AVOID_ZERO_NORMAL
    
      if(myabs(dv[0]) <= MYZERO && myabs(dv[1]) <= MYZERO && myabs(dv[2]) <= MYZERO)
        {
    
          REAL tempdu[4];
          REAL tempdata[4];
          REAL u1 = global_ev_u1;
          REAL u2 = global_ev_u2;
          if(u-MYDELTA*(u2-u1) < u1)
    	u = u+ MYDELTA*(u2-u1);
          else
    	u = u-MYDELTA*(u2-u1);
          inDoDomain2WithDerivs(global_ev_k, u,v,global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, tempdata, tempdu, dv);
        }
      if(myabs(du[0]) <= MYZERO && myabs(du[1]) <= MYZERO && myabs(du[2]) <= MYZERO)
        {
          REAL tempdv[4];
          REAL tempdata[4];
          REAL v1 = global_ev_v1;
          REAL v2 = global_ev_v2;
          if(v-MYDELTA*(v2-v1) < v1)
    	v = v+ MYDELTA*(v2-v1);
          else
    	v = v-MYDELTA*(v2-v1);
          inDoDomain2WithDerivs(global_ev_k, u,v,global_ev_u1, global_ev_u2, global_ev_uorder, global_ev_v1, global_ev_v2, global_ev_vorder, global_ev_ctlPoints, tempdata, du, tempdv);
        }
    #endif
    
      /*compute normal*/
      switch(global_ev_k){
      case 3:
        inComputeNormal2(du, dv, retNormal);
        break;
      case 4:
        inComputeFirstPartials(retPoint, du, dv);
        inComputeNormal2(du, dv, retNormal);
        /*transform the homegeneous coordinate of retPoint into inhomogenous one*/
        retPoint[0] /= retPoint[3];
        retPoint[1] /= retPoint[3];
        retPoint[2] /= retPoint[3];
        break;
      }
    //  glNormal3fv(retNormal);
    //  glVertex3fv(retPoint);
    }
     
    void OpenGLSurfaceEvaluator::inPreEvaluateBV(int k, int uorder, int vorder, REAL vprime, REAL *baseData)
    {
      int j,row,col;
      REAL p, pdv;
      REAL *data;
    
      if(global_vprime != vprime || global_vorder != vorder) {      
        inPreEvaluateWithDeriv(vorder, vprime, global_vcoeff, global_vcoeffDeriv);
        global_vprime = vprime;
        global_vorder = vorder;
      }
    
      for(j=0; j<k; j++){
        data = baseData+j;
        for(row=0; row<uorder; row++){
          p = global_vcoeff[0] * (*data);
          pdv = global_vcoeffDeriv[0] * (*data);
          data += k;
          for(col = 1; col < vorder; col++){
    	p += global_vcoeff[col] *  (*data);
    	pdv += global_vcoeffDeriv[col] * (*data);
    	data += k;
          }
          global_BV[row][j]  = p;
          global_PBV[row][j]  = pdv;
        }
      }
    }
    
    void OpenGLSurfaceEvaluator::inPreEvaluateBU(int k, int uorder, int vorder, REAL uprime, REAL *baseData)
    {
      int j,row,col;
      REAL p, pdu;
      REAL *data;
    
      if(global_uprime != uprime || global_uorder != uorder) {      
        inPreEvaluateWithDeriv(uorder, uprime, global_ucoeff, global_ucoeffDeriv);
        global_uprime = uprime;
        global_uorder = uorder;
      }
    
      for(j=0; j<k; j++){
        data = baseData+j;
        for(col=0; col<vorder; col++){
          data = baseData+j + k*col;
          p = global_ucoeff[0] * (*data);
          pdu = global_ucoeffDeriv[0] * (*data);
          data += k*uorder;
          for(row = 1; row < uorder; row++){
    	p += global_ucoeff[row] *  (*data);
    	pdu += global_ucoeffDeriv[row] * (*data);
    	data += k * uorder;
          }
          global_BU[col][j]  = p;
          global_PBU[col][j]  = pdu;
        }
      }
    }
     
    void OpenGLSurfaceEvaluator::inDoDomain2WithDerivsBU(int k, REAL u, REAL v,
    						      REAL u1, REAL u2, int uorder,
    						      REAL v1, REAL v2, int vorder,
    						      REAL *baseData,
    						      REAL *retPoint, REAL* retdu, REAL *retdv)
    {
      int j, col;
    
      REAL vprime;
    
    
      if((u2 == u1) || (v2 == v1))
        return;
    
      vprime = (v - v1) / (v2 - v1);
    
    
      if(global_vprime != vprime || global_vorder != vorder) {
        inPreEvaluateWithDeriv(vorder, vprime, global_vcoeff, global_vcoeffDeriv);
        global_vprime = vprime;
        global_vorder = vorder;
      }
    
    
      for(j=0; j<k; j++)
        {
          retPoint[j] = retdu[j] = retdv[j] = 0.0;
          for (col = 0; col < vorder; col++)  {
    	retPoint[j] += global_BU[col][j] * global_vcoeff[col];
    	retdu[j] += global_PBU[col][j] * global_vcoeff[col];
    	retdv[j] += global_BU[col][j] * global_vcoeffDeriv[col];
          }
        }
    }    
       
    void OpenGLSurfaceEvaluator::inDoDomain2WithDerivsBV(int k, REAL u, REAL v,
    						      REAL u1, REAL u2, int uorder,
    						      REAL v1, REAL v2, int vorder,
    						      REAL *baseData,
    						      REAL *retPoint, REAL* retdu, REAL *retdv)
    {
      int j, row;
      REAL uprime;
    
    
      if((u2 == u1) || (v2 == v1))
        return;
      uprime = (u - u1) / (u2 - u1);
    
    
      if(global_uprime != uprime || global_uorder != uorder) {
        inPreEvaluateWithDeriv(uorder, uprime, global_ucoeff, global_ucoeffDeriv);
        global_uprime = uprime;
        global_uorder = uorder;
      }
    
    
      for(j=0; j<k; j++)
        {
          retPoint[j] = retdu[j] = retdv[j] = 0.0;
          for (row = 0; row < uorder; row++)  {
    	retPoint[j] += global_BV[row][j] * global_ucoeff[row];
    	retdu[j] += global_BV[row][j] * global_ucoeffDeriv[row];
    	retdv[j] += global_PBV[row][j] * global_ucoeff[row];
          }
        }
    }
      
    
    /*
     *given a Bezier surface, and parameter (u,v), compute the point in the object space,
     *and the normal
     *k: the dimension of the object space: usually 2,3,or 4.
     *u,v: the paramter pair.
     *u1,u2,uorder: the Bezier polynomial of u coord is defined on [u1,u2] with order uorder.
     *v1,v2,vorder: the Bezier polynomial of v coord is defined on [v1,v2] with order vorder.
     *baseData: contrl points. arranged as: (u,v,k).
     *retPoint:  the computed point (one point) with dimension k.
     *retdu: the computed partial derivative with respect to u.
     *retdv: the computed partial derivative with respect to v.
     */
    void OpenGLSurfaceEvaluator::inDoDomain2WithDerivs(int k, REAL u, REAL v, 
    				REAL u1, REAL u2, int uorder, 
    				REAL v1,  REAL v2, int vorder, 
    				REAL *baseData,
    				REAL *retPoint, REAL *retdu, REAL *retdv)
    {
        int j, row, col;
        REAL uprime;
        REAL vprime;
        REAL p;
        REAL pdv;
        REAL *data;
    
        if((u2 == u1) || (v2 == v1))
    	return;
        uprime = (u - u1) / (u2 - u1);
        vprime = (v - v1) / (v2 - v1);
        
        /* Compute coefficients for values and derivs */
    
        /* Use already cached values if possible */
        if(global_uprime != uprime || global_uorder != uorder) {
            inPreEvaluateWithDeriv(uorder, uprime, global_ucoeff, global_ucoeffDeriv);
    	global_uorder = uorder;
    	global_uprime = uprime;
        }
        if (global_vprime != vprime || 
    	  global_vorder != vorder) {
    	inPreEvaluateWithDeriv(vorder, vprime, global_vcoeff, global_vcoeffDeriv);
    	global_vorder = vorder;
    	global_vprime = vprime;
        }
    
        for (j = 0; j < k; j++) {
    	data=baseData+j;
    	retPoint[j] = retdu[j] = retdv[j] = 0.0;
    	for (row = 0; row < uorder; row++)  {
    	    /* 
    	    ** Minor optimization.
    	    ** The col == 0 part of the loop is extracted so we don't
    	    ** have to initialize p and pdv to 0.
    	    */
    	    p = global_vcoeff[0] * (*data);
    	    pdv = global_vcoeffDeriv[0] * (*data);
    	    data += k;
    	    for (col = 1; col < vorder; col++) {
    		/* Incrementally build up p, pdv value */
    		p += global_vcoeff[col] * (*data);
    		pdv += global_vcoeffDeriv[col] * (*data);
    		data += k;
    	    }
    	    /* Use p, pdv value to incrementally add up r, du, dv */
    	    retPoint[j] += global_ucoeff[row] * p;
    	    retdu[j] += global_ucoeffDeriv[row] * p;
    	    retdv[j] += global_ucoeff[row] * pdv;
    	}
        }  
    }
    
    
    /*
     *compute the Bezier polynomials C[n,j](v) for all j at v with 
     *return values stored in coeff[], where 
     *  C[n,j](v) = (n,j) * v^j * (1-v)^(n-j),
     *  j=0,1,2,...,n.
     *order : n+1
     *vprime: v
     *coeff : coeff[j]=C[n,j](v), this array store the returned values.
     *The algorithm is a recursive scheme:
     *   C[0,0]=1;
     *   C[n,j](v) = (1-v)*C[n-1,j](v) + v*C[n-1,j-1](v), n>=1
     *This code is copied from opengl/soft/so_eval.c:PreEvaluate
     */
    void OpenGLSurfaceEvaluator::inPreEvaluate(int order, REAL vprime, REAL *coeff)
    {
      int i, j;
      REAL oldval, temp;
      REAL oneMinusvprime;
      
      /*
       * Minor optimization
       * Compute orders 1 and 2 outright, and set coeff[0], coeff[1] to
         * their i==1 loop values to avoid the initialization and the i==1 loop.
         */
      if (order == 1) {
        coeff[0] = 1.0;
        return;
      }
      
      oneMinusvprime = 1-vprime;
      coeff[0] = oneMinusvprime;
      coeff[1] = vprime;
      if (order == 2) return;
      
      for (i = 2; i < order; i++) {
        oldval = coeff[0] * vprime;
        coeff[0] = oneMinusvprime * coeff[0];
        for (j = 1; j < i; j++) {
          temp = oldval;
          oldval = coeff[j] * vprime;
    	    coeff[j] = temp + oneMinusvprime * coeff[j];
        }
        coeff[j] = oldval;
      }
    }
    
    /*
     *compute the Bezier polynomials C[n,j](v) and derivatives for all j at v with 
     *return values stored in coeff[] and coeffDeriv[].
     *see the head of function inPreEvaluate for the definition of C[n,j](v)
     *and how to compute the values. 
     *The algorithm to compute the derivative is:
     *   dC[0,0](v) = 0.
     *   dC[n,j](v) = n*(dC[n-1,j-1](v) - dC[n-1,j](v)).
     *
     *This code is copied from opengl/soft/so_eval.c:PreEvaluateWidthDeriv
     */
    void OpenGLSurfaceEvaluator::inPreEvaluateWithDeriv(int order, REAL vprime, 
        REAL *coeff, REAL *coeffDeriv)
    {
      int i, j;
      REAL oldval, temp;
      REAL oneMinusvprime;
      
      oneMinusvprime = 1-vprime;
      /*
       * Minor optimization
       * Compute orders 1 and 2 outright, and set coeff[0], coeff[1] to 
       * their i==1 loop values to avoid the initialization and the i==1 loop.
       */
      if (order == 1) {
        coeff[0] = 1.0;
        coeffDeriv[0] = 0.0;
        return;
      } else if (order == 2) {
        coeffDeriv[0] = -1.0;
        coeffDeriv[1] = 1.0;
        coeff[0] = oneMinusvprime;
        coeff[1] = vprime;
        return;
      }
      coeff[0] = oneMinusvprime;
      coeff[1] = vprime;
      for (i = 2; i < order - 1; i++) {
        oldval = coeff[0] * vprime;
        coeff[0] = oneMinusvprime * coeff[0];
        for (j = 1; j < i; j++) {
          temp = oldval;
          oldval = coeff[j] * vprime;
          coeff[j] = temp + oneMinusvprime * coeff[j];
        }
        coeff[j] = oldval;
      }
      coeffDeriv[0] = -coeff[0];
      /*
       ** Minor optimization:
       ** Would make this a "for (j=1; j<order-1; j++)" loop, but it is always
       ** executed at least once, so this is more efficient.
       */
      j=1;
      do {
        coeffDeriv[j] = coeff[j-1] - coeff[j];
        j++;
      } while (j < order - 1);
      coeffDeriv[j] = coeff[j-1];
      
      oldval = coeff[0] * vprime;
      coeff[0] = oneMinusvprime * coeff[0];
      for (j = 1; j < i; j++) {
        temp = oldval;
        oldval = coeff[j] * vprime;
        coeff[j] = temp + oneMinusvprime * coeff[j];
      }
      coeff[j] = oldval;
    }
    
    void OpenGLSurfaceEvaluator::inEvalULine(int n_points, REAL v, REAL* u_vals, 
    	int stride, REAL ret_points[][3], REAL ret_normals[][3])
    {
      int i,k;
      REAL temp[4];
    inPreEvaluateBV_intfac(v);
    
      for(i=0,k=0; i<n_points; i++, k += stride)
        {
          inDoEvalCoord2NOGE_BV(u_vals[k],v,temp, ret_normals[i]);
    
          ret_points[i][0] = temp[0];
          ret_points[i][1] = temp[1];
          ret_points[i][2] = temp[2];
    
        }
    
    }
    
    void OpenGLSurfaceEvaluator::inEvalVLine(int n_points, REAL u, REAL* v_vals, 
    	int stride, REAL ret_points[][3], REAL ret_normals[][3])
    {
      int i,k;
      REAL temp[4];
    inPreEvaluateBU_intfac(u);
      for(i=0,k=0; i<n_points; i++, k += stride)
        {
          inDoEvalCoord2NOGE_BU(u, v_vals[k], temp, ret_normals[i]);
          ret_points[i][0] = temp[0];
          ret_points[i][1] = temp[1];
          ret_points[i][2] = temp[2];
        }
    }
          
    
    /*triangulate a strip bounded by two lines which are parallel  to U-axis
     *upperVerts: the verteces on the upper line
     *lowerVertx: the verteces on the lower line
     *n_upper >=1
     *n_lower >=1
     */
    void OpenGLSurfaceEvaluator::inEvalUStrip(int n_upper, REAL v_upper, REAL* upper_val, int n_lower, REAL v_lower, REAL* lower_val)
    {
      int i,j,k,l;
      REAL leftMostV[2];
     typedef REAL REAL3[3];
    
      REAL3* upperXYZ = (REAL3*) malloc(sizeof(REAL3)*n_upper);
      assert(upperXYZ);
      REAL3* upperNormal = (REAL3*) malloc(sizeof(REAL3) * n_upper);
      assert(upperNormal);
      REAL3* lowerXYZ = (REAL3*) malloc(sizeof(REAL3)*n_lower);
      assert(lowerXYZ);
      REAL3* lowerNormal = (REAL3*) malloc(sizeof(REAL3) * n_lower);
      assert(lowerNormal);
      
      inEvalULine(n_upper, v_upper, upper_val,  1, upperXYZ, upperNormal);
      inEvalULine(n_lower, v_lower, lower_val,  1, lowerXYZ, lowerNormal);
    
    
    
      REAL* leftMostXYZ;
      REAL* leftMostNormal;
    
      /*
       *the algorithm works by scanning from left to right.
       *leftMostV: the left most of the remaining verteces (on both upper and lower).
       *           it could an element of upperVerts or lowerVerts.
       *i: upperVerts[i] is the first vertex to the right of leftMostV on upper line   *j: lowerVerts[j] is the first vertex to the right of leftMostV on lower line   */
    
      /*initialize i,j,and leftMostV
       */
      if(upper_val[0] <= lower_val[0])
        {
          i=1;
          j=0;
    
          leftMostV[0] = upper_val[0];
          leftMostV[1] = v_upper;
          leftMostXYZ = upperXYZ[0];
          leftMostNormal = upperNormal[0];
        }
      else
        {
          i=0;
          j=1;
    
          leftMostV[0] = lower_val[0];
          leftMostV[1] = v_lower;
    
          leftMostXYZ = lowerXYZ[0];
          leftMostNormal = lowerNormal[0];
        }
      
      /*the main loop.
       *the invariance is that: 
       *at the beginning of each loop, the meaning of i,j,and leftMostV are 
       *maintained
       */
      while(1)
        {
          if(i >= n_upper) /*case1: no more in upper*/
            {
              if(j<n_lower-1) /*at least two vertices in lower*/
                {
                  bgntfan();
    	      glNormal3fv(leftMostNormal);
                  glVertex3fv(leftMostXYZ);
    
                  while(j<n_lower){
    		glNormal3fv(lowerNormal[j]);
    		glVertex3fv(lowerXYZ[j]);
    		j++;
    
                  }
                  endtfan();
                }
              break; /*exit the main loop*/
            }
          else if(j>= n_lower) /*case2: no more in lower*/
            {
              if(i<n_upper-1) /*at least two vertices in upper*/
                {
                  bgntfan();
    	      glNormal3fv(leftMostNormal);
    	      glVertex3fv(leftMostXYZ);
    	      
                  for(k=n_upper-1; k>=i; k--) /*reverse order for two-side lighting*/
    		{
    		  glNormal3fv(upperNormal[k]);
    		  glVertex3fv(upperXYZ[k]);
    		}
    
                  endtfan();
                }
              break; /*exit the main loop*/
            }
          else /* case3: neither is empty, plus the leftMostV, there is at least one triangle to output*/
            {
              if(upper_val[i] <= lower_val[j])
                {
    	      bgntfan();
    
    	      glNormal3fv(lowerNormal[j]);
    	      glVertex3fv(lowerXYZ[j]);
    
                  /*find the last k>=i such that 
                   *upperverts[k][0] <= lowerverts[j][0]
                   */
                  k=i;
    
                  while(k<n_upper)
                    {
                      if(upper_val[k] > lower_val[j])
                        break;
                      k++;
    
                    }
                  k--;
    
    
                  for(l=k; l>=i; l--)/*the reverse is for two-side lighting*/
                    {
    		  glNormal3fv(upperNormal[l]);
    		  glVertex3fv(upperXYZ[l]);
    
                    }
    	      glNormal3fv(leftMostNormal);
    	      glVertex3fv(leftMostXYZ);
    
                  endtfan();
    
                  /*update i and leftMostV for next loop
                   */
                  i = k+1;
    
    	      leftMostV[0] = upper_val[k];
    	      leftMostV[1] = v_upper;
    	      leftMostNormal = upperNormal[k];
    	      leftMostXYZ = upperXYZ[k];
                }
              else /*upperVerts[i][0] > lowerVerts[j][0]*/
                {
    	      bgntfan();
    	      glNormal3fv(upperNormal[i]);
    	      glVertex3fv(upperXYZ[i]);
    	      
                  glNormal3fv(leftMostNormal);
    	      glVertex3fv(leftMostXYZ);
    	      
    
                  /*find the last k>=j such that
                   *lowerverts[k][0] < upperverts[i][0]
                   */
                  k=j;
                  while(k< n_lower)
                    {
                      if(lower_val[k] >= upper_val[i])
                        break;
    		  glNormal3fv(lowerNormal[k]);
    		  glVertex3fv(lowerXYZ[k]);
    
                      k++;
                    }
                  endtfan();
    
                  /*update j and leftMostV for next loop
                   */
                  j=k;
    	      leftMostV[0] = lower_val[j-1];
    	      leftMostV[1] = v_lower;
    
    	      leftMostNormal = lowerNormal[j-1];
    	      leftMostXYZ = lowerXYZ[j-1];
                }     
            }
        }
      //clean up 
      free(upperXYZ);
      free(lowerXYZ);
      free(upperNormal);
      free(lowerNormal);
    }
    
    /*triangulate a strip bounded by two lines which are parallel  to V-axis
     *leftVerts: the verteces on the left line
     *rightVertx: the verteces on the right line
     *n_left >=1
     *n_right >=1
     */
    void OpenGLSurfaceEvaluator::inEvalVStrip(int n_left, REAL u_left, REAL* left_val, int n_right, REAL u_right, REAL* right_val)
    {
      int i,j,k,l;
      REAL botMostV[2];
      typedef REAL REAL3[3];
    
      REAL3* leftXYZ = (REAL3*) malloc(sizeof(REAL3)*n_left);
      assert(leftXYZ);
      REAL3* leftNormal = (REAL3*) malloc(sizeof(REAL3) * n_left);
      assert(leftNormal);
      REAL3* rightXYZ = (REAL3*) malloc(sizeof(REAL3)*n_right);
      assert(rightXYZ);
      REAL3* rightNormal = (REAL3*) malloc(sizeof(REAL3) * n_right);
      assert(rightNormal);
      
      inEvalVLine(n_left, u_left, left_val,  1, leftXYZ, leftNormal);
      inEvalVLine(n_right, u_right, right_val,  1, rightXYZ, rightNormal);
    
    
    
      REAL* botMostXYZ;
      REAL* botMostNormal;
    
      /*
       *the algorithm works by scanning from bot to top.
       *botMostV: the bot most of the remaining verteces (on both left and right).
       *           it could an element of leftVerts or rightVerts.
       *i: leftVerts[i] is the first vertex to the top of botMostV on left line   
       *j: rightVerts[j] is the first vertex to the top of botMostV on rightline   */
    
      /*initialize i,j,and botMostV
       */
      if(left_val[0] <= right_val[0])
        {
          i=1;
          j=0;
    
          botMostV[0] = u_left;
          botMostV[1] = left_val[0];
          botMostXYZ = leftXYZ[0];
          botMostNormal = leftNormal[0];
        }
      else
        {
          i=0;
          j=1;
    
          botMostV[0] = u_right;
          botMostV[1] = right_val[0];
    
          botMostXYZ = rightXYZ[0];
          botMostNormal = rightNormal[0];
        }
      
      /*the main loop.
       *the invariance is that: 
       *at the beginning of each loop, the meaning of i,j,and botMostV are 
       *maintained
       */
      while(1)
        {
          if(i >= n_left) /*case1: no more in left*/
            {
              if(j<n_right-1) /*at least two vertices in right*/
                {
                  bgntfan();
    	      glNormal3fv(botMostNormal);
                  glVertex3fv(botMostXYZ);
    
                  while(j<n_right){
    		glNormal3fv(rightNormal[j]);
    		glVertex3fv(rightXYZ[j]);
    		j++;
    
                  }
                  endtfan();
                }
              break; /*exit the main loop*/
            }
          else if(j>= n_right) /*case2: no more in right*/
            {
              if(i<n_left-1) /*at least two vertices in left*/
                {
                  bgntfan();
    	      glNormal3fv(botMostNormal);
    	      glVertex3fv(botMostXYZ);
    	      
                  for(k=n_left-1; k>=i; k--) /*reverse order for two-side lighting*/
    		{
    		  glNormal3fv(leftNormal[k]);
    		  glVertex3fv(leftXYZ[k]);
    		}
    
                  endtfan();
                }
              break; /*exit the main loop*/
            }
          else /* case3: neither is empty, plus the botMostV, there is at least one triangle to output*/
            {
              if(left_val[i] <= right_val[j])
                {
    	      bgntfan();
    
    	      glNormal3fv(rightNormal[j]);
    	      glVertex3fv(rightXYZ[j]);
    
                  /*find the last k>=i such that 
                   *leftverts[k][0] <= rightverts[j][0]
                   */
                  k=i;
    
                  while(k<n_left)
                    {
                      if(left_val[k] > right_val[j])
                        break;
                      k++;
    
                    }
                  k--;
    
    
                  for(l=k; l>=i; l--)/*the reverse is for two-side lighting*/
                    {
    		  glNormal3fv(leftNormal[l]);
    		  glVertex3fv(leftXYZ[l]);
    
                    }
    	      glNormal3fv(botMostNormal);
    	      glVertex3fv(botMostXYZ);
    
                  endtfan();
    
                  /*update i and botMostV for next loop
                   */
                  i = k+1;
    
    	      botMostV[0] = u_left;
    	      botMostV[1] = left_val[k];
    	      botMostNormal = leftNormal[k];
    	      botMostXYZ = leftXYZ[k];
                }
              else /*left_val[i] > right_val[j])*/
                {
    	      bgntfan();
    	      glNormal3fv(leftNormal[i]);
    	      glVertex3fv(leftXYZ[i]);
    	      
                  glNormal3fv(botMostNormal);
    	      glVertex3fv(botMostXYZ);
    	      
    
                  /*find the last k>=j such that
                   *rightverts[k][0] < leftverts[i][0]
                   */
                  k=j;
                  while(k< n_right)
                    {
                      if(right_val[k] >= left_val[i])
                        break;
    		  glNormal3fv(rightNormal[k]);
    		  glVertex3fv(rightXYZ[k]);
    
                      k++;
                    }
                  endtfan();
    
                  /*update j and botMostV for next loop
                   */
                  j=k;
    	      botMostV[0] = u_right;
    	      botMostV[1] = right_val[j-1];
    
    	      botMostNormal = rightNormal[j-1];
    	      botMostXYZ = rightXYZ[j-1];
                }     
            }
        }
      //clean up 
      free(leftXYZ);
      free(rightXYZ);
      free(leftNormal);
      free(rightNormal);
    }
    
    /*-----------------------begin evalMachine-------------------*/
    void OpenGLSurfaceEvaluator::inMap2fEM(int which, int k,
    	     REAL ulower,
    	     REAL uupper,
    	     int ustride,
    	     int uorder,
    	     REAL vlower,
    	     REAL vupper,
    	     int vstride,
    	     int vorder,
    	     REAL *ctlPoints)
    {
      int i,j,x;
      surfEvalMachine *temp_em;
      switch(which){
      case 0: //vertex
        vertex_flag = 1;
        temp_em = &em_vertex;
        break;
      case 1: //normal
        normal_flag = 1;
        temp_em = &em_normal;
        break;
      case 2: //color
        color_flag = 1;
        temp_em = &em_color;
        break;
      default:
        texcoord_flag = 1;
        temp_em = &em_texcoord;
        break;
      }
    
      REAL *data = temp_em->ctlPoints;
      
      temp_em->uprime = -1;//initilized
      temp_em->vprime = -1;
    
      temp_em->k = k;
      temp_em->u1 = ulower;
      temp_em->u2 = uupper;
      temp_em->ustride = ustride;
      temp_em->uorder = uorder;
      temp_em->v1 = vlower;
      temp_em->v2 = vupper;
      temp_em->vstride = vstride;
      temp_em->vorder = vorder;
    
      /*copy the contrl points from ctlPoints to global_ev_ctlPoints*/
      for (i=0; i<uorder; i++) {
        for (j=0; j<vorder; j++) {
          for (x=0; x<k; x++) {
    	data[x] = ctlPoints[x];
          }
          ctlPoints += vstride;
          data += k;
        }
        ctlPoints += ustride - vstride * vorder;
      }
    }
    
    void OpenGLSurfaceEvaluator::inDoDomain2WithDerivsEM(surfEvalMachine *em, REAL u, REAL v, 
    				REAL *retPoint, REAL *retdu, REAL *retdv)
    {
        int j, row, col;
        REAL the_uprime;
        REAL the_vprime;
        REAL p;
        REAL pdv;
        REAL *data;
    
        if((em->u2 == em->u1) || (em->v2 == em->v1))
    	return;
        the_uprime = (u - em->u1) / (em->u2 - em->u1);
        the_vprime = (v - em->v1) / (em->v2 - em->v1);
        
        /* Compute coefficients for values and derivs */
    
        /* Use already cached values if possible */
        if(em->uprime != the_uprime) {
            inPreEvaluateWithDeriv(em->uorder, the_uprime, em->ucoeff, em->ucoeffDeriv);
    	em->uprime = the_uprime;
        }
        if (em->vprime != the_vprime) {
    	inPreEvaluateWithDeriv(em->vorder, the_vprime, em->vcoeff, em->vcoeffDeriv);
    	em->vprime = the_vprime;
        }
    
        for (j = 0; j < em->k; j++) {
    	data=em->ctlPoints+j;
    	retPoint[j] = retdu[j] = retdv[j] = 0.0;
    	for (row = 0; row < em->uorder; row++)  {
    	    /* 
    	    ** Minor optimization.
    	    ** The col == 0 part of the loop is extracted so we don't
    	    ** have to initialize p and pdv to 0.
    	    */
    	    p = em->vcoeff[0] * (*data);
    	    pdv = em->vcoeffDeriv[0] * (*data);
    	    data += em->k;
    	    for (col = 1; col < em->vorder; col++) {
    		/* Incrementally build up p, pdv value */
    		p += em->vcoeff[col] * (*data);
    		pdv += em->vcoeffDeriv[col] * (*data);
    		data += em->k;
    	    }
    	    /* Use p, pdv value to incrementally add up r, du, dv */
    	    retPoint[j] += em->ucoeff[row] * p;
    	    retdu[j] += em->ucoeffDeriv[row] * p;
    	    retdv[j] += em->ucoeff[row] * pdv;
    	}
        }  
    }  
    
    void OpenGLSurfaceEvaluator::inDoDomain2EM(surfEvalMachine *em, REAL u, REAL v, 
    				REAL *retPoint)
    {
        int j, row, col;
        REAL the_uprime;
        REAL the_vprime;
        REAL p;
        REAL *data;
    
        if((em->u2 == em->u1) || (em->v2 == em->v1))
    	return;
        the_uprime = (u - em->u1) / (em->u2 - em->u1);
        the_vprime = (v - em->v1) / (em->v2 - em->v1);
        
        /* Compute coefficients for values and derivs */
    
        /* Use already cached values if possible */
        if(em->uprime != the_uprime) {
            inPreEvaluate(em->uorder, the_uprime, em->ucoeff);
    	em->uprime = the_uprime;
        }
        if (em->vprime != the_vprime) {
    	inPreEvaluate(em->vorder, the_vprime, em->vcoeff);
    	em->vprime = the_vprime;
        }
    
        for (j = 0; j < em->k; j++) {
    	data=em->ctlPoints+j;
    	retPoint[j] = 0.0;
    	for (row = 0; row < em->uorder; row++)  {
    	    /* 
    	    ** Minor optimization.
    	    ** The col == 0 part of the loop is extracted so we don't
    	    ** have to initialize p and pdv to 0.
    	    */
    	    p = em->vcoeff[0] * (*data);
    	    data += em->k;
    	    for (col = 1; col < em->vorder; col++) {
    		/* Incrementally build up p, pdv value */
    		p += em->vcoeff[col] * (*data);
    		data += em->k;
    	    }
    	    /* Use p, pdv value to incrementally add up r, du, dv */
    	    retPoint[j] += em->ucoeff[row] * p;
    	}
        }  
    }  
    
    
    void OpenGLSurfaceEvaluator::inDoEvalCoord2EM(REAL u, REAL v)
    {
      REAL temp_vertex[5];
      REAL temp_normal[3];
      REAL temp_color[4];
      REAL temp_texcoord[4];
    
      if(texcoord_flag)
        {
          inDoDomain2EM(&em_texcoord, u,v, temp_texcoord);
          texcoordCallBack(temp_texcoord, userData);
        }
      if(color_flag)
        {
          inDoDomain2EM(&em_color, u,v, temp_color);
          colorCallBack(temp_color, userData);
        }
    
      if(normal_flag) //there is a normla map
        {
          inDoDomain2EM(&em_normal, u,v, temp_normal);
          normalCallBack(temp_normal, userData);
        
          if(vertex_flag)
    	{
    	  inDoDomain2EM(&em_vertex, u,v,temp_vertex);
    	  if(em_vertex.k == 4)
    	    {
    	      temp_vertex[0] /= temp_vertex[3];
    	      temp_vertex[1] /= temp_vertex[3];
    	      temp_vertex[2] /= temp_vertex[3];	      
    	    }
              temp_vertex[3]=u;
              temp_vertex[4]=v;	  
    	  vertexCallBack(temp_vertex, userData);
    	}
        }
      else if(auto_normal_flag) //no normal map but there is a normal callbackfunctin
        {
          REAL du[4];
          REAL dv[4];
          
          /*compute homegeneous point and partial derivatives*/
          inDoDomain2WithDerivsEM(&em_vertex, u,v,temp_vertex,du,dv);
    
          if(em_vertex.k ==4)
    	inComputeFirstPartials(temp_vertex, du, dv);
    
    #ifdef AVOID_ZERO_NORMAL
          if(myabs(dv[0]) <= MYZERO && myabs(dv[1]) <= MYZERO && myabs(dv[2]) <= MYZERO)
    	{
    	  
    	  REAL tempdu[4];
    	  REAL tempdata[4];
    	  REAL u1 = em_vertex.u1;
    	  REAL u2 = em_vertex.u2;
    	  if(u-MYDELTA*(u2-u1) < u1)
    	    u = u+ MYDELTA*(u2-u1);
    	  else
    	    u = u-MYDELTA*(u2-u1);
    	  inDoDomain2WithDerivsEM(&em_vertex,u,v, tempdata, tempdu, dv);
    
    	  if(em_vertex.k ==4)
    	    inComputeFirstPartials(temp_vertex, du, dv);	  
    	}
          else if(myabs(du[0]) <= MYZERO && myabs(du[1]) <= MYZERO && myabs(du[2]) <= MYZERO)
    	{
    	  REAL tempdv[4];
    	  REAL tempdata[4];
    	  REAL v1 = em_vertex.v1;
    	  REAL v2 = em_vertex.v2;
    	  if(v-MYDELTA*(v2-v1) < v1)
    	    v = v+ MYDELTA*(v2-v1);
    	  else
    	    v = v-MYDELTA*(v2-v1);
    	  inDoDomain2WithDerivsEM(&em_vertex,u,v, tempdata, du, tempdv);
    
    	  if(em_vertex.k ==4)
    	    inComputeFirstPartials(temp_vertex, du, dv);
    	}
    #endif
    
          /*compute normal*/
          switch(em_vertex.k){
          case 3:
    
    	inComputeNormal2(du, dv, temp_normal);
    	break;
          case 4:
    
    //	inComputeFirstPartials(temp_vertex, du, dv);
    	inComputeNormal2(du, dv, temp_normal);
    
    	/*transform the homegeneous coordinate of retPoint into inhomogenous one*/
    	temp_vertex[0] /= temp_vertex[3];
    	temp_vertex[1] /= temp_vertex[3];
    	temp_vertex[2] /= temp_vertex[3];
    	break;
          }
          normalCallBack(temp_normal, userData);
          temp_vertex[3] = u;
          temp_vertex[4] = v;
          vertexCallBack(temp_vertex, userData);
          
        }/*end if auto_normal*/
      else //no normal map, and no normal callback function
        {
          if(vertex_flag)
    	{
    	  inDoDomain2EM(&em_vertex, u,v,temp_vertex);
    	  if(em_vertex.k == 4)
    	    {
    	      temp_vertex[0] /= temp_vertex[3];
    	      temp_vertex[1] /= temp_vertex[3];
    	      temp_vertex[2] /= temp_vertex[3];	      
    	    }
              temp_vertex[3] = u;
              temp_vertex[4] = v;
    	  vertexCallBack(temp_vertex, userData);
    	}
        }
    }
    
    
    void OpenGLSurfaceEvaluator::inBPMEvalEM(bezierPatchMesh* bpm)
    {
      int i,j,k;
      float u,v;
    
      int ustride;
      int vstride;
    
    #ifdef USE_LOD
      if(bpm->bpatch != NULL)
        {
          bezierPatch* p=bpm->bpatch;
          ustride = p->dimension * p->vorder;
          vstride = p->dimension;
    
          glMap2f( (p->dimension == 3)? GL_MAP2_VERTEX_3 : GL_MAP2_VERTEX_4,
    	      p->umin,
    	      p->umax,
    	      ustride,
    	      p->uorder,
    	      p->vmin,
    	      p->vmax,
    	      vstride,
    	      p->vorder,
    	      p->ctlpoints);
    
    
    /*
        inMap2fEM(0, p->dimension,
    	  p->umin,
    	  p->umax,
    	  ustride,
    	  p->uorder,
    	  p->vmin,
    	  p->vmax,
    	  vstride,
    	  p->vorder,
    	  p->ctlpoints);
    */
        }
    #else
    
      if(bpm->bpatch != NULL){
        bezierPatch* p = bpm->bpatch;
        ustride = p->dimension * p->vorder;
        vstride = p->dimension;
        inMap2fEM(0, p->dimension,
    	  p->umin,
    	  p->umax,
    	  ustride,
    	  p->uorder,
    	  p->vmin,
    	  p->vmax,
    	  vstride,
    	  p->vorder,
    	  p->ctlpoints);
      }
      if(bpm->bpatch_normal != NULL){
        bezierPatch* p = bpm->bpatch_normal;
        ustride = p->dimension * p->vorder;
        vstride = p->dimension;
        inMap2fEM(1, p->dimension,
    	  p->umin,
    	  p->umax,
    	  ustride,
    	  p->uorder,
    	  p->vmin,
    	  p->vmax,
    	  vstride,
    	  p->vorder,
    	  p->ctlpoints);
      }
      if(bpm->bpatch_color != NULL){
        bezierPatch* p = bpm->bpatch_color;
        ustride = p->dimension * p->vorder;
        vstride = p->dimension;
        inMap2fEM(2, p->dimension,
    	  p->umin,
    	  p->umax,
    	  ustride,
    	  p->uorder,
    	  p->vmin,
    	  p->vmax,
    	  vstride,
    	  p->vorder,
    	  p->ctlpoints);
      }
      if(bpm->bpatch_texcoord != NULL){
        bezierPatch* p = bpm->bpatch_texcoord;
        ustride = p->dimension * p->vorder;
        vstride = p->dimension;
        inMap2fEM(3, p->dimension,
    	  p->umin,
    	  p->umax,
    	  ustride,
    	  p->uorder,
    	  p->vmin,
    	  p->vmax,
    	  vstride,
    	  p->vorder,
    	  p->ctlpoints);
      }
    #endif
    
    
      k=0;
      for(i=0; i<bpm->index_length_array; i++)
        {
    #ifdef USE_LOD
          if(bpm->type_array[i] == GL_POLYGON) //a mesh
    	{
    	  GLfloat *temp = bpm->UVarray+k;
    	  GLfloat u0 = temp[0];
    	  GLfloat v0 = temp[1];
    	  GLfloat u1 = temp[2];
    	  GLfloat v1 = temp[3];
    	  GLint nu = (GLint) ( temp[4]);
    	  GLint nv = (GLint) ( temp[5]);
    	  GLint umin = (GLint) ( temp[6]);
    	  GLint vmin = (GLint) ( temp[7]);
    	  GLint umax = (GLint) ( temp[8]);
    	  GLint vmax = (GLint) ( temp[9]);
    
    	  glMapGrid2f(LOD_eval_level*nu, u0, u1, LOD_eval_level*nv, v0, v1);
    	  glEvalMesh2(GL_FILL, LOD_eval_level*umin, LOD_eval_level*umax, LOD_eval_level*vmin, LOD_eval_level*vmax);
    	}
          else
    	{
    	  LOD_eval(bpm->length_array[i], bpm->UVarray+k, bpm->type_array[i],
    		   0
    		   );
    	}
    	  k+= 2*bpm->length_array[i];       
        
    #else //undef  USE_LOD
    
    #ifdef CRACK_TEST
    if(  bpm->bpatch->umin == 2 &&   bpm->bpatch->umax == 3
      && bpm->bpatch->vmin ==2 &&    bpm->bpatch->vmax == 3)
    {
    REAL vertex[4];
    REAL normal[4];
    #ifdef DEBUG
    printf("***number ****1\n");
    #endif
    
    beginCallBack(GL_QUAD_STRIP, NULL);
    inDoEvalCoord2EM(3.0, 3.0);
    inDoEvalCoord2EM(2.0, 3.0);
    inDoEvalCoord2EM(3.0, 2.7);
    inDoEvalCoord2EM(2.0, 2.7);
    inDoEvalCoord2EM(3.0, 2.0);
    inDoEvalCoord2EM(2.0, 2.0);
    endCallBack(NULL);
    
    beginCallBack(GL_TRIANGLE_STRIP, NULL);
    inDoEvalCoord2EM(2.0, 3.0);
    inDoEvalCoord2EM(2.0, 2.0);
    inDoEvalCoord2EM(2.0, 2.7);
    endCallBack(NULL);
    
    }
    if(  bpm->bpatch->umin == 1 &&   bpm->bpatch->umax == 2
      && bpm->bpatch->vmin ==2 &&    bpm->bpatch->vmax == 3)
    {
    #ifdef DEBUG
    printf("***number 3\n");
    #endif
    beginCallBack(GL_QUAD_STRIP, NULL);
    inDoEvalCoord2EM(2.0, 3.0);
    inDoEvalCoord2EM(1.0, 3.0);
    inDoEvalCoord2EM(2.0, 2.3);
    inDoEvalCoord2EM(1.0, 2.3);
    inDoEvalCoord2EM(2.0, 2.0);
    inDoEvalCoord2EM(1.0, 2.0);
    endCallBack(NULL);
    
    beginCallBack(GL_TRIANGLE_STRIP, NULL);
    inDoEvalCoord2EM(2.0, 2.3);
    inDoEvalCoord2EM(2.0, 2.0);
    inDoEvalCoord2EM(2.0, 3.0);
    endCallBack(NULL);
    
    }
    return;
    #endif //CRACK_TEST
    
          beginCallBack(bpm->type_array[i], userData);
    
          for(j=0; j<bpm->length_array[i]; j++)
    	{
    	  u = bpm->UVarray[k];
    	  v = bpm->UVarray[k+1];
    #ifdef USE_LOD
              LOD_EVAL_COORD(u,v);
    //	  glEvalCoord2f(u,v);
    #else
    
    #ifdef  GENERIC_TEST
              float temp_normal[3];
              float temp_vertex[3];
              if(temp_signal == 0)
    	    {
    	      gTessVertexSphere(u,v, temp_normal, temp_vertex);
    //printf("normal=(%f,%f,%f)\n", temp_normal[0], temp_normal[1], temp_normal[2])//printf("veretx=(%f,%f,%f)\n", temp_vertex[0], temp_vertex[1], temp_vertex[2]);
                  normalCallBack(temp_normal, userData);
    	      vertexCallBack(temp_vertex, userData);
    	    }
              else if(temp_signal == 1)
    	    {
    	      gTessVertexCyl(u,v, temp_normal, temp_vertex);
    //printf("normal=(%f,%f,%f)\n", temp_normal[0], temp_normal[1], temp_normal[2])//printf("veretx=(%f,%f,%f)\n", temp_vertex[0], temp_vertex[1], temp_vertex[2]);
                  normalCallBack(temp_normal, userData);
    	      vertexCallBack(temp_vertex, userData);
    	    }
    	  else
    #endif //GENERIC_TEST
    
    	    inDoEvalCoord2EM(u,v);
         
    #endif //USE_LOD
    
    	  k += 2;
    	}
          endCallBack(userData);
    
    #endif //USE_LOD
        }
    }
    
    void OpenGLSurfaceEvaluator::inBPMListEvalEM(bezierPatchMesh* list)
    {
      bezierPatchMesh* temp;
      for(temp = list; temp != NULL; temp = temp->next)
        {
          inBPMEvalEM(temp);
        }
    }