home *** CD-ROM | disk | FTP | other *** search
- /* Name : stereo.c
- *
- * Notes: No double buffer is done in this program, so an extra
- * WaitTOF() is needed to prevent the display from displaying
- * graphics before they are drawn, that is, it prevents the
- * top half of the screen from "disappearing." :)
- *
- * $Log$
- */
- /******************************************************************************/
-
- #include <stdlib.h>
- #include <string.h>
- #include <exec/types.h>
- #include <exec/memory.h>
-
- #include <intuition/intuition.h>
- #include <intuition/screens.h>
-
- #include <clib/exec_protos.h>
- #include <clib/intuition_protos.h>
- #include <clib/graphics_protos.h>
- #include <clib/dos_protos.h>
-
- /******************************************************************************/
-
- /* This is the general 8:8 fixed point data type. With this data type, 1.0
- * is represented by 0x0100. Because of this, multiplication and division will
- * introduce a ``magnitude error''. For example, 1.0 * 1.0 = 1.0, but 0x100 *
- * 0x100 = 0x10000. To correct this, the result of multiplication must be
- * shifted to the right by 8 bits, and the result of a division must be
- * shifted to the left by 8 bits. */
- typedef WORD Fixed;
-
-
- /* This is a 2D point and is usually considered to be in device coordinates. */
- typedef struct
- {
- WORD x;
- WORD y;
- } Point2D;
-
-
- /* This is a 3D point and is always in world/local coordinates. */
- typedef struct
- {
- Fixed x;
- Fixed y;
- Fixed z;
- } Point3D;
-
-
- /* This structure models one surface (polygon). It consists
- * of a pointer to the next polygon, the number of points for
- * itself, and a list of points. This list is NOT actual points,
- * but rather the indexes of the points in its objects point
- * list. */
- typedef struct
- {
- APTR * Next;
-
- WORD NumPoints;
- WORD * Points;
- } Surface;
-
-
- /* The object structure consists of two main parts. The first
- * is the list of points. This consists of the number of points
- * followed by a pointer to the array of points (local coords).
- * The second part is a pointer to a linked list of surfaces.
- * (See above). */
- typedef struct
- {
- WORD NumPoints;
- Point3D * Points;
-
- Surface * Surfaces;
- } Object3D;
-
-
- /******************************************************************************/
-
- void Rotate( Point3D * input, Point3D * World, Fixed XAngle, Fixed YAngle,
- Fixed ZAngle, WORD NumPoints );
- void Project( Point3D * world, Point2D * view,
- Fixed XCent, Fixed YCent, Fixed ZCent, WORD NumPoints );
- void Render( struct RastPort * rp, Object3D * obj, Point2D * points,
- BOOL hsr, UBYTE Pen );
- BOOL IsSurfaceVisible( Surface * surf, Point2D * points );
-
- /******************************************************************************/
-
- /* To generate sin() and cos(), I just use a 256+64 entry look-up
- * table that contains the values of sine from 0 to 360+90 degrees. */
- extern const Fixed SineTable[];
- #define DEG_90 (64)
-
- /******************************************************************************/
-
- /* This file contains the defs' for the objects */
- #include "objects.h"
-
- /******************************************************************************/
-
- #define LEFT_COLOR 1 /* Left eye is color01. */
- #define RIGHT_COLOR 2 /* Right eye is color02. */
-
- struct ColorSpec colors[] =
- {
- { 0, 0, 0, 0 }, /* Background color. */
- { 1, 8, 0, 0 }, /* Left eye color. */
- { 2, 0, 0, 8 }, /* Right eye color. */
- { 3, 8, 0, 8 }, /* Left and right color.*/
- { -1, 0, 0, 0 } /* End of color list. */
- };
-
- /******************************************************************************/
- /* This is the part that makes this type of routine difficult. The distnace *
- * between a persons eyes varys from person to person. This is the number *
- * that works for me. My eyes are about 328mm from the center of my nose. */
-
- #define EYE_DISTANCE 50 /* From your nose to the middle of your eye. */
-
- /******************************************************************************/
-
- int main()
- {
- struct Screen * scr; /* Our screen */
- struct RastPort * rp; /* RastPort to draw in */
-
- Object3D * Obj; /* Ptr to our object */
- /* These are my angles of rotation about the given axis. */
- Fixed XAngle, YAngle, ZAngle;
- Point3D * World; /* Array of rotated world coordinates. */
- Point2D * View; /* Array of projected screen pts */
- WORD frames; /* Number of frames remaining */
- BOOL hsr; /* This turns hidden surfaces on/off */
-
- /* Open the screen we want to work on. */
- scr = OpenScreenTags( NULL, SA_Width, 320L,
- SA_Height, 200L,
- SA_Type, CUSTOMSCREEN,
- SA_Depth, 2L,
- SA_Colors, colors,
- SA_DisplayID, LORES_KEY,
- SA_ShowTitle, FALSE,
- TAG_END );
- if ( scr == NULL )
- {
- PutStr( "Could not open screen.\n" );
- return( 5 );
- }
-
-
- /* Since I'm not double-buffering, I'll just use my screen's default
- * RastPort to draw in. */
- rp = &(scr->RastPort);
-
-
- /* Allocate memory for the arrays of rotated and projected points. */
- World = (Point3D *)AllocVec( sizeof( Point3D ) * Obj->NumPoints,
- MEMF_ANY|MEMF_CLEAR );
- View = (Point2D *)AllocVec( sizeof( Point2D ) * Obj->NumPoints,
- MEMF_ANY|MEMF_CLEAR );
-
-
- /* Initialize the angles of rotation. */
- XAngle = YAngle = ZAngle = 0L;
-
-
- Obj = &Cylinder;
- hsr = FALSE;
-
- /* For 600 frames (~10 seconds) we'll rotate and re-draw our happy
- * vector object. */
- for ( frames = 300 ; frames-- ; /* empty */ )
- {
- /* Wait until the end of the frame (i.e., the vertical blank) to
- * draw the next frame. */
- WaitTOF();
-
- /* Clear the screen. */
- Move( rp, 0, 0 );
- ClearScreen( rp );
-
-
- /* Rotate the object by the new angles */
- Rotate( Obj->Points, World, XAngle, YAngle, ZAngle, Obj->NumPoints );
-
-
- /* Project for the left eye. */
- Project( World, View, EYE_DISTANCE, 0, 0, Obj->NumPoints );
- Render( rp, Obj, View, hsr, LEFT_COLOR );
-
- /* Project for the right eye. */
- Project( World, View, -EYE_DISTANCE, 0, 0, Obj->NumPoints );
- Render( rp, Obj, View, hsr, RIGHT_COLOR );
-
- /* See the notes above as to why this is here. */
- WaitTOF();
-
- /* Get the new angles and make sure they are in range. */
- XAngle += 1; YAngle += 2; ZAngle += 3;
- XAngle &= 0xff; YAngle &= 0xff; ZAngle &= 0xff;
- }
-
-
- FreeVec( World ); FreeVec( View );
- CloseScreen( scr );
- return( 0 );
- }
-
- /******************************************************************************/
- /* Name : Rotate()
- *
- * Notes: Transforms the given array of points by the specified angles.
- */
-
- void Rotate( Point3D * input, Point3D * World, Fixed XAngle, Fixed YAngle,
- Fixed ZAngle, WORD NumPoints )
- {
- Fixed matrix[3][3]; /* transform matrix */
- /* These are my cos and sin values for the three angles */
- Fixed sinx, cosx, siny, cosy, sinz, cosz;
- Fixed temp; /* temp storage for sub exprs */
-
-
- /* Get all the sine and cosine values. */
- sinx = SineTable[ XAngle ]; cosx = SineTable[ XAngle + DEG_90 ];
- siny = SineTable[ YAngle ]; cosy = SineTable[ YAngle + DEG_90 ];
- sinz = SineTable[ ZAngle ]; cosz = SineTable[ ZAngle + DEG_90 ];
-
- /* My rotation matrix looks like this:
- *
- cos(z)cos(y) sin(z)cos(x)+cos(z)sin(y)sin(x) sin(z)sin(x)-cos(z)sin(y)cos(x)
- -sin(z)cos(y) cos(z)cos(x)-sin(z)sin(y)sin(x) cos(z)sin(x)+sin(z)sin(y)cos(x)
- sin(y) -cos(y)sin(x) cos(y)sin(x)
- *
- * I chose this matrix because it is easy to code and still provides a
- * reasonable modeling transform for this situation.
- */
-
-
- /* Calculate column 1. */
- matrix[0][0] = ( cosz * cosy) >> 8;
- matrix[1][0] = (-sinz * cosy) >> 8;
- matrix[2][0] = siny;
-
- /* Calculate column 2. */
- temp = ( sinx * siny ) >> 8;
- matrix[0][1] = (sinz*cosx + cosz*temp) >> 8;
- matrix[1][1] = (cosz*cosx - sinz*temp) >> 8;
- matrix[2][1] = (-cosy*sinx) >> 8;
-
- /* Calculate column 3. */
- temp = (siny*cosx) >> 8;
- matrix[0][2] = (sinz*sinx - cosz*temp) >> 8;
- matrix[1][2] = (cosz*sinx + sinz*temp) >> 8;
- matrix[2][2] = (cosy*cosx) >> 8;
-
-
- /* Send each input point through the transformation
- * matrix and store the result. */
- for( /* empty */ ; NumPoints-- ; input++, World++ )
- {
- World->x = ( matrix[0][0] * input->x +
- matrix[0][1] * input->y +
- matrix[0][2] * input->z ) >> 8;
- World->y = ( matrix[1][0] * input->x +
- matrix[1][1] * input->y +
- matrix[1][2] * input->z ) >> 8;
- World->z = ( matrix[2][0] * input->x +
- matrix[2][1] * input->y +
- matrix[2][2] * input->z ) >> 8;
- }
-
- return;
- }
-
- /******************************************************************************/
- /* Name : Project()
- *
- * Notes: This function projects an array of 3D points to a 2D array using
- * either perspective or parallel projection.
- */
-
- void Project( Point3D * world, Point2D * view,
- Fixed XCent, Fixed YCent, Fixed ZCent,
- WORD NumPoints )
- {
- while( NumPoints-- )
- {
- Fixed base; /* project by dividing by this. */
-
-
- /* First we need to decide if the Z value of the point should
- * have any bearing on the projection. A base of 1500 is used
- * for two reasons:
- * 1. Prevents divide by 0 and divide by negative numbers
- * 2. Minimizes the difference between parallel and
- * perspective projection.
- *
- * Then the object's Z value is added. This won't effect the
- * projection, but will give an overall depth-cue by scaling
- * the whole object. */
- base = (world->z + 1500) + ZCent;
-
- /* Project the point... */
- view->x = ((LONG)(world->x + XCent) << 8) / base;
- view->y = ((LONG)(world->y + YCent) << 8) / base;
-
- /* ...and transform to screen coordinates. */
- view->x += 160;
- view->y += 100;
-
- view++;
- world++;
- }
- }
-
- /******************************************************************************/
- /* Name : Render()
- *
- * Notes: This function renders the given 3D object, using the specified
- * 2D device coordinates to the given RastPort.
- */
-
- void Render( struct RastPort * rp, Object3D * obj, Point2D * points,
- BOOL hsr, UBYTE Pen )
- {
- WORD x,y; /* coords of the first point this poly */
- Surface * surface; /* ptr to the current surface */
-
-
- SetAPen( rp, Pen );
-
- for ( surface = obj->Surfaces ; surface != NULL ; surface = surface->Next )
- {
- if ( !hsr || IsSurfaceVisible( surface, points ) )
- {
- WORD NumPoints; /* # of points this surface */
- WORD lcv; /* current point for this polygon */
-
-
- /* Fetch the number of points and the first point. */
- NumPoints = surface->NumPoints;
- x = points[ surface->Points[0] - 1 ].x;
- y = points[ surface->Points[0] - 1 ].y;
-
-
- /* Move to the first point to start drawing. */
- Move( rp, x, y );
-
-
- /* For each point in the polygon, draw a line. Note: the
- * point indexes in the polygon structure are stored as
- * base 1, whereas the point array is base 0. */
- for( lcv = 1 ; lcv < NumPoints ; lcv++ )
- {
- Draw( rp, points[ surface->Points[lcv] - 1 ].x,
- points[ surface->Points[lcv] - 1 ].y );
- }
-
-
- /* Connect back to the first point. */
- Draw( rp, x, y );
- }
- }
-
- return;
- }
-
- /******************************************************************************/
- /* Name : IsSurfaceVisible()
- *
- * Notes: This function examines the give surface and determines if it
- * faces toward the viewer. If it does, this function will return
- * true, otherwise it will return false.
- *
- * The algorithm for this routine can be arrived at two different ways.
- * The way that I use goes something like this:
- *
- * If three points are counter-clockwise, then the angle between the
- * two rays they define will be less than 180 degrees. This means that
- * given the following two lines:
- *
- * / A The slope of line BC will be less than the slope
- * / of line AB. If DX1 is the delta X from A to B,
- * / DY1 is the delta Y from A to B, DX2 is the delta
- * / from B to C, and DY2 is the delta Y from B to C,
- * B < then (DY1 / DX1) > (DY2 / DX2) must be true for a
- * \ counter clockwise surface. Cross multiplication
- * \ simplifies this to (DX2 * DY1) > (DX1 * DY2).
- * \ C
- *
- * This same formula can be derived by reducing the cross product formula
- * to only look at the Z values, but I'll leave that as an exercise for
- * the reader. ;^)
- */
-
- BOOL IsSurfaceVisible( Surface * surf, Point2D * points )
- {
- Fixed x1, y1, x2, y2, x3, y3; /* three points on the surface */
- Fixed dx1, dx2, dy1, dy2; /* delta values */
-
-
- /* Fetch the first three points of the surface. */
- x1 = points[ surf->Points[0] - 1 ].x;
- y1 = points[ surf->Points[0] - 1 ].y;
- x2 = points[ surf->Points[1] - 1 ].x;
- y2 = points[ surf->Points[1] - 1 ].y;
- x3 = points[ surf->Points[2] - 1 ].x;
- y3 = points[ surf->Points[2] - 1 ].y;
-
-
- /* Calculate the deltas. */
- dx1 = x1 - x2;
- dx2 = x2 - x3;
- dy1 = y1 - y2;
- dy2 = y2 - y3;
-
-
- /* Do the magic! */
- return( (dx1 * dy2) < (dx2 * dy1) );
- }
-