home *** CD-ROM | disk | FTP | other *** search
/ Black Art of 3D Game Programming / Black_Art_of_3D_Game_Programming.iso / source / msc / chap_11 / black11.c next >
Encoding:
C/C++ Source or Header  |  1995-04-06  |  88.1 KB  |  3,456 lines

  1.  
  2. // I N C L U D E S ///////////////////////////////////////////////////////////
  3.  
  4. #include <io.h>
  5. #include <conio.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <dos.h>
  9. #include <bios.h>
  10. #include <fcntl.h>
  11. #include <memory.h>
  12. #include <malloc.h>
  13. #include <math.h>
  14. #include <string.h>
  15. #include <search.h>             // this one is needed for qsort()
  16.  
  17. // include all of our stuff
  18.  
  19. #include "black3.h"
  20. #include "black4.h"
  21. #include "black5.h"
  22. #include "black6.h"
  23. #include "black8.h"
  24. #include "black9.h"
  25. #include "black11.h"
  26.  
  27. // G L O B A L S //////////////////////////////////////////////////////////////
  28.  
  29. float  clip_near_z   = 100,        // the near or hither clipping plane
  30.        clip_far_z    = 3000,       // the far or yon clipping plane
  31.        screen_width  = 320,        // dimensions of the screen
  32.        screen_heigth = 200;
  33.  
  34. int viewing_distance = 200;         // distance of projection plane from camera
  35.  
  36. point_3d view_point  = {0,0,0,1};  // position of camera
  37.  
  38. vector_3d light_source = {-0.913913,0.389759,-0.113369}; // position of point light source
  39.  
  40. float ambient_light = 6;          // ambient light level
  41.  
  42. dir_3d view_angle = {0,0,0};      // angle of camera
  43.  
  44. matrix_4x4 global_view;           // the global inverse wortld to camera
  45.                                   // matrix
  46.  
  47. RGB_palette color_palette_3d;     // the color palette used for the 3D system
  48.  
  49. int num_objects;                  // number of objects in the world
  50.  
  51. object_ptr world_object_list[MAX_OBJECTS];  // the objects in the world
  52.  
  53. int num_polys_frame;                        // the number of polys in this frame
  54.  
  55. facet_ptr world_polys[MAX_POLYS_PER_FRAME]; // the visible polygons for this frame
  56.  
  57. facet world_poly_storage[MAX_POLYS_PER_FRAME];  // the storage for the visible
  58.                                                 // polygons is pre-allocated
  59.                                                 // so it doesn't need to be
  60.                                                 // allocated frame by frame
  61.  
  62. // look up tables
  63.  
  64. float sin_look[360+1],                          // SIN from 0 to 360
  65.       cos_look[360+1];                          // COSINE from 0 to 360
  66.  
  67. // the clipping region, set it to default on start up
  68.  
  69. int poly_clip_min_x = POLY_CLIP_MIN_X,
  70.     poly_clip_min_y = POLY_CLIP_MIN_Y,
  71.  
  72.     poly_clip_max_x = POLY_CLIP_MAX_X,
  73.     poly_clip_max_y = POLY_CLIP_MAX_Y;
  74.  
  75.  
  76. sprite textures;                        // this holds the textures
  77.  
  78. // F U N C T I O N S /////////////////////////////////////////////////////////
  79.  
  80. void Make_Grey_Palette(void)
  81. {
  82. // this function generates 64 shades of grey and places them in the palette
  83. // at locations 16 to 96
  84.  
  85. int index;
  86.  
  87. RGB_color color;
  88.  
  89. // generate 64 shades of grey
  90.  
  91. for (index=0; index<64; index++)
  92.     {
  93.     // grey equals equal percentage of Red, Green and Blue
  94.  
  95.     color.red   = index;
  96.     color.green = index;
  97.     color.blue  = index;
  98.  
  99.     // write the color in the palette starting at location 16, so as not to
  100.     // fry the EGA pallete
  101.  
  102.     Write_Color_Reg(index+16,(RGB_color_ptr)&color);
  103.  
  104.     } // end for index
  105.  
  106. } // end Make_Grey_Palette
  107.  
  108. ////////////////////////////////////////////////////////////////////////////////
  109.  
  110. void Draw_Triangle_2D_Text(int x1,int y1,
  111.                            int x2,int y2,
  112.                            int x3,int y3,
  113.                            unsigned char far *buffer,
  114.                            int texture_index)
  115. {
  116. // this function draws a textured triangle, but doesn't perform any clipping
  117. // and is very slow (mostly for demonstrative purposes)
  118.  
  119. int x,y,           // looping variables
  120.     x_index,       // integer texture coordinates
  121.     y_index,
  122.     bottom_1,      // distance from top to middle of triangle on y axis
  123.     bottom_2;      // distance from middle to borttom triangle on y axis
  124.  
  125. float dx_right,     // the dx/dy ratio of the right edge of line
  126.       dx_left,      // the dx/dy ratio of the left edge of line
  127.       xs,xe,        // the starting and ending points of the edges
  128.       height_left,  // the heights of the triangle
  129.       height_right;
  130.  
  131. float a,b,c,d,det,          // texture mapping inverse matrix elements
  132.       ix,iy,jx,jy,          // texture vectors
  133.       delta_uy,delta_vy,    // pre-computed deltas of textures coordinates on y axis
  134.       u_start,v_start,      // the starting u,v coordinates on each line
  135.       u_curr,v_curr;        // the current u,v texture coordinates
  136.  
  137. unsigned char far *dest_addr;   // final destination address of memory write
  138.  
  139. unsigned char far *text;        // texture memory
  140.  
  141. // assign text pointer to current texture map
  142.  
  143. text = textures.frames[texture_index];
  144.  
  145. // compute height of sub triangles
  146.  
  147. height_left  = y2-y1;
  148. height_right = y3-y1;
  149.  
  150. // compute distances from starting y vertex
  151.  
  152. if (y2>y3)
  153.    {
  154.    bottom_1 = y3;
  155.    bottom_2 = y2;
  156.    }
  157. else
  158.    {
  159.    bottom_1 = y2;
  160.    bottom_2 = y3;
  161.    } // end else
  162.  
  163. // compute edge deltas
  164.  
  165. dx_left  = (float)(x2-x1)/height_left;
  166. dx_right = (float)(x3-x1)/height_right;
  167.  
  168. // set starting points
  169.  
  170. xs = (float)x1;
  171. xe = (float)x1+(float)0.5;
  172.  
  173. // compute vector components for texture calculations
  174.  
  175. ix = (float)(x3-x1);
  176. iy = (float)(y3-y1);
  177.  
  178. jx = (float)(x2-x1);
  179. jy = (float)(y2-y1);
  180.  
  181. // compute determinant
  182.  
  183. det = (ix*jy - iy*jx);
  184.  
  185. // compute inverse matrix
  186.  
  187. a =  jy/det;
  188. b = -jx/det;
  189. c = -iy/det;
  190. d =  ix/det;
  191.  
  192. // compute starting texture coordinates
  193.  
  194. u_start = 0;
  195. v_start = 0;
  196.  
  197. // compute delta texture coordinates for unit change in y
  198.  
  199. delta_uy=(a*dx_left + b);
  200. delta_vy=(c*dx_left + d);
  201.  
  202. // compute starting address in video memory
  203.  
  204. dest_addr = buffer+(y1<<8)+(y1<<6);
  205.  
  206. // draw the triangle
  207.  
  208. for (y=y1; y<=bottom_1; y++,dest_addr+=320)
  209.     {
  210.  
  211.     // start off working texture coordinates for current row
  212.  
  213.     u_curr = u_start;
  214.     v_curr = v_start;
  215.  
  216.     // draw line
  217.  
  218.     for (x=(int)xs; x<=(int)xe; x++)
  219.         {
  220.  
  221.         x_index = abs((int)(u_curr*63+0.5));
  222.         y_index = abs((int)(v_curr*63+0.5));
  223.  
  224.         // printf("\nu=%d v=%d  ",x_index, y_index);
  225.  
  226.         dest_addr[x] = text[y_index*64 + x_index];
  227.  
  228.         // adjust x texture coordinates
  229.  
  230.         u_curr+=a;
  231.         v_curr+=c;
  232.  
  233.         } // end for x
  234.  
  235.     // adjust starting point and ending point
  236.  
  237.     xs+=dx_left;
  238.     xe+=dx_right;
  239.  
  240.     // adjust texture coordinates based on y change
  241.  
  242.     u_start += delta_uy;
  243.     v_start += delta_vy;
  244.  
  245.     } // end for
  246.  
  247. // now recompute slope of shorter edge to make it complete triangle
  248.  
  249. if (y3>y2)
  250.    {
  251.    // recompute left edge slope
  252.  
  253.    dx_left = (float)(x3-x2)/(float)(y3-y2);
  254.  
  255.    }
  256. else
  257.    {
  258.    // y2>y3, recompute right edge slope
  259.  
  260.    dx_right = (float)(x2-x3)/(float)(y2-y3);
  261.  
  262.    } // end else
  263.  
  264. // recompute texture space coordinate increments for y changes
  265.  
  266. delta_uy=(a*dx_left + b);
  267. delta_vy=(c*dx_left + d);
  268.  
  269. // draw remainder of triangle
  270.  
  271. for (y--; y<=bottom_2; y++,dest_addr+=320)
  272.     {
  273.  
  274.     // start off working texture coordinates for current row
  275.  
  276.     u_curr = u_start;
  277.     v_curr = v_start;
  278.  
  279.     // draw line
  280.  
  281.     for (x=(int)xs; x<=(int)xe; x++)
  282.         {
  283.         // scale each texture coordinate by 64 since texrtures are 64x64
  284.  
  285.         x_index = (int)(u_curr*63);
  286.         y_index = (int)(v_curr*63);
  287.  
  288.         // plot textel on screen
  289.  
  290.         dest_addr[x] = text[y_index*64 + x_index];
  291.  
  292.         // adjust x texture coordinates
  293.  
  294.         u_curr+=a;
  295.         v_curr+=c;
  296.  
  297.         } // end for x
  298.  
  299.     // adjust starting point and ending point
  300.  
  301.     xs+=dx_left;
  302.     xe+=dx_right;
  303.  
  304.     // adjust texture coordinates based on y change
  305.  
  306.     u_start += delta_uy;
  307.     v_start += delta_vy;
  308.  
  309.     } // end for
  310.  
  311. } // end Draw_Triangle_2D_Text
  312.  
  313. //////////////////////////////////////////////////////////////////////////////
  314.  
  315. void Draw_Triangle_2D_Gouraud(int x1,int y1,
  316.                               int x2,int y2,
  317.                               int x3,int y3,
  318.                               unsigned char far *buffer,
  319.                               int intensity_1,
  320.                               int intensity_2,
  321.                               int intensity_3)
  322. {
  323. // this function draws a Gouraud shaded triangle, but doesn't perform any clipping
  324. // and is very slow (mostly for demonstrative purposes)
  325.  
  326. int x,y,           // looping variables
  327.     x_index,       // integer texture coordinates
  328.     y_index,
  329.     bottom_1,      // distance from top to middle of triangle on y axis
  330.     bottom_2;      // distance from middle to borttom triangle on y axis
  331.  
  332. float dx_right,     // the dx/dy ratio of the right edge of line
  333.       dx_left,      // the dx/dy ratio of the left edge of line
  334.       xs,xe,        // the starting and ending points of the edges
  335.       height_left,  // the heights of the triangle
  336.       height_right;
  337.  
  338.  
  339. float intensity_right,   // the intensity of the right edge of the triangle
  340.       intensity_left,    // the intensity of the left edge of the triangle
  341.       intensity_mid,     // the average between the right and left
  342.       delta_y21,delta_y31;  // the y delta's
  343.  
  344. unsigned char far *dest_addr; // pointer to memory space of video write
  345.  
  346. // compute height of sub triangles
  347.  
  348. height_left  = y2-y1;
  349. height_right = y3-y1;
  350.  
  351. // compute distances from starting y vertex
  352.  
  353. if (y2>y3)
  354.    {
  355.    bottom_1 = y3;
  356.    bottom_2 = y2;
  357.    }
  358. else
  359.    {
  360.    bottom_1 = y2;
  361.    bottom_2 = y3;
  362.    } // end else
  363.  
  364. // compute edge deltas
  365.  
  366. dx_left  = (float)(x2-x1)/height_left;
  367. dx_right = (float)(x3-x1)/height_right;
  368.  
  369. // set starting points
  370.  
  371. xs = (float)x1;
  372. xe = (float)x1+(float)0.5;
  373.  
  374. // compute shading constants
  375.  
  376. delta_y21 = (float)1/(float)(y2-y1);
  377. delta_y31 = (float)1/(float)(y3-y1);
  378.  
  379. // compute starting address in video memory
  380.  
  381. dest_addr = buffer+(y1<<8)+(y1<<6);
  382.  
  383. // draw the triangle using Gouraud shading
  384.  
  385. for (y=y1; y<=bottom_1; y++,dest_addr+=320)
  386.     {
  387.     // compute left and right edge intensities as a function of y
  388.  
  389.     intensity_left  = delta_y21*(float)((y2-y)*intensity_1 + (y-y1)*intensity_2);
  390.     intensity_right = delta_y31*(float)((y3-y)*intensity_1 + (y-y1)*intensity_3);
  391.  
  392.     // draw line
  393.  
  394.     for (x=(int)xs; x<=(int)xe; x++)
  395.         {
  396.  
  397.         // compute x axis intensity interpolant
  398.  
  399.         intensity_mid = ((xe-x)*intensity_left + (x-xs)*intensity_right)/(xe-xs);
  400.  
  401.         // plot pixel on screen
  402.  
  403.         dest_addr[x] = (unsigned char)(16+intensity_mid);
  404.  
  405.         } // end for x
  406.  
  407.     // adjust starting point and ending point
  408.  
  409.     xs+=dx_left;
  410.     xe+=dx_right;
  411.  
  412.     } // end for
  413.  
  414. // now recompute slope of shorter edge to make it complete triangle
  415.  
  416. if (y3>y2)
  417.    {
  418.    // recompute left edge slope
  419.  
  420.    dx_left = (float)(x3-x2)/(float)(y3-y2);
  421.  
  422.    }
  423. else
  424.    {
  425.    // y2>y3, recompute right edge slope
  426.  
  427.    dx_right = (float)(x2-x3)/(float)(y2-y3);
  428.  
  429.    } // end else
  430.  
  431. // draw remainder of triangle
  432.  
  433.  
  434.  
  435. for (y--; y<=bottom_2; y++,dest_addr+=320)
  436.     {
  437.  
  438.     // compute left and right edge intensities as a function of y
  439.  
  440.     intensity_left  = (float)((y3-y)*intensity_2 + (y-y2)*intensity_3)/(float)(y3-y2);
  441.  
  442.     intensity_right = delta_y31*(float)((y3-y)*intensity_1 + (y-y1)*intensity_3);
  443.  
  444.     // draw line
  445.  
  446.     for (x=(int)xs; x<=(int)xe; x++)
  447.         {
  448.  
  449.         // compute x axis intensity interpolant
  450.  
  451.         intensity_mid = ((xe-x)*intensity_left + (x-xs)*intensity_right)/(xe-xs);
  452.  
  453.         // plot pixel on screen
  454.  
  455.         dest_addr[x] = (unsigned char)(16+intensity_mid);
  456.  
  457.         } // end for x
  458.  
  459.     // adjust starting point and ending point
  460.  
  461.     xs+=dx_left;
  462.     xe+=dx_right;
  463.  
  464.     } // end for
  465.  
  466. } // end Draw_Triangle_2D_Gouraud
  467.  
  468. ////////////////////////////////////////////////////////////////////////////////
  469.  
  470. void Triangle_Line(unsigned char far *dest_addr,
  471.                    unsigned int xs,
  472.                    unsigned int xe,
  473.                    int color)
  474. {
  475.  
  476. // this function draws a fast horizontal line by using WORD size writes
  477. // it's speed can be doubled by use of an external 32 bit DWORD version in
  478. // assembly...
  479.  
  480. // plot pixels at ends of line first
  481.  
  482. if (xs & 0x0001)
  483.    {
  484.    // plot a single starting pixel and move xs to an even boundary
  485.  
  486.    dest_addr[xs++] = (unsigned char)color;
  487.  
  488.    } // end if
  489.  
  490. if (!(xe & 0x0001))
  491.    {
  492.    // plot a single terminating pixel and move xe back to an odd boundary
  493.  
  494.    dest_addr[xe--] = (unsigned char)color;
  495.  
  496.    } // end if
  497.  
  498. // now plot the line
  499.  
  500. dest_addr+=xs;
  501.  
  502. // now blast the middle part of the line a WORD at a time, later use
  503. // an external assembly program to do it a DOUBLE WORD at a time!
  504.  
  505. _asm
  506.    {
  507.    les di,dest_addr      ; point es:di at data area
  508.  
  509.    mov al,BYTE PTR color ; move into al and ah the color
  510.    mov ah,al
  511.  
  512.    mov cx,xe             ; compute number of words to move  (xe-xs+1)/2
  513.    sub cx,xs
  514.    inc cx
  515.    shr cx,1              ; divide by 2
  516.  
  517.    rep stosw             ; draw the line
  518.  
  519.    } // end asm
  520.  
  521. } // end Triangle_Line
  522.  
  523. //////////////////////////////////////////////////////////////////////////////
  524.  
  525. void Draw_Top_Triangle(int x1,int y1, int x2,int y2, int x3,int y3,int color)
  526. {
  527. // this function draws a triangle that has a flat top
  528.  
  529. float dx_right,    // the dx/dy ratio of the right edge of line
  530.       dx_left,     // the dx/dy ratio of the left edge of line
  531.       xs,xe,       // the starting and ending points of the edges
  532.       height;      // the height of the triangle
  533.  
  534. int temp_x,        // used during sorting as temps
  535.     temp_y,
  536.     right,         // used by clipping
  537.     left;
  538.  
  539. unsigned char far *dest_addr;
  540.  
  541. // test order of x1 and x2
  542.  
  543. if (x2 < x1)
  544.    {
  545.    temp_x = x2;
  546.    x2     = x1;
  547.    x1     = temp_x;
  548.  
  549.    } // end if swap
  550.  
  551. // compute delta's
  552.  
  553. height = y3-y1;
  554.  
  555. dx_left  = (x3-x1)/height;
  556. dx_right = (x3-x2)/height;
  557.  
  558. // set starting points
  559.  
  560. xs = (float)x1;
  561. xe = (float)x2+(float)0.5;
  562.  
  563. // perform y clipping
  564.  
  565. if (y1<poly_clip_min_y)
  566.    {
  567.    // compute new xs and ys
  568.  
  569.    xs = xs+dx_left*(float)(-y1+poly_clip_min_y);
  570.    xe = xe+dx_right*(float)(-y1+poly_clip_min_y);
  571.  
  572.    // reset y1
  573.  
  574.    y1=poly_clip_min_y;
  575.  
  576.    } // end if top is off screen
  577.  
  578. if (y3>poly_clip_max_y)
  579.    y3=poly_clip_max_y;
  580.  
  581.  
  582. // compute starting address in video memory
  583.  
  584. dest_addr = double_buffer+(y1<<8)+(y1<<6);
  585.  
  586. // test if x clipping is needed
  587.  
  588. if (x1>=poly_clip_min_x && x1<=poly_clip_max_x &&
  589.     x2>=poly_clip_min_x && x2<=poly_clip_max_x &&
  590.     x3>=poly_clip_min_x && x3<=poly_clip_max_x)
  591.     {
  592.     // draw the triangle
  593.  
  594.     for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=320)
  595.         {
  596.  
  597.          Triangle_Line(dest_addr,(unsigned int)xs,(unsigned int)xe,color);
  598.  
  599.         //_fmemset((unsigned char far *)dest_addr+(unsigned int)xs,
  600.         //        color,
  601.         //        (unsigned int)(xe-xs));
  602.  
  603.  
  604.         // adjust starting point and ending point
  605.  
  606.         xs+=dx_left;
  607.         xe+=dx_right;
  608.  
  609.         } // end for
  610.  
  611.     } // end if no x clipping needed
  612. else
  613.    {
  614.    // clip x axis with slower version
  615.  
  616.    // draw the triangle
  617.  
  618.    for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=320)
  619.        {
  620.  
  621.        // do x clip
  622.  
  623.        left  = (int)xs;
  624.        right = (int)xe;
  625.  
  626.        // adjust starting point and ending point
  627.  
  628.        xs+=dx_left;
  629.        xe+=dx_right;
  630.  
  631.        // clip line
  632.  
  633.        if (left < poly_clip_min_x)
  634.           {
  635.           left = poly_clip_min_x;
  636.  
  637.           if (right < poly_clip_min_x)
  638.              continue;
  639.  
  640.           }
  641.  
  642.        if (right > poly_clip_max_x)
  643.           {
  644.           right = poly_clip_max_x;
  645.  
  646.           if (left > poly_clip_max_x)
  647.              continue;
  648.  
  649.           }
  650.  
  651.         Triangle_Line(dest_addr,(unsigned int)left,(unsigned int)right,color);
  652.  
  653.        //_fmemset((unsigned char far *)dest_addr+(unsigned int)left,
  654.        //        color,
  655.        //        (unsigned int)(right-left));
  656.  
  657.        } // end for
  658.  
  659.    } // end else x clipping needed
  660.  
  661. } // end Draw_Top_Triangle
  662.  
  663. /////////////////////////////////////////////////////////////////////////////
  664.  
  665. void Draw_Bottom_Triangle(int x1,int y1, int x2,int y2, int x3,int y3,int color)
  666. {
  667.  
  668. // this function draws a triangle that has a flat bottom
  669.  
  670. float dx_right,    // the dx/dy ratio of the right edge of line
  671.       dx_left,     // the dx/dy ratio of the left edge of line
  672.       xs,xe,       // the starting and ending points of the edges
  673.       height;      // the height of the triangle
  674.  
  675. int temp_x,        // used during sorting as temps
  676.     temp_y,
  677.     right,         // used by clipping
  678.     left;
  679.  
  680. unsigned char far *dest_addr;
  681.  
  682. // test order of x1 and x2
  683.  
  684. if (x3 < x2)
  685.    {
  686.    temp_x = x2;
  687.    x2     = x3;
  688.    x3     = temp_x;
  689.  
  690.    } // end if swap
  691.  
  692. // compute delta's
  693.  
  694. height = y3-y1;
  695.  
  696. dx_left  = (x2-x1)/height;
  697. dx_right = (x3-x1)/height;
  698.  
  699. // set starting points
  700.  
  701. xs = (float)x1;
  702. xe = (float)x1+(float)0.5;
  703.  
  704. // perform y clipping
  705.  
  706. if (y1<poly_clip_min_y)
  707.    {
  708.    // compute new xs and ys
  709.  
  710.    xs = xs+dx_left*(float)(-y1+poly_clip_min_y);
  711.    xe = xe+dx_right*(float)(-y1+poly_clip_min_y);
  712.  
  713.    // reset y1
  714.  
  715.    y1=poly_clip_min_y;
  716.  
  717.    } // end if top is off screen
  718.  
  719. if (y3>poly_clip_max_y)
  720.    y3=poly_clip_max_y;
  721.  
  722. // compute starting address in video memory
  723.  
  724. dest_addr = double_buffer+(y1<<8)+(y1<<6);
  725.  
  726. // test if x clipping is needed
  727.  
  728. if (x1>=poly_clip_min_x && x1<=poly_clip_max_x &&
  729.     x2>=poly_clip_min_x && x2<=poly_clip_max_x &&
  730.     x3>=poly_clip_min_x && x3<=poly_clip_max_x)
  731.     {
  732.     // draw the triangle
  733.  
  734.     for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=320)
  735.         {
  736.  
  737.         Triangle_Line(dest_addr,(unsigned int)xs,(unsigned int)xe,color);
  738.  
  739.         //_fmemset((unsigned char far *)dest_addr+(unsigned int)xs,
  740.         //        color,
  741.         //        (unsigned int)(xe-xs));
  742.  
  743.         // adjust starting point and ending point
  744.  
  745.         xs+=dx_left;
  746.         xe+=dx_right;
  747.  
  748.         } // end for
  749.  
  750.     } // end if no x clipping needed
  751. else
  752.    {
  753.    // clip x axis with slower version
  754.  
  755.    // draw the triangle
  756.  
  757.    for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=320)
  758.        {
  759.        // do x clip
  760.  
  761.        left  = (int)xs;
  762.        right = (int)xe;
  763.  
  764.        // adjust starting point and ending point
  765.  
  766.        xs+=dx_left;
  767.        xe+=dx_right;
  768.  
  769.        // clip line
  770.  
  771.        if (left < poly_clip_min_x)
  772.           {
  773.           left = poly_clip_min_x;
  774.  
  775.           if (right < poly_clip_min_x)
  776.              continue;
  777.  
  778.           }
  779.  
  780.        if (right > poly_clip_max_x)
  781.           {
  782.           right = poly_clip_max_x;
  783.  
  784.           if (left > poly_clip_max_x)
  785.              continue;
  786.           }
  787.  
  788.        Triangle_Line(dest_addr,(unsigned int)left,(unsigned int)right,color);
  789.  
  790.        //_fmemset((unsigned char far *)dest_addr+(unsigned int)left,
  791.        //        color,
  792.        //       (unsigned int)(right-left));
  793.  
  794.        } // end for
  795.  
  796.    } // end else x clipping needed
  797.  
  798. } // end Draw_Bottom_Triangle
  799.  
  800. ///////////////////////////////////////////////////////////////////////////////
  801.  
  802. void Draw_Triangle_2D(int x1,int y1,
  803.                       int x2,int y2,
  804.                       int x3,int y3,int color)
  805. {
  806.  
  807. int temp_x,
  808.     temp_y,
  809.     new_x;
  810.  
  811. // test for h lines and v lines
  812.  
  813. if ((x1==x2 && x2==x3)  ||  (y1==y2 && y2==y3))
  814.    return;
  815.  
  816. // sort p1,p2,p3 in ascending y order
  817.  
  818. if (y2<y1)
  819.    {
  820.    temp_x = x2;
  821.    temp_y = y2;
  822.    x2     = x1;
  823.    y2     = y1;
  824.    x1     = temp_x;
  825.    y1     = temp_y;
  826.    } // end if
  827.  
  828. // now we know that p1 and p2 are in order
  829.  
  830. if (y3<y1)
  831.    {
  832.    temp_x = x3;
  833.    temp_y = y3;
  834.    x3     = x1;
  835.    y3     = y1;
  836.    x1     = temp_x;
  837.    y1     = temp_y;
  838.    } // end if
  839.  
  840. // finally test y3 against y2
  841.  
  842. if (y3<y2)
  843.    {
  844.    temp_x = x3;
  845.    temp_y = y3;
  846.    x3     = x2;
  847.    y3     = y2;
  848.    x2     = temp_x;
  849.    y2     = temp_y;
  850.  
  851.    } // end if
  852.  
  853. // do trivial rejection tests
  854.  
  855. if ( y3<poly_clip_min_y || y1>poly_clip_max_y ||
  856.     (x1<poly_clip_min_x && x2<poly_clip_min_x && x3<poly_clip_min_x) ||
  857.     (x1>poly_clip_max_x && x2>poly_clip_max_x && x3>poly_clip_max_x) )
  858.    return;
  859.  
  860. // test if top of triangle is flat
  861.  
  862. if (y1==y2)
  863.    {
  864.  
  865.    Draw_Top_Triangle(x1,y1,x2,y2,x3,y3,color);
  866.  
  867.    } // end if
  868. else
  869. if (y2==y3)
  870.    {
  871.  
  872.    Draw_Bottom_Triangle(x1,y1,x2,y2,x3,y3,color);
  873.  
  874.  
  875.    } // end if bottom is flat
  876. else
  877.    {
  878.    // general triangle that's needs to be broken up along long edge
  879.  
  880.    new_x = x1 + (int)((float)(y2-y1)*(float)(x3-x1)/(float)(y3-y1));
  881.  
  882.    // draw each sub-triangle
  883.  
  884.    Draw_Bottom_Triangle(x1,y1,new_x,y2,x2,y2,color);
  885.  
  886.    Draw_Top_Triangle(x2,y2,new_x,y2,x3,y3,color);
  887.  
  888.    } // end else
  889.  
  890. } // end Draw_Triangle_2D
  891.  
  892. ////////////////////////////////////////////////////////////////////////////////
  893.  
  894. int Clip_Line(int *x1,int *y1,int *x2, int *y2)
  895. {
  896. // this function clips the sent line using the globally defined clipping
  897. // region
  898.  
  899. int point_1 = 0, point_2 = 0;  // tracks if each end point is visible or invisible
  900.  
  901. int clip_always = 0;           // used for clipping override
  902.  
  903. int xi,yi;                     // point of intersection
  904.  
  905. int right_edge=0,              // which edges are the endpoints beyond
  906.     left_edge=0,
  907.     top_edge=0,
  908.     bottom_edge=0;
  909.  
  910.  
  911. int success = 0;               // was there a successfull clipping
  912.  
  913. float dx,dy;                   // used to holds slope deltas
  914.  
  915. // test if line is completely visible
  916.  
  917. if ( (*x1>=poly_clip_min_x) && (*x1<=poly_clip_max_x) &&
  918.      (*y1>=poly_clip_min_y) && (*y1<=poly_clip_max_y) )
  919.      point_1 = 1;
  920.  
  921.  
  922. if ( (*x2>=poly_clip_min_x) && (*x2<=poly_clip_max_x) &&
  923.      (*y2>=poly_clip_min_y) && (*y2<=poly_clip_max_y) )
  924.      point_2 = 1;
  925.  
  926. // test endpoints
  927.  
  928. if (point_1==1 && point_2==1)
  929.    return(1);
  930.  
  931. // test if line is completely invisible
  932.  
  933. if (point_1==0 && point_2==0)
  934.    {
  935.  
  936.    // must test to see if each endpoint is on the same side of one of
  937.    // the bounding planes created by each clipping region boundary
  938.  
  939.    if ( ((*x1<poly_clip_min_x) && (*x2<poly_clip_min_x)) || // to the left
  940.         ((*x1>poly_clip_max_x) && (*x2>poly_clip_max_x)) || // to the right
  941.  
  942.         ((*y1<poly_clip_min_y) && (*y2<poly_clip_min_y)) || // above
  943.         ((*y1>poly_clip_max_y) && (*y2>poly_clip_max_y)) )  // below
  944.         {
  945.         // no need to draw line
  946.  
  947.         return(0);
  948.  
  949.         } // end if invisible
  950.  
  951.    // if we got here we have the special case where the line cuts into and
  952.    // out of the clipping region
  953.  
  954.    clip_always = 1;
  955.  
  956.    } // end if test for invisibly
  957.  
  958. // take care of case where either endpoint is in clipping region
  959.  
  960. if (( point_1==1) || (point_1==0 && point_2==0) )
  961.    {
  962.  
  963.    // compute deltas
  964.  
  965.    dx = *x2 - *x1;
  966.    dy = *y2 - *y1;
  967.  
  968.    // compute what boundary line need to be clipped against
  969.  
  970.    if (*x2 > poly_clip_max_x)
  971.       {
  972.       // flag right edge
  973.  
  974.       right_edge = 1;
  975.  
  976.       // compute intersection with right edge
  977.  
  978.       if (dx!=0)
  979.          yi = (int)(.5 + (dy/dx) * (poly_clip_max_x - *x1) + *y1);
  980.       else
  981.          yi = -1;  // invalidate intersection
  982.  
  983.       } // end if to right
  984.    else
  985.    if (*x2 < poly_clip_min_x)
  986.       {
  987.       // flag left edge
  988.  
  989.       left_edge = 1;
  990.  
  991.       // compute intersection with left edge
  992.  
  993.       if (dx!=0)
  994.          yi = (int)(.5 + (dy/dx) * (poly_clip_min_x - *x1) + *y1);
  995.       else
  996.          yi = -1;  // invalidate intersection
  997.  
  998.       } // end if to left
  999.  
  1000.    // horizontal intersections
  1001.  
  1002.    if (*y2 > poly_clip_max_y)
  1003.       {
  1004.       // flag bottom edge
  1005.  
  1006.       bottom_edge = 1;
  1007.  
  1008.       // compute intersection with right edge
  1009.  
  1010.       if (dy!=0)
  1011.          xi = (int)(.5 + (dx/dy) * (poly_clip_max_y - *y1) + *x1);
  1012.       else
  1013.          xi = -1;  // invalidate inntersection
  1014.  
  1015.       } // end if bottom
  1016.    else
  1017.    if (*y2 < poly_clip_min_y)
  1018.       {
  1019.       // flag top edge
  1020.  
  1021.       top_edge = 1;
  1022.  
  1023.       // compute intersection with top edge
  1024.  
  1025.       if (dy!=0)
  1026.          xi = (int)(.5 + (dx/dy) * (poly_clip_min_y - *y1) + *x1);
  1027.       else
  1028.          xi = -1;  // invalidate intersection
  1029.  
  1030.       } // end if top
  1031.  
  1032.    // now we know where the line passed thru
  1033.    // compute which edge is the proper intersection
  1034.  
  1035.    if (right_edge==1 && (yi>=poly_clip_min_y && yi<=poly_clip_max_y) )
  1036.       {
  1037.  
  1038.       *x2 = poly_clip_max_x;
  1039.       *y2 = yi;
  1040.  
  1041.       success = 1;
  1042.  
  1043.       } // end if intersected right edge
  1044.    else
  1045.    if (left_edge==1 && (yi>=poly_clip_min_y && yi<=poly_clip_max_y) )
  1046.       {
  1047.  
  1048.       *x2 = poly_clip_min_x;
  1049.       *y2 = yi;
  1050.  
  1051.       success = 1;
  1052.  
  1053.       } // end if intersected left edge
  1054.  
  1055.    if (bottom_edge==1 && (xi>=poly_clip_min_x && xi<=poly_clip_max_x) )
  1056.       {
  1057.  
  1058.       *x2 = xi;
  1059.       *y2 = poly_clip_max_y;
  1060.  
  1061.       success = 1;
  1062.  
  1063.       } // end if intersected bottom edge
  1064.    else
  1065.    if (top_edge==1 && (xi>=poly_clip_min_x && xi<=poly_clip_max_x) )
  1066.       {
  1067.  
  1068.       *x2 = xi;
  1069.       *y2 = poly_clip_min_y;
  1070.  
  1071.       success = 1;
  1072.  
  1073.       } // end if intersected top edge
  1074.  
  1075.    } // end if point_1 is visible
  1076.  
  1077. // reset edge flags
  1078.  
  1079. right_edge = left_edge= top_edge = bottom_edge = 0;
  1080.  
  1081. // test second endpoint
  1082.  
  1083. if ( (point_2==1) || (point_1==0 && point_2==0))
  1084.    {
  1085.    // compute deltas
  1086.  
  1087.    dx = *x1 - *x2;
  1088.    dy = *y1 - *y2;
  1089.  
  1090.    // compute what boundary line need to be clipped against
  1091.  
  1092.    if (*x1 > poly_clip_max_x)
  1093.       {
  1094.       // flag right edge
  1095.  
  1096.       right_edge = 1;
  1097.  
  1098.       // compute intersection with right edge
  1099.  
  1100.       if (dx!=0)
  1101.          yi = (int)(.5 + (dy/dx) * (poly_clip_max_x - *x2) + *y2);
  1102.       else
  1103.          yi = -1;  // invalidate inntersection
  1104.  
  1105.       } // end if to right
  1106.    else
  1107.    if (*x1 < poly_clip_min_x)
  1108.       {
  1109.       // flag left edge
  1110.  
  1111.       left_edge = 1;
  1112.  
  1113.       // compute intersection with left edge
  1114.  
  1115.       if (dx!=0)
  1116.          yi = (int)(.5 + (dy/dx) * (poly_clip_min_x - *x2) + *y2);
  1117.       else
  1118.          yi = -1;  // invalidate intersection
  1119.  
  1120.       } // end if to left
  1121.  
  1122.    // horizontal intersections
  1123.  
  1124.    if (*y1 > poly_clip_max_y)
  1125.       {
  1126.       // flag bottom edge
  1127.  
  1128.       bottom_edge = 1;
  1129.  
  1130.       // compute intersection with right edge
  1131.  
  1132.       if (dy!=0)
  1133.          xi = (int)(.5 + (dx/dy) * (poly_clip_max_y - *y2) + *x2);
  1134.       else
  1135.          xi = -1;  // invalidate inntersection
  1136.  
  1137.       } // end if bottom
  1138.    else
  1139.    if (*y1 < poly_clip_min_y)
  1140.       {
  1141.       // flag top edge
  1142.  
  1143.       top_edge = 1;
  1144.  
  1145.       // compute intersection with top edge
  1146.  
  1147.       if (dy!=0)
  1148.          xi = (int)(.5 + (dx/dy) * (poly_clip_min_y - *y2) + *x2);
  1149.       else
  1150.          xi = -1;  // invalidate inntersection
  1151.  
  1152.       } // end if top
  1153.  
  1154.    // now we know where the line passed thru
  1155.    // compute which edge is the proper intersection
  1156.  
  1157.    if (right_edge==1 && (yi>=poly_clip_min_y && yi<=poly_clip_max_y) )
  1158.       {
  1159.  
  1160.       *x1 = poly_clip_max_x;
  1161.       *y1 = yi;
  1162.  
  1163.       success = 1;
  1164.  
  1165.       } // end if intersected right edge
  1166.    else
  1167.    if (left_edge==1 && (yi>=poly_clip_min_y && yi<=poly_clip_max_y) )
  1168.       {
  1169.  
  1170.       *x1 = poly_clip_min_x;
  1171.       *y1 = yi;
  1172.  
  1173.       success = 1;
  1174.  
  1175.       } // end if intersected left edge
  1176.  
  1177.    if (bottom_edge==1 && (xi>=poly_clip_min_x && xi<=poly_clip_max_x) )
  1178.       {
  1179.  
  1180.       *x1 = xi;
  1181.       *y1 = poly_clip_max_y;
  1182.  
  1183.       success = 1;
  1184.  
  1185.       } // end if intersected bottom edge
  1186.    else
  1187.    if (top_edge==1 && (xi>=poly_clip_min_x && xi<=poly_clip_max_x) )
  1188.       {
  1189.  
  1190.       *x1 = xi;
  1191.       *y1 = poly_clip_min_y;
  1192.  
  1193.       success = 1;
  1194.  
  1195.       } // end if intersected top edge
  1196.  
  1197.  
  1198.    } // end if point_2 is visible
  1199.  
  1200. return(success);
  1201.  
  1202. } // end Clip_Line
  1203.  
  1204. //////////////////////////////////////////////////////////////////////////////
  1205.  
  1206. void Build_Look_Up_Tables(void)
  1207. {
  1208. // this function builds all the look up tables for the engine
  1209.  
  1210. int angle;
  1211.  
  1212. float rad;
  1213.  
  1214.  // generate sin/cos look up tables
  1215.  
  1216. for (angle=0; angle<=360; angle++)
  1217.     {
  1218.  
  1219.     rad = (float) (3.14159*(float)angle/(float)180);
  1220.  
  1221.     cos_look[angle] = (float)cos(rad);
  1222.     sin_look[angle] = (float)sin(rad);
  1223.  
  1224.     } // end for angle
  1225.  
  1226. } // end Build_Look_Up_Tables
  1227.  
  1228. //////////////////////////////////////////////////////////////////////////////
  1229.  
  1230. float Dot_Product_3D(vector_3d_ptr u,vector_3d_ptr v)
  1231. {
  1232. // this function computes the dot product of two vectors
  1233.  
  1234. return( (u->x * v->x) + (u->y * v->y) + (u->z * v->z));
  1235.  
  1236. } // end Dot_Product
  1237.  
  1238. //////////////////////////////////////////////////////////////////////////////
  1239.  
  1240. void Make_Vector_3D(point_3d_ptr init,
  1241.                     point_3d_ptr term,
  1242.                     vector_3d_ptr result)
  1243. {
  1244. // this function creates a vector from two points in 3D space
  1245.  
  1246. result->x = term->x - init->x;
  1247. result->y = term->y - init->y;
  1248. result->z = term->z - init->z;
  1249.  
  1250. } // end Make_Vector
  1251.  
  1252. //////////////////////////////////////////////////////////////////////////////
  1253.  
  1254. void Cross_Product_3D(vector_3d_ptr u,
  1255.                       vector_3d_ptr v,
  1256.                       vector_3d_ptr normal)
  1257. {
  1258. // this function computes the cross product between two vectors
  1259.  
  1260. normal->x =  (u->y*v->z - u->z*v->y);
  1261. normal->y = -(u->x*v->z - u->z*v->x);
  1262. normal->z =  (u->x*v->y - u->y*v->x);
  1263.  
  1264. } // end Cross_Product_3D
  1265.  
  1266. ///////////////////////////////////////////////////////////////////////////////
  1267.  
  1268. float Vector_Mag_3D(vector_3d_ptr v)
  1269. {
  1270. // computes the magnitude of a vector
  1271.  
  1272. return((float)sqrt(v->x*v->x + v->y*v->y + v->z*v->z));
  1273.  
  1274. } // end Vector_Mag_3D
  1275.  
  1276. ///////////////////////////////////////////////////////////////////////////////
  1277.  
  1278. void Mat_Print_4x4(matrix_4x4 a)
  1279. {
  1280. // this function prints out a 4x4 matrix
  1281.  
  1282. int row,      // looping variables
  1283.     column;
  1284.  
  1285. for (row=0; row<4; row++)
  1286.     {
  1287.     printf("\n");
  1288.  
  1289.     for (column=0; column<4; column++)
  1290.         printf("%f ",a[row][column]);
  1291.  
  1292.     } // end for row
  1293.  
  1294. printf("\n");
  1295.  
  1296. } // end Mat_Print_4x4
  1297.  
  1298. //////////////////////////////////////////////////////////////////////////////
  1299.  
  1300. void Mat_Print_1x4(matrix_1x4 a)
  1301. {
  1302. // this function prints out a 1x4 matrix
  1303.  
  1304. int column;     // looping variable
  1305.  
  1306. printf("\n");
  1307.  
  1308. for (column=0; column<4; column++)
  1309.     printf("%f ",a[column]);
  1310.  
  1311. printf("\n");
  1312.  
  1313. } // end Mat_Print_1x4
  1314.  
  1315. //////////////////////////////////////////////////////////////////////////////
  1316.  
  1317. void Mat_Mul_4x4_4x4(matrix_4x4 a,
  1318.                      matrix_4x4 b,
  1319.                      matrix_4x4 result)
  1320. {
  1321. // this function multiplies a 4x4 by a 4x4 and stores the result in a 4x4
  1322.  
  1323. int index_i,  // row and column looping vars
  1324.     index_j,
  1325.     index_k;
  1326.  
  1327. float sum;    // temp used to hold sum of products
  1328.  
  1329. // loop thru rows of a
  1330.  
  1331. for (index_i=0; index_i<4; index_i++)
  1332.     {
  1333.     // loop thru columns of b
  1334.  
  1335.     for (index_j=0; index_j<4; index_j++)
  1336.         {
  1337.  
  1338.         // multiply ith row of a by jth column of b and store the sum
  1339.         // of products in the position i,j of result
  1340.  
  1341.         sum=0;
  1342.  
  1343.         for (index_k=0; index_k<4; index_k++)
  1344.             sum+=a[index_i][index_k]*b[index_k][index_j];
  1345.  
  1346.         // store result
  1347.  
  1348.         result[index_i][index_j] = sum;
  1349.  
  1350.         } // end for index_j
  1351.  
  1352.     } // end for index_i
  1353.  
  1354. } // end Mat_Mul_4x4_4x4
  1355.  
  1356. //////////////////////////////////////////////////////////////////////////////
  1357.  
  1358. void Mat_Mul_1x4_4x4(matrix_1x4 a,
  1359.                      matrix_4x4 b,
  1360.                      matrix_1x4 result)
  1361. {
  1362. // this function multiplies a 1x4 by a 4x4 and stores the result in a 1x4
  1363.  
  1364. int index_j,    // column index
  1365.     index_k;    // row index
  1366.  
  1367. float sum;    // temp used to hold sum of products
  1368.  
  1369. // loop thru columns of b
  1370.  
  1371. for (index_j=0; index_j<4; index_j++)
  1372.     {
  1373.  
  1374.     // multiply ith row of a by jth column of b and store the sum
  1375.     // of products in the position i,j of result
  1376.  
  1377.     sum=0;
  1378.  
  1379.     for (index_k=0; index_k<4; index_k++)
  1380.         sum+=a[index_k]*b[index_k][index_j];
  1381.  
  1382.     // store result
  1383.  
  1384.     result[index_j] = sum;
  1385.  
  1386.     } // end for index_j
  1387.  
  1388. } // end Mat_Mul_1x4_4x4
  1389.  
  1390. //////////////////////////////////////////////////////////////////////////////
  1391.  
  1392. void Mat_Identity_4x4(matrix_4x4 a)
  1393. {
  1394. // this function creates a 4x4 identity matrix
  1395.  
  1396. a[0][1] = a[0][2] = a[0][3] = 0;
  1397. a[1][0] = a[1][2] = a[1][3] = 0;
  1398. a[2][0] = a[2][1] = a[2][3] = 0;
  1399. a[3][0] = a[3][1] = a[3][2] = 0;
  1400.  
  1401. // set main diagonal to 1's
  1402.  
  1403. a[0][0] = a[1][1] = a[2][2] = a[3][3] = 1;
  1404.  
  1405. } // end Mat_Identity_4x4
  1406.  
  1407. /////////////////////////////////////////////////////////////////////////////
  1408.  
  1409. void Mat_Zero_4x4(matrix_4x4 a)
  1410. {
  1411. // this function zero's out a 4x4 matrix
  1412.  
  1413. a[0][0] = a[0][1] = a[0][2] = a[0][3] = 0;
  1414. a[1][0] = a[1][1] = a[1][2] = a[1][3] = 0;
  1415. a[2][0] = a[2][1] = a[2][2] = a[2][3] = 0;
  1416. a[3][0] = a[3][1] = a[3][2] = a[3][3] = 0;
  1417.  
  1418. } // end Mat_Zero_4x4
  1419.  
  1420. /////////////////////////////////////////////////////////////////////////////
  1421.  
  1422. void Mat_Copy_4x4(matrix_4x4 source, matrix_4x4 destination)
  1423. {
  1424. // this function copies one 4x4 matrix to another
  1425.  
  1426. int index_i,
  1427.     index_j; // looping vars
  1428.  
  1429. // copy the matrix row by row
  1430.  
  1431. for (index_i=0; index_i<4; index_i++)
  1432.     for (index_j=0; index_j<4; index_j++)
  1433.         destination[index_i][index_j] = source[index_i][index_j];
  1434.  
  1435. } // end Mat_Copy_4x4
  1436.  
  1437. /////////////////////////////////////////////////////////////////////////////
  1438.  
  1439. void Local_To_World_Object(object_ptr the_object)
  1440. {
  1441. // this function converts an objects local coordinates to world coordinates
  1442. // by translating each point in the object by the objects current position
  1443.  
  1444. int index;  // looping variable
  1445.  
  1446. // move object from local position to world position
  1447.  
  1448. for (index=0; index<the_object->num_vertices; index++)
  1449.     {
  1450.  
  1451.     the_object->vertices_world[index].x = the_object->vertices_local[index].x +
  1452.                                           the_object->world_pos.x;
  1453.  
  1454.     the_object->vertices_world[index].y = the_object->vertices_local[index].y +
  1455.                                           the_object->world_pos.y;
  1456.  
  1457.     the_object->vertices_world[index].z = the_object->vertices_local[index].z +
  1458.                                           the_object->world_pos.z;
  1459.  
  1460.     } // end for index
  1461.  
  1462. // reset visibility flags for all polys
  1463.  
  1464. for (index=0; index<the_object->num_polys; index++)
  1465.     {
  1466.     the_object->polys[index].visible = 1;
  1467.     the_object->polys[index].clipped = 0;
  1468.     } // end for
  1469.  
  1470. } // end Local_To_World_Object
  1471.  
  1472. /////////////////////////////////////////////////////////////////////////////
  1473.  
  1474. void Create_World_To_Camera(void)
  1475. {
  1476. // this function creates the global inverse transformation matrix
  1477. // used to transform world coordinate to camera coordinates
  1478.  
  1479.  
  1480. matrix_4x4 translate,   // the translation matrix
  1481.  
  1482.            rotate_x,    // the x,y and z rotation matrices
  1483.            rotate_y,
  1484.            rotate_z,
  1485.            result_1,
  1486.            result_2;
  1487.  
  1488. // create identity matrices
  1489.  
  1490. Mat_Identity_4x4(translate);
  1491. Mat_Identity_4x4(rotate_x);
  1492. Mat_Identity_4x4(rotate_y);
  1493. Mat_Identity_4x4(rotate_z);
  1494.  
  1495. // make a translation matrix based on the inverse of the viewpoint
  1496.  
  1497. translate[3][0] = -view_point.x;
  1498. translate[3][1] = -view_point.y;
  1499. translate[3][2] = -view_point.z;
  1500.  
  1501. // make rotation matrices based on the inverse of the view angles
  1502. // note that since we use lookup tables for sin and cosine, it's hard to
  1503. // use negative angles, so we will use that fact that cos(-x) = cos(x)
  1504. // and sin(-x) = -sin(x) to implement the inverse instead of using
  1505. // an offset in the lookup table or using the technique that
  1506. // a rotation of -x = 360-x. note the original rotation formulas will be
  1507. // kept in parentheses, so you can better see the inversion
  1508.  
  1509. // x matrix
  1510.  
  1511. rotate_x[1][1] =  ( cos_look[view_angle.ang_x]);
  1512. rotate_x[1][2] = -( sin_look[view_angle.ang_x]);
  1513. rotate_x[2][1] = -(-sin_look[view_angle.ang_x]);
  1514. rotate_x[2][2] =  ( cos_look[view_angle.ang_x]);
  1515.  
  1516. // y matrix
  1517.  
  1518. rotate_y[0][0] =  ( cos_look[view_angle.ang_y]);
  1519. rotate_y[0][2] = -(-sin_look[view_angle.ang_y]);
  1520. rotate_y[2][0] = -( sin_look[view_angle.ang_y]);
  1521. rotate_y[2][2] =  ( cos_look[view_angle.ang_y]);
  1522.  
  1523. // z matrix
  1524.  
  1525. rotate_z[0][0] =  ( cos_look[view_angle.ang_z]);
  1526. rotate_z[0][1] = -( sin_look[view_angle.ang_z]);
  1527. rotate_z[1][0] = -(-sin_look[view_angle.ang_z]);
  1528. rotate_z[1][1] =  ( cos_look[view_angle.ang_z]);
  1529.  
  1530.  
  1531. // multiply all the matrices together to obtain a final world to camera
  1532. // viewing transformation matrix i.e.
  1533. // translation * rotate_x * rotate_y * rotate_z
  1534.  
  1535. Mat_Mul_4x4_4x4(translate,rotate_x,result_1);
  1536. Mat_Mul_4x4_4x4(result_1,rotate_y,result_2);
  1537. Mat_Mul_4x4_4x4(result_2,rotate_z,global_view);
  1538.  
  1539. } // end Create_World_To_Camera
  1540.  
  1541. /////////////////////////////////////////////////////////////////////////////
  1542.  
  1543. void World_To_Camera_Object(object_ptr the_object)
  1544. {
  1545. // this function converts an objects world coordinates to camera coordinates
  1546. // by multiplying each point of the object by the inverse viewing transformation
  1547. // matrix which is generated by concatenating the inverse of the view position
  1548. // and the view angles the result of which is in global_view
  1549.  
  1550. int index; // looping variable
  1551.  
  1552. // iterate thru all vertices of object and transform them into camera coordinates
  1553.  
  1554. for (index=0; index<=the_object->num_vertices; index++)
  1555.     {
  1556.  
  1557.     // multiply the point by the viewing transformation matrix
  1558.  
  1559.     // x component
  1560.  
  1561.     the_object->vertices_camera[index].x =
  1562.  
  1563.                   the_object->vertices_world[index].x * global_view[0][0] +
  1564.                   the_object->vertices_world[index].y * global_view[1][0] +
  1565.                   the_object->vertices_world[index].z * global_view[2][0] +
  1566.                                                         global_view[3][0];
  1567.  
  1568.     // y component
  1569.  
  1570.     the_object->vertices_camera[index].y =
  1571.  
  1572.                   the_object->vertices_world[index].x * global_view[0][1] +
  1573.                   the_object->vertices_world[index].y * global_view[1][1] +
  1574.                   the_object->vertices_world[index].z * global_view[2][1] +
  1575.                                                         global_view[3][1];
  1576.     // z component
  1577.  
  1578.     the_object->vertices_camera[index].z =
  1579.  
  1580.                   the_object->vertices_world[index].x * global_view[0][2] +
  1581.                   the_object->vertices_world[index].y * global_view[1][2] +
  1582.                   the_object->vertices_world[index].z * global_view[2][2] +
  1583.                                                         global_view[3][2];
  1584.  
  1585.     } // end for index
  1586.  
  1587. } // end World_To_Camera_Object
  1588.  
  1589. ////////////////////////////////////////////////////////////////////////////
  1590.  
  1591. void Rotate_Object(object_ptr the_object,int angle_x,int angle_y,int angle_z)
  1592. {
  1593. // this function rotates an object relative to it's own local coordinate system
  1594. // and allows simultaneous rotations
  1595.  
  1596. int index,       //  looping variable
  1597.     product=0;   // used to determine which matrices need multiplying
  1598.  
  1599. matrix_4x4 rotate_x,    // the x,y and z rotation matrices
  1600.            rotate_y,
  1601.            rotate_z,
  1602.            rotate,      // the final rotation matrix
  1603.            temp;        // temporary working matrix
  1604.  
  1605. float temp_x, // used to hold intermediate results during rotation
  1606.       temp_y,
  1607.       temp_z;
  1608.  
  1609. // test if we need to rotate at all
  1610.  
  1611. if (angle_x==0 && angle_y==0 && angle_z==0)
  1612.    return;
  1613.  
  1614. // create identity matrice
  1615.  
  1616. Mat_Identity_4x4(rotate);
  1617.  
  1618. // create X rotation matrix
  1619.  
  1620. if (angle_x)
  1621.    {
  1622.    // x matrix
  1623.  
  1624.    Mat_Identity_4x4(rotate_x);
  1625.  
  1626.    rotate_x[1][1] = ( cos_look[angle_x]);
  1627.    rotate_x[1][2] = ( sin_look[angle_x]);
  1628.    rotate_x[2][1] = (-sin_look[angle_x]);
  1629.    rotate_x[2][2] = ( cos_look[angle_x]);
  1630.  
  1631.    } // end if any X rotation
  1632.  
  1633. // create Y rotation matrix
  1634.  
  1635. if (angle_y)
  1636.    {
  1637.  
  1638.    Mat_Identity_4x4(rotate_y);
  1639.  
  1640.    rotate_y[0][0] = ( cos_look[angle_y]);
  1641.    rotate_y[0][2] = (-sin_look[angle_y]);
  1642.    rotate_y[2][0] = ( sin_look[angle_y]);
  1643.    rotate_y[2][2] = ( cos_look[angle_y]);
  1644.  
  1645.    } // end if any Y rotation
  1646.  
  1647. // create Z rotation matrix
  1648.  
  1649. if (angle_z)
  1650.    {
  1651.  
  1652.    Mat_Identity_4x4(rotate_z);
  1653.  
  1654.    rotate_z[0][0] = ( cos_look[angle_z]);
  1655.    rotate_z[0][1] = ( sin_look[angle_z]);
  1656.    rotate_z[1][0] = (-sin_look[angle_z]);
  1657.    rotate_z[1][1] = ( cos_look[angle_z]);
  1658.  
  1659.    } // end if any Z rotation
  1660.  
  1661. // compute final rotation matrix, determine the proper product of matrices
  1662. // use a switch statement along with a bit pattern to determine which
  1663. // matrices need multiplying, this is worth the time it would take
  1664. // to concatenate matrices together that don't have any effect
  1665.  
  1666. // if bit 2 of product is 1 then there is an x rotation
  1667. // if bit 1 of product is 1 then there is an y rotation
  1668. // if bit 0 of product is 1 then there is an z rotation
  1669.  
  1670. if (angle_x)
  1671.    product|=4;
  1672.  
  1673. if (angle_y)
  1674.    product|=2;
  1675.  
  1676. if (angle_z)
  1677.    product|=1;
  1678.  
  1679. // compute proper final rotation matrix
  1680.  
  1681. switch(product)
  1682.       {
  1683.       case 0: // do nothing there isn't any rotation
  1684.            {
  1685.            // this shouldn't happen
  1686.            } break;
  1687.  
  1688.       case 1: // final matrix = z
  1689.            {
  1690.  
  1691.            Mat_Copy_4x4(rotate_z,rotate);
  1692.  
  1693.            } break;
  1694.  
  1695.       case 2: // final matrix = y
  1696.            {
  1697.  
  1698.            Mat_Copy_4x4(rotate_y,rotate);
  1699.  
  1700.            } break;
  1701.  
  1702.       case 3: // final matrix = y*z
  1703.            {
  1704.  
  1705.            Mat_Mul_4x4_4x4(rotate_y,rotate_z,rotate);
  1706.  
  1707.            } break;
  1708.  
  1709.       case 4: // final matrix = x
  1710.            {
  1711.  
  1712.            Mat_Copy_4x4(rotate_x,rotate);
  1713.  
  1714.            } break;
  1715.  
  1716.       case 5: // final matrix = x*z
  1717.            {
  1718.  
  1719.            Mat_Mul_4x4_4x4(rotate_x,rotate_z,rotate);
  1720.  
  1721.            } break;
  1722.  
  1723.       case 6: // final matrix = x*y
  1724.            {
  1725.  
  1726.            Mat_Mul_4x4_4x4(rotate_x,rotate_y,rotate);
  1727.  
  1728.            } break;
  1729.  
  1730.       case 7: // final matrix = x*y*z
  1731.            {
  1732.  
  1733.            Mat_Mul_4x4_4x4(rotate_x,rotate_y,temp);
  1734.            Mat_Mul_4x4_4x4(temp,rotate_z,rotate);
  1735.  
  1736.            } break;
  1737.  
  1738.       default:break;
  1739.  
  1740.       } // end switch
  1741.  
  1742. // now multiply each point in object by transformation matrix
  1743.  
  1744. for (index=0; index<the_object->num_vertices; index++)
  1745.     {
  1746.  
  1747.     // x component
  1748.  
  1749.     temp_x = the_object->vertices_local[index].x * rotate[0][0] +
  1750.              the_object->vertices_local[index].y * rotate[1][0] +
  1751.              the_object->vertices_local[index].z * rotate[2][0];
  1752.  
  1753.     // y component
  1754.  
  1755.     temp_y = the_object->vertices_local[index].x * rotate[0][1] +
  1756.              the_object->vertices_local[index].y * rotate[1][1] +
  1757.              the_object->vertices_local[index].z * rotate[2][1];
  1758.  
  1759.     // z component
  1760.  
  1761.     temp_z = the_object->vertices_local[index].x * rotate[0][2] +
  1762.              the_object->vertices_local[index].y * rotate[1][2] +
  1763.              the_object->vertices_local[index].z * rotate[2][2];
  1764.  
  1765.     // store rotated point back into local array
  1766.  
  1767.     the_object->vertices_local[index].x = temp_x;
  1768.     the_object->vertices_local[index].y = temp_y;
  1769.     the_object->vertices_local[index].z = temp_z;
  1770.  
  1771.     } // end for index
  1772.  
  1773. } // end Rotate_Object
  1774.  
  1775. ////////////////////////////////////////////////////////////////////////////
  1776.  
  1777. void Position_Object(object_ptr the_object,int x,int y,int z)
  1778. {
  1779. // this function positions an object in the world
  1780.  
  1781. the_object->world_pos.x = x;
  1782. the_object->world_pos.y = y;
  1783. the_object->world_pos.z = z;
  1784.  
  1785. } // end Position_Object
  1786.  
  1787. ////////////////////////////////////////////////////////////////////////////
  1788.  
  1789. void Translate_Object(object_ptr the_object,int x_trans,int y_trans,int z_trans)
  1790. {
  1791. // this function translates an object relative to it's own local
  1792. // coordinate system
  1793.  
  1794. the_object->world_pos.x+=x_trans;
  1795. the_object->world_pos.y+=y_trans;
  1796. the_object->world_pos.z+=z_trans;
  1797.  
  1798. } // end Translate_Object
  1799.  
  1800. /////////////////////////////////////////////////////////////////////////////
  1801.  
  1802. void Scale_Object(object_ptr the_object,float scale_factor)
  1803. {
  1804. // this function scales an object relative to it's own local coordinate system
  1805. // equally in x,y and z
  1806.  
  1807. int curr_poly,    // the current polygon being processed
  1808.     curr_vertex;  // the current vertex being processed
  1809.  
  1810. float scale_2;    // holds the sqaure of the scaling factor, needed to
  1811.                   // resize the surface normal for lighting calculations
  1812.  
  1813. // multiply each vertex in the object definition by the scaling factor
  1814.  
  1815. for (curr_vertex=0; curr_vertex<the_object->num_vertices; curr_vertex++)
  1816.     {
  1817.  
  1818.     the_object->vertices_local[curr_vertex].x*=scale_factor;
  1819.     the_object->vertices_local[curr_vertex].y*=scale_factor;
  1820.     the_object->vertices_local[curr_vertex].z*=scale_factor;
  1821.  
  1822.     } // end for curr_vertex
  1823.  
  1824. // compute scaling factor squared
  1825.  
  1826. scale_2 = scale_factor*scale_factor;
  1827.  
  1828. // now scale all pre-computed normals
  1829.  
  1830. for (curr_poly=0; curr_poly<the_object->num_polys; curr_poly++)
  1831.     the_object->polys[curr_poly].normal_length*=scale_2;
  1832.  
  1833. // finally scale the radius up
  1834.  
  1835. the_object->radius*=scale_factor;
  1836.  
  1837. } // end Scale_Object
  1838.  
  1839. /////////////////////////////////////////////////////////////////////////////
  1840.  
  1841. int Objects_Collide(object_ptr object_1, object_ptr object_2)
  1842. {
  1843. // this function tests if the bounding spheres of two objects overlaps
  1844. // if a more accurate test is needed then polygons should be tested against
  1845. // polygons. note the function uses the fact that if x > y then x^2 > y^2
  1846. // to avoid using square roots. Finally, the function might be altered
  1847. // so that the bounding spheres are shrank to make sure that the collision
  1848. // is "solid"/ finally, soft and hard collisions are both detected
  1849.  
  1850. float dx,dy,dz,           // deltas in x,y and z
  1851.       radius_1,radius_2,  // radi of each object
  1852.       distance;           // distance between object centers
  1853.  
  1854. // compute delta's
  1855.  
  1856. dx = (object_1->world_pos.x - object_2->world_pos.x);
  1857. dy = (object_1->world_pos.y - object_2->world_pos.y);
  1858. dz = (object_1->world_pos.z - object_2->world_pos.z);
  1859.  
  1860. // compute length
  1861.  
  1862. distance = dx*dx + dy*dy + dz*dx;
  1863.  
  1864. // compute radius of each object squared
  1865.  
  1866. radius_1 = object_1->radius*object_1->radius;
  1867. radius_2 = object_2->radius*object_2->radius;
  1868.  
  1869. // test if distance is smaller than of radi
  1870.  
  1871. if (distance<radius_1 || distance <radius_2)
  1872.    return(HARD_COLLISION); // hard collision
  1873. else
  1874. if (distance < radius_1+radius_2)
  1875.    return(SOFT_COLLISION); // soft collision
  1876. else
  1877.    return(NO_COLLISION);
  1878.  
  1879. } // end Objects_Collide
  1880.  
  1881. /////////////////////////////////////////////////////////////////////////////
  1882.  
  1883. char *PLG_Get_Line(char *string, int max_length, FILE *fp)
  1884. {
  1885. // this function gets a line from a PLG file and strips comments
  1886. // just pretend it's a black box!
  1887.  
  1888. char buffer[80]; // temporary string storage
  1889.  
  1890. int length,      // length of line read
  1891.     index=0,     // looping variables
  1892.     index_2=0,
  1893.     parsed=0;    // has the current input line been parsed
  1894.  
  1895. // get the next line of input, make sure there is something on the line
  1896.  
  1897. while(1)
  1898.      {
  1899.      // get the line
  1900.  
  1901.      if (!fgets(buffer,max_length,fp))
  1902.         return(NULL);
  1903.  
  1904.      // get length of line
  1905.  
  1906.      length = strlen(buffer);
  1907.  
  1908.      // kill the carriage return
  1909.  
  1910.      buffer[length-1] = 0;
  1911.  
  1912.      // reset index
  1913.  
  1914.      index = 0;
  1915.  
  1916.      // eat leading white space
  1917.  
  1918.      while(buffer[index]==' ')
  1919.            index++;
  1920.  
  1921.      // read line into buffer, if "#" arrives in data stream then disregard
  1922.      // rest of line
  1923.  
  1924.      parsed=0;
  1925.      index_2=0;
  1926.  
  1927.      while(!parsed)
  1928.           {
  1929.  
  1930.           if (buffer[index]!='#' && buffer[index]!=';')
  1931.              {
  1932.              // insert character into output string
  1933.  
  1934.              string[index_2] = buffer[index];
  1935.  
  1936.              // test if this is a null terminator
  1937.  
  1938.              if (string[index_2]==0)
  1939.                 parsed=1;
  1940.  
  1941.              // move to next character
  1942.  
  1943.              index++;
  1944.              index_2++;
  1945.  
  1946.              } // end if not comment
  1947.           else
  1948.              {
  1949.              // insert a null termination since this is the end of the
  1950.              // string for all intense purposes
  1951.  
  1952.              string[index_2] = 0;
  1953.  
  1954.              parsed=1;
  1955.  
  1956.              } // end else
  1957.  
  1958.           } // end while parsing string
  1959.  
  1960.           // make sure we got a string and not a blank line
  1961.  
  1962.           if (strlen(string))
  1963.              return(string);
  1964.  
  1965.      } // end while
  1966.  
  1967. } // end PLG_Get_Line
  1968.  
  1969. /////////////////////////////////////////////////////////////////////////////
  1970.  
  1971. int PLG_Load_Object(object_ptr the_object,char *filename,float scale)
  1972. {
  1973. // this function loads an object off disk and allows it to be scaled
  1974.  
  1975. FILE *fp; // disk file
  1976.  
  1977. static int id_number = 0; // used to set object id's
  1978.  
  1979.  
  1980. char buffer[80],         // holds input string
  1981.      object_name[32],    // name of 3-D object
  1982.      *token;             // current parsing token
  1983.  
  1984.  
  1985. unsigned int total_vertices,    // total vertices in object
  1986.              total_polys,       // total polygons per object
  1987.              num_vertices,      // number of vertices on a polygon
  1988.              color_des,         // the color descriptor of a polygon
  1989.              logical_color,     // the final color of polygon
  1990.              shading,           // the type of shading used on polygon
  1991.              index,             // looping variables
  1992.              index_2,
  1993.              vertex_num,        // vertex numbers
  1994.              vertex_0,
  1995.              vertex_1,
  1996.              vertex_2;
  1997.  
  1998. float x,y,z;                    // a single vertex
  1999.  
  2000. vector_3d u,v,normal;           // working vectors
  2001.  
  2002. // open the disk file
  2003.  
  2004. if ((fp=fopen(filename,"r"))==NULL)
  2005.    {
  2006.    printf("\nCouldn't open file %s",filename);
  2007.    return(0);
  2008.  
  2009.    } // end if
  2010.  
  2011. // first we are looking for the header line that has the object name and
  2012. // the number of vertices and polygons
  2013.  
  2014. if (!PLG_Get_Line(buffer, 80,fp))
  2015.    {
  2016.    printf("\nError with PLG file %s",filename);
  2017.    fclose(fp);
  2018.    return(0);
  2019.    } // end if error
  2020.  
  2021. // extract object name and number of vertices and polygons
  2022.  
  2023. sscanf(buffer,"%s %d %d",object_name, &total_vertices, &total_polys);
  2024.  
  2025. // set proper fields in object
  2026.  
  2027. the_object->num_vertices = total_vertices;
  2028. the_object->num_polys    = total_polys;
  2029. the_object->state        = 1;
  2030.  
  2031. the_object->world_pos.x  = 0;
  2032. the_object->world_pos.y  = 0;
  2033. the_object->world_pos.z  = 0;
  2034.  
  2035. // set id number, maybe later also add the name of object in the
  2036. // structure???
  2037.  
  2038. the_object->id = id_number++;
  2039.  
  2040. // based on number of vertices, read vertex list into object
  2041.  
  2042. for (index=0; index<total_vertices; index++)
  2043.     {
  2044.  
  2045.     // read in vertex
  2046.  
  2047.     if (!PLG_Get_Line(buffer, 80,fp))
  2048.        {
  2049.        printf("\nError with PLG file %s",filename);
  2050.        fclose(fp);
  2051.        return(0);
  2052.        } // end if error
  2053.  
  2054.     sscanf(buffer,"%f %f %f",&x,&y,&z);
  2055.  
  2056.     // insert vertex into object
  2057.  
  2058.     the_object->vertices_local[index].x = x*scale;
  2059.     the_object->vertices_local[index].y = y*scale;
  2060.     the_object->vertices_local[index].z = z*scale;
  2061.  
  2062.     } // end for index
  2063.  
  2064. // now read in polygon list
  2065.  
  2066. for (index=0; index<total_polys; index++)
  2067.     {
  2068.     // read in color and number of vertices for next polygon
  2069.  
  2070.     if (!PLG_Get_Line(buffer, 80,fp))
  2071.        {
  2072.        printf("\nError with PLG file %s",filename);
  2073.        fclose(fp);
  2074.        return(0);
  2075.        } // end if error
  2076.  
  2077.     // intialize token getter and get first token which is color descriptor
  2078.  
  2079.     if (!(token = strtok(buffer," ")) )
  2080.        {
  2081.        printf("\nError with PLG file %s",filename);
  2082.        fclose(fp);
  2083.        return(0);
  2084.        } // end if problem
  2085.  
  2086.     // test if number is hexadecimal
  2087.  
  2088.     if (token[0]=='0' && (token[1]=='x' || token[1]=='X'))
  2089.        {
  2090.  
  2091.        sscanf(&token[2],"%x",&color_des);
  2092.  
  2093.        } // end if hex color specifier
  2094.     else
  2095.        {
  2096.  
  2097.        color_des = atoi(token);
  2098.  
  2099.        } // end if decimal
  2100.  
  2101.     // extract base color and type of shading
  2102.  
  2103.     logical_color = color_des & 0x00ff;
  2104.     shading       = color_des >> 12;
  2105.  
  2106.     // read number of vertices in polygon
  2107.  
  2108.     if (!(token = strtok(NULL," ")))
  2109.        {
  2110.        printf("\nError with PLG file %s",filename);
  2111.        fclose(fp);
  2112.        return(0);
  2113.        } // end if problem
  2114.  
  2115.     if ((num_vertices = atoi(token))<=0)
  2116.        {
  2117.        printf("\nError with PLG file (number of vertices) %s",filename);
  2118.        fclose(fp);
  2119.        return(0);
  2120.        } // end if no vertices or error
  2121.  
  2122.     // set fields in polygon structure
  2123.  
  2124.     the_object->polys[index].num_points = num_vertices;
  2125.     the_object->polys[index].color      = logical_color;
  2126.     the_object->polys[index].shading    = shading;
  2127.     the_object->polys[index].two_sided  = 0;
  2128.     the_object->polys[index].visible    = 1;
  2129.     the_object->polys[index].clipped    = 0;
  2130.     the_object->polys[index].active     = 1;
  2131.  
  2132.     // now read in polygon vertice list
  2133.  
  2134.     for (index_2=0; index_2<num_vertices; index_2++)
  2135.         {
  2136.  
  2137.         // read in next vertex number
  2138.  
  2139.         if (!(token = strtok(NULL," ")) )
  2140.            {
  2141.            printf("\nError with PLG file %s",filename);
  2142.            fclose(fp);
  2143.            return(0);
  2144.            } // end if problem
  2145.  
  2146.         vertex_num = atoi(token);
  2147.  
  2148.         // insert vertex number into polygon
  2149.  
  2150.         the_object->polys[index].vertex_list[index_2] = vertex_num;
  2151.  
  2152.         } // end for index_2
  2153.  
  2154.     // compute length of the two co-planer edges of the polygon, since they
  2155.     // will be used in the computation of the dot-product later
  2156.  
  2157.     vertex_0 = the_object->polys[index].vertex_list[0];
  2158.     vertex_1 = the_object->polys[index].vertex_list[1];
  2159.     vertex_2 = the_object->polys[index].vertex_list[2];
  2160.  
  2161.     // the vector u = vo->v1
  2162.  
  2163.     Make_Vector_3D((point_3d_ptr)&the_object->vertices_local[vertex_0],
  2164.                    (point_3d_ptr)&the_object->vertices_local[vertex_1],
  2165.                    (vector_3d_ptr)&u);
  2166.  
  2167.     // the vector v = vo->v2
  2168.  
  2169.     Make_Vector_3D((point_3d_ptr)&the_object->vertices_local[vertex_0],
  2170.                    (point_3d_ptr)&the_object->vertices_local[vertex_2],
  2171.                    (vector_3d_ptr)&v);
  2172.  
  2173.     Cross_Product_3D((vector_3d_ptr)&v,
  2174.                      (vector_3d_ptr)&u,
  2175.                      (vector_3d_ptr)&normal);
  2176.  
  2177.  
  2178.     // compute magnitude of normal and store in polygon structure
  2179.  
  2180.     the_object->polys[index].normal_length = Vector_Mag_3D((vector_3d_ptr)&normal);
  2181.  
  2182.     } // end for index
  2183.  
  2184. // close the file
  2185.  
  2186. fclose(fp);
  2187.  
  2188. // compute object radius
  2189.  
  2190. Compute_Object_Radius(the_object);
  2191.  
  2192. // return success
  2193.  
  2194. return(1);
  2195.  
  2196. } // end PLG_Load_Object
  2197.  
  2198. ///////////////////////////////////////////////////////////////////////////////
  2199.  
  2200. float Compute_Object_Radius(object_ptr the_object)
  2201. {
  2202.  
  2203. // this function computes maximum radius of object, maybe a better method would
  2204. // use average radius? Note that this functiopn shouldn't be used during
  2205. // runtime but when an object is created
  2206.  
  2207. float new_radius,   // used in average radius calculation of object
  2208.       x,y,z;        // a single vertex
  2209.  
  2210. int index;          // looping variable
  2211.  
  2212. // reset object radius
  2213.  
  2214. the_object->radius=0;
  2215.  
  2216. for (index=0; index<the_object->num_vertices; index++)
  2217.     {
  2218.  
  2219.     x = the_object->vertices_local[index].x;
  2220.     y = the_object->vertices_local[index].y;
  2221.     z = the_object->vertices_local[index].z;
  2222.  
  2223.     // compute distance to point
  2224.  
  2225.     new_radius = (float)sqrt(x*x + y*y + z*z);
  2226.  
  2227.     // is this radius bigger than last?
  2228.  
  2229.     if (new_radius > the_object->radius)
  2230.        the_object->radius = new_radius;
  2231.  
  2232.     } // end for index
  2233.  
  2234. // return radius just in case
  2235.  
  2236. return(the_object->radius);
  2237.  
  2238. } // end Compute_Object_Radius
  2239.  
  2240. /////////////////////////////////////////////////////////////////////////////
  2241.  
  2242. void Clip_Object_3D(object_ptr the_object, int mode)
  2243. {
  2244. // this function clips an object in camera coordinates against the 3D viewing
  2245. // volume. the function has two modes of operation. In CLIP_Z_MODE the
  2246. // function performs only a simple z extend clip with the near and far clipping
  2247. // planes. In CLIP_XYZ_MODE mode the function performs a full 3-D clip
  2248.  
  2249. int curr_poly;   // the current polygon being processed
  2250.  
  2251. float x1,y1,z1,
  2252.       x2,y2,z2,
  2253.       x3,y3,z3,
  2254.       x4,y4,z4,  // working variables used to hold vertices
  2255.  
  2256.       x1_compare, // used to hold clipping points on x and y
  2257.       y1_compare,
  2258.       x2_compare,
  2259.       y2_compare,
  2260.       x3_compare,
  2261.       y3_compare,
  2262.       x4_compare,
  2263.       y4_compare;
  2264.  
  2265.  
  2266. // test if trivial z clipping is being requested
  2267.  
  2268. if (mode==CLIP_Z_MODE)
  2269.    {
  2270.    // attempt to clip each polygon against viewing volume
  2271.  
  2272.    for (curr_poly=0; curr_poly<the_object->num_polys; curr_poly++)
  2273.        {
  2274.  
  2275.        // extract z components
  2276.  
  2277.        z1=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[0]].z;
  2278.        z2=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[1]].z;
  2279.        z3=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[2]].z;
  2280.  
  2281.        // test if this is a quad
  2282.  
  2283.        if (the_object->polys[curr_poly].num_points==4)
  2284.           {
  2285.           // extract 4th z component
  2286.  
  2287.           z4=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[3]].z;
  2288.  
  2289.           } // end if quad
  2290.        else
  2291.          z4=z3;
  2292.  
  2293.        // perform near and far z clipping test
  2294.  
  2295.        if ( (z1<clip_near_z && z2<clip_near_z && z3<clip_near_z && z4<clip_near_z) ||
  2296.             (z1>clip_far_z && z2>clip_far_z && z3>clip_far_z && z4>clip_far_z) )
  2297.           {
  2298.           // set clipped flag
  2299.  
  2300.           the_object->polys[curr_poly].clipped=1;
  2301.  
  2302.           } // end if clipped
  2303.  
  2304.        } // end for curr_poly
  2305.  
  2306.     } // end if CLIP_Z_MODE
  2307. else
  2308.    {
  2309.    // CLIP_XYZ_MODE, perform full 3D viewing volume clip
  2310.  
  2311.    for (curr_poly=0; curr_poly<the_object->num_polys; curr_poly++)
  2312.        {
  2313.  
  2314.        // extract x,y and z components
  2315.  
  2316.        x1=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[0]].x;
  2317.        y1=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[0]].y;
  2318.        z1=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[0]].z;
  2319.  
  2320.        x2=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[1]].x;
  2321.        y2=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[1]].y;
  2322.        z2=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[1]].z;
  2323.  
  2324.        x3=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[2]].x;
  2325.        y3=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[2]].y;
  2326.        z3=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[2]].z;
  2327.  
  2328.        // test if this is a quad
  2329.  
  2330.        if (the_object->polys[curr_poly].num_points==4)
  2331.           {
  2332.           // extract 4th vertex
  2333.  
  2334.           x4=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[3]].x;
  2335.           y4=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[3]].y;
  2336.           z4=the_object->vertices_camera[the_object->polys[curr_poly].vertex_list[3]].z;
  2337.  
  2338.           // do clipping tests
  2339.  
  2340.           // perform near and far z clipping test first
  2341.  
  2342.           if (!((z1>clip_near_z || z2>clip_near_z || z3>clip_near_z || z4>clip_near_z) &&
  2343.                 (z1<clip_far_z || z2<clip_far_z || z3<clip_far_z || z4<clip_far_z)) )
  2344.              {
  2345.              // set clipped flag
  2346.  
  2347.              the_object->polys[curr_poly].clipped=1;
  2348.              continue;
  2349.  
  2350.              } // end if clipped
  2351.  
  2352.           // pre-compute x comparision ranges
  2353.  
  2354.           x1_compare = (HALF_SCREEN_WIDTH*z1)/viewing_distance;
  2355.           x2_compare = (HALF_SCREEN_WIDTH*z2)/viewing_distance;
  2356.           x3_compare = (HALF_SCREEN_WIDTH*z3)/viewing_distance;
  2357.           x4_compare = (HALF_SCREEN_WIDTH*z4)/viewing_distance;
  2358.  
  2359.           // perform x test
  2360.  
  2361.           if (!((x1>-x1_compare || x2>-x1_compare || x3>-x3_compare || x4>-x4_compare) &&
  2362.                 (x1<x1_compare || x2<x2_compare || x3<x3_compare || x4<x4_compare))  )
  2363.              {
  2364.              // set clipped flag
  2365.  
  2366.              the_object->polys[curr_poly].clipped=1;
  2367.              continue;
  2368.  
  2369.              } // end if clipped
  2370.  
  2371.           // pre-compute x comparision ranges
  2372.  
  2373.           y1_compare = (HALF_SCREEN_HEIGHT*z1)/viewing_distance;
  2374.           y2_compare = (HALF_SCREEN_HEIGHT*z2)/viewing_distance;
  2375.           y3_compare = (HALF_SCREEN_HEIGHT*z3)/viewing_distance;
  2376.           y4_compare = (HALF_SCREEN_HEIGHT*z4)/viewing_distance;
  2377.  
  2378.           // perform x test
  2379.  
  2380.           if (!((y1>-y1_compare || y2>-y1_compare || y3>-y3_compare || y4>-y4_compare) &&
  2381.                 (y1<y1_compare || y2<y2_compare || y3<y3_compare || y4<y4_compare))  )
  2382.              {
  2383.              // set clipped flag
  2384.  
  2385.              the_object->polys[curr_poly].clipped=1;
  2386.              continue;
  2387.  
  2388.              } // end if clipped
  2389.  
  2390.           } // end if quad
  2391.        else
  2392.           {
  2393.           // must be triangle, perform clipping tests on only 3 vertices
  2394.  
  2395.           // do clipping tests
  2396.  
  2397.           // perform near and far z clipping test first
  2398.  
  2399.           if (!((z1>clip_near_z || z2>clip_near_z || z3>clip_near_z) &&
  2400.                 (z1<clip_far_z || z2<clip_far_z || z3<clip_far_z)) )
  2401.              {
  2402.              // set clipped flag
  2403.  
  2404.              the_object->polys[curr_poly].clipped=1;
  2405.              continue;
  2406.  
  2407.              } // end if clipped
  2408.  
  2409.           // pre-compute x comparision ranges
  2410.  
  2411.           x1_compare = (HALF_SCREEN_WIDTH*z1)/viewing_distance;
  2412.           x2_compare = (HALF_SCREEN_WIDTH*z2)/viewing_distance;
  2413.           x3_compare = (HALF_SCREEN_WIDTH*z3)/viewing_distance;
  2414.  
  2415.           // perform x test
  2416.  
  2417.           if (!((x1>-x1_compare || x2>-x1_compare || x3>-x3_compare ) &&
  2418.                 (x1<x1_compare || x2<x2_compare || x3<x3_compare ))  )
  2419.              {
  2420.              // set clipped flag
  2421.  
  2422.              the_object->polys[curr_poly].clipped=1;
  2423.              continue;
  2424.  
  2425.              } // end if clipped
  2426.  
  2427.           // pre-compute x comparision ranges
  2428.  
  2429.           y1_compare = (HALF_SCREEN_HEIGHT*z1)/viewing_distance;
  2430.           y2_compare = (HALF_SCREEN_HEIGHT*z2)/viewing_distance;
  2431.           y3_compare = (HALF_SCREEN_HEIGHT*z3)/viewing_distance;
  2432.  
  2433.           // perform x test
  2434.  
  2435.           if (!((y1>-y1_compare || y2>-y1_compare || y3>-y3_compare) &&
  2436.                 (y1<y1_compare || y2<y2_compare || y3<y3_compare ))  )
  2437.              {
  2438.              // set clipped flag
  2439.  
  2440.              the_object->polys[curr_poly].clipped=1;
  2441.              continue;
  2442.  
  2443.              } // end if clipped
  2444.  
  2445.            } // end else triangle
  2446.  
  2447.        } // end for curr_poly
  2448.  
  2449.    } // end else clip everything
  2450.  
  2451. } // end Clip_Object_3D
  2452.  
  2453. //////////////////////////////////////////////////////////////////////////////
  2454.  
  2455. void Remove_Backfaces_And_Shade(object_ptr the_object)
  2456. {
  2457. // this function removes all the backfaces of an object by setting the removed
  2458. // flag. This function assumes that the object has been transformed into
  2459. // camera coordinates. Also, the function computes the flat shading of the
  2460. // object
  2461.  
  2462. int vertex_0,         // vertex indices
  2463.     vertex_1,
  2464.     vertex_2,
  2465.     curr_poly;        // current polygon
  2466.  
  2467. float dp,             // the result of the dot product
  2468.       intensity;      // the final intensity of the surface
  2469.  
  2470. vector_3d u,v,           // general working vectors
  2471.           normal,        // the normal to the surface begin processed
  2472.           sight;         // line of sight vector
  2473.  
  2474. // for each polygon in the object determine if it is pointing away from the
  2475. // viewpoint and direction
  2476.  
  2477. for (curr_poly=0; curr_poly<the_object->num_polys; curr_poly++)
  2478.     {
  2479.  
  2480.     // compute two vectors on polygon that have the same intial points
  2481.  
  2482.     vertex_0 = the_object->polys[curr_poly].vertex_list[0];
  2483.     vertex_1 = the_object->polys[curr_poly].vertex_list[1];
  2484.     vertex_2 = the_object->polys[curr_poly].vertex_list[2];
  2485.  
  2486.     // the vector u = vo->v1
  2487.  
  2488.     Make_Vector_3D((point_3d_ptr)&the_object->vertices_world[vertex_0],
  2489.                    (point_3d_ptr)&the_object->vertices_world[vertex_1],
  2490.                    (vector_3d_ptr)&u);
  2491.  
  2492.     // the vector v = vo-v2
  2493.  
  2494.     Make_Vector_3D((point_3d_ptr)&the_object->vertices_world[vertex_0],
  2495.                    (point_3d_ptr)&the_object->vertices_world[vertex_2],
  2496.                    (vector_3d_ptr)&v);
  2497.  
  2498.     // compute the normal to polygon v x u
  2499.  
  2500.     Cross_Product_3D((vector_3d_ptr)&v,
  2501.                      (vector_3d_ptr)&u,
  2502.                      (vector_3d_ptr)&normal);
  2503.  
  2504.     // compute the line of sight vector, since all coordinates are world all
  2505.     // object vertices are already relative to (0,0,0), thus
  2506.  
  2507.     sight.x = view_point.x-the_object->vertices_world[vertex_0].x;
  2508.     sight.y = view_point.y-the_object->vertices_world[vertex_0].y;
  2509.     sight.z = view_point.z-the_object->vertices_world[vertex_0].z;
  2510.  
  2511.     // compute the dot product between line of sight vector and normal to surface
  2512.  
  2513.     dp = Dot_Product_3D((vector_3d_ptr)&normal,(vector_3d_ptr)&sight);
  2514.  
  2515.     // set the clip flagged appropriately
  2516.  
  2517.     if (dp>0)
  2518.        {
  2519.        // set visibility
  2520.  
  2521.        the_object->polys[curr_poly].visible = 1;
  2522.  
  2523.        // compute light intensity if needed
  2524.  
  2525.  
  2526.        if (the_object->polys[curr_poly].shading==FLAT_SHADING)
  2527.           {
  2528.  
  2529.           // compute the dot product between the light source vector
  2530.           // and normal vector to surface
  2531.  
  2532.           dp = Dot_Product_3D((vector_3d_ptr)&normal,
  2533.                               (vector_3d_ptr)&light_source);
  2534.  
  2535.           // test if light ray is reflecting off surface
  2536.  
  2537.           if (dp>0)
  2538.              {
  2539.              // now cos 0 = (u.v)/|u||v| or
  2540.  
  2541.              intensity = ambient_light + (15*dp/(the_object->polys[curr_poly].normal_length));
  2542.  
  2543.              // test if intensity has overflowed
  2544.  
  2545.              if (intensity >15)
  2546.                  intensity = 15;
  2547.  
  2548.              // intensity now varies from 0-1, 0 being black or grazing and 1 being
  2549.              // totally illuminated. use the value to index into color table
  2550.  
  2551.              the_object->polys[curr_poly].shade =
  2552.                         the_object->polys[curr_poly].color - (int)intensity;
  2553.  
  2554.              // printf("\nintensity of polygon %d is %f",curr_poly,intensity);
  2555.  
  2556.              } // end if light is reflecting off surface
  2557.           else
  2558.              the_object->polys[curr_poly].shade =
  2559.              the_object->polys[curr_poly].color - (int)ambient_light;
  2560.  
  2561.           } // end if use flat shading
  2562.        else
  2563.           {
  2564.           // assume constant shading and simply assign color to shade
  2565.  
  2566.           the_object->polys[curr_poly].shade = the_object->polys[curr_poly].color;
  2567.  
  2568.           } // end else constat shading
  2569.  
  2570.        } // end if face is visible
  2571.     else
  2572.        the_object->polys[curr_poly].visible = 0; // set invisible flag
  2573.  
  2574. #if DEBUG
  2575.  
  2576.     printf("\npolygon #%d",curr_poly);
  2577.     printf("\nu      = [%f,%f,%f]",u.x,u.y,u.z);
  2578.     printf("\nv      = [%f,%f,%f]",v.x,v.y,v.z);
  2579.     printf("\nnormal = [%f,%f,%f]",normal.x,normal.y,normal.z);
  2580.     printf("\nsight  = [%f,%f,%f]",sight.x,sight.y,sight.z);
  2581.     printf("\ndp     = %f\n",dp);
  2582.  
  2583. #endif
  2584.  
  2585.     } // end for curr_poly
  2586.  
  2587. } // end Remove_Backfaces_And_Shade
  2588.  
  2589. //////////////////////////////////////////////////////////////////////////////
  2590.  
  2591. int Remove_Object(object_ptr the_object, int mode)
  2592. {
  2593. // this function determines if an entire object is within the viewing volume
  2594. // or not by testing if the bounding sphere of the object in question
  2595. // is within the viewing volume.In essence, this function "culls" entire objects
  2596.  
  2597. float x_bsphere,   // the x,y and z components of the projected center of object
  2598.       y_bsphere,
  2599.       z_bsphere,
  2600.       radius,     // the radius of object
  2601.       x_compare,  // the extents of the clipping volume in x and y at the
  2602.       y_compare;  // bounding spheres current z
  2603.  
  2604. // first transform world position of object into camera coordinates
  2605.  
  2606. // compute x component
  2607.  
  2608. x_bsphere = the_object->world_pos.x * global_view[0][0] +
  2609.            the_object->world_pos.y * global_view[1][0] +
  2610.            the_object->world_pos.z * global_view[2][0] +
  2611.                                      global_view[3][0];
  2612.  
  2613. // compute y component
  2614.  
  2615. y_bsphere = the_object->world_pos.x * global_view[0][1] +
  2616.            the_object->world_pos.y * global_view[1][1] +
  2617.            the_object->world_pos.z * global_view[2][1] +
  2618.                                      global_view[3][1];
  2619. // compute z component
  2620.  
  2621. z_bsphere = the_object->world_pos.x * global_view[0][2] +
  2622.            the_object->world_pos.y * global_view[1][2] +
  2623.            the_object->world_pos.z * global_view[2][2] +
  2624.                                      global_view[3][2];
  2625.  
  2626. // extract radius of object
  2627.  
  2628. radius = the_object->radius;
  2629.  
  2630. if (mode==OBJECT_CULL_Z_MODE)
  2631.    {
  2632.  
  2633.    // first test against near and far z planes
  2634.  
  2635.    if ( ((z_bsphere-radius) > clip_far_z) ||
  2636.         ((z_bsphere+radius) < clip_near_z) )
  2637.         return(1);
  2638.    else
  2639.         return(0);
  2640.  
  2641.    } // end if z only
  2642. else
  2643.    {
  2644.    // perform full x,y,z test
  2645.  
  2646.    if ( ((z_bsphere-radius) > clip_far_z) ||
  2647.         ((z_bsphere+radius) < clip_near_z) )
  2648.         return(1);
  2649.  
  2650.    // test against x right and left planes, first compute viewing volume
  2651.    // extents at position z position of bounding sphere
  2652.  
  2653.    x_compare = (HALF_SCREEN_WIDTH*z_bsphere)/viewing_distance;
  2654.  
  2655.    if ( ((x_bsphere-radius) > x_compare) ||
  2656.         ((x_bsphere+radius) < -x_compare) )
  2657.       return(1);
  2658.  
  2659.    // finally test against y top and bottom planes
  2660.  
  2661.    y_compare = (INVERSE_ASPECT_RATIO*HALF_SCREEN_HEIGHT*z_bsphere)/viewing_distance;
  2662.  
  2663.    if ( ((y_bsphere-radius) > y_compare) ||
  2664.         ((y_bsphere+radius) < -y_compare) )
  2665.       return(1);
  2666.  
  2667.    // else it just can't be removed!!!
  2668.  
  2669.    return(0);
  2670.  
  2671.    } // end else
  2672.  
  2673. } // end Remove_Object
  2674.  
  2675. //////////////////////////////////////////////////////////////////////////////
  2676.  
  2677. void Generate_Poly_List(object_ptr the_object,int mode)
  2678. {
  2679. // this function is used to generate the final polygon list that will be
  2680. // rendered. Object by object the list is built up
  2681.  
  2682.  
  2683. int vertex,
  2684.     curr_vertex,
  2685.     curr_poly;
  2686.  
  2687. // test if this is the first object to be inserted
  2688.  
  2689. if (mode==RESET_POLY_LIST)
  2690.    {
  2691.    // reset number of polys to zero
  2692.  
  2693.    num_polys_frame=0;
  2694.    return;
  2695.  
  2696.    } // end if first
  2697.  
  2698. // insert all visible polygons into polygon list
  2699.  
  2700. for (curr_poly=0; curr_poly<the_object->num_polys; curr_poly++)
  2701.     {
  2702.  
  2703.     // test if this poly is visible, if so add it to poly list
  2704.  
  2705.     if (the_object->polys[curr_poly].visible &&
  2706.         !the_object->polys[curr_poly].clipped)
  2707.        {
  2708.        // add this poly to poly list
  2709.  
  2710.        // first copy data and vertices into an open slot in storage area
  2711.  
  2712.        world_poly_storage[num_polys_frame].num_points = the_object->polys[curr_poly].num_points;
  2713.        world_poly_storage[num_polys_frame].color      = the_object->polys[curr_poly].color;
  2714.        world_poly_storage[num_polys_frame].shade      = the_object->polys[curr_poly].shade;
  2715.        world_poly_storage[num_polys_frame].shading    = the_object->polys[curr_poly].shading;
  2716.        world_poly_storage[num_polys_frame].two_sided  = the_object->polys[curr_poly].two_sided;
  2717.        world_poly_storage[num_polys_frame].visible    = the_object->polys[curr_poly].visible;
  2718.        world_poly_storage[num_polys_frame].clipped    = the_object->polys[curr_poly].clipped;
  2719.        world_poly_storage[num_polys_frame].active     = the_object->polys[curr_poly].active;
  2720.  
  2721.        // now copy vertices
  2722.  
  2723.        for (curr_vertex=0; curr_vertex<the_object->polys[curr_poly].num_points; curr_vertex++)
  2724.            {
  2725.            // extract vertex number
  2726.  
  2727.            vertex=the_object->polys[curr_poly].vertex_list[curr_vertex];
  2728.  
  2729.            // extract x,y and z
  2730.  
  2731.            world_poly_storage[num_polys_frame].vertex_list[curr_vertex].x
  2732.                                     = the_object->vertices_camera[vertex].x;
  2733.  
  2734.            world_poly_storage[num_polys_frame].vertex_list[curr_vertex].y
  2735.                                     = the_object->vertices_camera[vertex].y;
  2736.  
  2737.            world_poly_storage[num_polys_frame].vertex_list[curr_vertex].z
  2738.                                     = the_object->vertices_camera[vertex].z;
  2739.  
  2740.  
  2741.            } // end for curr_vertex
  2742.  
  2743.        // assign pointer to it
  2744.  
  2745.        world_polys[num_polys_frame] = &world_poly_storage[num_polys_frame];
  2746.  
  2747.        // increment number of polys
  2748.  
  2749.        num_polys_frame++;
  2750.  
  2751.        } // end if poly visible
  2752.  
  2753.     } // end for curr_poly
  2754.  
  2755. } // end Generate_Poly_List
  2756.  
  2757. //////////////////////////////////////////////////////////////////////////////
  2758.  
  2759. void Project_Polys(void)
  2760. {
  2761. // this function performs the final 3-D to 2-D perspective projection of the
  2762. // polygons
  2763.  
  2764.  
  2765.  
  2766.  
  2767. } // end Project_Polys
  2768.  
  2769. //////////////////////////////////////////////////////////////////////////////
  2770.  
  2771. void Draw_Line(int xo, int yo, int x1,int y1, unsigned char color,unsigned char far *vb_start)
  2772. {
  2773. // this function draws a line from xo,yo to x1,y1 using differential error
  2774. // terms (based on Bresenahams work)
  2775.  
  2776. int dx,             // difference in x's
  2777.     dy,             // difference in y's
  2778.     x_inc,          // amount in pixel space to move during drawing
  2779.     y_inc,          // amount in pixel space to move during drawing
  2780.     error=0,        // the discriminant i.e. error i.e. decision variable
  2781.     index;          // used for looping
  2782.  
  2783. // pre-compute first pixel address in video buffer
  2784.  
  2785. vb_start = vb_start + ((unsigned int)yo<<6) +
  2786.                       ((unsigned int)yo<<8) +
  2787.                       (unsigned int)xo;
  2788.  
  2789. // compute horizontal and vertical deltas
  2790.  
  2791. dx = x1-xo;
  2792. dy = y1-yo;
  2793.  
  2794. // test which direction the line is going in i.e. slope angle
  2795.  
  2796. if (dx>=0)
  2797.    {
  2798.    x_inc = 1;
  2799.  
  2800.    } // end if line is moving right
  2801. else
  2802.    {
  2803.    x_inc = -1;
  2804.    dx    = -dx;  // need absolute value
  2805.  
  2806.    } // end else moving left
  2807.  
  2808. // test y component of slope
  2809.  
  2810. if (dy>=0)
  2811.    {
  2812.    y_inc = 320; // 320 bytes per line
  2813.  
  2814.    } // end if line is moving down
  2815. else
  2816.    {
  2817.    y_inc = -320;
  2818.    dy    = -dy;  // need absolute value
  2819.  
  2820.    } // end else moving up
  2821.  
  2822. // now based on which delta is greater we can draw the line
  2823.  
  2824. if (dx>dy)
  2825.    {
  2826.  
  2827.    // draw the line
  2828.  
  2829.    for (index=0; index<=dx; index++)
  2830.        {
  2831.        // set the pixel
  2832.  
  2833.        *vb_start = color;
  2834.  
  2835.        // adjust the error term
  2836.  
  2837.        error+=dy;
  2838.  
  2839.        // test if error has overflowed
  2840.  
  2841.        if (error>dx)
  2842.           {
  2843.  
  2844.           error-=dx;
  2845.  
  2846.           // move to next line
  2847.  
  2848.           vb_start+=y_inc;
  2849.  
  2850.           } // end if error overflowed
  2851.  
  2852.        // move to the next pixel
  2853.  
  2854.        vb_start+=x_inc;
  2855.  
  2856.        } // end for
  2857.  
  2858.    } // end if |slope| <= 1
  2859. else
  2860.    {
  2861.  
  2862.    // draw the line
  2863.  
  2864.    for (index=0; index<=dy; index++)
  2865.        {
  2866.        // set the pixel
  2867.  
  2868.        *vb_start = color;
  2869.  
  2870.        // adjust the error term
  2871.  
  2872.        error+=dx;
  2873.  
  2874.        // test if error overflowed
  2875.  
  2876.        if (error>0)
  2877.           {
  2878.  
  2879.           error-=dy;
  2880.  
  2881.           // move to next line
  2882.  
  2883.           vb_start+=x_inc;
  2884.  
  2885.           } // end if error overflowed
  2886.  
  2887.        // move to the next pixel
  2888.  
  2889.        vb_start+=y_inc;
  2890.  
  2891.        } // end for
  2892.  
  2893.    } // end else |slope| > 1
  2894.  
  2895. } // end Draw_Line
  2896.  
  2897. ///////////////////////////////////////////////////////////////////////////////
  2898.  
  2899. void Draw_Object_Wire(object_ptr the_object)
  2900. {
  2901. // this function draws an object out of wires
  2902.  
  2903. int curr_poly,      // the current polygon
  2904.     curr_vertex,    // the current vertex
  2905.     vertex;         // vertex index
  2906.  
  2907. float x1,y1,z1,     // working variables
  2908.       x2,y2,z2;
  2909.  
  2910. int ix1,iy1,        // integers used to hold screen coordinates
  2911.     ix2,iy2;
  2912.  
  2913. // compute position of object in world
  2914.  
  2915. for (curr_poly=0; curr_poly<the_object->num_polys; curr_poly++)
  2916.     {
  2917.  
  2918.     // is this polygon visible?
  2919.  
  2920.     if (the_object->polys[curr_poly].visible==0 ||
  2921.         the_object->polys[curr_poly].clipped )
  2922.        continue;
  2923.  
  2924.     // printf("\npolygon #%d",curr_poly);
  2925.  
  2926.     for (curr_vertex=0; curr_vertex<the_object->polys[curr_poly].num_points-1; curr_vertex++)
  2927.         {
  2928.  
  2929.         // extract two endpoints
  2930.  
  2931.         vertex=the_object->polys[curr_poly].vertex_list[curr_vertex];
  2932.  
  2933.         x1 = the_object->vertices_camera[vertex].x;
  2934.         y1 = the_object->vertices_camera[vertex].y;
  2935.         z1 = the_object->vertices_camera[vertex].z;
  2936.  
  2937.         vertex=the_object->polys[curr_poly].vertex_list[curr_vertex+1];
  2938.  
  2939.         x2 = the_object->vertices_camera[vertex].x;
  2940.         y2 = the_object->vertices_camera[vertex].y;
  2941.         z2 = the_object->vertices_camera[vertex].z;
  2942.  
  2943.         // convert to screen ccordinates
  2944.  
  2945.         x1=(HALF_SCREEN_WIDTH  + x1*viewing_distance/z1);
  2946.         y1=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y1*viewing_distance/z1);
  2947.  
  2948.  
  2949.         x2=(HALF_SCREEN_WIDTH  + x2*viewing_distance/z2);
  2950.         y2=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y2*viewing_distance/z2);
  2951.  
  2952.         // convert floats to integers for line clipper
  2953.  
  2954.         ix1=(int)x1;
  2955.         iy1=(int)y1;
  2956.         ix2=(int)x2;
  2957.         iy2=(int)y2;
  2958.  
  2959.  
  2960.         // draw clipped lines
  2961.  
  2962.         if (Clip_Line(&ix1,&iy1,&ix2,&iy2))
  2963.            {
  2964.            Draw_Line((int)ix1,(int)iy1,(int)ix2,(int)iy2,
  2965.                      (unsigned char)the_object->polys[curr_poly].color,
  2966.                      double_buffer);
  2967.  
  2968.            } // end if clip
  2969.  
  2970.         } // end for vertex
  2971.  
  2972.     // close polygon
  2973.  
  2974.     ix1=(int)x2;
  2975.     iy1=(int)y2;
  2976.  
  2977.     // extract starting point again to close polygon
  2978.  
  2979.     vertex=the_object->polys[curr_poly].vertex_list[0];
  2980.  
  2981.     x2 = the_object->vertices_camera[vertex].x;
  2982.     y2 = the_object->vertices_camera[vertex].y;
  2983.     z2 = the_object->vertices_camera[vertex].z;
  2984.  
  2985.     // compute screen coordinates
  2986.  
  2987.     x2=(HALF_SCREEN_WIDTH  + x2*viewing_distance/z2);
  2988.     y2=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y2*viewing_distance/z2);
  2989.  
  2990.     // convert floats to integers
  2991.  
  2992.     ix2=(int)x2;
  2993.     iy2=(int)y2;
  2994.  
  2995.     // draw clipped lines
  2996.  
  2997.     if (Clip_Line(&ix1,&iy1,&ix2,&iy2))
  2998.        {
  2999.        Draw_Line((int)ix1,(int)iy1,(int)ix2,(int)iy2,
  3000.                  (unsigned char)the_object->polys[curr_poly].color,
  3001.                  double_buffer);
  3002.  
  3003.        } // end if clip
  3004.  
  3005.     } // end for curr_poly
  3006.  
  3007. } // end Draw_Object_Wire
  3008.  
  3009. ///////////////////////////////////////////////////////////////////////////////
  3010.  
  3011. void Draw_Object_Solid(object_ptr the_object)
  3012. {
  3013.  
  3014. // this function draws an object shaded solid and can perform
  3015. // simple z entent clipping
  3016.  
  3017. int curr_poly,      // the current polygon
  3018.     vertex_1,       // vertex index numbers
  3019.     vertex_2,
  3020.     vertex_3,
  3021.     vertex_4,
  3022.     is_quad=0;      // quadrilateral flag
  3023.  
  3024. float x1,y1,z1,     // working variables
  3025.       x2,y2,z2,
  3026.       x3,y3,z3,
  3027.       x4,y4,z4;
  3028.  
  3029. // compute position of object in world
  3030.  
  3031. for (curr_poly=0; curr_poly<the_object->num_polys; curr_poly++)
  3032.     {
  3033.  
  3034.     // is this polygon visible?
  3035.  
  3036.     if (the_object->polys[curr_poly].visible==0 ||
  3037.         the_object->polys[curr_poly].clipped )
  3038.        continue;
  3039.  
  3040.     // extract the vertex numbers
  3041.  
  3042.     vertex_1=the_object->polys[curr_poly].vertex_list[0];
  3043.     vertex_2=the_object->polys[curr_poly].vertex_list[1];
  3044.     vertex_3=the_object->polys[curr_poly].vertex_list[2];
  3045.  
  3046.  
  3047.     // do Z clipping first before projection
  3048.  
  3049.     z1=the_object->vertices_camera[vertex_1].z;
  3050.     z2=the_object->vertices_camera[vertex_2].z;
  3051.     z3=the_object->vertices_camera[vertex_3].z;
  3052.  
  3053.     // test if this is a quad
  3054.  
  3055.     if (the_object->polys[curr_poly].num_points==4)
  3056.        {
  3057.  
  3058.        // extract vertex number and z component for clipping and projection
  3059.  
  3060.        vertex_4=the_object->polys[curr_poly].vertex_list[3];
  3061.        z4=the_object->vertices_camera[vertex_4].z;
  3062.  
  3063.        // set quad flag
  3064.  
  3065.        is_quad=1;
  3066.  
  3067.        } // end if quad
  3068.     else
  3069.       z4=z3;
  3070.  
  3071. #if 0
  3072.  
  3073.     // perform z clipping test
  3074.  
  3075.     if ( (z1<clip_near_z && z2<clip_near_z && z3<clip_near_z && z4<clip_near_z) ||
  3076.          (z1>clip_far_z && z2>clip_far_z && z3>clip_far_z && z4>clip_far_z) )
  3077.        continue;
  3078. #endif
  3079.  
  3080.         // extract points of polygon
  3081.  
  3082.         x1 = the_object->vertices_camera[vertex_1].x;
  3083.         y1 = the_object->vertices_camera[vertex_1].y;
  3084.  
  3085.         x2 = the_object->vertices_camera[vertex_2].x;
  3086.         y2 = the_object->vertices_camera[vertex_2].y;
  3087.  
  3088.         x3 = the_object->vertices_camera[vertex_3].x;
  3089.         y3 = the_object->vertices_camera[vertex_3].y;
  3090.  
  3091.  
  3092.         // compute screen position of points
  3093.  
  3094.         x1=(HALF_SCREEN_WIDTH  + x1*viewing_distance/z1);
  3095.         y1=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y1*viewing_distance/z1);
  3096.  
  3097.         x2=(HALF_SCREEN_WIDTH  + x2*viewing_distance/z2);
  3098.         y2=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y2*viewing_distance/z2);
  3099.  
  3100.         x3=(HALF_SCREEN_WIDTH  + x3*viewing_distance/z3);
  3101.         y3=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y3*viewing_distance/z3);
  3102.  
  3103.         // draw triangle
  3104.  
  3105.         Draw_Triangle_2D((int)x1,(int)y1,(int)x2,(int)y2,(int)x3,(int)y3,
  3106.                          the_object->polys[curr_poly].shade);
  3107.  
  3108.  
  3109.         // draw second poly if this is a quad
  3110.  
  3111.         if (is_quad)
  3112.            {
  3113.            // extract the point
  3114.  
  3115.            x4 = the_object->vertices_camera[vertex_4].x;
  3116.            y4 = the_object->vertices_camera[vertex_4].y;
  3117.  
  3118.            // poject to screen
  3119.  
  3120.            x4=(HALF_SCREEN_WIDTH  + x4*viewing_distance/z4);
  3121.            y4=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y4*viewing_distance/z4);
  3122.  
  3123.            // draw triangle
  3124.  
  3125.            Draw_Triangle_2D((int)x1,(int)y1,(int)x3,(int)y3,(int)x4,(int)y4,
  3126.                             the_object->polys[curr_poly].shade);
  3127.  
  3128.            } // end if quad
  3129.  
  3130.     } // end for curr_poly
  3131.  
  3132. } // end Draw_Object_Solid
  3133.  
  3134. ///////////////////////////////////////////////////////////////////////////////
  3135.  
  3136. void Draw_Poly_List(void)
  3137. {
  3138.  
  3139. // this function draws the global polygon list generated by calls to
  3140. // Generate_Poly_List
  3141.  
  3142. int curr_poly,          // the current polygon
  3143.     is_quad=0;          // quadrilateral flag
  3144.  
  3145. float x1,y1,z1,         // working variables
  3146.       x2,y2,z2,
  3147.       x3,y3,z3,
  3148.       x4,y4,z4;
  3149.  
  3150. // draw each polygon in list
  3151.  
  3152. for (curr_poly=0; curr_poly<num_polys_frame; curr_poly++)
  3153.     {
  3154.  
  3155.     // reset quad flag
  3156.  
  3157.     is_quad=0;
  3158.  
  3159.     // do Z clipping first before projection
  3160.  
  3161.     z1=world_polys[curr_poly]->vertex_list[0].z;
  3162.     z2=world_polys[curr_poly]->vertex_list[1].z;
  3163.     z3=world_polys[curr_poly]->vertex_list[2].z;
  3164.  
  3165.     // test if this is a quad
  3166.  
  3167.     if (world_polys[curr_poly]->num_points==4)
  3168.        {
  3169.        // extract vertex number and z component for clipping and projection
  3170.  
  3171.        z4=world_polys[curr_poly]->vertex_list[3].z;
  3172.  
  3173.        // set quad flag
  3174.  
  3175.        is_quad=1;
  3176.  
  3177.        } // end if quad
  3178.     else
  3179.       z4=z3;
  3180.  
  3181. #if 0
  3182.  
  3183.     // perform z clipping test
  3184.  
  3185.     if ( (z1<clip_near_z && z2<clip_near_z && z3<clip_near_z && z4<clip_near_z) ||
  3186.          (z1>clip_far_z && z2>clip_far_z && z3>clip_far_z && z4>clip_far_z) )
  3187.        continue;
  3188.  
  3189. #endif
  3190.  
  3191.         // extract points of polygon
  3192.  
  3193.         x1 = world_polys[curr_poly]->vertex_list[0].x;
  3194.         y1 = world_polys[curr_poly]->vertex_list[0].y;
  3195.  
  3196.         x2 = world_polys[curr_poly]->vertex_list[1].x;
  3197.         y2 = world_polys[curr_poly]->vertex_list[1].y;
  3198.  
  3199.         x3 = world_polys[curr_poly]->vertex_list[2].x;
  3200.         y3 = world_polys[curr_poly]->vertex_list[2].y;
  3201.  
  3202.  
  3203.         // compute screen position of points
  3204.  
  3205.         x1=(HALF_SCREEN_WIDTH  + x1*viewing_distance/z1);
  3206.         y1=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y1*viewing_distance/z1);
  3207.  
  3208.         x2=(HALF_SCREEN_WIDTH  + x2*viewing_distance/z2);
  3209.         y2=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y2*viewing_distance/z2);
  3210.  
  3211.         x3=(HALF_SCREEN_WIDTH  + x3*viewing_distance/z3);
  3212.         y3=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y3*viewing_distance/z3);
  3213.  
  3214.         // draw triangle
  3215.  
  3216.         Draw_Triangle_2D((int)x1,(int)y1,(int)x2,(int)y2,(int)x3,(int)y3,
  3217.                          world_polys[curr_poly]->shade);
  3218.  
  3219.  
  3220.         // draw second poly if this is a quad
  3221.  
  3222.         if (is_quad)
  3223.            {
  3224.            // extract the point
  3225.  
  3226.            x4 = world_polys[curr_poly]->vertex_list[3].x;
  3227.            y4 = world_polys[curr_poly]->vertex_list[3].y;
  3228.  
  3229.            // poject to screen
  3230.  
  3231.            x4=(HALF_SCREEN_WIDTH  + x4*viewing_distance/z4);
  3232.            y4=(HALF_SCREEN_HEIGHT - ASPECT_RATIO*y4*viewing_distance/z4);
  3233.  
  3234.            // draw triangle
  3235.  
  3236.            Draw_Triangle_2D((int)x1,(int)y1,(int)x3,(int)y3,(int)x4,(int)y4,
  3237.                             world_polys[curr_poly]->shade);
  3238.  
  3239.            } // end if quad
  3240.  
  3241.     } // end for curr_poly
  3242.  
  3243. } // end Draw_Poly_List
  3244.  
  3245. //////////////////////////////////////////////////////////////////////////////
  3246.  
  3247. int Poly_Compare(facet **arg1, facet **arg2)
  3248. {
  3249. // this function comapares the average z's of two polygons and is used by the
  3250. // depth sort surface ordering algorithm
  3251.  
  3252. float z1,z2;
  3253.  
  3254. facet_ptr poly_1,poly_2;
  3255.  
  3256. // dereference the poly pointers
  3257.  
  3258. poly_1 = (facet_ptr)*arg1;
  3259. poly_2 = (facet_ptr)*arg2;
  3260.  
  3261. // compute z average of each polygon
  3262.  
  3263. if (poly_1->num_points==3)
  3264.    {
  3265.    // compute average of 3 point polygon
  3266.  
  3267.    z1 = (float)0.33333*(poly_1->vertex_list[0].z+
  3268.                         poly_1->vertex_list[1].z+
  3269.                         poly_1->vertex_list[2].z);
  3270.    }
  3271. else
  3272.    {
  3273.    // compute average of 4 point polygon
  3274.  
  3275.    z1 = (float)0.25*(poly_1->vertex_list[0].z+
  3276.                      poly_1->vertex_list[1].z+
  3277.                      poly_1->vertex_list[2].z+
  3278.                      poly_1->vertex_list[3].z);
  3279.  
  3280.    } // end else
  3281.  
  3282.  
  3283.  
  3284.  
  3285. // now polygon 2
  3286.  
  3287. if (poly_2->num_points==3)
  3288.    {
  3289.    // compute average of 3 point polygon
  3290.  
  3291.    z2 =(float)0.33333*(poly_2->vertex_list[0].z+
  3292.                        poly_2->vertex_list[1].z+
  3293.                        poly_2->vertex_list[2].z);
  3294.    }
  3295. else
  3296.    {
  3297.    // compute average of 4 point polygon
  3298.  
  3299.    z2 = (float)0.25*(poly_2->vertex_list[0].z+
  3300.                      poly_2->vertex_list[1].z+
  3301.                      poly_2->vertex_list[2].z+
  3302.                      poly_2->vertex_list[3].z);
  3303.  
  3304.    } // end else
  3305.  
  3306. // compare z1 and z2, such that polys' will be sorted in descending Z order
  3307.  
  3308. if (z1>z2)
  3309.    return(-1);
  3310. else
  3311. if (z1<z2)
  3312.    return(1);
  3313. else
  3314.    return(0);
  3315.  
  3316. } // end Poly_Compare
  3317.  
  3318. /////////////////////////////////////////////////////////////////////////////
  3319.  
  3320. void Print_Poly_List(void)
  3321. {
  3322. // diagnostic function used to print out polygon list average z's
  3323.  
  3324. int index;
  3325.  
  3326. float z;
  3327.  
  3328. printf("\npoly list z values");
  3329.  
  3330. for (index=0; index<num_polys_frame; index++)
  3331.     {
  3332.  
  3333.     if (world_polys[index]->num_points==3)
  3334.        {
  3335.  
  3336.        z = (float)0.33333*(world_polys[index]->vertex_list[0].z+
  3337.                            world_polys[index]->vertex_list[1].z+
  3338.                            world_polys[index]->vertex_list[2].z);
  3339.        }
  3340.     else
  3341.        {
  3342.        z = (float)0.25*(world_polys[index]->vertex_list[0].z+
  3343.                         world_polys[index]->vertex_list[1].z+
  3344.                         world_polys[index]->vertex_list[2].z+
  3345.                         world_polys[index]->vertex_list[3].z);
  3346.  
  3347.        } // end else
  3348.  
  3349.     printf("\nz = %f",z);
  3350.  
  3351.     } // end for index
  3352.  
  3353. } // end Print_Poly_List
  3354.  
  3355. //////////////////////////////////////////////////////////////////////////////
  3356.  
  3357. void Sort_Poly_List(void)
  3358. {
  3359. // this function does a simple z sort on the poly list to order surfaces
  3360. // the list is sorted in descending order, i.e. farther polygons first
  3361.  
  3362. qsort((void *)world_polys,num_polys_frame, sizeof(facet_ptr), Poly_Compare);
  3363.  
  3364. } // end Sort_Poly_List
  3365.  
  3366. ///////////////////////////////////////////////////////////////////////////////
  3367.  
  3368. int Load_Palette_Disk(char *filename, RGB_palette_ptr the_palette)
  3369. {
  3370. // this function loads a color palette from disk
  3371.  
  3372. int index; // used for looping
  3373.  
  3374. RGB_color color;
  3375.  
  3376. FILE *fp;
  3377.  
  3378. // open the disk file
  3379.  
  3380. if (!(fp = fopen(filename,"r")))
  3381.    return(0);
  3382.  
  3383. // load in all the colors
  3384.  
  3385. for (index=0; index<=255; index++)
  3386.     {
  3387.     // get the next color
  3388.  
  3389.     fscanf(fp,"%d %d %d",&color.red,&color.green,&color.blue);
  3390.  
  3391.     // store the color in next element of palette
  3392.  
  3393.     the_palette->colors[index].red   = color.red;
  3394.     the_palette->colors[index].green = color.green;
  3395.     the_palette->colors[index].blue  = color.blue;
  3396.  
  3397.     } // end for index
  3398.  
  3399. // set palette size to a full palette
  3400.  
  3401. the_palette->start_reg = 0;
  3402. the_palette->end_reg   = 255;
  3403.  
  3404. // close the file and return success
  3405.  
  3406. fclose(fp);
  3407.  
  3408. return(1);
  3409.  
  3410. } // end Load_Palette_Disk
  3411.  
  3412. ///////////////////////////////////////////////////////////////////////////////
  3413.  
  3414. int Save_Palette_Disk(char *filename, RGB_palette_ptr the_palette)
  3415. {
  3416. // this function saves a palette to disk
  3417.  
  3418. int index; // used for looping
  3419.  
  3420. RGB_color color;
  3421.  
  3422. FILE *fp;
  3423.  
  3424. // open the disk file
  3425.  
  3426. if (!(fp = fopen(filename,"w")))
  3427.    return(0);
  3428.  
  3429. // write 255 lines of r g b
  3430.  
  3431. for (index=0; index<=255; index++)
  3432.     {
  3433.     // get the next color
  3434.  
  3435.     // store the color in next element of palette
  3436.  
  3437.     color.red   = the_palette->colors[index].red;
  3438.     color.green = the_palette->colors[index].green;
  3439.     color.blue  = the_palette->colors[index].blue;
  3440.  
  3441.     // write the color to disk file
  3442.  
  3443.     fprintf(fp,"\n%d %d %d",color.red, color.green, color.blue);
  3444.  
  3445.     } // end for index
  3446.  
  3447. // close the file and return success
  3448.  
  3449. fclose(fp);
  3450.  
  3451. return(1);
  3452.  
  3453. } // end Save_Palette_Disk
  3454.  
  3455.  
  3456.