/* ___________________________ A graphics source file of simple 3D graphics functions. Author: John E. Howland, Ph.D. Department of Computer Science Trinity University 715 Stadium Drive San Antonio, Texas 78212 Voice: (512) 736-7480 Fax: (512) 694-0125 Bitnet: jhowland@trinity.edu Last Change: 930303 fixed bug in x coordinate translation in displayPolyhedron3d */ #include "tools.h" #include #include /*#include */ #include #include /* ______________________________________________________________ constants */ #define PI 3.14159265358979 /* ______________________________________________________________ types and globals */ enum {wireFrame, quickShade}; typedef double Xform3d[4][4]; /* the 3D transform type */ typedef struct Point3d /* the 3D homogeneous point */ { double x, y, z, w; } Point3d, *Point3dPtr, **Point3dHdl; typedef struct Graph3dView /* the 3D graphics viewing parameters */ { CWindowPtr wPtr; /* the color graph port */ GrafPtr oldPort; /* the previous graph pointer */ Point3d vrp; /* the view reference point */ Point3d vpn; /* the view plane normal */ Point3d vup; /* the view up direction */ Point3d cop; /* the center of projection (viewpoint) */ Rect viewport; /* the intersection of the viewing pyramid */ double back; /* the z coordinate of the back clipping plane */ double front; /* the z coordinate of the front clipping plane */ double distance; /* the distance of the cop from the view plane */ Xform3d xform; /* the current transformation */ } Graph3dView, *Graph3dViewPtr, **Graph3dViewHdl; typedef struct Object3dFace /* the 3D object face structure */ { int face; /* the index in the object index vector of the first vertex of face */ int edges; /* the number of edges in face (each face is a closed polygon */ RGBColor faceColor; /* the color of face */ Point3d faceNormal; /* the face normal */ Boolean backFace; /* true if face is not visible from viewpoint */ double minDepth; /* the minimum depth of face */ double maxDepth; /* the maximum depth of face */ } Object3dFace, *Object3dFacePtr, **Object3dFaceHdl; typedef struct Polyhedron3d /* the 3D polyhedron structure */ { int numberVertices; /* the number of vertices */ Point3dPtr vertices; /* a pointer to an array of Point3d */ int numberIndices; /* the number of indices */ int *indices; /* an array of indices */ int numberFaces; /* the number of faces */ Object3dFacePtr faces; /* a pointer to an array of faces */ Xform3d *xform; /* a pointer to the polyhedron position */ } Polyhedron3d, *Polyhedron3dPtr, **Polyhedron3dHdl; typedef struct Picture3d /* the 3D scene structure */ { int numberPolyhedra; /* the number of polyhedra in the scene */ Polyhedron3dPtr poly; /* a pointer to an array of Polyhedron3d */ } Picture3d, *Picture3dPtr, **Picture3dHdl; /* ______________________________________________________________ function prototypes */ CWindowPtr makeDrawingWindow(char *name, int left, int top, int right, int bottom); void disposeDrawingWindow(CWindowPtr wPtr, int sec); Boolean getInteger(int *x); Boolean getDoubleFloat(double *x); void scale3d(double sx, double sy, double sz, Xform3d scaleMatrix); void translate3d(double tx, double ty, double tz, Xform3d transMatrix); void identity3d(Xform3d identity); void rotateX3d(double theta, Xform3d rotateMatrix); void rotateY3d(double theta, Xform3d rotateMatrix); void rotateZ3d(double theta, Xform3d rotateMatrix); void shearZ3d(double x, double y, double z, Xform3d zshear); void copy3dXform(Xform3d dst, Xform3d src); void mult3dXform(Xform3d xform1, Xform3d xform2, Xform3d resultxform); Boolean read3dXform(Xform3d xform); void write3dXform(Xform3d xform); void transform3dObject(int n, Point3d object[], Xform3d xform, Point3d result[]); void copy3dObject(int n, Point3d dst[], Point3d src[]); void pitch(double theta, Graph3dViewPtr viewPtr); void roll(double theta, Graph3dViewPtr viewPtr); void yaw(double theta, Graph3dViewPtr viewPtr); void write3dObject(int n, Point3d object[]); Boolean read3dObject(int *n, Point3d object[]); void write3dPoint(Point3dPtr p); Boolean read3dPoint(Point3dPtr p); void center3dObject(int n, Point3d object[], double *xcenter, double *ycenter, double *zcenter); double dotProduct(Point3dPtr p1, Point3dPtr p2); void scalarProduct(double a, Point3dPtr p, Point3dPtr result); void crossProduct(Point3dPtr p1, Point3dPtr p2, Point3dPtr result); void normalize(Point3dPtr p); void transform3dPoint(Point3dPtr p, Xform3d xform); void subtract3dPoint(Point3dPtr p1, Point3dPtr p2, Point3dPtr result); void add3dPoint(Point3dPtr p1, Point3dPtr p2, Point3dPtr result); void initGraph3dView(Graph3dViewPtr viewPtr); void closeGraph3dView(Graph3dViewPtr viewPtr); Boolean readPolyhedron3d(Polyhedron3dPtr poly); void writePolyhedron3d(Polyhedron3dPtr poly); void writeBox3dFile(double xSize, double ySize, double zSize, double x, double y, double z, RGBColor *color); Boolean makeBox3d(double xSize, double ySize, double zSize, double x, double y, double z, RGBColor *color, Polyhedron3dPtr box); Boolean makeTetrahedron3d(double size, double x, double y, double z, RGBColor *color, Polyhedron3dPtr tetra); Boolean getUnsignedChar(unsigned char *x); Boolean getUnsignedShort(unsigned short *x); void viewing(Graph3dViewPtr viewPtr, Xform3d *v); void displayPolyhedron3d(Graph3dViewPtr viewPtr, Polyhedron3dPtr poly, int render, Boolean erase); void disposePolyhedron3d(Polyhedron3dPtr poly); Boolean readPicture3d(Picture3dPtr pic); void writePicture3d(Picture3dPtr pic); void displayPicture3d(Graph3dViewPtr viewPtr, Picture3dPtr pic, int render, Boolean erase); void disposePicture3d(Picture3dPtr pic); /* ______________________________________________________________ 3D Scene Description File and Data Structures File Structure: In the description below, the following notation is used: type {commentary giving description of item(s)} The ellipsis is used to indicate "and so on in an understood manner". Individual items in the file structure are separated by the usual C standard input/output white-space characters so that the input may be formatted in many different, but convenient, ways. This file format is designed to be easy reading for humans as well as standard I/O. In particular, it should be possible for an object to be read and memory be allocated dynamically for that object. int k {number of objects in the scene} int n {number of vertices in an object} Point3d vertex[n] {the vertex array} int m {number of indices of vertices in all faces of the object} int index[m] {the index array (no indices for closing edges)} int i {number of faces in the object} int face {the index into the object index array for first vertex of this face} int edges {the number of edges in this face} RGBColor {the face color} ... {for a total of i faces} Xform3d xform {the position of the object} ... {for a total of k objects} */ /* ______________________________________________________________ functions */ /* ______________________________________________________________ makeDrawingWindow This function makes a plain (named) drawing window and returns a WindowPtr to that window. left, top, right, bottom are the coordinates (in device units) of the upper left and lower right corners of the window on the main gDevice (Mac II's support multiple gDevices). You must pass a pointer to a Pascal string for name. */ CWindowPtr makeDrawingWindow(char *name, int left, int top, int right, int bottom) { Rect windowRect; CWindowPtr wPtr; /* set the window rect from thePort(a toolbox global) */ SetRect(&windowRect, left , top , right, bottom); wPtr = (CWindowPtr)NewCWindow((Ptr)0L, &windowRect, (ConstStr255Param)name, (Boolean)TRUE, (short)noGrowDocProc, (WindowPtr)(-1L), (Boolean)FALSE, 0L); SetPort((GrafPtr)wPtr); /* Set the current grafport to be the window */ return wPtr; } /* End of makeDrawingWindow */ /* ______________________________________________________________ disposeDrawingWindow This function disposes the drawing window pointed to by wPtr after sec seconds or a keypress or mouse click, which ever comes first. */ void disposeDrawingWindow(CWindowPtr wPtr, int sec) { long i; EventRecord theEvent; /* Clear the event queue of all events. */ FlushEvents(everyEvent, 0); for(i = TickCount(); ((TickCount() - i) < (60 * sec)) && (!GetNextEvent((short)(mDownMask + keyDownMask), &theEvent));); DisposeWindow((WindowPtr)wPtr); /* Get rid of the window */ } /* End of disposeDrawingWindow */ /* ______________________________________________________________ getInteger This function reads a single integer from standard input. That integer is is then stored in the location in the calling program pointed to by the argument x. If a value is not read for some reason, the function returns FALSE, otherwise it returns TRUE. Arguments: x a pointer to an integer Returns: TRUE if an integer is read FALSE otherwise Side Effects: reads from standard input Problems: none */ Boolean getInteger(int *x) { if(1 == scanf("%d", x)) return TRUE; else return FALSE; } /* End of getInteger */ /* ______________________________________________________________ getDoubleFloat This function reads a single double value from standard input. That double is is then stored in the location in the calling program pointed to by the argument x. If a value is not read for some reason, the function returns FALSE, otherwise it returns TRUE. Arguments: x a pointer to a double Returns: TRUE if a double is read FALSE otherwise Side Effects: reads from standard input Problems: none */ Boolean getDoubleFloat(double *x) { if(1 == scanf("%lf", x)) return TRUE; else return FALSE; } /* End of getDoubleFloat */ /* ______________________________________________________________ scale3d This function returns the 3D scaling matrix given x, y and z scaling factors. */ void scale3d(double sx, double sy, double sz, Xform3d scaleMatrix) { scaleMatrix[0][0] = sx; scaleMatrix[0][1] = 0.0; scaleMatrix[0][2] = 0.0; scaleMatrix[0][3] = 0.0; scaleMatrix[1][0] = 0.0; scaleMatrix[1][1] = sy; scaleMatrix[1][2] = 0.0; scaleMatrix[1][3] = 0.0; scaleMatrix[2][0] = 0.0; scaleMatrix[2][1] = 0.0; scaleMatrix[2][2] = sz; scaleMatrix[2][3] = 0.0; scaleMatrix[3][0] = 0.0; scaleMatrix[3][1] = 0.0; scaleMatrix[3][2] = 0.0; scaleMatrix[3][3] = 1.0; } /* End of scale3d */ /* ______________________________________________________________ translate3d This function returns the 3d translation matrix given x, y and z translation factors. */ void translate3d(double tx, double ty, double tz, Xform3d transMatrix) { transMatrix[0][0] = 1.0; transMatrix[0][1] = 0.0; transMatrix[0][2] = 0.0; transMatrix[0][3] = 0.0; transMatrix[1][0] = 0.0; transMatrix[1][1] = 1.0; transMatrix[1][2] = 0.0; transMatrix[1][3] = 0.0; transMatrix[2][0] = 0.0; transMatrix[2][1] = 0.0; transMatrix[2][2] = 1.0; transMatrix[2][3] = 0.0; transMatrix[3][0] = tx; transMatrix[3][1] = ty; transMatrix[3][2] = tz; transMatrix[3][3] = 1.0; } /* End of translate3d */ /* ______________________________________________________________ identity3d This function returns the 4 by 4 identity matrix. */ void identity3d(Xform3d identity) { identity[0][0] = 1.0; identity[0][1] = 0.0; identity[0][2] = 0.0; identity[0][3] = 0.0; identity[1][0] = 0.0; identity[1][1] = 1.0; identity[1][2] = 0.0; identity[1][3] = 0.0; identity[2][0] = 0.0; identity[2][1] = 0.0; identity[2][2] = 1.0; identity[2][3] = 0.0; identity[3][0] = 0.0; identity[3][1] = 0.0; identity[3][2] = 0.0; identity[3][3] = 1.0; } /* End of identity3d */ /* ______________________________________________________________ rotateX3d This function returns the x axis rotation matrix given an angle in radians. */ void rotateX3d(double theta, Xform3d rotateMatrix) { double sine = sin(theta), cosine = cos(theta); rotateMatrix[0][0] = 1.0; rotateMatrix[0][1] = 0.0; rotateMatrix[0][2] = 0.0; rotateMatrix[0][3] = 0.0; rotateMatrix[1][0] = 0.0; rotateMatrix[1][1] = cosine; rotateMatrix[1][2] = sine; rotateMatrix[1][3] = 0.0; rotateMatrix[2][0] = 0.0; rotateMatrix[2][1] = -sine; rotateMatrix[2][2] = cosine; rotateMatrix[2][3] = 0.0; rotateMatrix[3][0] = 0.0; rotateMatrix[3][1] = 0.0; rotateMatrix[3][2] = 0.0; rotateMatrix[3][3] = 1.0; } /* End of rotateX3d */ /* ______________________________________________________________ rotateY3d This function returns the y axis rotation matrix given an angle in radians. */ void rotateY3d(double theta, Xform3d rotateMatrix) { double sine = sin(theta), cosine = cos(theta); rotateMatrix[0][0] = cosine; rotateMatrix[0][1] = 0.0; rotateMatrix[0][2] = sine; rotateMatrix[0][3] = 0.0; rotateMatrix[1][0] = 0.0; rotateMatrix[1][1] = 1.0; rotateMatrix[1][2] = 0.0; rotateMatrix[1][3] = 0.0; rotateMatrix[2][0] = -sine; rotateMatrix[2][1] = 0.0; rotateMatrix[2][2] = cosine; rotateMatrix[2][3] = 0.0; rotateMatrix[3][0] = 0.0; rotateMatrix[3][1] = 0.0; rotateMatrix[3][2] = 0.0; rotateMatrix[3][3] = 1.0; } /* End of rotateY3d */ /* ______________________________________________________________ rotateZ3d This function returns the z axis rotation matrix given an angle in radians. */ void rotateZ3d(double theta, Xform3d rotateMatrix) { double sine = sin(theta), cosine = cos(theta); rotateMatrix[0][0] = cosine; rotateMatrix[0][1] = sine; rotateMatrix[0][2] = 0.0; rotateMatrix[0][3] = 0.0; rotateMatrix[1][0] = -sine; rotateMatrix[1][1] = cosine; rotateMatrix[1][2] = 0.0; rotateMatrix[1][3] = 0.0; rotateMatrix[2][0] = 0.0; rotateMatrix[2][1] = 0.0; rotateMatrix[2][2] = 1.0; rotateMatrix[2][3] = 0.0; rotateMatrix[3][0] = 0.0; rotateMatrix[3][1] = 0.0; rotateMatrix[3][2] = 0.0; rotateMatrix[3][3] = 1.0; } /* End of rotateZ3d */ /* ______________________________________________________________ shearZ3d This function produces the Z shearing transformation which maps an arbitrary line through the origin and passing through the non-zero point (x, y, z) into the Z axis without changing the z values of points on the line. */ void shearZ3d(double x, double y, double z, Xform3d zshear) { zshear[0][0] = 1.0; zshear[0][1] = 0.0; zshear[0][2] = 0.0; zshear[0][3] = 0.0; zshear[1][0] = 0.0; zshear[1][1] = 1.0; zshear[1][2] = 0.0; zshear[1][3] = 0.0; zshear[2][0] = -x / z; zshear[2][1] = -y / z; zshear[2][2] = 1.0; zshear[2][3] = 0.0; zshear[3][0] = 0.0; zshear[3][1] = 0.0; zshear[3][2] = 0.0; zshear[3][3] = 1.0; } /* End of shearZ3d */ /* ______________________________________________________________ copy3dXform This function copies the src 4 by 4 transformation matrix to the dst 4 by 4 matrix. It is assumed that the storage for the matrices is allocated in the calling routine. */ void copy3dXform(Xform3d dst, Xform3d src) { register int i, j; for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) dst[i][j] = src[i][j]; } /* End of copy3dXform */ /* ______________________________________________________________ mult3dXform This function multiplies two 4 by 4 transformation matricies producing a resulting 4 by 4 transformation. For efficiency, we assume that the last column of the Xform3d is 0 0 0 1 (36 multiplications and 27 additions) */ void mult3dXform(Xform3d xform1, Xform3d xform2, Xform3d resultxform) { Xform3d result; /* row 0 (9 * and 6 +) */ result[0][0] = xform1[0][0] * xform2[0][0] + xform1[0][1] * xform2[1][0] + xform1[0][2] * xform2[2][0]; result[0][1] = xform1[0][0] * xform2[0][1] + xform1[0][1] * xform2[1][1] + xform1[0][2] * xform2[2][1]; result[0][2] = xform1[0][0] * xform2[0][2] + xform1[0][1] * xform2[1][2] + xform1[0][2] * xform2[2][2]; result[0][3] = 0.0; /* row 1 (9 * and 6 +) */ result[1][0] = xform1[1][0] * xform2[0][0] + xform1[1][1] * xform2[1][0] + xform1[1][2] * xform2[2][0]; result[1][1] = xform1[1][0] * xform2[0][1] + xform1[1][1] * xform2[1][1] + xform1[1][2] * xform2[2][1]; result[1][2] = xform1[1][0] * xform2[0][2] + xform1[1][1] * xform2[1][2] + xform1[1][2] * xform2[2][2]; result[1][3] = 0.0; /* row 2 (9 * and 6 +) */ result[2][0] = xform1[2][0] * xform2[0][0] + xform1[2][1] * xform2[1][0] + xform1[2][2] * xform2[2][0]; result[2][1] = xform1[2][0] * xform2[0][1] + xform1[2][1] * xform2[1][1] + xform1[2][2] * xform2[2][1]; result[2][2] = xform1[2][0] * xform2[0][2] + xform1[2][1] * xform2[1][2] + xform1[2][2] * xform2[2][2]; result[2][3] = 0.0; /* row 3 ( 9 * and 9 +) */ result[3][0] = xform1[3][0] * xform2[0][0] + xform1[3][1] * xform2[1][0] + xform1[3][2] * xform2[2][0] + xform2[3][0]; result[3][1] = xform1[3][0] * xform2[0][1] + xform1[3][1] * xform2[1][1] + xform1[3][2] * xform2[2][1] + xform2[3][1]; result[3][2] = xform1[3][0] * xform2[0][2] + xform1[3][1] * xform2[1][2] + xform1[3][2] * xform2[2][2] + xform2[3][2]; result[3][3] = 1.0; /* copy the result */ copy3dXform(resultxform, result); } /* End of mult3dXform */ /* ______________________________________________________________ read3dXform This function reads an Xform. */ Boolean read3dXform(Xform3d xform) { int i; /* a counter */ for(i = 0; i < 4; i++) if(!(getDoubleFloat(&xform[i][0]) && getDoubleFloat(&xform[i][1]) && getDoubleFloat(&xform[i][2]) && getDoubleFloat(&xform[i][3]))) return FALSE; return TRUE; } /* End of read3dXform */ /* ______________________________________________________________ write3dXform This function writes a 3D transformation matrix. It is used primarily for debugging purposes. */ void write3dXform(Xform3d xform) { int i,j; /* two counters */ for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { printf("%lf ",xform[i][j]); } printf("\n"); } printf("\n"); } /* End of write3dXform */ /* ______________________________________________________________ transform3dObject This function multiplies an n array of Point3d by a 4 by 4 transformation matrix producing a resulting n array of Point3d. We assume that the last column of xform is 0 0 0 1. ( 9n multiplications and 9n additions) */ void transform3dObject(int n, Point3d object[], Xform3d xform, Point3d result[]) { register int i; for(i = 0; i < n; i++) /* each row */ { /* column 0 (3 * and 3 +) */ result[i].x = object[i].x * xform[0][0] + object[i].y * xform[1][0] + object[i].z * xform[2][0] + xform[3][0]; /* column 1 (3 * and 3 +) */ result[i].y = object[i].x * xform[0][1] + object[i].y * xform[1][1] + object[i].z * xform[2][1] + xform[3][1]; /* column 2 (3 * and 3 +) */ result[i].z = object[i].x * xform[0][2] + object[i].y * xform[1][2] + object[i].z * xform[2][2] + xform[3][2]; /* column 3 */ result[i].w = object[i].w; } } /* End of transform3dObject */ /* ______________________________________________________________ copy3dObject This function copies a src n array of Point3d to a dst n array of Point3d. It is assumed that storage for the src and dst arrays is allocated in the calling function. */ void copy3dObject(int n, Point3d dst[], Point3d src[]) { register int i; for(i = 0; i < n; i++) /* each row */ dst[i] = src[i]; } /* End of copy3dObject */ /* ______________________________________________________________ pitch This function multiplies the transform associated with the given graph3dView by an X axis rotation and stores the resulting transformation as the new graph3dView xform. */ void pitch(double theta, Graph3dViewPtr viewPtr) { Xform3d rotX; rotateX3d(theta, rotX); mult3dXform((*viewPtr).xform, rotX, (*viewPtr).xform); } /* End of pitch */ /* ______________________________________________________________ roll This function multiplies the transform associated with the given graph3dView by an Z axis rotation and stores the resulting transformation as the new graph3dView xform. */ void roll(double theta, Graph3dViewPtr viewPtr) { Xform3d rotZ; rotateZ3d(theta, rotZ); mult3dXform((*viewPtr).xform, rotZ, (*viewPtr).xform); } /* End of roll */ /* ______________________________________________________________ yaw This function multiplies the transform associated with the given graph3dView by an Y axis rotation and stores the resulting transformation as the new graph3dView xform. */ void yaw(double theta, Graph3dViewPtr viewPtr) { Xform3d rotY; rotateY3d(theta, rotY); mult3dXform((*viewPtr).xform, rotY, (*viewPtr).xform); } /* End of yaw */ /* ______________________________________________________________ write3dObject This function writes an n array of Point3d. */ void write3dObject(int n, Point3d object[]) { int i; /* a counter */ for(i = 0; i < n; i++) printf("%lf %lf %lf %lf \n",object[i].x, object[i].y, object[i].z, object[i].w); printf("\n"); } /* End of write3dObject */ /* ______________________________________________________________ read3dObject This function reads an n array of Point3d. It is assumed that the object consists of the value for n followed by the n rows of data describing each Point3d ( x coord., y coord., z coord. and drawing code ). */ Boolean read3dObject(int *nptr, Point3d object[]) { int i; /* a counter */ if(getInteger(nptr)) /* read n */ { for(i = 0; i < *nptr; i++) if(!(getDoubleFloat(&object[i].x) && getDoubleFloat(&object[i].y) && getDoubleFloat(&object[i].z) && getDoubleFloat(&object[i].w))) return FALSE; return TRUE; } else return FALSE; } /* End of read3dObject */ /* ______________________________________________________________ write3dPoint This function writes a Point3d. */ void write3dPoint(Point3dPtr p) { printf("%lf %lf %lf %lf \n",(*p).x, (*p).y, (*p).z, (*p).w); } /* End of write3dPoint */ /* ______________________________________________________________ read3dPoint This function reads an n array of Point3d. It is assumed that the object consists of the value for n followed by the n rows of data describing each Point3d ( x coord., y coord., z coord. and drawing code ). */ Boolean read3dPoint(Point3dPtr p) { if(!(getDoubleFloat(&(*p).x) && getDoubleFloat(&(*p).y) && getDoubleFloat(&(*p).z) && getDoubleFloat(&(*p).w))) return FALSE; return TRUE; } /* End of read3dPoint */ /* ______________________________________________________________ center3dObject This function computes the center of an n array of Point3d. The center is defined as the center of the smallest rectangular polyhedron which contains the object. */ void center3dObject(int n, Point3d object[], double *xcenter, double *ycenter, double *zcenter) { int i; /* a counter */ double xmax = object[0].x, xmin = object[0].x, ymax = object[0].y, ymin = object[0].y, zmax = object[0].z, zmin = object[0].z; /* max and min values */ for(i = 1; i < n; i++) { if(object[i].x > xmax) xmax = object[i].x; if(object[i].x < xmin) xmin = object[i].x; if(object[i].y > ymax) ymax = object[i].y; if(object[i].y < ymin) ymin = object[i].y; if(object[i].z > zmax) zmax = object[i].z; if(object[i].z < zmin) zmin = object[i].z; } (*xcenter) = (xmax + xmin) / 2.0; (*ycenter) = (ymax + ymin) / 2.0; (*zcenter) = (zmax + zmin) / 2.0; } /* End of center3dObject */ /* ______________________________________________________________ dotProduct This function computes the dot product (inner product) of two Point3d's. The w component of the homogeneous representation of the 3d points is ignored in this calculation */ double dotProduct(Point3dPtr p1, Point3dPtr p2) { return (*p1).x * (*p2).x + (*p1).y * (*p2).y + (*p1).z * (*p2).z; } /* End of dotProduct */ /* ______________________________________________________________ scalarProduct This function computes the scalar product of a double and a Point3d. The w component of the homogeneous representation of the 3d points is ignored in this calculation. */ void scalarProduct(double a, Point3dPtr p, Point3dPtr result) { (*result).x = a * (*p).x; (*result).y = a * (*p).y; (*result).z = a * (*p).z; } /* End of scalarProduct */ /* ______________________________________________________________ crossProduct This function computes the cross product of two Point3d's. The w component of the homogeneous representation of the 3d points is ignored in this calculation. */ void crossProduct(Point3dPtr p1, Point3dPtr p2, Point3dPtr result) { (*result).x = (*p1).y * (*p2).z - (*p1).z * (*p2).y; (*result).y = (*p1).z * (*p2).x - (*p1).x * (*p2).z; (*result).z = (*p1).x * (*p2).y - (*p1).y * (*p2).x; } /* End of crossProduct */ /* ______________________________________________________________ normalize This function normalizes the Point3d, a pointer to which is passed as an argument. The w component of the homogeneous representation of the 3d point is ignored in this calculation. */ void normalize(Point3dPtr p) { double length = sqrt(dotProduct(p, p)); if(length != 0.0) { (*p).x /= length; (*p).y /= length; (*p).z /= length; } } /* End of normalize */ /* ______________________________________________________________ transform3dPoint This function applies an Xform3d to a Point3d, transforming that Point3d. A pointer to a Point3d is passed. The w component of the homogeneous representation of the 3d point is ignored in this calculation. */ void transform3dPoint(Point3dPtr p, Xform3d xform) { Point3d t = *p; (*p).x = t.x * xform[0][0] + t.y * xform[1][0] + t.z * xform[2][0] + xform[3][0]; (*p).y = t.x * xform[0][1] + t.y * xform[1][1] + t.z * xform[2][1] + xform[3][1]; (*p).z = t.x * xform[0][2] + t.y * xform[1][2] + t.z * xform[2][2] + xform[3][2]; } /* End of transform3dPoint */ /* ______________________________________________________________ subtract3dPoint This function subtracts Point3dPtr p2 from Point3dPtr p1 producing Point3dPtr result. The w component of the homogeneous representation of the 3d point is ignored in this calculation. */ void subtract3dPoint(Point3dPtr p1, Point3dPtr p2, Point3dPtr result) { (*result).x = (*p1).x - (*p2).x; (*result).y = (*p1).y - (*p2).y; (*result).z = (*p1).z - (*p2).z; } /* End of subtract3dPoint */ /* ______________________________________________________________ add3dPoint This function adds Point3dPtr p2 to Point3dPtr p1 producing Point3dPtr result. The w component of the homogeneous representation of the 3d point is ignored in this calculation. */ void add3dPoint(Point3dPtr p1, Point3dPtr p2, Point3dPtr result) { (*result).x = (*p1).x + (*p2).x; (*result).y = (*p1).y + (*p2).y; (*result).z = (*p1).z + (*p2).z; } /* End of add3dPoint */ /* ______________________________________________________________ initGraph3dView This function initializes the 3D graphics viewing structure and makes a full screen drawing window. The Graph3dView is initialized with some default viewing parameters. These parameters (except for the wPtr) may be changed before 3D drawing occurs. */ void initGraph3dView(Graph3dViewPtr viewPtr) { GDHandle mainDevice = GetMainDevice(); Rect mainRect = (**mainDevice).gdRect; short width, height; /* set view reference point to origin */ (*viewPtr).vrp.x = 0.0; (*viewPtr).vrp.y = 0.0; (*viewPtr).vrp.z = 0.0; (*viewPtr).vrp.w = 1.0; /* set view plane normal to z axis */ (*viewPtr).vpn.x = 0.0; (*viewPtr).vpn.y = 0.0; (*viewPtr).vpn.z = 1.0; (*viewPtr).vpn.w = 1.0; /* set view up direction to y axis */ (*viewPtr).vup.x = 0.0; (*viewPtr).vup.y = 1.0; (*viewPtr).vup.z = 0.0; (*viewPtr).vup.w = 1.0; /* set view center of projection (viewpoint) */ (*viewPtr).cop.x = 0.0; (*viewPtr).cop.y = 0.0; (*viewPtr).cop.z = 720.0; (*viewPtr).cop.w = 1.0; /* set view Rect */ width = (mainRect.right - mainRect.left - 5) / 2; height = (mainRect.bottom - mainRect.top - 43) / 2; /* order lower left to upper right */ SetRect(&(*viewPtr).viewport, - width, - height, width, height); /* set back and front Z clipping plane values */ (*viewPtr).back = 1000.0; (*viewPtr).front = 0.0; (*viewPtr).distance = (*viewPtr).cop.z - (*viewPtr).vrp.z; GetPort(&(*viewPtr).oldPort); (*viewPtr).wPtr = makeDrawingWindow((char *)"\p3D Graphics View", mainRect.left + 2, mainRect.top + 40, mainRect.right - 3, mainRect.bottom -3); /* set the transformation to be identity */ identity3d((*viewPtr).xform); } /* End of initGraph3dView */ /* ______________________________________________________________ closeGraph3dView This function closes down the 3d graphics view and disposes of the drawing window after the mouse is pressed or a key is pressed. The Graph3dViewPtr used must be the same as the one used in the call to initGraph3dView. */ void closeGraph3dView(Graph3dViewPtr viewPtr) { disposeDrawingWindow((*viewPtr).wPtr, 500); SetPort((*viewPtr).oldPort); } /* End of closeGraph3dView */ /* ______________________________________________________________ readPolyhedron3d This function reads a 3D polyhedron from standard input and allocates storage for the Polyhedron3d structure. If the read operation was successful, TRUE is return; otherwise FALSE is returned. */ Boolean readPolyhedron3d(Polyhedron3dPtr poly) { int i, j; int numberVertices; Point3dPtr vertices; int numberIndices; int *indices; int numberFaces; Object3dFacePtr faces; Xform3d *xform; double xCenter, yCenter, zCenter; /* read and build the vertex array */ if(getInteger(&numberVertices)) { vertices = (Point3dPtr)malloc((size_t)((size_t)numberVertices * (size_t)sizeof(Point3d))); if(NULL == vertices) return FALSE; } else return FALSE; for(i = 0; i < numberVertices; i++) { if(!( getDoubleFloat(&vertices[i].x) && getDoubleFloat(&vertices[i].y) && getDoubleFloat(&vertices[i].z) && getDoubleFloat(&vertices[i].w))) { free(vertices); return FALSE; } } /* read and build the vertex index list */ if(getInteger(&numberIndices)) { indices = (int *)malloc((size_t)((size_t)numberIndices * (size_t)sizeof(int))); if(NULL == indices) { free(vertices); return FALSE; } } else { free(vertices); return FALSE; } for(i = 0; i < numberIndices; i++) if(!getInteger(&indices[i])) { free(vertices); free(indices); return FALSE; } /* read and build the object face array */ if(getInteger(&numberFaces)) { faces = (Object3dFacePtr)malloc((size_t)((size_t)numberFaces * (size_t)sizeof(Object3dFace))); if(NULL == faces) { free(vertices); free(indices); return FALSE; } } else { free(vertices); free(indices); return FALSE; } for(i = 0; i < numberFaces; i++) { if(!( getInteger(&faces[i].face) && getInteger(&faces[i].edges) && getUnsignedShort(&(faces[i].faceColor.red)) && getUnsignedShort(&faces[i].faceColor.green) && getUnsignedShort(&faces[i].faceColor.blue))) { free(vertices); free(indices); free(faces); return FALSE; } } /* read and build the object xform */ xform = (Xform3d *)malloc((size_t)16 * (size_t)sizeof(double)); if(NULL == xform) { free(vertices); free(indices); free(faces); return FALSE; } for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) if(!getDoubleFloat(&(*xform)[i][j])) { free(vertices); free(indices); free(faces); free(xform); return FALSE; } /* setup the Polyhedron3d structure */ (*poly).numberVertices = numberVertices; (*poly).vertices = vertices; (*poly).numberIndices = numberIndices; (*poly).indices = indices; (*poly).numberFaces = numberFaces; (*poly).faces = faces; (*poly).xform = xform; return TRUE; } /* End of readPolyhedron3d */ /* ______________________________________________________________ writePolyhedron3d This function writes a 3D polyhedron on standard output, as a side effect, in a form suitable for reading by the readPolyhedron3d function. */ void writePolyhedron3d(Polyhedron3dPtr poly) { int i; /* the vertices */ printf("\n\t%d\n", (*poly).numberVertices); for(i = 0; i < (*poly).numberVertices; i++) printf("\t\t%lf %lf %lf %lf\n", (*poly).vertices[i].x, (*poly).vertices[i].y, (*poly).vertices[i].z, (*poly).vertices[i].w); /* the polygon vertex index array */ printf("\n\t%d", (*poly).numberIndices); for(i = 0; i < (*poly).numberIndices; i++) { if(0 == i % 4) printf("\n\t\t"); printf("%d ", (*poly).indices[i]); } /* the polyhedron face array */ printf("\n\n\t%d\n", (*poly).numberFaces); for(i = 0; i < (*poly).numberFaces; i++) { printf("\t\t%d %d ", (*poly).faces[i].face, (*poly).faces[i].edges); printf("%u %u %u\n", (*poly).faces[i].faceColor.red, (*poly).faces[i].faceColor.green, (*poly).faces[i].faceColor.blue); } /* the polyhedron positioning transformation */ printf("\n"); for(i = 0; i < 4; i++) printf("\t%lf %lf %lf %lf\n", (*(*poly).xform)[i][0], (*(*poly).xform)[i][1], (*(*poly).xform)[i][2], (*(*poly).xform)[i][3]); } /* End of writePolyhedron3d */ /* ______________________________________________________________ writeBox3dFile This function writes a 3D box polyhedron on standard output, as a side effect, in a form suitable for reading by the readPolyhedron3d function. The size of the box is xSize, ySize, zSize. The center of the box is located at (x, y, z). */ void writeBox3dFile(double xSize, double ySize, double zSize, double x, double y, double z, RGBColor *color) { int index[] = { 0, 1, 2, 3, 0, 4, 5, 1, 4, 7, 6, 5, 3, 2, 6, 7, 0, 3, 7, 4, 1, 5, 6, 2}; Point3d vertex[] = { {0.0, 0.0, 0.0, 1.0}, {0.0, 0.0, 0.0, 1.0}, {0.0, 0.0, 0.0, 1.0}, {0.0, 0.0, 0.0, 1.0}, {0.0, 0.0, 0.0, 1.0}, {0.0, 0.0, 0.0, 1.0}, {0.0, 0.0, 0.0, 1.0}, {0.0, 0.0, 0.0, 1.0}}; Point3d result[8]; int face[] = { 0, 4, 8, 12, 16, 20}; int edges[] = { 4, 4, 4, 4, 4, 4}; Xform3d size, location, center, origin, xform; int i; /* a counter */ double xCenter, yCenter, zCenter; /* compute the vertices of the box */ vertex[4].z = 1.0; vertex[5].z = 1.0; vertex[6].z = 1.0; vertex[7].z = 1.0; vertex[2].x = 1.0; vertex[3].x = 1.0; vertex[6].x = 1.0; vertex[7].x = 1.0; vertex[1].y = 1.0; vertex[2].y = 1.0; vertex[5].y = 1.0; vertex[6].y = 1.0; /* compute the location of the box */ scale3d(xSize, ySize, zSize, size); translate3d(x, y, z, location); center3dObject(6, vertex, &xCenter, &yCenter, &zCenter); translate3d(-xCenter, -yCenter, -zCenter, origin); mult3dXform(origin, size, center); mult3dXform(center, location, xform); /* transform the vertex array so that its center of volume is at origin */ transform3dObject(8, vertex, xform, result); identity3d(xform); /* start the output */ printf("\n\t%d\n", 8); /* 8 vertices in a box polyhedron */ for(i = 0; i < 8; i++) /* the 8 vertices */ printf("\t\t%lf %lf %lf %lf\n", result[i].x, result[i].y, result[i].z, result[i].w); printf("\n\t%d", 24); /* 24 edges in a box polyhedron */ for(i = 0; i < 24; i++) /* the 24 indices */ { if(0 == i % 4) printf("\n\t\t"); printf("%d ", index[i]); } printf("\n\n\t%d\n", 6); /* 6 faces in a box polyhedron */ for(i = 0; i < 6; i++) /* the face attributes */ { printf("\t\t%d %d ", face[i], edges[i]); printf("%u %u %u\n", (*color).red, (*color).green, (*color).blue); } printf("\n"); for(i = 0; i < 4; i++) /* the object positioning transformation */ printf("\t%lf %lf %lf %lf\n", xform[i][0], xform[i][1], xform[i][2], xform[i][3]); } /* End of writeBox3dFile */ /* ______________________________________________________________ makeBox3d This function makes a box polyhedron. The size of the box is xSize, ySize, zSize. The center of the box is located at (x, y, z). */ Boolean makeBox3d(double xSize, double ySize, double zSize, double x, double y, double z, RGBColor *color, Polyhedron3dPtr box) { int *index; Point3dPtr vertex, result; Object3dFacePtr faces; Xform3d size, location, center, origin, *xform; int i; double xCenter, yCenter, zCenter; /* build the vertex index list */ index = (int *)malloc((size_t)((size_t)24 * (size_t)sizeof(int))); if(NULL == index) return FALSE; index[0] = 0; index[1] = 1; index[2] = 2; index[3] = 3; index[4] = 0; index[5] = 4; index[6] = 5; index[7] = 1; index[8] = 4; index[9] = 7; index[10] = 6; index[11] = 5; index[12] = 3; index[13] = 2; index[14] = 6; index[15] = 7; index[16] = 0; index[17] = 3; index[18] = 7; index[19] = 4; index[20] = 1; index[21] = 5; index[22] = 6; index[23] = 2; /* build the vertex array */ vertex = (Point3dPtr)malloc((size_t)((size_t)8 * (size_t)sizeof(Point3d))); if(NULL == vertex) { free(index); return FALSE; } vertex[0].x = 0.0; vertex[0].y = 0.0; vertex[0].z = 0.0; vertex[0].w = 1.0; vertex[1].x = 0.0; vertex[1].y = 0.0; vertex[1].z = 0.0; vertex[1].w = 1.0; vertex[2].x = 0.0; vertex[2].y = 0.0; vertex[2].z = 0.0; vertex[2].w = 1.0; vertex[3].x = 0.0; vertex[3].y = 0.0; vertex[3].z = 0.0; vertex[3].w = 1.0; vertex[4].x = 0.0; vertex[4].y = 0.0; vertex[4].z = 0.0; vertex[4].w = 1.0; vertex[5].x = 0.0; vertex[5].y = 0.0; vertex[5].z = 0.0; vertex[5].w = 1.0; vertex[6].x = 0.0; vertex[6].y = 0.0; vertex[6].z = 0.0; vertex[6].w = 1.0; vertex[7].x = 0.0; vertex[7].y = 0.0; vertex[7].z = 0.0; vertex[7].w = 1.0; vertex[4].z = 1.0; vertex[5].z = 1.0; vertex[6].z = 1.0; vertex[7].z = 1.0; vertex[2].x = 1.0; vertex[3].x = 1.0; vertex[6].x = 1.0; vertex[7].x = 1.0; vertex[1].y = 1.0; vertex[2].y = 1.0; vertex[5].y = 1.0; vertex[6].y = 1.0; /* build the object face array */ faces = (Object3dFacePtr)malloc((size_t)((size_t)6 * (size_t)sizeof(Object3dFace))); if(NULL == faces) { free(index); free(vertex); return FALSE; } faces[0].face = 0; faces[1].face = 4; faces[2].face = 8; faces[3].face = 12; faces[4].face = 16; faces[5].face = 20; faces[0].edges = 4; faces[1].edges = 4; faces[2].edges = 4; faces[3].edges = 4; faces[4].edges = 4; faces[5].edges = 4; for(i = 0; i < 6; i++) faces[i].faceColor = *color; /* build the object xform */ xform = (Xform3d *)malloc((size_t)16 * (size_t)sizeof(double)); if(NULL == xform) { free(index); free(vertex); free(faces); return FALSE; } /* compute the location of the box */ scale3d(xSize, ySize, zSize, size); translate3d(x, y, z, location); center3dObject(6, vertex, &xCenter, &yCenter, &zCenter); translate3d(-xCenter, -yCenter, -zCenter, origin); mult3dXform(origin, size, center); mult3dXform(center, location, *xform); /* build a copy of the transformed vertex array */ result = (Point3dPtr)malloc((size_t)((size_t)8 * (size_t)sizeof(Point3d))); if(NULL == result) { free(index); free(vertex); free(faces); free(xform); return FALSE; } /* transform the vertex array so that its center of volume is at origin */ transform3dObject(8, vertex, *xform, result); /* free extra copy of vertex array */ free(vertex); identity3d(*xform); /* setup the Polyhedron3d structure */ (*box).numberVertices = 8; (*box).vertices = result; (*box).numberIndices = 24; (*box).indices = index; (*box).numberFaces = 6; (*box).faces = faces; (*box).xform = xform; return TRUE; } /* End of makeBox3d */ /* ______________________________________________________________ makeTetrahedron3d This function makes a tetrahedro. The size of the tetrahedron is the size of one of the edges of the tetrahedron. The center of the tetrahedron is located at (x, y, z). */ Boolean makeTetrahedron3d(double size, double x, double y, double z, RGBColor *color, Polyhedron3dPtr tetra) { int *index; Point3dPtr vertex, result; Object3dFacePtr faces; Xform3d scale, location, center, origin, *xform; int i; double xCenter, yCenter, zCenter; /* build the vertex index list */ index = (int *)malloc((size_t)((size_t)12 * (size_t)sizeof(int))); if(NULL == index) return FALSE; index[0] = 0; index[1] = 1; index[2] = 3; index[3] = 1; index[4] = 2; index[5] = 3; index[6] = 2; index[7] = 1; index[8] = 0; index[9] = 0; index[10] = 3; index[11] = 2; /* build the vertex array */ vertex = (Point3dPtr)malloc((size_t)((size_t)4 * (size_t)sizeof(Point3d))); if(NULL == vertex) { free(index); return FALSE; } vertex[0].x = 0.0; vertex[0].y = 0.0; vertex[0].z = 0.0; vertex[0].w = 1.0; vertex[1].x = 0.0; vertex[1].y = 0.0; vertex[1].z = 0.0; vertex[1].w = 1.0; vertex[2].x = 0.0; vertex[2].y = 0.0; vertex[2].z = 0.0; vertex[2].w = 1.0; vertex[3].x = 0.0; vertex[3].y = 0.0; vertex[3].z = 0.0; vertex[3].w = 1.0; vertex[0].z = 1.0; vertex[3].z = 1.0; vertex[1].x = 1.0; vertex[3].x = 1.0; vertex[2].y = 1.0; vertex[3].y = 1.0; /* build the object face array */ faces = (Object3dFacePtr)malloc((size_t)((size_t)4 * (size_t)sizeof(Object3dFace))); if(NULL == faces) { free(index); free(vertex); return FALSE; } faces[0].face = 0; faces[1].face = 3; faces[2].face = 6; faces[3].face = 9; faces[0].edges = 3; faces[1].edges = 3; faces[2].edges = 3; faces[3].edges = 3; for(i = 0; i < 4; i++) faces[i].faceColor = *color; /* build the object xform */ xform = (Xform3d *)malloc((size_t)16 * (size_t)sizeof(double)); if(NULL == xform) { free(index); free(vertex); free(faces); return FALSE; } /* compute the location of the box */ size /= sqrt(2.0); scale3d(size, size, size, scale); translate3d(x, y, z, location); center3dObject(4, vertex, &xCenter, &yCenter, &zCenter); translate3d(-xCenter, -yCenter, -zCenter, origin); mult3dXform(origin, scale, center); mult3dXform(center, location, *xform); /* build a copy of the transformed vertex array */ result = (Point3dPtr)malloc((size_t)((size_t)4 * (size_t)sizeof(Point3d))); if(NULL == result) { free(index); free(vertex); free(faces); free(xform); return FALSE; } /* transform the vertex array so that its center of volume is at origin */ transform3dObject(4, vertex, *xform, result); /* free extra copy of vertex array */ free(vertex); identity3d(*xform); /* setup the Polyhedron3d structure */ (*tetra).numberVertices = 4; (*tetra).vertices = result; (*tetra).numberIndices = 12; (*tetra).indices = index; (*tetra).numberFaces = 4; (*tetra).faces = faces; (*tetra).xform = xform; return TRUE; } /* End of makeTetrahedron3d */ /* ______________________________________________________________ getUnsignedChar This function reads a single integer from standard input. That integer is is then stored in the location in the calling program pointed to by the argument x as an unsigned char. If a value is not read for some reason, the function returns FALSE, otherwise it returns TRUE. Arguments: x a pointer to an integer Returns: TRUE if an integer is read FALSE otherwise Side Effects: reads from standard input Problems: none */ Boolean getUnsignedChar(unsigned char *x) { int temp; if(1 == scanf("%d", &temp)) { *x = (unsigned char)temp; return TRUE; } else return FALSE; } /* End of getUnsignedChar */ /* ______________________________________________________________ getUnsignedShort This function reads a single integer from standard input. That integer is is then stored in the location in the calling program pointed to by the argument x as an unsigned char. If a value is not read for some reason, the function returns FALSE, otherwise it returns TRUE. Arguments: x a pointer to an unsigned short Returns: TRUE if an unsigned short is read FALSE otherwise Side Effects: reads from standard input Problems: none */ Boolean getUnsignedShort(unsigned short *x) { if(1 == scanf("%u", x)) return TRUE; else return FALSE; } /* End of getUnsignedShort */ /* ______________________________________________________________ viewing This function produces the viewing transformation matrix which transforms an abject from world coordinates to eye coordinates.. */ void viewing(Graph3dViewPtr viewPtr, Xform3d *view) { Point3d t, v, u; Xform3d tr, rot; /* project vup on the viewing plane getting the v axis for viewing plane*/ scalarProduct(dotProduct(&((*viewPtr).vpn), &((*viewPtr).vup)), &((*viewPtr).vpn), &t); subtract3dPoint(&((*viewPtr).vup), &t, &v); /* compute the u axis of the viewing plane */ crossProduct(&((*viewPtr).vpn), &v, &u); /* translate the cop + vrp to the origin */ add3dPoint(&((*viewPtr).cop), &((*viewPtr).vrp), &t); translate3d(-t.x, -t.y, -t.z, tr); /* compute the rotation matrix */ identity3d(rot); rot[0][0] = u.x; rot[1][0] = u.y; rot[2][0] = u.z; rot[0][1] = v.x; rot[1][1] = v.y; rot[2][1] = v.z; rot[0][2] = (*viewPtr).vpn.x; rot[1][2] = (*viewPtr).vpn.y; rot[2][2] = (*viewPtr).vpn.z; /* multiply the translation and rotation producing the view transformation */ mult3dXform(tr, rot, *view); } /* End of viewing */ /* ______________________________________________________________ displayPolyhedron3d This function displays a polyhedron in the given Graph3dView. */ void displayPolyhedron3d(Graph3dViewPtr viewPtr, Polyhedron3dPtr poly, int render, Boolean erase) { int i; Point3dPtr vertices; Xform3d temp, temp1, view; PixPatHandle pat; /* compute the viewing transformation */ /* allocate memory for a copy of the vertex array */ vertices = (Point3dPtr)malloc((size_t)((size_t)(*poly).numberVertices * (size_t)sizeof(Point3d))); if(NULL == vertices) { warn("Not enough memory to allocate a copy of the vertex array"); return; } /* calculate the transformed vertices */ viewing(viewPtr, &view); mult3dXform((*(*poly).xform), view, temp); mult3dXform(temp, (*viewPtr).xform, temp1); transform3dObject((*poly).numberVertices, (*poly).vertices, temp1, vertices); if(erase) { int j, firstX, firstY; Point3d normal, p1, p2; PolyHandle polygon; CGrafPtr OldPort; GDHandle OldDevice; GWorldPtr GW; Rect wRect = (*(*viewPtr).wPtr).portRect; GetGWorld(&OldPort, &OldDevice); if (noErr != NewGWorld(&GW, 8, &wRect, (CTabHandle)0L, (GDHandle)0L, (GWorldFlags)0)) { free(vertices); return; } NoPurgePixels(GW->portPixMap); if(LockPixels(GW->portPixMap)) { SetGWorld((CGrafPtr)GW, 0L); EraseRect(&wRect); switch(render) { case wireFrame: /* draw each edge of each face */ for(i = 0; i < (*poly).numberFaces; i++) { polygon = OpenPoly(); for(j = 0; j < (*poly).faces[i].edges; j++) { int x, y; /* get vertex */ p1 = vertices[(*poly).indices[(*poly).faces[i].face + j]]; /* project on viewing plane */ x = ((*viewPtr).distance * p1.x ) / p1.z; y = ((*viewPtr).distance * p1.y ) / p1.z; /* translate to viewport */ x = (*viewPtr).viewport.right + x; y = (*viewPtr).viewport.bottom + y; if(j == 0) { MoveTo(x, y); firstX = x; firstY = y; } else LineTo(x, y); } LineTo(firstX, firstY); ClosePoly(); FramePoly(polygon); KillPoly(polygon); } break; case quickShade: /* calculate each face normal and mark back faces and draw front faces */ for(i = 0; i < (*poly).numberFaces; i++) { subtract3dPoint( &(vertices[(*poly).indices[(*poly).faces[i].face + 1]]), &(vertices[(*poly).indices[(*poly).faces[i].face]]), &p1); subtract3dPoint( &(vertices[(*poly).indices[(*poly).faces[i].face + 2]]), &(vertices[(*poly).indices[(*poly).faces[i].face + 1]]), &p2); crossProduct(&p2, &p1, &normal); (*poly).faces[i].faceNormal = normal; /* is this a back face (we use first vertex to approximate line of view) */ if(dotProduct(&normal, &(vertices[(*poly).indices[(*poly).faces[i].face]])) <= 0.0) { /* set up a PixPatHandle from the face color */ pat = NewPixPat(); MakeRGBPat(pat, &((*poly).faces[i].faceColor)); /* render the polyhedron face */ polygon = OpenPoly(); for(j = 0; j < (*poly).faces[i].edges; j++) { int x, y; /* get vertex */ p1 = vertices[(*poly).indices[(*poly).faces[i].face + j]]; /* project on viewing plane */ x = ((*viewPtr).distance * p1.x ) / p1.z; y = ((*viewPtr).distance * p1.y ) / p1.z; /* translate to viewport */ x = (*viewPtr).viewport.right + x; y = (*viewPtr).viewport.bottom + y; if(j == 0) { MoveTo(x, y); firstX = x; firstY = y; } else LineTo(x, y); } LineTo(firstX, firstY); ClosePoly(); FillCPoly(polygon, pat); FramePoly(polygon); KillPoly(polygon); DisposPixPat(pat); } else (*poly).faces[i].backFace = TRUE; } break; default: break; } SetGWorld(OldPort, OldDevice); HLock((Handle)GW->portPixMap); HLock((Handle)OldPort->portPixMap); CopyBits( *((BitMap **)GW->portPixMap), *((BitMap **)OldPort->portPixMap), &wRect, &wRect, srcCopy, (RgnHandle)0L ); HUnlock((Handle)OldPort->portPixMap); HUnlock((Handle)GW->portPixMap); UnlockPixels(GW->portPixMap); } AllowPurgePixels(GW->portPixMap); DisposeGWorld(GW); } else { int j, firstX, firstY; Point3d normal, p1, p2; PolyHandle polygon; switch(render) { case wireFrame: /* draw each edge of each face */ for(i = 0; i < (*poly).numberFaces; i++) { polygon = OpenPoly(); for(j = 0; j < (*poly).faces[i].edges; j++) { int x, y; /* get vertex */ p1 = vertices[(*poly).indices[(*poly).faces[i].face + j]]; /* project on viewing plane */ x = ((*viewPtr).distance * p1.x ) / p1.z; y = ((*viewPtr).distance * p1.y ) / p1.z; /* translate to viewport */ x = (*viewPtr).viewport.right + x; y = (*viewPtr).viewport.bottom + y; if(j == 0) { MoveTo(x, y); firstX = x; firstY = y; } else LineTo(x, y); } LineTo(firstX, firstY); ClosePoly(); FramePoly(polygon); KillPoly(polygon); } break; case quickShade: /* calculate each face normal and mark back faces and draw front faces */ for(i = 0; i < (*poly).numberFaces; i++) { subtract3dPoint( &(vertices[(*poly).indices[(*poly).faces[i].face + 1]]), &(vertices[(*poly).indices[(*poly).faces[i].face]]), &p1); subtract3dPoint( &(vertices[(*poly).indices[(*poly).faces[i].face + 2]]), &(vertices[(*poly).indices[(*poly).faces[i].face + 1]]), &p2); crossProduct(&p2, &p1, &normal); (*poly).faces[i].faceNormal = normal; /* is this a back face (we use first vertex to approximate line of view) */ if(dotProduct(&normal, &(vertices[(*poly).indices[(*poly).faces[i].face]])) <= 0.0) { /* set up a PixPatHandle from the face color */ pat = NewPixPat(); MakeRGBPat(pat, &((*poly).faces[i].faceColor)); /* render the polyhedron face */ polygon = OpenPoly(); for(j = 0; j < (*poly).faces[i].edges; j++) { int x, y; /* get vertex */ p1 = vertices[(*poly).indices[(*poly).faces[i].face + j]]; /* project on viewing plane */ x = ((*viewPtr).distance * p1.x ) / p1.z; y = ((*viewPtr).distance * p1.y ) / p1.z; /* translate to viewport */ x = (*viewPtr).viewport.right + x; y = (*viewPtr).viewport.bottom + y; if(j == 0) { MoveTo(x, y); firstX = x; firstY = y; } else LineTo(x, y); } LineTo(firstX, firstY); ClosePoly(); FillCPoly(polygon, pat); FramePoly(polygon); KillPoly(polygon); DisposPixPat(pat); } else (*poly).faces[i].backFace = TRUE; } break; default: break; } } free(vertices); } /* End of displayPolyhedron3d */ /* ______________________________________________________________ disposePolyhedron3d This function disposes the dynamic storage associated a 3D polyhedron. It is assumed that the vertices, vertex index array, face array and xform have all been allocated using malloc. */ void disposePolyhedron3d(Polyhedron3dPtr poly) { /* the vertices */ free((*poly).vertices); /* the polygon vertex index array */ free((*poly).indices); /* the polyhedron face array */ free((*poly).faces); /* the polyhedron positioning transformation */ free((*poly).xform); } /* End of disposePolyhedron3d */ /* ______________________________________________________________ readPicture3d This function reads a 3D picture from standard input and allocates storage for the Picture3d structure. If the read operation was successful, TRUE is return; otherwise FALSE is returned. */ Boolean readPicture3d(Picture3dPtr pic) { int i; int numberPolyhedra; Polyhedron3dPtr poly; /* read and build the vertex array */ if(getInteger(&numberPolyhedra)) { poly = (Polyhedron3dPtr)malloc((size_t)((size_t)numberPolyhedra * (size_t)sizeof(Polyhedron3d))); if(NULL == poly) return FALSE; } else return FALSE; for(i = 0; i < numberPolyhedra; i++) { if(!readPolyhedron3d(&poly[i])) { free(poly); return FALSE; } } (*pic).numberPolyhedra = numberPolyhedra; (*pic).poly = poly; return TRUE; } /* End of readPolyhedron3d */ /* ______________________________________________________________ writePicture3d This function writes a 3D picture to standard output. */ void writePicture3d(Picture3dPtr pic) { int i; printf("%d\n", (*pic).numberPolyhedra); for(i = 0; i < (*pic).numberPolyhedra; i++) writePolyhedron3d(&(*pic).poly[i]); } /* End of writePicture3d */ /* ______________________________________________________________ displayPicture3d This function displays a 3D picture. */ void displayPicture3d(Graph3dViewPtr viewPtr, Picture3dPtr pic, int render, Boolean erase) { int i; for(i = 0; i < (*pic).numberPolyhedra; i++) displayPolyhedron3d(viewPtr, &(*pic).poly[i], render, erase); } /* End of displayPicture3d */ /* ______________________________________________________________ disposePicture3d This function disposes of a 3D picture. It is assumed that the dynamic storage in each of the polyhedra have been allocated using malloc. */ void disposePicture3d(Picture3dPtr pic) { int i; for(i = 0; i < (*pic).numberPolyhedra; i++) disposePolyhedron3d(&(*pic).poly[i]); } /* End of disposePicture3d */