[net.sources] Graphics source in C: hsalgs/poly_drawl.c

ken@turtleva.UUCP (Ken Turkowski) (12/16/83)

echo x - hsalgs/poly_drawl.c
cat >hsalgs/poly_drawl.c <<'!Funky!Stuff!'






/* --------------------------------------------------------------
   Clip, and display, as lines, polygonal objects in the standard
   binary format.

	input format is defined in "man hsalg_input"
  ---------------------------------------------------------------
*/


#include <ctype.h>
#include <stdio.h>

#define TRUE             1
#define FALSE            0
#define NULLCHAR	'\0'
#define DtoR		3.14159 / 180.
#define DET_CODE	0x20746564
#define IN               0
#define OUTLFT           1
#define OUTRGT           2
#define OUTTOP           4
#define OUTBOT           8
#define OUTHTR          16
#define POLYSIZE        64
#define MAXPTS       32768  /* must be <= 32768 indexed by shorts */
#define MAXPOLYS     32768  /*  "  */
#define MAXVTCES    131072
#define sqr(x)    ((x)*(x))
#define LINE_LENGTH     81


static struct {float x,y,z;} pts[MAXPTS];
static short npts,npolys,nvtces,clipping,backfaces;
static short cnt[MAXPTS],vtces[MAXVTCES];
static short bfacing[MAXPOLYS],clp[MAXPOLYS];

static double matrix[16];		/* current object transform matrix */
static double cot_x,cot_y;

/* +++++++++++++++++++++++++ MAIN +++++++++++++++++++++++++++++++++++++++++ */
main()
{
    char instrg[LINE_LENGTH],keywd[LINE_LENGTH],remainder[LINE_LENGTH],dvc[3];
    char *eofchk,*gets();
    short i,first_obj;
    long ivtx;
    double atof(),sqrt(),fabs();
    FILE *popen(),*stream;

    nocore();			   /* prevent dumping core on error */

    clipping = TRUE;	backfaces = FALSE;   first_obj = TRUE;

    /* scan input for keywords */
    do
    {   eofchk = gets(instrg);
	if (eofchk != NULL) get_term(instrg,keywd,remainder);  /* get keyword */

	if ((eofchk == NULL) || ((strcmp(keywd,"object") == 0) && (!first_obj)))
        {   prepare_obj();  /* transform, etc. last object if end or next obj */

            /* ------ run through polygons, if not rejected,
                  put out move and draw commands   ------------------  */
            ivtx = 0;
            for (i=0; i<npolys; i++)
            {   short size;
                size = vtces[ivtx++];

	        if ((clp[i] >= 0) && (!bfacing[i] || backfaces))/*rejected?*/
                {   struct { double x,y,z; } poly [POLYSIZE],poly2[POLYSIZE];
                    short k,m,npts;
		    npts = size;
                    if (npts > POLYSIZE)
                        error("(poly_drawl) %d > POLYSIZE\n",npts);

                    for (k=0; k<npts; k++)                  /* copy polygon */
                    {   m = vtces[ivtx++];
                        poly[k].x = pts[m].x;
                        poly[k].y = pts[m].y;
                        poly[k].z = pts[m].z;
		    }

                    for (k=0; k<npts; k++)       /* predistort for clipping */
                    {   poly[k].x *= cot_x;    poly[k].y *= cot_y;   }
                    if (clp[i] > 0)  polclp(0,&npts,poly,poly2);
                    if (npts > 2)
                    {  for (k=0; k<npts; k++)       /* take to screen space */
                       { poly[k].x /= poly[k].z; poly[k].y /= poly[k].z*4./3.; }

		       fprintf(stream,"m %g %g 1.\n",poly[npts-1].x,poly[npts-1].y);
		       for (k=0; k<npts; k++)
		           fprintf(stream,"d %g %g 1.\n",poly[k].x,poly[k].y);
                    }
                }
	        else ivtx += size;
	    }   /* for each polygon */
	}

	/* process input line, beginning with keyword */
	if ((strcmp(keywd,"device") == 0) && (first_obj)) /*get disply dvc*/
    	{   sscanf(remainder,"%s",dvc);
	         if (dvc[0] == 'm')  stream = popen("megdrawl","w");
	    else if (dvc[0] == 'v')  stream = popen("vtdrawl","w");
	    else if (dvc[0] == 'c')  stream = popen("crtdrawl","w");
	    else if (dvc[0] == 'h')  stream = popen("h19drawl","w");
	    else error(" strange line-drawing device - %s",dvc);
    	}

    	else if ( strcmp(keywd,"object") == 0 ) 	/* get an object file */
	{   getobject(remainder,&npts,pts,&npolys,&nvtces,vtces);
	    first_obj = FALSE;   clipping = TRUE;   backfaces = FALSE;
	}
	else if ( strcmp(keywd,"transform") == 0 )/*get eyespace transform*/
	    for (i=0; i<16; i+=4) sscanf(gets(instrg),"%F%F%F%F",&matrix[i],
			                &matrix[i+1],&matrix[i+2],&matrix[i+3]);

        else if ( strcmp(keywd,"no_clipping") == 0 )   /* need clipping? */
	    clipping = FALSE;

	else if ( strcmp(keywd,"view_angle") == 0)
 	{   double view_angle,cos(),sin();
	    sscanf(remainder,"%F",&view_angle);
	    cot_x = cos(DtoR * view_angle/2.) / sin(DtoR * view_angle/2.);
	    cot_y = cot_x / .75;
	}

    }   while (eofchk != NULL);		/* continue until input exhausted */
    pclose(stream);	/* close stream to line drawer, wait for termination */
}   /* done with main program */





/* ++++++++++++++++++++++++ GETOBJECT ++++++++++++++++++++++++++++++++++++ */
getobject(instrg,npts,pts,npolys,nvtces,vtces)	/* read in object file */
    char *instrg;  short *npts,*npolys,*nvtces,vtces[];
    struct { float x,y,z; } pts[];
{   FILE *input;    short i;    long j,code;
    char fname[LINE_LENGTH],string[LINE_LENGTH],keywd[LINE_LENGTH];

    get_term(instrg,fname,instrg);     /* strip leading and trailing blanks */
    input = fopen(fname,"r");
    if (input == NULL) error("(poly_drawl) can't open .obj file %s\n",fname);

    while (fgets(string,LINE_LENGTH,input) != NULL)   /* read for detail file */
    {   get_term(string,keywd,string);
	if      (strcmp(keywd,"detail") == 0)   get_term(string,fname,string);

    	else if (strcmp(keywd,"type") == 0)  /*keep backfacing*/
	{   while (strlen(string) > 4)
	    {   get_term(string,keywd,string);
	        if (strcmp(keywd,"open") == 0)  backfaces = TRUE;
	    }
	}
    }
    fclose(input);		   /* close ".obj" file and open detail file */
    input = fopen(fname,"r");
    if (input == NULL) error("(poly_drawl) can't open .det file %s\n",fname);

    fread(&code,4,1,input);			   /* read file type header */
    if (code != DET_CODE)
    {   fprintf(stderr,"poly_drawl:  %s notted tagged detail file\n",fname);
	close(input);    return;    /* apparently not a detail file, give up */
    }
    fread(npts,2,1,input);     fread(npolys,2,1,input);
    if ((*npts > MAXPTS) || (*npolys > MAXPOLYS))
        error(" (poly_drawl) object too big!! %d > MAXPTS or %d > MAXPOLYS\n",
              *npts,*npolys);

    fread(pts,4,(*npts)*3,input);

    j = 0;
    for (i=0; i<*npolys; i++)
    {   short  num;    long k;
        fread(&num,2,1,input);
        vtces[j++] = num;
        if (fread(&vtces[j],2,num,input) < num) error(" bad polygon # %d\n",i);
        for (k=j; k<j+num; k++) vtces[k] -=1;  /* -1  so pointers start at 0 */
        j += num;
    }
    *nvtces = j;
    fclose(input);
}




/* +++++++++++++++++++++ GET_TERM +++++++++++++++++++++++++++++++++ */
get_term(instrg,term,remainder)		/* remove first term from string */
char *instrg,*term,*remainder;  /* blanks, tabs, nulls, commas are separators */
{   short i,index1,index2;
    index1 = 0;				/* find first non-separator */
    while ((instrg[index1] == ' ') 	|| (instrg[index1] == '\t') ||
	   (instrg[index1] == NULLCHAR) || (instrg[index1] == ',' )) index1++;
    index2 = index1;			/* find next separator */
    while ((instrg[index2] != ' ') 	&& (instrg[index2] != '\t') &&
	   (instrg[index2] != NULLCHAR) && (instrg[index2] != ',' ) &&
	   (instrg[index2] != '\n'))				     index2++;
    for (i=index1; i<index2; i++)  term[i-index1] = instrg[i];
    term[i-index1] = NULLCHAR;
    while ((instrg[i] != NULLCHAR) && (instrg[i] != '\n'))
	{  remainder[i-index2] = instrg[i];  i++;  }
    remainder[i-index2] = NULLCHAR;
}





/* ++++++++++++++++++++++++ POLCLP +++++++++++++++++++++++++++++++++++++ */
polclp(pass,npts,pts,pt2)        /* polygon clipper (eyespace) */
    short *npts,pass;    struct { double x,y,z; } pts[],pt2[];
{   short i,lk,m;   float dist,last_dist;
    if ((pass == 4) || (*npts < 3))  return;        /* completion conditions */
    last_dist = 0.0;    m = 0;    lk = 0;
    for (i=0; i<=*npts; i++)
    {   short k;
        k = (i == *npts) ?  0 : i;
        switch (pass)
        {   case 0: { dist = pts[k].z+pts[k].x; break; }  /* left side  */
            case 1: { dist = pts[k].z-pts[k].x; break; }  /* right side */
            case 2: { dist = pts[k].z+pts[k].y; break; }  /* bottom  */
            case 3: { dist = pts[k].z-pts[k].y; break; }  /* top  */
        }
        if (i == 0) {  last_dist = dist;  lk = k;  continue;  } /* 1st pnt? */

        if (last_dist * dist < 0.0)  /* put out point if clip plane crossed */
        {   float t,t1;
            t = dist / (dist - last_dist);  t1 = 1.0 - t;
            pt2[m].x = pts[k].x * t1 + pts[lk].x * t;
            pt2[m].y = pts[k].y * t1 + pts[lk].y * t;
            pt2[m].z = pts[k].z * t1 + pts[lk].z * t;
	    m++;
        }
        if (dist >= 0.0)                            /* copy point if inside */
        {   pt2[m].x = pts[k].x;   pt2[m].y = pts[k].y;   pt2[m].z = pts[k].z;
            m++;
        }
        lk = k;    last_dist = dist;
    }						   /* recurse for next plane */
    *npts = m;   polclp(++pass,npts,pt2,pts);
}



/* ++++++++++++++++++++++ PREPARE_OBJ ++++++++++++++++++++++++++++++++++ */
prepare_obj()           /* transform vertices, compute normals, etc. */
{   short i;
    /* ----- run through points, transform ----- */
    for (i=0; i<npts; i++)
    {   transform(&pts[i],matrix,&pts[i]);
        cnt[i] = IN;                                    /* set clip code */
    }
    if (clipping) for (i=0; i<npts; i++)  /* tag if clipping */
    {   if ( pts[i].z < 0.0)               cnt[i] |= OUTHTR;
        if ((pts[i].x)*cot_x < -pts[i].z)  cnt[i] |= OUTLFT;
        if ((pts[i].x)*cot_x >  pts[i].z)  cnt[i] |= OUTRGT;
        if ((pts[i].y)*cot_y < -pts[i].z)  cnt[i] |= OUTBOT;
        if ((pts[i].y)*cot_y >  pts[i].z)  cnt[i] |= OUTTOP;
    }


    /* ------------- run through polygons, check backfacing,
                     do trivial clip tests and tag polys -------------- */
    {   short in,out;    long ivtx;
        ivtx = in = out = 0;
        for (i=0; i<npolys; i++)
        {   struct { float x,y,z; } vec;   short size;   long j;

            size = vtces[ivtx++];
            if (clipping)           /* trivial clip test if clipping */
            {   out = 0;    in = 0;
                for (j=ivtx; j<ivtx+size; j++)
                {   out &= cnt[vtces[j]];   in |= cnt[vtces[j]];   }
            }
            if (out)   clp[i] = -out;   /* trivial rejection, skip this one */
            else
            {   clp[i] = in;     /* store code for trivial acceptance or clip */

                j = ivtx;            /* get normal vector, do backface test */
                X_prod(&vec,pts[vtces[j]],pts[vtces[j+1]],pts[vtces[j+2]]);
                j = vtces[ivtx];
                if ((vec.x*pts[j].x + vec.y*pts[j].y + vec.z*pts[j].z) >= 0.0)
                     bfacing[i] = TRUE;                        /* backfacing */
                else bfacing[i] = FALSE;
            }
            ivtx += size;                         /* increment to next poly */
        }
    }
}



/* +++++++++++++++++++++++++ TRANSFORM ++++++++++++++++++++++++++++++++++++ */
transform(orgpt,mtx,pt)                          /* apply transform to point */
    struct { float x,y,z; } *orgpt,*pt;    double mtx[16];
{   float tx,ty,tz;
    tx = mtx[0]*orgpt->x + mtx[4]*orgpt->y + mtx[8]*orgpt->z   + mtx[12];
    ty = mtx[1]*orgpt->x + mtx[5]*orgpt->y + mtx[9]*orgpt->z   + mtx[13];
    tz = mtx[2]*orgpt->x + mtx[6]*orgpt->y + mtx[10]*orgpt->z  + mtx[14];
    pt->x = tx;    pt->y = ty;    pt->z = tz;
}




/* ++++++++++++++++++++++++++ X_PROD +++++++++++++++++++++++++++++++++++ */
X_prod(vec,pt1,pt2,pt3)                        /* vector cross-product */
    struct { float x,y,z; } *vec,pt1,pt2,pt3;
{   pt1.x = pt2.x - pt1.x;
    pt1.y = pt2.y - pt1.y;
    pt1.z = pt2.z - pt1.z;
    pt2.x = pt3.x - pt2.x;
    pt2.y = pt3.y - pt2.y;
    pt2.z = pt3.z - pt2.z;
    vec->x = pt1.y*pt2.z - pt1.z*pt2.y;
    vec->y = pt1.z*pt2.x - pt1.x*pt2.z;
    vec->z = pt1.x*pt2.y - pt1.y*pt2.x;
}



!Funky!Stuff!