home *** CD-ROM | disk | FTP | other *** search
/ HomeWare 14 / HOMEWARE14.bin / prog / pcgpe10.arj / TUT8.TXT < prev    next >
Text File  |  1994-05-05  |  29KB  |  793 lines

  1.                    ╒═══════════════════════════════╕
  2.                    │         W E L C O M E         │
  3.                    │  To the VGA Trainer Program   │ │
  4.                    │              By               │ │
  5.                    │      DENTHOR of ASPHYXIA      │ │ │
  6.                    ╘═══════════════════════════════╛ │ │
  7.                      ────────────────────────────────┘ │
  8.                        ────────────────────────────────┘
  9.  
  10.                            --==[ PART 8 ]==--
  11.  
  12.  
  13.  
  14. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  15. ■ Introduction
  16.  
  17. Hello everybody! Christmas is over, the last of the chocolates have been
  18. eaten, so it's time to get on with this, the eighth part of the ASPHYXIA
  19. Demo Trainer Series. This particular part is primarily about 3-D, but
  20. also includes a bit on optimisation.
  21.  
  22. If you are already a 3-D guru, you may as well skip this text file, have
  23. a quick look at the sample program then go back to sleep, because I am
  24. going to explain in minute detail exactly how the routines work ;)
  25.  
  26. If you would like to contact me, or the team, there are many ways you
  27. can do it : 1) Write a message to Grant Smith/Denthor/Asphyxia in private mail
  28.                   on the ASPHYXIA BBS.
  29.             2) Write a message in the Programming conference on the
  30.                   For Your Eyes Only BBS (of which I am the Moderator )
  31.                   This is preferred if you have a general programming query
  32.                   or problem others would benefit from.
  33.             4) Write to Denthor, EzE or Goth on Connectix.
  34.             5) Write to :  Grant Smith
  35.                            P.O.Box 270 Kloof
  36.                            3640
  37.                            Natal
  38.             6) Call me (Grant Smith) at (031) 73 2129 (leave a message if you
  39.                   call during varsity)
  40.             7) Write to mcphail@beastie.cs.und.ac.za on InterNet, and
  41.                   mention the word Denthor near the top of the letter.
  42.  
  43. NB : If you are a representative of a company or BBS, and want ASPHYXIA
  44.        to do you a demo, leave mail to me; we can discuss it.
  45. NNB : If you have done/attempted a demo, SEND IT TO ME! We are feeling
  46.         quite lonely and want to meet/help out/exchange code with other demo
  47.         groups. What do you have to lose? Leave a message here and we can work
  48.         out how to transfer it. We really want to hear from you!
  49.  
  50.  
  51.  
  52. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  53. ■ Optimisation
  54.  
  55. Before I begin with the note on 3-D, I would like to stress that many of
  56. these routines, and probably most of your own, could be sped up quite a
  57. bit with a little optimisation. One must realise, however, that you must
  58. take a look at WHAT to optimise ... converting a routine that is only
  59. called once at startup into a tightly coded assembler routine may show
  60. off your merits as a coder, but does absolutely nothing to speed up your
  61. program. Something that is called often per frame is something that
  62. needs to be as fast as possible. For some, a much used procedure is the
  63. PutPixel procedure. Here is the putpixel procedure I gave you last week:
  64.  
  65. Procedure Putpixel (X,Y : Integer; Col : Byte; where:word);
  66. BEGIN
  67.   Asm
  68.     push    ds                      { 14   clock ticks }
  69.     push    es                      { 14 }
  70.     mov     ax,[where]              { 8  }
  71.     mov     es,ax                   { 2 }
  72.     mov     bx,[X]                  { 8  }
  73.     mov     dx,[Y]                  { 8  }
  74.     push    bx                      { 15 }
  75.     mov     bx, dx                  { 2  }
  76.     mov     dh, dl                  { 2  }
  77.     xor     dl, dl                  { 3  }
  78.     shl     bx, 1                   { 2  }
  79.     shl     bx, 1                   { 2  }
  80.     shl     bx, 1                   { 2  }
  81.     shl     bx, 1                   { 2  }
  82.     shl     bx, 1                   { 2  }
  83.     shl     bx, 1                   { 2  }
  84.     add     dx, bx                  { 3  }
  85.     pop     bx                      { 12 }
  86.     add     bx, dx                  { 3  }
  87.     mov     di, bx                  { 2 }
  88.     xor     al,al                   { 3  }
  89.     mov     ah, [Col]               { 8  }
  90.     mov     es:[di],ah              { 10 }
  91.     pop     es                      { 12 }
  92.     pop     ds                      { 12 }
  93.   End;
  94. END;
  95.                             Total = 153 clock ticks
  96. NOTE : Don't take my clock ticks as gospel, I probably got one or two
  97.        wrong.
  98.  
  99. Right, now for some optimising. Firstly, if you have 286 instructions
  100. turned on, you may replace the 6 shl,1 with shl,6. Secondly, the Pascal
  101. compiler automatically pushes and pops ES, so those two lines may be
  102. removed. DS:[SI] is not altered in this procedure, so we may remove
  103. those too. Also, instead of moving COL into ah, we move it into AL and
  104. call stosb (es:[di]:=al; inc di). Let's have a look at the routine now :
  105.  
  106. Procedure Putpixel (X,Y : Integer; Col : Byte; where:word);
  107. BEGIN
  108.   Asm
  109.     mov     ax,[where]              { 8  }
  110.     mov     es,ax                   { 2 }
  111.     mov     bx,[X]                  { 8  }
  112.     mov     dx,[Y]                  { 8  }
  113.     push    bx                      { 15 }
  114.     mov     bx, dx                  { 2  }
  115.     mov     dh, dl                  { 2  }
  116.     xor     dl, dl                  { 3  }
  117.     shl     bx, 6                   { 8  }
  118.     add     dx, bx                  { 3  }
  119.     pop     bx                      { 12 }
  120.     add     bx, dx                  { 3  }
  121.     mov     di, bx                  { 2 }
  122.     mov     al, [Col]               { 8  }
  123.     stosb                           { 11 }
  124.   End;
  125. END;
  126.                             Total = 95 clock ticks
  127.  
  128. Now, let us move the value of BX directly into DI, thereby removing a
  129. costly push and pop. The MOV and the XOR of DX can be replaced by it's
  130. equivalent, SHL DX,8
  131.  
  132. Procedure Putpixel (X,Y : Integer; Col : Byte; where:word); assembler;
  133. asm
  134.     mov     ax,[where]              { 8  }
  135.     mov     es,ax                   { 2  }
  136.     mov     bx,[X]                  { 8  }
  137.     mov     dx,[Y]                  { 8  }
  138.     mov     di,bx                   { 2  }
  139.     mov     bx, dx                  { 2  }
  140.     shl     dx, 8                   { 8  }
  141.     shl     bx, 6                   { 8  }
  142.     add     dx, bx                  { 3  }
  143.     add     di, dx                  { 3  }
  144.     mov     al, [Col]               { 8  }
  145.     stosb                           { 11 }
  146. end;
  147.                             Total = 71 clock ticks
  148.  
  149. As you can see, we have brought the clock ticks down from 153 ticks to
  150. 71 ticks ... quite an improvement. (The current ASPHYXIA putpixel takes
  151. 48 clock ticks) . As you can see, by going through your routines a few
  152. times, you can spot and remove unnecessary instructions, thereby greatly
  153. increasing the speed of your program.
  154.  
  155.  
  156. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  157. ■ Defining a 3-D object
  158.  
  159. Drawing an object in 3-D is not that easy. Sitting down and plotting a
  160. list of X,Y and Z points can be a time consuming business. So, let us
  161. first look at the three axes you are drawing them on :
  162.  
  163.                     Y    Z
  164.                    /|\  /
  165.                     | /
  166.              X<-----|----->
  167.                     |
  168.                    \|/
  169.  
  170. X is the horisontal axis, from left to right. Y is the vertical axis,
  171. from top to bottom. Z is the depth, going straight into the screen.
  172.  
  173. In this trainer, we are using lines, so we define 2 X,Y and Z
  174. coordinates, one for each end of the line. A line from far away, in the
  175. upper left of the X and Y axes, to close up in the bottom right of the
  176. X and Y axes, would look like this :
  177.  
  178. {       x1 y1  z1   x2  y2 z2    }
  179.     ( (-10,10,-10),(10,-10,10) )
  180.  
  181.  
  182. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  183. ■ Rotating a point with matrixes
  184.  
  185. NOTE : I thought that more then one matix are matrisese (sp), but my 
  186.        spellchecker insists it is matrixes, so I let it have it's way 
  187.        ;-)
  188.  
  189. Having a 3-D object is useless unless you can rotate it some way. For
  190. demonstration purposes, I will begin by working in two dimensions, X and
  191. Y.
  192.  
  193. Let us say you have a point, A,B, on a graph.
  194.                       Y
  195.                       |  /O1 (Cos (a)*A-Sin (a)*B , Sin (a)*A+Cos (a)*B)
  196.                       |/      (A,B)
  197.                X<-----|------O-->
  198.                       |
  199.                       |
  200.  
  201. Now, let us say we rotate this point by 45 degrees anti-clockwise. The
  202. new A,B can be easily be calculated using sin and cos, by an adaption of
  203. our circle algorithm, ie.
  204.            A2:=Cos (45)*A - Sin (45)*B
  205.            B2:=Sin (45)*A + Cos (45)*B
  206. I recall that in standard 8 and 9, we went rather heavily into this in
  207. maths. If you have troubles, fine a 8/9/10 maths book and have a look;
  208. it will go through the proofs etc.
  209.  
  210. Anyway, we have now rotated an object in two dimensions, AROUND THE Z
  211. AXIS. In matrix form, the equation looks like this :
  212.  
  213.    [  Cos (a)   -Sin (a)      0        0     ]    [  x ]
  214.    [  Sin (a)    Cos (a)      0        0     ]  . [  y ]
  215.    [     0         0          1        0     ]    [  z ]
  216.    [     0         0          0        1     ]    [  1 ]
  217.  
  218. I will not go to deeply into matrixes math at this stage, as there are
  219. many books on the subject (it is not part of matric maths, however). To
  220. multiply a matrix, to add the products of the row of the left matrix and
  221. the column of the right matrix, and repeat this for all the columns of the
  222. left matrix. I don't explain it as well as my first year maths lecturer,
  223. but have a look at how I derived A2 and B2 above. Here are the other
  224. matrixes :
  225.  
  226. Matrix for rotation around the Y axis :
  227.    [  Cos (a)      0       -Sin (a)    0     ]    [  x ]
  228.    [     0         1          0        0     ]  . [  y ]
  229.    [  Sin (a)      0        Cos (a)    0     ]    [  z ]
  230.    [     0         0          0        1     ]    [  1 ]
  231.  
  232. Matrix for rotation around the X axis :
  233.    [     1         0                   0     ]    [  x ]
  234.    [     0       Cos (a)   -Sin (a)    0     ]  . [  y ]
  235.    [     0       Sin (a)    Cos (a)    0     ]    [  z ]
  236.    [     0         0          0        1     ]    [  1 ]
  237.  
  238. By putting all these matrixes together, we can translate out 3D points
  239. around the origin of 0,0,0. See the sample program for how we put them
  240. together.
  241.  
  242. In the sample program, we have a constant, never changing base object.
  243. This is rotated into a second variable, which is then drawn. I am sure
  244. many of you can thing of cool ways to change the base object, the
  245. effects of which will appear while the object is rotating. One idea is
  246. to "pulsate" a certain point of the object according to the beat of the
  247. music being played in the background. Be creative. If you feel up to it,
  248. you could make your own version of transformers ;)
  249.  
  250.  
  251.  
  252. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  253. ■ Drawing a 3D point to screen
  254.  
  255. Having a rotated 3D object is useless unless we can draw it to screen.
  256. But how do we show a 3D point on a 2D screen? The answer needs a bit of
  257. explaining. Examine the following diagram :
  258.  
  259.               |         ________-------------
  260.           ____|___------      o Object at X,Y,Z     o1 Object at X,Y,Z2
  261.  Eye -> O)____|___
  262.               |   ------________
  263.               |                 -------------- Field of vision
  264.             Screen
  265.  
  266. Let us pretend that the centre of the screen is the horizon of our
  267. little 3D world. If we draw a three dimensional line from object "o" to
  268. the centre of the eye, and place a pixel on the X and Y coordinates
  269. where it passes through the screen, we will notice that when we do the
  270. same with object o1, the pixel is closer to the horizon, even though
  271. their 3D X and Y coords are identical, but "o1"'s Z is larger then
  272. "o"'s. This means that the further away a point is, the closer to the
  273. horizon it is, or the smaller the object will appear. That sounds
  274. right, doesent it? But, I hear you cry, how do we translate this into a
  275. formula? The answer is quite simple. Divide your X and your Y by your Z.
  276. Think about it. The larger the number you divide by, the closer to zero,
  277. or the horizon, is the result! This means, the bigger the Z, the
  278. further away is the object! Here it is in equation form :
  279.  
  280.        nx := 256*x div (z-Zoff)+Xoff
  281.        ny := 256*y div (z-Zoff)+Yoff
  282.  
  283. NOTE : Zoff is how far away the entire object is, Xoff is the objects X
  284.        value, and Yoff is the objects Y value. In the sample program,
  285.        Xoff start off at 160 and Yoff starts off at 100, so that the
  286.        object is in the middle of the screen.
  287.  
  288. The 256 that you times by is the perspective with which you are viewing.
  289. Changing this value gives you a "fish eye" effect when viewing the
  290. object. Anyway, there you have it! Draw a pixel at nx,ny, and viola! you
  291. are now doing 3D! Easy, wasn't it?
  292.  
  293.  
  294. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  295. ■ Possible improvements
  296.  
  297. This program is not the most optimised routine you will ever encounter
  298. (;-)) ... it uses 12 muls and 2 divs per point. (Asphyxia currently has
  299. 9 muls and 2 divs per point) Real math is used for all the calculations
  300. in the sample program, which is slow, so fixed point math should be
  301. implemented (I will cover fixed point math in a future trainer). The
  302. line routine currently being used is very slow. Chain-4 could be used to
  303. cut down on screen flipping times.
  304.  
  305. Color values per line should be added, base object morphing could be put
  306. in, polygons could be used instead of lines, handling of more then one
  307. object should be implemented, clipping should be added instead of not
  308. drawing something if any part of it is out of bounds.
  309.  
  310. In other words, you have a lot of work ahead of you ;)
  311.  
  312.  
  313. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  314. ■  In closing
  315.  
  316. There are a lot of books out there on 3D, and quite a few sample
  317. programs too. Have a look at them, and use the best bits to create your
  318. own, unique 3D engine, with which you can do anything you want. I am
  319. very interested in 3D (though EzE and Goth wrote most of ASPHYXIA'S 3D
  320. routines), and would like to see what you can do with it. Leave me a
  321. message through one of the means described above.
  322.  
  323. I am delving into the murky world of texture mapping. If anyone out 
  324. there has some routines on the subject and are interested in swapping, 
  325. give me a buzz!
  326.  
  327. What to do in future trainers? Help me out on this one! Are there any
  328. effects/areas you would like a bit of info on? Leave me a message!
  329.  
  330. I unfortunately did not get any messages regarding BBS's that carry this
  331. series, so the list that follows is the same one from last time. Give
  332. me your names, sysops!
  333.  
  334. Aaaaargh!!! Try as I might, I can't think of a new quote. Next time, I
  335. promise! ;-)
  336.  
  337. Bye for now,
  338.   - Denthor
  339.  
  340.  
  341. These fine BBS's carry the ASPHYXIA DEMO TRAINER SERIES : (alphabetical)
  342.  
  343. ╔══════════════════════════╦════════════════╦═════╦═══╦════╦════╗
  344. ║BBS Name                  ║Telephone No.   ║Open ║Msg║File║Past║
  345. ╠══════════════════════════╬════════════════╬═════╬═══╬════╬════╣
  346. ║ASPHYXIA BBS #1           ║(031) 765-5312  ║ALL  ║ * ║ *  ║ *  ║
  347. ║ASPHYXIA BBS #2           ║(031) 765-6293  ║ALL  ║ * ║ *  ║ *  ║
  348. ║Connectix BBS             ║(031) 266-9992  ║ALL  ║ * ║    ║    ║
  349. ║For Your Eyes Only BBS    ║(031) 285-318   ║A/H  ║ * ║ *  ║ *  ║
  350. ╚══════════════════════════╩════════════════╩═════╩═══╩════╩════╝
  351.  
  352. Open = Open at all times or only A/H
  353. Msg  = Available in message base
  354. File = Available in file base
  355. Past = Previous Parts available
  356.  
  357. ┌──────────────┬─────────────────────────────────────────────────────────────
  358. │ TUTPROG8.PAS │
  359. └──────────────┘
  360.  
  361.  
  362. {$X+}
  363. USES Crt;
  364.  
  365. CONST VGA = $A000;
  366.       MaxLines = 12;
  367.       Obj : Array [1..MaxLines,1..2,1..3] of integer =
  368.         (
  369.         ((-10,-10,-10),(10,-10,-10)),((-10,-10,-10),(-10,10,-10)),
  370.         ((-10,10,-10),(10,10,-10)),((10,-10,-10),(10,10,-10)),
  371.         ((-10,-10,10),(10,-10,10)),((-10,-10,10),(-10,10,10)),
  372.         ((-10,10,10),(10,10,10)),((10,-10,10),(10,10,10)),
  373.         ((-10,-10,10),(-10,-10,-10)),((-10,10,10),(-10,10,-10)),
  374.         ((10,10,10),(10,10,-10)),((10,-10,10),(10,-10,-10))
  375.         );  { The 3-D coordinates of our object ... stored as (X1,Y1,Z1), }
  376.             { (X2,Y2,Z2) ... for the two ends of a line }
  377.  
  378.  
  379. Type Point = Record
  380.                x,y,z:real;                { The data on every point we rotate}
  381.              END;
  382.      Virtual = Array [1..64000] of byte;  { The size of our Virtual Screen }
  383.      VirtPtr = ^Virtual;                  { Pointer to the virtual screen }
  384.  
  385.  
  386. VAR Lines : Array [1..MaxLines,1..2] of Point;  { The base object rotated }
  387.     Translated : Array [1..MaxLines,1..2] of Point; { The rotated object }
  388.     Xoff,Yoff,Zoff:Integer;               { Used for movement of the object }
  389.     lookup : Array [0..360,1..2] of real; { Our sin and cos lookup table }
  390.     Virscr : VirtPtr;                     { Our first Virtual screen }
  391.     Vaddr  : word;                        { The segment of our virtual screen}
  392.  
  393.  
  394. {──────────────────────────────────────────────────────────────────────────}
  395. Procedure SetMCGA;  { This procedure gets you into 320x200x256 mode. }
  396. BEGIN
  397.   asm
  398.      mov        ax,0013h
  399.      int        10h
  400.   end;
  401. END;
  402.  
  403.  
  404. {──────────────────────────────────────────────────────────────────────────}
  405. Procedure SetText;  { This procedure returns you to text mode.  }
  406. BEGIN
  407.   asm
  408.      mov        ax,0003h
  409.      int        10h
  410.   end;
  411. END;
  412.  
  413. {──────────────────────────────────────────────────────────────────────────}
  414. Procedure Cls (Where:word;Col : Byte);
  415.    { This clears the screen to the specified color }
  416. BEGIN
  417.      asm
  418.         push    es
  419.         mov     cx, 32000;
  420.         mov     es,[where]
  421.         xor     di,di
  422.         mov     al,[col]
  423.         mov     ah,al
  424.         rep     stosw
  425.         pop     es
  426.      End;
  427. END;
  428.  
  429. {──────────────────────────────────────────────────────────────────────────}
  430. Procedure SetUpVirtual;
  431.    { This sets up the memory needed for the virtual screen }
  432. BEGIN
  433.   GetMem (VirScr,64000);
  434.   vaddr := seg (virscr^);
  435. END;
  436.  
  437.  
  438. {──────────────────────────────────────────────────────────────────────────}
  439. Procedure ShutDown;
  440.    { This frees the memory used by the virtual screen }
  441. BEGIN
  442.   FreeMem (VirScr,64000);
  443. END;
  444.  
  445.  
  446. {──────────────────────────────────────────────────────────────────────────}
  447. procedure flip(source,dest:Word);
  448.   { This copies the entire screen at "source" to destination }
  449. begin
  450.   asm
  451.     push    ds
  452.     mov     ax, [Dest]
  453.     mov     es, ax
  454.     mov     ax, [Source]
  455.     mov     ds, ax
  456.     xor     si, si
  457.     xor     di, di
  458.     mov     cx, 32000
  459.     rep     movsw
  460.     pop     ds
  461.   end;
  462. end;
  463.  
  464.  
  465. {──────────────────────────────────────────────────────────────────────────}
  466. Procedure Pal(Col,R,G,B : Byte);
  467.   { This sets the Red, Green and Blue values of a certain color }
  468. Begin
  469.    asm
  470.       mov    dx,3c8h
  471.       mov    al,[col]
  472.       out    dx,al
  473.       inc    dx
  474.       mov    al,[r]
  475.       out    dx,al
  476.       mov    al,[g]
  477.       out    dx,al
  478.       mov    al,[b]
  479.       out    dx,al
  480.    end;
  481. End;
  482.  
  483.  
  484. {──────────────────────────────────────────────────────────────────────────}
  485. Function rad (theta : real) : real;
  486.   {  This calculates the degrees of an angle }
  487. BEGIN
  488.   rad := theta * pi / 180
  489. END;
  490.  
  491.  
  492. {──────────────────────────────────────────────────────────────────────────}
  493. Procedure SetUpPoints;
  494.   { This sets the basic offsets of the object, creates the lookup table and
  495.     moves the object from a constant to a variable }
  496. VAR loop1:integer;
  497. BEGIN
  498.   Xoff:=160;
  499.   Yoff:=100;
  500.   Zoff:=-256;
  501.   For loop1:=0 to 360 do BEGIN
  502.     lookup [loop1,1]:=sin (rad (loop1));
  503.     lookup [loop1,2]:=cos (rad (loop1));
  504.   END;
  505.   For loop1:=1 to MaxLines do BEGIN
  506.     Lines [loop1,1].x:=Obj [loop1,1,1];
  507.     Lines [loop1,1].y:=Obj [loop1,1,2];
  508.     Lines [loop1,1].z:=Obj [loop1,1,3];
  509.     Lines [loop1,2].x:=Obj [loop1,2,1];
  510.     Lines [loop1,2].y:=Obj [loop1,2,2];
  511.     Lines [loop1,2].z:=Obj [loop1,2,3];
  512.   END;
  513. END;
  514.  
  515.  
  516. {──────────────────────────────────────────────────────────────────────────}
  517. Procedure Putpixel (X,Y : Integer; Col : Byte; where:word);
  518.   { This puts a pixel on the screen by writing directly to memory. }
  519. BEGIN
  520.   Asm
  521.     mov     ax,[where]
  522.     mov     es,ax
  523.     mov     bx,[X]
  524.     mov     dx,[Y]
  525.     mov     di,bx
  526.     mov     bx, dx                  {; bx = dx}
  527.     shl     dx, 8
  528.     shl     bx, 6
  529.     add     dx, bx                  {; dx = dx + bx (ie y*320)}
  530.     add     di, dx                  {; finalise location}
  531.     mov     al, [Col]
  532.     stosb
  533.   End;
  534. END;
  535.  
  536.  
  537.  
  538. {──────────────────────────────────────────────────────────────────────────}
  539. Procedure Line(a,b,c,d:integer;col:byte;where:word);
  540.   { This draws a solid line from a,b to c,d in colour col }
  541.   function sgn(a:real):integer;
  542.   begin
  543.        if a>0 then sgn:=+1;
  544.        if a<0 then sgn:=-1;
  545.        if a=0 then sgn:=0;
  546.   end;
  547. var i,s,d1x,d1y,d2x,d2y,u,v,m,n:integer;
  548. begin
  549.      u:= c - a;
  550.      v:= d - b;
  551.      d1x:= SGN(u);
  552.      d1y:= SGN(v);
  553.      d2x:= SGN(u);
  554.      d2y:= 0;
  555.      m:= ABS(u);
  556.      n := ABS(v);
  557.      IF NOT (M>N) then
  558.      BEGIN
  559.           d2x := 0 ;
  560.           d2y := SGN(v);
  561.           m := ABS(v);
  562.           n := ABS(u);
  563.      END;
  564.      s := m shr 1;
  565.      FOR i := 0 TO m DO
  566.      BEGIN
  567.           putpixel(a,b,col,where);
  568.           s := s + n;
  569.           IF not (s<m) THEN
  570.           BEGIN
  571.                s := s - m;
  572.                a:= a + d1x;
  573.                b := b + d1y;
  574.           END
  575.           ELSE
  576.           BEGIN
  577.                a := a + d2x;
  578.                b := b + d2y;
  579.           END;
  580.      end;
  581. END;
  582.  
  583.  
  584. {──────────────────────────────────────────────────────────────────────────}
  585. Procedure DrawLogo;
  586.   { This draws 'ASPHYXIA' at the top of the screen in little balls }
  587. CONST ball : Array [1..5,1..5] of byte =
  588.          ((0,1,1,1,0),
  589.           (1,4,3,2,1),
  590.           (1,3,3,2,1),
  591.           (1,2,2,2,1),
  592.           (0,1,1,1,0));
  593.  
  594. VAR Logo : Array [1..5] of String;
  595.     loop1,loop2,loop3,loop4:integer;
  596. BEGIN
  597.   pal (13,0,63,0);
  598.   pal (1,0,0,40);
  599.   pal (2,0,0,45);
  600.   pal (3,0,0,50);
  601.   pal (4,0,0,60);
  602.   Logo[1]:=' O  OOO OOO O O O O O O OOO  O ';
  603.   Logo[2]:='O O O   O O O O O O O O  O  O O';
  604.   Logo[3]:='OOO OOO OOO OOO  O   O   O  OOO';
  605.   Logo[4]:='O O   O O   O O  O  O O  O  O O';
  606.   Logo[5]:='O O OOO O   O O  O  O O OOO O O';
  607.   For loop1:=1 to 5 do
  608.     For loop2:=1 to 31 do
  609.       if logo[loop1][loop2]='O' then
  610.         For loop3:=1 to 5 do
  611.           For loop4:=1 to 5 do
  612.             putpixel (loop2*10+loop3,loop1*4+loop4,ball[loop3,loop4],vaddr);
  613. END;
  614.  
  615.  
  616.  
  617. {──────────────────────────────────────────────────────────────────────────}
  618. Procedure RotatePoints (X,Y,Z:Integer);
  619.   { This rotates object lines by X,Y and Z; then places the result in
  620.     TRANSLATED }
  621. VAR loop1:integer;
  622.     temp:point;
  623. BEGIN
  624.   For loop1:=1 to maxlines do BEGIN
  625.     temp.x:=lines[loop1,1].x;
  626.     temp.y:=lookup[x,2]*lines[loop1,1].y - lookup[x,1]*lines[loop1,1].z;
  627.     temp.z:=lookup[x,1]*lines[loop1,1].y + lookup[x,2]*lines[loop1,1].z;
  628.  
  629.     translated[loop1,1]:=temp;
  630.  
  631.     If y>0 then BEGIN
  632.       temp.x:=lookup[y,2]*translated[loop1,1].x - lookup[y,1]*translated[loop1,1].y;
  633.       temp.y:=lookup[y,1]*translated[loop1,1].x + lookup[y,2]*translated[loop1,1].y;
  634.       temp.z:=translated[loop1,1].z;
  635.       translated[loop1,1]:=temp;
  636.     END;
  637.  
  638.     If z>0 then BEGIN
  639.       temp.x:=lookup[z,2]*translated[loop1,1].x + lookup[z,1]*translated[loop1,1].z;
  640.       temp.y:=translated[loop1,1].y;
  641.       temp.z:=-lookup[z,1]*translated[loop1,1].x + lookup[z,2]*translated[loop1,1].z;
  642.       translated[loop1,1]:=temp;
  643.     END;
  644.  
  645.     temp.x:=lines[loop1,2].x;
  646.     temp.y:=cos (rad(X))*lines[loop1,2].y - sin (rad(X))*lines[loop1,2].z;
  647.     temp.z:=sin (rad(X))*lines[loop1,2].y + cos (rad(X))*lines[loop1,2].z;
  648.  
  649.     translated[loop1,2]:=temp;
  650.  
  651.     If y>0 then BEGIN
  652.       temp.x:=cos (rad(Y))*translated[loop1,2].x - sin (rad(Y))*translated[loop1,2].y;
  653.       temp.y:=sin (rad(Y))*translated[loop1,2].x + cos (rad(Y))*translated[loop1,2].y;
  654.       temp.z:=translated[loop1,2].z;
  655.       translated[loop1,2]:=temp;
  656.     END;
  657.  
  658.     If z>0 then BEGIN
  659.       temp.x:=cos (rad(Z))*translated[loop1,2].x + sin (rad(Z))*translated[loop1,2].z;
  660.       temp.y:=translated[loop1,2].y;
  661.       temp.z:=-sin (rad(Z))*translated[loop1,2].x + cos (rad(Z))*translated[loop1,2].z;
  662.       translated[loop1,2]:=temp;
  663.     END;
  664.   END;
  665. END;
  666.  
  667.  
  668.  
  669. {──────────────────────────────────────────────────────────────────────────}
  670. Procedure DrawPoints;
  671.   { This draws the translated object to the virtual screen }
  672. VAR loop1:Integer;
  673.     nx,ny,nx2,ny2:integer;
  674.     temp:integer;
  675. BEGIN
  676.   For loop1:=1 to MaxLines do BEGIN
  677.     If (translated[loop1,1].z+zoff<0) and (translated[loop1,2].z+zoff<0) then BEGIN
  678.       temp:=round (translated[loop1,1].z+zoff);
  679.       nx :=round (256*translated[loop1,1].X) div temp+xoff;
  680.       ny :=round (256*translated[loop1,1].Y) div temp+yoff;
  681.       temp:=round (translated[loop1,2].z+zoff);
  682.       nx2:=round (256*translated[loop1,2].X) div temp+xoff;
  683.       ny2:=round (256*translated[loop1,2].Y) div temp+yoff;
  684.       If (NX > 0) and (NX < 320) and (NY > 25) and (NY < 200) and
  685.          (NX2> 0) and (NX2< 320) and (NY2> 25) and (NY2< 200) then
  686.            line (nx,ny,nx2,ny2,13,vaddr);
  687.     END;
  688.   END;
  689. END;
  690.  
  691. {──────────────────────────────────────────────────────────────────────────}
  692. Procedure ClearPoints;
  693.   { This clears the translated object from the virtual screen ... believe it
  694.     or not, this is faster then a straight "cls (vaddr,0)" }
  695. VAR loop1:Integer;
  696.     nx,ny,nx2,ny2:Integer;
  697.     temp:integer;
  698. BEGIN
  699.   For loop1:=1 to MaxLines do BEGIN
  700.     If (translated[loop1,1].z+zoff<0) and (translated[loop1,2].z+zoff<0) then BEGIN
  701.       temp:=round (translated[loop1,1].z+zoff);
  702.       nx :=round (256*translated[loop1,1].X) div temp+xoff;
  703.       ny :=round (256*translated[loop1,1].Y) div temp+yoff;
  704.       temp:=round (translated[loop1,2].z+zoff);
  705.       nx2:=round (256*translated[loop1,2].X) div temp+xoff;
  706.       ny2:=round (256*translated[loop1,2].Y) div temp+yoff;
  707.       If (NX > 0) and (NX < 320) and (NY > 25) and (NY < 200) and
  708.          (NX2> 0) and (NX2< 320) and (NY2> 25) and (NY2< 200) then
  709.            line (nx,ny,nx2,ny2,0,vaddr);
  710.     END;
  711.   END;
  712. END;
  713.  
  714.  
  715. {──────────────────────────────────────────────────────────────────────────}
  716. Procedure MoveAround;
  717.   { This is the main display procedure. Firstly it brings the object towards
  718.     the viewer by increasing the Zoff, then passes control to the user }
  719. VAR deg,loop1:integer;
  720.     ch:char;
  721. BEGIN
  722.   deg:=0;
  723.   ch:=#0;
  724.   Cls (vaddr,0);
  725.   DrawLogo;
  726.   For loop1:=-256 to -40 do BEGIN
  727.     zoff:=loop1*2;
  728.     RotatePoints (deg,deg,deg);
  729.     DrawPoints;
  730.     flip (vaddr,vga);
  731.     ClearPoints;
  732.     deg:=(deg+5) mod 360;
  733.   END;
  734.  
  735.   Repeat
  736.     if keypressed then BEGIN
  737.       ch:=upcase (Readkey);
  738.       Case ch of 'A' : zoff:=zoff+5;
  739.                  'Z' : zoff:=zoff-5;
  740.                  ',' : xoff:=xoff-5;
  741.                  '.' : xoff:=xoff+5;
  742.                  'S' : yoff:=yoff-5;
  743.                  'X' : yoff:=yoff+5;
  744.       END;
  745.     END;
  746.     DrawPoints;
  747.     flip (vaddr,vga);
  748.     ClearPoints;
  749.     RotatePoints (deg,deg,deg);
  750.     deg:=(deg+5) mod 360;
  751.   Until ch=#27;
  752. END;
  753.  
  754.  
  755. BEGIN
  756.   SetUpVirtual;
  757.   Writeln ('Greetings and salutations! Hope you had a great Christmas and New');
  758.   Writeln ('year! ;-) ... Anyway, this tutorial is on 3-D, so this is what is');
  759.   Writeln ('going to happen ... a wireframe square will come towards you.');
  760.   Writeln ('When it gets close, you get control. "A" and "Z" control the Z');
  761.   Writeln ('movement, "," and "." control the X movement, and "S" and "X"');
  762.   Writeln ('control the Y movement. I have not included rotation control, but');
  763.   Writeln ('it should be easy enough to put in yourself ... if you have any');
  764.   Writeln ('hassles, leave me mail.');
  765.   Writeln;
  766.   Writeln ('Read the main text file for ideas on improving this code ... and');
  767.   Writeln ('welcome to the world of 3-D!');
  768.   writeln;
  769.   writeln;
  770.   Write ('  Hit any key to contine ...');
  771.   Readkey;
  772.   SetMCGA;
  773.   SetUpPoints;
  774.   MoveAround;
  775.   SetText;
  776.   ShutDown;
  777.   Writeln ('All done. This concludes the eigth sample program in the ASPHYXIA');
  778.   Writeln ('Training series. You may reach DENTHOR under the names of GRANT');
  779.   Writeln ('SMITH/DENTHOR/ASPHYXIA on the ASPHYXIA BBS. I am also an avid');
  780.   Writeln ('Connectix BBS user, and occasionally read RSAProg.');
  781.   Writeln ('For discussion purposes, I am also the moderator of the Programming');
  782.   Writeln ('newsgroup on the For Your Eyes Only BBS.');
  783.   Writeln ('The numbers are available in the main text. You may also write to me at:');
  784.   Writeln ('             Grant Smith');
  785.   Writeln ('             P.O. Box 270');
  786.   Writeln ('             Kloof');
  787.   Writeln ('             3640');
  788.   Writeln ('I hope to hear from you soon!');
  789.   Writeln; Writeln;
  790.   Write   ('Hit any key to exit ...');
  791.   Readkey;
  792. END.
  793.