#include "polygon.h" double sintable[360]; // Trig lookup tables are far faster than the functions extern unsigned _stklen = 51200U; // We need a larger stack for this one... void maketables (void) { // Purpose: This function creates the global trig lookup table. int angle; for (angle = 0; angle < 360; ++angle) sintable[angle] = sin((((double)angle)*2.0*3.1415927)/360.0); } double sint(int X) { // Purpose: This function takes an angle (0 to 359) in an integral number of degrees // and returns the sine of the angle. if (X < 0) return -(sintable[(-X)%360]); return (sintable[X%360]); } double cost(int X) { // Purpose: This function takes an angle (0 to 359) in an integral number of degrees // and returns the cosine of the angle. X += 90; if (X < 0) X = -X; X = X%360; return (sintable[X]); } screen::screen(int m) { open(m); } screen::screen() { active = 0; } void screen::open(int m) { int gdriver = VGA, gmode = VGAMED, errorcode; initgraph(&gdriver, &gmode, ""); // Init graphics. errorcode = graphresult(); // Get the error code, if (m != 0) { setactivepage(0); setvisualpage(1); vispage = 1; } else vispage = -1; if (errorcode != grOk) // an error occurred { printf("Graphics error: %s\n", grapherrormsg(errorcode)); printf("Press any key to halt:"); getch(); exit(1); // return with error code } active = 1; } void screen::swap() { if ((!active) || (vispage == -1)) return; if (vispage == 0) { setactivepage(0); setvisualpage(1); vispage = 1; } else { setactivepage(1); setvisualpage(0); vispage = 0; } } screen::~screen() { if (!active) return; close(); // Close the graphics system. active = 0; } void screen::close(void) { if (!active) return; closegraph(); // Shut down the graphics system, and return to text mode. active = 0; } void screen::plot(long x, long y, int c) { putpixel((int)x,(int)y,(int)c); // Plot a pixel. } void screen::sline(long x1, long y1, long x2, long y2, int c) { setcolor(c); // Set the color, line((int)x1,(int)y1,(int)x2,(int)y2); // and draw a line. } void screen::sfillpoly(int num_points, int *points, int fc, int bc) { setcolor(bc); setlinestyle(SOLID_LINE,1,1); setfillstyle(SOLID_FILL,fc); fillpoly(num_points,points); drawpoly(num_points,points); // This is needed to correct for a bug in TC++ 3.0 } void screen::clear(int c) { setbkcolor(c); cleardevice(); } void point::normalize(void) { float veclen; veclen = sqrt(x*x+y*y+z*z); x = x/veclen; y = y/veclen; z = z/veclen; } polygon::polygon() { list=NULL; color=15; } void polygon::dellist(void) { polyNODE *pt, *lpt; pt = list; while(pt != NULL) { lpt = pt; pt = pt->Next; delete lpt; } list = NULL; } polygon::~polygon() { dellist(); } int polygon::addpoint(point &P) { polyNODE *pt, *lpt, *npt; lpt = pt = list; // Start at the beginning of the list. while (pt != NULL) // Find the end of the list. { lpt = pt; pt = pt->Next; } npt = new polyNODE; // Create the new point. if (npt == NULL) // If memory problem, indicate it. return 0; if (list == NULL) // Add the point to the list. list = npt; else lpt->Next = npt; npt->P = P; // Copy the value into the new point. npt->Next = NULL; // Ground the end. return 1; // return success. } void polygon::translate(float x, float y, float z) { polyNODE *pt; pt=list; while (pt!= NULL) { pt->P.x += x; pt->P.y += y; pt->P.z += z; pt = pt->Next; } } void polygon::rotate(int t, int p, int a) { polyNODE *pt; float TmpX, TmpY, TmpZ; float cosv, sinv; pt = list; while (pt != NULL) { if (a != 0) // Z axis rotation { cosv = cost(a); sinv = sint(a); TmpX = ((pt->P.x * cosv) - (pt->P.y * sinv)); TmpY = ((pt->P.x * sinv) + (pt->P.y * cosv)); pt->P.x = TmpX; pt->P.y = TmpY; } if (t != 0) // X Axis rotation. { cosv = cost(t); sinv = sint(t); TmpY = ((pt->P.y * cosv)-(pt->P.z*sinv)); TmpZ = ((pt->P.y * sinv)+(pt->P.z*cosv)); pt->P.y = TmpY; pt->P.z = TmpZ; } if (p != 0) // Y axiz rotation { cosv = cost(p); sinv = sint(p); TmpX = ((pt->P.x * cosv) - (pt->P.z*sinv)); TmpZ = ((pt->P.x * sinv) + (pt->P.z*cosv)); pt->P.x = TmpX; pt->P.z = TmpZ; } pt = pt->Next; } } void polygon::scale(float s) { polyNODE *pt; pt=list; while (pt!= NULL) { pt->P.x *= s; pt->P.y *= s; pt->P.z *= s; pt = pt->Next; } } polygon &polygon::operator =(polygon &P) { polyNODE *pt; dellist(); // Delete the current list. pt = P.list; // Start at the beginning of the other list. while (pt != NULL) // continue until the end of the other list. { if (!addpoint(pt->P)) // Try to add the point. { closegraph(); // If there was a problem, indicate it. (manual close of graphics system) printf("Memory allocation error in polygon::operator =(point &P)\n"); exit(1); } pt = pt->Next; // Go to the next point. } color = P.color; // Make sure the color's the same. return *this; } void polygon::getcenter(point &C) { polyNODE *pt; float Xavg = 0.0, Yavg = 0.0, Zavg = 0.0, fpoints; int points = 0; pt = list; // From the beginning of the list while (pt != NULL) // to the end of the list { ++points; Xavg += pt->P.x; // Add the value of the points x,y,z to the avg counters. Yavg += pt->P.y; Zavg += pt->P.z; pt = pt->Next; } if (points > 1) // If there was more than 1 point, { fpoints = (float)points; Xavg = Xavg/fpoints; // Calculate the average. Yavg = Yavg/fpoints; Zavg = Zavg/fpoints; } C.x = Xavg; // Return the values to the caller. C.y = Yavg; C.z = Zavg; } float polygon::getdist(point &P) { point Cntr; float dist, dx, dy, dz; getcenter(Cntr); dx = Cntr.x - P.x; dy = Cntr.y - P.y; dz = Cntr.z - P.z; dist = sqrt(dx*dx+dy*dy+dz*dz); return dist; } void polygon::getnormal(point &N) { point v1(0,0,0), v2(0,0,0); polyNODE *currnode, *nextnode; // Step 1. find a vector from point 2 to point 1 on the polygon. currnode = list; if (currnode == NULL) return; nextnode = currnode->Next; if (nextnode == NULL) return; v1.x = currnode->P.x - nextnode->P.x; v1.y = currnode->P.y - nextnode->P.y; v1.z = currnode->P.z - nextnode->P.z; // Step 3. find a vector from point 2 to point 3 on the polygon currnode = nextnode; nextnode = currnode->Next; if (nextnode == NULL) return; v2.x = nextnode->P.x - currnode->P.x; v2.y = nextnode->P.y - currnode->P.y; v2.z = nextnode->P.z - currnode->P.z; // Step 5. N = V1 X V2. N.x = v1.y*v2.z-v1.z*v2.y; N.y = v1.z*v2.x-v1.x*v2.z; N.z = v1.x*v2.y-v1.y*v2.x; // Step 6. normalize N. N.normalize(); } void player::delorderlist(orderNODE *list) { orderNODE *currnode, *prevnode; currnode = list; while (currnode != NULL) { prevnode = currnode; currnode = currnode->next; delete(prevnode); } } void player::dellist(void) { playerNODE *currnode, *prevnode; currnode = list; while (currnode != NULL) { prevnode = currnode; currnode = currnode->Next; delete(prevnode); } list = NULL; } int player::setorder(int view, int *ordering) { // Takes the array of ints in ordering, creates an array of pointers to the // polygons in the order specified by ordering, and stores it in the orderings // array. The array of ints is terminated by a value of 0. orderNODE *newnode, *currordernode; playerNODE *currplayernode; int count; if ((view < 1) || (view > 8)) // Make sure our order index is valid. return 0;// Indicate error. --view; // Delete any previous ordernig for that view. delorderlist(orderings[view]); orderings[view] = NULL; currordernode = NULL; // Now sort the list, and create a new orderings[view]. while (*ordering != 0) { // Set up to find the next polygon in the list, in order. count = 1; currplayernode = list; // Find the polygon. while ((count != *ordering) && (currplayernode != NULL)) { currplayernode = currplayernode->Next; ++count; }; if (currplayernode == NULL) // Did we actually find a polygon? return 0; // If not, indicate an error. newnode = new orderNODE; // If so, Add the node to the order list. if (newnode == NULL) { delorderlist(orderings[view]); // If alloc error, clean up, return -1; // and indicate it. } newnode->p = &(currplayernode->P); newnode->next = NULL; // Insert newnode into the linked list. if (currordernode == NULL) orderings[view] = newnode; else currordernode->next = newnode; currordernode = newnode; // Now look for the next item in the ordering list. ++ordering; } return 1; } int player::getorder(point &Origin) { // Computes the vextor from the object to the viewpoint, and returns the number // of the orderings array to use in displaying the player. float TmpX, TmpY, TmpZ; float x, y, z; float sinv, cosv; int dir; // Compute the vector from the player to the viewpoint. x = Origin.x - X; y = Origin.y - Y; z = Origin.z - Z; // Rotate the vector to take into account the Yaw, Pitch, and Roll of the player. if (A != 0) // Z axis rotation { cosv = cost(A); sinv = sint(A); TmpX = ((x * cosv) + (y * sinv)); TmpY = (-(x * sinv) + (y * cosv)); x = TmpX; y = TmpY; } if (T != 0) // X Axis rotation. { sinv = cost(T); cosv = sint(T); TmpY = ((y * cosv)+(z*sinv)); TmpZ = (-(y * sinv)+(z*cosv)); y = TmpY; z = TmpZ; } if (P != 0) // Y axiz rotation { cosv = cost(P); sinv = sint(P); TmpX = ((x * cosv) + (z*sinv)); TmpZ = (-(x * sinv) + (z*cosv)); x = TmpX; z = TmpZ; } // Determine the octant of the vector. if (y >= 0) { if (x < 0) { if (z < 0) dir = 4; else dir = 1; } else { if (z < 0) dir = 3; else dir = 2; } } else { if (x < 0) { if (z < 0) dir = 8; else dir = 5; } else { if (z < 0) dir = 7; else dir = 6; } } return dir; } player::player(float x, float y, float z) { X=x;Y=y;Z=z; Dx = Dy = Dz = 0; T = P = A = 0; list=NULL; orderings[0] = orderings[1] = orderings[2] = orderings[3] = orderings[4] = orderings[5] = orderings[6] = orderings[7] = NULL; } player::~player() { dellist(); delorderlist(orderings[0]); delorderlist(orderings[1]); delorderlist(orderings[2]); delorderlist(orderings[3]); delorderlist(orderings[4]); delorderlist(orderings[5]); delorderlist(orderings[6]); delorderlist(orderings[7]); } int player::addpoly(polygon &P) { playerNODE *newnode, *currnode; newnode = new playerNODE; if (newnode == NULL) return 0; newnode->P = P; newnode->Next = NULL; currnode = list; if (currnode == NULL) { list = newnode; return 1; } while(currnode->Next != NULL) { currnode = currnode->Next; } currnode->Next = newnode; return 1; } void player::move() { X += Dx; Y += Dy; Z += Dz; } void player::chgvel(float ddX, float ddY, float ddZ) { Dx += ddX; Dy += ddY; Dz += ddZ; } void player::moveto(float x, float y, float z) { X = x; Y = y; Z = z; } void player::translate(float x, float y, float z) { X += x; Y += y; Z += z; } void player::rotate(int p, int y, int r) { T += p; P += y; A += r; if (T >= 360) T -= 360; if (T < 0) T += 360; if (P >= 360) P -= 360; if (P < 0) P += 360; if (A >= 360) A -= 360; if (A < 0) A += 360; } void player::rotateto(int p, int y, int r) { rotate(360-T,360-P,360-A); rotate(p,y,r); } void player::scale(float pcnt) { playerNODE *currnode; currnode = list; while(currnode != NULL) { currnode->P.scale(pcnt); currnode = currnode->Next; } } point player::getpos(void) { static point Centr; Centr.x = X; Centr.y = Y; Centr.z = Z; return Centr; } player &player::operator = (player &P) { playerNODE *currnode; dellist(); currnode = P.list; while(currnode != NULL) { if (!addpoly(currnode->P)) { closegraph(); printf("Memory allocation error in player::operator =(player &P)\n"); exit(1); } currnode = currnode->Next; } return *this; } Viewport3D::Viewport3D (int mode) : screen(mode) { Rp.x = 0.0; Rp.y = 0.0; Rp.z = 0.0; X1 = Rp; X1.x -= 1.0; X2 = Rp; X2.x += 1.0; Vp.x = 0.0; Vp.y = 10000.0; Vp.z = -20000.0; Ud.x = 0.0; Ud.y = 1.0; Ud.z = 0.0; No.x = Rp.x - Vp.x; No.y = Rp.y - Vp.y; No.z = Rp.z - Vp.z; Dist = sqrt(No.x*No.x+No.y*No.y+No.z*No.z); make_vpt_matrix(); } Viewport3D::~Viewport3D() { clear(0); } float Identity[4][4] = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}; // The identity matrix. void Viewport3D::make_identity(float matrix[4][4]) { // Purpose: Copies Identity into matrix. int i,j; for (i=0;i<4;++i) for (j=0;j<4;++j) matrix[i][j] = Identity[i][j]; } void Viewport3D::matrix_multiply (float matrix1[4][4], float matrix2[4][4]) { // Purpose: Cross multiplies mareix1 and matrix2, and places the result back into matrix1. float h[4][4]; int i,j,k; float sum; for (i=0; i<4; ++i) for (j=0; j<4; ++j) h[i][j] = matrix1[i][j]; for (i=0; i<4; ++i) for (j=0; j<4; ++j) { sum = 0; for (k=0; k<4; ++k) sum += h[i][k] * matrix2[k][j]; matrix1[i][j] = sum; } } void Viewport3D::make_vpt_matrix(void) { // Purpose: Takes Rp, No, Ud, and Dist and creates the transformation matrix for the // view plane tranform. float M[4][4]; float p,w; point N,U; float D; N = No; U = Ud; D = Dist; p = sqrt(N.y*N.y+N.z*N.z); if (p > 0.00001) { U.x = U.x*p/D-N.x/(D*p)*(U.y*N.y+U.z*N.z); U.y = (U.y*N.z+U.z*N.y)/p; } else U.x = -U.z*N.x/D; w = sqrt(U.x*U.x+U.y*U.y); make_identity(VPT_Matrix); VPT_Matrix[3][0] = -Rp.x+N.x; VPT_Matrix[3][1] = -Rp.y+N.y; VPT_Matrix[3][2] = -Rp.z+N.z; if (p > 0.00001) { make_identity(M); M[1][1] = N.z/p; M[1][2] = N.y/p; M[2][1] = -N.y/p; M[2][2] = N.z/p; matrix_multiply(VPT_Matrix,M); } make_identity(M); M[0][0] = p/D; M[0][2] = N.x/D; M[2][0] = -N.x/D; M[2][2] = p/D; matrix_multiply(VPT_Matrix,M); if (w > 0.00001) { make_identity(M); M[0][0] = U.y/w; M[0][1] = U.x/w; M[1][0] = -U.x/w; M[1][1] = U.y/w; matrix_multiply(VPT_Matrix,M); } } void Viewport3D::transform(point &P) { // Actually transforms the point P using the transformation matrix. float sumx, sumy, sumz; sumx = (P.x*VPT_Matrix[0][0] + P.y*VPT_Matrix[1][0] + P.z*VPT_Matrix[2][0] + VPT_Matrix[3][0]); sumy = (P.x*VPT_Matrix[0][1] + P.y*VPT_Matrix[1][1] + P.z*VPT_Matrix[2][1] + VPT_Matrix[3][1]); sumz = (P.x*VPT_Matrix[0][2] + P.y*VPT_Matrix[1][2] + P.z*VPT_Matrix[2][2] + VPT_Matrix[3][2]); P.x = sumx; P.y = sumy; P.z = sumz; } int Viewport3D::poly_visible(polygon &P) { // // This is a temporary implementation. It checks the center of each polygon to see if it maps within the viewplane. // It should check for other things. // point Cntr; P.getcenter(Cntr); transform(Cntr); if ((Cntr.z) <= 0) return 0; if (((Cntr.x*512)/(Cntr.z+512) > 320) || ((Cntr.x*512)/(Cntr.z+512) < -320)) return 0; if (((Cntr.y*512)/(Cntr.z+512) > 175) || ((Cntr.y*512)/(Cntr.z+512) < -175)) return 0; return 1; } int Viewport3D::player_visible(player &P) { // // This is a temporary implementation. It checks the center of each player to see if it maps within the viewplane. // It should check for other things. // point Pt(P.X,P.Y,P.Z); transform(Pt); if ((Pt.z) <= 0) return 0; if ((((Pt.x*512)/(Pt.z+512)) > 320) || (((Pt.x*512)/(Pt.z+512)) < -320)) return 0; if ((((Pt.y*512)/(Pt.z+512)) > 175) || (((Pt.y*512)/(Pt.z+512)) < -175)) return 0; return 1; } char Viewport3D::GetRCode(point &P) { char Rcode; Rcode = 0x00; if (P.x < floor(-320-0.6237816764*(P.z-1.0))) Rcode |= 0x20; if (P.x > ceil(320.0+0.6237816764*(P.z-1.0))) Rcode |= 0x10; if (P.y < floor(-175.0-0.3411306043*(P.z-1.0))) Rcode |= 0x08; if (P.y > ceil(175.0+0.3411306043*(P.z-1.0))) Rcode |= 0x04; if (P.z < 1) Rcode |= 0x02; return Rcode; } int Viewport3D::Clip_Line(point &P1, point &P2) { double x,y,z,s; char Rcode, Rcode1, Rcode2; // Determine region codes for end points. Rcode1 = GetRCode(P1); Rcode2 = GetRCode(P2); while ((Rcode1 != 0x00) || (Rcode2 != 0x00)) { if ((Rcode1 & Rcode2) != 0x00) return 0; if ((Rcode1 | Rcode2) == 0x00) return 1; if (Rcode1 != 0x00) Rcode = Rcode1; else Rcode = Rcode2; if (Rcode & 0x02) { s=((P1.z-1.0)/(P1.z-P2.z)); x = P1.x+s*(P2.x-P1.x); y = P1.y+s*(P2.y-P1.y); z = 1; } else if (Rcode & 0x04) { s = (P1.y-175.0+0.3411306043*(1.0-P1.z))/(P1.y-P2.y+0.3411306043*(P2.z-P1.z)); x = floor(P1.x+s*(P2.x-P1.x)); y = floor(P1.y+s*(P2.y-P1.y)); z = floor(P1.z+s*(P2.z-P1.z)); } else if (Rcode & 0x08) { s = (P1.y+175.0+(-0.3411306043)*(1.0-P1.z))/(P1.y-P2.y+(-0.3411306043)*(P2.z-P1.z)); x = ceil(P1.x+s*(P2.x-P1.x)); y = ceil(P1.y+s*(P2.y-P1.y)); z = ceil(P1.z+s*(P2.z-P1.z)); } else if (Rcode & 0x10) { s = (P1.x-320.0+0.6237816764*(1.0-P1.z))/(P1.x-P2.x+0.6237816764*(P2.z-P1.z)); x = floor(P1.x+s*(P2.x-P1.x)); y = floor(P1.y+s*(P2.y-P1.y)); z = floor(P1.z+s*(P2.z-P1.z)); } else if (Rcode & 0x20) { s = (P1.x+320.0+(-0.6237816764)*(1.0-P1.z))/(P1.x-P2.x+(-0.6237816764)*(P2.z-P1.z)); x = ceil(P1.x+s*(P2.x-P1.x)); y = ceil(P1.y+s*(P2.y-P1.y)); z = ceil(P1.z+s*(P2.z-P1.z)); } if (Rcode1 != 0x00) { P1.x = x; P1.y = y; P1.z = z; } else { P2.x = x; P2.y = y; P2.z = z; } Rcode1 = GetRCode(P1); Rcode2 = GetRCode(P2); } return 1; } void Viewport3D::drawline(float x1, float y1, float z1, float x2, float y2, float z2, int c) { float Tmpx1, Tmpy1, Tmpx2, Tmpy2; point P1,P2; P1.x = x1; P1.y = y1; P1.z = z1; P2.x = x2; P2.y = y2; P2.z = z2; transform(P1); transform(P2); if (Clip_Line(P1,P2)) { Tmpx1 = ((P1.x*512.0)/(P1.z+512))+320.0; // Perspective Transformation, and map to physical screen. Tmpy1 = 175.0-((P1.y*512.0)/(P1.z+512)); Tmpx2 = ((P2.x*512.0)/(P2.z+512))+320.0; Tmpy2 = 175.0-((P2.y*512.0)/(P2.z+512)); sline( (long)Tmpx1,(long)Tmpy1,(long)Tmpx2,(long)Tmpy2,c); // Draw the line. } } void Viewport3D::Clip_Poly_L (point &P, polygon &Po) { point I; double s; char R = 0; if (LastL.x >= (-320-0.625*LastL.z)) R |= 0x10; if (P.x >= (-320-0.625*P.z)) R |= 0x01; if ((R == 0x10) || (R == 0x01)) { s = (LastL.x+320+0.625*LastL.z)/(LastL.x-P.x+(-0.625)*(P.z-LastL.z)); I.x = (LastL.x+s*(P.x-LastL.x)); I.y = (LastL.y+s*(P.y-LastL.y)); I.z = (LastL.z+s*(P.z-LastL.z)); } switch (R) { case 0x11: Clip_Poly_R(P,Po);break; case 0x10: Clip_Poly_R(I,Po);break; case 0x01: Clip_Poly_R(I,Po);Clip_Poly_R(P,Po); break; default: break; } LastL = P; return; } void Viewport3D::Clip_Poly_R (point &P, polygon &Po) { point I; double s; char R = 0; if (P.x <= (320+0.625*P.z)) R |= 0x01; if (LastR.x <= (320+0.625*LastR.z)) R |= 0x02; if ((R == 0x02) || (R == 0x01)) { s = (LastR.x-320-0.625*LastR.z)/(LastR.x-P.x+0.625*(P.z-LastR.z)); I.x = (LastR.x+s*(P.x-LastR.x)); I.y = (LastR.y+s*(P.y-LastR.y)); I.z = (LastR.z+s*(P.z-LastR.z)); } switch (R) { case 0x03: Clip_Poly_B(P,Po);break; case 0x02: Clip_Poly_B(I,Po);break; case 0x01: Clip_Poly_B(I,Po);Clip_Poly_B(P,Po); break; default: break; } LastR = P; return; } void Viewport3D::Clip_Poly_B (point &P, polygon &Po) { point I; double s; char R = 0; if (P.y >= (-175-0.341796875*P.z)) R |= 0x01; if (LastB.y >= (-175-0.341796875*LastB.z)) R |= 0x02; if ((R == 0x02) || (R == 0x01)) { s = (LastB.y+175+0.341796875*LastB.z)/(LastB.y-P.y+(-0.341796875)*(P.z-LastB.z)); I.x = (LastB.x+s*(P.x-LastB.x)); I.y = (LastB.y+s*(P.y-LastB.y)); I.z = (LastB.z+s*(P.z-LastB.z)); } switch (R) { case 0x03: Clip_Poly_T(P,Po);break; case 0x02: Clip_Poly_T(I,Po);break; case 0x01: Clip_Poly_T(I,Po);Clip_Poly_T(P,Po); break; default: break; } LastB = P; return; } void Viewport3D::Clip_Poly_T (point &P, polygon &Po) { point I; double s; char R = 0; if (P.y <= (175+0.341796875*P.z)) R |= 0x01; if (LastT.y <= (175+0.341796875*LastT.z)) R |= 0x02; if ((R == 0x02) || (R == 0x01)) { s = (LastT.y-175-0.341796875*LastT.z)/(LastT.y-P.y+0.341796875*(P.z-LastT.z)); I.x = ceil(LastT.x+s*(P.x-LastT.x)); I.y = ceil(LastT.y+s*(P.y-LastT.y)); I.z = ceil(LastT.z+s*(P.z-LastT.z)); } switch (R) { case 0x03: Clip_Poly_N(P,Po);break; case 0x02: Clip_Poly_N(I,Po);break; case 0x01: Clip_Poly_N(I,Po);Clip_Poly_N(P,Po); break; default: break; } LastT = P; return; } void Viewport3D::Clip_Poly_N (point &P, polygon &Po) { point I; float s; char R = 0; if (P.z >= 1) R |= 0x01; if (LastN.z >= 1) R |= 0x02; if ((R == 0x02) || (R == 0x01)) { s=((LastN.z-1.0)/(LastN.z-P.z)); I.x = LastN.x+s*(P.x-LastN.x); I.y = LastN.y+s*(P.y-LastN.y); I.z = 1; } switch (R) { case 0x03: Po.addpoint(P);break; case 0x02: Po.addpoint(I);break; case 0x01: Po.addpoint(I);Po.addpoint(P); break; default: break; } LastN = P; return; } int Viewport3D::Clip_Poly(polygon &Pi, polygon &Po) { // // Clips the polygon Pi against the view frustrum, and returns the // clipped polygon in Po. // polyNODE *pt, *lpt; lpt = pt = Pi.list; // Initialize Pointers. while (pt != NULL) // Find the last point of the list. { lpt = pt; pt = pt->Next; } pt = Pi.list; LastL = LastR = LastT = LastB = LastN = lpt->P; // Initialize lastpoints. // Now, do a dry run to set the lastpoints for the real clip. Clip_Poly_L(pt->P, Po); Po.dellist(); // Now, we do the real clipping... while(pt != NULL) { Clip_Poly_L(pt->P, Po); // Clip this edge. pt = pt->Next; } Clip_Poly_L((Pi.list)->P,Po); if (Po.list != NULL) return 1; else return 0; } void Viewport3D::drawpoly(float x, float y, float z, polygon &P) { polyNODE *currnode, *nextnode; P.translate(x,y,z); // Translate it out [X,Y,Z] if (poly_visible(P)) // Is it visible? { currnode = P.list; while (currnode != NULL)// If so, go through all its points, { nextnode = currnode->Next; if (nextnode == NULL) nextnode = P.list; drawline(currnode->P.x,currnode->P.y,currnode->P.z,nextnode->P.x,nextnode->P.y,nextnode->P.z,P.color); // and draw the lines. currnode = currnode->Next; } } P.translate(-x,-y,-z); // Then translate it back. } void Viewport3D::drawfillpoly(float x, float y, float z, polygon &P) { polyNODE *currnode; polygon Pi,Po; int points[30], index; point Pt; P.translate(x,y,z); // Translate it by [X,Y,Z]. Pi=P; currnode = Pi.list; while (currnode != NULL) // If so, go through all its points, { transform(currnode->P); // Transform the point, currnode = currnode->Next; } if(Clip_Poly(Pi,Po)) { currnode = Po.list; index = 0; while (currnode != NULL) // If so, go through all its points, { Pt = currnode->P; points[index++] = (int)(((Pt.x*512.0)/(Pt.z+512.0))+320.0); // Do the perspective transform, points[index++] = (int)(175.0-((Pt.y*512.0)/(Pt.z+512.0))); // and add it to a list of points. currnode = currnode->Next; } points[index++] = points[0]; // close the loop, points[index++] = points[1]; sfillpoly(index/2, points, P.color, 0); } P.translate(-x,-y,-z); // translate it back. } void Viewport3D::translateR(float x, float z) { point Vec; float tx,tz; Vec.x = X2.x-Rp.x; Vec.y = 0; Vec.z = X2.z-Rp.z; Vec.normalize(); tx = x*Vec.x - z*Vec.z; tz = x*Vec.z + z*Vec.x; x = tx; z = tz; Rp.x += x; Rp.z += z; X1.x += x; X1.z += z; X2.x += x; X2.z += z; Vp.x += x; Vp.z += z; make_vpt_matrix(); } void Viewport3D::scaleD(float s) { // Translate the viewpoint by -Rp Vp.x -= Rp.x; Vp.y -= Rp.y; Vp.z -= Rp.z; // Scale it.. Vp.x *= s; Vp.y *= s; Vp.z *= s; // Translate the viewpoint back; Vp.x += Rp.x; Vp.y += Rp.y; Vp.z += Rp.z; // Re-calculate the normal, and the transformation. No.x = Rp.x - Vp.x; No.y = Rp.y - Vp.y; No.z = Rp.z - Vp.z;; Dist = sqrt(No.x*No.x+No.y*No.y+No.z*No.z); make_vpt_matrix(); } void Viewport3D::rotateY(int P) { float TmpX, TmpZ; float costp, sintp; if (P == 0) return; P = -P; costp = cost(P); sintp = sint(P); // Translate the viewpoint, X1, and X2 by -Rp Vp.x -= Rp.x; Vp.y -= Rp.y; Vp.z -= Rp.z; X1.x -= Rp.x; X1.y -= Rp.y; X1.z -= Rp.z; X2.x -= Rp.x; X2.y -= Rp.y; X2.z -= Rp.z; // Rotate the Viewpoint. TmpX = ((Vp.x * costp) - (Vp.z*sintp)); TmpZ = ((Vp.x * sintp) + (Vp.z*costp)); Vp.x = TmpX; Vp.z = TmpZ; // Rotate X1 TmpX = ((X1.x * costp) - (X1.z*sintp)); TmpZ = ((X1.x * sintp) + (X1.z*costp)); X1.x = TmpX; X1.z = TmpZ; // Rotate X2 TmpX = ((X2.x * costp) - (X2.z*sintp)); TmpZ = ((X2.x * sintp) + (X2.z*costp)); X2.x = TmpX; X2.z = TmpZ; // Translate the viewpoint, X1, and X2 back; Vp.x += Rp.x; Vp.y += Rp.y; Vp.z += Rp.z; X1.x += Rp.x; X1.y += Rp.y; X1.z += Rp.z; X2.x += Rp.x; X2.y += Rp.y; X2.z += Rp.z; // Re-calculate the normal and the tranformation. No.x = Rp.x - Vp.x; No.y = Rp.y - Vp.y; No.z = Rp.z - Vp.z; Dist = sqrt(No.x*No.x+No.y*No.y+No.z*No.z); make_vpt_matrix(); } void Viewport3D::rotateUD(int t) { float TmpX, TmpY, TmpZ, A, B, C, L, P, costv, sintv; float pl, al; if (t == 0) return; A = X2.x-X1.x; B = X2.y-X1.y; C = X2.z-X1.z; L = sqrt(A*A+B*B+C*C); if (B*B+C*C == 0) P = 0; else P = sqrt(B*B+C*C); // Translate the viewpoint by -X1 Vp.x -= X1.x; Vp.y -= X1.y; Vp.z -= X1.z; // Now Align with Z axis. pl = P/L; al = A/L; TmpX = Vp.x*pl - Vp.z*al; TmpZ = Vp.x*al + Vp.z*pl; Vp.x = TmpX; Vp.z = TmpZ; // Now rotate the point around Z axis. costv = cost(t); sintv = sint(t); TmpX = Vp.x*costv-Vp.y*sintv; TmpY = Vp.x*sintv+Vp.y*costv; Vp.x = TmpX; Vp.y = TmpY; // Now rotate back from the Z axis. TmpX = Vp.x*pl + Vp.z*al; TmpZ = -Vp.x*al + Vp.z*pl; Vp.x = TmpX; Vp.z = TmpZ; // Translate the viewpoint back; Vp.x += X1.x; Vp.y += X1.y; Vp.z += X1.z; // Re-calculate the normal and the tranformation. No.x = Rp.x - Vp.x; No.y = Rp.y - Vp.y; No.z = Rp.z - Vp.z; Dist = sqrt(No.x*No.x+No.y*No.y+No.z*No.z); make_vpt_matrix(); } void Viewport3D::drawplayer(player &P) { orderNODE*currnode; polygon current; if (!player_visible(P)) // Is the player visible? return; currnode = P.orderings[P.getorder(Vp)-1]; while(currnode != NULL) { current = *(currnode->p); current.rotate(P.T,P.P,P.A); drawfillpoly(P.X,P.Y,P.Z,current); // Draw filled polygon. currnode = currnode->next; } }