home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume38
/
tessel
/
part01
/
triangle.c
< prev
Wrap
C/C++ Source or Header
|
1993-06-20
|
15KB
|
379 lines
/*+-----------------------------------------------------------------------+
*| This file contains subroutines used to smooth shade a triangle. |
*| |
*| Author: Michael S. A. Robb Version: 1.5 Date: 15/06/93 |
*+-----------------------------------------------------------------------+
*/
#include <mem.h>
#include "24bit.h"
/*+-----------------------------------------------------------------------+
*| These variables are used for smooth shading. |
*+-----------------------------------------------------------------------+
*/
long hdred, hdgreen, hdblue, hdblend, hdzpos; /* Horizontal increments. */
long red, green, blue, blend, zpos; /* Current colour values. */
int xstart, xend, ypos; /* Current scan line. */
/*+-----------------------------------------------------------------------+
*| These variables are used to sub-divide the triangle. |
*+-----------------------------------------------------------------------+
*/
COORD triangle[3]; /* Triangle vertices. */
EDGE edge[3]; /* Triangle edges. */
/*+-----------------------------------------------------------------------+
*| This macro is used to implement blending between fixed point values. |
*| All values are expected to be in fixed point format. The colours are |
*| blended according to the formula: |
*| |
*| C = BLEND * ( OC - NC ) + NC |
*+-----------------------------------------------------------------------+
*/
#define BLEND_FUNC( FC, OC, NC, COL, BLEND )\
FC.col_rgb.COL = (((BLEND*(OC.col_rgb.COL-NC.col_rgb.COL))>>FIXED_POINT )\
+ NC.col_rgb.COL)
#define BLEND_PIXEL( F, O, N, B )\
{ BLEND_FUNC( F, O, N, col_red, B );\
BLEND_FUNC( F, O, N, col_green, B );\
BLEND_FUNC( F, O, N, col_blue, B );\
F.col_rgb.col_overlay = O.col_rgb.col_overlay; }
/*+-----------------------------------------------------------------------+
*| This macro is used to convert a set of Red/Green/Blue/Overlay |
*| components into a single 32-bit integer. |
*| |
*| Note that this this macro is hardware specific to the graphics |
*| board. |
*| |
*| Each pixel is represented by the following format: |
*| |
*| 31 MSB 24 23 16 15 8 7 LSB 0 |
*| +---------+---------+-------------------+ |
*| | Red | Green | Overlay | Blue | |
*| +---------+---------+---------+---------+ |
*+-----------------------------------------------------------------------+
*/
#define CONVERT_RGB( R, G, B )\
( ( ( (R)&0x00FF0000L ) <<8 )+\
( ( (G)&0x00FF0000L ) )+\
( ( (B)&0x00FF0000L ) >>16 ) )
/*+-----------------------------------------------------------------------+
*| This subroutine is used to render a horizontal line using Gouraud |
*| shading. |
*| |
*| Uses the following variables: |
*| |
*| xstart = X pixel start coordinate |
*| xend = X pixel end coordinate |
*| ypos = Y pixel coordinate |
*| red = Current value of red component |
*| green = Current value of green component |
*| blue = Current value of blue component |
*| blend = Current value of blending component |
*| hdred = Incremental for red component |
*| hdgreen = Incremental for green component. |
*| hdblue = Incremental for blue component. |
*| blend = Incremental for blending component. |
*+-----------------------------------------------------------------------+
*/
void render_horizontal_line()
{
COLOUR32 nc; /* New colour */
COLOUR32 oc; /* Old colour */
COLOUR32 fc; /* Final colour */
ADDRESS addr = ADDRESS_PIXEL( xstart, ypos );
ADDRESS zbuffer = ADDRESS_ZBUFFER( xstart, ypos );
ZBUFFER zval;
while ( xstart++ < xend ) /* For each pixel in row */
{
nc.col_value = CONVERT_RGB( red, green, blue ); /* Convert to pixel. */
READ_PIXEL( addr, oc ); /* Read old pixel. */
READ_ZBUFFER( zbuffer, zval ); /* Read old Z-buffer value. */
if ( zpos<=zval.zbuf_value && zpos >= 0 ) /* Test Z-buffer. */
{
zval.zbuf_value = zpos;
BLEND_PIXEL( fc, oc, nc, blend ); /* Blend colours. */
WRITE_PIXEL( addr, fc ); /* Write new pixel. */
WRITE_ZBUFFER( zbuffer, zval ); /* Write new Z-buffer value. */
}
red += hdred; /* Add incrementals. */
green += hdgreen;
blue += hdblue;
blend += hdblend;
zpos += hdzpos;
addr += PIXEL_SIZE; /* Update Addresses. */
zbuffer += ZBUFFER_SIZE;
}
}
/*+-----------------------------------------------------------------------+
*| This macro is used to calculate the size of the increments used when |
*| moving vertically down an edge vector. |
*+-----------------------------------------------------------------------+
*/
#define EDGE_DIFF( F, D )\
( ( ( fin->F - strt->F ) << FIXED_POINT ) / (D) )
/*+-----------------------------------------------------------------------+
*| This subroutine is used to initialise a vector given the start and |
*| finishing coordinates. |
*+-----------------------------------------------------------------------+
*/
void edge_init( vec, strt, fin )
EDGE *vec;
COORD *strt;
COORD *fin;
{
long delta_y = fin -> c_ypos - strt -> c_ypos;
vec -> e_deltay = delta_y;
vec -> e_xpos = strt -> c_xpos << FIXED_POINT;
vec -> e_ypos = strt -> c_ypos;
vec -> e_zpos = strt -> c_zpos << FIXED_POINT;
vec -> e_red = strt -> c_red << FIXED_POINT;
vec -> e_green = strt -> c_green << FIXED_POINT;
vec -> e_blue = strt -> c_blue << FIXED_POINT;
vec -> e_blend = (strt -> c_blend << FIXED_POINT) / MAX_BLEND;
if ( delta_y != 0 )
{
vec -> e_dxpos = EDGE_DIFF( c_xpos, delta_y );
vec -> e_dzpos = EDGE_DIFF( c_zpos, delta_y );
vec -> e_dred = EDGE_DIFF( c_red, delta_y );
vec -> e_dgreen = EDGE_DIFF( c_green, delta_y );
vec -> e_dblue = EDGE_DIFF( c_blue, delta_y );
vec -> e_dblend = EDGE_DIFF( c_blend, (MAX_BLEND * delta_y) );
}
else
{
vec -> e_dxpos =
vec -> e_dzpos =
vec -> e_dred =
vec -> e_dgreen =
vec -> e_dblue =
vec -> e_dblend = 0;
}
}
/*+-----------------------------------------------------------------------+
*| This subroutine is used to update the vector after every scan-line. |
*+-----------------------------------------------------------------------+
*/
void edge_update( vec )
EDGE *vec;
{
vec -> e_xpos += vec -> e_dxpos;
vec -> e_zpos += vec -> e_dzpos;
vec -> e_red += vec -> e_dred;
vec -> e_green += vec -> e_dgreen;
vec -> e_blue += vec -> e_dblue;
vec -> e_blend += vec -> e_dblend;
}
/*+-----------------------------------------------------------------------+
*| This macro is used to calculate the size of the increments used when |
*| moving horizontally across a scan-line. |
*+-----------------------------------------------------------------------+
*/
#define HORIZ_INCREMENT( F )\
((eright -> F - eleft -> F ) / delta_x )
/*+-----------------------------------------------------------------------+
*| This subroutine is used to render a sub-triangle given the vertex |
*| coordinate and the two other left and right hand vertices. |
*+-----------------------------------------------------------------------+
*/
void render_half( eleft, eright, delta_y, ystart )
EDGE *eleft, *eright;
long delta_y, ystart;
{
long delta_x;
ypos = ystart; /* Initial Y coordinate. */
while ( delta_y-- >= 0 ) /* For every scan line. */
{
xstart = eleft ->e_xpos>>FIXED_POINT; /* Starting X coordinate. */
xend = eright->e_xpos>>FIXED_POINT; /* Finishing X coordinate. */
delta_x = xend - xstart; /* Width of scan-line. */
zpos = eleft -> e_zpos;
red = eleft -> e_red; /* Iniitial colour values. */
green = eleft -> e_green;
blue = eleft -> e_blue;
blend = eleft -> e_blend;
if ( delta_x != 0 ) /* Any change in X coordinate? */
{
hdzpos = HORIZ_INCREMENT( e_zpos ); /* Yes, so get incrementals. */
hdred = HORIZ_INCREMENT( e_red );
hdgreen = HORIZ_INCREMENT( e_green );
hdblue = HORIZ_INCREMENT( e_blue );
hdblend = HORIZ_INCREMENT( e_blend );
}
render_horizontal_line(); /* Then render the line. */
edge_update( eleft ); /* Update left edge vector. */
edge_update( eright ); /* Update right edge vector. */
ypos++; /* Update Y coordinate. */
}
}
/*+-----------------------------------------------------------------------+
*| This macro is used to perform a comparison and swap (if required) on |
*| the two pairs of coordinates. |
*+-----------------------------------------------------------------------+
*/
#define COMPARE_VERTICES( X, A, B, F, T )\
if ((X)[(A)].F > (X)[(B)].F )\
{\
(T) = (X)[(A)];\
(X)[(A)] = (X)[(B)];\
(X)[(B)] = (T);\
}
/*+-----------------------------------------------------------------------+
*| This subroutine is used to smooth shade a triangle. It takes an |
*| array of coordinates/colours as a parameter. |
*+-----------------------------------------------------------------------+
*/
void render_triangle( coord )
COORD *coord;
{
COORD swapbuf; /* Used for swapping */
HARDWARE_PREPARE();
memcpy( triangle, coord, 3*sizeof(COORD) ); /* Make copy of coords. */
/*+-------------------------------------------------------------+
*| [1] Sort by Y coordinate so that vertices with the smallest |
*| Y coordinate are at the top of the vertex list. |
*+-------------------------------------------------------------+
*/
COMPARE_VERTICES( triangle, 0, 1, c_ypos, swapbuf );
COMPARE_VERTICES( triangle, 1, 2, c_ypos, swapbuf );
COMPARE_VERTICES( triangle, 0, 1, c_ypos, swapbuf );
/*+----------------------------------------------------------------+
*| [2] Sort by X coordinate so that vertices which have identical |
*| Y coordinates but smaller X coordinates are at the top of |
*| the vertex list. |
*+----------------------------------------------------------------+
*/
if ( triangle[0].c_ypos == triangle[1].c_ypos )
COMPARE_VERTICES( triangle, 0, 1, c_xpos, swapbuf );
if ( triangle[1].c_ypos == triangle[2].c_ypos )
{
COMPARE_VERTICES( triangle, 1, 2, c_xpos, swapbuf );
if ( triangle[0].c_ypos == triangle[1].c_ypos )
COMPARE_VERTICES( triangle, 0, 1, c_xpos, swapbuf );
}
/*+-----------------------------------------------------------------+
*| [3] Initialise three edge vectors by calculating incrementals. |
*+-----------------------------------------------------------------+
*/
edge_init( edge+0, &triangle[0], &triangle[1] ); /* Top edge. */
edge_init( edge+1, &triangle[0], &triangle[2] ); /* Longest edge. */
edge_init( edge+2, &triangle[1], &triangle[2] ); /* Bottom edge. */
/*+-----------------------------------------------------------------+
*| [4.1] The bottom two Y coordinates are identical, so only the |
*| top half of the triangle has to be rendered. |
*+-----------------------------------------------------------------+
*/
if ( triangle[1].c_ypos == triangle[2].c_ypos )
render_half( edge+0, edge+1, edge[0].e_deltay, edge[0].e_ypos );
else
/*+-----------------------------------------------------------------+
*| [4.2] The top two Y coordinates are identical, so only the |
*| bottom half of the triangle has to be rendered. |
*+-----------------------------------------------------------------+
*/
if ( triangle[0].c_ypos == triangle[1].c_ypos )
render_half( edge+1, edge+2, edge[2].e_deltay, edge[2].e_ypos );
else
/*+-----------------------------------------------------------------+
*| [4.3] Both top and bottom half-triangles must be rendered. |
*| However, a check must be performed to determine whether |
*| the longest edge is on the left or right hand side. |
*+-----------------------------------------------------------------+
*/
{
long mult, div, delta;
int new_xpos;
mult = (long) triangle[1].c_ypos - (long) triangle[0].c_ypos;
div = (long) triangle[2].c_ypos - (long) triangle[0].c_ypos;
delta = (long) triangle[2].c_xpos - (long) triangle[0].c_xpos;
new_xpos = (long) triangle[0].c_xpos + (long) (delta * mult) / div;
if ( new_xpos < triangle[1].c_xpos )
{
/*+-------------------------------------------------------------+
*| Render triangle with longest edge on the left hand side. |
*+-------------------------------------------------------------+
*/
render_half( edge+1, edge+0, edge[0].e_deltay-1, edge[0].e_ypos );
render_half( edge+1, edge+2, edge[2].e_deltay, edge[2].e_ypos );
}
else
{
/*+-------------------------------------------------------------+
*| Render triangle with longest edge on the right hand side. |
*+-------------------------------------------------------------+
*/
render_half( edge+0, edge+1, edge[0].e_deltay-1, edge[0].e_ypos );
render_half( edge+2, edge+1, edge[2].e_deltay, edge[2].e_ypos );
}
}
HARDWARE_RESTORE();
}