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