

|
Volume Number: | 4 | |
Issue Number: | 6 | |
Column Tag: | Forth Forum |
Graf3D Library Access
By Jörg Langowski, MacTutor Editorial Board
Graf3D and Mach2 - accessing external libraries
The subject of this month has been initiated by one of our readers, Paul Thomas, who in a desperate mood sent me both a paper letter and GEnie mail, because he needed to know whether it would be possible at all to access the Graf3D routines from Mach2.
A little background for those who weren’t with the Mac (or with us, for that matter) from the beginning: Graf3D is a set of routines on top of QuickDraw, which enable to create a 3-d GrafPort with all necessary support to do 3-dimensional perspective drawing. The routines aren’t explained in Inside Macintosh, but in several other different documents, one of them being the MPW Pascal manual (Appendix J).
Graf3D was also part of the Lisa Workshop, and one of the first Macintosh demo programs, Boxes, used those routines. The Boxes program - many of you might have seen it - draws at random fifteen rectangular boxes on a plane and displays them in a 3-d perspective view. The program is written originally in Lisa Pascal, and also contained on the TML Pascal disk as an example. Listing 4 shows the original program.
Glancing through the code, you might notice that this example certainly wouldn’t please the User Interface Thought Police today. Using the whole screen as a GrafPort without regard to anything else on there is a definite no-no in the days of Multifinder, and if you run the example under Multifinder, you won’t find your windows back on the screen afterwards because they are all covered by little boxes. Nevertheless, it is a nice example for the use of Graf3D, and we’ll show you this month how to translate it into Mach2 (We’ll also correct the garbled screen problem under Multifinder).
First and most difficult question: How can we access routines from Forth that are clearly labeled (Not in ROM) in the documentation? This remark tells us, of course, that they are part of some object library, no source code available and inaccessible to the Forth user. Just like the printing manager was before it was implemented in the Mach2 and MacForth systems.
There may be other libraries to come, also not in ROM, and waiting for updates takes a long time. So I’ll give you two general strategies to get access to object library code from Forth, making you independent of those updates.
The first scheme I thought up was simply to write a short Pascal program that calls all the routines at least once - so that they will be loaded -, compile that program, create a linker map if possible, and pass the final application through Nosy. That will get you a more or less annotated assembly listing from which you can extract the source code of the not in ROM routines that you are interested in.
Well, that works for you and me on our respective desks, but we certainly cannot publish reverse-engineered Apple source code in MacTutor, at least if we want to stay in business. Also, it is a lot of work (i.e. translating to a different assembler dialect, writing the glue code, etc.), and all in all qualifies as a master example of a dirty hack.
There is an easier way to go, and much more general, too: write some sort of a linker for Pascal library code. We write an assembly main program (Listing 2), which is just a table of JMP instructions to the routines that we want to call, and let the MPW linker do its job (listing 3).
The assembly/link script will create a new resource, gr3D, which contains the jump instructions at the beginning, followed by the linked Graf3D code. All we have to do now is to provide a block of memory in our Forth code where this resource can be copied to.
In order to assign Forth words to the JMP instructions, we simply make a table of CREATEs, which will allocate 4 bytes to each name (the length of a JMP d(PC) instruction), and create a dictionary entry so that we know the routine’s address in the jump table. After the block of CREATEs, we allocate sufficient memory so that the gr3D resource can be loaded (see listing 1).
We write a word, Init3D, that gets the gr3D resource and copies it into the table, starting with the address of the first Graf3d word, gInitGrf3D. Now the Graf3D routines are all set up for use by Mach2. As simple as that.
Well, not quite. We still need to write glue code to move the parameters from the Forth stack (A6) to the Pascal stack (A7). That glue code is rather simple and is just the same as is used for calling toolbox ROM routines. Like the traps, the Graf3D code preserves all registers that are vital to Mach2 (A2-A7, D4-D7). Like in the case of traps, we have to make A7 point to the lowest stack (EXG D4,A7) in order to avoid overwriting of Mach2 space by the intermediate Quickdraw record that is put between the stack and the heap. The glue code for the different Graf3D routines is given at the beginning of listing 1.
The Boxes examples should be self-explanatory when you compare it with the Pascal code; in fact, it might help some of you Pascal programmers and casual Forth readers appreciate that there is really no big difference between Forth and Pascal code...
I should point out some more things. First, the ‘access words’ for fields in a record, .x, .y, .pt1, and so on, defined at the beginning of the Forth code. Second, the word restore.screen, which cleans up the display after the example has run; very important when running under Multifinder. It basically does a PaintBehind(FrontWindow, grayRgn), the same mechanism by which many screen savers restore a blacked-out screen. Third, we define a terminal task that runs the Boxes example; that way, we can simply check for ?terminal after each drawing loop to see whether we’re done. Again, we have to make sure (like always in Mach2 multitasking) that the correct GrafPort is set by calling myPort3D SetPort3D before each passage through the loop. This is because any PAUSE-containing word, such as ?terminal, may change the active GrafPort.
For using the example, you will first have to create the Graf3Dglue resource file with the gr3D resource in it, then move that resource into the MACH.RSRC file before starting your Mach2 system. In case you don’t have access to MPW, the source code disk contains the Graf3Dglue and MACH.RSRC files. The complete program, of course, is also on the disk.
That’s it for this month; next month I plan to discuss various projects of object-oriented extensions to Mach2 that have recently appeared on the networks. Some of you might also appreciate that NEON hasn’t died altogether, but that there is at least one person trying to extend it in a very interesting way.
Listing 1: The Boxes example in Mach2 \ Graf3d / Mach2 glue code \ An example for calling MPW routines from Forth \ J. Langowski April 1988 \ __________________________________________ Only Forth Also Mac Also Assembler \ some general definitions first 16 CONSTANT portRect GLOBAL CODE SCALE MOVE.L (A6)+,D0 BMI.S @1 MOVE.L (A6),D1 ASL.L D0,D1 MOVE.L D1,(A6) RTS @1 MOVE.L (A6),D1 NEG.L D0 ASR.L D0,D1 MOVE.L D1,(A6) RTS END-CODE global CODE white MOVE.L (A5),-(A6) SUBQ.L #8,(A6) RTS END-CODE MACH global CODE black MOVE.L (A5),-(A6) SUBI.L #16,(A6) RTS END-CODE MACH global CODE gray MOVE.L (A5),-(A6) SUBI.L #24,(A6) RTS END-CODE MACH : 4ASCII 0 4 0 DO 8 SCALE 0 WORD 1+ C@ + LOOP ; 4ASCII gr3D CONSTANT “gr3D \ resource ID \ Graf3D jump table CREATE gInitGrf3D CREATE gOpen3DPort CREATE gSetPort3D CREATE gGetPort3D CREATE gMoveTo2D CREATE gMoveTo3D CREATE gLineTo2D CREATE gLineTo3D CREATE gMove2D CREATE gMove3D CREATE gLine2D CREATE gLine3D CREATE gViewPort CREATE gLookAt CREATE gViewAngle CREATE gIdentity CREATE gScale CREATE gTranslate CREATE gPitch CREATE gYaw CREATE gRoll CREATE gSkew CREATE gTransform CREATE gClip3D CREATE gSetPt3D CREATE gSetPt2D \ The glue code is 2636 bytes long, so we allocate \ sufficient additional buffer space to put it into 2600 ALLOT : Init3D \ gets Graf3D code from gr3D=1 resource \ and copies it into buffer “gr3D 1 call GetResource dup @ swap call SizeRsrc [‘] gInitGrf3D swap cmove ; CODE InitGrf3d ( globalPtr -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gInitGrf3d EXG D4,A7 RTS END-CODE CODE Open3DPort ( port -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gOpen3DPort EXG D4,A7 RTS END-CODE CODE SetPort3d ( port -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gSetPort3d EXG D4,A7 RTS END-CODE CODE GetPort3d ( VAR port -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gGetPort3d EXG D4,A7 RTS END-CODE CODE MoveTo2d ( x y -- ) EXG D4,A7 MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #8,A6 JSR gMoveTo2d EXG D4,A7 RTS END-CODE CODE MoveTo3d ( x y z -- ) EXG D4,A7 MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #12,A6 JSR gMoveTo3d EXG D4,A7 RTS END-CODE CODE LineTo2d ( x y -- ) EXG D4,A7 MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #8,A6 JSR gLineTo2d EXG D4,A7 RTS END-CODE CODE LineTo3d ( x y z -- ) EXG D4,A7 MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #12,A6 JSR gLineTo3d EXG D4,A7 RTS END-CODE CODE Move2d ( dx dy -- ) EXG D4,A7 MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #8,A6 JSR gMove2d EXG D4,A7 RTS END-CODE CODE Move3d ( dx dy dz -- ) EXG D4,A7 MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #12,A6 JSR gMove3d EXG D4,A7 RTS END-CODE CODE Line2d ( x y -- ) EXG D4,A7 MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #8,A6 JSR gLine2d EXG D4,A7 RTS END-CODE CODE Line3d ( x y z -- ) EXG D4,A7 MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #12,A6 JSR gLine3d EXG D4,A7 RTS END-CODE CODE ViewPort ( r -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gViewPort EXG D4,A7 RTS END-CODE CODE LookAt ( left top right bottom -- ) EXG D4,A7 MOVE.L 12(A6),-(A7) MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #16,A6 JSR gLookAt EXG D4,A7 RTS END-CODE CODE ViewAngle ( angle -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gViewAngle EXG D4,A7 RTS END-CODE CODE Identity EXG D4,A7 JSR gIdentity EXG D4,A7 RTS END-CODE CODE Scal ( xfactor yfactor zfactor -- ) EXG D4,A7 MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #12,A6 JSR gScale EXG D4,A7 RTS END-CODE CODE Translate ( dx dy dz -- ) EXG D4,A7 MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #12,A6 JSR gTranslate EXG D4,A7 RTS END-CODE CODE Pitch( xangle -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gPitch EXG D4,A7 RTS END-CODE CODE Yaw( yangle -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gYaw EXG D4,A7 RTS END-CODE CODE Rol( zangle -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gRoll EXG D4,A7 RTS END-CODE CODE Skew ( zangle -- ) EXG D4,A7 MOVE.L (A6)+,-(A7) JSR gSkew EXG D4,A7 RTS END-CODE CODE Transform ( src dst -- ) EXG D4,A7 MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #8,A6 JSR gTransform EXG D4,A7 RTS END-CODE CODE Clip3D ( src1 src2 dst1 dst2 -- flag ) EXG D4,A7 CLR.W -(A7) MOVE.L 12(A6),-(A7) MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #16,A6 JSR gClip3D MOVE.W (A7)+,D0 EXT.L D0 MOVE.L D0,-(A6) EXG D4,A7 RTS END-CODE CODE SetPt3d( pt3D x y z -- ) EXG D4,A7 MOVE.L 12(A6),-(A7) MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #16,A6 JSR gSetPt3D EXG D4,A7 RTS END-CODE CODE SetPt2d( pt2D x y -- ) EXG D4,A7 MOVE.L 8(A6),-(A7) MOVE.L 4(A6),-(A7) MOVE.L (A6),-(A7) ADDA.W #12,A6 JSR gSetPt2D EXG D4,A7 RTS END-CODE \ Translation of Boxes.pas example into Forth follows \ _________________________________________ : .x ; mach : .y 4 + ; mach : .z 8 + ; mach : .pt1 ; mach : .pt2 12 + ; mach 15 CONSTANT BoxCount Variable MyPort 104 vallot \ grafPort is 108 bytes long Variable MyPort3D 150 vallot \ graf3DPort is 154 bytes long Variable boxArray 24 BoxCount * vallot \ BoxCount * 2* point3D @ 3 long words Variable nboxes \ # of boxes made Variable MyBox 20 vallot \ 24 bytes for 2* point3d Variable p1 8 vallot \ point3d Variable p2 8 vallot \ point3d Variable myRect 4 vallot \ Rect Variable testRect 4 vallot \ Rect : DrawBrick { pt1 pt2 | tempRgn -- } call NewRgn -> tempRgn call OpenRgn pt1 .x @ pt1 .y @ pt1 .z @ MoveTo3D pt1 .x @ pt1 .y @ pt2 .z @ LineTo3D pt2 .x @ pt1 .y @ pt2 .z @ LineTo3D pt2 .x @ pt1 .y @ pt1 .z @ LineTo3D pt1 .x @ pt1 .y @ pt1 .z @ LineTo3D tempRgn call CloseRgn tempRgn white call FillRgn call OpenRgn pt1 .x @ pt1 .y @ pt2 .z @ MoveTo3D pt1 .x @ pt2 .y @ pt2 .z @ LineTo3D pt2 .x @ pt2 .y @ pt2 .z @ LineTo3D pt2 .x @ pt1 .y @ pt2 .z @ LineTo3D pt1 .x @ pt1 .y @ pt2 .z @ LineTo3D tempRgn call CloseRgn tempRgn gray call FillRgn call OpenRgn pt2 .x @ pt1 .y @ pt1 .z @ MoveTo3D pt2 .x @ pt1 .y @ pt2 .z @ LineTo3D pt2 .x @ pt2 .y @ pt2 .z @ LineTo3D pt2 .x @ pt2 .y @ pt1 .z @ LineTo3D pt2 .x @ pt1 .y @ pt1 .z @ LineTo3D tempRgn call CloseRgn tempRgn black call FillRgn white call penpat pt2 .x @ pt2 .y @ pt2 .z @ MoveTo3D pt2 .x @ pt2 .y @ pt1 .z @ LineTo3D pt2 .x @ pt1 .y @ pt1 .z @ LineTo3D call pennormal tempRgn call DisposRgn ; : hi -16 scale ; : chkBox { | box -- } 1 ( flag ) nBoxes @ 0 DO boxArray i 24 * + -> box testRect box .pt1 .x @ hi box .pt1 .y @ hi box .pt2 .x @ hi box .pt2 .y @ hi call SetRect testRect -1 -1 call InSetRect myRect testRect testRect call SectRect IF drop 0 leave THEN LOOP ; : MakeBox { | p1x p1y p1z p2x p2y p2z box ii -- } call random 70 mod 15 - 1 call FixRatio -> p1x call random 70 mod 10 - 1 call FixRatio -> p1y 0 -> p1z call random 30 mod abs 10 + 1 call FixRatio p1x + -> p2x call random 45 mod abs 10 + 1 call FixRatio p1y + -> p2y call random 30 mod abs 10 + 1 call FixRatio p1z + -> p2z myRect p1x hi p1y hi p2x hi p2y hi call SetRect chkBox IF p1x myBox .pt1 .x ! p1y myBox .pt1 .y ! p1z myBox .pt1 .z ! p2x myBox .pt2 .x ! p2y myBox .pt2 .y ! p2z myBox .pt2 .z ! 0 -> ii myBox boxArray nBoxes @ 24 * + 24 cmove BEGIN myBox .pt1 .y @ boxArray ii + .pt2 .y @ > myBox .pt2 .y @ boxArray ii + .pt1 .y @ > and myBox .pt1 .x @ boxArray ii + .pt2 .x @ < myBox .pt2 .x @ boxArray ii + .pt1 .x @ < and or WHILE 24 +> ii REPEAT ii 24 / -> ii ii 1+ nBoxes @ DO boxArray i 1- 24 * + boxArray i 24 * + 24 cmove -1 +loop myBox boxArray ii 24 * + 24 cmove 1 nBoxes +! THEN ; : drawGrid 11 -10 DO i 10 * 1 call FixRatio -100 1 call FixRatio 0 MoveTo3D i 10 * 1 call FixRatio 100 1 call FixRatio 0 LineTo3D LOOP 11 -10 DO -100 1 call FixRatio i 10 * 1 call FixRatio 0 MoveTo3D 100 1 call FixRatio i 10 * 1 call FixRatio 0 LineTo3D LOOP ; : restore.screen call drawmenubar call frontwindow grayrgn @ call paintbehind call showcursor ; : main init3d call hidecursor myPort call OpenPort myPort3D Open3DPort myPort portRect + ViewPort -100 1 call FixRatio 75 1 call FixRatio 100 1 call FixRatio -75 1 call FixRatio LookAt 30 1 call FixRatio ViewAngle Identity 20 1 call FixRatio Rol 70 1 call FixRatio Pitch BEGIN myPort3D SetPort3D 0 nBoxes ! BEGIN makeBox nBoxes @ boxCount = UNTIL white call penPat black call backPat myPort portRect + call EraseRect drawGrid 0 nBoxes @ 1- DO boxArray i 24 * + dup .pt1 swap .pt2 DrawBrick -1 +LOOP ?terminal UNTIL restore.screen bye ; NEW.WINDOW Boxes “ Boxes” Boxes TITLE 0 0 20 20 Boxes BOUNDS Plain Visible NoCloseBox NoGrowBox Boxes ITEMS 600 5000 terminal Box : go.box activate main ; : start Boxes add Boxes Box build Box go.Box ; .( To create a turkey application ) cr .( type TURNKEY START BOXES ) Listing 2: MPW Assembly glue code for accessing the Graf3D Pascal library TITLE ‘Graf3D glue code for Mach2’ BLANKS OFF PRINT OFF INCLUDE ‘Traps.a’ INCLUDE ‘ToolEqu.a’ INCLUDE ‘QuickEqu.a’ INCLUDE ‘SysEqu.a’ INCLUDE ‘Graf3DEqu.a’ PRINT ON GrafGlueMAIN JMP InitGrf3D JMP Open3DPort JMP SetPort3D JMP GetPort3D JMP MoveTo2D JMP MoveTo3D JMP LineTo2D JMP LineTo3D JMP Move2D JMP Move3D JMP Line2D JMP Line3D JMP ViewPort JMP LookAt JMP ViewAngle JMP Identity JMP Scale JMP Translate JMP Pitch JMP Yaw JMP Roll JMP Skew JMP Transform JMP Clip3D JMP SetPt3D JMP SetPt2D END Listing 3: MPW assembly / link script for generating the gr3D resource (to be put into the MACH.RSRC file) Asm Graf3dglue.a -l Link Graf3dglue.a.o {Libraries}Interface.o -rt gr3D=1 -o Graf3dglue -l > Graf3dglue.map Listing 4: The original Boxes example (from TML Pascal) PROGRAM Boxes; { Boxes demonstrates the use of the Three Dimensional Quickdraw package Graf3D. Pascal source: Boxes.Pas Resources: *none* This program is from Apple’s Lisa Software Supplement, modified for TML Systems Pascal compiler. } USES MacIntf, FixMath, Graf3D; CONST boxCount = 15; keyOrMouse = 10; TYPE Box3D = RECORD pt1: Point3D; pt2: Point3D; END; VARmyPort: GrafPort; myPort3D: Port3D; boxArray: ARRAY [0..boxCount] OF Box3D; nBoxes: INTEGER; i: INTEGER; dummy: EventRecord; PROCEDURE DrawBrick(pt1, pt2: Point3D); { draws a 3D brick with shaded faces. only shades correctly in one direction. } VARtempRgn: RgnHandle; BEGIN tempRgn := NewRgn; OpenRgn; MoveTo3D(pt1.X, pt1.Y, pt1.Z); { front face, y=y1 } LineTo3D(pt1.X, pt1.Y, pt2.Z); LineTo3D(pt2.X, pt1.Y, pt2.Z); LineTo3D(pt2.X, pt1.Y, pt1.Z); LineTo3D(pt1.X, pt1.Y, pt1.Z); CloseRgn(tempRgn); FillRgn(tempRgn, white); OpenRgn; MoveTo3D(pt1.X, pt1.Y, pt2.Z); { top face, z=z2 } LineTo3D(pt1.X, pt2.Y, pt2.Z); LineTo3D(pt2.X, pt2.Y, pt2.Z); LineTo3D(pt2.X, pt1.Y, pt2.Z); LineTo3D(pt1.X, pt1.Y, pt2.Z); CloseRgn(tempRgn); FillRgn(tempRgn, gray); OpenRgn; MoveTo3D(pt2.X, pt1.Y, pt1.Z); { right face, x=x2 } LineTo3D(pt2.X, pt1.Y, pt2.Z); LineTo3D(pt2.X, pt2.Y, pt2.Z); LineTo3D(pt2.X, pt2.Y, pt1.Z); LineTo3D(pt2.X, pt1.Y, pt1.Z); CloseRgn(tempRgn); FillRgn(tempRgn, black); PenPat(white); MoveTo3D(pt2.X, pt2.Y, pt2.Z); { outline right } LineTo3D(pt2.X, pt2.Y, pt1.Z); LineTo3D(pt2.X, pt1.Y, pt1.Z); PenNormal; DisposeRgn(tempRgn); END; PROCEDURE MakeBox; LABEL 1; VARmyBox: Box3D; i, j, h, v: INTEGER; p1, p2: Point3D; myRect: Rect; testRect: Rect; BEGIN p1.x := FixRatio((Random mod 70 - 15), 1); p1.y := FixRatio((Random mod 70 - 10), 1); p1.z := 0; p2.x := p1.x + FixRatio((10 + ABS(Random) MOD 30), 1); p2.y := p1.y + FixRatio((10 + ABS(Random) MOD 45), 1); p2.z := p1.z + FixRatio((10 + ABS(Random) MOD 35), 1); { reject box if it intersects one already in list } SetRect(myRect, HiWord(p1.x), HiWord(p1.y), HiWord(p2.x), HiWord(p2.y)); FOR i := 0 TO nBoxes - 1 DO BEGIN WITH boxArray[i] DO SetRect(testRect, HiWord(pt1.x), HiWord(pt1.y), HiWord(pt2.x), HiWord(pt2.y)); InSetRect(testRect, -1, -1); IF SectRect(myRect, testRect, testRect) THEN goto 1 END; myBox.pt1 := p1; myBox.pt2 := p2; { calc midpoint of box and its distance from the eye } i := 0; boxArray[nBoxes].pt1 := myBox.pt1; { sentinel } boxArray[nBoxes].pt2 := myBox.pt2; WHILE ((myBox.pt1.y > boxArray[i].pt2.y) AND (myBox.pt2.y > boxArray[i].pt1.y)) OR ((myBox.pt1.x < boxArray[i].pt2.x) AND (myBox.pt2.x < boxArray[i].pt1.x)) DO inc(i); { insert in order of dist } FOR j := nBoxes DOWNTO i + 1 DO boxArray[j] := boxArray[j - 1]; boxArray[i] := myBox; inc(nBoxes); 1: END; BEGIN { main program } FlushEvents(everyEvent, 0); InitGraf(@thePort); InitCursor; HideCursor; OpenPort(@myPort); Open3DPort(@myPort3D); { put the image in this rect } ViewPort(myPort.portRect); { aim the camera into 3D space } LookAt(FixRatio(-100, 1), FixRatio(75, 1), FixRatio(100, 1), FixRatio(-75, 1)); { choose lens focal length } ViewAngle(FixRatio(30, 1)); Identity; Roll(FixRatio(20, 1)); Pitch(FixRatio(70, 1)); { roll and pitch the plane } REPEAT nBoxes := 0; REPEAT MakeBox UNTIL nBoxes=boxCount; PenPat(white); BackPat(black); EraseRect(myPort.portRect); FOR i := -10 TO 10 DO BEGIN MoveTo3D(FixRatio(i*10, 1), FixRatio(-100, 1), 0); LineTo3D(FixRatio(i*10, 1), FixRatio(100, 1), 0); END; FOR i := -10 TO 10 DO BEGIN MoveTo3D(FixRatio(-100, 1), FixRatio(i*10, 1), 0); LineTo3D(FixRatio(100, 1), FixRatio(i*10, 1), 0); END; FOR i := nBoxes-1 DOWNTO 0 DO DrawBrick(boxArray[i].pt1,boxArray[i].pt2); UNTIL OSEventAvail(keyOrMouse, dummy); END.

- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine