home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume38 / tessel / part01 / triangle.c < prev   
C/C++ Source or Header  |  1993-06-20  |  15KB  |  379 lines

  1. /*+-----------------------------------------------------------------------+
  2.  *| This file contains subroutines used to smooth shade a triangle.       |
  3.  *|                                                                       |
  4.  *| Author: Michael S. A. Robb         Version: 1.5        Date: 15/06/93 |
  5.  *+-----------------------------------------------------------------------+
  6.  */
  7.  
  8. #include <mem.h>
  9.  
  10. #include "24bit.h"
  11.  
  12. /*+-----------------------------------------------------------------------+
  13.  *| These variables are used for smooth shading.                          |
  14.  *+-----------------------------------------------------------------------+
  15.  */
  16.  
  17. long hdred, hdgreen, hdblue, hdblend, hdzpos; /* Horizontal increments.  */
  18. long red,   green,   blue,   blend,   zpos;   /* Current colour values.  */
  19.  
  20. int  xstart, xend, ypos;                      /* Current scan line.      */
  21.  
  22. /*+-----------------------------------------------------------------------+
  23.  *| These variables are used to sub-divide the triangle.                  |
  24.  *+-----------------------------------------------------------------------+
  25.  */
  26.  
  27. COORD triangle[3]; /* Triangle vertices. */
  28. EDGE  edge[3];     /* Triangle edges.    */
  29.  
  30. /*+-----------------------------------------------------------------------+
  31.  *| This macro is used to implement blending between fixed point values.  |
  32.  *| All values are expected to be in fixed point format. The colours are  |
  33.  *| blended according to the formula:                                     |
  34.  *|                                                                       |
  35.  *| C = BLEND * ( OC - NC ) + NC                                          |
  36.  *+-----------------------------------------------------------------------+
  37.  */
  38.  
  39. #define BLEND_FUNC( FC, OC, NC, COL, BLEND )\
  40.   FC.col_rgb.COL = (((BLEND*(OC.col_rgb.COL-NC.col_rgb.COL))>>FIXED_POINT )\
  41.                  + NC.col_rgb.COL)
  42.  
  43. #define BLEND_PIXEL( F, O, N, B )\
  44.   { BLEND_FUNC( F, O, N, col_red,   B );\
  45.     BLEND_FUNC( F, O, N, col_green, B );\
  46.     BLEND_FUNC( F, O, N, col_blue,  B );\
  47.     F.col_rgb.col_overlay = O.col_rgb.col_overlay; }
  48.  
  49. /*+-----------------------------------------------------------------------+
  50.  *| This macro is used to convert a set of Red/Green/Blue/Overlay         |
  51.  *| components into a single 32-bit integer.                              |
  52.  *|                                                                       |
  53.  *| Note that this this macro is hardware specific to the graphics        |
  54.  *| board.                                                                |
  55.  *|                                                                       |
  56.  *| Each pixel is represented by the following format:                    |
  57.  *|                                                                       |
  58.  *| 31 MSB  24 23    16 15      8  7    LSB 0                             |
  59.  *| +---------+---------+-------------------+                             |
  60.  *| |   Red   |  Green  | Overlay |   Blue  |                             |
  61.  *| +---------+---------+---------+---------+                             |
  62.  *+-----------------------------------------------------------------------+
  63.  */
  64.  
  65. #define CONVERT_RGB( R, G, B )\
  66.  ( ( ( (R)&0x00FF0000L ) <<8  )+\
  67.    ( ( (G)&0x00FF0000L )      )+\
  68.    ( ( (B)&0x00FF0000L ) >>16 ) )
  69.  
  70. /*+-----------------------------------------------------------------------+
  71.  *| This subroutine is used to render a horizontal line using Gouraud     |
  72.  *| shading.                                                              |
  73.  *|                                                                       |
  74.  *| Uses the following variables:                                         |
  75.  *|                                                                       |
  76.  *| xstart  = X pixel start coordinate                                    |
  77.  *| xend    = X pixel end coordinate                                      |
  78.  *| ypos    = Y pixel coordinate                                          |
  79.  *| red     = Current value of red component                              |
  80.  *| green   = Current value of green component                            |
  81.  *| blue    = Current value of blue component                             |
  82.  *| blend   = Current value of blending component                         |
  83.  *| hdred   = Incremental for red component                               |
  84.  *| hdgreen = Incremental for green component.                            |
  85.  *| hdblue  = Incremental for blue component.                             |
  86.  *| blend   = Incremental for blending component.                         |
  87.  *+-----------------------------------------------------------------------+
  88.  */
  89.  
  90. void render_horizontal_line()
  91.   {
  92.   COLOUR32 nc; /* New colour */
  93.   COLOUR32 oc; /* Old colour */
  94.   COLOUR32 fc; /* Final colour */
  95.  
  96.   ADDRESS  addr    = ADDRESS_PIXEL(   xstart, ypos );
  97.   ADDRESS  zbuffer = ADDRESS_ZBUFFER( xstart, ypos );
  98.  
  99.   ZBUFFER  zval;
  100.  
  101.   while ( xstart++ < xend )                   /* For each pixel in row     */
  102.     {
  103.     nc.col_value = CONVERT_RGB( red, green, blue ); /* Convert to pixel.   */
  104.  
  105.     READ_PIXEL(   addr,    oc   );            /* Read old pixel.           */
  106.     READ_ZBUFFER( zbuffer, zval );            /* Read old Z-buffer value.  */
  107.  
  108.     if ( zpos<=zval.zbuf_value && zpos >= 0 ) /* Test Z-buffer.            */
  109.       {
  110.       zval.zbuf_value = zpos;
  111.  
  112.       BLEND_PIXEL( fc, oc, nc, blend );       /* Blend colours.            */
  113.       WRITE_PIXEL( addr, fc );                /* Write new pixel.          */
  114.       WRITE_ZBUFFER( zbuffer, zval );         /* Write new Z-buffer value. */
  115.       }
  116.  
  117.     red   += hdred;                           /* Add incrementals.         */
  118.     green += hdgreen;
  119.     blue  += hdblue;
  120.     blend += hdblend;
  121.     zpos  += hdzpos;
  122.  
  123.     addr    += PIXEL_SIZE;                    /* Update Addresses.         */
  124.     zbuffer += ZBUFFER_SIZE;
  125.     }
  126.   }
  127.  
  128. /*+-----------------------------------------------------------------------+
  129.  *| This macro is used to calculate the size of the increments used when  |
  130.  *| moving vertically down an edge vector.                                |
  131.  *+-----------------------------------------------------------------------+
  132.  */
  133.  
  134. #define EDGE_DIFF( F, D )\
  135.   ( ( ( fin->F - strt->F ) << FIXED_POINT ) / (D) )
  136.  
  137. /*+-----------------------------------------------------------------------+
  138.  *| This subroutine is used to initialise a vector given the start and    |
  139.  *| finishing coordinates.                                                |
  140.  *+-----------------------------------------------------------------------+
  141.  */
  142.  
  143. void edge_init( vec, strt, fin )
  144.   EDGE *vec;
  145.   COORD *strt;
  146.   COORD *fin;
  147.   {
  148.   long delta_y    =  fin  -> c_ypos - strt -> c_ypos;
  149.  
  150.   vec -> e_deltay =  delta_y;
  151.   vec -> e_xpos   =  strt -> c_xpos  << FIXED_POINT;
  152.   vec -> e_ypos   =  strt -> c_ypos;
  153.   vec -> e_zpos   =  strt -> c_zpos  << FIXED_POINT;
  154.  
  155.   vec -> e_red    =  strt -> c_red   << FIXED_POINT;
  156.   vec -> e_green  =  strt -> c_green << FIXED_POINT;
  157.   vec -> e_blue   =  strt -> c_blue  << FIXED_POINT;
  158.   vec -> e_blend  = (strt -> c_blend << FIXED_POINT) / MAX_BLEND;
  159.  
  160.   if ( delta_y != 0 )
  161.     {
  162.     vec -> e_dxpos  = EDGE_DIFF( c_xpos,  delta_y );
  163.     vec -> e_dzpos  = EDGE_DIFF( c_zpos,  delta_y );
  164.     vec -> e_dred   = EDGE_DIFF( c_red,   delta_y );
  165.     vec -> e_dgreen = EDGE_DIFF( c_green, delta_y );
  166.     vec -> e_dblue  = EDGE_DIFF( c_blue,  delta_y );
  167.     vec -> e_dblend = EDGE_DIFF( c_blend, (MAX_BLEND * delta_y) );
  168.     }
  169.   else
  170.     {
  171.     vec -> e_dxpos  =
  172.     vec -> e_dzpos  =
  173.     vec -> e_dred   =
  174.     vec -> e_dgreen =
  175.     vec -> e_dblue  =
  176.     vec -> e_dblend = 0;
  177.     }
  178.   }
  179.  
  180. /*+-----------------------------------------------------------------------+
  181.  *| This subroutine is used to update the vector after every scan-line.   |
  182.  *+-----------------------------------------------------------------------+
  183.  */
  184.  
  185. void edge_update( vec )
  186.   EDGE *vec;
  187.   {
  188.   vec -> e_xpos  += vec -> e_dxpos;
  189.   vec -> e_zpos  += vec -> e_dzpos;
  190.   vec -> e_red   += vec -> e_dred;
  191.   vec -> e_green += vec -> e_dgreen;
  192.   vec -> e_blue  += vec -> e_dblue;
  193.   vec -> e_blend += vec -> e_dblend;
  194.   }
  195.  
  196. /*+-----------------------------------------------------------------------+
  197.  *| This macro is used to calculate the size of the increments used when  |
  198.  *| moving horizontally across a scan-line.                               |
  199.  *+-----------------------------------------------------------------------+
  200.  */
  201.  
  202. #define HORIZ_INCREMENT( F )\
  203.   ((eright -> F - eleft -> F ) / delta_x )
  204.  
  205. /*+-----------------------------------------------------------------------+
  206.  *| This subroutine is used to render a sub-triangle given the vertex     |
  207.  *| coordinate and the two other left and right hand vertices.            |
  208.  *+-----------------------------------------------------------------------+
  209.  */
  210.  
  211. void render_half( eleft, eright, delta_y, ystart )
  212.   EDGE *eleft, *eright;
  213.   long    delta_y, ystart;
  214.   {
  215.   long delta_x;
  216.  
  217.   ypos = ystart;                            /* Initial Y coordinate.       */
  218.  
  219.   while ( delta_y-- >= 0 )                  /* For every scan line.        */
  220.     {
  221.     xstart  = eleft ->e_xpos>>FIXED_POINT;  /* Starting X coordinate.      */
  222.     xend    = eright->e_xpos>>FIXED_POINT;  /* Finishing X coordinate.     */
  223.  
  224.     delta_x = xend - xstart;                /* Width of scan-line.         */
  225.  
  226.     zpos    = eleft -> e_zpos;
  227.     red     = eleft -> e_red;               /* Iniitial colour values.     */
  228.     green   = eleft -> e_green;
  229.     blue    = eleft -> e_blue;
  230.     blend   = eleft -> e_blend;
  231.  
  232.     if ( delta_x != 0 )                     /* Any change in X coordinate? */
  233.       {
  234.       hdzpos  = HORIZ_INCREMENT( e_zpos  ); /* Yes, so get incrementals.  */
  235.       hdred   = HORIZ_INCREMENT( e_red   );
  236.       hdgreen = HORIZ_INCREMENT( e_green );
  237.       hdblue  = HORIZ_INCREMENT( e_blue  );
  238.       hdblend = HORIZ_INCREMENT( e_blend );
  239.       }
  240.  
  241.     render_horizontal_line();               /* Then render the line.       */
  242.  
  243.     edge_update( eleft  );                  /* Update left edge vector.    */
  244.     edge_update( eright );                  /* Update right edge vector.   */
  245.  
  246.     ypos++;                                 /* Update Y coordinate.        */
  247.     }
  248.   }
  249.  
  250. /*+-----------------------------------------------------------------------+
  251.  *| This macro is used to perform a comparison and swap (if required) on  |
  252.  *| the two pairs of coordinates.                                         |
  253.  *+-----------------------------------------------------------------------+
  254.  */
  255.  
  256. #define COMPARE_VERTICES( X, A, B, F, T )\
  257.   if ((X)[(A)].F > (X)[(B)].F )\
  258.     {\
  259.     (T)      = (X)[(A)];\
  260.     (X)[(A)] = (X)[(B)];\
  261.     (X)[(B)] = (T);\
  262.     }
  263.  
  264. /*+-----------------------------------------------------------------------+
  265.  *| This subroutine is used to smooth shade a triangle. It takes an       |
  266.  *| array of coordinates/colours as a parameter.                          |
  267.  *+-----------------------------------------------------------------------+
  268.  */
  269.  
  270. void render_triangle( coord )
  271.   COORD *coord;
  272.   {
  273.   COORD swapbuf; /* Used for swapping */
  274.  
  275.   HARDWARE_PREPARE();
  276.  
  277.   memcpy( triangle, coord, 3*sizeof(COORD) );   /* Make copy of coords. */
  278.  
  279.   /*+-------------------------------------------------------------+
  280.    *| [1] Sort by Y coordinate so that vertices with the smallest |
  281.    *|     Y coordinate are at the top of the vertex list.         |
  282.    *+-------------------------------------------------------------+
  283.    */
  284.  
  285.   COMPARE_VERTICES( triangle, 0, 1, c_ypos, swapbuf );
  286.   COMPARE_VERTICES( triangle, 1, 2, c_ypos, swapbuf );
  287.   COMPARE_VERTICES( triangle, 0, 1, c_ypos, swapbuf );
  288.  
  289.   /*+----------------------------------------------------------------+
  290.    *| [2] Sort by X coordinate so that vertices which have identical |
  291.    *|     Y coordinates but smaller X coordinates are at the top of  |
  292.    *|     the vertex list.                                           |
  293.    *+----------------------------------------------------------------+
  294.    */
  295.  
  296.   if ( triangle[0].c_ypos == triangle[1].c_ypos )
  297.     COMPARE_VERTICES( triangle, 0, 1, c_xpos, swapbuf );
  298.  
  299.   if ( triangle[1].c_ypos == triangle[2].c_ypos )
  300.     {
  301.     COMPARE_VERTICES( triangle, 1, 2, c_xpos, swapbuf );
  302.  
  303.     if ( triangle[0].c_ypos == triangle[1].c_ypos )
  304.       COMPARE_VERTICES( triangle, 0, 1, c_xpos, swapbuf );
  305.     }
  306.  
  307.   /*+-----------------------------------------------------------------+
  308.    *| [3] Initialise three edge vectors by calculating incrementals.  |
  309.    *+-----------------------------------------------------------------+
  310.    */
  311.  
  312.   edge_init( edge+0, &triangle[0], &triangle[1] ); /* Top edge.     */
  313.   edge_init( edge+1, &triangle[0], &triangle[2] ); /* Longest edge. */
  314.   edge_init( edge+2, &triangle[1], &triangle[2] ); /* Bottom edge.  */
  315.  
  316.   /*+-----------------------------------------------------------------+
  317.    *| [4.1] The bottom two Y coordinates are identical, so only the   |
  318.    *|       top half of the triangle has to be rendered.              |
  319.    *+-----------------------------------------------------------------+
  320.    */
  321.  
  322.   if ( triangle[1].c_ypos == triangle[2].c_ypos )
  323.       render_half( edge+0, edge+1, edge[0].e_deltay, edge[0].e_ypos );
  324.   else
  325.  
  326.   /*+-----------------------------------------------------------------+
  327.    *| [4.2] The top two Y coordinates are identical, so only the      |
  328.    *|       bottom half of the triangle has to be rendered.           |
  329.    *+-----------------------------------------------------------------+
  330.    */
  331.  
  332.   if ( triangle[0].c_ypos == triangle[1].c_ypos )
  333.       render_half( edge+1, edge+2, edge[2].e_deltay, edge[2].e_ypos );
  334.   else
  335.  
  336.   /*+-----------------------------------------------------------------+
  337.    *| [4.3] Both top and bottom half-triangles must be rendered.      |
  338.    *|       However, a check must be performed to determine whether   |
  339.    *|       the longest edge is on the left or right hand side.       |
  340.    *+-----------------------------------------------------------------+
  341.    */
  342.  
  343.     {
  344.     long mult, div, delta;
  345.     int  new_xpos;
  346.  
  347.     mult     = (long) triangle[1].c_ypos - (long) triangle[0].c_ypos;
  348.     div      = (long) triangle[2].c_ypos - (long) triangle[0].c_ypos;
  349.  
  350.     delta    = (long) triangle[2].c_xpos - (long) triangle[0].c_xpos;
  351.     new_xpos = (long) triangle[0].c_xpos + (long) (delta * mult) / div;
  352.  
  353.     if ( new_xpos < triangle[1].c_xpos )
  354.       {
  355.       /*+-------------------------------------------------------------+
  356.        *| Render triangle with longest edge on the left hand side.    |
  357.        *+-------------------------------------------------------------+
  358.        */
  359.  
  360.       render_half( edge+1, edge+0, edge[0].e_deltay-1, edge[0].e_ypos );
  361.       render_half( edge+1, edge+2, edge[2].e_deltay,   edge[2].e_ypos );
  362.       }
  363.     else
  364.       {
  365.       /*+-------------------------------------------------------------+
  366.        *| Render triangle with longest edge on the right hand side.   |
  367.        *+-------------------------------------------------------------+
  368.        */
  369.  
  370.       render_half( edge+0, edge+1, edge[0].e_deltay-1, edge[0].e_ypos );
  371.       render_half( edge+2, edge+1, edge[2].e_deltay,   edge[2].e_ypos );
  372.       }
  373.     }
  374.  
  375.   HARDWARE_RESTORE();
  376.   }
  377.  
  378.  
  379.