home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / PCTV3N3.ZIP / THREED.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-01  |  8.9 KB  |  285 lines

  1. // THREED.CPP: A three-dimensional viewing package. Displays
  2. // wire-frame three-dimensional views of objects using perspective
  3. // projection. Objects are read from files and displayed according
  4. // to the settings of the from, at, up, and viewing angle.
  5.  
  6. #include <windows.h>
  7. #include <stdio.h>
  8. #include <math.h>
  9. #include "threed.h"
  10.  
  11. // Returns the larger of two values
  12. inline double MaxOf(double val1, double val2) {
  13.   return (val1 > val2) ? val1 : val2;
  14. }
  15.  
  16. // The constructor sets up various default values for the
  17. // camera model
  18. void TThreeD::TThreeD()
  19. {
  20.   // Set up the dimensions of the viewing region
  21.   pcb = PCB;  pct = PCT;
  22.   pcr = PCR;  pcl = PCL;
  23.   a = (pcr - pcl) / (1 + 1);       // Set viewport and window
  24.   b = pcl - a * (-1);              // mapping variables
  25.   c = (pct - pcb) / (1 + 1);
  26.   d = pcb - c * (-1);
  27.   // Set default values for from, at, and up vectors
  28.   from.X = 1.0;  from.Y = 0.0;  from.Z = 0.0;
  29.   at.X = 0.0;    at.Y = 0.0;    at.Z = 0.0;
  30.   up.X = 0.0;    up.Y = 0.0;    up.Z = 1.0;
  31.   Angle = 60.0 * 0.017453293;      // Convert to radians
  32. }
  33.  
  34. // High level routine to display a three-dimensional object
  35. // already read into memory. Sets up the mapping mode and
  36. // then calls View to display the object.
  37. void TThreeD::Display(HDC hDC, RECT& rect)
  38. {
  39.   // Use the isotropic mapping mode so that the X and Y
  40.   // dimensions of equal sizes will appear the same within
  41.   // the window
  42.   SetMapMode(hDC, MM_ISOTROPIC);
  43.  
  44.   // Set the logical coordinates of the window. Normally
  45.   // our logical coordinates will be between 0 and 1, but
  46.   // since we need integer coordinates we'll use 0 to 1000.
  47.   SetWindowExt(hDC, PCR-PCL, PCB-PCT);
  48.  
  49.   // Now set the viewport to use the largest square that fits
  50.   // in the window. The origin defaults to the top left of the
  51.   // viewport.
  52.   if (rect.right <= rect.bottom) {
  53.     SetViewportOrg(hDC, 0, (rect.bottom-rect.right)/2);
  54.     SetViewportExt(hDC, rect.right, rect.right);
  55.   }
  56.   else {
  57.     SetViewportOrg(hDC, (rect.right-rect.bottom)/2,0);
  58.     SetViewportExt(hDC, rect.bottom, rect.bottom);
  59.   }
  60.   SetEye();
  61.   View(hDC);    // Display the figure
  62. }
  63.  
  64. // Converts clipped world coordinates to the window's coordinates
  65. void TThreeD::WORLDtoPC(double xw, double yw, POINT& pc)
  66. {
  67.   pc.x = (int)(a * xw + b);
  68.   pc.y = (int)(c * yw + d);
  69. }
  70.  
  71. // Return the minimum and maximum values in the point array
  72. // for the X, Y, and Z axes
  73. void TThreeD::MinMax()
  74. {
  75.   int i;
  76.  
  77.   objxmin = 32000;  objymin = 32000;  objzmin = 32000;
  78.   objxmax = -32000; objymax = -32000; objzmax = -32000;
  79.   for (i=1; i<=vertices; i++) {
  80.     if (pointarray[i].X > objxmax) objxmax = pointarray[i].X;
  81.       else if (pointarray[i].X < objxmin) objxmin = pointarray[i].X;
  82.     if (pointarray[i].Y > objymax) objymax = pointarray[i].Y;
  83.       else if (pointarray[i].Y < objymin) objymin = pointarray[i].Y;
  84.     if (pointarray[i].Z > objzmax) objzmax = pointarray[i].Z;
  85.       else if (pointarray[i].Z < objzmin) objzmin = pointarray[i].Z;
  86.   }
  87. }
  88.  
  89. // Routine to provide a default value for the at point. It
  90. // is set to the midpoint of the extents of the object.
  91. void TThreeD::SetAt()
  92. {
  93.   MinMax();
  94.   at.X = (objxmin+objxmax) / 2.0;
  95.   at.Y = (objymin+objymax) / 2.0;
  96.   at.Z = (objzmin+objzmax) / 2.0;
  97. }
  98.  
  99. // Routine that provides a default value for the from point. It
  100. // is dependent on the at point and the view angle.
  101. void TThreeD::SetFrom()
  102. {
  103.   const double WIDTH = 1.8;  // Ratio used to determine from point
  104.                  // It is based on size of object
  105.   from.X = at.X + (objxmax-objxmin) / 2.0 + WIDTH *
  106.        MaxOf((objzmax-objzmin)/2.0, (objymax-objymin)/2.0);
  107.   from.Y = at.Y;
  108.   from.Z = at.Z;
  109. }
  110.  
  111. // There must be a valid object in pointarray before calling this
  112. // function. It sets up the various variables used in transforming
  113. // an object from world to eye coordinates.
  114. void TThreeD::SetEye()
  115. {
  116.   double amarkmag, tempmag;
  117.   VECTOR temp;
  118.  
  119.   dval = cos(Angle/2.0) / sin(Angle/2.0);
  120.   dist = Subtract(&at, &from);
  121.   amarkmag = Mag(&dist);
  122.   a3 = Divide(&dist, amarkmag);
  123.   temp = Cross(&dist, &up);
  124.   tempmag = Mag(&temp);
  125.   a1 = Divide(&temp, tempmag);
  126.   temp = Cross(&a1, &a3);
  127.   tempmag = Mag(&temp);
  128.   a2 = Divide(&temp, tempmag);
  129.   offsx = -a1.X * from.X - a1.Y * from.Y - a1.Z * from.Z;
  130.   offsy = -a2.X * from.X - a2.Y * from.Y - a2.Z * from.Z;
  131.   offsz = -a3.X * from.X - a3.Y * from.Y - a3.Z * from.Z;
  132. }
  133.  
  134. const int NOEDGE = 0x00;
  135. const int LEFTEDGE = 0x01;
  136. const int RIGHTEDGE = 0x02;
  137. const int BOTTOMEDGE = 0x04;
  138. const int TOPEDGE = 0x08;
  139.  
  140. // Returns a code specifying which edge in the viewing
  141. // pyramid was crossed. There may be more than one.
  142. int TThreeD::Code(double x, double y, double z)
  143. {
  144.   int c;
  145.  
  146.   c = NOEDGE;
  147.   if (x < -z) c |= LEFTEDGE;
  148.   if (x > z)  c |= RIGHTEDGE;
  149.   if (y < -z) c |= BOTTOMEDGE;
  150.   if (y > z)  c |= TOPEDGE;
  151.   return(c);
  152. }
  153.  
  154. // Clips the line segment in 3D coordinates to the viewing pyramid.
  155. // The clipped coordinates are returned as screen coordinates
  156. // in the variables (pc1.x,pc1.y) and (pc2.x,pc2.y).
  157. void TThreeD::Clip3D(HDC hDC, double x1, double y1, double z1,
  158.   double x2, double y2, double z2, POINT& pc1, POINT& pc2)
  159. {
  160.   int c, c1, c2;
  161.   double x, y, z, t;
  162.  
  163.   c1 = Code(x1, y1, z1);
  164.   c2 = Code(x2, y2, z2);
  165.   while (c1 != NOEDGE || c2 != NOEDGE) {
  166.     if ((c1 & c2) != NOEDGE)  // The line is not in the viewing
  167.       return;                 // pyramid. Don't draw anything.
  168.     c = c1;
  169.     if (c == NOEDGE) c = c2;
  170.     if ((c & LEFTEDGE) == LEFTEDGE) {  // Crosses left edge
  171.       t = (z1 + x1) / ((x1 - x2) - (z2 - z1));
  172.       z = t * (z2 - z1) + z1;
  173.       x = -z;
  174.       y = t * (y2 - y1) + y1;
  175.     }
  176.     else if ((c & RIGHTEDGE) == RIGHTEDGE) {
  177.       // Crosses right edge of the viewing pyramid
  178.       t = (z1 - x1) / ((x2 - x1) - (z2 - z1));
  179.       z = t * (z2 - z1) + z1;
  180.       x = z;
  181.       y = t * (y2 - y1) + y1;
  182.     }
  183.     else if ((c & BOTTOMEDGE) == BOTTOMEDGE) {
  184.       // Crosses bottom edge of the viewing pyramid
  185.       t = (z1 + y1) / ((y1 - y2) - (z2 - z1));
  186.       z = t * (z2 - z1) + z1;
  187.       x = t * (x2 - x1) + x1;
  188.       y = -z;
  189.     }
  190.     else if ((c & TOPEDGE) == TOPEDGE) {
  191.       // Crosses top edge of the viewing pyramid
  192.       t = (z1 - y1) / ((y2 - y1) - (z2 - z1));
  193.       z = t * (z2 - z1) + z1;
  194.       x = t * (x2 - x1) + x1;
  195.       y = z;
  196.     }
  197.     if (c == c1) {
  198.       x1 = x;  y1 = y;  z1 = z;
  199.       c1 = Code(x, y, z);
  200.     }
  201.     else {
  202.       x2 = x;  y2 = y;  z2 = z;
  203.       c2 = Code(x, y, z);
  204.     }
  205.   }
  206.   if (z1 != 0) {
  207.     WORLDtoPC(x1/z1, y1/z1, pc1);
  208.     WORLDtoPC(x2/z2, y2/z2, pc2);
  209.   }
  210.   else {
  211.     WORLDtoPC(x1, y1, pc1);
  212.     WORLDtoPC(x2, y2, pc2);
  213.   }
  214.   MoveTo(hDC, pc1.x, pc1.y);
  215.   LineTo(hDC, pc2.x, pc2.y);
  216. }
  217.  
  218. // Transform the segment connecting the two vectors into
  219. // the viewing plane. Clip3D() clips and draws the line if visible.
  220. void TThreeD::TransformSeg(HDC hDC, VECTOR *v1, VECTOR *v2,
  221.   POINT& pc1, POINT& pc2)
  222. {
  223.   double x1, y1, z1, x2, y2, z2;
  224.  
  225.   x1 = (v1->X * a1.X + a1.Y * v1->Y + a1.Z * v1->Z + offsx) * dval;
  226.   y1 = (v1->X * a2.X + a2.Y * v1->Y + a2.Z * v1->Z + offsy) * dval;
  227.   z1 =  v1->X * a3.X + a3.Y * v1->Y + a3.Z * v1->Z + offsz;
  228.   x2 = (v2->X * a1.X + a1.Y * v2->Y + a1.Z * v2->Z + offsx) * dval;
  229.   y2 = (v2->X * a2.X + a2.Y * v2->Y + a2.Z * v2->Z + offsy) * dval;
  230.   z2 =  v2->X * a3.X + a3.Y * v2->Y + a3.Z * v2->Z + offsz;
  231.   Clip3D(hDC, x1, y1, z1, x2, y2, z2, pc1, pc2);
  232. }
  233.  
  234. // Increment through the pointarray which contains the vertices of the
  235. // object and display them as you go. This will draw out the object.
  236. void TThreeD::View(HDC hDC)
  237. {
  238.   int i=1, startofside;
  239.   POINT pc1, pc2;
  240.  
  241.   while (i<length) {
  242.     startofside = i;   i++;
  243.     while (connect[i] > 0) {
  244.       TransformSeg(hDC, &pointarray[connect[i-1]],
  245.     &pointarray[connect[i]], pc1, pc2);
  246.       i++;
  247.     }
  248.     // Close off the polygon
  249.     TransformSeg(hDC, &pointarray[connect[i-1]],
  250.       &pointarray[connect[startofside]], pc1, pc2);
  251.     // Skip the negative value in the connect array; it'll be
  252.     // used in Chapter 16 to specify the polygon's color.
  253.     i++;
  254.   }
  255. }
  256.  
  257. // Read in a file describing a polygon which adheres to the
  258. // format described in Chapter 14. Returns 1 if file is read
  259. // successfully; otherwise it returns a negative error flag.
  260. int TThreeD::Read3DObject(char *FileName)
  261. {
  262.   int i, SkipNumColors, r, g, b;
  263.   FILE *infile;
  264.  
  265.   if ((infile=fopen(FileName, "r")) == NULL)
  266.     return EM_FILEOPENERROR;
  267.   fscanf(infile, "%d", &SkipNumColors);  // Skip this field
  268.   // Skip colors if they exist
  269.   for (i=0; i<SkipNumColors; i++)
  270.     fscanf(infile, "%d %d %d", &r, &g, &b);
  271.   fscanf(infile, "%d %d", &vertices, &length);
  272.   if (vertices >= NUMVERTICES || length >= NUMCONNECTIONS)
  273.     return EM_FILETOOBIG;
  274.   for (i=1; i<=vertices; i++)
  275.     fscanf(infile, "%lf %lf %lf", &pointarray[i].X,
  276.       &pointarray[i].Y, &pointarray[i].Z);
  277.   for (i=1; i<=length; i++)
  278.     fscanf(infile, "%d", &connect[i]);
  279.   fclose(infile);
  280.   return TRUE;
  281. }
  282.  
  283.  
  284.  
  285.