OSDN Git Service

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