#include "xmfract.h" #include "fractype.h" #include "macros.h" #include "helpdefs.h" #include "mpmath.h" #include "globals.h" #include "prototyp.h" #ifndef NO_IDENT #ident "$Id: line3d.c,v 1.400 1995/06/20 15:39:23 darryl Exp $" #endif /************************************************************************/ /* */ /* This file contains a 3D replacement for the out_line function called */ /* by the decoder. The purpose is to apply various 3D transformations */ /* before displaying points. Called once per line of the input file. */ /* */ /* Original Author Tim Wegner, with extensive help from Marc Reinig. */ /************************************************************************/ /* static functions */ static void vdraw_line(double *v1, double *v2, int color); static void draw_light_box(double *origin, double *direct, MATRIX light_m); static void draw_rect (VECTOR V0, VECTOR V1, VECTOR V2, VECTOR V3, int color, int rect); static void putminmax(int x, int y, int color); static void putatriangle (struct point pt1, struct point pt2, struct point pt3, int color); static void clipcolor(int x, int y, int color); static void T_clipcolor(int x, int y, int color); static void interpcolor(int x, int y, int color); static int set_pixel_buff (unsigned char *pixels, unsigned char *fraction, unsigned linelen); static void File_Error(char *File_Name1, int ERROR); static int startdisk1(char *File_Name2, FILE *Source, int overlay); static int targa_validate(char *File_Name); static int R_H (unsigned char R, unsigned char G, unsigned char B, unsigned long *H, unsigned long *S, unsigned long *V); static int H_R (unsigned char *R, unsigned char *G, unsigned char *B, unsigned long H, unsigned long S, unsigned long V); static int RAY_Header(void); static int out_triangle (struct f_point pt1, struct f_point pt2, struct f_point pt3, int c1, int c2, int c3); static void triangle_bounds(float pt_t[3 ][3 ]); static int start_object(void); static int end_object(int triout); static void line3d_cleanup(void); /* static variables */ void (*mult_vec)(VECTOR) = mult_vec_c; void (*outln_cleanup)(); void (*normalplot)(int,int,int); void (*fillplot)(int,int,int); static char acro_s1[] = {"Set Layer 1\nSet Color 2\nEndpointList X Y Z Name\n"}; static char acro_s2[] = {"LineList From To\n"}; static char s3[] = {"{ Created by xmfract ver. "}; static char s3a[] = {" }\n\n"}; static char banner[] ="%s%#4.2f%s"; static VECTOR light_direction; static FILE *File_Ptr1; static VECTOR tmpcross; static double rXrscale; static float *costhetaarray; static float *sinthetaarray; static float max_xyz[3]; static float min_xyz[3]; static int HAZE_MULT; static int evenoddrow; static int line_length1; static int localpreviewfactor; static int rand_factor; static int zcoord = 256; static long num_tris; static struct f_point *f_lastrow; static struct minmax *minmax_x; static struct point *lastrow; static unsigned char upr_lwr[4]; static unsigned char Real_Color; static unsigned char T24; static unsigned char T32; static char targa_temp[14]; static unsigned int IAmbient; int line3d(unsigned char *pixels, unsigned linelen) { /* These variables must preserve their values across calls */ static float deltaphi; /* increment of latitude, longitude */ static double rscale; /* surface roughness factor */ static long xcenter,ycenter; /* circle center */ static double sclx,scly,sclz; /* scale factors */ static double R; /* radius values */ static double Rfactor; /* for intermediate calculation */ static LMATRIX lm; /* "" */ static LVECTOR lview; /* for perspective views */ static double zcutoff; /* perspective backside cutoff value */ static float twocosdeltaphi; static float cosphi,sinphi; /* precalculated sin/cos of longitude */ static float oldcosphi1,oldsinphi1; static float oldcosphi2,oldsinphi2; double r; /* sphere radius */ double xval,yval,zval; /* rotation values */ float costheta,sintheta; /* precalculated sin/cos of latitude */ float twocosdeltatheta; MATRIX lightm; /* m w/no trans, keeps obj. on screen */ int next; /* used by preview and grid */ int col; /* current column (original GIF) */ struct point cur; /* current pixels */ struct point old; /* old pixels */ static struct point bad; /* out of range value */ struct f_point f_cur; struct f_point f_old; static struct f_point f_bad; /* out of range value */ static unsigned char *fraction;/* float version of pixels array */ VECTOR v; /* double vector */ VECTOR v1,v2; VECTOR crossavg; char crossnotinit; /* flag for crossavg init indication */ static char msg[128]; double v_length; VECTOR origin, direct, tmp; LVECTOR lv; /* long equivalent of v */ LVECTOR lv0; /* long equivalent of v */ /* corners of transformed xdotx by ydots x colors box */ double xmin, ymin, zmin, xmax, ymax, zmax; int i,j; int lastdot; long fudge; double r0; int tout; /* triangle has been sent to ray trace file */ int RND; int xcenter0 = 0; int ycenter0 = 0; /* Unfudged versions */ float f_water = 0.0; /* transformed WATERLINE for ray trace files */ fudge = 1L<<16; if(transparent[0] || transparent[1]) plot = normalplot = T_clipcolor; /* Use transparent plot function */ else /* Use the usual xmfract plot function with clipping */ plot = normalplot = clipcolor; currow = rowcount; /* use separate variable to allow for pot16bit files */ if (pot16bit) currow >>= 1; /************************************************************************/ /* This IF clause is executed ONCE per image. All precalculations are */ /* done here, with out any special concern about speed. DANGER - */ /* communication with the rest of the program is generally via static */ /* or global variables. */ /************************************************************************/ if(rowcount++ == 0) { long check_extra; float theta,theta1,theta2; /* current,start,stop latitude */ float phi1,phi2; /* current start,stop longitude */ float deltatheta; /* increment of latitude */ outln_cleanup = line3d_cleanup; calctime = evenoddrow = 0; /* mark as in-progress, and enable timer display */ calc_status = IMG_ACTV_CALC; update_draw_menu(NEW_IMAGE); IAmbient = (unsigned int) (255 * (float)(100 - Ambient) / 100.0); if(IAmbient < 1) IAmbient = 1; tout = 0; num_tris = 0; /* Open file for RAY trace output and write header */ if (RAY) { RAY_Header(); xxadjust = yyadjust = 0; /* Disable shifting in ray tracing */ xshift = yshift = 0; } CO_MAX=CO=RO=0; upr_lwr[0] = xdots & 0xff; upr_lwr[1] = xdots >> 8; upr_lwr[2] = ydots & 0xff; upr_lwr[3] = ydots >> 8; line_length1 = 3 * xdots; /* line length @ 3 bytes per pixel */ xmerror = 0; if (whichimage < 2) T_Safe = 0; /* Not safe yet to mess with the source image */ if (Targa_Out && !((glassestype==1 || glassestype==2) && whichimage==2)) { if (Targa_Overlay) { /* Make sure target file is a supportable Targa File */ if(targa_validate (light_name)) return(-1); } else { check_writefile(light_name,".tga"); if (startdisk1(light_name, NULL, 0)) /* Open new file */ return(-1); } } rand_factor = 14 - RANDOMIZE; zcoord = filecolors; crossavg[0] = 0; crossavg[1] = 0; crossavg[2] = 0; /*********************************************************************/ /* Memory allocation - assumptions - a 64K segment starting at */ /* extraseg has been grabbed. It may have other purposes elsewhere, */ /* but it is assumed that it is totally free for use here. Our */ /* strategy is to assign all the pointers needed here to various */ /* spots in the extra segment, depending on the pixel dimensions of */ /* the video mode, and check whether we have run out. There is */ /* basically one case where the extra segment is not big enough */ /* -- SPHERE mode with a fill type that uses putatriangle() (array */ /* minmax_x) at the largest resolution of MAXPIXELSxMAXPIXELS or */ /* thereabouts. In that case we use malloc to grab memory */ /* for minmax_x. This memory is never freed. */ /*********************************************************************/ /* * lastrow stores the previous row of the original GIF image for * the purpose of filling in gaps with triangle procedure */ lastrow = mkptr(extraseg,0); check_extra = sizeof(*lastrow)*xdots; if(SPHERE) { sinthetaarray = (float *)(lastrow+xdots); check_extra += sizeof(*sinthetaarray)*xdots; costhetaarray = (float *)(sinthetaarray+xdots); check_extra += sizeof(*costhetaarray)*xdots; f_lastrow = (struct f_point *)(costhetaarray+xdots); } else f_lastrow = (struct f_point *)(lastrow+xdots); check_extra += sizeof(*f_lastrow)*(xdots); if(pot16bit) { fraction = (unsigned char *)(f_lastrow+xdots); check_extra += sizeof(*fraction)*xdots; } minmax_x = (struct minmax *)NULL; /* these fill types call putatriangle which uses minmax_x */ if( FILLTYPE == 2 || FILLTYPE == 3 || FILLTYPE == 5 || FILLTYPE == 6) { /* end of arrays if we use extra segement */ check_extra += sizeof(struct minmax)*ydots; if(check_extra > (1L<<16)) /* run out of extra segment? */ { static struct minmax *got_mem = NULL; /* not using extra segment so decrement check_extra */ check_extra -= sizeof(struct minmax)*ydots; if(got_mem == NULL) got_mem = (struct minmax *)(XtMalloc(MAXPIXELS * sizeof(struct minmax))); if(got_mem) minmax_x = got_mem; else { return(-1); } } else /* ok to use extra segment */ { if(pot16bit) minmax_x = (struct minmax *)(fraction+xdots); else minmax_x = (struct minmax *)(f_lastrow+xdots); } } if(debugflag == 2222 || check_extra > (1L<<16)) { char tmpmsg[70]; static char extramsg[] = {" of extra segment"}; sprintf((char *) &tmpmsg,"used %ld%s", check_extra, (char *) &extramsg); inform(main_window, (char *) &tmpmsg, NULL); } /* get scale factors */ sclx = XSCALE/100.0; scly = YSCALE/100.0; /* if rough=0 make it very flat but plot something */ if (ROUGH) sclz = -ROUGH/100.0; else rscale = sclz = -0.0001; /* aspect ratio calculation - assume screen is 4 x 3 */ aspect = (double)xdots*.75/(double)ydots; if(SPHERE==FALSE) /* skip this slow stuff in sphere case */ { /*********************************************************************/ /* What is done here is to create a single matrix, m, which has */ /* scale, rotation, and shift all combined. This allows us to use */ /* a single matrix to transform any point. Additionally, we create */ /* two perspective vectors. */ /* */ /* Start with a unit matrix. Add scale and rotation. Then calculate */ /* the perspective vectors. Finally add enough translation to center */ /* the final image plus whatever shift the user has set. */ /*********************************************************************/ /* start with identity */ identity (mtrix); identity (lightm); /* translate so origin is in center of box, so that when we rotate */ /* it, we do so through the center */ trans ( (double)xdots/(-2.0),(double)ydots/(-2.0), (double)zcoord/(-2.0),mtrix); trans ( (double)xdots/(-2.0),(double)ydots/(-2.0), (double)zcoord/(-2.0),lightm); /* apply scale factors */ scale(sclx,scly,sclz,mtrix); scale(sclx,scly,sclz,lightm); /* rotation values - converting from degrees to radians */ xval = XROT / 57.29577; yval = YROT / 57.29577; zval = ZROT / 57.29577; if (RAY) {xval = yval = zval = 0;} xrot (xval,mtrix); xrot (xval,lightm); yrot (yval,mtrix); yrot (yval,lightm); zrot (zval,mtrix); zrot (zval,lightm); /* Find values of translation that make all x,y,z negative */ /* mtrix current matrix */ /* 0 means don't show box */ /* returns minimum and maximum values of x,y,z in fractal */ corners(mtrix,0,&xmin,&ymin,&zmin,&xmax,&ymax,&zmax); } /* perspective 3D vector - lview[2] == 0 means no perspective */ /* set perspective flag */ persp = 0; if (ZVIEWER != 0) { persp = 1; if(ZVIEWER < 80) /* force float */ usr_floatflag |= 2; /* turn on second bit */ } /* set up view vector, and put viewer in center of screen */ lview[0] = xdots >> 1; lview[1] = ydots >> 1; /* z value of user's eye - should be more negative than extreme negative part of image */ if(SPHERE) /* sphere case */ lview[2] = -(long)((double)ydots*(double)ZVIEWER/100.0); else /* non-sphere case */ lview[2] = (long)((zmin-zmax)*(double)ZVIEWER/100.0); view[0] = lview[0]; view[1] = lview[1]; view[2] = lview[2]; lview[0] = lview[0] << 16; lview[1] = lview[1] << 16; lview[2] = lview[2] << 16; if(SPHERE==FALSE) /* sphere skips this */ { /* translate back exactly amount we translated earlier plus enough to center image so maximum values are non-positive */ trans(((double)xdots-xmax-xmin)/2,((double)ydots-ymax-ymin) / 2, -zmax,mtrix); /* Keep the box centered and on screen regardless of shifts */ trans(((double)xdots-xmax-xmin)/2,((double)ydots-ymax-ymin)/2, -zmax,lightm); trans((double)(xshift),(double)(-yshift),0.0,mtrix); /* matrix m now contains ALL those transforms composed together !! convert m to long integers shifted 16 bits */ for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) lm[i][j] = mtrix[i][j] * 65536.0; } else /* sphere stuff goes here */ { /* Sphere is on side - north pole on right. Top is -90 degrees latitude; bottom 90 degrees */ /* Map X to this LATITUDE range */ theta1 = THETA1*PI/180.0; theta2 = THETA2*PI/180.0; /* Map Y to this LONGITUDE range */ phi1 = PHI1*PI/180.0; phi2 = PHI2*PI/180.0; theta = theta1; /*********************************************************************/ /* Thanks to Hugh Bray for the following idea: when calculating */ /* a table of evenly spaced sines or cosines, only a few initial */ /* values need be calculated, and the remaining values can be */ /* gotten from a derivative of the sine/cosine angle sum formula */ /* at the cost of one multiplication and one addition per value! */ /* */ /* This idea is applied once here to get a complete table for */ /* latitude, and near the bottom of this routine to incrementally */ /* calculate longitude. */ /* */ /* Precalculate 2*cos(deltaangle), sin(start) and sin(start+delta). */ /* Then apply recursively: */ /* sin(angle+2*delta) = sin(angle+delta) * 2cosdelta - sin(angle) */ /* */ /* Similarly for cosine. Neat! */ /*********************************************************************/ deltatheta = (float)(theta2 - theta1)/(float)linelen; /* initial sin,cos theta */ sinthetaarray[0] = sin((double)theta); costhetaarray[0] = cos((double)theta); sinthetaarray[1] = sin((double)(theta + deltatheta)); costhetaarray[1] = cos((double)(theta + deltatheta)); /* sin,cos delta theta */ twocosdeltatheta = 2.0*cos((double)deltatheta); /* build table of other sin,cos with trig identity */ for(i=2;i= 5) if (haze && Targa_Out) { HAZE_MULT = haze * ( (float)((long)(ydots - 1 - currow) * (long)(ydots - 1 - currow)) / (float)((long)(ydots - 1) * (long)(ydots - 1))); HAZE_MULT = 100 - HAZE_MULT; } if (previewfactor >= ydots || previewfactor > lastdot) previewfactor = min ( ydots - 1, lastdot); localpreviewfactor = ydots/previewfactor; tout = 0; /* Insure last line is drawn in preview and filltypes <0 */ if ((RAY || preview || FILLTYPE < 0) && (currow != ydots-1) && (currow % localpreviewfactor) && /* Draw mod preview lines */ !(!RAY && (FILLTYPE > 4) && (currow == 1))) /* Get init geometry in lightsource modes */ goto reallythebottom; /* skip over most of the line3d calcs */ if(!col && RAY && currow != 0) start_object(); if(FILLTYPE==0 && !SPHERE && !pot16bit && !RAY && debugflag != 2224 ) /* This while loop contains just a limited non-sphere case for speed */ /* Other while loop still has the old logic. Use debugflag to compare*/ while(col < linelen) { Real_Color = cur.color = pixels[col]; if (cur.color > 0 && cur.color < WATERLINE) Real_Color = cur.color = WATERLINE; /* "lake" */ if(!usr_floatflag) { lv0[0] = 0; /* don't save vector before perspective */ /* use 32-bit multiply math to snap this out */ lv[0] = col; lv[0] = lv[0] << 16; lv[1] = currow; lv[1] = lv[1] << 16; lv[2] = cur.color; lv[2] = lv[2] << 16; if(longvmultpersp(lv,lm,lv0,lv,lview,16) == -1) { cur = bad; goto loopbottom; } cur.x = ((lv[0]/* +32768L */) >> 16) + xxadjust; cur.y = ((lv[1]/* +32768L */) >> 16) + yyadjust; } /* do in float if integer math overflowed */ if(usr_floatflag || overflow) { /* slow float version for comparison */ v[0] = col; v[1] = currow; v[2] = cur.color; mult_vec(v); /* matrix*vector routine */ if(persp) perspective(v); cur.x = v[0] + xxadjust /* + .5 */; cur.y = v[1] + yyadjust /* + .5 */; } (*plot)(cur.x,cur.y,cur.color); col++; } /* End of while statement for plotting line */ else /* PROCESS ROW LOOP BEGINS HERE */ while(col < linelen) { if ((RAY || preview || FILLTYPE < 0) && (col != lastdot) && /* if this is not the last col */ /* if not the 1st or mod factor col*/ (col % (int)(aspect * localpreviewfactor)) && (!(!RAY && FILLTYPE > 4 && col == 1))) goto loopbottom; f_cur.color = Real_Color = cur.color = pixels[col]; if (RAY || preview || FILLTYPE < 0) { next = col + aspect * localpreviewfactor; if (next == col) next = col + 1; } else next = col + 1; if (next >= lastdot) next = lastdot; if (cur.color > 0 && cur.color < WATERLINE) f_cur.color = Real_Color = cur.color = WATERLINE; /* "lake" */ else if(pot16bit) f_cur.color += ((float)fraction[col])/(float)(1<<8); if(SPHERE) /* sphere case */ { sintheta = sinthetaarray[col]; costheta = costhetaarray[col]; if(sinphi < 0 && !(RAY || FILLTYPE < 0)) { cur = bad; f_cur = f_bad; goto loopbottom; /* another goto ! */ } /******************************************************************/ /* KEEP THIS FOR DOCS - original formula -- */ /* if(rscale < 0.0) */ /* r = 1.0+((double)cur.color/(double)zcoord)*rscale; */ /* else */ /* r = 1.0-rscale+((double)cur.color/(double)zcoord)*rscale; */ /* R = (double)ydots/2; */ /* r = r*R; */ /* cur.x = xdots/2 + sclx*r*sintheta*aspect + xup ; */ /* cur.y = ydots/2 + scly*r*costheta*cosphi - yup ; */ /******************************************************************/ if(rscale < 0.0) r = R + Rfactor*(double)f_cur.color*costheta; else if(rscale > 0.0) r = R -rXrscale + Rfactor*(double)f_cur.color*costheta; else r = R; if(persp || RAY) /* Allow Ray trace to go through so display is ok */ { /* mrr how do lv[] and cur and f_cur all relate */ /* NOTE: fudge was pre-calculated above in r and R */ /* (almost) guarantee negative */ lv[2] = -R - r*costheta*sinphi; /* z */ if((lv[2] > zcutoff) && !FILLTYPE < 0) { cur = bad; f_cur = f_bad; goto loopbottom; /* another goto ! */ } lv[0] = xcenter + sintheta*sclx*r; /* x */ lv[1] = ycenter + costheta*cosphi*scly*r; /* y */ if((FILLTYPE >= 5) || RAY) { /* calculate illumination normal before persp */ r0 = r/65536; f_cur.x = xcenter0 + sintheta*sclx*r0; f_cur.y = ycenter0 + costheta*cosphi*scly*r0; f_cur.color = -r0*costheta*sinphi; } if(!(usr_floatflag || RAY)) { if(longpersp(lv,lview,16) == -1) { cur = bad; f_cur = f_bad; goto loopbottom; /* another goto ! */ } cur.x = ((lv[0]+32768L) >> 16) + xxadjust; cur.y = ((lv[1]+32768L) >> 16) + yyadjust; } if(usr_floatflag || overflow || RAY) { v[0] = lv[0]; v[1] = lv[1]; v[2] = lv[2]; v[0] /= fudge; v[1] /= fudge; v[2] /= fudge; perspective(v); cur.x = v[0]+.5 + xxadjust; cur.y = v[1]+.5 + yyadjust; } } else if (!(persp && RAY)) { /* mrr Not sure how this and 3rd if above relate */ /* mrr Why the xx- and yyadjust here and not above? */ cur.x = f_cur.x = xcenter + sintheta*sclx*r + xxadjust; cur.y = f_cur.y = ycenter + costheta*cosphi*scly*r + yyadjust; if(FILLTYPE >= 5 || RAY) /* mrr why do we do this for filltype>5? */ f_cur.color = -r * costheta * sinphi * sclz; v[0]=v[1]=v[2]=0; /* MRR Why do we do this? */ } } else /* non-sphere 3D */ { if(!usr_floatflag && !RAY) { if(FILLTYPE >= 5) /* flag to save vector before perspective */ lv0[0] = 1; /* in longvmultpersp calculation */ else lv0[0] = 0; /* use 32-bit multiply math to snap this out */ lv[0] = col; lv[0] = lv[0] << 16; lv[1] = currow; lv[1] = lv[1] << 16; if(filetype || pot16bit) /* don't truncate fractional part */ lv[2] = f_cur.color*65536.0; else /* there IS no fractaional part here! */ { lv[2] = f_cur.color; lv[2] = lv[2] << 16; } if(longvmultpersp(lv,lm,lv0,lv,lview,16) == -1) { cur = bad; f_cur = f_bad; goto loopbottom; } cur.x = ((lv[0]+32768L) >> 16) + xxadjust; cur.y = ((lv[1]+32768L) >> 16) + yyadjust; if(FILLTYPE >= 5 && !overflow) { f_cur.x = lv0[0]; f_cur.x /= 65536.0; f_cur.y = lv0[1]; f_cur.y /= 65536.0; f_cur.color = lv0[2]; f_cur.color /= 65536.0; } } if(usr_floatflag || overflow || RAY) /* do in float if integer math overflowed or doing Ray trace */ { /* slow float version for comparison */ v[0] = col; v[1] = currow; v[2] = f_cur.color; /* Actually the z value */ mult_vec(v); /* matrix*vector routine */ if (FILLTYPE > 4 || RAY) { f_cur.x = v[0]; f_cur.y = v[1]; f_cur.color = v[2]; if(RAY == 6) { f_cur.x = f_cur.x * (2.0 / xdots) - 1; f_cur.y = f_cur.y * (2.0 / ydots) - 1; f_cur.color = -f_cur.color * (2.0 / numcolors) - 1; } } if(persp && !RAY) perspective(v); cur.x = v[0] + xxadjust + .5; cur.y = v[1] + yyadjust + .5; v[0] = 0; v[1] = 0; v[2] = WATERLINE; mult_vec(v); f_water = v[2]; } } if (RANDOMIZE) if (cur.color > WATERLINE) { RND = rand15() >> 8; /* 7-bit number */ RND = RND * RND >> rand_factor; /* n-bit number */ if (rand() & 1) RND = -RND; /* Make +/- n-bit number */ if ((int)(cur.color) + RND >= colors) cur.color = colors-2; else if ((int)(cur.color) + RND <= WATERLINE) cur.color = WATERLINE + 1; else cur.color = cur.color + RND; Real_Color = cur.color; } if (RAY) { if (col && currow && old.x > bad_check && old.x < (xdots - bad_check) && lastrow[col].x > bad_check && lastrow[col].y > bad_check && lastrow[col].x < (xdots - bad_check) && lastrow[col].y < (ydots - bad_check)) { /* Get rid of all the triangles in the plane at the base of the object */ if (f_cur.color == f_water && f_lastrow[col].color == f_water && f_lastrow[next].color == f_water) goto loopbottom; if (RAY != 6) /* Output the vertex info */ out_triangle(f_cur, f_old, f_lastrow[col], cur.color, old.color, lastrow[col].color); tout = 1; draw_line (old.x, old.y, cur.x, cur.y, old.color); draw_line (old.x, old.y, lastrow[col].x, lastrow[col].y, old.color); draw_line (lastrow[col].x, lastrow[col].y, cur.x, cur.y, cur.color); num_tris++; } if (col < lastdot && currow && lastrow[col].x > bad_check && lastrow[col].y > bad_check && lastrow[col].x < (xdots - bad_check) && lastrow[col].y < (ydots - bad_check) && lastrow[next].x > bad_check && lastrow[next].y > bad_check && lastrow[next].x < (xdots - bad_check) && lastrow[next].y < (ydots - bad_check)) { /* * Get rid of all the triangles in the plane at the * base of the object */ if (f_cur.color == f_water && f_lastrow[col].color == f_water && f_lastrow[next].color == f_water) goto loopbottom; if (RAY != 6) /* Output the vertex info */ out_triangle(f_cur, f_lastrow[col], f_lastrow[next], cur.color, lastrow[col].color, lastrow[next].color); tout = 1; draw_line (lastrow[col].x, lastrow[col].y, cur.x, cur.y, cur.color); draw_line (lastrow[next].x, lastrow[next].y, cur.x, cur.y, cur.color); draw_line (lastrow[next].x, lastrow[next].y, lastrow[col].x, lastrow[col].y, lastrow[col].color); num_tris++; } if (RAY == 6) /* Output vertex info for Acrospin */ { fprintf (File_Ptr1, "% #4.4f % #4.4f % #4.4f R%dC%d\n", f_cur.x, f_cur.y, f_cur.color, RO, CO); if (CO > CO_MAX) CO_MAX = CO; CO++; } goto loopbottom; } switch(FILLTYPE) { case -1: if (col && old.x > bad_check && old.x < (xdots - bad_check)) draw_line (old.x, old.y, cur.x, cur.y, cur.color); if (currow && lastrow[col].x > bad_check && lastrow[col].y > bad_check && lastrow[col].x < (xdots - bad_check) && lastrow[col].y < (ydots - bad_check)) draw_line (lastrow[col].x,lastrow[col].y,cur.x, cur.y,cur.color); break; case 0: (*plot)(cur.x,cur.y,cur.color); break; case 1: /* connect-a-dot */ if ((old.x < xdots) && (col) && old.x > bad_check && old.y > bad_check) /* Don't draw from old to cur on col 0 */ draw_line(old.x,old.y,cur.x,cur.y,cur.color); break; case 2: /* with interpolation */ case 3: /* no interpolation */ /***************************************************************/ /* "triangle fill" - consider four points: current point, */ /* previous point same row, point opposite current point in */ /* previous row, point after current point in previous row. */ /* The object is to fill all points inside the two triangles. */ /* */ /* lastrow[col].x/y___ lastrow[next] */ /* / 1 / */ /* / 1 / */ /* / 1 / */ /* oldrow/col _____ trow/col */ /***************************************************************/ if(currow && !col) putatriangle(lastrow[next],lastrow[col],cur,cur.color); if(currow && col) /* skip first row and first column */ { if(col == 1) putatriangle(lastrow[col],oldlast,old,old.color); if(col < lastdot) putatriangle(lastrow[next],lastrow[col], cur, cur.color); putatriangle(old, lastrow[col], cur, cur.color); } break; case 4: /* "solid fill" */ if (SPHERE) { if (persp) { old.x = xcenter>>16; old.y = ycenter>>16; } else { old.x = xcenter; old.y = ycenter; } } else { lv[0] = col; lv[1] = currow; lv[2] = 0; /* apply fudge bit shift for integer math */ lv[0] = lv[0] << 16; lv[1] = lv[1] << 16; /* Since 0, unnecessary lv[2] = lv[2] << 16;*/ if(longvmultpersp(lv,lm,lv0,lv,lview,16)) { cur = bad; f_cur = f_bad; goto loopbottom; /* another goto ! */ } /* Round and fudge back to original */ old.x = (lv[0]+32768L) >> 16; old.y = (lv[1]+32768L) >> 16; } if (old.x < 0) old.x = 0; if (old.x >= xdots) old.x = xdots-1; if (old.y < 0) old.y = 0; if (old.y >= ydots) old.y = ydots-1; draw_line(old.x,old.y,cur.x,cur.y,cur.color); break; case 5: case 6: /* light-source modulated fill */ if(currow && col) /* skip first row and first column */ { if(f_cur.color < bad_check || f_old.color < bad_check || f_lastrow[col].color < bad_check) break; v1[0] = f_cur.x - f_old.x; v1[1] = f_cur.y - f_old.y; v1[2] = f_cur.color - f_old.color; v2[0] = f_lastrow[col].x - f_cur.x; v2[1] = f_lastrow[col].y - f_cur.y; v2[2] = f_lastrow[col].color - f_cur.color; cross_product (v1, v2, cross); /* normalize cross - and check if non-zero */ if(normalize_vector(cross)) { if(debugflag) { sprintf((char *) &msg, "debug, cur.color=bad"); inform(main_window, (char *) &msg, NULL); } cur.color = f_cur.color = bad.color; } else { /* line-wise averaging scheme */ if(LIGHTAVG>0) { if(crossnotinit) { /* initialize array of old normal vectors */ crossavg[0] = cross[0]; crossavg[1] = cross[1]; crossavg[2] = cross[2]; crossnotinit = 0; } tmpcross[0] = (crossavg[0]*LIGHTAVG+cross[0]) / (LIGHTAVG+1); tmpcross[1] = (crossavg[1]*LIGHTAVG+cross[1]) / (LIGHTAVG+1); tmpcross[2] = (crossavg[2]*LIGHTAVG+cross[2]) / (LIGHTAVG+1); cross[0] = tmpcross[0]; cross[1] = tmpcross[1]; cross[2] = tmpcross[2]; if(normalize_vector(cross)) { /* this shouldn't happen */ if(debugflag) { sprintf((char *) &msg, "debug, normal vector err2"); inform(main_window, (char *) &msg, NULL); /* use next instead if you ever need details: static char tmp[] = {"debug, vector err"}; sprintf((char *) &msg,"%s\n%f %f %f\n%f %f %f\n%f %f %f", tmp, f_cur.x, f_cur.y, f_cur.color, f_lastrow[col].x, f_lastrow[col].y, f_lastrow[col].color, f_lastrow[col-1].x, f_lastrow[col-1].y,f_lastrow[col-1].color); inform(main_window, (char *) &msg, NULL); */ } cur.color = f_cur.color = colors; } } crossavg[0] = tmpcross[0]; crossavg[1] = tmpcross[1]; crossavg[2] = tmpcross[2]; /* dot product of unit vectors is cos of angle between */ /* we will use this value to shade surface */ cur.color = 1 + (colors-2) * (1.0-dot_product(cross,light_direction)); } /* if colors out of range, set them to min or max color index but avoid background index. This makes colors "opaque" so SOMETHING plots. These conditions shouldn't happen but just in case */ if(cur.color < 1) /* prevent transparent colors */ cur.color = 1; /* avoid background */ if(cur.color > colors-1) cur.color = colors-1; /* why "col < 2"? So we have sufficient geometry for the fill */ /* algorithm, which needs previous point in same row to have */ /* already been calculated (variable old) */ /* fix ragged left margin in preview */ if (col == 1 && currow > 1) putatriangle(lastrow[next],lastrow[col],cur,cur.color); if(col < 2 || currow < 2) /* don't have valid colors yet */ break; if(col < lastdot) putatriangle(lastrow[next],lastrow[col],cur,cur.color); putatriangle(old,lastrow[col],cur,cur.color); plot=standardplot; } break; } /* End of CASE statement for fill type */ loopbottom: if (RAY || (FILLTYPE != 0 && FILLTYPE != 4)) { /* for triangle and grid fill purposes */ oldlast = lastrow[col]; old = lastrow[col] = cur; /* for illumination model purposes */ f_old = f_lastrow[col] = f_cur; if (currow && RAY && col >= lastdot) /* if we're at the end of a row, close the object */ { end_object(tout); tout=0; if (ferror (File_Ptr1)) { fclose (File_Ptr1); remove (light_name); File_Error(ray_name, 2); return(-1); } } } col++; } /* End of while statement for plotting line */ RO++; reallythebottom: /* stuff that HAS to be done, even in preview mode, goes here */ if(SPHERE) { /* incremental sin/cos phi calc */ if(currow == 0) { sinphi = oldsinphi2; cosphi = oldcosphi2; } else { sinphi = twocosdeltaphi*oldsinphi2 - oldsinphi1; cosphi = twocosdeltaphi*oldcosphi2 - oldcosphi1; oldsinphi1 = oldsinphi2; oldsinphi2 = sinphi; oldcosphi1 = oldcosphi2; oldcosphi2 = cosphi; } } return(0); /* decoder needs to know all is well !!! */ } /* vector version of line draw */ static void vdraw_line(v1,v2,color) double *v1,*v2; int color; { int x1,y1,x2,y2; x1 = (int)v1[0]; y1 = (int)v1[1]; x2 = (int)v2[0]; y2 = (int)v2[1]; draw_line(x1,y1,x2,y2,color); } void corners(m,show,pxmin,pymin,pzmin,pxmax,pymax,pzmax) MATRIX m; int show; /* turns on box-showing feature */ double *pxmin,*pymin,*pzmin,*pxmax,*pymax,*pzmax; { int i,j; VECTOR S[2][4]; /* Holds the top an bottom points, S[0][]=bottom */ /* define corners of box fractal is in in x,y,z plane "b" stands for "bottom" - these points are the corners of the screen in the x-y plane. The "t"'s stand for Top - they are the top of the cube where 255 color points hit. */ *pxmin = *pymin = *pzmin = (int)INT_MAX; *pxmax = *pymax = *pzmax = (int)INT_MIN; for (j = 0; j < 4; ++j) for (i=0;i<3;i++) S[0][j][i]=S[1][j][i]=0; S[0][1][0] = S[0][2][0] = S[1][1][0] = S[1][2][0] = xdots-1; S[0][2][1] = S[0][3][1] = S[1][2][1] = S[1][3][1] = ydots-1; S[1][0][2] = S[1][1][2] = S[1][2][2] = S[1][3][2] = zcoord-1; for (i = 0; i < 4; ++i) { /* transform points */ vmult(S[0][i],m,S[0][i]); vmult(S[1][i],m,S[1][i]); /* update minimums and maximums */ if (S[0][i][0] <= *pxmin) *pxmin = S[0][i][0]; if (S[0][i][0] >= *pxmax) *pxmax = S[0][i][0]; if (S[1][i][0] <= *pxmin) *pxmin = S[1][i][0]; if (S[1][i][0] >= *pxmax) *pxmax = S[1][i][0]; if (S[0][i][1] <= *pymin) *pymin = S[0][i][1]; if (S[0][i][1] >= *pymax) *pymax = S[0][i][1]; if (S[1][i][1] <= *pymin) *pymin = S[1][i][1]; if (S[1][i][1] >= *pymax) *pymax = S[1][i][1]; if (S[0][i][2] <= *pzmin) *pzmin = S[0][i][2]; if (S[0][i][2] >= *pzmax) *pzmax = S[0][i][2]; if (S[1][i][2] <= *pzmin) *pzmin = S[1][i][2]; if (S[1][i][2] >= *pzmax) *pzmax = S[1][i][2]; } if(show) { if(persp) { for (i=0;i<4;i++) { perspective(S[0][i]); perspective(S[1][i]); } } /* Keep the box surrounding the fractal */ for (j=0;j<2;j++) for (i = 0; i < 4; ++i) { S[j][i][0] += xxadjust; S[j][i][1] += yyadjust; } draw_rect(S[0][0],S[0][1],S[0][2],S[0][3],2,1);/* Bottom */ draw_rect(S[0][0],S[1][0],S[0][1],S[1][1], 5, 0); /* Sides */ draw_rect(S[0][2],S[1][2],S[0][3],S[1][3], 6, 0); draw_rect(S[1][0],S[1][1],S[1][2],S[1][3],8,1); /* Top */ } } /* This function draws a vector from origin[] to direct[] and a box around it. The vector and box are transformed or not depending on FILLTYPE. */ static void draw_light_box (origin, direct, light_m) double *origin, *direct; MATRIX light_m; { VECTOR S[2][4]; int i,j; double temp; S[1][0][0] = S[0][0][0] = origin[0]; S[1][0][1] = S[0][0][1] = origin[1]; S[1][0][2] = direct[2]; for (i=0;i<2;i++) { S[i][1][0] = S[i][0][0]; S[i][1][1] = direct[1]; S[i][1][2] = S[i][0][2]; S[i][2][0] = direct[0]; S[i][2][1] = S[i][1][1]; S[i][2][2] = S[i][0][2]; S[i][3][0] = S[i][2][0]; S[i][3][1] = S[i][0][1]; S[i][3][2] = S[i][0][2]; } /* transform the corners if necessary */ if (FILLTYPE == 6) for (i=0;i<4;i++) { vmult (S[0][i],light_m,S[0][i]); vmult (S[1][i],light_m,S[1][i]); } /* always use perspective to aid viewing */ temp = view[2]; /* save perspective distance for a later restore */ view[2] = - XMP * 300.0 / 100.0; for (i=0;i<4;i++) { perspective(S[0][i]); perspective(S[1][i]); } view[2] = temp; /* Restore perspective distance*/ /* Adjust for aspect */ for (i=0;i<4;i++) { S[0][i][0] = S[0][i][0] * aspect; S[1][i][0] = S[1][i][0] * aspect; } /* draw box connecting transformed points. NOTE order and COLORS */ draw_rect(S[0][0],S[0][1],S[0][2],S[0][3],2,1); vdraw_line (S[0][0],S[1][2],8); /* sides */ draw_rect(S[0][0],S[1][0],S[0][1],S[1][1], 4, 0); draw_rect(S[0][2],S[1][2],S[0][3],S[1][3], 5, 0); draw_rect(S[1][0],S[1][1],S[1][2],S[1][3],3,1); /* Draw the "arrow head" */ for (i= -3;i<4;i++) for (j= -3;j<4;j++) if (abs(i) + abs(j) < 6) plot((int)(S[1][2][0]+i),(int)(S[1][2][1]+j),10); } static void draw_rect (V0, V1, V2, V3, color, rect) VECTOR V0, V1, V2, V3; int color, rect; { VECTOR V[4]; int i; for (i=0;i<2;i++) { /* Since V[2] is not used by vdraw_line don't bother setting it */ V[0][i] = V0[i]; V[1][i] = V1[i]; V[2][i] = V2[i]; V[3][i] = V3[i]; } if (rect) /* Draw a rectangle */ { for (i=0;i<4;i++) if (fabs(V[i][0]-V[(i+1)%4][0]) < -2*bad_check && fabs(V[i][1]-V[(i+1)%4][1]) < -2*bad_check) vdraw_line (V[i],V[(i+1)%4],color); } else /* Draw 2 lines instead */ { for(i=0;i<3;i+=2) if (fabs(V[i][0]-V[i+1][0]) < -2*bad_check && fabs(V[i][1]-V[i+1][1]) < -2*bad_check) vdraw_line (V[i],V[i+1],color); } return; } /* replacement for plot - builds a table of min and max x's instead of plot */ /* called by draw_line as part of triangle fill routine */ /*ARGSUSED*/ static void putminmax(int x,int y,int color) { if(y >= 0 && y < ydots) { if(x < minmax_x[y].minx) minmax_x[y].minx = x; if(x > minmax_x[y].maxx) minmax_x[y].maxx = x; } } /* This routine fills in a triangle. Extreme left and right values for each row are calculated by calling the line function for the sides. Then rows are filled in with horizontal lines */ static void putatriangle(struct point pt1,struct point pt2, struct point pt3,int color) { int miny,maxy; int x,y,xlim; /* Too many points off the screen? */ if(offscreen(pt1) + offscreen(pt2) + offscreen(pt3) > MAXOFFSCREEN) return; p1 = pt1; /* needed by interpcolor */ p2 = pt2; p3 = pt3; /* fast way if single point or single line */ if (p1.y == p2.y && p1.x == p2.x) { plot = fillplot; if (p1.y == p3.y && p1.x == p3.x) (*plot)(p1.x, p1.y, color); else draw_line(p1.x,p1.y,p3.x,p3.y,color); plot = normalplot; return; } else if ( (p3.y == p1.y && p3.x == p1.x) || (p3.y == p2.y && p3.x == p2.x) ) { plot = fillplot; draw_line(p1.x,p1.y,p2.x,p2.y,color); plot = normalplot; return; } /* find min max y */ miny = maxy = p1.y; if(p2.y < miny) miny = p2.y; else maxy = p2.y; if(p3.y < miny) miny = p3.y; else if(p3.y > maxy) maxy = p3.y; /* only worried about values on screen */ if(miny < 0) miny = 0; if(maxy >= ydots) maxy = ydots-1; for(y=miny;y<=maxy;y++) { minmax_x[y].minx = (int)INT_MAX; minmax_x[y].maxx = (int)INT_MIN; } /* set plot to "fake" plot function */ plot = putminmax; /* build table of extreme x's of triangle */ draw_line(p1.x,p1.y,p2.x,p2.y,0); draw_line(p2.x,p2.y,p3.x,p3.y,0); draw_line(p3.x,p3.y,p1.x,p1.y,0); for(y=miny;y<=maxy;y++) { xlim = minmax_x[y].maxx; for(x=minmax_x[y].minx;x<=xlim;x++) (*fillplot)(x,y,color); } plot = normalplot; } int offscreen(struct point pt) { if(pt.x >= 0) if(pt.x < xdots) if(pt.y >= 0) if(pt.y < ydots) return(0); /* point is ok */ if (abs(pt.x) > 0-bad_check || abs(pt.y) > 0-bad_check) return(99); /* point is bad */ return(1); /* point is off the screen */ } static void clipcolor(int x,int y,int color) { if(0 <= x && x < xdots && 0 <= y && y < ydots && 0 <= color && color < filecolors) { standardplot(x,y,color); if (Targa_Out) if (!(glassestype==1 || glassestype==2)) /* standardplot modifies color in these types */ targa_color(x, y, color); } } /*********************************************************************/ /* This function is the same as clipcolor but checks for color being */ /* in transparent range. Intended to be called only if transparency */ /* has been enabled. */ /*********************************************************************/ static void T_clipcolor(int x,int y,int color) { if (0 <= x && x < xdots && /* is the point on screen? */ 0 <= y && y < ydots && /* Yes? */ 0 <= color && color < colors && /* Colors in valid range? */ /* Lets make sure its not a transparent color */ (transparent[0] > color || color > transparent[1])) { standardplot(x,y,color); /* I guess we can plot then */ if (Targa_Out) if (!(glassestype==1 || glassestype==2)) /* standardplot modifies color in these types */ targa_color(x, y, color); } } /************************************************************************/ /* A substitute for plotcolor that interpolates the colors according */ /* to the x and y values of three points (p1,p2,p3) which are static in */ /* this routine */ /* */ /* In Light source modes, color is light value, not actual color */ /* Real_Color always contains the actual color */ /************************************************************************/ static void interpcolor(int x,int y,int color) { int D,d1,d2,d3; /* this distance formula is not the usual one - but it has the virtue that it uses ONLY additions (almost) and it DOES go to zero as the points get close. */ d1 = abs(p1.x-x)+abs(p1.y-y); d2 = abs(p2.x-x)+abs(p2.y-y); d3 = abs(p3.x-x)+abs(p3.y-y); D = (d1 + d2 + d3) << 1; if(D) { /* calculate a weighted average of colors - long casts prevent integer overflow. This can evaluate to zero */ color = ((long)(d2+d3)*(long)p1.color + (long)(d1+d3)*(long)p2.color + (long)(d1+d2)*(long)p3.color) / D; } if(0 <= x && x < xdots && 0 <= y && y < ydots && 0 <= color && color < colors && (transparent[1] == 0 || (int) Real_Color > transparent[1] || transparent[0] > (int) Real_Color)) { if (Targa_Out) if (!(glassestype==1 || glassestype==2)) /* standardplot modifies color in these types */ D = targa_color(x, y, color); if (FILLTYPE >= 5) if (Real_V && Targa_Out) color = D; else { color = (1 + (unsigned)color * IAmbient)/256; if (color == 0) color = 1; } standardplot(x,y,color); } } /* In non light source modes, both color and Real_Color contain the actual pixel color. In light source modes, color contains the light value, and Real_Color contains the origninal color This routine takes a pixel modifies it for lightshading if appropriate and plots it in a Targa file. Used in plot3d.c */ int targa_color(int x,int y,int color) { unsigned long H, S, V; unsigned char RGB[3]; if (FILLTYPE == 2 || glassestype==1 || glassestype==2) Real_Color = color; /* So Targa gets interpolated color */ RGB[0] = dacbox [Real_Color] [0]; RGB[1] = dacbox [Real_Color] [1]; RGB[2] = dacbox [Real_Color] [2]; /* Now lets convert it to HSV */ R_H(RGB[0], RGB[1], RGB[2], &H, &S, &V); /* Modify original S and V components */ if (FILLTYPE > 4 && !(glassestype==1 || glassestype==2)) /* Adjust for Ambient */ V = (V * (65535 - (unsigned)(color * IAmbient))) / 65535; if (haze) { S = (unsigned long)(S * HAZE_MULT) / 100; /* Haze lowers sat of colors */ if (V >= 32640) /* Haze reduces contrast */ { V = V - 32640; V = (unsigned long)((V * HAZE_MULT) / 100); V = V + 32640; } else { V = 32640 - V; V = (unsigned long)((V * HAZE_MULT) / 100); V = 32640 - V; } } /* Now lets convert it back to RGB. Original Hue, modified Sat and Val */ H_R(&RGB[0], &RGB[1], &RGB[2], H, S, V); if (Real_V) V = (35 * (int)RGB[0] + 45 * (int)RGB[1] + 20 * (int)RGB[2]) / 100; /* Now write the color triple to its transformed location */ /* on the disk. */ targa_writedisk((unsigned int)(x+sxoffs), (unsigned int)(y+syoffs), RGB[0], RGB[1], RGB[2]); return(255-V); } static int set_pixel_buff (unsigned char *pixels,unsigned char *fraction,unsigned linelen) { int i; if ((evenoddrow++ & 1) == 0) /* even rows are color value */ { for(i=0;i", OOPS, str1, File_Name1); break; case 2: /* Not enough room */ sprintf(msgbuf,"%s%s%s >", OOPS, tempx, File_Name1); break; case 3: /* Image wrong size */ sprintf(msgbuf, fs, OOPS, str3); break; case 4: /* Wrong file type */ sprintf(msgbuf, fs, OOPS, E1); break; } warning(main_window, msgbuf, NULL); return; } /************************************************************************/ /* */ /* This function opens a TARGA_24 file for reading and writing. If */ /* its a new file, (overlay == 0) it writes a header. If it is to */ /* overlay an existing file (overlay == 1) it copies the original */ /* header whose lenght and validity was determined in */ /* Targa_validate. */ /* */ /* It Verifies there is enough disk space, and leaves the file */ /* at the start of the display data area. */ /* */ /* If this is an overlay, closes source and copies to "targa_temp" */ /* If there is an error close the file. */ /* */ /* **********************************************************************/ static int startdisk1 (char *File_Name2, FILE *Source, int overlay) { int i,j,k, inc; FILE *fps; /* Open File for both reading and writing */ if ((fps=fopen(File_Name2,"w+b"))==NULL) { File_Error(File_Name2, 1); return(-1); /* Oops, somethings wrong! */ } inc = 1; /* Assume we are overlaying a file */ /* Write the header */ if (overlay) /* We are overlaying a file */ for (i=0;i-1;k--) fputc(back_color[k], fps); /* Targa order (B, G, R) */ } if (ferror (fps)) { /* Almost certainly not enough disk space */ fclose (fps); fclose (Source); remove (File_Name2); File_Error(File_Name2, 2); return(-2); } if (check_key() != 0) return(-3); } if (targa_startdisk(fps, T_header_24) != 0) { enddisk(); remove (File_Name2); return(-4); } return(0); } static int targa_validate(char *File_Name) { FILE *fp; int i, j = 0; /* Attempt to open source file for reading */ if ((fp=fopen(File_Name,"rb"))==NULL) { File_Error(File_Name, 1); return(-1); /* Oops, file does not exist */ } T_header_24 += fgetc(fp); /* Check ID field and adjust header size */ if (fgetc(fp)) /* Make sure this is an unmapped file */ { File_Error(File_Name, 4); return(-1); } if (fgetc(fp)!=2) /* Make sure it is a type 2 file */ { File_Error(File_Name, 4); return(-1); } /* Skip color map specification */ for (i=0;i<5;i++) fgetc(fp); for (i=0;i<4;i++) { /* Check image origin */ fgetc(fp); if(j != 0) { File_Error(File_Name, 4); return(-1); } } /* Check Image specs */ for (i=0;i<4;i++) if(fgetc(fp) != upr_lwr[i]) { File_Error(File_Name,3); return(-1); } if(fgetc(fp) != T24) xmerror=4; /* Is it a targa 24 file? */ if(fgetc(fp) != T32) xmerror=4; /* Is the origin at the upper left? */ if (xmerror == 4) { File_Error(File_Name,4); return(-1); } rewind(fp); /* Now that we know its a good file, create a working copy */ if (startdisk1(targa_temp, fp, 1)) return(-1); fclose(fp); /* Close the source */ T_Safe=1; /* Original file successfully copied to targa_temp */ return(0); } static int R_H (unsigned char R, unsigned char G, unsigned char B, unsigned long *H, unsigned long *S, unsigned long *V) { unsigned long R1, G1, B1, DENOM; unsigned char MINIM, TMPVAL; TMPVAL = R; *V = TMPVAL; MINIM = G; if (R < G) { TMPVAL = G; *V = TMPVAL; MINIM = R; if (G < B) { TMPVAL = B; *V = TMPVAL; } if (B < R) MINIM = B; } else { if (B < G) MINIM = B; if (R < B) { TMPVAL = B; *V = TMPVAL; } } DENOM = *V - MINIM; if (*V != 0 && DENOM !=0) { *S = ((DENOM << 16) / *V) - 1; } else *S = 0;/* Color is black! and Sat has no meaning */ if(*S == 0) /* R=G=B => shade of grey and Hue has no meaning */ { *H = 0; *V = *V << 8; return(1); /* v or s or both are 0 */ } if (*V == MINIM) { *H = 0; *V = *V << 8; return(0); } R1 = (((*V - R) * 60) << 6) / DENOM; /* distance of color from red */ G1 = (((*V - G) * 60) << 6) / DENOM; /* distance of color from green */ B1 = (((*V - B) * 60) << 6) / DENOM; /* distance of color from blue */ if(*V == R) if (MINIM == G) *H = (300 << 6) + B1; else *H = (60 << 6) - G1; if(*V == G) if (MINIM == B) *H = (60 << 6) + R1; else *H = (180 << 6) - B1; if(*V == B) if(MINIM == R) *H = (180 << 6) + G1; else *H = (300 << 6) - R1; *V = *V << 8; return(0); } static int H_R (R, G, B, H, S, V) unsigned char *R, *G, *B; unsigned long H, S, V; { unsigned long P1, P2, P3; int RMD, I; if(H >= 23040) H = H % 23040; /* Makes h circular */ I = H / 3840; RMD = H % 3840; /* RMD = fractional part of H */ P1 = ((V * (65535 - S)) / 65280) >> 8; P2 = (((V * (65535 - (S * RMD) / 3840)) / 65280) - 1) >> 8; P3 = (((V * (65535 - (S * (3840 - RMD)) / 3840)) / 65280)) >> 8; V = V >> 8; switch (I) { case 0: *R = V; *G = P3; *B = P1; break; case 1: *R = P2; *G = V; *B = P1; break; case 2: *R = P1; *G = V; *B = P3; break; case 3: *R = P1; *G = P2; *B = V; break; case 4: *R = P3; *G = P1; *B = V; break; case 5: *R = V ; *G = P1; *B = P2; break; } return(0); } /***************************************************************************/ /* */ /* EB & DG fiddled with outputs for Rayshade so they work. with v4.x. */ /* EB == eli brandt. ebrandt@jarthur.claremont.edu */ /* DG == dan goldwater. daniel_goldwater@brown.edu & dgold@math.umass.edu */ /* (NOTE: all the stuff we fiddled with is commented with "EB & DG" */ /* (plus even some extra bonus comments!) */ /* general raytracing code info/notes: */ /* */ /* ray == 0 means no raytracer output ray == 7 is for dxf */ /* ray == 1 is for dkb/pov ray == 4 is for mtv */ /* ray == 2 is for vivid ray == 5 is for rayshade */ /* ray == 3 is for raw ray == 6 is for acrospin */ /* */ /* rayshade needs counterclockwise triangles. raytracers that support */ /* the 'heightfield' primitive include rayshade and pov. */ /* anyone want to write code to make heightfields? they are *MUCH* */ /* faster to trace than triangles when doing landscapes... */ /* */ /* stuff EB & DG changed: */ /* made the rayshade output create a "grid" aggregate object */ /* (one of rayshade's primitives), instead of a global grid. */ /* As a result, the grid can be optimized based on the number of */ /* triangles. */ /* the z component of the grid can always be 1 since the surface formed */ /* by the triangles is flat (ie, it doesnt curve over itself). */ /* This is a major optimization. the x and y grid size is */ /* also optimized for a 4:3 aspect ratio image, to get the fewest */ /* possible traingles in each grid square. */ /* also, we fixed the rayshade code so it actually produces output that */ /* works with rayshade. (maybe the old code was for a really old version */ /* of rayshade?). */ /***************************************************************************/ /********************************************************************/ /* */ /* This routine writes a header to a ray tracer data file. It */ /* Identifies the version of xmfract which created it an the */ /* key 3D parameters in effect at the time. */ /* */ /********************************************************************/ static char declare[] = {"DECLARE "}; static char frac_default[] = {"F_Dflt"}; static char c_color[] = {"COLOR "}; static char dflt[] = {"RED 0.8 GREEN 0.4 BLUE 0.1\n"}; static char d_color[] = {"0.8 0.4 0.1"}; static char r_surf[] = {"0.95 0.05 5 0 0\n"}; static char surf[] = {"surf={diff="}; static char rs_surf[] = {"applysurf diffuse "}; /* EB & DG: changed "surface T" to "applysurf" and "diff" to "diffuse" */ static char end[] = {"END_"}; static char plane[] = {"PLANE"}; static char m1[] = {"-1.0 "}; static char one[] = {" 1.0 "}; static char z[] = {" 0.0 "}; static char bnd_by[] = {" BOUNDED_BY\n"}; static char end_bnd[] = {" END_BOUND\n"}; static char inter[] = {"INTERSECTION\n"}; static char fmt[] = " %s <%s%s%s> % #4.3f %s%s\n"; static char dxf_begin[] = {" 0\nSECTION\n 2\nTABLES\n 0\nTABLE\n 2\nLAYER\n\ 70\n 2\n 0\nLAYER\n 2\n0\n 70\n 0\n 62\n 7\n 6\nCONTINUOUS\n\ 0\nLAYER\n 2\nFRACTAL\n 70\n 64\n 62\n 1\n 6\nCONTINUOUS\n 0\n\ ENDTAB\n 0\nENDSEC\n 0\nSECTION\n 2\nENTITIES\n"}; static char dxf_3dface[] = {" 0\n3DFACE\n 8\nFRACTAL\n 62\n%3d\n"}; static char dxf_vertex[] = {"%3d\n%g\n"}; static char dxf_end[] = {" 0\nENDSEC\n 0\nEOF\n"}; static char composite[] = {"COMPOSITE"}; static char object[] = {"OBJECT"}; static char triangle[] = {"TRIANGLE "}; static char l_tri[] = {"triangle"}; static char texture[] = {"TEXTURE\n"}; /* static char end_texture[] = {" END_TEXTURE\n"}; */ static char red[] = {"RED"}; static char green[] = {"GREEN"}; static char blue[] = {"BLUE"}; static char frac_texture[] = {" AMBIENT 0.25 DIFFUSE 0.75"}; static char polygon[] = {"polygon={points=3;"}; static char vertex[] = {" vertex = "}; static char d_vert[] = {" <"}; static char f1[] = "% #4.4f "; /* EB & DG: changed this to much better values. now it actually makes a good trace */ static char grid[] = {"screen 640 480\neyep 0 2.1 0.8\nlookp 0 0 -0.95\nlight 1 point -2 1 1.5\n"}; static char grid2[] = {"background .3 0 0\nreport verbose\n"}; static char n[] = "\n"; static char f2[] = "R%dC%d R%dC%d\n"; static char ray_comment1[] = {"/* make a gridded aggregate. this size grid is fast for landscapes. */\n"}; static char ray_comment2[] = {"/* make z grid = 1 always for landscapes. */\n\n"}; static char grid3[] = {"grid 33 25 1\n"}; static int RAY_Header(void) { /* Open the ray tracing output file */ check_writefile(ray_name,".ray"); if ((File_Ptr1=fopen(ray_name,"w"))==NULL) return(-1); /* Oops, somethings wrong! */ if (RAY == 2) fprintf(File_Ptr1, "//"); if (RAY == 4) fprintf(File_Ptr1, "#"); if (RAY == 5) fprintf(File_Ptr1, "/*\n"); if (RAY == 6) fprintf(File_Ptr1, "--"); if (RAY == 7) fprintf(File_Ptr1, dxf_begin); if (RAY != 7) fprintf(File_Ptr1, banner, s3, release/100., s3a); if (RAY == 5) fprintf(File_Ptr1, "*/\n"); /* Set the default color */ if (RAY == 1) { fprintf (File_Ptr1, fs, declare, frac_default); fprintf (File_Ptr1, " = "); fprintf (File_Ptr1, fs, c_color, dflt); } if (BRIEF) { if (RAY == 2) { fprintf (File_Ptr1, fs, surf, d_color); fprintf (File_Ptr1, ";}\n"); } if (RAY == 4) { fprintf (File_Ptr1, "f "); fprintf (File_Ptr1, fs, d_color, r_surf); } if (RAY == 5) fprintf (File_Ptr1, fs, rs_surf, d_color); } if (RAY != 7) fprintf (File_Ptr1, n); if (RAY == 5) /* EB & DG: open "grid" opject, a speedy way to do aggregates in rayshade */ fprintf (File_Ptr1, fff, ray_comment1,ray_comment2,grid3); if (RAY == 6) fprintf (File_Ptr1, "%s", acro_s1); return(0); } /********************************************************************/ /* */ /* This routine describes the triangle to the ray tracer, it */ /* sets the color of the triangle to the average of the color */ /* of its verticies and sets the light parameters to arbitrary */ /* values. */ /* */ /* Note: numcolors (number of colors in the source */ /* file) is used instead of colors (number of colors avail. with */ /* display) so you can generate ray trace files with your LCD */ /* or monochrome display */ /* */ /********************************************************************/ static int out_triangle(struct f_point pt1, struct f_point pt2, struct f_point pt3, int c1, int c2, int c3) { int i, j; float c[3]; float pt_t[3][3]; /* Normalize each vertex to screen size and adjust coordinate system */ pt_t[0][0] = 2 * pt1.x / xdots - 1; pt_t[0][1] = (2 * pt1.y / ydots - 1); pt_t[0][2] = -2 * pt1.color / numcolors - 1; pt_t[1][0] = 2 * pt2.x / xdots - 1; pt_t[1][1] = (2 * pt2.y / ydots - 1); pt_t[1][2] = -2 * pt2.color / numcolors - 1; pt_t[2][0] = 2 * pt3.x / xdots - 1; pt_t[2][1] = (2 * pt3.y / ydots - 1); pt_t[2][2] = -2 * pt3.color / numcolors - 1; /* Color of triangle is average of colors of its verticies */ if (!BRIEF) for (i=0;i<=2;i++) c[i] = (float)((float)(dacbox[c1][i]) + (float)(dacbox[c2][i]) + (float)(dacbox[c3][i]) / (float)(3 * 63)); /* get rid of degenerate triangles: any two points equal */ if ((pt_t[0][0] == pt_t[1][0] && pt_t[0][1] == pt_t[1][1] && pt_t[0][2] == pt_t[1][2]) || (pt_t[0][0] == pt_t[2][0] && pt_t[0][1] == pt_t[2][1] && pt_t[0][2] == pt_t[2][2]) || (pt_t[2][0] == pt_t[1][0] && pt_t[2][1] == pt_t[1][1] && pt_t[2][2] == pt_t[1][2])) return(0); /* Describe the triangle */ if (RAY == 1) fprintf (File_Ptr1, " %s\n %s", object, triangle); if (RAY == 2 && !BRIEF) fprintf (File_Ptr1, "%s", surf); if (RAY == 4 && !BRIEF) fprintf (File_Ptr1, "f"); if (RAY == 5 && !BRIEF) fprintf (File_Ptr1, "%s", rs_surf); if (!BRIEF && RAY != 1 && RAY != 7) for (i=0;i<=2;i++) fprintf (File_Ptr1, f1, c[i]); if (RAY == 2) { if (!BRIEF) fprintf (File_Ptr1, ";}\n"); fprintf (File_Ptr1, "%s", polygon); } if (RAY == 4) { if (!BRIEF) fprintf (File_Ptr1, "%s", r_surf); fprintf (File_Ptr1, "p 3"); } if (RAY == 5) { if (!BRIEF) fprintf (File_Ptr1, n); fprintf (File_Ptr1, "%s", l_tri); /* EB & DG: removed "T" after "triangle" */ } if (RAY == 7) fprintf (File_Ptr1, dxf_3dface, min(255, max(1, c1))); for(i=0;i<=2;i++) /* Describe each Vertex */ { if (RAY != 7) fprintf (File_Ptr1, n); if (RAY == 1) fprintf (File_Ptr1, "%s", d_vert); if (RAY == 2) fprintf (File_Ptr1, "%s", vertex); if (RAY > 3 && RAY != 7) fprintf (File_Ptr1, " "); for(j=0;j<=2;j++) { if (RAY == 7) { /* write 3dface entity to dxf file */ fprintf (File_Ptr1, dxf_vertex, 10*(j+1)+i, pt_t[i][j]); if (i == 2) /* 3dface needs 4 vertecies */ fprintf (File_Ptr1, dxf_vertex, 10*(j+1)+i+1, pt_t[i][j]); } else if (!(RAY == 4 || RAY == 5)) fprintf (File_Ptr1, f1, pt_t[i][j]); /* Right handed */ else fprintf (File_Ptr1, f1, pt_t[2-i][j]); /* Left handed */ } if (RAY == 1) fprintf (File_Ptr1, ">"); if (RAY == 2) fprintf (File_Ptr1, ";"); } if (RAY == 1) { fprintf (File_Ptr1, " %s%s\n", end, triangle); if (!BRIEF) { fprintf (File_Ptr1, " %s %s%s% #4.4f %s% #4.4f %s% #4.4f\n%s %s%s", texture, c_color, red, c[0], green, c[1], blue, c[2], frac_texture, end, texture); } fprintf (File_Ptr1, " %s%s %s%s", c_color, frac_default, end, object); triangle_bounds(pt_t); /* update bounding info */ } if (RAY == 2) fprintf (File_Ptr1, "}"); if (RAY == 3 && !BRIEF) fprintf (File_Ptr1, n); if (RAY != 7) fprintf (File_Ptr1, n); return(0); } /********************************************************************/ /* */ /* This routine calculates the min and max values of a triangle */ /* for use in creating ray tracer data files. The values of min */ /* and max x, y, and z are assumed to be global. */ /* */ /********************************************************************/ static void triangle_bounds(float pt_t[3][3]) { int i,j; for (i=0;i<=2;i++) for (j=0;j<=2;j++) { if(pt_t[i][j] < min_xyz[j]) min_xyz[j] = pt_t[i][j]; if(pt_t[i][j] > max_xyz[j]) max_xyz[j] = pt_t[i][j]; } return; } /********************************************************************/ /* */ /* This routine starts a composite object for ray trace data files.*/ /* */ /********************************************************************/ static int start_object(void) { if (RAY != 1) return(0); /* Reset the min/max values, for bounding box */ min_xyz[0] = min_xyz[1] = min_xyz[2] = 999999; max_xyz[0] = max_xyz[1] = max_xyz[2] = -999999; fprintf (File_Ptr1, "%s\n", composite); return(0); } /********************************************************************/ /* */ /* This routine adds a bounding box for the triangles drawn */ /* in the last block and completes the composite object created. */ /* It uses the globals min and max x,y and z calculated in */ /* z calculated in Triangle_Bounds(). */ /* */ /********************************************************************/ static int end_object(int triout) { if (RAY == 7) return(0); if (RAY == 1) { if (triout) { /* Make sure the bounding box is slightly larger than the object */ int i; for (i=0;i<=2;i++) { if (min_xyz[i] == max_xyz[i]) { min_xyz[i] -= 0.01; max_xyz[i] += 0.01; } else { min_xyz[i] -= (max_xyz[i] - min_xyz[i]) * 0.01; max_xyz[i] += (max_xyz[i] - min_xyz[i]) * 0.01; } } /* Add the bounding box info */ fprintf (File_Ptr1, "%s %s", bnd_by, inter); fprintf (File_Ptr1, fmt, plane,m1,z,z,-min_xyz[0],end,plane); fprintf (File_Ptr1, fmt, plane,one,z,z,max_xyz[0],end,plane); fprintf (File_Ptr1, fmt, plane,z,m1,z,-min_xyz[1],end,plane); fprintf (File_Ptr1, fmt, plane,z,one,z,max_xyz[1],end,plane); fprintf (File_Ptr1, fmt, plane,z,z,m1,-min_xyz[2],end,plane); fprintf (File_Ptr1, fmt, plane,z,z,one,max_xyz[2],end,plane); fprintf (File_Ptr1, " %s%s%s", end, inter, end_bnd); } /* Complete the composite object statement */ fprintf (File_Ptr1, "%s%s\n", end, composite); } if (RAY!=6 && RAY!=5) fprintf (File_Ptr1, n); /* EB & DG: too many newlines */ return(0); } static void line3d_cleanup() { int i,j; if(RAY && File_Ptr1) { /* Finish up the ray tracing files */ static char n_ta[] ={"{ No. Of Triangles = "}; if (RAY != 5 && RAY != 7) fprintf (File_Ptr1, n); /* EB & DG: too many newlines */ if (RAY == 2) fprintf(File_Ptr1, "\n\n//"); if (RAY == 4) fprintf(File_Ptr1, "\n\n#"); if (RAY == 5) /* EB & DG: end grid aggregate */ fprintf (File_Ptr1, "end\n\n/*good landscape:*/\n%s%s\n/*", grid,grid2); if (RAY == 6) { fprintf (File_Ptr1, "%s", acro_s2); for (i=0;i