File:  [OS/2 SDKs] / pmsdk / samples / shapes / sdraw.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 12:28:12 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: pmsdk-1988, HEAD
Microsoft OS/2 SDK PM 08-08-1988

/*--------------------------------------------------------*\
    SDRAW.C - 3D to 2D transformations and draw routines
\*--------------------------------------------------------*/

#include <stddef.h>
#include <os2.h>
#include <math.h>
#include "sstruct.h"

extern SHAPE rgshape [];

POINT3D pt3FillBuffer [MAX_POINTS];

extern INT yPelAdjust;


/*
 *  ScalePoint:
 *	- replace destination point by source point after scaling
 */

VOID ScalePoint (
    POINT3D *ppt3Source,
    POINT3D *ppt3Dest,
    LONG iScale )
    {
    ppt3Dest->x = (INT) ( ((LONG) ppt3Source->x * iScale) >> 6 );
    ppt3Dest->y = (INT) ( ((LONG) ppt3Source->y * iScale) >> 6 );
    ppt3Dest->z = (INT) ( ((LONG) ppt3Source->z * iScale) >> 6 );
    }


/*
 *  ChangeScale:
 *	- to be called after a window changes size
 *	- scales a shape's appropriate variables for the new size
 *	- the centre of the window indicates the new window size
 */

extern VOID ChangeScale (
    PSV psv )
    {
    LONG xScale = psv->ptCentre.x;
    LONG yScale = (psv->ptCentre.y << 10) / yPelAdjust;
    LONG iScale = (xScale > yScale) ? yScale : xScale;
    INIT *pinit = rgshape [psv->iShape].pinit;

    if (iScale < MIN_SCALE)
	{
	/* set lower bound on the scale of a shape */

	iScale = MIN_SCALE;
	}

    /* scale the initial size of the shape */

    ScalePoint (&pinit->pt3SizeMid, &psv->pt3Size, iScale);
    psv->pt3SizeMid = psv->pt3Size;

    /* scale the magnitude of the sinusoidal size change */

    ScalePoint (&pinit->pt3SizeMag, &psv->pt3SizeMag, iScale);

    /* scale the viewing distance */
    /* (so that perspective is independent of window size) */

    psv->zViewDist = pinit->zViewDist * iScale;
    }


/*
 *  lSin:
 *	- fast integer sine function with granularity of 1 degree
 *	- constraint:  -360 <= angle <= 360
 *	- returns the sine of an angle * 16384 (shifted left by 14)
 */

LONG lSin(
    INT iAngle )
    {
    static INT iSinTable [91] =
	{ 0, 286, 572, 857, 1143, 1428, 1713, 1997, 2280, 2563, 2845, 3126,
	3406, 3686, 3964, 4240, 4516, 4790, 5063, 5334, 5604, 5872, 6138,
	6402, 6664, 6924, 7182, 7438, 7692, 7943, 8192, 8438, 8682, 8923,
	9162, 9397, 9630, 9860, 10087, 10311, 10531, 10749, 10963, 11174,
	11381, 11585, 11786, 11982, 12176, 12365, 12551, 12733, 12911, 13085,
	13255, 13421, 13583, 13741, 13894, 14044, 14189, 14330, 14466, 14598,
	14726, 14849, 14968, 15082, 15191, 15296, 15396, 15491, 15582, 15668,
	15749, 15826, 15897, 15964, 16026, 16083, 16135, 16182, 16225, 16262,
	16294, 16322, 16344, 16362, 16374, 16382, 16384 };

    if (iAngle < 0)
	iAngle += 360;

    if (iAngle < 180)
	if (iAngle < 90)
	    return( (LONG) iSinTable [iAngle] );
	else
	    return( (LONG) iSinTable [180 - iAngle] );
    else
	if (iAngle < 270)
	    return( (LONG) -iSinTable [iAngle - 180] );
	else
	    return( (LONG) -iSinTable [360 - iAngle] );
    }


/*
 *  iAngleAdd (macro):
 *	- adds the second angle to first angle
 *	- stores result (which is in range 0 to 359) in the first angle
 */

#define iAngleAdd( iA1,iA2)			    \
    if ( ((iA1) += (iA2)) < 0 ) (iA1) += 360;	    \
    else if ( (iA1) >= 360 )	(iA1) -= 360;


/*
 *  AddToAngle:
 *	- adds the 3D angular velocity to the 3D angle
 */

VOID AddToAngle(
    POINT3D *ppt3Angle,
    POINT3D *ppt3AngVel )
    {
    iAngleAdd (ppt3Angle->x, ppt3AngVel->x)
    iAngleAdd (ppt3Angle->y, ppt3AngVel->y)
    iAngleAdd (ppt3Angle->z, ppt3AngVel->z)
    }


/*
 *  CalcSize:
 *	- calculates the new size for sinusoidal size variation
 *	  ie.  <size> = <middle> + <magnitude> * SIN (<angle>)
 */

VOID CalcSize(
    POINT3D *ppt3Size,
    POINT3D *ppt3SizeMid,
    POINT3D *ppt3SizeMag,
    POINT3D *ppt3SizeAng )
    {
    ppt3Size->x = ppt3SizeMid->x +
	    (INT) (ppt3SizeMag->x * lSin( ppt3SizeAng->x ) >> 14);
    ppt3Size->y = ppt3SizeMid->y +
	    (INT) (ppt3SizeMag->y * lSin( ppt3SizeAng->y ) >> 14);
    ppt3Size->z = ppt3SizeMid->z +
	    (INT) (ppt3SizeMag->z * lSin( ppt3SizeAng->z ) >> 14);
    }


/*
 *  CalcPoints:
 *	- transform the 3D points to 2D points
 *	- transformation includes:
 *	    a) scaling points
 *	    b) rotating points around x/y/z axis
 *	    c) perspective transformation
 *	    d) translating to centre of window
 */

VOID CalcPoints (
    PSV psv,
    INT cPoints,
    POINT3D rgpt3 [],
    POINTL rgpt [] )
    {
    LONG sinx = lSin( psv->pt3Angle.x );
    LONG cosx = lSin((psv->pt3Angle.x + 90) % 360 );
    LONG siny = lSin( psv->pt3Angle.y );
    LONG cosy = lSin((psv->pt3Angle.y + 90) % 360 );
    LONG sinz = lSin( psv->pt3Angle.z );
    LONG cosz = lSin((psv->pt3Angle.z + 90) % 360 );
    LONG x, y, z, x2, y2, z2;
    LONG z0 = psv->zViewDist;
    INT i;
    LONG xSize = psv->pt3Size.x;
    LONG ySize = psv->pt3Size.y;
    LONG zSize = psv->pt3Size.z;

    for( i=0; i < cPoints; i++ )
	{
	/* scale points relative to x/y/z axis of shape */

	x = (LONG) rgpt3 [i].x * xSize;
	y = (LONG) rgpt3 [i].y * ySize;
	z = (LONG) rgpt3 [i].z * zSize;

	/* rotate around X axis */

	y2 = ( y * cosx - z * sinx ) >> 14;
	z2 = ( z * cosx + y * sinx ) >> 14;

	/* rotate around Y axis */

	z  = ( z2 * cosy - x * siny ) >> 14;
	x2 = ( x * cosy + z2 * siny ) >> 14;

	/* rotate around Z axis */

	x  = ( x2 * cosz - y2 * sinz ) >> 14;
	y  = ( y2 * cosz + x2 * sinz ) >> 14;

	if (psv->fShaded)
	    {
	    /* store points used for shading */
	    /* (before perspective - light source is at infinity) */

	    pt3FillBuffer [i].x = (INT) (x >> 8);
	    pt3FillBuffer [i].y = (INT) (y >> 8);
	    pt3FillBuffer [i].z = (INT) (z >> 8);
	    }

	if (psv->fPerspective)
	    {
	    /* avoid overflow when doing perspective */

	    x = (x * (z0 >> 6)) / ((z0 - z) >> 6);
	    y = (y * (z0 >> 6)) / ((z0 - z) >> 6);
	    }

	/* store translated points in 2D table */
	/* note: points must be rescaled to fit in window */

	rgpt [i].x = (x >> 8) + psv->ptCentre.x;
	rgpt [i].y = (( (y >> 8) * yPelAdjust ) >> 10) + psv->ptCentre.y;
	}
    }


/*
 *  DrawLines:
 *	- draws a shape using the lines of the object (clipping off)
 */

VOID DrawLines (
    HPS hps,
    LINE rgline [],
    POINTL rgpt [] )
    {
    INT i;
    INT iPoint1, iPoint2 = -1;

    for( i=0; (iPoint1 = rgline [i].p1) >= 0; i++ )
	{
	if (iPoint1 != iPoint2)
	    {
	    GpiSetCurrentPosition( hps, &rgpt [iPoint1] );
	    }
	iPoint2 = rgline [i].p2;
	GpiLine( hps, &rgpt [iPoint2] );
	}
    }


/*
 *  DrawFaces:
 *	- draws a shape using the faces of the object (clipping on)
 */

VOID DrawFaces (
    HPS hps,
    FACE rgface [],
    POINTL rgpt [] )
    {
    INT i = 0;
    INT iEndFace, cSpread;
    INT iPoint1, iPoint2, iPoint3;
    LONG xV1, yV1, xV2, yV2;
    LONG zNormal;

    iPoint1 = rgface [i++];
    do
	{
	/* get 3 points spread out on surface */

	iEndFace = i;
	while (rgface [iEndFace++] >= 0)
	    ;
	cSpread = iEndFace - i + 1;

	if (cSpread > 5)
	    {
	    cSpread /= 3;
	    iPoint2 = rgface [i - 1 + cSpread];
	    iPoint3 = rgface [i - 1 + 2 * cSpread];
	    }
	else
	    {
	    iPoint2 = rgface [i];
	    iPoint3 = rgface [i + 1];
	    }

	/* calculate z coordinate of normal (using 3 points on surface) */

	xV1 = rgpt [iPoint3].x - rgpt [iPoint2].x;
	yV1 = rgpt [iPoint3].y - rgpt [iPoint2].y;
	xV2 = rgpt [iPoint1].x - rgpt [iPoint2].x;
	yV2 = rgpt [iPoint1].y - rgpt [iPoint2].y;
	zNormal = xV1 * yV2 - yV1 * xV2;

	if (zNormal > 0)
	    {
	    /* face is pointing towards the user */

	    /* draw the perimeter of the face */

	    GpiSetCurrentPosition( hps, &rgpt [iPoint1] );
	    while (( iPoint2 = rgface [i++] ) >= 0)
		GpiLine( hps, &rgpt [iPoint2] );
	    GpiLine( hps, &rgpt [iPoint1] );

	    while (iPoint2 == -1)
		{
		/* draw a pattern associated with the face */

		iPoint1 = rgface [i++];
		GpiSetCurrentPosition( hps, &rgpt [iPoint1] );
		while ((iPoint2 = rgface [i++] ) >= 0)
		    GpiLine( hps, &rgpt [iPoint2] );
		}
	    }
	else
	    /* face is not visible - skip over it */

	    while (rgface [i++] >= -1)
		;
	}
    while (( iPoint1 = rgface [i++] ) >= 0);
    }


/*
 *  ChangeShapePosition:
 *	- calculate 2D points for new position
 *	- blank out old position
 *	- draw in new position
 */

extern VOID ChangeShapePosition (
    PSV psv )
    {
    HPS hps;
    POINTL *pptTemp;
    SHAPE *pshape = &rgshape [psv->iShape];
    OBJECT *pobject = pshape->pobject;

    if (psv->fVarySize)
	{
	/* vary the size */

	AddToAngle (&psv->pt3SizeAng, &pshape->pinit->pt3SizeVel );
	CalcSize (&psv->pt3Size, &psv->pt3SizeMid,
	    &psv->pt3SizeMag, &psv->pt3SizeAng );
	}

    /* rotate */

    AddToAngle (&psv->pt3Angle, &psv->pt3AngVel );

    /* calculate new position */

    CalcPoints (psv, pobject->cPoints, pobject->rgpt3, psv->rgptNew );

    hps = psv->hpsClient;

    /* blank out old position */

    if (psv->fErased)
	psv->fErased = FALSE;
    else
	{
	GpiSetColor (hps, CLR_WHITE);
	if (psv->fClipping)
	    DrawFaces (hps, pobject->rgface, psv->rgptOld );
	else
	    DrawLines (hps, pobject->rgline, psv->rgptOld );
	}

    /* draw at new position */

    GpiSetColor (hps, CLR_BLACK);
    if (psv->fClipping)
	DrawFaces (hps, pobject->rgface, psv->rgptNew );
    else
	DrawLines (hps, pobject->rgline, psv->rgptNew );

    /* swap 2D points buffers - new becomes old */

    pptTemp = psv->rgptOld;
    psv->rgptOld = psv->rgptNew;
    psv->rgptNew = pptTemp;
    }


VOID DrawAndFill (
    PSV psv )
    {
    HPS hps = psv->hpsClient;
    OBJECT *pobject = rgshape [psv->iShape].pobject;
    FACE *rgface = pobject->rgface;
    POINTL *rgpt = psv->rgptNew;
    INT i = 0;
    INT iEndFace, cSpread;
    INT iPoint1, iPoint2, iPoint3;
    LONG xV1, yV1, zV1, xV2, yV2, zV2;
    LONG iDiv;
    LONG xN, yN, zN;
    LONG zNormal;
    LONG sinx = lSin( psv->pt2LightAng.x );
    LONG cosx = lSin((psv->pt2LightAng.x + 90) % 360 );
    LONG siny = lSin( psv->pt2LightAng.y );
    LONG cosy = lSin((psv->pt2LightAng.y + 90) % 360 );
    POINTL pt;

    CalcPoints (psv, pobject->cPoints, pobject->rgpt3, rgpt );

    GpiSavePS (hps);
    GpiCreateLogColorTable (hps, LCOL_RESET, LCOLF_RGB, 0L, 0L, NULL);

    iPoint1 = rgface [i++];
    do
	{
	/* get 3 points spread out on surface */

	iEndFace = i;
	while (rgface [iEndFace++] >= 0)
	    ;
	cSpread = iEndFace - i + 1;

	if (cSpread > 5)
	    {
	    cSpread /= 3;
	    iPoint2 = rgface [i - 1 + cSpread];
	    iPoint3 = rgface [i - 1 + 2 * cSpread];
	    }
	else
	    {
	    iPoint2 = rgface [i];
	    iPoint3 = rgface [i + 1];
	    }

	/* calculate z coordinate of normal (using 3 points on surface) */

	xV1 = rgpt [iPoint3].x - rgpt [iPoint2].x;
	yV1 = rgpt [iPoint3].y - rgpt [iPoint2].y;
	xV2 = rgpt [iPoint1].x - rgpt [iPoint2].x;
	yV2 = rgpt [iPoint1].y - rgpt [iPoint2].y;
	zNormal = xV1 * yV2 - yV1 * xV2;

	if (zNormal > 0)
	    {
	    /* face is pointing towards the user */

	    /* normalize vectors */

	    xV1 = pt3FillBuffer [iPoint3].x - pt3FillBuffer [iPoint2].x;
	    yV1 = pt3FillBuffer [iPoint3].y - pt3FillBuffer [iPoint2].y;
	    zV1 = pt3FillBuffer [iPoint3].z - pt3FillBuffer [iPoint2].z;

	    xV2 = pt3FillBuffer [iPoint1].x - pt3FillBuffer [iPoint2].x;
	    yV2 = pt3FillBuffer [iPoint1].y - pt3FillBuffer [iPoint2].y;
	    zV2 = pt3FillBuffer [iPoint1].z - pt3FillBuffer [iPoint2].z;

	    iDiv = (LONG) sqrt ((double) (xV1 * xV1 + yV1 * yV1 + zV1 * zV1) );
	    xV1 = (xV1 << 10) / iDiv;
	    yV1 = (yV1 << 10) / iDiv;
	    zV1 = (zV1 << 10) / iDiv;

	    iDiv = (LONG) sqrt ((double) (xV2 * xV2 + yV2 * yV2 + zV2 * zV2) );
	    xV2 = (xV2 << 10) / iDiv;
	    yV2 = (yV2 << 10) / iDiv;
	    zV2 = (zV2 << 10) / iDiv;

	    /* calculate normal to surface */

	    xN = (yV1 * zV2 - zV1 * yV2) >> 10;
	    yN = (zV1 * xV2 - xV1 * zV2) >> 10;
	    zN = (xV1 * yV2 - yV1 * xV2) >> 10;

	    /* rotate normal around X and Y axes */

	    zN = ( zN * cosx + xN * sinx ) >> 14;
	    zN = ( zN * cosy + yN * siny ) >> 14;

	    /* change range of zNormal from [0,1023] to [128,255] */

	    zNormal = zN >> 3;
	    if (zNormal < 0)
		zNormal = 0;
	    else if (zNormal > 127)
		zNormal = 127;
	    zNormal += 128;

	    GpiSetColor (hps, zNormal);

	    /* draw the perimeter of the face */

	    GpiBeginArea (hps, BA_NOBOUNDARY | BA_ALTERNATE);

	    GpiSetCurrentPosition( hps, &rgpt [iPoint1] );
	    while (( iPoint2 = rgface [i++] ) >= 0)
		GpiLine( hps, &rgpt [iPoint2] );
	    GpiLine( hps, &rgpt [iPoint1] );

	    GpiEndArea (hps);

	    GpiSetColor (hps, zNormal << 16 );

	    while (iPoint2 == -1)
		{
		/* draw a pattern associated with the face */

		iPoint1 = rgface [i++];
		GpiSetCurrentPosition( hps, &rgpt [iPoint1] );
		while ((iPoint2 = rgface [i++] ) >= 0)
		    GpiLine( hps, &rgpt [iPoint2] );
		}
	    }
	else
	    /* face is not visible - skip over it */

	    while (rgface [i++] >= -1)
		;
	}
    while (( iPoint1 = rgface [i++] ) >= 0);

    GpiRestorePS (hps, -1L);
    }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.