/************************************************************************/ /* 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. */ /* July 1994 - TW broke out several pieces of code and added pragma */ /* to eliminate compiler warnings. Did a long-overdue */ /* formatting cleanup. */ /************************************************************************/ #include /* see Fractint.c for a description of the "include" hierarchy */ #include "port.h" #include "prototyp.h" struct point { int x; int y; int color; }; struct f_point { float x; float y; float color; }; struct minmax { int minx; int maxx; }; /* routines in this module */ int line3d(BYTE *, unsigned); int _fastcall targa_color(int, int, int); int targa_validate(char *); static int first_time(int, VECTOR); static int H_R(BYTE *, BYTE *, BYTE *, unsigned long, unsigned long, unsigned long); static int line3dmem(void); static int R_H(BYTE, BYTE, BYTE, unsigned long *, unsigned long *, unsigned long *); static int set_pixel_buff(BYTE *, BYTE far *, unsigned); int startdisk1(char *, FILE *, int); static void set_upr_lwr(void); static int _fastcall end_object(int); static int _fastcall offscreen(struct point); static int _fastcall out_triangle(struct f_point, struct f_point, struct f_point, int, int, int); static int _fastcall RAY_Header(void); static int _fastcall start_object(void); static void corners(MATRIX, int, double *, double *, double *, double *, double *, double *); static void draw_light_box(double *, double *, MATRIX); static void draw_rect(VECTOR, VECTOR, VECTOR, VECTOR, int, int); static void File_Error(char *, int); static void line3d_cleanup(void); static void _fastcall clipcolor(int, int, int); static void _fastcall interpcolor(int, int, int); static void _fastcall putatriangle(struct point, struct point, struct point, int); static void _fastcall putminmax(int, int, int); static void _fastcall triangle_bounds(float pt_t[3][3]); static void _fastcall T_clipcolor(int, int, int); static void _fastcall vdraw_line(double *, double *, int color); static void (_fastcall * fillplot) (int, int, int); static void (_fastcall * normalplot) (int, int, int); /* static variables */ 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 llm; /* "" */ 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; static BYTE far *fraction; /* float version of pixels array */ static float min_xyz[3], max_xyz[3]; /* For Raytrace output */ static int line_length1; static int T_header_24 = 18;/* Size of current Targa-24 header */ static FILE *File_Ptr1 = NULL; static unsigned int IAmbient; static int rand_factor; static int HAZE_MULT; static void File_Error(char *File_Name1, int ERROR); static BYTE T24 = 24; static BYTE T32 = 32; static BYTE upr_lwr[4]; static int T_Safe; /* Original Targa Image successfully copied to targa_temp */ static VECTOR light_direction; static BYTE Real_Color; /* Actual color of cur pixel */ static int RO, CO, CO_MAX; /* For use in Acrospin support */ static FCODE acro_s1[] = {"Set Layer 1\nSet Color 2\nEndpointList X Y Z Name\n"}; static FCODE acro_s2[] = {"LineList From To\n"}; static FCODE s3[] = {"{ Created by FRACTINT Ver. "}; static FCODE s3a[] = {" }\n\n"}; #ifndef XFRACT static char banner[] = "%Fs%#4.2f%Fs"; #else static char banner[] = "%s%#4.2f%s"; #endif static int localpreviewfactor; static int zcoord = 256; static double aspect; /* aspect ratio */ static int evenoddrow; static float far *sinthetaarray; /* all sine thetas go here */ static float far *costhetaarray; /* all cosine thetas go here */ static double rXrscale; /* precalculation factor */ static int persp; /* flag for indicating perspective transformations */ static struct point p1, p2, p3; static struct f_point f_bad;/* out of range value */ static struct point bad; /* out of range value */ static long num_tris; /* number of triangles output to ray trace file */ /* global variables defined here */ struct f_point far *f_lastrow; void (_fastcall * standardplot) (int, int, int); MATRIX m; /* transformation matrix */ void (*mult_vec) (VECTOR) = mult_vec_c; int Ambient; int RANDOMIZE; int haze; int Real_V = 0; /* mrr Actual value of V for fillytpe>4 monochrome images */ char light_name[80] = "fract001"; int Targa_Overlay, error; char targa_temp[14] = "fractemp.tga"; int P = 250; /* Perspective dist used when viewing light vector */ BYTE back_color[3]; char ray_name[80] = "fract001"; char preview = 0; char showbox = 0; int previewfactor = 20; int xadjust = 0; int yadjust = 0; int xxadjust; int yyadjust; int xshift; int yshift; int bad_value = -10000; /* set bad values to this */ int bad_check = -3000; /* check values against this to determine if good */ struct point far *lastrow; /* this array remembers the previous line */ int RAY = 0; /* Flag to generate Ray trace compatible files in 3d */ int BRIEF = 0; /* 1 = short ray trace files */ /* array of min and max x values used in triangle fill */ struct minmax far *minmax_x; VECTOR view; /* position of observer for perspective */ VECTOR cross; VECTOR tmpcross; struct point oldlast = { 0, 0, 0 }; /* old pixels */ int line3d(BYTE * pixels, unsigned linelen) { int tout; /* triangle has been sent to ray trace file */ int RND; float f_water = (float)0.0; /* transformed WATERLINE for ray trace files */ double r0; int xcenter0 = 0; int ycenter0 = 0; /* Unfudged versions */ double r; /* sphere radius */ float costheta, sintheta; /* precalculated sin/cos of latitude */ int next; /* used by preview and grid */ int col; /* current column (original GIF) */ struct point cur; /* current pixels */ struct point old; /* old pixels */ struct f_point f_cur; struct f_point f_old; VECTOR v; /* double vector */ VECTOR v1, v2; VECTOR crossavg; char crossnotinit; /* flag for crossavg init indication */ LVECTOR lv; /* long equivalent of v */ LVECTOR lv0; /* long equivalent of v */ int lastdot; long fudge; fudge = 1L << 16; if (transparent[0] || transparent[1]) plot = normalplot = T_clipcolor; /* Use transparent plot function */ else /* Use the usual FRACTINT 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) { int err; if ((err = first_time(linelen, v)) != 0) return (err); tout = 0; crossavg[0] = 0; crossavg[1] = 0; crossavg[2] = 0; xcenter0 = (int) (xcenter = xdots / 2 + xshift); ycenter0 = (int) (ycenter = ydots / 2 - yshift); } /* make sure these pixel coordinates are out of range */ old = bad; f_old = f_bad; /* copies pixels buffer to float type fraction buffer for fill purposes */ if (pot16bit) { if (set_pixel_buff(pixels, fraction, linelen)) return (0); } else if (grayflag) /* convert color numbers to grayscale values */ for (col = 0; col < (int) linelen; col++) { int pal, colornum; colornum = pixels[col]; /* effectively (30*R + 59*G + 11*B)/100 scaled 0 to 255 */ pal = ((int) dacbox[colornum][0] * 77 + (int) dacbox[colornum][1] * 151 + (int) dacbox[colornum][2] * 28); pal >>= 6; pixels[col] = (BYTE) pal; } crossnotinit = 1; col = 0; CO = 0; /*************************************************************************/ /* This section of code allows the operation of a preview mode when the */ /* preview flag is set. Enabled, it allows the drawing of only the first */ /* line of the source image, then every 10th line, until and including */ /* the last line. For the undrawn lines, only necessary calculations are */ /* made. As a bonus, in non-sphere mode a box is drawn to help visualize */ /* the effects of 3D transformations. Thanks to Marc Reinig for this idea*/ /* and code -- BTW, Marc did NOT put the goto in, but WE did, to avoid */ /* copying code here, and to avoid a HUGE "if-then" construct. Besides, */ /* we have ALREADY sinned, so why not sin some more? */ /*************************************************************************/ lastdot = min(xdots - 1, (int) linelen - 1); if (FILLTYPE >= 5) if (haze && Targa_Out) { HAZE_MULT = (int) (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 (dotmode == 11) { static FCODE mapping[] = {"mapping to 3d, reading line "}; char s[40]; #ifndef XFRACT sprintf(s, "%Fs%d", (char far *)mapping, currow); #else sprintf(s, "%s%d", mapping, currow); #endif dvid_status(1, s); } if (!col && RAY && currow != 0) start_object(); /* PROCESS ROW LOOP BEGINS HERE */ while (col < (int) 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 = cur.color = Real_Color = pixels[col]; if (RAY || preview || FILLTYPE < 0) { next = (int) (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 = cur.color = Real_Color = (BYTE)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; /* Allow Ray trace to go through so display ok */ if (persp || RAY) { /* 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] = (long) (-R - r * costheta * sinphi); /* z */ if ((lv[2] > zcutoff) && !FILLTYPE < 0) { cur = bad; f_cur = f_bad; goto loopbottom; /* another goto ! */ } lv[0] = (long) (xcenter + sintheta * sclx * r); /* x */ lv[1] = (long) (ycenter + costheta * cosphi * scly * r); /* y */ if ((FILLTYPE >= 5) || RAY) { /* calculate illumination normal before persp */ r0 = r / 65536L; f_cur.x = (float) (xcenter0 + sintheta * sclx * r0); f_cur.y = (float) (ycenter0 + costheta * cosphi * scly * r0); f_cur.color = (float) (-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 = (int) (((lv[0] + 32768L) >> 16) + xxadjust); cur.y = (int) (((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 = (int) (v[0] + .5 + xxadjust); cur.y = (int) (v[1] + .5 + yyadjust); } } /* mrr Not sure how this an 3rd if above relate */ else if (!(persp && RAY)) { /* mrr Why the xx- and yyadjust here and not above? */ cur.x = (int) (f_cur.x = (float) (xcenter + sintheta * sclx * r + xxadjust)); cur.y = (int) (f_cur.y = (float) (ycenter + costheta * cosphi * scly * r + yyadjust)); if (FILLTYPE >= 5 || RAY) /* mrr why do we do this for * filltype>5? */ f_cur.color = (float) (-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] = (long) (f_cur.color * 65536.0); else /* there IS no fractaional part here! */ { lv[2] = (long) f_cur.color; lv[2] = lv[2] << 16; } if (longvmultpersp(lv, llm, lv0, lv, lview, 16) == -1) { cur = bad; f_cur = f_bad; goto loopbottom; } cur.x = (int) (((lv[0] + 32768L) >> 16) + xxadjust); cur.y = (int) (((lv[1] + 32768L) >> 16) + yyadjust); if (FILLTYPE >= 5 && !overflow) { f_cur.x = (float) lv0[0]; f_cur.x /= (float)65536.0; f_cur.y = (float) lv0[1]; f_cur.y /= (float)65536.0; f_cur.color = (float) lv0[2]; f_cur.color /= (float)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 = (float) v[0]; f_cur.y = (float) v[1]; f_cur.color = (float) v[2]; if (RAY == 6) { f_cur.x = f_cur.x * ((float)2.0 / xdots) - (float)1.0; f_cur.y = f_cur.y * ((float)2.0 / ydots) - (float)1.0; f_cur.color = -f_cur.color * ((float)2.0 / numcolors) - (float)1.0; } } if (persp && !RAY) perspective(v); cur.x = (int) (v[0] + xxadjust + .5); cur.y = (int) (v[1] + yyadjust + .5); v[0] = 0; v[1] = 0; v[2] = WATERLINE; mult_vec(v); f_water = (float) 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 = (BYTE)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 = (int) (xcenter >> 16); old.y = (int) (ycenter >> 16); } else { old.x = (int) xcenter; old.y = (int) 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, llm, lv0, lv, lview, 16)) { cur = bad; f_cur = f_bad; goto loopbottom; /* another goto ! */ } /* Round and fudge back to original */ old.x = (int) ((lv[0] + 32768L) >> 16); old.y = (int) ((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) { static FCODE msg[] = {"debug, cur.color=bad"}; stopmsg(0, msg); } cur.color = (int)(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) { static FCODE msg[] = {"debug, normal vector err2"}; stopmsg(0, msg); /* use next instead if you ever need details: * static char far tmp[] = {"debug, vector err"}; * char msg[200]; #ifndef XFRACT * sprintf(msg,"%Fs\n%f %f %f\n%f %f %f\n%f %f * %f", #else sprintf(msg,"%s\n%f %f %f\n%f %f * %f\n%f %f %f", #endif 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); * stopmsg(0,msg); */ } cur.color = (int)(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 = (int) (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 _fastcall vdraw_line(double *v1, double *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); } static void corners(MATRIX m, int show, double *pxmin, double *pymin, double *pzmin, double *pxmax, double *pymax, double *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(double *origin, double *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] = -P * 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(VECTOR V0, VECTOR V1, VECTOR V2, VECTOR V3, int color, int rect) { VECTOR V[4]; int i; /* Since V[2] is not used by vdraw_line don't bother setting it */ for (i = 0; i < 2; i++) { 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 */ static void _fastcall putminmax(int x, int y, int color) { color = 0; /* to supress warning only */ 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 */ #define MAXOFFSCREEN 2 /* allow two of three points to be off screen */ static void _fastcall 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; } static int _fastcall 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 _fastcall 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) /* standardplot modifies color in these types */ if (!(glassestype == 1 || glassestype == 2)) 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 _fastcall 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) /* standardplot modifies color in these types */ if (!(glassestype == 1 || glassestype == 2)) 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 _fastcall 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 = (int) (((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) /* standardplot modifies color in these types */ if (!(glassestype == 1 || glassestype == 2)) 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 _fastcall targa_color(int x, int y, int color) { unsigned long H, S, V; BYTE RGB[3]; if (FILLTYPE == 2 || glassestype == 1 || glassestype == 2) Real_Color = (BYTE)color; /* So Targa gets interpolated color */ RGB[0] = (BYTE)(dacbox[Real_Color][0] << 2); /* Move color space to */ RGB[1] = (BYTE)(dacbox[Real_Color][1] << 2); /* 256 color primaries */ RGB[2] = (BYTE)(dacbox[Real_Color][2] << 2); /* from 64 colors */ /* 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 * (65535L - (unsigned) (color * IAmbient))) / 65535L; if (haze) { /* Haze lowers sat of colors */ S = (unsigned long) (S * HAZE_MULT) / 100; 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(x + sxoffs, y + syoffs, RGB[0], RGB[1], RGB[2]); return ((int) (255 - V)); } static int set_pixel_buff(BYTE * pixels, BYTE far * fraction, unsigned linelen) { int i; if ((evenoddrow++ & 1) == 0) /* even rows are color value */ { for (i = 0; i < (int) linelen; i++) /* add the fractional part in * odd row */ fraction[i] = pixels[i]; return (1); } else /* swap */ { BYTE tmp; for (i = 0; i < (int) linelen; i++) /* swap so pixel has color */ { tmp = pixels[i]; pixels[i] = fraction[i]; fraction[i] = tmp; } } return (0); } /************************************************************************** Common routine for printing error messages to the screen for Targa and other files **************************************************************************/ #ifndef XFRACT static char s_f[] = "%Fs%Fs"; static char s_fff[] = "%Fs%Fs%Fs"; #else static char s_f[] = "%s%s"; static char s_fff[] = "%s%s%s"; #endif static FCODE OOPS[] = {"OOPS, "}; static FCODE E1[] = {"can't handle this type of file.\n"}; static FCODE str1[] = {"couldn't open < "}; static FCODE str3[] = {"image wrong size\n"}; static FCODE outofdisk[] = {"ran out of disk space. < "}; static void File_Error(char *File_Name1, int ERROR) { char msgbuf[200]; error = ERROR; switch (ERROR) { case 1: /* Can't Open */ #ifndef XFRACT sprintf(msgbuf, "%Fs%Fs%s >", (char far *)OOPS, (char far *)str1, File_Name1); #else sprintf(msgbuf, "%s%s%s >", OOPS, str1, File_Name1); #endif break; case 2: /* Not enough room */ #ifndef XFRACT sprintf(msgbuf, "%Fs%Fs%s >", (char far *)OOPS, (char far *)outofdisk, File_Name1); #else sprintf(msgbuf, "%s%s%s >", OOPS, outofdisk, File_Name1); #endif break; case 3: /* Image wrong size */ sprintf(msgbuf, s_f, (char far *)OOPS, (char far *)str3); break; case 4: /* Wrong file type */ sprintf(msgbuf, s_f, (char far *)OOPS, (char far *)E1); break; } stopmsg(0, msgbuf); 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. */ /* */ /* **********************************************************************/ extern int truecolor; 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 = dir_fopen(workdir,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 < T_header_24; i++) /* Copy the header from the Source */ fputc(fgetc(Source), fps); else { /* Write header for a new file */ /* ID field size = 0, No color map, Targa type 2 file */ for (i = 0; i < 12; i++) { extern int truecolor; if (i == 0 && truecolor != 0) { set_upr_lwr(); fputc(4, fps); /* make room to write an extra number */ T_header_24 = 18 + 4; } else if (i == 2) fputc(i, fps); else fputc(0, fps); } /* Write image size */ for (i = 0; i < 4; i++) fputc(upr_lwr[i], fps); fputc(T24, fps); /* Targa 24 file */ fputc(T32, fps); /* Image at upper left */ inc = 3; } if(truecolor) /* write maxit */ { fputc((BYTE)(maxit & 0xff), fps); fputc((BYTE)((maxit>>8 ) & 0xff), fps); fputc((BYTE)((maxit>>16) & 0xff), fps); fputc((BYTE)((maxit>>24) & 0xff), fps); } /* Finished with the header, now lets work on the display area */ for (i = 0; i < ydots; i++) /* "clear the screen" (write to the disk) */ { for (j = 0; j < line_length1; j = j + inc) { if (overlay) fputc(fgetc(Source), fps); else for (k = 2; k > -1; k--) fputc(back_color[k], fps); /* Targa order (B, G, R) */ } if (ferror(fps)) { /* Almost certainly not enough disk space */ fclose(fps); if(overlay) fclose(Source); dir_remove(tempdir,File_Name2); File_Error(File_Name2, 2); return (-2); } if (keypressed()) return (-3); } if (targa_startdisk(fps, T_header_24) != 0) { enddisk(); dir_remove(tempdir,File_Name2); return (-4); } return (0); } int targa_validate(char *File_Name) { FILE *fp; int i; #if 0 int j = 0; #endif /* 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 0 if (j != 0) { File_Error(File_Name, 4); return (-1); } #endif } /* Check Image specs */ for (i = 0; i < 4; i++) if (fgetc(fp) != (int) upr_lwr[i]) { File_Error(File_Name, 3); return (-1); } if (fgetc(fp) != (int) T24) error = 4; /* Is it a targa 24 file? */ if (fgetc(fp) != (int) T32) error = 4; /* Is the origin at the upper left? */ if (error == 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(BYTE R, BYTE G, BYTE B, unsigned long *H, unsigned long *S, unsigned long *V) { unsigned long R1, G1, B1, DENOM; BYTE MIN; *V = R; MIN = G; if (R < G) { *V = G; MIN = R; if (G < B) *V = B; if (B < R) MIN = B; } else { if (B < G) MIN = B; if (R < B) *V = B; } DENOM = *V - MIN; 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 == MIN) { *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 (MIN == G) *H = (300 << 6) + B1; else *H = (60 << 6) - G1; if (*V == G) if (MIN == B) *H = (60 << 6) + R1; else *H = (180 << 6) - B1; if (*V == B) if (MIN == R) *H = (180 << 6) + G1; else *H = (300 << 6) - R1; *V = *V << 8; return (0); } static int H_R(BYTE *R, BYTE *G, BYTE *B, unsigned long H, unsigned long S, unsigned long V) { unsigned long P1, P2, P3; int RMD, I; if (H >= 23040) H = H % 23040; /* Makes h circular */ I = (int) (H / 3840); RMD = (int) (H % 3840); /* RMD = fractional part of H */ P1 = ((V * (65535L - S)) / 65280L) >> 8; P2 = (((V * (65535L - (S * RMD) / 3840)) / 65280L) - 1) >> 8; P3 = (((V * (65535L - (S * (3840 - RMD)) / 3840)) / 65280L)) >> 8; V = V >> 8; switch (I) { case 0: *R = (BYTE) V; *G = (BYTE) P3; *B = (BYTE) P1; break; case 1: *R = (BYTE) P2; *G = (BYTE) V; *B = (BYTE) P1; break; case 2: *R = (BYTE) P1; *G = (BYTE) V; *B = (BYTE) P3; break; case 3: *R = (BYTE) P1; *G = (BYTE) P2; *B = (BYTE) V; break; case 4: *R = (BYTE) P3; *G = (BYTE) P1; *B = (BYTE) V; break; case 5: *R = (BYTE) V; *G = (BYTE) P1; *B = (BYTE) 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" ) */ /* 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 FRACTINT which created it an the */ /* key 3D parameters in effect at the time. */ /* */ /********************************************************************/ static FCODE declare[] = {"DECLARE "}; static FCODE frac_default[] = {"F_Dflt"}; static FCODE s_color[] = {"COLOR "}; static FCODE dflt[] = {"RED 0.8 GREEN 0.4 BLUE 0.1\n"}; static FCODE d_color[] = {"0.8 0.4 0.1"}; static FCODE r_surf[] = {"0.95 0.05 5 0 0\n"}; static FCODE surf[] = {"surf={diff="}; /* EB & DG: changed "surface T" to "applysurf" and "diff" to "diffuse" */ static FCODE rs_surf[] = {"applysurf diffuse "}; static FCODE end[] = {"END_"}; static FCODE plane[] = {"PLANE"}; static FCODE m1[] = {"-1.0 "}; static FCODE one[] = {" 1.0 "}; static FCODE z[] = {" 0.0 "}; static FCODE bnd_by[] = {" BOUNDED_BY\n"}; static FCODE end_bnd[] = {" END_BOUND\n"}; static FCODE inter[] = {"INTERSECTION\n"}; #ifndef XFRACT static char fmt[] = " %Fs <%Fs%Fs%Fs> % #4.3f %Fs%Fs\n"; #else static char fmt[] = " %s <%s%s%s> % #4.3f %s%s\n"; #endif 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 FCODE composite[] = {"COMPOSITE"}; static FCODE object[] = {"OBJECT"}; static FCODE triangle[] = {"TRIANGLE "}; static FCODE l_tri[] = {"triangle"}; static FCODE texture[] = {"TEXTURE\n"}; /* static FCODE end_texture[] = {" END_TEXTURE\n"}; */ static FCODE red[] = {"RED"}; static FCODE green[] = {"GREEN"}; static FCODE blue[] = {"BLUE"}; static FCODE frac_texture[] = {" AMBIENT 0.25 DIFFUSE 0.75"}; static FCODE polygon[] = {"polygon={points=3;"}; static FCODE vertex[] = {" vertex = "}; static FCODE d_vert[] = {" <"}; static char f1[] = "% #4.4f "; /* EB & DG: changed this to much better values */ static FCODE grid[] = {"screen 640 480\neyep 0 2.1 0.8\nlookp 0 0 -0.95\nlight 1 point -2 1 1.5\n"}; static FCODE grid2[] = {"background .3 0 0\nreport verbose\n"}; static char s_n[] = "\n"; static char f2[] = "R%dC%d R%dC%d\n"; static FCODE ray_comment1[] = {"/* make a gridded aggregate. this size grid is fast for landscapes. */\n"}; static FCODE ray_comment2[] = {"/* make z grid = 1 always for landscapes. */\n\n"}; static FCODE grid3[] = {"grid 33 25 1\n"}; static int _fastcall 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, (char far *)s3, release / 100., (char far *)s3a); if (RAY == 5) fprintf(File_Ptr1, "*/\n"); /* Set the default color */ if (RAY == 1) { fprintf(File_Ptr1, s_f, (char far *)declare, (char far *)frac_default); fprintf(File_Ptr1, " = "); fprintf(File_Ptr1, s_f, (char far *)s_color, (char far *)dflt); } if (BRIEF) { if (RAY == 2) { fprintf(File_Ptr1, s_f, (char far *)surf, (char far *)d_color); fprintf(File_Ptr1, ";}\n"); } if (RAY == 4) { fprintf(File_Ptr1, "f "); fprintf(File_Ptr1, s_f, (char far *)d_color, (char far *)r_surf); } if (RAY == 5) fprintf(File_Ptr1, s_f, (char far *)rs_surf, (char far *)d_color); } if (RAY != 7) fprintf(File_Ptr1, s_n); /* EB & DG: open "grid" opject, a speedy way to do aggregates in rayshade */ if (RAY == 5) fprintf(File_Ptr1, s_fff, (char far *)ray_comment1, (char far *)ray_comment2, (char far *)grid3); if (RAY == 6) #ifndef XFRACT fprintf(File_Ptr1, "%Fs", (char far *)acro_s1); #else fprintf(File_Ptr1, "%s", acro_s1); #endif 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 _fastcall 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++) #ifdef __SVR4 c[i] = (float) ((int)(dacbox[c1][i] + dacbox[c2][i] + dacbox[c3][i]) / (3 * 63)); #else c[i] = (float) (dacbox[c1][i] + dacbox[c2][i] + dacbox[c3][i]) / (3 * 63); #endif /* 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 */ #ifndef XFRACT if (RAY == 1) fprintf(File_Ptr1, " %Fs\n %Fs", (char far *)object, (char far *)triangle); if (RAY == 2 && !BRIEF) fprintf(File_Ptr1, "%Fs", (char far *)surf); #else if (RAY == 1) fprintf(File_Ptr1, " %s\n %s", object, triangle); if (RAY == 2 && !BRIEF) fprintf(File_Ptr1, "%s", surf); #endif if (RAY == 4 && !BRIEF) fprintf(File_Ptr1, "f"); if (RAY == 5 && !BRIEF) #ifndef XFRACT fprintf(File_Ptr1, "%Fs", (char far *)rs_surf); #else fprintf(File_Ptr1, "%s", rs_surf); #endif 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"); #ifndef XFRACT fprintf(File_Ptr1, "%Fs", (char far *)polygon); #else fprintf(File_Ptr1, "%s", polygon); #endif } if (RAY == 4) { if (!BRIEF) #ifndef XFRACT fprintf(File_Ptr1, "%Fs", (char far *)r_surf); #else fprintf(File_Ptr1, "%s", r_surf); #endif fprintf(File_Ptr1, "p 3"); } if (RAY == 5) { if (!BRIEF) fprintf(File_Ptr1, s_n); /* EB & DG: removed "T" after "triangle" */ #ifndef XFRACT fprintf(File_Ptr1, "%Fs", (char far *)l_tri); #else fprintf(File_Ptr1, "%s", l_tri); #endif } 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, s_n); #ifndef XFRACT if (RAY == 1) fprintf(File_Ptr1, "%Fs", (char far *)d_vert); if (RAY == 2) fprintf(File_Ptr1, "%Fs", (char far *)vertex); #else if (RAY == 1) fprintf(File_Ptr1, "%s", d_vert); if (RAY == 2) fprintf(File_Ptr1, "%s", vertex); #endif 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) { #ifndef XFRACT fprintf(File_Ptr1, " %Fs%Fs\n", (char far *)end, (char far *)triangle); #else fprintf(File_Ptr1, " %s%s\n", end, triangle); #endif if (!BRIEF) { #ifndef XFRACT fprintf(File_Ptr1, " %Fs" " %Fs%Fs% #4.4f %Fs% #4.4f %Fs% #4.4f\n" "%Fs" " %Fs%Fs", #else fprintf(File_Ptr1, " %s %s%s% #4.4f %s% #4.4f %s% #4.4f\n%s %s%s", #endif (char far *)texture, (char far *)s_color, (char far *)red, c[0], (char far *)green, c[1], (char far *)blue, c[2], (char far *)frac_texture, (char far *)end, (char far *)texture); } #ifndef XFRACT fprintf(File_Ptr1, " %Fs%Fs %Fs%Fs", #else fprintf(File_Ptr1, " %s%s %s%s", #endif (char far *)s_color, (char far *)frac_default, (char far *)end, (char far *)object); triangle_bounds(pt_t); /* update bounding info */ } if (RAY == 2) fprintf(File_Ptr1, "}"); if (RAY == 3 && !BRIEF) fprintf(File_Ptr1, s_n); if (RAY != 7) fprintf(File_Ptr1, s_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 _fastcall 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 _fastcall 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] = (float)999999.0; max_xyz[0] = max_xyz[1] = max_xyz[2] = (float)-999999.0; #ifndef XFRACT fprintf(File_Ptr1, "%Fs\n", (char far *)composite); #else fprintf(File_Ptr1, "%s\n", composite); #endif 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 _fastcall 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] -= (float)0.01; max_xyz[i] += (float)0.01; } else { min_xyz[i] -= (max_xyz[i] - min_xyz[i]) * (float)0.01; max_xyz[i] += (max_xyz[i] - min_xyz[i]) * (float)0.01; } } /* Add the bounding box info */ #ifndef XFRACT fprintf(File_Ptr1, "%Fs %Fs", (char far *)bnd_by, (char far *)inter); #else fprintf(File_Ptr1, "%s %s", bnd_by, inter); #endif fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)m1, (char far *)z, (char far *)z, -min_xyz[0], (char far *)end, (char far *)plane); fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)one, (char far *)z, (char far *)z, max_xyz[0], (char far *)end, (char far *)plane); fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)z, (char far *)m1, (char far *)z, -min_xyz[1], (char far *)end, (char far *)plane); fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)z, (char far *)one, (char far *)z, max_xyz[1], (char far *)end, (char far *)plane); fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)z, (char far *)z, (char far *)m1, -min_xyz[2], (char far *)end, (char far *)plane); fprintf(File_Ptr1, fmt, (char far *)plane, (char far *)z, (char far *)z, (char far *)one, max_xyz[2], (char far *)end, (char far *)plane); #ifndef XFRACT fprintf(File_Ptr1, " %Fs%Fs%Fs", (char far *)end, (char far *)inter, (char far *)end_bnd); #else fprintf(File_Ptr1, " %s%s%s", end, inter, end_bnd); #endif } /* Complete the composite object statement */ #ifndef XFRACT fprintf(File_Ptr1, "%Fs%Fs\n", (char far *)end, (char far *)composite); #else fprintf(File_Ptr1, "%s%s\n", end, composite); #endif } if (RAY != 6 && RAY != 5) fprintf(File_Ptr1, s_n); /* EB & DG: too many newlines */ return (0); } static void line3d_cleanup(void) { int i, j; if (RAY && File_Ptr1) { /* Finish up the ray tracing files */ static FCODE n_ta[] = {"{ No. Of Triangles = "}; if (RAY != 5 && RAY != 7) fprintf(File_Ptr1, s_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) #ifndef XFRACT /* EB & DG: end grid aggregate */ fprintf(File_Ptr1, "end\n\n/*good landscape:*/\n%Fs%Fs\n/*", (char far *)grid, (char far *)grid2); #else /* EB & DG: end grid aggregate */ fprintf(File_Ptr1, "end\n\n/*good landscape:*/\n%s%s\n/*", grid, grid2); #endif if (RAY == 6) { #ifndef XFRACT fprintf(File_Ptr1, "%Fs", (char far *)acro_s2); #else fprintf(File_Ptr1, "%s", acro_s2); #endif for (i = 0; i < RO; i++) for (j = 0; j <= CO_MAX; j++) { if (j < CO_MAX) fprintf(File_Ptr1, f2, i, j, i, j + 1); if (i < RO - 1) fprintf(File_Ptr1, f2, i, j, i + 1, j); if (i && i < RO && j < CO_MAX) fprintf(File_Ptr1, f2, i, j, i - 1, j + 1); } fprintf(File_Ptr1, "\n\n--"); } if (RAY != 7) #ifndef XFRACT fprintf(File_Ptr1, "%Fs%ld }*/\n\n", (char far *)n_ta, num_tris); #else fprintf(File_Ptr1, "%s%ld }*/\n\n", n_ta, num_tris); #endif if (RAY == 7) fprintf(File_Ptr1, dxf_end); fclose(File_Ptr1); File_Ptr1 = NULL; } if (Targa_Out) { /* Finish up targa files */ T_header_24 = 18; /* Reset Targa header size */ enddisk(); if (!debugflag && T_Safe && !error && Targa_Overlay) { remove(light_name); rename(targa_temp, light_name); } if (!debugflag && Targa_Overlay) remove(targa_temp); } usr_floatflag &= 1; /* strip second bit */ error = T_Safe = 0; } static void set_upr_lwr(void) { upr_lwr[0] = (BYTE)(xdots & 0xff); upr_lwr[1] = (BYTE)(xdots >> 8); upr_lwr[2] = (BYTE)(ydots & 0xff); upr_lwr[3] = (BYTE)(ydots >> 8); line_length1 = 3 * xdots; /* line length @ 3 bytes per pixel */ } static int first_time(int linelen, VECTOR v) { int err; MATRIX lightm; /* m w/no trans, keeps obj. on screen */ float twocosdeltatheta; double xval, yval, zval; /* rotation values */ /* corners of transformed xdotx by ydots x colors box */ double xmin, ymin, zmin, xmax, ymax, zmax; int i, j; double v_length; VECTOR origin, direct, tmp; 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 = 1; IAmbient = (unsigned int) (255 * (float) (100 - Ambient) / 100.0); if (IAmbient < 1) IAmbient = 1; 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; set_upr_lwr(); error = 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; if((err=line3dmem()) != 0) return(err); /* get scale factors */ sclx = XSCALE / 100.0; scly = YSCALE / 100.0; if (ROUGH) sclz = -ROUGH / 100.0; else rscale = sclz = -0.0001; /* if rough=0 make it very flat but plot * something */ /* 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(m); 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), m); trans((double) xdots / (-2.0), (double) ydots / (-2.0), (double) zcoord / (-2.0), lightm); /* apply scale factors */ scale(sclx, scly, sclz, m); 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, m); xrot(xval, lightm); yrot(yval, m); yrot(yval, lightm); zrot(zval, m); zrot(zval, lightm); /* Find values of translation that make all x,y,z negative */ /* m current matrix */ /* 0 means don't show box */ /* returns minimum and maximum values of x,y,z in fractal */ corners(m, 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, m); /* 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, m); /* 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++) llm[i][j] = (long) (m[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 = (float) (THETA1 * PI / 180.0); theta2 = (float) (THETA2 * PI / 180.0); /* Map Y to this LONGITUDE range */ phi1 = (float) (PHI1 * PI / 180.0); phi2 = (float) (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] = (float) sin((double) theta); costhetaarray[0] = (float) cos((double) theta); sinthetaarray[1] = (float) sin((double) (theta + deltatheta)); costhetaarray[1] = (float) cos((double) (theta + deltatheta)); /* sin,cos delta theta */ twocosdeltatheta = (float) (2.0 * cos((double) deltatheta)); /* build table of other sin,cos with trig identity */ for (i = 2; i < (int) linelen; i++) { sinthetaarray[i] = sinthetaarray[i - 1] * twocosdeltatheta - sinthetaarray[i - 2]; costhetaarray[i] = costhetaarray[i - 1] * twocosdeltatheta - costhetaarray[i - 2]; } /* now phi - these calculated as we go - get started here */ deltaphi = (float) (phi2 - phi1) / (float) height; /* initial sin,cos phi */ sinphi = oldsinphi1 = (float) sin((double) phi1); cosphi = oldcosphi1 = (float) cos((double) phi1); oldsinphi2 = (float) sin((double) (phi1 + deltaphi)); oldcosphi2 = (float) cos((double) (phi1 + deltaphi)); /* sin,cos delta phi */ twocosdeltaphi = (float) (2.0 * cos((double) deltaphi)); /* affects how rough planet terrain is */ if (ROUGH) rscale = .3 * ROUGH / 100.0; /* radius of planet */ R = (double) (ydots) / 2; /* precalculate factor */ rXrscale = R * rscale; sclz = sclx = scly = RADIUS / 100.0; /* Need x,y,z for RAY */ /* adjust x scale factor for aspect */ sclx *= aspect; /* precalculation factor used in sphere calc */ Rfactor = rscale * R / (double) zcoord; if (persp) /* precalculate fudge factor */ { double radius; double zview; double angle; xcenter = xcenter << 16; ycenter = ycenter << 16; Rfactor *= 65536.0; R *= 65536.0; /* calculate z cutoff factor attempt to prevent out-of-view surfaces * from being written */ zview = -(long) ((double) ydots * (double) ZVIEWER / 100.0); radius = (double) (ydots) / 2; angle = atan(-radius / (zview + radius)); zcutoff = -radius - sin(angle) * radius; zcutoff *= 1.1; /* for safety */ zcutoff *= 65536L; } } /* set fill plot function */ if (FILLTYPE != 3) fillplot = interpcolor; else { fillplot = clipcolor; if (transparent[0] || transparent[1]) /* If transparent colors are set */ fillplot = T_clipcolor;/* Use the transparent plot function */ } /* Both Sphere and Normal 3D */ direct[0] = light_direction[0] = XLIGHT; direct[1] = light_direction[1] = -YLIGHT; direct[2] = light_direction[2] = ZLIGHT; /* Needed because sclz = -ROUGH/100 and light_direction is transformed in * FILLTYPE 6 but not in 5. */ if (FILLTYPE == 5) direct[2] = light_direction[2] = -ZLIGHT; if (FILLTYPE == 6) /* transform light direction */ { /* Think of light direction as a vector with tail at (0,0,0) and head * at (light_direction). We apply the transformation to BOTH head and * tail and take the difference */ v[0] = 0.0; v[1] = 0.0; v[2] = 0.0; vmult(v, m, v); vmult(light_direction, m, light_direction); for (i = 0; i < 3; i++) light_direction[i] -= v[i]; } normalize_vector(light_direction); if (preview && showbox) { normalize_vector(direct); /* move light vector to be more clear with grey scale maps */ origin[0] = (3 * xdots) / 16; origin[1] = (3 * ydots) / 4; if (FILLTYPE == 6) origin[1] = (11 * ydots) / 16; origin[2] = 0.0; v_length = min(xdots, ydots) / 2; if (persp && ZVIEWER <= P) v_length *= (long) (P + 600) / ((long) (ZVIEWER + 600) * 2); /* Set direct[] to point from origin[] in direction of untransformed * light_direction (direct[]). */ for (i = 0; i < 3; i++) direct[i] = origin[i] + direct[i] * v_length; /* center light box */ for (i = 0; i < 2; i++) { tmp[i] = (direct[i] - origin[i]) / 2; origin[i] -= tmp[i]; direct[i] -= tmp[i]; } /* Draw light source vector and box containing it, draw_light_box will * transform them if necessary. */ draw_light_box(origin, direct, lightm); /* draw box around original field of view to help visualize effect of * rotations 1 means show box - xmin etc. do nothing here */ if (!SPHERE) corners(m, 1, &xmin, &ymin, &zmin, &xmax, &ymax, &zmax); } /* bad has values caught by clipping */ f_bad.x = bad.x = bad_value; f_bad.y = bad.y = bad_value; f_bad.color = bad.color = bad_value; for (i = 0; i < (int) linelen; i++) { lastrow[i] = bad; f_lastrow[i] = f_bad; } got_status = 3; if (iit > 0) { load_mat(m); /* load matrix into iit registers */ mult_vec = mult_vec_iit; } else mult_vec = mult_vec_c; return (0); } /* end of once-per-image intializations */ /* This pragma prevents optimizer failure in MSC/C++ 7.0. Program compiles ok without pragma, but error message is real ugly, paraphrasing loosely, something like "optimizer screwed up big time, call Bill Gates collect ... (Note: commented out pragma because we removed the compiler "/Og" option in MAKEFRACT.BAT - left these notes as a warning... */ #ifdef _MSC_VER #if (_MSC_VER >= 600) /* #pragma optimize( "g", off ) */ #endif #endif static int line3dmem(void) { /*********************************************************************/ /* 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 far 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 legal resolution of MAXPIXELSxMAXPIXELS */ /* or thereabouts. In that case we use farmemalloc to grab memory */ /* for minmax_x. This memory is never freed. */ /*********************************************************************/ long check_extra; /* keep track ofd extraseg array use */ /* lastrow stores the previous row of the original GIF image for the purpose of filling in gaps with triangle procedure */ /* first 8k of extraseg now used in decoder TW 3/95 */ lastrow = MK_FP(extraseg, 0); check_extra = sizeof(*lastrow) * xdots; if (SPHERE) { sinthetaarray = (float far *) (lastrow + xdots); check_extra += sizeof(*sinthetaarray) * xdots; costhetaarray = (float far *) (sinthetaarray + xdots); check_extra += sizeof(*costhetaarray) * xdots; f_lastrow = (struct f_point far *) (costhetaarray + xdots); } else f_lastrow = (struct f_point far *) (lastrow + xdots); check_extra += sizeof(*f_lastrow) * (xdots); if (pot16bit) { fraction = (BYTE far *) (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 FCODE msg[] = {"farmemalloc minmax"}; static struct minmax far *got_mem = NULL; if(debugflag == 2222) stopmsg(0,msg); /* not using extra segment so decrement check_extra */ check_extra -= sizeof(struct minmax) * ydots; if (got_mem == NULL) got_mem = (struct minmax far *) (farmemalloc(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 far *) (fraction + xdots); else minmax_x = (struct minmax far *) (f_lastrow + xdots); } } if (debugflag == 2222 || check_extra > (1L << 16)) { char tmpmsg[70]; static FCODE extramsg[] = {" of extra segment"}; #ifndef XFRACT sprintf(tmpmsg, "used %ld%Fs", check_extra, (char far *)extramsg); #else sprintf(tmpmsg, "used %ld%s", check_extra, extramsg); #endif stopmsg(4, tmpmsg); } return(0); } #ifdef _MSC_VER #if (_MSC_VER >= 600) #pragma optimize( "g", on ) #endif #endif