Table des matières

PART 09 : Draw Polygons

Hi there! ASPHYXIA is BACK with our first MegaDemo, Psycho Neurosis! A paltry 1.3MB download is all it takes to see the group from Durbs first major production! We are quite proud of it, and think you should see it ;)

Secondly, I released a small little trainer (a trainerette ;-)) on RsaPROG and Connexctix BBS mail, also on the ASPHYXIA BBS as COPPERS.ZIP. It is a small Pascal program demonstrating how to display copper bars in text mode. Also includes a check for horizontal retrace (A lot of people wanted it, that is why I wrote the program) (ASPHYXIA … first with the trainer goodies ;-) aargh, sorry, had to be done ))

Thirdly, sorry about the problems with Tut 8! If you had all the checking on, the tutorial would probably die on the first points. The reason is this : in the first loop, we have DrawPoints then RotatePoints. The variables used in DrawPoints are set in RotatePoints, so if you put RotatePoints before DrawPoints, the program should work fine. Alternatively, turn off error checking 8-)

Fourthly, I have had a surprisingly large number of people saying that “I get this, like, strange '286 instructions not enabled' message! What's wrong with your code, dude?” To all of you, get into Pascal, hit Alt-O (for options), hit enter and a 2 (for Enable 286 instructions). Hard hey? Doesn't anyone EVER set up their version of Pascal?

Now, on to todays tutorial! 3D solids. That is what the people wanted, that is what the people get! This tutorial is mainly on how to draw the polygon on screen. For details on how the 3D stuff works, check out tut 8.

If you would like to contact me, or the team, there are many ways you can do it :

1) Write a message to Grant Smith/Denthor/Asphyxia in private mail on the ASPHYXIA BBS.
2) Write to Denthor, EzE or Goth on Connectix.
3) Write to :  Grant Smith
               P.O.Box 270 Kloof
               3640
               Natal
4) Call me (Grant Smith) at (031) 73 2129 (leave a message if you call during varsity)
5) Write to mcphail@beastie.cs.und.ac.za on InterNet, and mention the word Denthor near the top of the letter.

NB : If you are a representative of a company or BBS, and want ASPHYXIA to do you a demo, leave mail to me; we can discuss it. NNB : If you have done/attempted a demo, SEND IT TO ME! We are feeling quite lonely and want to meet/help out/exchange code with other demo groups. What do you have to lose? Leave a message here and we can work out how to transfer it. We really want to hear from you!

How to draw a polygon

Sounds easy enough, right? WRONG! There are many, many different ways to go about this, and today I'll only be showing you one. Please don't take what is written here as anything approaching the best method, it is just here to get you on your way…

The procedure I will be using here is based on something most of us learned in standard eight … I think. I seem to recall doing something like this in Mrs. Reids maths class all those years ago ;)

Take two points, x1,y1 and x2,y2. Draw them :

                  + (x1,y1)
                   \
                     \  <-- Point a somewhere along the line
                       \
                         + (x2,y2)

Right, so what we have to do is this : if we know the y-coord of a, what is it's x-coord? To prove the method we will give the points random values.

                 + (2,10)
                  \
                    \  <-- a.y = 12
                      \
                        +  (15,30)

Right. Simple enough problem. This is how we do it :

   (a.y-y1) = (12 - 10)  {to get a.y as though y1 was zero}
   *(x2-x1) = *(15 - 2)  {the total x-length of the line}
   /(y2-y1) = /(30 - 10) {the total y-length of the line}
        +x1 = +2         { to get the equation back to real coords}

So our equation is :

 (a.y-y1)*(x2-x1)/(y2-y1)+x4    or
 (12-10)*(15-2)/(30-10)+2

which gives you :

                      2*13/20+2 = 26/20+2
                                = 3.3

That means that along the line with y=12, x is equal to 3.3. Since we are not concerned with the decimal place, we replace the / with a div, which in Pascal gives us an integer result, and is faster too. All well and good, I hear you cry, but what does this have to do with life and how it relates to polygons in general. The answer is simple. For each of the four sides of the polygon we do the above test for each y line. We store the smallest and the largest x values into separate variables for each line, and draw a horizontal line between them. Ta-Dah! We have a cool polygon!

For example : Two lines going down :

    
                +             +
               / <-x1     x2->|   <--For this y line
             /                |
           +                  +

Find x1 and x2 for that y, then draw a line between them. Repeat for all y values.

Of course, it's not as simple as that. We have to make sure we only check those y lines that contain the polygon (a simple min y, max y test for all the points). We also have to check that the line we are calculating actually extends as far as where our current y is (check that the point is between both y's). We have to compare each x to see weather it is smaller then the minimum x value so far, or bigger then the maximum (the original x min is set as a high number, and the x max is set as a small number). We must also check that we only draw to the place that we can see ( 0-319 on the x ; 0-199 on the y (the size of the MCGA screen))

To see how this looks in practice, have a look at the sample code provided. (Mrs. Reid would probably kill me for the above explanation, so when you learn it in school, split it up into thousands of smaller equations to get the same answer ;))

Okay, that's it! What's that? How do you draw a vertical line? Thats simple …

Drawing a vertical line

Right, this is a lot easier than drawing a normal line (Tut 5 .. I think), because you stay on the same y value. So, what you do is you set ES to the screen you want to write to, and get DI to the start of the y-line (see earlier trainers for a description of how SEGMENT:OFFSET works.

IN   : x1 , x2, y, color, where
 
           asm
             mov    ax,where
             mov    es,ax
             mov    di,y
             mov    ax,y
             shl    di,8   { di:=di*256 }
             shl    ax,6   { ax:=ax*64 }
             add    di,ax  { di := (y*256)+(y*64) := y*320 Faster then a
                             straight multiplication }
</code pascal>
 
Right, now you add the first x value to get your startoff.
<code asm>
             add    di,x1

Move the color to store into ah and al

             mov    al,color
             mov    ah,al       { ah:=al:=color }

then get CX equal to how many pixels across you want to go

             mov    cx,x2
             sub    cx,x1   { cx:=x2-x1 }

Okay, as we all know, moving a word is a lot faster then moving a byte,so we halve CX

             shr    cx,1    { cx:=cx/2 }

but what happens if CX was an odd number. After a shift, the value of the last number is placed in the carry flag, so what we do is jump over a single byte move if the carry flag is zero, or execute it if it is one.

            jnc     @Start  { If there is no carry, jump to label Start }
            stosb           { ES:[DI]:=al ; increment DI }
        @Start :            { Label Start }
            rep     stosw   { ES:[DI]:=ax ; DI:=DI+2; repeat CX times }

Right, the finished product looks like this :

Procedure Hline (x1,x2,y:word;col:byte;where:word); assembler;
  { This draws a horizontal line from x1 to x2 on line y in color col }
asm
  mov   ax,where
  mov   es,ax
  mov   ax,y
  mov   di,ax
  shl   ax,8
  shl   di,6
  add   di,ax
  add   di,x1
 
  mov   al,col
  mov   ah,al
  mov   cx,x2
  sub   cx,x1
  shr   cx,1
  jnc   @start
  stosb
@Start :
  rep   stosw
end;

Done!

In closing

This 3D system is still not perfect. It needs to be faster, and now I have also dumped the problem of face-sorting on you! Nyahahahaha!

My sister and I were driving along the other day when she asked me, what would I like for my computer.
I thought long and hard about it, and came up with the following hypothesis. 
When a girl gets a Barbie doll, she then wants the extra ballgown for the doll, then the hairbrush, and the car, and the house, and the friends etc.
When a guy gets a computer, he wants the extra memory, the bigger hard drive, the maths co-pro, the better motherboard, the latest software, and the bigger monitor etc.
I told my sister all of this, and finished up with : "So as you can see, computers are Barbie dolls for MEN!"
She called me a chauvinist. And hit me. Hard.

Grant Smith 19:24 26/2/94

See you next time!

  1. Denthor

These fine BBS's carry the ASPHYXIA DEMO TRAINER SERIES : (alphabetical)

╔══════════════════════════╦════════════════╦═════╦═══╦════╦════╗
║BBS Name                  ║Telephone No.   ║Open ║Msg║File║Past║
╠══════════════════════════╬════════════════╬═════╬═══╬════╬════╣
║ASPHYXIA BBS #1           ║(031) 765-5312  ║ALL  ║ * ║ *  ║ *  ║
║ASPHYXIA BBS #2           ║(031) 765-6293  ║ALL  ║ * ║ *  ║ *  ║
║Connectix BBS             ║(031) 266-9992  ║ALL  ║   ║ *  ║ *  ║
╚══════════════════════════╩════════════════╩═════╩═══╩════╩════╝

Open = Open at all times or only A/H Msg = Available in message base File = Available in file base Past = Previous Parts available

Does no other BBS's ANYWHERE carry the trainer? Am I writing this for three people who get it from one of these BBS's each week? Should I go on? (Hehehehe … I was pleased to note that Tut 8 was THE most downloaded file from ASPHYXIA BBS last month … )

Code Source

PaSCAL

(*****************************************************************************)
(*                                                                           *)
(* TUT9.PAS - VGA Trainer Program 9 (in Pascal)                              *)
(*                                                                           *)
(* "The VGA Trainer Program" is written by Denthor of Asphyxia.  However it  *)
(* was limited to Pascal only in its first run.  All I have done is taken    *)
(* his original release, translated it to C++, and touched up a few things.  *)
(* I take absolutely no credit for the concepts presented in this code.      *)
(*        -Christopher G. Mann (Snowman)                                     *)
(*                                                                           *)
(* Program Notes : This program presents polygons.                           *)
(*                                                                           *)
(* Author        : Grant Smith (Denthor)  - denthor@beastie.cs.und.ac.za     *)
(*                                                                           *)
(*****************************************************************************)
 
{$X+}
USES Crt;
 
CONST VGA = $A000;
      maxpolys = 5;
      A : Array [1..maxpolys,1..4,1..3] of integer =
        (
         ((-10,10,0),(-2,-10,0),(0,-10,0),(-5,10,0)),
         ((10,10,0),(2,-10,0),(0,-10,0),(5,10,0)),
         ((-2,-10,0),(2,-10,0),(2,-5,0),(-2,-5,0)),
         ((-6,0,0),(6,0,0),(7,5,0),(-7,5,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0))
        );  { The 3-D coordinates of our object ... stored as (X1,Y1,Z1), }
            { (X2,Y2,Z2) ... for the 4 points of a poly }
     S : Array [1..maxpolys,1..4,1..3] of integer =
        (
         ((-10,-10,0),(10,-10,0),(10,-7,0),(-10,-7,0)),
         ((-10,10,0),(10,10,0),(10,7,0),(-10,7,0)),
         ((-10,1,0),(10,1,0),(10,-2,0),(-10,-2,0)),
         ((-10,-8,0),(-7,-8,0),(-7,0,0),(-10,0,0)),
         ((10,8,0),(7,8,0),(7,0,0),(10,0,0))
        );  { The 3-D coordinates of our object ... stored as (X1,Y1,Z1), }
            { (X2,Y2,Z2) ... for the 4 points of a poly }
     P : Array [1..maxpolys,1..4,1..3] of integer =
        (
         ((-10,-10,0),(-7,-10,0),(-7,10,0),(-10,10,0)),
         ((10,-10,0),(7,-10,0),(7,0,0),(10,0,0)),
         ((-9,-10,0),(9,-10,0),(9,-7,0),(-9,-7,0)),
         ((-9,-1,0),(9,-1,0),(9,2,0),(-9,2,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0))
        );  { The 3-D coordinates of our object ... stored as (X1,Y1,Z1), }
            { (X2,Y2,Z2) ... for the 4 points of a poly }
     H : Array [1..maxpolys,1..4,1..3] of integer =
        (
         ((-10,-10,0),(-7,-10,0),(-7,10,0),(-10,10,0)),
         ((10,-10,0),(7,-10,0),(7,10,0),(10,10,0)),
         ((-9,-1,0),(9,-1,0),(9,2,0),(-9,2,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0))
        );  { The 3-D coordinates of our object ... stored as (X1,Y1,Z1), }
            { (X2,Y2,Z2) ... for the 4 points of a poly }
     Y : Array [1..maxpolys,1..4,1..3] of integer =
        (
         ((-7,-10,0),(0,-3,0),(0,0,0),(-10,-7,0)),
         ((7,-10,0),(0,-3,0),(0,0,0),(10,-7,0)),
         ((-2,-3,0),(2,-3,0),(2,10,0),(-2,10,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0))
        );  { The 3-D coordinates of our object ... stored as (X1,Y1,Z1), }
            { (X2,Y2,Z2) ... for the 4 points of a poly }
     X : Array [1..maxpolys,1..4,1..3] of integer =
        (
         ((-7,-10,0),(10,7,0),(7,10,0),(-10,-7,0)),
         ((7,-10,0),(-10,7,0),(-7,10,0),(10,-7,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0))
        );  { The 3-D coordinates of our object ... stored as (X1,Y1,Z1), }
            { (X2,Y2,Z2) ... for the 4 points of a poly }
     I : Array [1..maxpolys,1..4,1..3] of integer =
        (
         ((-10,-10,0),(10,-10,0),(10,-7,0),(-10,-7,0)),
         ((-10,10,0),(10,10,0),(10,7,0),(-10,7,0)),
         ((-2,-9,0),(2,-9,0),(2,9,0),(-2,9,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0)),
         ((0,0,0),(0,0,0),(0,0,0),(0,0,0))
        );  { The 3-D coordinates of our object ... stored as (X1,Y1,Z1), }
            { (X2,Y2,Z2) ... for the 4 points of a poly }
 
 
Type Point = Record
               x,y,z:real;                { The data on every point we rotate}
             END;
     Virtual = Array [1..64000] of byte;  { The size of our Virtual Screen }
     VirtPtr = ^Virtual;                  { Pointer to the virtual screen }
 
 
VAR Lines : Array [1..maxpolys,1..4] of Point;  { The base object rotated }
    Translated : Array [1..maxpolys,1..4] of Point; { The rotated object }
    Xoff,Yoff,Zoff:Integer;               { Used for movement of the object }
    lookup : Array [0..360,1..2] of real; { Our sin and cos lookup table }
    Virscr : VirtPtr;                     { Our first Virtual screen }
    Vaddr  : word;                        { The segment of our virtual screen}
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure SetMCGA;  { This procedure gets you into 320x200x256 mode. }
BEGIN
  asm
     mov        ax,0013h
     int        10h
  end;
END;
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure SetText;  { This procedure returns you to text mode.  }
BEGIN
  asm
     mov        ax,0003h
     int        10h
  end;
END;
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure Cls (Where:word;Col : Byte);
   { This clears the screen to the specified color }
BEGIN
     asm
        push    es
        mov     cx, 32000;
        mov     es,[where]
        xor     di,di
        mov     al,[col]
        mov     ah,al
        rep     stosw
        pop     es
     End;
END;
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure SetUpVirtual;
   { This sets up the memory needed for the virtual screen }
BEGIN
  GetMem (VirScr,64000);
  vaddr := seg (virscr^);
END;
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure ShutDown;
   { This frees the memory used by the virtual screen }
BEGIN
  FreeMem (VirScr,64000);
END;
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
procedure flip(source,dest:Word);
  { This copies the entire screen at "source" to destination }
begin
  asm
    push    ds
    mov     ax, [Dest]
    mov     es, ax
    mov     ax, [Source]
    mov     ds, ax
    xor     si, si
    xor     di, di
    mov     cx, 32000
    rep     movsw
    pop     ds
  end;
end;
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure Pal(Col,R,G,B : Byte);
  { This sets the Red, Green and Blue values of a certain color }
Begin
   asm
      mov    dx,3c8h
      mov    al,[col]
      out    dx,al
      inc    dx
      mov    al,[r]
      out    dx,al
      mov    al,[g]
      out    dx,al
      mov    al,[b]
      out    dx,al
   end;
End;
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure Hline (x1,x2,y:word;col:byte;where:word); assembler;
  { This draws a horizontal line from x1 to x2 on line y in color col }
asm
  mov   ax,where
  mov   es,ax
  mov   ax,y
  mov   di,ax
  shl   ax,8
  shl   di,6
  add   di,ax
  add   di,x1
 
  mov   al,col
  mov   ah,al
  mov   cx,x2
  sub   cx,x1
  shr   cx,1
  jnc   @start
  stosb
@Start :
  rep   stosw
end;
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure DrawPoly(x1,y1,x2,y2,x3,y3,x4,y4:integer;color:byte;where:word);
  { This draw a polygon with 4 points at x1,y1 , x2,y2 , x3,y3 , x4,y4
    in color col }
var
  x:integer;
  mny,mxy:integer;
  mnx,mxx,yc:integer;
  mul1,div1,
  mul2,div2,
  mul3,div3,
  mul4,div4:integer;
 
begin
  mny:=y1; mxy:=y1;
  if y2<mny then mny:=y2;
  if y2>mxy then mxy:=y2;
  if y3<mny then mny:=y3;
  if y3>mxy then mxy:=y3;    { Choose the min y mny and max y mxy }
  if y4<mny then mny:=y4;
  if y4>mxy then mxy:=y4;
 
  if mny<0 then mny:=0;
  if mxy>199 then mxy:=199;
  if mny>199 then exit;
  if mxy<0 then exit;        { Verticle range checking }
 
  mul1:=x1-x4; div1:=y1-y4;
  mul2:=x2-x1; div2:=y2-y1;
  mul3:=x3-x2; div3:=y3-y2;
  mul4:=x4-x3; div4:=y4-y3;  { Constansts needed for intersection calc }
 
  for yc:=mny to mxy do
    begin
      mnx:=320;
      mxx:=-1;
      if (y4>=yc) or (y1>=yc) then
        if (y4<=yc) or (y1<=yc) then   { Check that yc is between y1 and y4 }
          if not(y4=y1) then
            begin
              x:=(yc-y4)*mul1 div div1+x4; { Point of intersection on x axis }
              if x<mnx then
                mnx:=x;
              if x>mxx then
                mxx:=x;       { Set point as start or end of horiz line }
            end;
      if (y1>=yc) or (y2>=yc) then
        if (y1<=yc) or (y2<=yc) then   { Check that yc is between y1 and y2 }
          if not(y1=y2) then
            begin
              x:=(yc-y1)*mul2 div div2+x1; { Point of intersection on x axis }
              if x<mnx then
                mnx:=x;
              if x>mxx then
                mxx:=x;       { Set point as start or end of horiz line }
            end;
      if (y2>=yc) or (y3>=yc) then
        if (y2<=yc) or (y3<=yc) then   { Check that yc is between y2 and y3 }
          if not(y2=y3) then
            begin
              x:=(yc-y2)*mul3 div div3+x2; { Point of intersection on x axis }
              if x<mnx then
                mnx:=x;
              if x>mxx then
                mxx:=x;       { Set point as start or end of horiz line }
            end;
      if (y3>=yc) or (y4>=yc) then
        if (y3<=yc) or (y4<=yc) then   { Check that yc is between y3 and y4 }
          if not(y3=y4) then
            begin
              x:=(yc-y3)*mul4 div div4+x3; { Point of intersection on x axis }
              if x<mnx then
                mnx:=x;
              if x>mxx then
                mxx:=x;       { Set point as start or end of horiz line }
            end;
      if mnx<0 then
        mnx:=0;
      if mxx>319 then
        mxx:=319;          { Range checking on horizontal line }
      if mnx<=mxx then
        hline (mnx,mxx,yc,color,where);   { Draw the horizontal line }
    end;
  end;
 
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Function rad (theta : real) : real;
  {  This calculates the degrees of an angle }
BEGIN
  rad := theta * pi / 180
END;
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure SetUpPoints;
  { This creates the lookup table }
VAR loop1,loop2:integer;
BEGIN
  For loop1:=0 to 360 do BEGIN
    lookup [loop1,1]:=sin (rad (loop1));
    lookup [loop1,2]:=cos (rad (loop1));
  END;
END;
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure Putpixel (X,Y : Integer; Col : Byte; where:word);
  { This puts a pixel on the screen by writing directly to memory. }
BEGIN
  Asm
    mov     ax,[where]
    mov     es,ax
    mov     bx,[X]
    mov     dx,[Y]
    mov     di,bx
    mov     bx, dx                  {; bx = dx}
    shl     dx, 8
    shl     bx, 6
    add     dx, bx                  {; dx = dx + bx (ie y*320)}
    add     di, dx                  {; finalise location}
    mov     al, [Col]
    stosb
  End;
END;
 
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure RotatePoints (X,Y,Z:Integer);
  { This rotates object lines by X,Y and Z; then places the result in
    TRANSLATED }
VAR loop1,loop2:integer;
    temp:point;
BEGIN
  For loop1:=1 to maxpolys do BEGIN
    For loop2:=1 to 4 do BEGIN
      temp.x:=lines[loop1,loop2].x;
      temp.y:=lookup[x,2]*lines[loop1,loop2].y - lookup[x,1]*lines[loop1,loop2].z;
      temp.z:=lookup[x,1]*lines[loop1,loop2].y + lookup[x,2]*lines[loop1,loop2].z;
 
      translated[loop1,loop2]:=temp;
 
      If y>0 then BEGIN
        temp.x:=lookup[y,2]*translated[loop1,loop2].x - lookup[y,1]*translated[loop1,loop2].y;
        temp.y:=lookup[y,1]*translated[loop1,loop2].x + lookup[y,2]*translated[loop1,loop2].y;
        temp.z:=translated[loop1,loop2].z;
        translated[loop1,loop2]:=temp;
      END;
 
      If z>0 then BEGIN
        temp.x:=lookup[z,2]*translated[loop1,loop2].x + lookup[z,1]*translated[loop1,loop2].z;
        temp.y:=translated[loop1,loop2].y;
        temp.z:=-lookup[z,1]*translated[loop1,loop2].x + lookup[z,2]*translated[loop1,loop2].z;
        translated[loop1,loop2]:=temp;
      END;
    END;
  END;
END;
 
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure DrawPoints;
  { This draws the translated object to the virtual screen }
VAR loop1:Integer;
    nx,ny,nx2,ny2,nx3,ny3,nx4,ny4:integer;
    temp:integer;
BEGIN
  For loop1:=1 to maxpolys do BEGIN
    If (translated[loop1,1].z+zoff<0) and (translated[loop1,2].z+zoff<0) and
       (translated[loop1,3].z+zoff<0) and (translated[loop1,4].z+zoff<0) then BEGIN
      temp:=round (translated[loop1,1].z+zoff);
      nx :=round (256*translated[loop1,1].X) div temp+xoff;
      ny :=round (256*translated[loop1,1].Y) div temp+yoff;
      temp:=round (translated[loop1,2].z+zoff);
      nx2:=round (256*translated[loop1,2].X) div temp+xoff;
      ny2:=round (256*translated[loop1,2].Y) div temp+yoff;
      temp:=round (translated[loop1,3].z+zoff);
      nx3:=round (256*translated[loop1,3].X) div temp+xoff;
      ny3:=round (256*translated[loop1,3].Y) div temp+yoff;
      temp:=round (translated[loop1,4].z+zoff);
      nx4:=round (256*translated[loop1,4].X) div temp+xoff;
      ny4:=round (256*translated[loop1,4].Y) div temp+yoff;
      drawpoly (nx,ny,nx2,ny2,nx3,ny3,nx4,ny4,13,vaddr);
    END;
  END;
END;
 
 
 
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
Procedure MoveAround;
  { This is the main display procedure. Firstly it brings the object towards
    the viewer by increasing the Zoff, then passes control to the user }
VAR deg,loop1,loop2:integer;
    ch:char;
 
  Procedure Whizz (sub:boolean);
  VAR loop1:integer;
  BEGIN
    For loop1:=-64 to -5 do BEGIN
      zoff:=loop1*8;
      if sub then xoff:=xoff-7 else xoff:=xoff+7;
      RotatePoints (deg,deg,deg);
      DrawPoints;
      flip (vaddr,vga);
      Cls (vaddr,0);
      deg:=(deg+5) mod 360;
    END;
  END;
 
BEGIN
  deg:=0;
  ch:=#0;
  Yoff:=100;
  Xoff:=350;
  Cls (vaddr,0);
  For loop1:=1 to maxpolys do
    For loop2:=1 to 4 do BEGIN
      Lines [loop1,loop2].x:=a [loop1,loop2,1];
      Lines [loop1,loop2].y:=a [loop1,loop2,2];
      Lines [loop1,loop2].z:=a [loop1,loop2,3];
    END;
  Whizz (TRUE);
 
  For loop1:=1 to maxpolys do
    For loop2:=1 to 4 do BEGIN
      Lines [loop1,loop2].x:=s [loop1,loop2,1];
      Lines [loop1,loop2].y:=s [loop1,loop2,2];
      Lines [loop1,loop2].z:=s [loop1,loop2,3];
    END;
  Whizz (FALSE);
 
  For loop1:=1 to maxpolys do
    For loop2:=1 to 4 do BEGIN
      Lines [loop1,loop2].x:=p [loop1,loop2,1];
      Lines [loop1,loop2].y:=p [loop1,loop2,2];
      Lines [loop1,loop2].z:=p [loop1,loop2,3];
    END;
  Whizz (TRUE);
 
  For loop1:=1 to maxpolys do
    For loop2:=1 to 4 do BEGIN
      Lines [loop1,loop2].x:=h [loop1,loop2,1];
      Lines [loop1,loop2].y:=h [loop1,loop2,2];
      Lines [loop1,loop2].z:=h [loop1,loop2,3];
    END;
  Whizz (FALSE);
 
  For loop1:=1 to maxpolys do
    For loop2:=1 to 4 do BEGIN
      Lines [loop1,loop2].x:=y [loop1,loop2,1];
      Lines [loop1,loop2].y:=y [loop1,loop2,2];
      Lines [loop1,loop2].z:=y [loop1,loop2,3];
    END;
  Whizz (TRUE);
 
  For loop1:=1 to maxpolys do
    For loop2:=1 to 4 do BEGIN
      Lines [loop1,loop2].x:=x [loop1,loop2,1];
      Lines [loop1,loop2].y:=x [loop1,loop2,2];
      Lines [loop1,loop2].z:=x [loop1,loop2,3];
    END;
  Whizz (FALSE);
 
  For loop1:=1 to maxpolys do
    For loop2:=1 to 4 do BEGIN
      Lines [loop1,loop2].x:=i [loop1,loop2,1];
      Lines [loop1,loop2].y:=i [loop1,loop2,2];
      Lines [loop1,loop2].z:=i [loop1,loop2,3];
    END;
  Whizz (TRUE);
 
  For loop1:=1 to maxpolys do
    For loop2:=1 to 4 do BEGIN
      Lines [loop1,loop2].x:=a [loop1,loop2,1];
      Lines [loop1,loop2].y:=a [loop1,loop2,2];
      Lines [loop1,loop2].z:=a [loop1,loop2,3];
    END;
  Whizz (FALSE);
 
  cls (vaddr,0);
  cls (vga,0);
  Xoff := 160;
 
  Repeat
    if keypressed then BEGIN
      ch:=upcase (Readkey);
      Case ch of 'A' : zoff:=zoff+5;
                 'Z' : zoff:=zoff-5;
                 ',' : xoff:=xoff-5;
                 '.' : xoff:=xoff+5;
                 'S' : yoff:=yoff-5;
                 'X' : yoff:=yoff+5;
      END;
    END;
    DrawPoints;
    flip (vaddr,vga);
    cls (vaddr,0);
    RotatePoints (deg,deg,deg);
    deg:=(deg+5) mod 360;
  Until ch=#27;
END;
 
 
BEGIN
  SetUpVirtual;
  clrscr;
  Writeln ('Hello there! Varsity has begun once again, so it is once again');
  Writeln ('back to the grindstone ;-) ... anyway, this tutorial is, by');
  Writeln ('popular demand, on poly-filling, in relation to 3-D solids.');
  Writeln;
  Writeln ('In this program, the letters of ASPHYXIA will fly past you. As you');
  Writeln ('will see, they are solid, not wireframe. After the last letter has');
  Writeln ('flown by, a large A will be left in the middle of the screen.');
  Writeln;
  Writeln ('You will be able to move it around the screen, and you will notice');
  Writeln ('that it may have bits only half on the screen, i.e. clipping is');
  Writeln ('perfomed. To control it use the following : "A" and "Z" control the Z');
  Writeln ('movement, "," and "." control the X movement, and "S" and "X"');
  Writeln ('control the Y movement. I have not included rotation control, but');
  Writeln ('it should be easy enough to put in yourself ... if you have any');
  Writeln ('hassles, leave me mail.');
  Writeln;
  Writeln ('I hope this is what you wanted...leave me mail for new ideas.');
  writeln;
  writeln;
  Write ('  Hit any key to contine ...');
  Readkey;
  SetMCGA;
  SetUpPoints;
  MoveAround;
  SetText;
  ShutDown;
  Writeln ('All done. This concludes the ninth sample program in the ASPHYXIA');
  Writeln ('Training series. You may reach DENTHOR under the names of GRANT');
  Writeln ('SMITH/DENTHOR/ASPHYXIA on the ASPHYXIA BBS. I am also an avid');
  Writeln ('Connectix BBS user, and occasionally read RSAProg.');
  Writeln ('The numbers are available in the main text. You may also write to me at:');
  Writeln ('             Grant Smith');
  Writeln ('             P.O. Box 270');
  Writeln ('             Kloof');
  Writeln ('             3640');
  Writeln ('I hope to hear from you soon!');
  Writeln; Writeln;
  Write   ('Hit any key to exit ...');
  Readkey;
END.

C

file:tut9.cpp
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// TUTPROG9.CPP - VGA Trainer Program 9 (in Turbo C++ 3.0)                 //
//                                                                         //
// "The VGA Trainer Program" is written by Denthor of Asphyxia. However it //
// was limited to only Pascal in its first run.  All I have done is taken  //
// his original release, translated it to C++ and touched up a few things. //
// I take absolutely no credit for the concepts presented in this code.    //
//                                                                         //
// Program Notes : This program demonstrates polygon moving and rotation.  //
//                                                                         //
//                 If you are compiling this program from within the       //
//                 Turbo C++ environment, you must go under Options,       //
//                 Debugger, and change the "Program Heap Size" to a value //
//                 80 or greater.  If you are going to be fooling around   //
//                 with the code a bit, I suggest raising this to about    //
//                 100 just to be on the safe side.  You don't have to     //
//                 worry about this if you are compiling command line.     //
//                                                                         //
//                 Just for reference, this is what I use:                 //
//                                                                         //
//                    tcc -mc -a -G -2 -O tut9.cpp                         //
//                                                                         //
//                 The way things are set up, there is no need to compile  //
//                 or link tut9.cpp and gfx2.cpp seperately.               //
//                                                                         //
//                 The Compact memory model (-mc) seems to provide the     //
//                 best results for this tutorial.  Remember, use this     //
//                 memory model when you have little code (less than 64k)  //
//                 and lots of data.                                       //
//                                                                         //
// Author        : Grant Smith (Denthor) - denthor@beastie.cs.und.ac.za    //
// Translator    : Christopher G. Mann   - r3cgm@dax.cc.uakron.edu         //
//                                                                         //
// Last Modified : January 21, 1995                                        //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
//               //
// INCLUDE FILES //
//               //
 
  #include <conio.h>
                             // clrscr(), getch(), kbhit()
  #include <iostream.h>
                             // cout()
  #include <math.h>
                             // sin(), cos()
  #include <stdlib.h>
                             // exit()
  #include "gfx2.cpp"
                             // our graphics library tools
 
//          //
// TYPEDEFS //
//          //
 
  typedef unsigned char byte;
  typedef unsigned int  word;
 
//           //
// CONSTANTS //
//           //
 
  const MAXPOLYS   = 5;
  const POLYPOINTS = 4;
  const POLYCORDS  = 3;
 
//             //
// LETTER DATA //
//             //
 
  // The 3-D coordinates of our object ... stored as {X1,Y1,Z1},
  // {X2,Y2,Z2} ... for the 4 points of a poly.
  const int A[MAXPOLYS][POLYPOINTS][POLYCORDS] =
            { {{-10, 10, 0},{ -2,-10, 0},{ 0,-10, 0},{ -5,10, 0}},   // 1
              {{ 10, 10, 0},{  2,-10, 0},{ 0,-10, 0},{  5,10, 0}},   // 2
              {{ -2,-10, 0},{  2,-10, 0},{ 2, -5, 0},{ -2,-5, 0}},   // 3
              {{ -6,  0, 0},{  6,  0, 0},{ 7,  5, 0},{ -7, 5, 0}},   // 4
              {{  0,  0, 0},{  0,  0, 0},{ 0,  0, 0},{  0, 0, 0}} }; // 5
 
  //                             1----1    +    2----2
  //                             |....|    |    |....|
  //                             |....|    |    |....|
  //                             `....`    |    '....'
  //                              |....|   |   |....|
  //                              `.4------+------4.'
  //                               ||......|......||
  //                               ``......|......''
  //                                ||.....|.....||
  //                                ||.....|.....||
  //                      -------+---4+----+----+4---+-------
  //                                 |...| | |...|
  //                                 `...` | '...'
  //                                  |...|||...|
  //                                   |...|...|
  //                                   |.3-+-3.|
  //                                   `.|.|.|.'
  //                                    ||.|.||
  //                                    ||.|.||
  //                                    `|.|.|'
  //                                     3-2-3
 
  const int S[MAXPOLYS][POLYPOINTS][POLYCORDS] =
            { {{-10,-10, 0},{ 10,-10, 0},{10, -7, 0},{-10, -7, 0}},
              {{-10, 10, 0},{ 10, 10, 0},{10,  7, 0},{-10,  7, 0}},
              {{-10,  1, 0},{ 10,  1, 0},{10, -2, 0},{-10, -2, 0}},
              {{-10, -8, 0},{ -7, -8, 0},{-7,  0, 0},{-10,  0, 0}},
              {{ 10,  8, 0},{  7,  8, 0},{ 7,  0, 0},{ 10,  0, 0}} };
 
  const int P[MAXPOLYS][POLYPOINTS][POLYCORDS] =
            { {{-10,-10,0},{-7,-10,0},{-7,10,0},{-10,10,0}},
              {{10,-10,0},{7,-10,0},{7,0,0},{10,0,0}},
              {{-9,-10,0},{9,-10,0},{9,-7,0},{-9,-7,0}},
              {{-9,-1,0},{9,-1,0},{9,2,0},{-9,2,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} };
 
  const int H[MAXPOLYS][POLYPOINTS][POLYCORDS] =
            { {{-10,-10,0},{-7,-10,0},{-7,10,0},{-10,10,0}},
              {{10,-10,0},{7,-10,0},{7,10,0},{10,10,0}},
              {{-9,-1,0},{9,-1,0},{9,2,0},{-9,2,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} };
 
  const int Y[MAXPOLYS][POLYPOINTS][POLYCORDS] =
            { {{-7,-10,0},{0,-3,0},{0,0,0},{-10,-7,0}},
              {{7,-10,0},{0,-3,0},{0,0,0},{10,-7,0}},
              {{-2,-3,0},{2,-3,0},{2,10,0},{-2,10,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} };
 
  const int X[MAXPOLYS][POLYPOINTS][POLYCORDS] =
            { {{-7,-10,0},{10,7,0},{7,10,0},{-10,-7,0}},
              {{7,-10,0},{-10,7,0},{-7,10,0},{10,-7,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} };
 
  const int I[MAXPOLYS][POLYPOINTS][POLYCORDS] =
            { {{-10,-10,0},{10,-10,0},{10,-7,0},{-10,-7,0}},
              {{-10,10,0},{10,10,0},{10,7,0},{-10,7,0}},
              {{-2,-9,0},{2,-9,0},{2,9,0},{-2,9,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}},
              {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} };
 
//                     //
// FUNCTION PROTOTYPES //
//                     //
 
  void DrawPoly     (int x1, int y1, int x2, int y2,
                     int x3, int y3, int x4, int y4,
                     byte Col, word Where);
  void SetUpPoints  ();
  void RotatePoints (int X, int Y, int Z);
  void DrawPoints   ();
  void Whizz        (int sub, int &deg);
  void MoveAround   ();
 
//            //
// STRUCTURES //
//            //
 
  // The data for every point we rotate
  struct Point {
    float x;
    float y;
    float z;
  };
 
//                              //
// GLOBAL VARIABLE DECLARATIONS //
//                              //
 
  byte far *Virscr=NULL;           // Pointer to our virtual screen
  word Vaddr;                      // Segment of our virtual screen
  float Lookup[360][2];            // Our sin and cos lookup tables
  int Xoff, Yoff, Zoff;            // Used for movement of the object
  Point Lines[MAXPOLYS][4];        // The base object being rotated
  Point Translated[MAXPOLYS][4];   // The rotated object
 
 
///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                                MAIN FUNCTION                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
 
void main() {
 
  SetUpVirtual(Virscr,Vaddr);
  // always check to see if enough memory was allocated
  if (Virscr == NULL) {
    SetText();
    cout << "Insufficient memory for virtual screens, exiting...";
    exit(1);
  }
 
  clrscr();
  cout
    << "Hello there! Varsity has begun once again, so it is once again\n"
    << "back to the grindstone ;-) ... anyway, this tutorial is, by\n"
    << "popular demand, on poly-filling, in relation to 3-D solids.\n\n"
    << "In this program, the letters of ASPHYXIA will fly past you. As you\n"
    << "will see, they are solid, not wireframe. After the last letter has\n"
    << "flown by, a large A will be left in the middle of the screen.\n\n"
    << "You will be able to move it around the screen, and you will notice\n"
    << "that it may have bits only half on the screen, i.e. clipping is\n"
    << "perfomed. To control it use the following : ""A"" and ""Z"" control the Z\n"
    << "movement, "","" and ""."" control the X movement, and ""S"" and ""X""\n"
    << "control the Y movement. I have not included rotation control, but\n"
    << "it should be easy enough to put in yourself ... if you have any\n"
    << "hassles, leave me mail.\n\n";
  cout << "Hit any key to continue ...\n";
  getch();
  SetMCGA();
 
  SetUpPoints();
 
  MoveAround();
  SetText();
 
  ShutDown(Virscr);
 
  cout
    << "All done. This concludes the ninth sample program in the ASPHYXIA\n"
    << "Training series. You may reach DENTHOR under the names of GRANT\n"
    << "SMITH/DENTHOR/ASPHYXIA on the ASPHYXIA BBS. I am also an avid\n"
    << "Connectix BBS user, and occasionally read RSAProg.\n"
    << "The numbers are available in the main text. You may also write to me at:\n"
    << "             Grant Smith\n"
    << "             P.O. Box 270\n"
    << "             Kloof\n"
    << "             3640\n"
    << "I hope to hear from you soon!\n\n";
  cout << "Hit any key to exit ...\n";
  getch();
 
}
 
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// DrawPoly() - This draws a polygon with 4 points at x1,y1, x2,y2, x3,y3, //
//              x4,y4 in color Col at location Where                       //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void DrawPoly(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4,
              byte Col, word Where) {
 
  int x, mny, mxy, mnx, mxx, yc;
  int mul1, div1, mul2, div2, mul3, div3, mul4, div4;
 
  // find the maximum y (mny) and minimum y (mny)
              mny = y1;
              mxy = y1;
  if (y2<mny) mny = y2;
  if (y2>mxy) mxy = y2;
  if (y3<mny) mny = y3;
  if (y3>mxy) mxy = y3;
  if (y4<mny) mny = y4;
  if (y4>mxy) mxy = y4;
 
  // if the mimimum or maximum is out of bounds, bring it back in
  if (mny<  0) mny =   0;
  if (mxy>199) mxy = 199;
 
  // verticle range checking
  if (mny>199) return;
  if (mxy<  0) return;
 
  // constants needed for intersection calculations
  mul1 = x1-x4;  div1 = y1-y4;
  mul2 = x2-x1;  div2 = y2-y1;
  mul3 = x3-x2;  div3 = y3-y2;
  mul4 = x4-x3;  div4 = y4-y3;
 
  for (yc=mny; yc<mxy; yc++) {
    mnx = 320;
    mxx =  -1;
 
    if ((y4 >= yc) || (y1 >= yc))
      if ((y4 <= yc) || (y1 <= yc))
        if (y4 != y1) {
          x = ((yc-y4) * mul1 / div1) + x4;
          if (x<mnx) mnx = x;
          if (x>mxx) mxx = x;
        }
 
    if ((y1 >= yc) || (y2 >= yc))
      if ((y1 <= yc) || (y2 <= yc))
        if (y1 != y2) {
          x = ((yc-y1) * mul2 / div2) + x1;
          if (x<mnx) mnx = x;
          if (x>mxx) mxx = x;
        }
 
    if ((y2 >= yc) || (y3 >= yc))
      if ((y2 <= yc) || (y3 <= yc))
        if (y2 != y3) {
          x = ((yc-y2) * mul3 / div3) + x2;
          if (x<mnx) mnx = x;
          if (x>mxx) mxx = x;
        }
 
    if ((y3 >= yc) || (y4 >= yc))
      if ((y3 <= yc) || (y4 <= yc))
        if (y3 != y4) {
          x = ((yc-y3) * mul4 / div4) + x3;
          if (x<mnx) mnx = x;
          if (x>mxx) mxx = x;
        }
 
    // horizontal range checking
    if (mnx<  0)  mnx =   0;
    if (mxx>319)  mxx = 319;
 
    if (mnx<=mxx)
      // draw the horizontal line
      Hline(mnx,mxx,yc,Col,Where);
 
  }
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// SetUpPoints() - This creates the lookup table.                          //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void SetUpPoints() {
 
  int loop1;
 
  // generate the sin() and cos() tables
  for (loop1=0; loop1<360; loop1++) {
    Lookup [loop1][0] = sin(rad(loop1));
    Lookup [loop1][1] = cos(rad(loop1));
  }
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// RotatePoints() - This rotates object lines by X,Y and Z, then places    //
//                  the result in Translated[]                             //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void RotatePoints(int X, int Y, int Z) {
 
  int loop1, loop2;
  Point temp;
 
  for (loop1=0; loop1<MAXPOLYS; loop1++) {
    for (loop2=0; loop2<4; loop2++) {
      temp.x = Lines[loop1][loop2].x;
      temp.y = Lookup[X][1] * Lines[loop1][loop2].y - Lookup[X][0] * Lines[loop1][loop2].z;
      temp.z = Lookup[X][0] * Lines[loop1][loop2].y + Lookup[X][1] * Lines[loop1][loop2].z;
      Translated[loop1][loop2] = temp;
 
      if (Y>0) {
        temp.x = Lookup[Y][1] * Translated[loop1][loop2].x - Lookup[Y][0] * Translated[loop1][loop2].y;
        temp.y = Lookup[Y][0] * Translated[loop1][loop2].x + Lookup[Y][1] * Translated[loop1][loop2].y;
        temp.z = Translated[loop1][loop2].z;
        Translated[loop1][loop2] = temp;
      }
 
      if (Z>0) {
        temp.x = Lookup[Z][1] * Translated[loop1][loop2].x + Lookup[Z][0] * Translated[loop1][loop2].z;
        temp.y = Translated[loop1][loop2].y;
        temp.z = Lookup[Z][0] * Translated[loop1][loop2].x + Lookup[Z][1] * Translated[loop1][loop2].z;
        Translated[loop1][loop2] = temp;
      }
    }
  }
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// DrawPoints() - This draws the translated object to the virtual screen.  //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void DrawPoints() {
 
  int nx, ny, nx2, ny2, nx3, ny3, nx4, ny4, temp, loop1;
 
  for (loop1=0; loop1<MAXPOLYS; loop1++) {
    if ((Translated[loop1][0].z+Zoff<0) && (Translated[loop1][1].z+Zoff<0) &&
        (Translated[loop1][2].z+Zoff<0) && (Translated[loop1][4].z+Zoff<0)) {
 
      temp = Translated[loop1][0].z + Zoff;
      nx   = ((256*Translated[loop1][0].x) / temp) + Xoff;
      ny   = ((256*Translated[loop1][0].y) / temp) + Yoff;
 
      temp = Translated[loop1][1].z + Zoff;
      nx2  = ((256*Translated[loop1][1].x) / temp) + Xoff;
      ny2  = ((256*Translated[loop1][1].y) / temp) + Yoff;
 
      temp = Translated[loop1][2].z + Zoff;
      nx3  = ((256*Translated[loop1][2].x) / temp) + Xoff;
      ny3  = ((256*Translated[loop1][2].y) / temp) + Yoff;
 
      temp = Translated[loop1][3].z + Zoff;
      nx4  = ((256*Translated[loop1][3].x) / temp) + Xoff;
      ny4  = ((256*Translated[loop1][3].y) / temp) + Yoff;
 
      DrawPoly(nx,ny,nx2,ny2,nx3,ny3,nx4,ny4,13,Vaddr);
    }
  }
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// Whizz() - This function moves the letters from one side of the screen   //
//           to the other and also zooms them closer as they move.         //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Whizz(int sub, int &deg) {
 
  int loop1;
 
  for (loop1=(-64); loop1<(-4); loop1++) {
 
    Zoff = (loop1 * 8) - 15;
    if (sub == 1) Xoff -= 7; else Xoff += 7;
    RotatePoints(deg,deg,deg);
    DrawPoints();
    Flip(Vaddr,VGA);
    Cls(0,Vaddr);
    deg = (deg + 5) % 360;
  }
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// MoveAround() - This is the main display function.  First it brings the  //
//                object towards the viewer by increasing the Zoff, then   //
//                it passes control to the user.                           //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void MoveAround() {
 
  int deg=0, loop1, loop2;
  byte ch=1; // assign a dummy value to ch
 
  Yoff = 100;
  Xoff = 350;
  Cls(0,Vaddr);
 
  for (loop1=0; loop1<MAXPOLYS; loop1++)
    for (loop2=0; loop2<POLYPOINTS; loop2++) {
      Lines[loop1][loop2].x = A[loop1][loop2][0];
      Lines[loop1][loop2].y = A[loop1][loop2][1];
      Lines[loop1][loop2].z = A[loop1][loop2][2];
    }
  Whizz(1,deg);
 
  for (loop1=0; loop1<MAXPOLYS; loop1++)
    for (loop2=0; loop2<POLYPOINTS; loop2++) {
      Lines[loop1][loop2].x = S[loop1][loop2][0];
      Lines[loop1][loop2].y = S[loop1][loop2][1];
      Lines[loop1][loop2].z = S[loop1][loop2][2];
    }
  Whizz(0,deg);
 
  for (loop1=0; loop1<MAXPOLYS; loop1++)
    for (loop2=0; loop2<POLYPOINTS; loop2++) {
      Lines[loop1][loop2].x = P[loop1][loop2][0];
      Lines[loop1][loop2].y = P[loop1][loop2][1];
      Lines[loop1][loop2].z = P[loop1][loop2][2];
    }
  Whizz(1,deg);
 
  for (loop1=0; loop1<MAXPOLYS; loop1++)
    for (loop2=0; loop2<POLYPOINTS; loop2++) {
      Lines[loop1][loop2].x = H[loop1][loop2][0];
      Lines[loop1][loop2].y = H[loop1][loop2][1];
      Lines[loop1][loop2].z = H[loop1][loop2][2];
    }
  Whizz(0,deg);
 
  for (loop1=0; loop1<MAXPOLYS; loop1++)
    for (loop2=0; loop2<POLYPOINTS; loop2++) {
      Lines[loop1][loop2].x = Y[loop1][loop2][0];
      Lines[loop1][loop2].y = Y[loop1][loop2][1];
      Lines[loop1][loop2].z = Y[loop1][loop2][2];
    }
  Whizz(1,deg);
 
  for (loop1=0; loop1<MAXPOLYS; loop1++)
    for (loop2=0; loop2<POLYPOINTS; loop2++) {
      Lines[loop1][loop2].x = X[loop1][loop2][0];
      Lines[loop1][loop2].y = X[loop1][loop2][1];
      Lines[loop1][loop2].z = X[loop1][loop2][2];
    }
  Whizz(0,deg);
 
  for (loop1=0; loop1<MAXPOLYS; loop1++)
    for (loop2=0; loop2<POLYPOINTS; loop2++) {
      Lines[loop1][loop2].x = I[loop1][loop2][0];
      Lines[loop1][loop2].y = I[loop1][loop2][1];
      Lines[loop1][loop2].z = I[loop1][loop2][2];
    }
  Whizz(1,deg);
 
  for (loop1=0; loop1<MAXPOLYS; loop1++)
    for (loop2=0; loop2<POLYPOINTS; loop2++) {
      Lines[loop1][loop2].x = A[loop1][loop2][0];
      Lines[loop1][loop2].y = A[loop1][loop2][1];
      Lines[loop1][loop2].z = A[loop1][loop2][2];
    }
  Whizz(0,deg);
 
  Cls(0,Vaddr);
  Cls(0,VGA);
 
  Xoff = 160;
 
  do {
    if (kbhit()) {
      ch = getch();
      switch (ch) {
        case 'A': case 'a': Zoff += 5; break;  // away
        case 'Z': case 'z': Zoff -= 5; break;  // toward
        case ',':           Xoff -= 5; break;  // left
        case '.':           Xoff += 5; break;  // right
        case 'S': case 's': Yoff -= 5; break;  // down
        case 'X': case 'x': Yoff += 5; break;  // up
      }
    }
    DrawPoints();
    Flip(Vaddr,VGA);
    Cls(0,Vaddr);
    RotatePoints(deg,deg,deg);
    deg = (deg + 5) % 360;
 
    // if the key pressed above was 0 (i.e. a control character) then
    // read the character code
    if (ch == 0) ch = getch();
 
  } while (ch != 27); // if the escape code was 27 (escape key) then exit
 
}
file:gfx2.cpp
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// GFX2.CPP - VGA Trainer Program secondary module containing graphics     //
//            functions.  Note: This module does not follow a lot of good  //
//            programming practices.  It was built to be used with the     //
//            VGA tutorial series.  If you are planning on using this      //
//            module with a different source file, some modifications may  //
//            be necessary.                                                //
//                                                                         //
// Author        : Grant Smith (Denthor) - denthor@beastie.cs.und.ac.za    //
// Translator    : Christopher G. Mann   - r3cgm@dax.cc.uakron.edu         //
//                                                                         //
// Last Modified : January 21, 1995                                        //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
//               //
// INCLUDE FILES //
//               //
 
  #include <alloc.h>
                           // farcalloc(), farfree()
  #include <dos.h>
                           // geninterrupt(), FP_SEG
 
//         //
// DEFINES //
//         //
 
  #if !defined(PI)
    #define PI 3.1415927
  #endif
 
  #if !defined(VGA)
    #define VGA 0xA000
  #endif
 
//          //
// TYPEDEFS //
//          //
 
  typedef unsigned char byte;
  typedef unsigned int  word;
 
//                     //
// FUNCTION PROTOTYPES //
//                     //
 
  // VIRTUAL SCREEN FUNCTIONS
  void  SetUpVirtual(byte far *&Virscr, word &Vaddr);
  void  ShutDown    (byte far *&Virscr);
  void  Cls         (byte Col, word Where);
  void  Flip        (word source, word dest);
 
  // MODE SETTING FUNCTIONS
  void  SetMCGA     ();
  void  SetText     ();
 
  // PALLETTE CLASS (DATA OBJECT AND RELATED FUNCTIONS)
  class Pal {
    public:
      Pal();
      void PalSet   (byte Rset, byte Gset, byte Bset);
      void PalGet   (byte Col);
      void PalPut   (byte Col);
      void PalInc   ();
      void PalDec   ();
    private:
      byte R;   // 0-63
      byte G;   // 0-63
      byte B;   // 0-63
  };
 
  // MATH-LIKE FUNCTIONS
  float rad         (float theta);
  int   sgn         (int a);
 
  template<class T>
  T abso(T value) {  if (value >= 0) return value;  else return -value; }
 
  // DRAWING FUNCTIONS
  void  Putpixel    (word X, word Y, byte Col, word Where);
  void  PutpixelVGA (word X, word Y, byte Col);
  void  Line        (int a, int b, int c, int  d, int col, word Where);
  void  Hline       (word X1, word X2, word Y, byte Col, word Where);
 
 
//-------------------------VIRTUAL SCREEN FUNCTIONS------------------------//
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// SetUpVirtual() - This sets up the memory needed for a virtual screen.   //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void SetUpVirtual(byte far *&Virscr, word &Vaddr) {
  Virscr = (byte far *) farcalloc(64000,1);
  Vaddr = FP_SEG(Virscr);
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// ShutDown() - This frees the memory used by a virtual screen.            //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void ShutDown(byte far *&Virscr) {
  farfree(Virscr);
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// Cls() - This clears the screen at Where to color Col.                   //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Cls(byte Col, word Where) {
  asm {
    push    es           // save ES
    mov     cx, 32000    // this is our loop counter.  we want to clear
                         //   64000 bytes of memory, so why do we use 32000?
                         //   1 word = 2 bytes, and we are moving a word at
                         //   a time
    mov     es, [Where]  // move address in Where to ES
    xor     di, di       // zero out DI
    mov     al, [Col]    // move color to AL
    mov     ah, al       // move color to AH (Remember, will be moving
                         //   a WORDS, so we need two copies
    rep     stosw        // copy AX to Where and drecrement CX by 1
                         //   until CX equals 0
    pop     es           // restore ES
  }
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// Flip() - This copies 64000 bytes from "source" to "destination".        //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Flip(word source, word dest) {
  asm {
    push    ds           // save DS
    mov     ax, [dest]   // copy segment of destination to AX
    mov     es, ax       // set ES to point to destination
    mov     ax, [source] // copy segment of source to AX
    mov     ds, ax       // set DS to point to source
    xor     si, si       // zero out SI
    xor     di, di       // zero out DI
    mov     cx, 32000    // set our counter to 32000
    rep     movsw        // move source to destination by words.  decrement
                         //   CX by 1 each time until CX is 0
    pop     ds           // restore DS
  }
}
 
 
//--------------------------MODE SETTING FUNCTIONS-------------------------//
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// SetMCGA() - This function gets you into 320x200x256 mode.               //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void SetMCGA() {
  _AX = 0x0013;
  geninterrupt (0x10);
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// SetText() - This function gets you into text mode.                      //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void SetText() {
  _AX = 0x0003;
  geninterrupt (0x10);
}
 
 
//----------------------------PALLETTE FUNCTIONS---------------------------//
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// Pal() - This constructor initializes all Pal variables (R, G, and B) to //
//         zero.  This ensures that all Pal objects start in a consistent  //
//         state.                                                          //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
Pal::Pal() { R = B = G = 0; }
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// PalSet() - This sets the Red, Green, and Blue values of a given color.  //
//            Set invalid colors ( >63 ) equal to 0.                       //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Pal::PalSet(byte Rset, byte Gset, byte Bset) {
 
   R = (Rset < 64) ? Rset : 0;
   G = (Gset < 64) ? Gset : 0;
   B = (Bset < 64) ? Bset : 0;
 
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// PalGet() - This reads the values of the Red, Green, and Blue values of  //
//            a certain color.                                             //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Pal::PalGet(byte Col) {
 
  byte Rtemp, Gtemp, Btemp;
 
  asm {
    mov     dx, 0x03C7   // load DX with 3C7 (read pallette function)
    mov     al, [Col]    // move color to AL
    out     dx, al       // write DX to the VGA (tell VGA that we want to
                         //   work with the color in AL
    add     dx, 2        // load DX with 3C9 (read RGB colors)
    in      al, dx       // read Red   to AL
    mov     [Rtemp],al   // copy AL to rr
    in      al, dx       // read Green to AL
    mov     [Gtemp],al   // copy AL to gg
    in      al, dx       // read Blue  to AL
    mov     [Btemp],al   // copy AL to bb
  }
 
  R = Rtemp;
  G = Gtemp;
  B = Btemp;
 
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// PalPut() - This sets the Red, Green, and Blue values of a color.        //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Pal::PalPut (byte Col) {
 
  byte Rtemp = R, Gtemp = G, Btemp = B;
 
  asm {
    mov     dx, 0x3C8    // load DX with 3C8 (write pallette function)
    mov     al, [Col]    // move color to AL
    out     dx, al       // write DX to the VGA (tell VGA that we want to
                         //   work with the color in AL
    inc     dx           // load DX with 3C9 (write RGB colors)
    mov     al, [Rtemp]  // move Red   to AL
    out     dx, al       // write DX to VGA (tell VGA that we want to use
                         //   the Red value in AL
    mov     al, [Gtemp]  // move Green to AL
    out     dx, al       // write DX to VGA
    mov     al, [Btemp]  // move Blue  to AL
    out     dx, al       // write DX to VGA
  }
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// PalInc() - This increments the R, G, and B values of a given Pal        //
//            variable, keeping all values less than 64.                   //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Pal::PalInc() {
 
  // if (R < 63) R = R + 1; else R = 63;
  R = (R < 63) ? (R + 1) : 63;
  G = (G < 63) ? (G + 1) : 63;
  B = (B < 63) ? (B + 1) : 63;
 
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// PalDec() - This decrements the R, G, and B values of a given Pal        //
//            variable, keeping all values greater than or equal to zero.  //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Pal::PalDec() {
 
  // if (R > 0) R = R - 1; else R = 0;
  R = (R > 0) ? (R - 1) : 0;
  G = (G > 0) ? (G - 1) : 0;
  B = (B > 0) ? (B - 1) : 0;
 
}
 
 
//----------------------------MATH-LIKE FUNCTIONS--------------------------//
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// rad() - This calculates the degrees of an angle.                        //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
float rad(float theta) {
  return ((theta * PI)/180);
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// sgn() - This checks the sign of an integer and returns a 1, -1, or 0.   //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
int sgn (int a) {
 
  if (a > 0)  return +1;
  if (a < 0)  return -1;
  return 0;
}
 
 
//-----------------------------DRAWING FUNCTIONS---------------------------//
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// Putpixel() - This puts a pixel on the screen by writing directly to     //
//              memory.                                                    //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Putpixel (word X, word Y, byte Col, word Where) {
  asm {
    mov     ax, [Where]  // move segment of Where to AX
    mov     es, ax       // ES = VGA
    mov     bx, [X]      // BX = X
    mov     dx, [Y]      // DX = Y
    mov     ah, dl       // AH = Y*256
    xor     al, al       // AX = Y*256
    shl     dx, 6        // DX = Y*64
    add     dx, ax       // DX = Y*320
    add     bx, dx       // BX = Y*320 + X
    mov     ah, [Col]    // move value of Col into AH
    mov     byte ptr es:[bx], ah  // move Col to the offset in memory (DI)
  }
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// PutpixelVGA() - This puts a pixel on the screen by writing directly to  //
//                 VGA memory.                                             //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void PutpixelVGA (word X, word Y, byte Col) {
  asm {
    mov     ax, 0xA000   // AX = VGA Segment
    mov     es, ax       // ES = VGA Segment
    mov     bx, [X]      // BX = X
    mov     dx, [Y]      // DX = Y
    mov     ah, dl       // AX = Y*256 (AL is already 0 from A000 address)
    shl     dx, 6        // DX = Y*64
    add     dx, ax       // DX = Y*320
    add     bx, dx       // BX = Y*320 + X
    mov     ah, [Col]    // move value of Col into AH
    mov     byte ptr es:[bx], ah  // move Col to the offset in memory (DI)
  }
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// Line() - This draws a line from a,b to c,d of color col on screne Where //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Line(int a, int b, int c, int d, int col, word Where) {
 
  int i,u,s,v,d1x,d1y,d2x,d2y,m,n;
 
  u   = c-a;       // x2-x1
  v   = d-b;       // y2-y1
  d1x = sgn(u);    // d1x is the sign of u (x2-x1) (VALUE -1,0,1)
  d1y = sgn(v);    // d1y is the sign of v (y2-y1) (VALUE -1,0,1)
  d2x = sgn(u);    // d2x is the sign of u (x2-x1) (VALUE -1,0,1)
  d2y = 0;
  m   = abso(u);   // m is the distance between x1 and x2
  n   = abso(v);   // n is the distance between y1 and y2
 
  if (m<=n) {      // if the x distance is greater than the y distance
    d2x = 0;
    d2y = sgn(v);  // d2y is the sign of v (x2-x1) (VALUE -1,0,1)
    m   = abso(v); // m is the distance between y1 and y2
    n   = abso(u); // n is the distance between x1 and x2
  }
 
  s = m / 2; // s is the m distance (either x or y) divided by 2
 
  for (i=0;i<m+1;i++) { // repeat this loop until it
                 // is = to m (y or x distance)
    Putpixel(a,b,col,Where); // plot a pixel at the original x1, y1
    s += n;                  // add n (dis of x or y) to s (dis of x of y)
    if (s >= m) {            // if s is >= m (distance between y1 and y2)
      s -= m;
      a += d1x;
      b += d1y;
    }
    else {
      a += d2x;
      b += d2y;
    }
  }
 
}
 
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// Hline() - This draws a horizontal line from X1 to X2 on line Y in color //
//           Col at memory location Where.                                 //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
 
void Hline (word X1, word X2, word Y, byte Col, word Where) {
  asm {
    mov     ax, [Where]  // move segment of Where to AX
    mov     es, ax       // set ES to segment of Where
    mov     ax, [Y]      // set AX to Y
    mov     di, ax       // set DI to Y
    shl     ax, 8        // shift AX left 8 places (multiply Y by 256)
    shl     di, 6        // shift DI left 6 places (multiply Y by 64)
    add     di, ax       // add AX to DI (Y*64 + Y*256 = Y*320)
    add     di, [X1]     // add the X1 offset to DI
    mov     al, [Col]    // move Col to AL
    mov     ah, al       // move Col to AH (we want 2 copies for word moving)
    mov     cx, [X2]     // move X2 to CX
    sub     cx, [X1]     // move the change in X to CX
    shr     cx, 1        // divide change in X by 2 (for word moving)
    jnc     Start        // if we have an even number of moves, go to Start
    stosb                // otherwise, move one byte more
  }
  Start: asm {
    rep     stosw        // do it!
  }
}