home *** CD-ROM | disk | FTP | other *** search
- // THREED.CPP: A three-dimensional viewing package. Displays
- // wire-frame three-dimensional views of objects using perspective
- // projection. Objects are read from files and displayed according
- // to the settings of the from, at, up, and viewing angle.
-
- #include <windows.h>
- #include <stdio.h>
- #include <math.h>
- #include "threed.h"
-
- // Returns the larger of two values
- inline double MaxOf(double val1, double val2) {
- return (val1 > val2) ? val1 : val2;
- }
-
- // The constructor sets up various default values for the
- // camera model
- void TThreeD::TThreeD()
- {
- // Set up the dimensions of the viewing region
- pcb = PCB; pct = PCT;
- pcr = PCR; pcl = PCL;
- a = (pcr - pcl) / (1 + 1); // Set viewport and window
- b = pcl - a * (-1); // mapping variables
- c = (pct - pcb) / (1 + 1);
- d = pcb - c * (-1);
- // Set default values for from, at, and up vectors
- from.X = 1.0; from.Y = 0.0; from.Z = 0.0;
- at.X = 0.0; at.Y = 0.0; at.Z = 0.0;
- up.X = 0.0; up.Y = 0.0; up.Z = 1.0;
- Angle = 60.0 * 0.017453293; // Convert to radians
- }
-
- // High level routine to display a three-dimensional object
- // already read into memory. Sets up the mapping mode and
- // then calls View to display the object.
- void TThreeD::Display(HDC hDC, RECT& rect)
- {
- // Use the isotropic mapping mode so that the X and Y
- // dimensions of equal sizes will appear the same within
- // the window
- SetMapMode(hDC, MM_ISOTROPIC);
-
- // Set the logical coordinates of the window. Normally
- // our logical coordinates will be between 0 and 1, but
- // since we need integer coordinates we'll use 0 to 1000.
- SetWindowExt(hDC, PCR-PCL, PCB-PCT);
-
- // Now set the viewport to use the largest square that fits
- // in the window. The origin defaults to the top left of the
- // viewport.
- if (rect.right <= rect.bottom) {
- SetViewportOrg(hDC, 0, (rect.bottom-rect.right)/2);
- SetViewportExt(hDC, rect.right, rect.right);
- }
- else {
- SetViewportOrg(hDC, (rect.right-rect.bottom)/2,0);
- SetViewportExt(hDC, rect.bottom, rect.bottom);
- }
- SetEye();
- View(hDC); // Display the figure
- }
-
- // Converts clipped world coordinates to the window's coordinates
- void TThreeD::WORLDtoPC(double xw, double yw, POINT& pc)
- {
- pc.x = (int)(a * xw + b);
- pc.y = (int)(c * yw + d);
- }
-
- // Return the minimum and maximum values in the point array
- // for the X, Y, and Z axes
- void TThreeD::MinMax()
- {
- int i;
-
- objxmin = 32000; objymin = 32000; objzmin = 32000;
- objxmax = -32000; objymax = -32000; objzmax = -32000;
- for (i=1; i<=vertices; i++) {
- if (pointarray[i].X > objxmax) objxmax = pointarray[i].X;
- else if (pointarray[i].X < objxmin) objxmin = pointarray[i].X;
- if (pointarray[i].Y > objymax) objymax = pointarray[i].Y;
- else if (pointarray[i].Y < objymin) objymin = pointarray[i].Y;
- if (pointarray[i].Z > objzmax) objzmax = pointarray[i].Z;
- else if (pointarray[i].Z < objzmin) objzmin = pointarray[i].Z;
- }
- }
-
- // Routine to provide a default value for the at point. It
- // is set to the midpoint of the extents of the object.
- void TThreeD::SetAt()
- {
- MinMax();
- at.X = (objxmin+objxmax) / 2.0;
- at.Y = (objymin+objymax) / 2.0;
- at.Z = (objzmin+objzmax) / 2.0;
- }
-
- // Routine that provides a default value for the from point. It
- // is dependent on the at point and the view angle.
- void TThreeD::SetFrom()
- {
- const double WIDTH = 1.8; // Ratio used to determine from point
- // It is based on size of object
- from.X = at.X + (objxmax-objxmin) / 2.0 + WIDTH *
- MaxOf((objzmax-objzmin)/2.0, (objymax-objymin)/2.0);
- from.Y = at.Y;
- from.Z = at.Z;
- }
-
- // There must be a valid object in pointarray before calling this
- // function. It sets up the various variables used in transforming
- // an object from world to eye coordinates.
- void TThreeD::SetEye()
- {
- double amarkmag, tempmag;
- VECTOR temp;
-
- dval = cos(Angle/2.0) / sin(Angle/2.0);
- dist = Subtract(&at, &from);
- amarkmag = Mag(&dist);
- a3 = Divide(&dist, amarkmag);
- temp = Cross(&dist, &up);
- tempmag = Mag(&temp);
- a1 = Divide(&temp, tempmag);
- temp = Cross(&a1, &a3);
- tempmag = Mag(&temp);
- a2 = Divide(&temp, tempmag);
- offsx = -a1.X * from.X - a1.Y * from.Y - a1.Z * from.Z;
- offsy = -a2.X * from.X - a2.Y * from.Y - a2.Z * from.Z;
- offsz = -a3.X * from.X - a3.Y * from.Y - a3.Z * from.Z;
- }
-
- const int NOEDGE = 0x00;
- const int LEFTEDGE = 0x01;
- const int RIGHTEDGE = 0x02;
- const int BOTTOMEDGE = 0x04;
- const int TOPEDGE = 0x08;
-
- // Returns a code specifying which edge in the viewing
- // pyramid was crossed. There may be more than one.
- int TThreeD::Code(double x, double y, double z)
- {
- int c;
-
- c = NOEDGE;
- if (x < -z) c |= LEFTEDGE;
- if (x > z) c |= RIGHTEDGE;
- if (y < -z) c |= BOTTOMEDGE;
- if (y > z) c |= TOPEDGE;
- return(c);
- }
-
- // Clips the line segment in 3D coordinates to the viewing pyramid.
- // The clipped coordinates are returned as screen coordinates
- // in the variables (pc1.x,pc1.y) and (pc2.x,pc2.y).
- void TThreeD::Clip3D(HDC hDC, double x1, double y1, double z1,
- double x2, double y2, double z2, POINT& pc1, POINT& pc2)
- {
- int c, c1, c2;
- double x, y, z, t;
-
- c1 = Code(x1, y1, z1);
- c2 = Code(x2, y2, z2);
- while (c1 != NOEDGE || c2 != NOEDGE) {
- if ((c1 & c2) != NOEDGE) // The line is not in the viewing
- return; // pyramid. Don't draw anything.
- c = c1;
- if (c == NOEDGE) c = c2;
- if ((c & LEFTEDGE) == LEFTEDGE) { // Crosses left edge
- t = (z1 + x1) / ((x1 - x2) - (z2 - z1));
- z = t * (z2 - z1) + z1;
- x = -z;
- y = t * (y2 - y1) + y1;
- }
- else if ((c & RIGHTEDGE) == RIGHTEDGE) {
- // Crosses right edge of the viewing pyramid
- t = (z1 - x1) / ((x2 - x1) - (z2 - z1));
- z = t * (z2 - z1) + z1;
- x = z;
- y = t * (y2 - y1) + y1;
- }
- else if ((c & BOTTOMEDGE) == BOTTOMEDGE) {
- // Crosses bottom edge of the viewing pyramid
- t = (z1 + y1) / ((y1 - y2) - (z2 - z1));
- z = t * (z2 - z1) + z1;
- x = t * (x2 - x1) + x1;
- y = -z;
- }
- else if ((c & TOPEDGE) == TOPEDGE) {
- // Crosses top edge of the viewing pyramid
- t = (z1 - y1) / ((y2 - y1) - (z2 - z1));
- z = t * (z2 - z1) + z1;
- x = t * (x2 - x1) + x1;
- y = z;
- }
- if (c == c1) {
- x1 = x; y1 = y; z1 = z;
- c1 = Code(x, y, z);
- }
- else {
- x2 = x; y2 = y; z2 = z;
- c2 = Code(x, y, z);
- }
- }
- if (z1 != 0) {
- WORLDtoPC(x1/z1, y1/z1, pc1);
- WORLDtoPC(x2/z2, y2/z2, pc2);
- }
- else {
- WORLDtoPC(x1, y1, pc1);
- WORLDtoPC(x2, y2, pc2);
- }
- MoveTo(hDC, pc1.x, pc1.y);
- LineTo(hDC, pc2.x, pc2.y);
- }
-
- // Transform the segment connecting the two vectors into
- // the viewing plane. Clip3D() clips and draws the line if visible.
- void TThreeD::TransformSeg(HDC hDC, VECTOR *v1, VECTOR *v2,
- POINT& pc1, POINT& pc2)
- {
- double x1, y1, z1, x2, y2, z2;
-
- x1 = (v1->X * a1.X + a1.Y * v1->Y + a1.Z * v1->Z + offsx) * dval;
- y1 = (v1->X * a2.X + a2.Y * v1->Y + a2.Z * v1->Z + offsy) * dval;
- z1 = v1->X * a3.X + a3.Y * v1->Y + a3.Z * v1->Z + offsz;
- x2 = (v2->X * a1.X + a1.Y * v2->Y + a1.Z * v2->Z + offsx) * dval;
- y2 = (v2->X * a2.X + a2.Y * v2->Y + a2.Z * v2->Z + offsy) * dval;
- z2 = v2->X * a3.X + a3.Y * v2->Y + a3.Z * v2->Z + offsz;
- Clip3D(hDC, x1, y1, z1, x2, y2, z2, pc1, pc2);
- }
-
- // Increment through the pointarray which contains the vertices of the
- // object and display them as you go. This will draw out the object.
- void TThreeD::View(HDC hDC)
- {
- int i=1, startofside;
- POINT pc1, pc2;
-
- while (i<length) {
- startofside = i; i++;
- while (connect[i] > 0) {
- TransformSeg(hDC, &pointarray[connect[i-1]],
- &pointarray[connect[i]], pc1, pc2);
- i++;
- }
- // Close off the polygon
- TransformSeg(hDC, &pointarray[connect[i-1]],
- &pointarray[connect[startofside]], pc1, pc2);
- // Skip the negative value in the connect array; it'll be
- // used in Chapter 16 to specify the polygon's color.
- i++;
- }
- }
-
- // Read in a file describing a polygon which adheres to the
- // format described in Chapter 14. Returns 1 if file is read
- // successfully; otherwise it returns a negative error flag.
- int TThreeD::Read3DObject(char *FileName)
- {
- int i, SkipNumColors, r, g, b;
- FILE *infile;
-
- if ((infile=fopen(FileName, "r")) == NULL)
- return EM_FILEOPENERROR;
- fscanf(infile, "%d", &SkipNumColors); // Skip this field
- // Skip colors if they exist
- for (i=0; i<SkipNumColors; i++)
- fscanf(infile, "%d %d %d", &r, &g, &b);
- fscanf(infile, "%d %d", &vertices, &length);
- if (vertices >= NUMVERTICES || length >= NUMCONNECTIONS)
- return EM_FILETOOBIG;
- for (i=1; i<=vertices; i++)
- fscanf(infile, "%lf %lf %lf", &pointarray[i].X,
- &pointarray[i].Y, &pointarray[i].Z);
- for (i=1; i<=length; i++)
- fscanf(infile, "%d", &connect[i]);
- fclose(infile);
- return TRUE;
- }
-
-
-
-