#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;
}
}