|
|
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);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.