X-NEWS: ids rec.games.programmer: 2889 Path: paperboy.ids.net!uunet!spool.mu.edu!sol.ctr.columbia.edu!news.kei.com!ub!toz!news From: cyberman@toz.buffalo.ny.us Newsgroups: rec.games.programmer Subject: RGP FAQ Message-ID: Date: Tue, 09 Nov 93 22:24:41 EST Organization: TOZ automated posting Lines: 1076 Revision 0.0.5 of REC.GAMES.FAQ This document will be made available by mail request at a later date (mail server difficulties). You may attempt to see if this mail server works by sending to: aser@toz.buffalo.ny.us I cannot be sure it's working yet, please leave feedback on what state it is in. There are currently 3 FAQ's RGP.FAQ - rec.games.programmer General FAQ RGP.IBM - rec.games.ibm relevant FAQ MAZE.FAQ - FAQ about MAZE's (more of a compilation of articles) This was done so that someone who is looking for more general information will be able to find game programing information without being inundated by IBM, other computer-SPECIFIC information, or the topsy turvey world of maze generation. The current Editor is Cyberman@toz.buffalo.ny.us -------------------------------------------------------------------------------- REC.GAMES.PROGRAMMER General FAQ Contents -------------------------------------------------------------------------------- LEGAL This document is FREE! It is for your self enlightenment. No warranty is provided nor implied with this information. The accuracy of the information contained herein is subject to conjecture. Therefore the editor and contributers will take NO liability for improper use, misuse, or abuse of this information, and any damage to ANYTHING or ANYONE whether physical, financial, etc. in no way, shape, or form can be attributed to this document. Use this information at your OWN risk. The above statement is to keep those who contributed and the editor free from personal injury for being nice. Special thanks to: andrean@cs.utexas.edu (Andre A. Nurwono) roberts@brahms.amd.com (Dave Roberts) sean@stat.tamu.edu (Sean Barrett) Cyberman@toz.buffalo.ny.us (Cyberman) (and anyone else I forgot to mention for that matter) If you wish to contribute CODE be sure it's something you DO NOT wish to COPYRIGHT! All contributions and suggestions should be sent to me at: Cyberman@toz.buffalo.ny.us place in subject header RGP.FAQ.* where * is any of the following SUGGESTION - to suggest Adding an area (I recommend that you send the information if you want it added) ADD - an addition (ie TBA's [see KEY for what TBA means]) EDIT - for a correction somewhere (it is suggested contributers do there own editing) FLAME - complaints - I'll read these but be sure to be tasteful in you commentary. You may or may NOT get a reply. For now I'm going to use my personal mail account. So please be careful. Format: There are several categories and each category contains it's relevant questions as suggested. Key: TBA - To Be Added [Frequently Asked Questions about terminology] Q1.1 What are .MOD files? Q1.2 What is a pixel? Q1.3 What is a bitmap? Q1.4 What is flicker? Q1.5 What is snow? Q1.6 What is a frame buffer? Q1.7 What is a Sprite? Q1.8 What is vertical/horizontal retrace? [Frequently Asked Questions about Animation] Q2.1 How do I make sprites on my xxx? Q2.2 What about collision detection? TBA Q2.3 What about bitmap scaling? TBA Q2.4 What is perspective mapping? TBA Q2.5 What about 3d objects and manipulation? [Frequently Asked Questions about Map Generation] Q3.1 How do I generate a maze? Q3.2 How do I generate a landscape/terrain? TBA Q3.3 How do I generate a hex map? TBA Q3.4 What about random number generators? [Frequently Asked Questions about Libraries and support software] Q4.1 What is there for the IBM compatible? Q4.2 " " " " " Amiga? TBA Q4.3 " " " " " Atari? TBA Q4.4 " " " " " Macintosh? TBA Q4.5 " " " " " Sun/Sparc? Q4.6 " " " " " X? TBA [Frequently Asked Question about Where is ...] Q5.1 Where is Joshua Jensens famous Perspective Mapping Code? Q5.2 Where else should I read? Q5.3 Where can I find out about ray traceing? Note 1: Fixing snow and flicker Note 2: 2 Part Tutorial on 3d graphics -------------------------------------------------------------------------------- REC.GAMES.PROGRAMMER General FAQ Terminology -------------------------------------------------------------------------------- Q1.1 What are .MOD files? A .MOD file is a file generated for the Amiga. A tracker is used to play the MOD. A mod consists of digitized sound and sequencing information to play music. Here is a BRIEF and incomplete text file that is somewhat informative on the subject. Note - Joshua Jensen has written a mod player that can be linked into your code. So has the author of mod play. SEE ALSO - Mod Format specification file. (Modform.zoo) Q1.2 What is a Pixel? Pixel is short for Picture Element. It is a single dot address on a grid used to display images on a monitor (Tv included). Q1.3 What is a bitmap? A bitmap aka bitimage is the representation of an image in the form of a sequence of bits. For example most graphic modes on computers represent the pixels using a BITMAP. Q1.4 What is flicker? Flicker is a noticeable pulse or change in an image. This can be on a CRT (Cathode Ray Tube) or other type display device. The cause is that the refresh rate of the CRT is lower than the persistance of vision of the person observing it. Another form of flicker is due to rapid drawing of data on the screen. What happens is that the data is drawn out of syncronization with the horizontal and vertical scan. So your image apears to fade in and out of visability. SEE ALSO fixing snow and Flicker Q1.5 What is SNOW? Snow are small specals on the screen that appear like SNOW falling on the screen (hence the name). Snow is caused by a number of things. One of which is when one is writting to the screen while the display card is scanning that address of the screen, this causes a conflict and can produce random specals. Another cause of snow is an improperly connected monitor. This can damage the monitor. Yet another source is noise from an electro magnetic source, if indeed it's not from anything you own the FCC might need to be notified. SEE ALSO fixing snow and Flicker Q1.6 What is a frame buffer? Memory that contains image data that is translated to the image on your screen. A frame buffer does not have to be "visible". Also the methode of this tranformation is not always the same, so no effort here will be made to explain further. Q1.7 What is a Sprite? A sprite is an image usually moveable about the screen. Common information stored in a sprite are, width, height, position, and image data. A sprite ussually does not effect the screen background, this is of course dependant on implementation. Q1.8 What is vertical/horizontal retrace? Most monitors are what is called a RASTER display. This means that an image is represented by setting the intensity of a grid of dots on the display. Each dot is called a pixel. In order to display the grid a CRT sweeps an electron beam across the display surface sending pulses corresponding to the intensity of the pixel. However the stream of video information is almost always represented left to right top to bottom. This mean that the beam must scan across the screen and then move BACK to start a line. This is called Horizontal retrace. Vertical retrace is when the beam finishes painting the screen from top to bottom again the beam must be moved to the top of the screen. The horizontal sweep is controlled by an electric field the vertical sweep is controlled usually magnetic field. -------------------------------------------------------------------------------- REC.GAMES.PROGRAMMER General FAQ Animation -------------------------------------------------------------------------------- [Frequently Asked Questions about Animation] Q2.1 How do I make sprites on my xxx? This, unfortunately, is a rather complex problem and its implementation is often computer-specific, however some of it can be addressed in the general FAQ. Sprites require one to copy pixels to the display in a certain area of the screen. There are various issues that must be addressed about this. Getting and Putting an image: getting means you copy a section of a screen (bitmap) to a buffer, putting means you dump a buffer of data (bitmap) to a screen location. These are often used for less sophisticated sprite animation. The problem with this technique is put destroys the background information. So if you want a background at all, there must be a way to "overlay" an image onto it without causing a large area around the sprite to disappear. An old way of "fixing" this is to copy the background of an image before you destroy it, then put the background back after you move it. Another methode of sprite creation requires custome hardware (Amiga C64 Atari 8bits). Q2.2 What about collision detection? TBA Q2.3 What about bitmap scaling? TBA Q2.4 What is perspective mapping? What is it? Basically it takes an image and "pastes" it in 3d perspective onto a surface. This is much faster than rendering surfaces in real time. Article 7716 of r.g.p written by Joshua C. Jensen (sl8nl@cc.usu.edu) is an example of this technique (implemented in Turbo Pascal). Unfortunately this article # may not corespond to anything in usenet! :) However the date it was posted was 1 Aug 92 01:36:51 GMT. SO you can grab the coresponding articles there! :) Q2.5 What about 3d objects and manipulation? Note 2 is a pair of tutorials on this subject. Another suggestion is to check comp.graphics FAQ and the comp.graphics resources guide as well. -------------------------------------------------------------------------------- REC.GAMES.PROGRAMMER General FAQ Map Generation -------------------------------------------------------------------------------- Q3.1 How do I generate a maze? This is a VERY frequently asked question in rgp so we have a unoffical maze FAQ. It's is now posted WITH the RGP Faq. Q3.2 How do I generate a landscape/terrain? Another fun filled FAQ it has too many answers. A suggestion is to read sci.fractals. Now if someone would conribute some information on this I would be happy to construct the "land.faq". Q3.3 How do I generate/use a hex map? Well this has discussed considerably on rgp. We haven't a FAQ for it until I get permission to use something I captured. Q3.4 What about random number generators? These are mandatory for making a "new" universe each time a program loads. Usually the ones included in a C compiler library are sufficient for most needs. However sometimes one must go the extra mile and use there own random number generator. Currently we have no FAQ on random number generators. (sorry) -------------------------------------------------------------------------------- REC.GAMES.PROGRAMMER General FAQ Libraries and support software -------------------------------------------------------------------------------- Q4.1 What is there for the IBM and Compatibles? There is a lot. I suggest scrounging around wuarchive personally, but here is a terse UNOFFICIAL list! contributed by andrean@cs.utexas.edu (Andre A. Nurwono) ========== -------- Graphics -------- *. Executable File(s) : TWEAK06.ZIP Who : Robert Schmidt (robert@solan.unit.no) When : 1992 What : A program to experiment w/ VGA registers, very useful if you want to define new modes (like mode X & 360x400 modes) Where : simtel & mirrors *. Executable, (source code) File : SPRITE.ZIP Who : Billy Dalrymple When : 1989 What : A sprite editor, produces sprites w/ mask in files. info available for $10 source code available for $25 Where : I forget *. Executable, Source Code File(s) : VGA.ZIP Who : wardt@a.cs.okstate.edu When : 1992 What : A sprite editor, includes full source (.ASM & .C) Where : I forget *. Source Code, Library File(s) : DDJ****.ZIP, XSHARP**.ZIP Who : M. Abrash When : Aug-Dec 1991, Jun-Aug 1992 What : Mode X introduction. Sources to do animation, polygon plotting, anti-aliasing, etc, on Mode X. Where : SIMTEL and mirrors, ftp.mv.com (Official DDJ site) in pub/ddj *. Source Code File(s) : article 7198 of rec.games.programmer Who : Frederick J. Haab (otto@nevada.edu) When : 26 Jun 92 00:17:52 GMT What : Scrolling in mode 13h, C program Where : USENET archives *. Source Code File(s) : article 7716 of rec.games.programmer Who : Joshua C. Jensen (sl8nl@cc.usu.edu) When : 30 Jul 92 00:02:36 GMT What : Bitmap manipulation (scaling + perspective), Turbo Pascal source w/ inline assembly. Where : USENET archives *. Source Code File(s) : VESAVGA.ZIP Who : Randy Buckland When : 6/18/92 What : .ASM & .C source to provide fast routines for VESA VGA modes. Where : garbo *. Sprite Library, Code File(s) : STK110.LZH Who : Jari Karjala When : 1991 (v 1.1) What : Sprite library & toolkit for Hi-Res EGA, BW includes C source, demo & good docs. Where : simtel & mirrors *. Sprite Library, Source Code File : SPRITES.ZIP Who : Marius Kjeldahl When : 1991 What : Sprite library for VGA mode $13 includes TPU, .PAS source. (shareware, $69 ?) Where : garbo *. Sprite Library, Toolkit File(s) : WGT_SPR2.ZIP, WGT_TC21.ZIP, WGT_TP2.ZIP Who : When : 1992 What : Shareware Sprite toolkit for VGA mode $13 includes TPU (WGT_TP2.ZIP), example programs for usage Nag-shareware program Where : wuarchive, if somebody hasn't erased it yet *. Library (Pascal) Files : EGOF10.ZIP, EGOF10P.ZIP, EGOF10M.ZIP, EGOF10B.ZIP, EGOF106.ZIP Who : Logi Ragnarsson When : 1993 What : 256-colour graphics library for Turbo/Borland Pascal 6.0 and 7.0, VESA SVGA, Mode-X (and more), VGA/MCGA 320x200, example programs, manual, shareware ($20). Where : garbo *. Library Source File(s) : Xlib04c.zip Who : Themie Gouthas (and company) When : 11 Mar 93 What : mode X library for game, to many feature to mention Where : pub/MSDOS_UPLOADS@wuarchive.wustl.edu ------------- Sound & Music ------------- *. Documentation File(s) : Article 6077 of rec.games.programmer Who : Jeffrey S. Lee (jlee@smylex.uucp) When : 25 Feb 92 15:02:02 GMT What : Programming the AdLib/Sound Blaster FM Music Chips Where : usenet archives, also at the SB project site (tybalt.cco.caltech.edu), & SB mailserver (listserv@porter.geo.brown.edu) *. Executable, Runtime Library File(s) : MODTECH.ZIP Who : Mark J. Cox (M.J.H.Cox@bradford.ac.uk) When : 1991 What : TSR Library to play .MOD files in the background Supports PC Speaker, SB, DisneySS, LPT DACs, etc Where : ftp.brad.ac.uk in /misc/msdos/mp *. Library File(s) : MODOBJ.ZIP Who : Mark J. Cox (M.J.H.Cox@bradford.ac.uk) When : 1992 What : .OBJ file w/ routines to play .MOD files in the background Supports PC Speaker, SB, DisneySS, LPT DACs, etc Includes examples in TC & TP (shareware, $30) Where : ftp.brad.ac.uk in /misc/msdos/mp *. Source code File(s) : NH10SRC.ZIP Who : When : What : Eliminate noise on sound samples, incl. .C source Where : SB project site & mailserv site *. Source code, Executable File(s) : SB_OSC.ZIP Who : When : What : SB input scope / oscillator. Incl. .ASM source Where : SB project site & mailserv site *. Source code, Executable File(s) : SBDAC.ZIP Who : Jeff Bird (cejjb@marlin.jcu.edu.au) When : 12 Feb 92 What : SB DAC programming using DMA. Incl. .ASM & .C source Where : I forget (probably on SB project sites too) ========== Q4.2 " " " " " Amiga? TBA Q4.3 What is there for the Atari? from warwick@cs.uq.oz.au AMS library - Atari Machine Specific library - C++ classes for Sprites, Screen, Joysticks, Double buffering, etc. - beta testing now - contact: warwick@cs.uq.oz.au Q4.4 " " " " " Macintosh? from jmunkki@vipunen.hut.fi *. Source code Files : Arashi_Source.cpt.bin Who : ??? When : ??? What : source code for an arcade quality game, vector graphics, multichannel sound, (no sprites) Where : pub/mac/think-c/code@ics.uci.edu Q4.5 " " " " " Sun/Sparcs? contributed by andrean@cs.utexas.edu (Andre A. Nurwono) ========== -------- Graphics -------- *. Library What : Standard PIXRECT library Bitmap manipulation routines for frame buffer. ------------- Music & Audio ------------- *. Source code File(s) : tracker.tar.Z Who : When : What : .MOD file player through the audio device. Works on SPARCS w/ audio devices. Where : *. Source code File(s) : csound.tar.Z What : FFT & signal processor *. Source code What : MixView ========== Q4.6 " " " " " X TBA Additions are welcome. -------------------------------------------------------------------------------- REC.GAMES.PROGRAMMER General FAQ FAQ's about Where is ... -------------------------------------------------------------------------------- Q5.1 Where is Joshua Jensens famous Perspective Mapping Code? Beats me no one has found it yet (or at least hasn't told me they have and where to get it!) in one of the many usenet archive sites. Places to look are gatekeeper.com and wuarchive.wustl.edu. Both of these ftp sites archive usenet news. I suspect all the rec.games.programmer articles are on these sites and maybe (heavem forbid) the original FAQ even. Q5.2 Where else should I read? Here are SUGGESTED places you get information for things like perspective mapping programming the SB etc. comp.graphics comp.graphics.animation comp.sys.ibm.pc.soundcard I suggest before posting you read ALL newsgroups a minimum of 1 week. Ussually you will see the FAQ or where to get it. places suggested NOT to look for information comp.graphics.research if you want to get famed for posting off topic do it there. That group is for research IE new frontiers. Q5.3 Where can I find out about ray traceing? Ask about POV in comp.graphics should be good for about 30 to 40 replies in your mailbox. There are several packages available for free and comercially. I suggest POV because it's free and actually quite good. Vivid, Rayshade are just a few of the others. POV seems to be the most popular (and portable also), with the most handy utilities. -------------------------------------------------------------------------------- END OF FAQ -------------------------------------------------------------------------------- REC.GAMES.PROGRAMMER General FAQ NOTES -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- NOTE 1: contributed by -------------------------------------------------------------------------------- sean@stat.tamu.edu (Sean Barrett) There are two things that qualify as flicker. Well, hell, to make it simpler, let's call it three. At the end of this list I'll give a rough definition of the problem. 1) You move a shape by erasing it and plotting it in a new position, and there is a screen refresh during the time it is erased, resulting in the background showing through. 2) You're using CGA and you try to write anywhere on the screen during retrace, causing "noise" (due to DMA problems, I guess?). 3) You're moving a shape by some sort of "one-pass" technique in which the screen memory is never set to the background temporarily, so (1) can't happen, but the screen area the shape is in is refreshed during the draw, so half of it gets displayed at the old location, and half at the new. I think a general rule would be that flicker occurs when a pixel on the screen is displayed with a color other than the "correct" or "intended" pixel value, as compared to an ideal "intended" case. (Thus, if you're simulating something moving, you shouldn't see the background; if you're trying to simulate something moving and turning on and off simultaneously, then you should see it; it's all a question of intent.) Back to the above problems. Now, so far as I know, there are only two solutions to #2. Only redraw during the vertical retrace (blank), or don't support CGA. So I'm going to forget about this problem. You can handle #1, as I suggested in #3, by simply making sure you never write to the screen a value you don't want displayed. Below I'll put a list of ways I can think of to do this for bitmapped graphics--i.e., how to avoid the erase-redraw cycle. If you really want to handle #3, then things get funky, and more power to you. I don't personally believe it's a problem. If it's periodic, so this one shape when you move it horizontally in the middle of the screen is always split with the top half leading the bottom half by a pixel, it's a problem; but if it just happens randomly once in a while I doubt it's noticeable. In general, though, the solutions require paying attention to where the screen refresh is in some way. My main point, the reason I'm posting this whole thing, is that *solving problem #1 does not require messing with knowing what section of the screen is being refreshed.* As far as I know, the methods to combatting #3 are to redraw everything during vertical refresh (same as #2, but overkill) or drawing your shapes from top to bottom lagging about half the time of a screen refresh behind the refresh, or some such. Solutions to #1 that don't require timing considerations, e.g., how never to write a wrong pixel to the screen. o Don't use raw bitmaps, use sprite hardware. o Use hardware page flipping: display one screen, draw a new screen "off-screen" and when it's finished, direct the display hardware to display the new one. This can get messy for fast animation because you have to keep track of where sprites were the last two frames to erase and update them. o Use software page flipping; display one screen, draw a new screen "off-screen" and when it's finished, copy the new screen into screen memory. o Use techniques such as "store" sprites which overwrite the background. Generally an out-of-date technique now, though; only works for mono-color backgrounds. You simply write the sprite onto the screen; the sprite has enough border around it to overwrite its previous image. This is gross, very fast, and flicker-free except when shapes get on top of each other, at which point you get massive flicker. o Use scratch-pad calculations, a variant of software page flipping. Copy the section of the screen off that you need to update, update it offscreen, and put it back on the screen. A lengthy time ago I posted a discussion of how to do this effectively for XOR-style graphics for 8-bit type machines--you can xor a single image onto the screen that both erases and replots in a new place the old sprite. And you can calculate the image to do it on the fly, without additional memory, if you set up your shape tables, and it's faster than the normal draw shape with XOR to erase, draw shape with XOR to plot cycle because it only reads and writes screen memory once. Performance enhancements for bit blitting: o Unroll loops. o Write a custom bit blit for each shape, dedicated to that shape. Cuts your memory accessing down if the machine has an "immediate" operand mode that's faster than an index-addressed one. Memory performance enhancements for techniques that require many copies of shapes or large routines (such as pre-shifted shape tables): o If you're only using some of your shapes at any point in time (e.g. if you can divide your display up into "scenes"; for a certain period of time only these shapes are used), calculate the "larger" derived tables on the fly when the scene starts up. For large games (this is rec.games.programmer, not rec.graphics.programmer, right?) that have to access the disk anyway to change scenes, this is no big time loss. Also, if you write the code right then while you're idling the processor before starting work on the next display, you can do this stuff then. -------------------------------------------------------------------------------- NOTE 2: contributed by -------------------------------------------------------------------------------- sean@stat.tamu.edu (Sean Barrett) 3D graphics: Using matrices and vectors Part 1 - Allows you to independently rotate objects and move the camera anywhere. - Does not discuss clipping. - Algorithm uses 9 multiplies, 2 divides, and 9 additions per point, plus overhead per independently located object. - Part 2 gives the derivation for these formulas. Assume a right-handed universe, with x horizontal, y depth, and z vertical, and a screen display unit with x horizontal to the right and y vertical downward. The following are the rotation matrices: Rx(t) Ry(t) Rz(t) __ __ __ __ __ __ | 1 0 0 0 | | cos(t) 0 -sin(t) 0 | | cos(t) sin(t) 0 0 | | 0 cos(t) sin(t) 0 | | 0 1 0 0 | | -sin(t) cos(t) 0 0 | | 0 -sin(t) cos(t) 0 | | sin(t) 0 cos(t) 0 | | 0 0 1 0 | | 0 0 0 1 | | 0 0 0 1 | | 0 0 0 1 | -- -- -- -- -- -- rotate about x rotate about y rotate about z The following is the translation matrix: T(a,b,c) __ __ | 1 0 0 a | | 0 1 0 b | | 0 0 1 c | | 0 0 0 1 | -- -- Let each object be a collection of points or lines. If lines, they are drawn as lines connecting two 3D points, which can each be individually transformed. So this derivation just handles converting individual points in three-space into pixel coordinates. ---- BEGIN FORMULAS ---- If d is the distance from the eye to the window, h is the width of the window, v is the height of the window (use the sizes of the actual display if you don't know what else, but this is actually referring to the virtual camera's window); num_x is the number of pixels on the display in the x direction, num_y the number of pixels in the y direction, and (center_x,center_y) the location of the center of the display; let scale_x = d/h*number_of_x_pixels, scale_y = d/v*number_of_y_pixels. Then let S be defined as __ __ | scale_x center_x 0 0 | | 0 1 0 0 | | 0 center_y -scale_y 0 | | 0 0 0 1 | -- -- Let the camera be located at (cx,cy,cz). Let the vector E be pointing in the direction the camera is facing, the vector D be pointing to the right (along the camera's x axis), and the vector F be pointing up (along the camera's z axis); and let D, E, and F be of length 1. Then define the following matrices: matrix J matrix C | Dx Dy Dz 0 | | 1 0 0 -cx | | Ex Ey Ez 0 | | 0 1 0 -cy | | Fx Fy Fz 0 | | 0 0 1 -cz | | 0 0 0 1 | | 0 0 0 1 | and let N = S*J*C. Let each object i be at location cix, ciy, ciz, and let the matrix which holds the current rotation for that object be O(i). To rotate the object around the q axis by n degrees, let new O(i) = Rq(n) * O(i). To rotate the object about *its* q axis, let new O(i) = O(i) * Rq(n) (I think. I haven't looked at this closely, so it's probably wrong). Let Otemp(i) be O(i) with the rightmost column of zeros replaced by cix, ciy, and ciz, or in other words, the product of Temp(i)*O(i) where Temp(i) is __ __ | 1 0 0 cix | | 0 1 0 ciy | | 0 0 1 ciz | | 0 0 0 1 | -- -- Now, for each object i let M = N * Otemp(i). Now, for each point P in i, let V = M * P, that is: Vx = M[0,0]*Px + M[0,1]*Py + M[0,2]*Pz + M[0,3]; Vy = M[1,0]*Px + M[1,1]*Py + M[1,2]*Pz + M[1,3]; Vz = M[2,0]*Px + M[2,1]*Py + M[2,2]*Pz + M[2,3]; Then the pixel to plot to is: If Vy>0 (Vx/Vy, Vz/Vy) Done. -- 2 -- 3D graphics: using matrices and vectors Part 2 - Allows you to independently rotate objects and move the camera anywhere. - Does not discuss clipping. - Algorithm uses 16 multiplies, 2 divides, and 12 adds for each point, plus overhead per independently located object. Also shows how some of the calculations are wasted, and reduces it to 9 multiplies and 9 additions. The folowomg is a derivation of some of the math for using 3D graphics with matrices and vectors. I don't see any way of explaining how to use the matrix formulas without all the extra context, so you'll have to wade through it. In general, you can simplify things by multiplying out the matrices and similar techniques. You have a world described by three dimensional coordinates--it could be lines or points or polygons, whatever. You have an imaginary camera in this world, and you want to draw exactly what this camera would see. We represent the camera as a point where an "eye" is and a window through which it's looking--that is, a point for the eye, a vector from the eye to the center of the window, and another vector to tell us which way is the up direction on the window. We can figure out the sideways direction of the window by taking a cross-product, but it may be better to represent it explicitly, as discussed far eventually below. What we want to know is where on our screen we should plot a particular point. The solution is to figure out where on the imaginary window the point would appear to be, and then to map the window onto our screen. Suppose the eye is at the origin, facing along the X axis, and the point is in the XY plane so we can only look at two dimensions for illustration purposes. Y axis | ___--- | ___--- | ___--- | Point | --- | eye----------+------------------ X axis ---___ | ---_|_ window ---___ ---___ Suppose the window ranges from (d,h/2) to (d,-h/2) and the point is at (a,b). We want to know where the line from the point to the eye intersects the window line. Well, the point is only visible if it's in front of the eye, so assume a>0. Now, the two lines are y = (b/a)*x (from Point to eye) and x = d (line window is on) So the location of intersection is (d,(b/a)*d). In other words, the point appears on our imaginary window with horizontal position d*b/a if |d*b/a| <= h/2. Thus, the quick and dirty 3d graphics formula for assuming an eyepoint at the origin, X horizontal, positive to the right, Y vertical, positive up, and Z depth, positive going into the distance (this is a left-handed universe, whereas the rest of the derivations will be for a right handed universe), and screen coordinates sx horizontal, positive to the right sy vertical, positive down, is: if (Z>0) then sx = x_scale * X / Z + x_center; sy = - y_scale * Y / Z + y_center; endif where x_center, y_center is the pixel address of the center of the screen; x_scale is d/h*number_of_pixels_horizontally and y_scale is d/v*number_of_pixels_vertically; d is the distance between the eye and the window in the imaginary camera, h and v are the height and width respectively of the imaginary window. Ok, back to the messy stuff. Since our eyepoint won't necessarily be at the origin and facing in the right direction, we need to be able to handle arbitrary translations and rotations. In general, to rotate (a,b) into (a',b') based on a given angle t, we do a' = cos(t)*a + sin(t)*b; b' = cos(t)*b - sin(t)*a; (or switch the signs depending on which way you want to define as the positive direction of rotation). Now, to cleanly handle multiple rotations, we want to use matrices to handle this. This is the same as | cos(t) sin(t)| |a| _ |a'| |-sin(t) cos(t)| |b| - |b'| that is, a 2x2 matrix times a 2x1 matrix gives a 2x1 matrix. Now to handle an arbitrary 3D rotation, we need to rotate on any axis: rotate about x rotate about y rotate about z | 1 0 0 | | cos(t) 0 -sin(t) | | cos(t) sin(t) 0 | | 0 cos(t) sin(t) | | 0 1 0 | | -sin(t) cos(t) 0 | | 0 -sin(t) cos(t) | | sin(t) 0 cos(t) | | 0 0 1 | Now, to rotate about a particular point, we have to translate to that point. Say we want to rotate about (d,e,f). Then we subtract (d,e,f) from our point, rotate, and then add (d,e,f) again. It would be nice if we could do that automatically with the matrices, and there is a way to, a cute trick. We switch from 3x3 matrices to 4x4 matrices, and use 4-vectors. For the rotation matrices, the new elements are all zero, except the bottom right one which is 1; for example: | cos(t) sin(t) 0 0 | | -sin(t) cos(t) 0 0 | | 0 0 1 0 | | 0 0 0 1 | Also, all of our vectors have a fourth element, which is always 1. (In programming this, you can just continue to use three-vectors and program the 'multiply matrix by vector routine' to pretend there's one there.) Now, to do a translation we want: x' = x + k1; y' = y + k2; z' = z + k3; It turns out that this does the trick: | 1 0 0 k1 | | x | | 0 1 0 k2 | | y | | 0 0 1 k3 | | z | | 0 0 0 1 | | 1 | Now, let's define some matrix terms so we can compress our notation. Let Rx(t), Ry(t), and Rz(t) be the 4x4 rotation matrices, and let T(k1,k2,k3) signify the appropriate matrix as above. To rotate a point (a,b,c) theta around the y axis at point (d,e,f), we do the following in sequence: T(-d,-e,-f), Ry(theta), T(d,e,f). Now, one nice thing about matrices is that we can get the effect of sequential application by multiplying matrices; that is, if U = (a,b,c,1) and V=(a',b',c',1), then do T(-d,-e,-f)*U, and take that and do Ry(theta)*that, and take this and do T(d,e,f)*this, giving V, then this is: T(d,e,f) * ( Ry(theta) * ( T(-d,-e,-f) * U ) ) ) = V Since matrix operations are associative, this is the same as T(d,e,f) * Ry(theta) * T(-d,-e,-f) * U = V or, in other words, let M = T(d,e,f)*Ry(theta)*T(-d,-e,-f), then M is a matrix which performs the rotation we desire. Ok, now to wrap it all up. Suppose we have a bunch of objects in 3D we want to display, and the aforementioned camera. The camera is at (cx,cy,cz), and we have a vector E pointing in the direction the camera is aiming, a vector D which shows which way the window is pointing to the right, and a vector F which points along the window upward. These vectors form an orthonormal basis, so to rotate into the frame of reference for them we use the matrix | Dx Dy Dz | | Ex Ey Ez | | Fx Fy Fz | also, we want to use 4x4 matrices and first we want to translate to cx..cz, so we use matrix J matrix C | Dx Dy Dz 0 | | 0 0 0 -cx | | Ex Ey Ez 0 | | 0 0 0 -cy | | Fx Fy Fz 0 | | 0 0 0 -cz | | 0 0 0 1 | | 0 0 0 1 | So for an arbitrary point (a,b,c) in three space, the screen coordinates for it sx, sy are: let U = (a,b,c,1); let V = J*C*U. Then if (Vy>0) then sx = scale_x * Vx/Vy + center_x; sy = - scale_y * Vz/Vy + center_y; endif Generally, we want to store J and C separately. It is pretty simple to move the camera, now; if the camera is always moving in the direction its facing, use the E vector and factor direction in by hand, and put this into C. To rotate the camera, just multiply a rotation matrix on the left by J. Now, to rotate objects properly, keep a separate rotation matrix for each object. Then for each point in that object, rotate it and translate it. It's simplest to store each object with the origin as the center of the object. Then to calculate a point, you multiply by the rotation matrix and then by the translation to put the objects center where it should be in world space; because you're doing the translation second, it's easy to put it into one matrix: translation rotation | 1 0 0 l | | a b c 0 | | a b c l | | 0 1 0 m | | d e f 0 | _ | d e f m | | 0 0 1 n | * | g h i 0 | - | g h i n | | 0 0 0 1 | | 0 0 0 1 | | 0 0 0 1 | Call this matrix O(q) where q is the object number. Then, for each point in object q, the final multiply is V = J * C * O(q) * U. Finally, we can move some of the final calculation into a matrix. We have: sx = scale_x * Vx/Vy + center_x; sy = -scale_y * Vz/Vy + center_y; If we factor out Vy, we get sx = (scale_x * Vx + center_x * Vy)/Vy; sy = (-scale_y * Vz + center_y * Vy)/Vy; Suppose from V we calculate V': (scale_x*Vx + center_x*Vy, Vy, -scale_y*Vz + center_y*Vz, 1) Then sx = V'x / V'y; sy = V'z / V'y; Well, to get V', we just multiply V by the matrix | scale_x center_x 0 0 | | 0 1 0 0 | | 0 center_y -scale_y 0 | | 0 0 0 1 | Let us call this matrix S. Remember, scale_x = d/h*number_of_x_pixels, scale_y = d/v*number_of_y_pixels, d is distance from eye to window, h is width of imaginary screen, v is height of imaginary screen. So, now, the basic idea is this. To minimize calculation, let N = S * J * C. For each object q, let M = N * O(q). For every point U in q, let V = M * U. If Vy>0, then that point is at (Vx/Vy,Vz/Vy) on the screen. In pseudo-code, that's /* matrix_multiply (destination, left_multiplicand, right_multiplicand) */ for each time slice do if camera has moved then matrix_multiply ( N, J, C); matrix_multiply ( N, S, N); endif for q an object do matrix_multiply( M, N, O[q]); for p a point in object q do matrix_by_vector_multiply( V, M, U[q][p]); do something with ( V[0]/V[1], V[2]/V[1] ); endfor endfor endfor In truth, the matrix_by_vector_multiply wastes a bit of time, if you're trying to tune the code, it'd be worth tuning it. Normally, it does this: V0 = M00*U0 + M01*U1 + M02*U2 + M03*U3; V1 = M10*U0 + M11*U1 + M12*U2 + M13*U3; V2 = M20*U0 + M21*U1 + M22*U2 + M23*U3; V3 = M30*U0 + M31*U1 + M32*U2 + M33*U3; However, we know that the bottom row is never used, since V3 is always 1; furthermore, we know that U3 is 1, so we can just do V0 = M00*U0 + M01*U1 + M02*U2 + M03; V1 = M10*U0 + M11*U1 + M12*U2 + M13; V2 = M20*U0 + M21*U1 + M22*U2 + M23; This uses nine multiplies and nine adds, plus the two divides required to calculate the screen coordinate. I believe this is the minimum possible for arbitrary 3D graphics. (You can turn the two divides into one divide and two multiplies by calculating 1/Vy and multiplying by that, which may be a win on some machines.)