program TP_EX1;

{

  "Introduction to 3D Programming" Tutorial series
  Article #1 - For DemoNews 114

  Sample code for Turbo Pascal 7.0 or above (might work with lesser versions,
  probably at least 6.0, but it's untested so I can't guarantee anything).

  Make sure at least 286 instructions are enabled in your compiler options.

}

uses crt;


{ "vector" structure - holds a 3D cartesian point, and its projection. }

type
    vector = record
             x : integer;
             y : integer;
             z : integer;
             scrx : integer;
             scry : integer;
             end;

{ Box object, used for this example. }
var
   Box : array[0..7] of vector;  {The values are initialized later}

{
Videomode and Putpixel functions for graphics display

(Yes, the putpixel is not optimized, but that's not the point of
 this example program :-)
}

procedure videomode(mode : word); assembler;
asm
   mov ax, mode
   int 10h
end;

procedure putpixel(x, y : word; color : byte); assembler;
asm
   mov ax, 0a000h
   mov es, ax
   mov ax, y
   shl ax, 6
   mov bx, ax
   shl ax, 2
   add ax, bx
   add ax, x
   mov di, ax
   mov al, color
   mov [es:di], al
end;

{
procedure PointProject(distance : integer; point : vector,
                       var dest : vector; center : vector);

Takes a viewing distance from the origin along the Z axis (in our examples
we've been using 256, so that's what I used in the main program), and the
3D point you want to project.  Fills in the 2D scrx and scry values of the
given vector, or a separate "after projection" vector, depending on what
you pass in "dest" (I used the same vector for this example).

The center vector parameter is just added to the point you give.  Stupid
by itself, yes... but if you use the same center for the set of points in
an object (I used a box in this program), the whole object is moved (called
"translation") by the center.  Just run the example, and you'll see what I
mean. :-)

}


procedure PointProject(distance : integer; point : vector;
                       var dest : vector; center : vector);
begin
     dest.scrx := (256*(point.x+center.x)
                  div (distance-(point.z+center.z)))+160;

     dest.scry := 100-(256*(point.y+center.y)
                  div (distance-(point.z+center.z)));
end;

{
procedure DrawBox(cenx, ceny, cenz : integer; color : byte);

Draws the Box object with the 3D center you give.  The center points are
merged into the one "center" vector that PointProject wants.  All the points
use the same center, which "moves" the object.
}

procedure DrawBox(cenx, ceny, cenz : integer; color : byte);
var
   count : integer;
   BoxCenter : vector;
begin
     BoxCenter.x := cenx;
     BoxCenter.y := ceny;
     BoxCenter.z := cenz;

     for count := 0 to 7 do
     begin
          PointProject(256, Box[count], Box[count], BoxCenter);
          putpixel(Box[count].scrx, Box[count].scry, color);
     end;
end;


{
Main program - Draws three boxes at different 3D centers, showing how
	       the depth perception works.
}

begin

     Box[0].x := 20;   Box[0].y := 40;   Box[0].z := 30;
     Box[1].x := 20;   Box[1].y := 40;   Box[1].z := -30;
     Box[2].x := 20;   Box[2].y := -40;  Box[2].z := 30;
     Box[3].x := 20;   Box[3].y := -40;  Box[3].z := -30;
     Box[4].x := -20;  Box[4].y := 40;   Box[4].z := 30;
     Box[5].x := -20;  Box[5].y := 40;   Box[5].z := -30;
     Box[6].x := -20;  Box[6].y := -40;  Box[6].z := 30;
     Box[7].x := -20;  Box[7].y := -40;  Box[7].z := -30;


     videomode($13);
     DrawBox(-60, 0, -40, 13);  { Purple box, Z=-40 so it's further away }
     DrawBox(  0, 0,   0, 14);  { Yellow box, Z=0 so it's at the image plane}
     DrawBox( 60, 0,  40, 15);  { White box,  Z=40 so it's closer to you }
     repeat until keypressed;
     videomode(3);
end.

