CH13

You might also like

Download as rtf, pdf, or txt
Download as rtf, pdf, or txt
You are on page 1of 59

XIII 3D GRAPHICS

13.1 INTRODUCTION

Hold on to your mathematical hats. This is a heavy-duty chapter that presents us, the
illustrious authors of this book, with a no-win situation. If we explain every detail of the
programs presented in this chapter, you will most assuredly wonder about our Student
Friendly title. It is not possible to explain complex three-dimensional graphics in a simple,
easy to learn approach. It is also possible to avoid complex programs by not including
them in this chapter or this book. You see our no-win situation. Either avoid complexity
and do not include some nice program examples or start explaining concepts that are
tough to explain and digest.

Actually, there is a third choice. We have decided on a compromise. A group of programs


will be presented and explained that helps to get a flavor of the techniques involved in 3d
graphics. After the introduction of these basic concepts, we will show a second set of
programs that demonstrate some nice features. This second set of programs will not be
explained. We do provide the source code and, it is a little like the second icon editor
program. The programs are included for your curiosity, but it is your job to digest the
information slowly. Basically, the advanced programs are included for those people who
have a very strong mathematical background and can handle the mathematical complexity
involved. These programs may only be found on the companion diskette.

This chapter will not plunge into 3d graphics. We will start with 2d graphics and explore a
variety of rotation techniques and perspective. These techniques are necessary for
successful programming in 3d.

The initial programs will start with the usual inefficiency. We simply cannot escape this
pattern. Inefficient, less desirable executions are frequently much easier to code and

Chapter XIII Three Dimensional Graphics -13.1-


explain. We will rapidly move to more sophisticated stuff and at some point you will
appreciate why it is not easy to stay Student Friendly.

When all is said and done, the truth is that 3d graphics is very satisfying. It is somewhat
like the Animation chapter. Topics like Animation and 3d Graphics are precisely why we
enjoy working with graphics programs. A few eager folks out there may like 3d graphics
because of some masochistic tendencies towards complex math. However, the majority
of computer enthusiasts like the dramatic effects that 3d graphics bring to your monitor. As
always, have fun.

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

13.2 ROTATION BASICS

Way, way back in Chapter IV the humble Circle function was introduced. The concept of
the Circle function was based on sin and cos function values. We will repeat a program
here that is specifically designed to demonstrate how a combination of a sin and a cos
value identifies a pixel coordinate in a rotating pattern. Program CH13_01.CPP
demonstrates this rotation by drawing 16 points. A circle rotates through 2 PI radians and
the program below shows sin and cos values that use arguments from 0 radians to
almost 2 PI radians.

────────────────────────────────────────────────────────────────────────────────

/**********************************************************************/
/*** CH13_01.CPP demonstrates how the sin and cos functions ***/
/*** compute points on a circle. This program is identical to an ***/
/*** earlier program, CH4_05.CPP, presented in Chapter IV. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"

void TrigPixel(float Radian);

void main()
{
StartGfx();
Line(MIDX,0,MIDX,MAXY,1);
Line(0,MIDY,MAXX,MIDY,1);
TrigPixel(0.000*PI); // (0/8) * PI
TrigPixel(0.125*PI); // (1/8) * PI
TrigPixel(0.250*PI); // (2/8) * PI

Chapter XIII Three Dimensional Graphics -13.2-


TrigPixel(0.375*PI); // (3/8) * PI
TrigPixel(0.500*PI); // (4/8) * PI
TrigPixel(0.625*PI); // (5/8) * PI
TrigPixel(0.750*PI); // (6/8) * PI
TrigPixel(0.875*PI); // (7/8) * PI
TrigPixel(1.000*PI); // (8/8) * PI
TrigPixel(1.125*PI); // (9/8) * PI
TrigPixel(1.250*PI); // (10/8) * PI
TrigPixel(1.375*PI); // (11/8) * PI
TrigPixel(1.500*PI); // (12/8) * PI
TrigPixel(1.625*PI); // (13/8) * PI
TrigPixel(1.750*PI); // (14/8) * PI
TrigPixel(1.875*PI); // (15/8) * PI
EndGfx();
} // End main program CH13_01.CPP.

void TrigPixel(float Radian)


{
int X,Y; // coordinates of each plotted pixel

X = Round(cos(Radian) * 50) + MIDX;


Y = Round(sin(Radian) * 50) + MIDY;
Pixel(X,Y,15);
Stop();
} // End void function TrigPixel
────────────────────────────────────────────────────────────────────────────────

The ever-wonderful sin and cos functions give us circular values that plot points on a
circle. The same values can be used to rotate graphics objects. In the next program,
CH13_02.CPP, we take four points on the circle, connect them in a square, and then
move the points in a clockwise rotation. The result is a rotating square. If you look closely
you will see that this program is conceptually the exact same as the previous program.
The only difference is that we are not plotting points but we are moving a square by
selecting four specific points on the circle. Function TrigPixel is not totally gone, it has just
been changed into FindTrigCoords. A new function called TrigSquare calls function
FindTrigCoords four times, draws four lines and we are in business. You may wonder if
this second program is really the same as the previous program. Look at it closely. The
code, and some names may be different, but the logic is exactly the same.
────────────────────────────────────────────────────────────────────────────────

/**********************************************************************/
/*** CH13_02.CPP is similar to the previous, except that it draws ***/
/*** four points and connects them to make a rotating square. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"

const RADIUS = 50;

void FindTrigCoords(int& X, int& Y, float Radian);


void DrawAxes();
void TrigSquare(float Radian);

Chapter XIII Three Dimensional Graphics -13.3-


void main()
{
int X,Y;
StartGfx();
TrigSquare(0.000*PI); // (0/8) * PI
TrigSquare(0.125*PI); // (1/8) * PI
TrigSquare(0.250*PI); // (2/8) * PI
TrigSquare(0.375*PI); // (3/8) * PI
TrigSquare(0.500*PI); // (4/8) * PI
TrigSquare(0.625*PI); // (5/8) * PI
TrigSquare(0.750*PI); // (6/8) * PI
TrigSquare(0.875*PI); // (7/8) * PI
TrigSquare(1.000*PI); // (8/8) * PI
TrigSquare(1.125*PI); // (9/8) * PI
TrigSquare(1.250*PI); // (10/8) * PI
TrigSquare(1.375*PI); // (11/8) * PI
TrigSquare(1.500*PI); // (12/8) * PI
TrigSquare(1.625*PI); // (13/8) * PI
TrigSquare(1.750*PI); // (14/8) * PI
TrigSquare(1.875*PI); // (15/8) * PI
EndGfx();
} // End main program CH13_02.CPP.

void FindTrigCoords(int& X, int& Y, float Radian)


{
X = (cos(Radian)*RADIUS) + MIDX;
Y = (sin(Radian)*RADIUS) + MIDY;
} // End void function FindTrigCoords.

void DrawAxes()
{
Line(MIDX,0,MIDX,MAXY,15);
Line(0,MIDY,MAXX,MIDY,15);
} // End void function DrawAxes.

void TrigSquare(float Radian)


{
int X1,Y1,X2,Y2,X3,Y3,X4,Y4;
ClearGfx(0);
DrawAxes();
FindTrigCoords(X1,Y1,Radian);
FindTrigCoords(X2,Y2,Radian+PI/2);
FindTrigCoords(X3,Y3,Radian+PI);
FindTrigCoords(X4,Y4,Radian+3*PI/2);
Line(X1,Y1,X2,Y2,1);
Line(X2,Y2,X3,Y3,2);
Line(X3,Y3,X4,Y4,3);
Line(X4,Y4,X1,Y1,4);
Stop();
} // End void function TrigSquare.

────────────────────────────────────────────────────────────────────────────────

Program CH13_03.CPP goes one more step in the "rotating square" business. We now
make the square rotate smoothly. This requires very minimal changes. Program
CH13_02.CPP called function TrigSquare 16 times in the main program body. Program
CH13_03.CPP uses a loop in the main body that calls the TrigSquare function
continuously until a key is pressed.

Chapter XIII Three Dimensional Graphics -13.4-


────────────────────────────────────────────────────────────────────────────────

Chapter XIII Three Dimensional Graphics -13.5-


/**********************************************************************/
/*** CH13_03.CPP is similar to CH13_03.CPP except that the square ***/
/*** rotates smoothly until a key is pressed. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"

const RADIUS = 50;

void FindTrigCoords(int& X, int& Y, float Radian);


void DrawAxes();
void TrigSquare(float Radian);

void main()
{
int X,Y;
float Angle = 0;
StartGfx();
while ( !kbhit() )
{
ClearGfx(0);
DrawAxes();
TrigSquare(Angle);
Angle = Angle + (PI * 0.03125);
delay(10);
} // End while loop.
EndGfx();
} // End main program CH13_03.CPP.

void FindTrigCoords(int& X, int& Y, float Radian)


{
X = (cos(Radian)*RADIUS) + MIDX;
Y = (sin(Radian)*RADIUS) + MIDY;
} // End void function TrigPixel

void DrawAxes()
{
Line(MIDX,0,MIDX,MAXY,15);
Line(0,MIDY,MAXX,MIDY,15);
} // End void function DrawAxes.

void TrigSquare(float Radian)


{
int X1,Y1,X2,Y2,X3,Y3,X4,Y4;
ClearGfx(0);
DrawAxes();
FindTrigCoords(X1,Y1,Radian);
FindTrigCoords(X2,Y2,Radian+PI/2);
FindTrigCoords(X3,Y3,Radian+PI);
FindTrigCoords(X4,Y4,Radian+3*PI/2);
Line(X1,Y1,X2,Y2,1);
Line(X2,Y2,X3,Y3,2);
Line(X3,Y3,X4,Y4,3);
Line(X4,Y4,X1,Y1,4);
} // End void function TrigSquare

────────────────────────────────────────────────────────────────────────────────

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

13.3 ROTATION WITH COORDINATES, NOT ANGLES

Chapter XIII Three Dimensional Graphics -13.6-


This title is a little fuzzy. Did we not use coordinates as we rotated around the circle in the
last three programs? We did, but we used a vector quantity. We never specified a starting
coordinate. We specified a starting angle argument in radians, and we specified the size
of the radius. A vector is an angle and magnitude, and magnitude in this case is the
radius. The result of this vector quantity is a coordinate point that can be plotted.

When you work with 3d graphics it becomes necessary to define rotation in terms of
coordinates. Imagine that a cube is placed somewhere in 3d space. The vertices of the
cube are eight different coordinates. Rotating this cube just using some center point,
angles and radiuses would be a major pain. We need to work with these eight coordinates
and find new coordinate locations as the cube rotates.

The execution of program CH13_04.CPP will once again demonstrate a set of rotating
pixels. This execution will appear the same as the first program in this chapter. There is a
very significant difference. Program CH13_01.CPP started with 0 radian value as
arguments for the sin angle and cos angle.

Now take a look at program CH13_04.CPP and you will see that the initial values are X =
80 and Y = -10. This is a set of coordinates. We are not concerned in the slightest with a
starting angle. Now, do not get confused because we will still need to use an angle, but
now the angle must be computed based on a given coordinate point. The computation of
this angle business will be explained shortly. Look at the program execution. It shows the
same result, but it is achieved in a different manner.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_04.CPP shows how to rotate a given (X,Y) point by ***/
/*** a given radian value. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"

void RotatePixel(int& X, int& Y, float Angle);


void DrawAxes();

void main()
{
int X = 80,Y = -10; // starting coordinates
float Angle;
StartGfx();
DrawAxes();
Angle = atan((float) Y / X); // calculate starting angle
if (X < 0)
Angle = PI + Angle; // correct if angle is in quadrant II or III
do
{
RotatePixel(X,Y,Angle);
Pixel(X + MIDX,Y + MIDY,15);
Angle = Angle + (PI * 0.0625); // PI / 16
delay(100);
} // End do loop.

Chapter XIII Three Dimensional Graphics -13.7-


while (Angle < (1.9 * PI));
Stop();
EndGfx();
} // End main program CH13_04.CPP.

void RotatePixel(int& X, int& Y, float Angle)


{
float Radius;
Radius = sqrt(X*X+Y*Y);
X = Round(cos(Angle)*Radius);
Y = Round(sin(Angle)*Radius);
} // End void function RotatePixel.

void DrawAxes()
{
Line(MIDX,0,MIDX,MAXY,1);
Line(0,MIDY,MAXX,MIDY,1);
} // End void function DrawAxes.

────────────────────────────────────────────────────────────────────────────────

Now comes the fun part, explaining the math involved. Imagine that we are starting
rotation at coordinate point (3,2). We now need to find the vector quantity that equals this
coordinate point. So where does the angle come from? Well, just a moment ago we
mentioned that a vector quantity is an angle times its magnitude, which is the radius in our
case.

We have a coordinate so let us start by computing the radius. The trusty distance formula
can be used to for this calculation. The program statement in our program accomplishes
this with:

────────────────────────────────────────────────────────────────────────────────
Radius = sqrt(sqr(X)+sqr(Y));
────────────────────────────────────────────────────────────────────────────────

Now trigonometry tells us that the Tangent of an angle of triangle equals the opposite side
over the adjacent side. It should not be difficult to create a right triangle in a coordinate
system. Look at the drawing on the next page.

Chapter XIII Three Dimensional Graphics -13.8-


Figure 1

Figure 1 shows a lovely triangle between the coordinate (3,2) and the origin. N
represents the radius. The vertical distance of the triangle is 2, the horizontal distance is
3, and we need to compute the value of theta.

If Tangent Θ = 2/3 then the angle theta we want is the inverse, or arctan of 2/3. In the
program this is handled with the statement:

────────────────────────────────────────────────────────────────────────────────
Angle = arctan(Y/X);
────────────────────────────────────────────────────────────────────────────────

We are not "out of the woods" yet. The coordinate system operates in four quadrants, and
the arctan function behaves periodically. It is true that a computed angle is correct in
quadrants I and IV, but not in quadrants II and III. Any coordinate point that is found in
quadrants II and III will equal an arctan value that is negative. This means that we must
add PI when the X value is less than 0.
The program statement to accomplish the quadrant problem is:

────────────────────────────────────────────────────────────────────────────────
if (X < 0) then

Chapter XIII Three Dimensional Graphics -13.9-


Angle = PI + Angle; { correct if angle is in quadrant II or III }
────────────────────────────────────────────────────────────────────────────────

Basically, the distance formula combined with some trigonometry and some quadrant
concerns result in giving us the desired angle. Do study this section carefully. You may
get fooled and believe that we are doing the same stuff. Just remember one way is
starting with an angle and ending up at an unknown coordinate. The other, more practical
way, is starting with a coordinate and computing the desired angle.

ROTATING A SET OF POINTS

The last program rotated a single point to an absolute angle. Working with multiple points
requires rotation of all points relative to each other. Function RotatePixel in the previous
program rotated to an absolute angle. Now we want this function to rotate a specific angle
amount. This is different from rotating to a specific angle.

Therefore we need to store the starting coordinates, as well as the starting angle and
radius. With this information we can compute the desired angle amount that needs to be
increased each time. This concept will be shown with a hexagon that rotates all six
coordinates clockwise by the same angle amount. The main workhorse in program
CH13_05.CPP is function SetAngle, which computes the angle amounts.

Notice the use of the first library, GRAF3D01.HPP. This contains two functions,
SetZAngle and RotateAroundZAxis. SetZAngle accepts an X,Y coordinate pair and
returns the correct angle and radius. It checks for any quadrant problems and also makes
protects against divide by zero errors when X = 0. The second function,
RotateAroundZAxis, performs the same rotation process used before in functions like
TrigPixel and FindTrigCoords. Do not be concerned about the letter 'Z' appearing in
these function names. This is the correct term for this type of rotation (Z-Axis rotation).
This name will make more sense to you later in the chapter.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** This CH13_05.CPP shows how to rotate a set of (X,Y) points. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"
#include "GRAF3D01.HPP"

int X1,Y1,X2,Y2,X3,Y3,X4,Y4,X5,Y5,X6,Y6;
float A1,A2,A3,A4,A5,A6;
float R1,R2,R3,R4,R5,R6;
float ZAngle;

void MakeHexagon();
void DrawHexagon(float Angle);

void main()
{

Chapter XIII Three Dimensional Graphics -13.10-


StartGfx();
MakeHexagon();
ZAngle = 1;
do
{
ClearGfx(0);
ZAngle = ZAngle + (PI * 0.08333); // PI / 12
DrawHexagon(ZAngle);
delay(100);
} // End do loop.
while ( !kbhit() );
EndGfx();
} // End main program CH13_05.CPP.

void MakeHexagon()
{
X1 = 0;
Y1 = -40;
SetZAngle(X1, Y1, A1, R1);
X2 = 40;
Y2 = -25;
SetZAngle(X2, Y2,A2, R2);
X3 = 40;
Y3 = 25;
SetZAngle(X3, Y3, A3, R3);
X4 = 0;
Y4 = 40;
SetZAngle(X4, Y4, A4, R4);
X5 = -40;
Y5 = 25;
SetZAngle(X5, Y5, A5, R5);
X6 = -40;
Y6 = -25;
SetZAngle(X6, Y6, A6, R6);
} // End void function MakeHexagon.

void DrawHexagon(float Angle)


{
int ResX1, ResY1, ResX2, ResY2, ResX3, ResY3, ResX4, ResY4,
ResX5, ResY5, ResX6, ResY6;

RotateAroundZAxis(ResX1,ResY1,A1,R1,Angle);
RotateAroundZAxis(ResX2, ResY2, A2, R2, Angle);
RotateAroundZAxis(ResX3, ResY3, A3, R3, Angle);
RotateAroundZAxis(ResX4, ResY4, A4, R4, Angle);
RotateAroundZAxis(ResX5, ResY5, A5, R5, Angle);
RotateAroundZAxis(ResX6, ResY6, A6, R6, Angle);

Line(ResX1+MIDX, ResY1+MIDY, ResX2+MIDX, ResY2+MIDY, 1);


Line(ResX2+MIDX, ResY2+MIDY, ResX3+MIDX, ResY3+MIDY, 2);
Line(ResX3+MIDX, ResY3+MIDY, ResX4+MIDX, ResY4+MIDY, 3);
Line(ResX4+MIDX, ResY4+MIDY, ResX5+MIDX, ResY5+MIDY, 4);
Line(ResX5+MIDX, ResY5+MIDY, ResX6+MIDX, ResY6+MIDY, 5);
Line(ResX6+MIDX, ResY6+MIDY, ResX1+MIDX, ResY1+MIDY, 6);
} // End void function DrawHexagon

────────────────────────────────────────────────────────────────────────────────

This program may look pretty massive. Once again, examine each section as a
maneagable part to make understanding easier. First, look at function MakeHexagon.

Chapter XIII Three Dimensional Graphics -13.11-


Each of the coordinates are set to starting locations, and then angle and radius values are
calculated. DrawHexagon is the real workhorse here. First, each point is rotated around
the Z-Axis. Notice that the new X and Y values are stored in different variables. Then,
lines are drawn, offset by MIDX and MIDY to the center of the screen.

It may seem silly to store the original values for X1, Y1, X2, Y2, X3, Y3, X4 and Y4 in
function MakeHexagon if they are only used once to calculate angle and radius values.
Hang in there, because this silliness will soon be explained, and proper understanding of
why we did it this way will make 3d graphics easier.

This stuff may require some multiple chewing before it soaks in properly. Please keep one
thing in mind. This book does not attempt to teach Geometry, Trigonometry or any other
type of mathematics. Three-dimensional graphics cannot exist without using a lot of math.
We will try to make some attempt at explaining some of the math involved but please do
not feel bad if this stuff does not make sense right away. This is especially true if you have
not have had a Trigonometry or Pre-Calculus class.

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

13.4 ROTATING 2D OBJECTS IN 3D SPACE

We are going to put the math skills, and concepts of the previous sections to work and
start rotating the XY plane. We have operated strictly in a two-dimensional world. This is
logical because the screen is two-dimensional, but if we hope to play around with 3d
graphics then we need to think about handling the third dimension.

In the coming programs you need to understand the difference between coordinates and
resultants. Mathematically speaking there is a correct X coordinate, Y coordinate and
Z coordinate for every point in 3d space. When we write 3d programs we must operate
with the XResultant, YResultant and ZResultant. These resulants are also coordinates,
but they are the coordinates that we need to use in 2d space to make the graphics objects
appear to be in 3d space. The tricks used to make a drawn 2d object appear to be a 3d
object are called perspective. Most people are familiar with this technique. Railroad
tracks are not drawn parallel but angled towards each other in the distance. The issue of
how perspective is achieved will be discussed later, but it needs to be mentioned now to
justify why we will be using some weird sounding critters like XResultant, YResultant and
ZResultant.

Chapter XIII Three Dimensional Graphics -13.12-


Generally speaking this means that 3d graphics uses a two-step process. First, a program
needs to compute correct mathematical coordinates. Then some formula needs to be
used to compute screen resultants that determine where the pixels should be displayed.
This is a tricky process and creates considerable complexity.

ROTATING AROUND THE Y AXIS

Now back to the immediate task at hand. Program CH13_06.CPP rotates a square
around the Y Axis. We are not concerned with perspective right now. We are trying to
make the square rotate away from the monitor towards us and then into the monitor. This
depth is something that cannot be handled right now so we ignore it and any computation
of ZResultant is a waste of time. The ZResultant calculation is mathematically correct,
however, so we will leave it in comments in the program for later reference. Also, the true
Y coordinate does not change when an object rotates around the Y Axis, so the
YResultant does not need to be calculated. This leaves the XResultant as the only value
to be calculated.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_06.CPP, unlike the last few programs, rotates a ***/
/*** square around the Y-axis. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
float YAn1, YAn2, YAn3, YAn4;
float YRd1, YRd2, YRd3, YRd4;
int XRes1, XRes2, XRes3, XRes4;
float YAngle;

void SetYAngle(int X, int Z, float& YAng, float& R);


void RotateAroundYAxis(int& XRes, float OrgAng, float Radius, float YAng);
void MakeSquare();
void DrawSquare(float YAngle);
void DrawAxes();

void main()
{
StartGfx();
YAngle = 0.0;
MakeSquare();
do
{
ClearGfx(0);
DrawAxes();
DrawSquare(YAngle);
YAngle = YAngle + PI * 0.01325; // PI / 32
delay(100);
} // End do loop.
while ( !kbhit() );
EndGfx();
} // End main program CH13_06.CPP.

Chapter XIII Three Dimensional Graphics -13.13-


void SetYAngle(int X, int Z, float& YAng, float& R)
{
if (Z != 0)
YAng = atan((float) X / Z);
else
if (X > 0)
YAng = PI * 0.5;
else
YAng = PI * 1.5;
if (Z < 0)
YAng = PI + YAng;
R = sqrt(Z * Z + X * X);
} // End void function SetYAngle

void RotateAroundYAxis(int& XRes, float OrgAng, float Radius, float YAng)


{
XRes = Round(sin(OrgAng + YAng) * Radius);
} // End void function RotateAroundYAxis.

void MakeSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 0;
SetYAngle(X1, Z1, YAn1, YRd1);
X2 = 50;
Y2 = -50;
Z2 = 0;
SetYAngle(X2, Z2, YAn2, YRd2);
X3 = 50;
Y3 = 50;
Z3 = 0;
SetYAngle(X3, Z3, YAn3, YRd3);
X4 = -50;
Y4 = 50;
Z4 = 0;
SetYAngle(X4, Z4, YAn4, YRd4);
} // End void function MakeSquare

void DrawSquare(float YAngle)


{
RotateAroundYAxis(XRes1, YAn1, YRd1, YAngle);
RotateAroundYAxis(XRes2, YAn2, YRd2, YAngle);
RotateAroundYAxis(XRes3, YAn3, YRd3, YAngle);
RotateAroundYAxis(XRes4, YAn4, YRd4, YAngle);

Line(XRes1+MIDX, Y1+MIDY, XRes2+MIDX, Y2+MIDY, 1);


Line(XRes2+MIDX, Y2+MIDY, XRes3+MIDX, Y3+MIDY, 2);
Line(XRes3+MIDX, Y3+MIDY, XRes4+MIDX, Y4+MIDY, 3);
Line(XRes4+MIDX, Y4+MIDY, XRes1+MIDX, Y1+MIDY, 4);
} // End void function DrawSquare.

void DrawAxes()
{
Line(0, MIDY, MAXX, MIDY, 15);
Line(MIDX, 0, MIDX, MAXY, 15);
} // End void function DrawAxes.

────────────────────────────────────────────────────────────────────────────────

Chapter XIII Three Dimensional Graphics -13.14-


This program is not all that different from the previous example, as far as code and logic is
concerned. The SetYAngle function is pretty much identical to the SetZAngle function in
the library. Notice the special exceptions to prevent pesky divide-by-zero errors for certain
coordinates. RotateAroundYAxis looks very similar to the previous rotation formula. In
fact, we felt that by including the commented line, you could better see the whole formula,
even though in this program, ZRes calculations have no effect. Overall, little is different
about this program. Interested readers can check out the Y-Axis rotation formula using
their own Geometry skills.

ROTATING AROUND THE X AXIS

It will take only some small changes to rotate the square around the X Axis. Once again
we will ignore the ZResultant because depth is not available on the monitor, at least not
without perspective tricks. This time the X coordinates do not change, which leaves only
the YResultant to be computed. Program CH13_07.CPP demonstrates this rotation. You
will find the source using the same logic as the previous program.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_07.CPP rotates a square around the X-axis. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
float XAn1, XAn2, XAn3, XAn4;
float XRd1, XRd2, XRd3, XRd4;
int YRes1,YRes2, YRes3, YRes4;
float XAngle;

void SetXAngle(int Y, int Z, float& XAng, float& R);


void RotateAroundXAxis(int& YRes, float OrgAng, float Radius, float XAng);
void MakeSquare();
void DrawSquare(float XAngle);
void DrawAxes();

void main()
{
StartGfx();
XAngle = 0.0;
MakeSquare();
do
{
ClearGfx(0);
DrawAxes();
DrawSquare(XAngle);
XAngle = XAngle + 0.03125;
delay(100);
} // End do loop.
while ( !kbhit() );

Chapter XIII Three Dimensional Graphics -13.15-


EndGfx();
} // End main program CH13_07.CPP.

void SetXAngle(int Y, int Z, float& XAng, float& R)


{
if (Z != 0)
XAng = atan((float) Y / Z);
else
if (Y > 0)
XAng = PI * 0.5;
else
XAng = PI * 1.5;
if (Z < 0)
XAng = PI + XAng;
R = sqrt(Z*Z+Y*Y);
} // End void function SetXAngle.

void RotateAroundXAxis(int& YRes, float OrgAng, float Radius, float XAng)


{
YRes = Round(sin(OrgAng + XAng) * Radius);
} // End void function RotateAroundYAxis.

void MakeSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 0;
SetXAngle(Y1, Z1, XAn1, XRd1);
X2 = 50;
Y2 = -50;
Z2 = 0;
SetXAngle(Y2, Z2, XAn2, XRd2);
X3 = 50;
Y3 = 50;
Z3 = 0;
SetXAngle(Y3, Z3, XAn3, XRd3);
X4 = -50;
Y4 = 50;
Z4 = 0;
SetXAngle(Y4, Z4, XAn4, XRd4);
} // End void function MakeSquare.

void DrawSquare(float XAngle)


{
RotateAroundXAxis(YRes1, XAn1, XRd1, XAngle);
RotateAroundXAxis(YRes2, XAn2, XRd2, XAngle);
RotateAroundXAxis(YRes3, XAn3, XRd3, XAngle);
RotateAroundXAxis(YRes4, XAn4, XRd4, XAngle);

Line(X1+MIDX, YRes1+MIDY, X2+MIDX, YRes2+MIDY,1);


Line(X2+MIDX, YRes2+MIDY, X3+MIDX, YRes3+MIDY,2);
Line(X3+MIDX, YRes3+MIDY, X4+MIDX, YRes4+MIDY,3);
Line(X4+MIDX, YRes4+MIDY, X1+MIDX, YRes1+MIDY,4);
} // End void function DrawSquare.

void DrawAxes()
{
Line(0,MIDY,MAXX,MIDY,15);
Line(MIDX,0,MIDX,MAXY,15);
} // End void function DrawAxes

Chapter XIII Three Dimensional Graphics -13.16-


────────────────────────────────────────────────────────────────────────────────

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

13.5 ADDING PERSPECTIVE TO GRAPHICS

Perspective is a big deal in our world. It is all around us. Every time that we watch
television, go to a movie or look at somebody's pictures, we are exposed to perspective.
We walk, eat and breathe in 3d space, but at the same time we constantly represent this
3d space in a 2d environment. A camera has no problems with perspective at all. It picks
up all the 3d images, and what is closer is larger, and what is further away becomes
smaller. Basically, our eyes are fooled to perceive the true 2d object to be a
representation of a 3d object.

Consider program CH13_08.CPP. This program displays a simple little cube sitting still
and minding its own business. The cube makes some attempt at 3d perspective by using
angled lines and making one square appear to be "behind" the "front" square. This
program actually shows the cube three times. The first display shows a cube of all white
lines. Now, which square is the front and which square is the back? You will probably find
that you mind switches them, and some people will argue which is in front. Your 3d clues
are confusing. The perspective is not very good. Even some minor tricks can assist you.
Press the <Enter> key once and you will see the same cube with some color added. It is
more clear now that the green square represents the "front" of the cube. This perception
is created because the green square lines cover the red square lines. The red lines
appear to be "behind" the green lines. Press <Enter> once more. Now the red square
appears to be in front due to the same "cover line" logic.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_08.CPP displays three cubes using simple routines. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"

void DrawCube(Byte Color1, Byte Color2);

void main()
{
StartGfx();
DrawCube(15, 15);
Stop();
DrawCube(2, 4);
Stop();
DrawCube(4, 2);

Chapter XIII Three Dimensional Graphics -13.17-


Stop();
EndGfx();
} // End main program CH13_08.CPP.

void DrawCube(Byte Color1,Byte Color2)


{
Rectangle(100, 100, 150, 150, Color1, OPEN);
Rectangle(125, 125, 175, 175, Color2, OPEN);
Line(100, 100, 125, 125, 15);
Line(150, 100, 175, 125, 15);
Line(100, 150, 125, 175, 15);
Line(150, 150, 175, 175, 15);
} // End void function DrawCube.

────────────────────────────────────────────────────────────────────────────────
Program CH13_08 is not an attempt to demonstrate perspective. This program is meant
to illustrate that correct perspective is not easily achieved. You will find that programs that
try hard to be correct can still be perceived differently from their intentions. Our eyes are
easily fooled, and it takes a number of tricks to put our mind at ease that we see an image
only one possible way.

Let us take a look at two perspective examples side by side. Program CH13_09.CPP
shows two rectangles. At the start of execution you see two identical rectangles side by
side. You have no clue that these rectangles are meant to be rotated. now press the
<Enter> key. Both rectangles rotate 45 degrees on the X Axis. This means that the
bottom of the rectangle is closest to the viewer, and the top of the rectangle is farther
away.

The rectangle on the left works in a strict mathematical sense as if you looked at the
rectangle straight on without any perspective. The result is that you see a shorter
rectangle. This view makes the top and bottom of the rectangle come closer to each
other.

The rectangle on the right side uses perspective, and it is easier to see that rotation has
occurred. The top of the rectangle appears further away. Now look at the code of
program CH13_09.CPP. There is some explaining to do. Perspective is no obvious topic
by anybody's measuring stick.
────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_09.CPP displays a square on the screen. It is not created ***/
/*** flush to the screen--it is actually laid back 45 degrees. ***/
/*** If you press enter, the square pops into perspective. ***/
/**********************************************************************/
/* This is how perspective works: The distance of a point from the origin along the
x axis is determined by the x coordinate:
- Overhead view of entire monitor box: -

Z Axis -> | +------ This (n) is a point at (X,0,0).


| v
|
----------*--------n--- <- X axis
│ | : │
│ | :║ │ (The * is the vertical axis)

Chapter XIII Three Dimensional Graphics -13.18-


│ | : ║ │
│ | : ║ │
│ | : v │
└─================!====0====─┘ <- glass on monitor
/ | :
monitor | : ^
casing |: +-- This is where (n) showed up on the monitor
: in the old system, where we ignored the
.-o-o-. Z coordinate.
| |
^^^
(Observer)

Before, if we had a point at X (n), we placed it at the corresponding


X coordinate on the monitor (0). However, this resulted in a
"straight-on" appearance which is quite unappealing and not
mathematically correct. The line (made up of colons) indicates the
most direct path from the viewer's eyes to the point "buried inside
the monitor." The intersection of this line (called the line-of-sight)
and the glass (called the viewing plane) is the point at which we
should place the pixel on the monitor. Note that in this case this point
is "inside" of the original X location (the "0"), or more towards the
origin.

Before we can begin to derive a formula, we have to determine what


the distance is from the observer to the xyz origin, in pixels.
The screen is 320 pixels across, and most people sit about 640 away
from the screen. That makes the focal length 640 pixels.
In addition, we don't want the origin to be exactly on the screen.
So, we add an additional amount of your preference is used to back
the origin away from the surface of the screen. Therefore, any number
larger than 320 is needed for the ZDistance.

Now that we know that distance, look at the problem as a pair


of similar right triangles.

Overhead view:

X Axis
|
V

B
n××××××
| ××××××
| ××××××
| ××××××
X | ×××××× ║
| ××××××║b
| !×××××
| X' ║ ××××××
|┐ ║┐ Θ ×××××× o--
+------------------------------------║--------------------- ( Observer

Chapter XIII Three Dimensional Graphics -13.19-


A Z Axis ║a C o--

Monitor glass

|---------------------|
focal length
(ZDist')

|----------------------------------------------------------|
ZDistance

Basic geometry tells us that two triangles with two congruent


angles are similar meaning that they have proportional side lengths
These two triangles (ABC) and (abC) share a right angle and an
angle at Θ, meaning they are similar and therefore proportional.

That means that this ratio is :

ZDistance X
------------ = ---
Focal Length X'

A little bit of algebra follows:

ZDistance * X' = X * Focal Length

X * Focal Length
X' = ----------------
ZDistance

This is great for any point located directly on the x-axis. What
if a point is in front of that axis (closer to the screen and the
observer) or farther away?

It should be apparant without another diagram that the ZDistance


is what changes, and nothing else. The ZDistance now equals
the distance from the observer to the origin, minus the offset of
the z coordinate (the depth into the screen from the origin). The
minus is due to the fact that positive Z coordiantes go toward the
screen, and negative Z coordiantes move away. That is, that
a coordinate which is in front of the origin (or the XY plane) has
a positive value, making the ZDistance smaller (shorter), meaning we
have to subtract. Likewise, negative coordinates behind the origin
are subtracted from the ZDistance, and it becomes larger, because
the ZDistance (which is positive) minus a negative number becomes even
larger.

The formula now changes a little:

ZDistance - Z X
--------------- = ---
Focal Length X'

Chapter XIII Three Dimensional Graphics -13.20-


(ZDistance + Z) * X' = X * Focal Length

X * Focal Length
X' = ----------------
ZDistance - Z

Likewise, if all of this was redone using the Y axis, the results would
be the same, with Y substituted in for X.

*/

#include "GFXLIB8.HPP"
#include "GRAF3D02.HPP"

const ZDIST = 400;


const FOCAL = 300;

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
float XRd1, XRd2, XRd3, XRd4;
float XAn1, XAn2, XAn3, XAn4;

void CreateSquare();
void DrawAxes();
void DrawSquare(int XOffset);
void CalculatePerspective();
void RotateSquareBack();

void main()
{
StartGfx();
CreateSquare();
ClearGfx(0);
DrawAxes();
DrawSquare(MIDX / 2);
DrawSquare(MAXX - MIDX / 2);
Stop();
RotateSquareBack();
ClearGfx(0);
DrawAxes();
DrawSquare(MIDX / 2);
CalculatePerspective();
DrawSquare(MAXX - MIDX / 2);
Stop();
EndGfx();
} // End main program CH13_09.CPP.

void CreateSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 0;
X2 = 50;
Y2 = -50;
Z2 = 0;
X3 = 50;
Y3 = 50;
Z3 = 0;
X4 = -50;
Y4 = 50;

Chapter XIII Three Dimensional Graphics -13.21-


Z4 = 0;
SetXAngle(Y1, Z1, XAn1, XRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);
} // End void function CreateSquare.

void DrawAxes()
{
Line(MIDX / 2, 0, MIDX / 2, MAXY, 8);
Line(MAXX - MIDX / 2, 0, MAXX - MIDX / 2, MAXY, 8);
Line(0, MIDY, MAXX, MIDY, 8);
} // End void function DrawAxes.

void DrawSquare(int XOffset)


{
Line(X1+XOffset, Y1+MIDY, X2+XOffset, Y2+MIDY, 1);
Line(X2+XOffset, Y2+MIDY, X3+XOffset, Y3+MIDY, 2);
Line(X3+XOffset, Y3+MIDY, X4+XOffset, Y4+MIDY, 3);
Line(X4+XOffset, Y4+MIDY, X1+XOffset, Y1+MIDY, 4);
} // End void function DrawSquare.

void CalculatePerspective()
{
X1 = (X1 * FOCAL) / (ZDIST - Z1);
Y1 = (Y1 * FOCAL) / (ZDIST - Z1);
X2 = (X2 * FOCAL) / (ZDIST - Z2);
Y2 = (Y2 * FOCAL) / (ZDIST - Z2);
X3 = (X3 * FOCAL) / (ZDIST - Z3);
Y3 = (Y3 * FOCAL) / (ZDIST - Z3);
X4 = (X4 * FOCAL) / (ZDIST - Z4);
Y4 = (Y4 * FOCAL) / (ZDIST - Z4);
} // End void function CalculatePerspective.

void RotateSquareBack()
{
RotateAroundXAxis(Y1, Z1, XAn1, XRd1, -PI * 0.25);
RotateAroundXAxis(Y2, Z2, XAn2, XRd2, -PI * 0.25);
RotateAroundXAxis(Y3, Z3, XAn3, XRd3, -PI * 0.25);
RotateAroundXAxis(Y4, Z4, XAn4, XRd4, -PI * 0.25);
} // End void function RotateSquareBack.

────────────────────────────────────────────────────────────────────────────────

CREATING A PERSPECTIVE FORMULA

Chapter XIII Three Dimensional Graphics -13.22-


Figure 2
The whole point of perspective is that we want 3d objects to look correct on 2d surfaces.
You need very little math to recognize bad perspective. In an early chapter we used the
Aspect Ratio precisely for the reason of making the circle look "correct". Our circle
formula was correct mathematically speaking but the graphics object we saw was an egg,
not a circle. That circle stuff had nothing to do with perspective, but it has everything to do
with the desire to make pictures look correct. Let us consider what happens when
somebody views a monitor. A diagram of a computer monitor is shown in Figure 2. Keep
in mind that this is the top view of the monitor.

The first thing we need to do is visualize 3d space in the monitor. It is not possible to draw
the X,Y and Z axes correctly. From the top view we see the XZ plane and the Y Axis is
vertical from the origin straight up to the view point from above.

Now imagine a pixel at point 1. This is at location (X,0,0), which is mathematically


speaking "inside" the monitor. Without perspective this point is plotted on the monitor pixel
at point 2. From the point of view of the observer this will create a distortion. You will
notice in Figure 2 that a line is drawn from the observer to Pixel Point 1. This line is called
the line-of-sight. The intersection of this line and the glass of the monitor (called the
viewing plane) is the point at which we should place the pixel on the monitor. This point
is located at point 3 on the diagram.

Basically, our perspective problem revolves around determining the location of point 3.

Chapter XIII Three Dimensional Graphics -13.23-


Before we can begin to derive a formula, we have to determine what the distance is from
the observer to the XYZ origin, in pixels. It is possible to use a ruler and more geometry to
calculate this infomation, but trial-and-error is good enough for us. We like a focal
length of 300 pixels. In addition, we do not want the origin to be exactly on the surface of
the screen. If we did, how could we plot pixels whose actual location is between the
viewer's nose and the glass? So, we add an additional amount to back the origin away
from the screen. Therefore, any number larger than the focal length is needed for the
ZDistance. Now that we know that distance, look at the problem as a pair of similar
triangles, shown in the overhead view of Figure 3, shown next.

Figure 3
Geometry tells us that two triangles with two congruent angles are similar meaning that
they have proportional side lengths. These two triangles (ABC) and (abC) both have a
right angle, and they share an angle at C. This means that the triangles are similar and
therefore proportional.

That means that this ratio is true:

ZDistance = X
FocalLength X’

Chapter XIII Three Dimensional Graphics -13.24-


ZDistance * X’ = X * FocalLength

X * FocalLength

Using algebra we now get the following:

X1 = ZDistance

This is great for any point located directly on the X Axis. What if the point is in front of that
axis (closer to the screen and the observer) or farther away?

The ZDistance is what changes, and nothing else. The ZDistance now equals the
distance from the observer to the origin, minus the offset of the Z Coordinate (the depth
into the screen from the origin). The minus is due to the fact that positive Z coordinates
move away. That is, that a coordinate which is in front of the origin (or the XY plane) has a
positive value, making the ZDistance smaller (shorter), meaning we have to subtract.
Likewise, negative coordinates behind the origin are subtracted from the ZDistance, and it
becomes larger, because the ZDistance (which is positive) minus a negative number
becomes even larger.

The formula now changes a little:

ZDistance – Z X
FocalLength = X’

(ZDistance + Z) * X’ = X * Focal Length

X * Focal Length
X’ = Z Distance – Z

Chapter XIII Three Dimensional Graphics -13.25-


Likewise, if all of this was redone using the Y Axis, the results would be the same, with Y
substituted in for X. Look at function CalculatePerspective, to see how these formulas
are used in program GFX3D09. It may still seem bizzare. Try working out the math for
yourself.

────────────────────────────────────────────────────────────────────────────────
void CalculatePerspective()
{
X1 = (X1 * FOCAL) / (ZDIST - Z1);
Y1 = (Y1 * FOCAL) / (ZDIST - Z1);
X2 = (X2 * FOCAL) / (ZDIST - Z2);
Y2 = (Y2 * FOCAL) / (ZDIST - Z2);
X3 = (X3 * FOCAL) / (ZDIST - Z3);
Y3 = (Y3 * FOCAL) / (ZDIST - Z3);
X4 = (X4 * FOCAL) / (ZDIST - Z4);
Y4 = (Y4 * FOCAL) / (ZDIST - Z4);
} // End void function CalculatePerspective.
────────────────────────────────────────────────────────────────────────────────

ROTATION AROUND THE X AXIS WITH PERSPECTIVE

Program CH13_10.CPP is similar to the previous program. This time we will let the two
squares continue to rotate around the X Axis. The square on the left will rotate without
perspective, and the plane on the right will use the perspective formulas. When you look
at the main body of the program you will note a loop that calls function DrawSquare twice.
Each time after the first DrawSquare call to display the left square, function
CalculatePerspective is called before the second square is drawn.

Also, the need for resulting variables makes a little more sense in this program. We do
not want to store the "adjusted" persepective values for rotation. TrueX and TrueY are
used instead to keep perspective and original coordinates seperate.

────────────────────────────────────────────────────────────────────────────────

/*************************************************************************/
/*** CH13_10.CPP compares two squares rotating around the X axis. The ***/
/*** one on the left does not use persepective, and the one on the ***/
/*** right does. ***/
/*************************************************************************/

#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
int TrueX1, TrueY1, TrueX2, TrueY2;
int TrueX3, TrueY3, TrueX4, TrueY4;
float XAn1, XAn2, XAn3, XAn4;
float XRd1, XRd2, XRd3, XRd4;
float XAngle;

void CreateSquare();

Chapter XIII Three Dimensional Graphics -13.26-


void DrawAxes();
void RotateSquare(float XAngle);
void DrawSquare(int XOffset);
void CalcPersp();

void main()
{
StartGfx();
CreateSquare();
XAngle = 0.0;
do
{
RotateSquare(XAngle);
DrawAxes();
DrawSquare(MIDX / 2);
CalcPersp();
DrawSquare(MAXX - MIDX / 2);
XAngle = XAngle + 0.1;
delay(100);
} // End do loop.
while ( !kbhit() );
Stop();
} // End main program CH13_10.CPP.

void CreateSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 0;
X2 = 50;
Y2 = -50;
Z2 = 0;
X3 = 50;
Y3 = 50;
Z3 = 0;
X4 = -50;
Y4 = 50;
Z4 = 0;
SetXAngle(Y1, Z1, XAn1, XRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);
} // End void function CreateSquare.

void DrawAxes()
{
ClearGfx(0);
Line((MIDX / 2), 0, (MIDX / 2), MAXY, 8);
Line(MAXX - (MIDX / 2), 0, MAXX - (MIDX / 2), MAXY, 8);
Line(0, MIDY, MAXX, MIDY, 8);
} // End void function DrawAxes.

void RotateSquare(float XAngle)


{
RotateAroundXAxis(Y1, Z1, XAn1, XRd1, XAngle);
RotateAroundXAxis(Y2, Z2, XAn2, XRd2, XAngle);
RotateAroundXAxis(Y3, Z3, XAn3, XRd3, XAngle);
RotateAroundXAxis(Y4, Z4, XAn4, XRd4, XAngle);
TrueX1 = X1;
TrueY1 = Y1;
TrueX2 = X2;
TrueY2 = Y2;
TrueX3 = X3;

Chapter XIII Three Dimensional Graphics -13.27-


TrueY3 = Y3;
TrueX4 = X4;
TrueY4 = Y4;
} // End void function RotateSquare.

void DrawSquare(int XOffset)


{
Line(TrueX1+XOffset, TrueY1+MIDY, TrueX2+XOffset, TrueY2+MIDY, 1);
Line(TrueX2+XOffset, TrueY2+MIDY, TrueX3+XOffset, TrueY3+MIDY, 2);
Line(TrueX3+XOffset, TrueY3+MIDY, TrueX4+XOffset, TrueY4+MIDY, 3);
Line(TrueX4+XOffset, TrueY4+MIDY, TrueX1+XOffset, TrueY1+MIDY, 4);
} // End void function DrawSquare.

void CalcPersp()
{
CalculatePerspective(TrueX1,TrueY1,X1,Y1,Z1);
CalculatePerspective(TrueX2,TrueY2,X2,Y2,Z2);
CalculatePerspective(TrueX3,TrueY3,X3,Y3,Z3);
CalculatePerspective(TrueX4,TrueY4,X4,Y4,Z4);
} // End void function CalcPersp.

────────────────────────────────────────────────────────────────────────────────

CalculatePerspective is included in the new GRAF3D03.LIB along with all three angle
setting and rotation routines.

ROTATION AROUND THE Y AXIS WITH PERSPECTIVE

All the groundwork is now available for rotating around another axis. We have the formula
available to compute perspective. We also know how to compute required information of
the resultants. What changes the rotation is the different resultants that are computed. In
the previous program we rotated around the X Axis. This meant that we needed to
compute the YResultant and the ZResultant. Now we want to rotate around the Y Axis.
All the Y coordinates stay fixed and now we must compute the XResultant and the
ZResultant. Everything else follows the same logic.

────────────────────────────────────────────────────────────────────────────────
/***********************************************************************/
/*** CH13_11.CPP compares 2 squares rotating around the Y axis. The ***/
/*** one on the left does not use persepective, and the one on the ***/
/*** right does. ***/
/***********************************************************************/

#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
int TrueX1, TrueY1, TrueX2, TrueY2;
int TrueX3, TrueY3, TrueX4, TrueY4;
float YAn1, YAn2, YAn3, YAn4;

Chapter XIII Three Dimensional Graphics -13.28-


float YRd1, YRd2, YRd3, YRd4;
float YAngle;

void CreateSquare();
void DrawAxes();
void RotateSquare(float YAngle);
void DrawSquare(int XOffset);
void CalcPersp();

Chapter XIII Three Dimensional Graphics -13.29-


void main()
{
StartGfx();
CreateSquare();
YAngle = 0.0;
do
{
RotateSquare(YAngle);
DrawAxes();
DrawSquare(MIDX / 2);
CalcPersp();
DrawSquare(MAXX - MIDX / 2);
YAngle = YAngle + 0.1;
delay(100);
} // End do loop.
while ( !kbhit() );
Stop();
} // End main program CH13_11.CPP.

void CreateSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 0;
X2 = 50;
Y2 = -50;
Z2 = 0;
X3 = 50;
Y3 = 50;
Z3 = 0;
X4 = -50;
Y4 = 50;
Z4 = 0;
SetYAngle(X1, Z1, YAn1, YRd1);
SetYAngle(X2, Z2, YAn2, YRd2);
SetYAngle(X3, Z3, YAn3, YRd3);
SetYAngle(X4, Z4, YAn4, YRd4);
} // End void function CreateSquare.

void DrawAxes()
{
ClearGfx(0);
Line((MIDX / 2), 0, (MIDX / 2), MAXY, 8);
Line(MAXX - (MIDX / 2), 0, MAXX - (MIDX / 2), MAXY, 8);
Line(0, MIDY, MAXX, MIDY, 8);
} // End void function DrawAxes.

void RotateSquare(float YAngle)


{
RotateAroundYAxis(X1, Z1, YAn1, YRd1, YAngle);
RotateAroundYAxis(X2, Z2, YAn2, YRd2, YAngle);
RotateAroundYAxis(X3, Z3, YAn3, YRd3, YAngle);
RotateAroundYAxis(X4, Z4, YAn4, YRd4, YAngle);
TrueX1 = X1;
TrueY1 = Y1;
TrueX2 = X2;
TrueY2 = Y2;
TrueX3 = X3;
TrueY3 = Y3;
TrueX4 = X4;
TrueY4 = Y4;
} // End void function RotateSquare.

void DrawSquare(int XOffset)

Chapter XIII Three Dimensional Graphics -13.30-


{
Line(TrueX1+XOffset, TrueY1+MIDY, TrueX2+XOffset, TrueY2+MIDY, 1);
Line(TrueX2+XOffset, TrueY2+MIDY, TrueX3+XOffset, TrueY3+MIDY, 2);
Line(TrueX3+XOffset, TrueY3+MIDY, TrueX4+XOffset, TrueY4+MIDY, 3);
Line(TrueX4+XOffset, TrueY4+MIDY, TrueX1+XOffset, TrueY1+MIDY, 4);
} // End void function DrawSquare.

Chapter XIII Three Dimensional Graphics -13.31-


void CalcPersp()
{
CalculatePerspective(TrueX1, TrueY1, X1, Y1, Z1);
CalculatePerspective(TrueX2, TrueY2, X2, Y2, Z2);
CalculatePerspective(TrueX3, TrueY3, X3, Y3, Z3);
CalculatePerspective(TrueX4, TrueY4, X4, Y4, Z4);
} // End void function CalcPersp.

────────────────────────────────────────────────────────────────────────────────

ROTATION AROUND THE Z AXIS WITH PERSPECTIVE

The last program in this sequence will rotate two squares around the Z Axis. By now you
know the routine. The ZResultant is ignored because it does not change and the
XResultant and the YResultant are computed. The square on the left does not get the
benefit of perspective and the square on the right is called after the perspective formulas
are activated. So what do you see in program CH13_12.CPP? Two lovely squares that
show no discernible difference, with or without perspective. The reason for this oddity is
the issue of depth. Perspective tries to give the illusion of depth. When an object rotates
around the Z Axis depth is not an issue. Every point on the object stays the same
distance away from the observer. Furthermore, the object rotates in the XY plane, which
happens to be parallel to the plane of the computer monitor screen.

────────────────────────────────────────────────────────────────────────────────
/***********************************************************************/
/*** CH13_12.CPP compares 2 squares rotating around the Z axis. The ***/
/*** one on the left does not use perspective, and the one on the ***/
/*** right does. Notice that there is no discernable difference. ***/
/***********************************************************************/
#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
int TrueX1, TrueY1, TrueX2, TrueY2;
int TrueX3, TrueY3, TrueX4, TrueY4;
float ZAn1, ZAn2, ZAn3, ZAn4;
float ZRd1, ZRd2, ZRd3, ZRd4;
float ZAngle;

void CreateSquare();
void DrawAxes();
void RotateSquare(float ZAngle);
void DrawSquare(int XOffset);
void CalcPersp();

void main()
{
StartGfx();
CreateSquare();
ZAngle = 0.0;
do

Chapter XIII Three Dimensional Graphics -13.32-


{
RotateSquare(ZAngle);
DrawAxes();
DrawSquare(MIDX / 2);
CalcPersp();
DrawSquare(MAXX - MIDX / 2);
ZAngle = ZAngle + 0.1;
delay(100);
} // End do loop.
while ( !kbhit() );
Stop();
} // End main program CH13_12.CPP.

void CreateSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 0;
X2 = 50;
Y2 = -50;
Z2 = 0;
X3 = 50;
Y3 = 50;
Z3 = 0;
X4 = -50;
Y4 = 50;
Z4 = 0;
SetZAngle(X1, Y1, ZAn1, ZRd1);
SetZAngle(X2, Y2, ZAn2, ZRd2);
SetZAngle(X3, Y3, ZAn3, ZRd3);
SetZAngle(X4, Y4, ZAn4, ZRd4);
} // End void function CreateSquare.

void DrawAxes()
{
ClearGfx(0);
Line((MIDX / 2), 0, (MIDX / 2), MAXY, 8);
Line(MAXX - (MIDX / 2), 0, MAXX - (MIDX / 2), MAXY, 8);
Line(0, MIDY, MAXX, MIDY, 8);
} // End void function DrawAxes.

void RotateSquare(float ZAngle)


{
RotateAroundZAxis(X1, Y1, ZAn1, ZRd1, ZAngle);
RotateAroundZAxis(X2, Y2, ZAn2, ZRd2, ZAngle);
RotateAroundZAxis(X3, Y3, ZAn3, ZRd3, ZAngle);
RotateAroundZAxis(X4, Y4, ZAn4, ZRd4, ZAngle);
TrueX1 = X1;
TrueY1 = Y1;
TrueX2 = X2;
TrueY2 = Y2;
TrueX3 = X3;
TrueY3 = Y3;
TrueX4 = X4;
TrueY4 = Y4;
} // End void function RotateSquare.

void DrawSquare(int XOffset)


{
Line(TrueX1+XOffset, TrueY1+MIDY, TrueX2+XOffset, TrueY2+MIDY, 1);
Line(TrueX2+XOffset, TrueY2+MIDY, TrueX3+XOffset, TrueY3+MIDY, 2);
Line(TrueX3+XOffset, TrueY3+MIDY, TrueX4+XOffset, TrueY4+MIDY, 3);
Line(TrueX4+XOffset, TrueY4+MIDY, TrueX1+XOffset, TrueY1+MIDY, 4);
} // End void function DrawSquare.

Chapter XIII Three Dimensional Graphics -13.33-


void CalcPersp()
{
CalculatePerspective(TrueX1, TrueY1, X1, Y1, Z1);
CalculatePerspective(TrueX2, TrueY2, X2, Y2, Z2);
CalculatePerspective(TrueX3, TrueY3, X3, Y3, Z3);
CalculatePerspective(TrueX4, TrueY4, X4, Y4, Z4);
} // End void function CalcPersp.
────────────────────────────────────────────────────────────────────────────────
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
13.6 ROTATION AROUND MORE THAN 1 AXIS

You are slowly expected to become more independent. Complete, line-by-line


explanations of 3d graphics programs use up too much paper. Simply put, you can write a
book just on 3d graphics and still worry about being too lengthy. For us, 3d graphics is
only one chapter. We have just passed the stage where you received rather lengthy
explanations. In the next stage we provide programs and some information, but lengthy
comments inside each program will be your primary source of 3d knowledge. It will take
more effort to understand the logic in each example. In the final stage a variety of 3d
graphics programs will be shown that are interesting to watch and play with. However,
these programs are very complex and require serious studying.

In this section we are moving to greater complexity by rotating around more than one axis.
This is quite a bit more complex. All the formulas are basically the same but the end result
is a combination of various computations to compute the proper pixel display location.
Program CH13_13.CPP rotates around the X Axis and the Y Axis. The rotation is
somewhat strange looking. It looks like a diagonal rotation around the origin between the
X Axis and the Y Axis. This program has a problem called a "propagated error".

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_13.CPP rotates a square around two axes, X and Y. This ***/
/*** program will have some errors if run for a few minutes. ***/
/**********************************************************************/

/***************************************************************************
This program perfoms a rotation around two axes, but several things which
are not immediately apparent are neccessary. This program is intended not
to work correctly all the time ... as is noted below.

First, try to visualize the problem of rotating a point around two seperate
axes. Do you see that there are two main steps, rotation around the first
axis, and then the second.

Step 1: The X-Axis

Part a: Calculate the X angle and the radius between (Z,Y)

Wait--why? Before we calculated these two values just once: at


the initialization stage. We knew that in X-Axis rotation the radius
and the initial angle would never change.

Chapter XIII Three Dimensional Graphics -13.34-


However, if we are also rotating around other axes, can we be sure
that the X-angle and the x distance are constant? Even if you can't
visualize the change, there is no harm in recalculating these values
before each change. You may want to remove this operation in the
program and see it's effect.

Part b: Do the rotation...

Side view of monitor:


(From left side)
This is a bit small, but the point at (Z,Y)
n | ║ is the "o" with radius "r" and the point after
;""r'| r ,,o ║ rotation (z',y') is noted with a "n".
y' ;┐ " | ,,'' ┌: y ║
-----------+------------║ The line at r (made of commas) rotates to the
z' | z ║ line at r' (made of double quotes).
| ║
| ║ Just for illustration, assume (Z,Y) as (3,2)
and (z',y') as (-2,3).
^^^
Monitor glass

If we assume a rotation by some amount from (3,2) to (-2,3) (which is


contrived for the purpose of illustration, ignore the math), then
the total change in the X,Y,Z coordinates is

(x,2,3) ===> X axis rotation ===> (x,3,-2)

This does make sense, with the previous programs showing how the X coordinate
does not change in X axis rotation. Now it is time for Y axis rotation:

Step 2: The Y-Axis

Part a: Calculate the Y angle and the radius between (x,z)

This can be argued for the same reason as in step one, but consider
our current (X,Y,Z) coordinates: (X,3,-2).

Part b: Do the rotation...

We recall that our Z value and Y value have been recently changed.
So, we use those values from then on.

Top of view of monitor:

This is a bit small, but the point at (z,x)


n | ║ is the "o" with radius "r" and the point after
;""r'| r ,,o ║ rotation (z',x') is noted with a "n".
x' ;┐ " | ,,'' ┌: x ║
-----------+------------║ The line at r (made of commas) rotates to the
z' | z ║ line at r' (made of double quotes).

Chapter XIII Three Dimensional Graphics -13.35-


| ║
| ║ Just for illustration, assume (z,y) as (3,2)
and (z',y') as (-2,3).
^^^
Monitor glass

The best analogy we can think of is the standard "Rubik's cube" puzzle.
If you think about a point on the graph being somewhere on one of the
six outside planes of a "Rubik's" cube, you can rotate that plane in any
increment of 90 degrees. Rotation around two axes must be done seperately
with the "Rubik's" cube, as with rotation of a point around two axes.

This all makes sense, however you will notice two subtle differences.
First, notice that the actual (X,Y,Z) coordinates are changed at each step
according to the rotation. This is necessary because the rotation is a two
step process, and the second step requires the results of the first.
Therefore, the paramaters to RotateSquare remain constant.
The second difference lies in the execution of the program. Watch the square
rotate around for several minutes. You will see it begin to stretch out of
proportion and become a strange figure. This is called "propagated error".

Also, we have left off the newly acquired "perspective" for ease of
illustration.
*****************************************************************************/
#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
float XAn1, XAn2, XAn3, XAn4;
float YAn1, YAn2, YAn3, YAn4;
float XRd1, XRd2, XRd3, XRd4;
float YRd1, YRd2, YRd3, YRd4;

void MakeSquare();
void RotateSquare(float XAngle, float YAngle);
void DrawSquare();

void main()
{
StartGfx();
MakeSquare();
do
{
ClearGfx(0);
Line(MIDX, 0, MIDX, MAXY, 15);
Line(0, MIDY, MAXX, MIDY, 15);
RotateSquare(PI * 0.03125, PI * 0.03125);
DrawSquare();
delay(100);
} // End do loop.
while ( !kbhit() );
EndGfx();
} // End main program CH13_13.CPP.

void MakeSquare()
{
X1 = -50;
Y1 = -50;

Chapter XIII Three Dimensional Graphics -13.36-


Z1 = 50;
X2 = 50;
Y2 = -50;
Z2 = 50;
X3 = 50;
Y3 = 50;
Z3 = 50;
X4 = -50;
Y4 = 50;
Z4 = 50;
SetXAngle(Y1, Z1, XAn1, XRd1);
SetYAngle(X1, Z1, YAn1, YRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetYAngle(X2, Z2, YAn2, YRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetYAngle(X3, Z3, YAn3, YRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);
SetYAngle(X4, Z4, YAn4, YRd4);
} // End void function MakeSquare.

void RotateSquare(float XAngle, float YAngle)


{
SetXAngle(Y1, Z1, XAn1, XRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);

RotateAroundXAxis(Y1, Z1, XAn1, XRd1, XAngle);


RotateAroundXAxis(Y2, Z2, XAn2, XRd2, XAngle);
RotateAroundXAxis(Y3, Z3, XAn3, XRd3, XAngle);
RotateAroundXAxis(Y4, Z4, XAn4, XRd4, XAngle);

SetYAngle(X1, Z1, YAn1, YRd1);


SetYAngle(X2, Z2, YAn2, YRd2);
SetYAngle(X3, Z3, YAn3, YRd3);
SetYAngle(X4, Z4, YAn4, YRd4);

RotateAroundYAxis(X1, Z1, YAn1, YRd1, YAngle);


RotateAroundYAxis(X2, Z2, YAn2, YRd2, YAngle);
RotateAroundYAxis(X3, Z3, YAn3, YRd3, YAngle);
RotateAroundYAxis(X4, Z4, YAn4, YRd4, YAngle);
} // End void function RotateSquare.

void DrawSquare()
{
Line(X1+MIDX, Y1+MIDY, X2+MIDX, Y2+MIDY, 1);
Line(X2+MIDX, Y2+MIDY, X3+MIDX, Y3+MIDY, 2);
Line(X3+MIDX, Y3+MIDY, X4+MIDX, Y4+MIDY, 3);
Line(X4+MIDX, Y4+MIDY, X1+MIDX, Y1+MIDY, 4);
} // End void function DrawSquare.

────────────────────────────────────────────────────────────────────────────────

It makes sense that you need to recalculate the angles and radii of each point when you
are rotating around multiple axes, because you can be assured they will interfere with
each other and get each other out of whack. The proof of this is elegant but complex, and
this is not a Geometry book anyway.

FIXING THE PROPAGATED ERROR

Chapter XIII Three Dimensional Graphics -13.37-


Program CH13_14.CPP also rotates a plane around the X Axis and the Y Axis. This
program fixes the "propagated error" that the previous program created with round-off
problems. Continuous rounding off made each calculation less accurate in the previous
program. This program makes sure that the original coordinates are used for all
computations. We are making lots of copies, but every copy is made from the same clean
original.

────────────────────────────────────────────────────────────────────────────────
/***********************************************************************/
/*** CH13_14.CPP rotates a square around two axes, X and Y. It also ***/
/*** corrects the "propigated error" of the last program. ***/
/***********************************************************************/

/**************************************************************************
Why is there propigated error in the last program? The answer lies in
the problem of round-off. At each rotation, the coordinates are rounded.
We start with correct original coordinates, rotate them to some correct
location, and then round off that decimal approximation. This new rounded
off value is now stored as the original value, which is very incorrect.

In this program, we use the original coordinates for every calculation.


These original coordinates for the square never change, just the angles
of rotation are accumulated. The result is that each specific rotation
is rounded, but since those rounded coordinates are only used to once
(to plot pixels on the screen), error is never propigated.
***************************************************************************/

#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
float XAn1, XAn2, XAn3, XAn4;
float YAn1, YAn2, YAn3, YAn4;
float XRd1, XRd2, XRd3, XRd4;
float YRd1, YRd2, YRd3, YRd4;
int XRes1, YRes1, ZRes1, XRes2, YRes2, ZRes2;
int XRes3, YRes3, ZRes3, XRes4, YRes4, ZRes4;
float XAngle, YAngle;

void MakeSquare();
void RotateSquare(float XAngle, float YAngle);
void DrawSquare();

void main()
{
StartGfx();
MakeSquare();
XAngle = 0.0;
YAngle = 0.0;
do
{
ClearGfx(0);
Line(MIDX,0,MIDX,MAXY,15);
Line(0,MIDY,MAXX,MIDY,15);
RotateSquare(XAngle,YAngle);
DrawSquare();
XAngle = XAngle + PI * 0.03125;

Chapter XIII Three Dimensional Graphics -13.38-


YAngle = YAngle + PI * 0.03125;
delay(100);
} // End do loop.
while (!kbhit());
EndGfx();
} // End main program CH13_14.CPP.

void MakeSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 50;
X2 = 50;
Y2 = -50;
Z2 = 50;
X3 = 50;
Y3 = 50;
Z3 = 50;
X4 = -50;
Y4 = 50;
Z4 = 50;
SetXAngle(Y1, Z1, XAn1, XRd1);
SetYAngle(X1, Z1, YAn1, YRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetYAngle(X2, Z2, YAn2, YRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetYAngle(X3, Z3, YAn3, YRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);
SetYAngle(X4, Z4, YAn4, YRd4);
} // End void function MakeSquare.

void RotateSquare(float XAngle, float YAngle)


{
SetXAngle(Y1, Z1, XAn1, XRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);

RotateAroundXAxis(YRes1, ZRes1, XAn1, XRd1, XAngle);


RotateAroundXAxis(YRes2, ZRes2, XAn2, XRd2, XAngle);
RotateAroundXAxis(YRes3, ZRes3, XAn3, XRd3, XAngle);
RotateAroundXAxis(YRes4, ZRes4, XAn4, XRd4, XAngle);

SetYAngle(X1, ZRes1, YAn1, YRd1);


SetYAngle(X2, ZRes2, YAn2, YRd2);
SetYAngle(X3, ZRes3, YAn3, YRd3);
SetYAngle(X4, ZRes4, YAn4, YRd4);

RotateAroundYAxis(XRes1, ZRes1, YAn1, YRd1, YAngle);


RotateAroundYAxis(XRes2, ZRes2, YAn2, YRd2, YAngle);
RotateAroundYAxis(XRes3, ZRes3, YAn3, YRd3, YAngle);
RotateAroundYAxis(XRes4, ZRes4, YAn4, YRd4, YAngle);
} // End void function RotateSquare.

void DrawSquare()
{
Line(XRes1+MIDX, YRes1+MIDY, XRes2+MIDX, YRes2+MIDY, 1);
Line(XRes2+MIDX, YRes2+MIDY, XRes3+MIDX, YRes3+MIDY, 2);
Line(XRes3+MIDX, YRes3+MIDY, XRes4+MIDX, YRes4+MIDY, 3);
Line(XRes4+MIDX, YRes4+MIDY, XRes1+MIDX, YRes1+MIDY, 4);
} // End void function DrawSquare.

────────────────────────────────────────────────────────────────────────────────

Chapter XIII Three Dimensional Graphics -13.39-


ROTATING AROUND ALL THE AXES

Program CH13_15.CPP rotates a plane around the X Axis, Y Axis and the Z Axis. The
logic of the program follows the same angle computation, resultant computation of
previous programs. There are more computations because there are more situations to
consider as the object rotates through three different dimensions. Study the
RotateSquare function carefully. The transition between original coordinates and
resultant coordinates is very, very important. Notice how the function starts by using
X1,Y1, and Z1 in the SetXAngle function. In the next section, X1,YRes1, and ZRes1 are
being used, and by the last part only resultants are in place. Draw many pictures and
visualize the situations in order to understand this function.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_15.CPP rotates a square around all three axes. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
float XAn1, XAn2, XAn3, XAn4;
float YAn1, YAn2, YAn3, YAn4;
float ZAn1, ZAn2, ZAn3, ZAn4;
float XRd1, XRd2, XRd3, XRd4;
float YRd1, YRd2, YRd3, YRd4;
float ZRd1, ZRd2, ZRd3, ZRd4;
int XRes1, YRes1, ZRes1, XRes2, YRes2, ZRes2;
int XRes3, YRes3, ZRes3, XRes4, YRes4, ZRes4;
float XAngle, YAngle, ZAngle;

void MakeSquare();
void RotateSquare(float XAngle, float YAngle, float ZAngle);
void DrawSquare();

void main()
{
StartGfx();
MakeSquare();
XAngle = 0.0;
YAngle = 0.0;
ZAngle = 0.0;
do
{
ClearGfx(0);
Line(MIDX, 0, MIDX, MAXY, 15);
Line(0, MIDY, MAXX, MIDY, 15);
RotateSquare(XAngle, YAngle, ZAngle);
DrawSquare();
XAngle = XAngle + PI * 0.015625; // 1 / 64
YAngle = YAngle + PI * 0.015625;
ZAngle = ZAngle + PI * 0.015625;
delay(100);
} // End do loop.
while ( !kbhit() );

Chapter XIII Three Dimensional Graphics -13.40-


EndGfx();
} // End main program CH13_15.CPP.

void MakeSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 50;
X2 = 50;
Y2 = -50;
Z2 = 50;
X3 = 50;
Y3 = 50;
Z3 = 50;
X4 = -50;
Y4 = 50;
Z4 = 50;
SetXAngle(Y1, Z1, XAn1, XRd1);
SetYAngle(X1, Z1, YAn1, YRd1);
SetZAngle(X1, Y1, ZAn1, ZRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetYAngle(X2, Z2, YAn2, YRd2);
SetZAngle(X2, Y2, ZAn2, ZRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetYAngle(X3, Z3, YAn3, YRd3);
SetZAngle(X3, Y3, ZAn3, ZRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);
SetYAngle(X4, Z4, YAn4, YRd4);
SetZAngle(X4, Y4, ZAn4, ZRd4);
} // End void function MakeSquare.

void RotateSquare(float XAngle, float YAngle, float ZAngle)


{
SetXAngle(Y1, Z1, XAn1, XRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);

RotateAroundXAxis(YRes1, ZRes1, XAn1, XRd1, XAngle);


RotateAroundXAxis(YRes2, ZRes2, XAn2, XRd2, XAngle);
RotateAroundXAxis(YRes3, ZRes3, XAn3, XRd3, XAngle);
RotateAroundXAxis(YRes4, ZRes4, XAn4, XRd4, XAngle);

SetYAngle(X1, ZRes1, YAn1, YRd1);


SetYAngle(X2, ZRes2, YAn2, YRd2);
SetYAngle(X3, ZRes3, YAn3, YRd3);
SetYAngle(X4, ZRes4, YAn4, YRd4);

RotateAroundYAxis(XRes1, ZRes1, YAn1, YRd1, YAngle);


RotateAroundYAxis(XRes2, ZRes2, YAn2, YRd2, YAngle);
RotateAroundYAxis(XRes3, ZRes3, YAn3, YRd3, YAngle);
RotateAroundYAxis(XRes4, ZRes4, YAn4, YRd4, YAngle);

SetZAngle(XRes1, YRes1, ZAn1, ZRd1);


SetZAngle(XRes2, YRes2, ZAn2, ZRd2);
SetZAngle(XRes3, YRes3, ZAn3, ZRd3);
SetZAngle(XRes4, YRes4, ZAn4, ZRd4);

RotateAroundZAxis(XRes1, YRes1, ZAn1, ZRd1, ZAngle);


RotateAroundZAxis(XRes2, YRes2, ZAn2, ZRd2, ZAngle);
RotateAroundZAxis(XRes3, YRes3, ZAn3, ZRd3, ZAngle);
RotateAroundZAxis(XRes4, YRes4, ZAn4, ZRd4, ZAngle);
} // End void function RotateSquare.

Chapter XIII Three Dimensional Graphics -13.41-


void DrawSquare()
{
Line(XRes1+MIDX, YRes1+MIDY, XRes2+MIDX, YRes2+MIDY, 1);
Line(XRes2+MIDX, YRes2+MIDY, XRes3+MIDX, YRes3+MIDY, 2);
Line(XRes3+MIDX, YRes3+MIDY, XRes4+MIDX, YRes4+MIDY, 3);
Line(XRes4+MIDX, YRes4+MIDY, XRes1+MIDX, YRes1+MIDY, 4);
} // End void function DrawSquare.

────────────────────────────────────────────────────────────────────────────────

It is not easy to visualize the movement of the square through three dimensions. Your
eyes can get fooled very easily. The next program will assist your eyes. We are going to
connect the square with four lines to the origin. The result is a graphic object that looks
like a pyramid. The vertex of the pyramid stays fixed, and as the pyramid rotates around,
its base (the square) is easier to follow. This program example is concerned with all the
computations except perspective, which will be added in the next program.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_16.CPP rotates a square around all three axes. Each ***/
/*** point of the square is connected to the origin to create a ***/
/*** "contrived" 3-d pyramid. ***/
/**********************************************************************/

/**************************************************************************
In this program, we connect each of the points of the square to the origin
to create a "contrived" 3-D shape. We have actually only created a square,
as before, but by connecting to the origin, the square looks like a pyramid.
***************************************************************************/

#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
float XAn1, XAn2, XAn3, XAn4;
float YAn1, YAn2, YAn3, YAn4;
float ZAn1, ZAn2, ZAn3, ZAn4;
float XRd1, XRd2, XRd3, XRd4;
float YRd1, YRd2, YRd3, YRd4;
float ZRd1, ZRd2, ZRd3, ZRd4;
int XRes1, YRes1, ZRes1, XRes2, YRes2, ZRes2;
int XRes3, YRes3, ZRes3, XRes4, YRes4, ZRes4;
float XAngle, YAngle, ZAngle;

void MakeSquare();
void RotateSquare(float XAngle, float YAngle, float ZAngle);
void DrawSquare();

void main()
{
StartGfx();
MakeSquare();
XAngle = 0.0;
YAngle = 0.0;
ZAngle = 0.0;
do
{
ClearGfx(0);
Line(MIDX, 0, MIDX, MAXY,15);
Line(0, MIDY, MAXX, MIDY,15);

Chapter XIII Three Dimensional Graphics -13.42-


RotateSquare(XAngle, YAngle, ZAngle);
DrawSquare();
XAngle = XAngle + PI * 0.015625; // 1 / 64
YAngle = YAngle + PI * 0.015625;
ZAngle = ZAngle + PI * 0.015625;
delay(100);
} // End do loop.
while ( !kbhit() );
EndGfx();
} // End main program CH13_16.CPP.

void MakeSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 50;
X2 = 50;
Y2 = -50;
Z2 = 50;
X3 = 50;
Y3 = 50;
Z3 = 50;
X4 = -50;
Y4 = 50;
Z4 = 50;
SetXAngle(Y1, Z1, XAn1, XRd1);
SetYAngle(X1, Z1, YAn1, YRd1);
SetZAngle(X1, Y1, ZAn1, ZRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetYAngle(X2, Z2, YAn2, YRd2);
SetZAngle(X2, Y2, ZAn2, ZRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetYAngle(X3, Z3, YAn3, YRd3);
SetZAngle(X3, Y3, ZAn3, ZRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);
SetYAngle(X4, Z4, YAn4, YRd4);
SetZAngle(X4, Y4, ZAn4, ZRd4);
} // End void function MakeSquare.

void RotateSquare(float XAngle, float YAngle, float ZAngle)


{
SetXAngle(Y1, Z1, XAn1, XRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);

RotateAroundXAxis(YRes1, ZRes1, XAn1, XRd1, XAngle);


RotateAroundXAxis(YRes2, ZRes2, XAn2, XRd2, XAngle);
RotateAroundXAxis(YRes3, ZRes3, XAn3, XRd3, XAngle);
RotateAroundXAxis(YRes4, ZRes4, XAn4, XRd4, XAngle);

SetYAngle(X1, ZRes1, YAn1, YRd1);


SetYAngle(X2, ZRes2, YAn2, YRd2);
SetYAngle(X3, ZRes3, YAn3, YRd3);
SetYAngle(X4, ZRes4, YAn4, YRd4);

RotateAroundYAxis(XRes1, ZRes1, YAn1, YRd1, YAngle);


RotateAroundYAxis(XRes2, ZRes2, YAn2, YRd2, YAngle);
RotateAroundYAxis(XRes3, ZRes3, YAn3, YRd3, YAngle);
RotateAroundYAxis(XRes4, ZRes4, YAn4, YRd4, YAngle);

SetZAngle(XRes1, YRes1, ZAn1, ZRd1);


SetZAngle(XRes2, YRes2, ZAn2, ZRd2);
SetZAngle(XRes3, YRes3, ZAn3, ZRd3);

Chapter XIII Three Dimensional Graphics -13.43-


SetZAngle(XRes4, YRes4, ZAn4, ZRd4);

RotateAroundZAxis(XRes1, YRes1, ZAn1, ZRd1, ZAngle);


RotateAroundZAxis(XRes2, YRes2, ZAn2, ZRd2, ZAngle);
RotateAroundZAxis(XRes3, YRes3, ZAn3, ZRd3, ZAngle);
RotateAroundZAxis(XRes4, YRes4, ZAn4, ZRd4, ZAngle);
} // End void function RotateSquare.

void DrawSquare()
{
Line(XRes1+MIDX, YRes1+MIDY, XRes2+MIDX, YRes2+MIDY, 1);
Line(XRes2+MIDX, YRes2+MIDY, XRes3+MIDX, YRes3+MIDY, 2);
Line(XRes3+MIDX, YRes3+MIDY, XRes4+MIDX, YRes4+MIDY, 3);
Line(XRes4+MIDX, YRes4+MIDY, XRes1+MIDX, YRes1+MIDY, 4);
Line(XRes1+MIDX, YRes1+MIDY, MIDX, MIDY, 14);
Line(XRes2+MIDX, YRes2+MIDY, MIDX, MIDY, 14);
Line(XRes3+MIDX, YRes3+MIDY, MIDX, MIDY, 14);
Line(XRes4+MIDX, YRes4+MIDY, MIDX, MIDY, 14);
} // End void function DrawSquare.

────────────────────────────────────────────────────────────────────────────────

ADDING PERSPECTIVE TO THE PYRAMID

Program CH13_17.CPP adds the computation of perspective to the previous program. If


we had not connected the lines to the origin, you would be hard pressed to follow the
square rotation. It is still tough with perspective, but the movement is now more realistic.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_17.CPP rotates a square around all 3 axes. Each point ***/
/*** of the square is connected to the origin to create a ***/
/*** "contrived" 3-d pyramid. Perspective is also added. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
float XAn1, XAn2, XAn3, XAn4;
float YAn1, YAn2, YAn3, YAn4;
float ZAn1, ZAn2, ZAn3, ZAn4;
float XRd1, XRd2, XRd3, XRd4;
float YRd1, YRd2, YRd3, YRd4;
float ZRd1, ZRd2, ZRd3, ZRd4;
int XRes1, YRes1, ZRes1, XRes2, YRes2, ZRes2;
int XRes3, YRes3, ZRes3, XRes4, YRes4, ZRes4;
float XAngle, YAngle, ZAngle;

void MakeSquare();
void RotateSquare(float XAngle, float YAngle, float ZAngle);
void DrawSquare();
void CalcPersp();

void main()
{
StartGfx();
MakeSquare();

Chapter XIII Three Dimensional Graphics -13.44-


XAngle = 0.0;
YAngle = 0.0;
ZAngle = 0.0;
do
{
ClearGfx(0);
Line(MIDX, 0, MIDX, MAXY, 15);
Line(0, MIDY, MAXX, MIDY, 15);
RotateSquare(XAngle,YAngle,ZAngle);
CalcPersp();
DrawSquare();
XAngle = XAngle + PI * 0.015625; // 1 / 64
YAngle = YAngle + PI * 0.015625;
ZAngle = ZAngle + PI * 0.015625;
delay(100);
} // End do loop.
while ( !kbhit() );
EndGfx();
} // End main program CH13_17.CPP.

void MakeSquare()
{
X1 = -50;
Y1 = -50;
Z1 = 50;
X2 = 50;
Y2 = -50;
Z2 = 50;
X3 = 50;
Y3 = 50;
Z3 = 50;
X4 = -50;
Y4 = 50;
Z4 = 50;
SetXAngle(Y1, Z1, XAn1, XRd1);
SetYAngle(X1, Z1, YAn1, YRd1);
SetZAngle(X1, Y1, ZAn1, ZRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetYAngle(X2, Z2, YAn2, YRd2);
SetZAngle(X2, Y2, ZAn2, ZRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetYAngle(X3, Z3, YAn3, YRd3);
SetZAngle(X3, Y3, ZAn3, ZRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);
SetYAngle(X4, Z4, YAn4, YRd4);
SetZAngle(X4, Y4, ZAn4, ZRd4);
} // End void function MakeSquare.

void RotateSquare(float XAngle, float YAngle, float ZAngle)


{
SetXAngle(Y1, Z1, XAn1, XRd1);
SetXAngle(Y2, Z2, XAn2, XRd2);
SetXAngle(Y3, Z3, XAn3, XRd3);
SetXAngle(Y4, Z4, XAn4, XRd4);

RotateAroundXAxis(YRes1, ZRes1, XAn1, XRd1, XAngle);


RotateAroundXAxis(YRes2, ZRes2, XAn2, XRd2, XAngle);
RotateAroundXAxis(YRes3, ZRes3, XAn3, XRd3, XAngle);
RotateAroundXAxis(YRes4, ZRes4, XAn4, XRd4, XAngle);

SetYAngle(X1, ZRes1, YAn1, YRd1);


SetYAngle(X2, ZRes2, YAn2, YRd2);
SetYAngle(X3, ZRes3, YAn3, YRd3);
SetYAngle(X4, ZRes4, YAn4, YRd4);

Chapter XIII Three Dimensional Graphics -13.45-


RotateAroundYAxis(XRes1, ZRes1, YAn1, YRd1, YAngle);
RotateAroundYAxis(XRes2, ZRes2, YAn2, YRd2, YAngle);
RotateAroundYAxis(XRes3, ZRes3, YAn3, YRd3, YAngle);
RotateAroundYAxis(XRes4, ZRes4, YAn4, YRd4, YAngle);

SetZAngle(XRes1, YRes1, ZAn1, ZRd1);


SetZAngle(XRes2, YRes2, ZAn2, ZRd2);
SetZAngle(XRes3, YRes3, ZAn3, ZRd3);
SetZAngle(XRes4, YRes4, ZAn4, ZRd4);

RotateAroundZAxis(XRes1, YRes1, ZAn1, ZRd1, ZAngle);


RotateAroundZAxis(XRes2, YRes2, ZAn2, ZRd2, ZAngle);
RotateAroundZAxis(XRes3, YRes3, ZAn3, ZRd3, ZAngle);
RotateAroundZAxis(XRes4, YRes4, ZAn4, ZRd4, ZAngle);
} // End void function RotateSquare.

void DrawSquare()
{
Line(XRes1+MIDX, YRes1+MIDY, XRes2+MIDX, YRes2+MIDY, 1);
Line(XRes2+MIDX, YRes2+MIDY, XRes3+MIDX, YRes3+MIDY, 2);
Line(XRes3+MIDX, YRes3+MIDY, XRes4+MIDX, YRes4+MIDY, 3);
Line(XRes4+MIDX, YRes4+MIDY, XRes1+MIDX, YRes1+MIDY, 4);
Line(XRes1+MIDX, YRes1+MIDY, MIDX, MIDY, 14);
Line(XRes2+MIDX, YRes2+MIDY, MIDX, MIDY, 14);
Line(XRes3+MIDX, YRes3+MIDY, MIDX, MIDY, 14);
Line(XRes4+MIDX, YRes4+MIDY, MIDX, MIDY, 14);
} // End void function DrawSquare.

void CalcPersp()
{
CalculatePerspective(XRes1, YRes1, XRes1, YRes1, ZRes1);
CalculatePerspective(XRes2, YRes2, XRes2, YRes2, ZRes2);
CalculatePerspective(XRes3, YRes3, XRes3, YRes3, ZRes3);
CalculatePerspective(XRes4, YRes4, XRes4, YRes4, ZRes4);
} // End void function CalcPersp.

────────────────────────────────────────────────────────────────────────────────

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

13.7 ROTATING 3D OBJECTS IN 3D SPACE

We are ready for the big boys now. The time has come to rotate some serious objects.
The last couple of programs may have appeared that we rotated some 3d objects
(pyramids) through 3d space. In reality we were rotating a square that was connected to
the origin. In other words the four points of the square did the rotation. The fifth point of
the pyramid always stayed in place.

For the first program example, we will view what happens when two parallel squares are

Chapter XIII Three Dimensional Graphics -13.46-


rotated simultaneously through 3d space. This will be a good stepping stone to achieving
true 3d object rotation. Run program CH13_18.CPP and think how close this is to a 3d
rotating object.
────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_18.CPP rotates two squares around all three axes. The ***/
/*** squares are both placed opposite each other. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

int X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, X4, Y4, Z4;
int X5, Y5, Z5, X6, Y6, Z6, X7, Y7, Z7, X8, Y8, Z8;
float XAn1, XAn2, XAn3, XAn4, XAn5, XAn6, XAn7, XAn8;
float YAn1, YAn2, YAn3, YAn4, YAn5, YAn6, YAn7, YAn8;
float ZAn1, ZAn2, ZAn3, ZAn4, ZAn5, ZAn6, ZAn7, ZAn8;
float XRd1, XRd2, XRd3, XRd4, XRd5, XRd6, XRd7, XRd8;
float YRd1, YRd2, YRd3, YRd4, YRd5, YRd6, YRd7, YRd8;
float ZRd1, ZRd2, ZRd3, ZRd4, ZRd5, ZRd6, ZRd7, ZRd8;
int XRes1, YRes1, ZRes1, XRes2, YRes2, ZRes2;
int XRes3, YRes3, ZRes3, XRes4, YRes4, ZRes4;
int XRes5, YRes5, ZRes5, XRes6, YRes6, ZRes6;
int XRes7, YRes7, ZRes7, XRes8, YRes8, ZRes8;
float XAngle, YAngle, ZAngle;

void MakeSquares();
void RotateSquares(float XAngle, float YAngle, float ZAngle);
void DrawSquares();
void CalcPersp();

void main()
{
StartGfx();
XAngle = 0.0;
YAngle = 0.0;
ZAngle = 0.0;
MakeSquares();
do
{
ClearGfx(0);
Line(MIDX, 0, MIDX, MAXY, 15);
Line(0, MIDY, MAXX, MIDY, 15);
RotateSquares(XAngle, YAngle, ZAngle);
CalcPersp();
DrawSquares();
XAngle = XAngle + PI * 0.015625; // 1 / 64
YAngle = YAngle + PI * 0.015625;
ZAngle = ZAngle + PI * 0.015625;
delay(100);
} // End do loop.
while ( !kbhit() );
EndGfx();
} // End main program CH13_18.CPP.

void MakeSquares()
{
void SetXYZAngle(int X, int Y, int Z, float& XAn, float& YAn, float& ZAn,
float& XRd, float& YRd, float& ZRd);

X1 = -50; Y1 = -50; Z1 = -50;


X2 = 50; Y2 = -50; Z2 = -50;

Chapter XIII Three Dimensional Graphics -13.47-


X3 = 50; Y3 = 50; Z3 = -50;
X4 = -50; Y4 = 50; Z4 = -50;
X5 = -50; Y5 = -50; Z5 = 50;
X6 = 50; Y6 = -50; Z6 = 50;
X7 = 50; Y7 = 50; Z7 = 50;
X8 = -50; Y8 = 50; Z8 = 50;
SetXYZAngle(X1, Y1, Z1, XAn1, YAn1, ZAn1, XRd1, YRd1, ZRd1);
SetXYZAngle(X2, Y2, Z2, XAn2, YAn2, ZAn2, XRd2, YRd2, ZRd2);
SetXYZAngle(X3, Y3, Z3, XAn3, YAn3, ZAn3, XRd3, YRd3, ZRd3);
SetXYZAngle(X4, Y4, Z4, XAn4, YAn4, ZAn4, XRd4, YRd4, ZRd4);
SetXYZAngle(X5, Y5, Z5, XAn5, YAn5, ZAn5, XRd5, YRd5, ZRd5);
SetXYZAngle(X6, Y6, Z6, XAn6, YAn6, ZAn6, XRd6, YRd6, ZRd6);
SetXYZAngle(X7, Y7, Z7, XAn7, YAn7, ZAn7, XRd7, YRd7, ZRd7);
SetXYZAngle(X8, Y8, Z8, XAn8, YAn8, ZAn8, XRd8, YRd8, ZRd8);
} // End void function MakeSquares.

void SetXYZAngle(int X, int Y, int Z, float& XAn, float& YAn, float& ZAn,
float& XRd, float& YRd, float& ZRd)
{
SetXAngle(Y, Z, XAn, XRd);
SetYAngle(X, Z, YAn, YRd);
SetZAngle(X, Y, ZAn, ZRd);
} // End void function SetXYZAngle.

void RotateSquares(float XAngle, float YAngle, float ZAngle)


{
SetXAngle(Y1, Z1, XAn1, XRd1); SetXAngle(Y2, Z2, XAn2, XRd2);
SetXAngle(Y3, Z3, XAn3, XRd3); SetXAngle(Y4, Z4, XAn4, XRd4);
SetXAngle(Y5, Z5, XAn5, XRd5); SetXAngle(Y6, Z6, XAn6, XRd6);
SetXAngle(Y7, Z7, XAn7, XRd7); SetXAngle(Y8, Z8, XAn8, XRd8);

RotateAroundXAxis(YRes1, ZRes1, XAn1, XRd1, XAngle);


RotateAroundXAxis(YRes2, ZRes2, XAn2, XRd2, XAngle);
RotateAroundXAxis(YRes3, ZRes3, XAn3, XRd3, XAngle);
RotateAroundXAxis(YRes4, ZRes4, XAn4, XRd4, XAngle);
RotateAroundXAxis(YRes5, ZRes5, XAn5, XRd5, XAngle);
RotateAroundXAxis(YRes6, ZRes6, XAn6, XRd6, XAngle);
RotateAroundXAxis(YRes7, ZRes7, XAn7, XRd7, XAngle);
RotateAroundXAxis(YRes8, ZRes8, XAn8, XRd8, XAngle);

SetYAngle(X1, ZRes1, YAn1, YRd1);


SetYAngle(X2, ZRes2, YAn2, YRd2);
SetYAngle(X3, ZRes3, YAn3, YRd3);
SetYAngle(X4, ZRes4, YAn4, YRd4);
SetYAngle(X5, ZRes5, YAn5, YRd5);
SetYAngle(X6, ZRes6, YAn6, YRd6);
SetYAngle(X7, ZRes7, YAn7, YRd7);
SetYAngle(X8, ZRes8, YAn8, YRd8);

RotateAroundYAxis(XRes1, ZRes1, YAn1, YRd1, YAngle);


RotateAroundYAxis(XRes2, ZRes2, YAn2, YRd2, YAngle);
RotateAroundYAxis(XRes3, ZRes3, YAn3, YRd3, YAngle);
RotateAroundYAxis(XRes4, ZRes4, YAn4, YRd4, YAngle);
RotateAroundYAxis(XRes5, ZRes5, YAn5, YRd5, YAngle);
RotateAroundYAxis(XRes6, ZRes6, YAn6, YRd6, YAngle);
RotateAroundYAxis(XRes7, ZRes7, YAn7, YRd7, YAngle);
RotateAroundYAxis(XRes8, ZRes8, YAn8, YRd8, YAngle);

SetZAngle(XRes1, YRes1, ZAn1, ZRd1);


SetZAngle(XRes2, YRes2, ZAn2, ZRd2);
SetZAngle(XRes3, YRes3, ZAn3, ZRd3);
SetZAngle(XRes4, YRes4, ZAn4, ZRd4);
SetZAngle(XRes5, YRes5, ZAn5, ZRd5);
SetZAngle(XRes6, YRes6, ZAn6, ZRd6);

Chapter XIII Three Dimensional Graphics -13.48-


SetZAngle(XRes7, YRes7, ZAn7, ZRd7);
SetZAngle(XRes8, YRes8, ZAn8, ZRd8);

RotateAroundZAxis(XRes1, YRes1, ZAn1, ZRd1, ZAngle);


RotateAroundZAxis(XRes2, YRes2, ZAn2, ZRd2, ZAngle);
RotateAroundZAxis(XRes3, YRes3, ZAn3, ZRd3, ZAngle);
RotateAroundZAxis(XRes4, YRes4, ZAn4, ZRd4, ZAngle);
RotateAroundZAxis(XRes5, YRes5, ZAn5, ZRd5, ZAngle);
RotateAroundZAxis(XRes6, YRes6, ZAn6, ZRd6, ZAngle);
RotateAroundZAxis(XRes7, YRes7, ZAn7, ZRd7, ZAngle);
RotateAroundZAxis(XRes8, YRes8, ZAn8, ZRd8, ZAngle);
} // End void function RotateSquares.

void DrawSquares()
{
Line(XRes1+MIDX, YRes1+MIDY, XRes2+MIDX, YRes2+MIDY, 1);
Line(XRes2+MIDX, YRes2+MIDY, XRes3+MIDX, YRes3+MIDY, 2);
Line(XRes3+MIDX, YRes3+MIDY, XRes4+MIDX, YRes4+MIDY, 3);
Line(XRes4+MIDX, YRes4+MIDY, XRes1+MIDX, YRes1+MIDY, 4);
Line(XRes5+MIDX, YRes5+MIDY, XRes6+MIDX, YRes6+MIDY, 1);
Line(XRes6+MIDX, YRes6+MIDY, XRes7+MIDX, YRes7+MIDY, 2);
Line(XRes7+MIDX, YRes7+MIDY, XRes8+MIDX, YRes8+MIDY, 3);
Line(XRes8+MIDX, YRes8+MIDY, XRes5+MIDX, YRes5+MIDY, 4);
} // End void function DrawSquares.

void CalcPersp()
{
CalculatePerspective(XRes1,YRes1,XRes1,YRes1,ZRes1);
CalculatePerspective(XRes2,YRes2,XRes2,YRes2,ZRes2);
CalculatePerspective(XRes3,YRes3,XRes3,YRes3,ZRes3);
CalculatePerspective(XRes4,YRes4,XRes4,YRes4,ZRes4);
CalculatePerspective(XRes5,YRes5,XRes5,YRes5,ZRes5);
CalculatePerspective(XRes6,YRes6,XRes6,YRes6,ZRes6);
CalculatePerspective(XRes7,YRes7,XRes7,YRes7,ZRes7);
CalculatePerspective(XRes8,YRes8,XRes8,YRes8,ZRes8);
} // End void function CalcPersp.

────────────────────────────────────────────────────────────────────────────────

ROTATING A CUBE THROUGH 3D SPACE

We have arrived. Finally, program CH13_19.CPP shows a honest 3d object, a cube,


rotating through 3d space. When you compare the program code you will find that the
logic is very similar to the simultaneous rotations of two squares. You will also be pleased
that many variables have been eliminated.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH13_19.CPP rotates a cube around all 3 axes. Arrays are used ***/
/*** to simplify the variable declaration section. ***/
/**********************************************************************/

#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

const NUM = 8;

int X[NUM], Y[NUM], Z[NUM], XRes[NUM], YRes[NUM], ZRes[NUM];

Chapter XIII Three Dimensional Graphics -13.49-


float XAn[NUM], YAn[NUM], ZAn[NUM], XRd[NUM], YRd[NUM], ZRd[NUM];
float XAngle, YAngle, ZAngle;
int Cnt;
int TrueX[NUM], TrueY[NUM];

void MakeCube();
void RotateCube(float XAngle, float YAngle, float ZAngle);
void CalcPersp();
void DrawCube();
void DrawAxes();

void main()
{
StartGfx();
XAngle = 0.0;
YAngle = 0.0;
ZAngle = 0.0;
MakeCube();
do
{
DrawAxes();
RotateCube(XAngle, YAngle, ZAngle);
CalcPersp();
DrawCube();
XAngle = XAngle + PI * 0.015625; // 1 / 64
YAngle = YAngle + PI * 0.015625;
ZAngle = ZAngle + PI * 0.015625;
delay(100);
} // End do loop.
while ( !kbhit() );
EndGfx();
} // End main program CH13_19.CPP.

void MakeCube()
{
X[0] = -50; Y[0] = -50; Z[0] = -50;
X[1] = 50; Y[1] = -50; Z[1] = -50;
X[2] = 50; Y[2] = -50; Z[2] = 50;
X[3] = -50; Y[3] = -50; Z[3] = 50;
X[4] = -50; Y[4] = 50; Z[4] = -50;
X[5] = 50; Y[5] = 50; Z[5] = -50;
X[6] = 50; Y[6] = 50; Z[6] = 50;
X[7] = -50; Y[7] = 50; Z[7] = 50;

for (Cnt = 0; Cnt < NUM; Cnt++)


{
SetXAngle(Y[Cnt], Z[Cnt], XAn[Cnt], XRd[Cnt]);
SetYAngle(X[Cnt], Z[Cnt], YAn[Cnt], YRd[Cnt]);
SetZAngle(X[Cnt], Y[Cnt], ZAn[Cnt], ZRd[Cnt]);
} // End for loop.
} // End void function MakeCube.

void RotateCube(float XAngle, float YAngle, float ZAngle)


{
for (Cnt = 0; Cnt < NUM; Cnt++)
{
SetXAngle(Y[Cnt], Z[Cnt], XAn[Cnt], XRd[Cnt]);
RotateAroundXAxis(YRes[Cnt], ZRes[Cnt], XAn[Cnt], XRd[Cnt], XAngle);
SetYAngle(X[Cnt], ZRes[Cnt], YAn[Cnt], YRd[Cnt]);
RotateAroundYAxis(XRes[Cnt], ZRes[Cnt], YAn[Cnt], YRd[Cnt], YAngle);
SetZAngle(XRes[Cnt], YRes[Cnt], ZAn[Cnt], ZRd[Cnt]);
RotateAroundZAxis(XRes[Cnt], YRes[Cnt], ZAn[Cnt], ZRd[Cnt], ZAngle);
} // End for loop.
} // End void function RotateCube.

Chapter XIII Three Dimensional Graphics -13.50-


void CalcPersp()
{
for (Cnt = 0; Cnt < NUM; Cnt++)
CalculatePerspective(TrueX[Cnt], TrueY[Cnt], XRes[Cnt],
YRes[Cnt], ZRes[Cnt]);
} // End void function CalcPersp.

void DrawCube()
{
Line(TrueX[0]+MIDX, TrueY[0]+MIDY, TrueX[1]+MIDX, TrueY[1]+MIDY, 4);
Line(TrueX[1]+MIDX, TrueY[1]+MIDY, TrueX[2]+MIDX, TrueY[2]+MIDY, 4);
Line(TrueX[2]+MIDX, TrueY[2]+MIDY, TrueX[3]+MIDX, TrueY[3]+MIDY, 4);
Line(TrueX[3]+MIDX, TrueY[3]+MIDY, TrueX[0]+MIDX, TrueY[0]+MIDY, 4);

Line(TrueX[4]+MIDX, TrueY[4]+MIDY, TrueX[5]+MIDX, TrueY[5]+MIDY, 14);


Line(TrueX[5]+MIDX, TrueY[5]+MIDY, TrueX[6]+MIDX, TrueY[6]+MIDY, 14);
Line(TrueX[6]+MIDX, TrueY[6]+MIDY, TrueX[7]+MIDX, TrueY[7]+MIDY, 14);
Line(TrueX[7]+MIDX, TrueY[7]+MIDY, TrueX[4]+MIDX, TrueY[4]+MIDY, 14);

Line(TrueX[0]+MIDX, TrueY[0]+MIDY, TrueX[4]+MIDX, TrueY[4]+MIDY, 1);


Line(TrueX[1]+MIDX, TrueY[1]+MIDY, TrueX[5]+MIDX, TrueY[5]+MIDY, 1);
Line(TrueX[2]+MIDX, TrueY[2]+MIDY, TrueX[6]+MIDX, TrueY[6]+MIDY, 1);
Line(TrueX[3]+MIDX, TrueY[3]+MIDY, TrueX[7]+MIDX, TrueY[7]+MIDY, 1);
} // End void function DrawCube.

void DrawAxes()
{
ClearGfx(0);
Line(MIDX, 0, MIDX, MAXY, 15);
Line(0, MIDY, MAXX, MIDY, 15);
} // End void function DrawAxes.

────────────────────────────────────────────────────────────────────────────────
ROTATING WITH A NEW ROTATION FORMULA

All the rotation math that has been used up to this point was specifically picked because it
was simpler. Some folks right now are not feeling so comfortable. Your idea of simple
does not include the last 40 odd pages of this chapter. Well you are right this has been the
toughest chapter in the whole book, and yet we tried to simplify the complexity of 3d
graphics. The methods used in the previous program slowed down program execution.
This was not a problem for our simple squares and modest cube, but it a problem for more
complex movements. We have introduced a new formula which is explained in the
comment of the next program.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** GFX3D20.CPP rotates a cube around all three axes. The new ***/
/*** rotation formula is used. ***/
/**********************************************************************/

/*
By polar definition: a point (x,y) is x = r×cos Θ y = r×sin Θ

Θz+αz

Chapter XIII Three Dimensional Graphics -13.51-


x' = r×cos(Θz+αz) : our situation
x' = r(cos Θ×cos α - sin Θ×sin α) : trig identity
x' = r×cos Θ×cos α - r×sin Θ×sin α : distribute r
x' = x × cos α - y × sin α : substitute definition

y' = r×sin(Θ+α) : our situation


y' = r(sin Θ×cos α + cos Θ×sin α) : trig identity
y' = r×sin Θ×cos α + r×cos Θ×sin α : distribute r
y' = y × cos α + x × sin α : substitute definition

in above equations x, y, Θ, and α can be replaced with appropriate


coordinate values and angle values to achieve a rotated coordinate.

:: around z axis ..
x' = x × cos α - y × sin α
y' = y × cos α + x × sin α

:: around x axis ..
z'' = z × cos α - y × sin α
y'' = y × cos α + z × sin α

:: around y axis ..
x''' = x'' × cos α - z'' × sin α
z''' = z'' × cos α + x'' × sin α

It looks like we need to store intermediate values of x', y'


z'',y'' and x''',z'''. This is not entirely true. Look at this:

:: around z axis ..
x' = x × cos α - y × sin α
y' = y × cos α + x × sin α

:: around x axis ..
z' = z × cos α - y' × sin α
y' = y'× cos α + z × sin α

:: around y axis ..
x'' = x' × cos α - z' × sin α
z'' = z' × cos α + x' × sin α

This is also correct--and so we only need to create two sets of variables


and some temporary place to put the value of x''. Look at the code
for more detail.
*/

#include "GFXLIB8.HPP"
#include "GRAF3D03.HPP"

const NUM = 8;

int X[NUM], Y[NUM], Z[NUM], XRes[NUM], YRes[NUM], ZRes[NUM];


float XAngle, YAngle, ZAngle;
int Cnt;
int TrueX[NUM], TrueY[NUM];

Chapter XIII Three Dimensional Graphics -13.52-


void MakeCube();
void RotateCube(float XAngle, float YAngle, float ZAngle);
void CalcPersp();
void DrawCube();
void DrawAxes();

void main()
{
StartGfx();
XAngle = 0.0;
YAngle = 0.0;
ZAngle = 0.0;
MakeCube();
do
{
DrawAxes();
RotateCube(XAngle,YAngle,ZAngle);
CalcPersp();
DrawCube();
XAngle = XAngle + PI / 64;
YAngle = YAngle + PI / 64;
ZAngle = YAngle + PI / 64;
delay(100);
} // End do loop.
while ( !kbhit() );
EndGfx();
} // End main program CH13_20.

void MakeCube()
{
X[0] = -50; Y[0] = -50; Z[0] = -50;
X[1] = 50; Y[1] = -50; Z[1] = -50;
X[2] = 50; Y[2] = -50; Z[2] = 50;
X[3] = -50; Y[3] = -50; Z[3] = 50;
X[4] = -50; Y[4] = 50; Z[4] = -50;
X[5] = 50; Y[5] = 50; Z[5] = -50;
X[6] = 50; Y[6] = 50; Z[6] = 50;
X[7] = -50; Y[7] = 50; Z[7] = 50;
} // End void function MakeCube.

void RotateCube(float XAngle, float YAngle, float ZAngle)


{
int Temp;
for (Cnt = 0; Cnt < NUM; Cnt++)
{
XRes[Cnt] = Round(X[Cnt] * cos(ZAngle) - Y[Cnt] * sin(ZAngle));
YRes[Cnt] = Round(Y[Cnt] * cos(ZAngle) + X[Cnt] * sin(ZAngle));
ZRes[Cnt] = Round(Z[Cnt] * cos(XAngle) - YRes[Cnt] * sin(XAngle));
YRes[Cnt] = Round(YRes[Cnt] * cos(XAngle) + Z[Cnt] * sin(XAngle));
Temp = XRes[Cnt];
XRes[Cnt] = Round(XRes[Cnt] * cos(YAngle) - ZRes[Cnt] * sin(YAngle));
ZRes[Cnt] = Round(ZRes[Cnt] * cos(YAngle) + Temp * sin(YAngle));
} // End for loop.
} // End void function RotateCube.

void CalcPersp()
{
for (Cnt = 0; Cnt < NUM; Cnt++)
CalculatePerspective(TrueX[Cnt], TrueY[Cnt], XRes[Cnt],
YRes[Cnt], ZRes[Cnt]);
} // End void function CalcPersp.

void DrawCube()
{

Chapter XIII Three Dimensional Graphics -13.53-


Line(TrueX[0]+MIDX, TrueY[0]+MIDY, TrueX[1]+MIDX, TrueY[1]+MIDY, 4);
Line(TrueX[1]+MIDX, TrueY[1]+MIDY, TrueX[2]+MIDX, TrueY[2]+MIDY, 4);
Line(TrueX[2]+MIDX, TrueY[2]+MIDY, TrueX[3]+MIDX, TrueY[3]+MIDY, 4);
Line(TrueX[3]+MIDX, TrueY[3]+MIDY, TrueX[0]+MIDX, TrueY[0]+MIDY, 4);

Line(TrueX[4]+MIDX, TrueY[4]+MIDY, TrueX[5]+MIDX, TrueY[5]+MIDY, 14);


Line(TrueX[5]+MIDX, TrueY[5]+MIDY, TrueX[6]+MIDX, TrueY[6]+MIDY, 14);
Line(TrueX[6]+MIDX, TrueY[6]+MIDY, TrueX[7]+MIDX, TrueY[7]+MIDY, 14);
Line(TrueX[7]+MIDX, TrueY[7]+MIDY, TrueX[4]+MIDX, TrueY[4]+MIDY, 14);

Line(TrueX[0]+MIDX, TrueY[0]+MIDY, TrueX[4]+MIDX, TrueY[4]+MIDY, 1);


Line(TrueX[1]+MIDX, TrueY[1]+MIDY, TrueX[5]+MIDX, TrueY[5]+MIDY, 1);
Line(TrueX[2]+MIDX, TrueY[2]+MIDY, TrueX[6]+MIDX, TrueY[6]+MIDY, 1);
Line(TrueX[3]+MIDX, TrueY[3]+MIDY, TrueX[7]+MIDX, TrueY[7]+MIDY, 1);
} // End void function DrawCube.

void DrawAxes()
{
ClearGfx(0);
Line(MIDX, 0, MIDX, MAXY, 15);
Line(0, MIDY, MAXX, MIDY, 15);
} // End void function DrawAxes.

────────────────────────────────────────────────────────────────────────────────

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

13.7 ADDITIONAL PROGRAMS

There are many more interesting programs to show with 3d graphics. We are having a
serious space limitation. This book is limited to a definite finite size for the binding method
that our printer can handle. There are also budget problems, and it may not surprise you
that this book became thicker than we intended.

Our solution is simple. Everybody does get two disks with this book. All the previous
programs have been printed in this book because it is easier to study the program logic,
and it helps in explaining the program flow. We did mention that the final program
examples in 3d graphics were meant to be seen and studied, but they would not be
explained.

Check out the graphics disk and in the ADV3DGFX subdirectory and you will see an
additional set of programs that demonstrate some very interesting 3d graphics concepts.
Be aware, the math is sophisticated, and we did say before that a healthy knowledge of
pre-calculus math is a prerequisite to a clear understanding of all this rotation business.

Chapter XIII Three Dimensional Graphics -13.54-


Three dimensional graphics is a lot of fun, it is very satisfying, but it is also very complex
and challenging.

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

13.8 LIBRARY FILES


/**********************************************************************/
/*** GRAF3D01.HPP contains two basic routines for 3D grafix: ***/
/*** SetZAngle and RotateAroundZAxis. ***/
/**********************************************************************/

void SetZAngle(int X, int Y, float &Angle, float &R)


{
if (X != 0)
// assuming the point is not vertical to the origin...
Angle = atan((float) Y / X);
else
// otherwise it can only be at one of two places--above or below
if (Y > 0)
// if it is below...
Angle = PI * 0.5; // ...clockwise rotation to 90 degrees
else
Angle = PI * 1.5; // if it is above...rotate to 270
if (X < 0)
Angle = PI + Angle; // if the point is left of the origin,
// make sure to accomidate for the sign
// error provided by ArcTan
R = sqrt(X*X+Y*Y); // calculate radius
} // void function setzangle

void RotateAroundZAxis(int &X, int &Y, float OrgAng, float Radius,


float Angle)
{
X = Round(cos(OrgAng+Angle)*Radius);
Y = Round(sin(OrgAng+Angle)*Radius);
} // void function rotatearoundzaxis

────────────────────────────────────────────────────────────────────────────────

Chapter XIII Three Dimensional Graphics -13.55-


/**********************************************************************/
/*** GRAF3D02.HPP contains routines for setting X, Y, and Z angles, ***/
/*** and rotating points around the X, Y, and Z axes. ***/
/**********************************************************************/
void SetXAngle(int Y,int Z,float &XAng,float &R)
{
if (Z != 0)
XAng = atan((float) Y / Z);
else
if (Y > 0)
XAng = PI * 0.5;
else
XAng = PI * 1.5;
if (Z < 0)
XAng = PI + XAng;
R = sqrt(Z*Z+Y*Y);
} // void function setxangle

void SetYAngle(int X,int Z,float &YAng, float &R)


{
if (Z != 0)
YAng = atan((float) X / Z);
else
if (X > 0)
YAng = PI * 0.5;
else
YAng = PI * 1.5;
if (Z < 0)
YAng = PI + YAng;
R = sqrt(Z*Z+X*X);
} // void function setyangle

void SetZAngle(int X, int Y, float &Angle, float &R)


{
if (X != 0)
// assuming the point is not vertical to the origin...
Angle = atan((float) Y / X);
else
// otherwise it can only be at one of two places--above or below
if (Y > 0)
// if it is below...
Angle = PI * 0.5; // ...clockwise rotation to 90 degrees
else
Angle = PI * 1.5; // if it is above...rotate to 270
if (X < 0)
Angle = PI + Angle; // if the point is left of the origin,
// accomodate for the sign error by arctan
R = sqrt(X*X+Y*Y); // calculate radius
} // void function setzangle

void RotateAroundXAxis(int &YRes,int &ZRes,float OrgAng,float Radius,


float XAng)
{
YRes = Round(sin(OrgAng + XAng) * Radius);
ZRes = Round(cos(OrgAng + XAng) * Radius);
} // void function rotatearoundxaxis

void RotateAroundYAxis(int &XRes,int &ZRes, float OrgAng,float Radius,


float YAng)
{
XRes = Round(sin(OrgAng + YAng) * Radius);
ZRes = Round(sin(OrgAng + YAng) * Radius);
} // void function rotatearoundyaxis

Chapter XIII Three Dimensional Graphics -13.56-


void RotateAroundZAxis(int &XRes, int &YRes, float OrgAng, float Radius,
float Angle)
{
XRes = Round(cos(OrgAng+Angle)*Radius);
YRes = Round(sin(OrgAng+Angle)*Radius);
} // void function rotatearoundzaxis

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** GRAF3D03.HPP contains routines for setting X, Y, and Z angles, ***/
/*** and rotating points around the X, Y, and Z axes, and a ***/
/*** perspective routine. ***/
/**********************************************************************/

const ZDIST = 400;


const FOCAL = 300;

void SetXAngle(int Y, int Z, float& XAng, float &R)


{
if (Z != 0)
XAng = atan((float) Y / Z);
else
if (Y > 0)
XAng = PI * 0.5;
else
XAng = PI * 1.5;
if (Z < 0)
XAng = PI + XAng;
R = sqrt(Z*Z+Y*Y);
} // End void function SetXAngle.

void SetYAngle(int X, int Z, float& YAng, float &R)


{
if (Z != 0)
YAng = atan((float) X / Z);
else
if (X > 0)
YAng = PI * 0.5;
else
YAng = PI * 1.5;
if (Z < 0)
YAng = PI + YAng;
R = sqrt(Z*Z+X*X);
} // End void function SetYAngle.

void SetZAngle(int X, int Y, float& ZAng, float &R)


{
if (X != 0)
// Assuming the point is not vertical to the origin...
ZAng = atan((float) Y / X);
else
// Otherwise it can only be at one of two places--above or below.
if (Y > 0)
// If it is below...
ZAng = PI * 0.5; // ... clockwise rotation to 90 degrees.
else
ZAng = PI * 1.5; // If it is above...rotate to 270.
if (X < 0)
ZAng = PI + ZAng; // If the point is left of the origin,
// make sure to accomidate for the sign
// error provided by arctan
R = sqrt(X*X+Y*Y); // calculate radius.
} // End void function SetZAngle.

Chapter XIII Three Dimensional Graphics -13.57-


void RotateAroundXAxis(int& YRes, int& ZRes, float OrgAng, float Radius,
float XAng)
{
YRes = Round(sin(OrgAng + XAng) * Radius);
ZRes = Round(cos(OrgAng + XAng) * Radius);
} // End void function RotateAroundXAxis.

void RotateAroundYAxis(int& XRes, int& ZRes, float OrgAng, float Radius,


float YAng)
{
XRes = Round(sin(OrgAng + YAng) * Radius);
ZRes = Round(cos(OrgAng + YAng) * Radius);
} // End void function RotateAroundYAxis.

Chapter XIII Three Dimensional Graphics -13.58-


void RotateAroundZAxis(int& XRes, int& YRes, float OrgAng, float Radius,
float ZAng)
{
XRes = Round(cos(OrgAng + ZAng) * Radius);
YRes = Round(sin(OrgAng + ZAng) * Radius);
} // End void function RotateAroundZAxis.

void CalculatePerspective(int& XRes, int& YRes, int X, int Y, int Z)


{
XRes = (X * FOCAL) / (ZDIST - Z);
YRes = (Y * FOCAL) / (ZDIST - Z);
} // End void function CalculatePerspective.

────────────────────────────────────────────────────────────────────────────────

Chapter XIII Three Dimensional Graphics -13.59-

You might also like