home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 3 / PDCD_3.iso / utilities / utilsf / graphtext / cga / text0000.txt < prev    next >
Encoding:
Text File  |  1995-03-27  |  37.8 KB  |  934 lines

  1. REPOSTED DUE TO POPULAR DEMAND!
  2.  
  3. It seems only selected parts of the globe got my last posting...?
  4.  
  5. Please keep followups to comp.graphics.algorithm
  6.  
  7. ----Snip----
  8.                  SPEEDY FREE DIRECTION TEXTURE MAPPING
  9.                  =====================================
  10.                         AND OTHER NIFTY TRICKS
  11.  
  12.     Some Wild-Ass Speculations and Untested Theories (that will work :-)
  13.  
  14.                       by Hakan 'Zap' Andersson
  15.  
  16.                               95-02-02
  17.  
  18. -1. Greed/Copyright
  19.  
  20. If you use any of this stuff in your commercial game, I at least think I
  21. deserve to:
  22.    A) Be mentioned and thanked thoroughly in the game. Thank
  23.       Hakan 'Zap' Andersson, zap@lysator.liu.se
  24.    B) Receive a copy of the game!!
  25.    C) Receive obscene amounts of cash :-)
  26.  
  27. If you fail to comply to the above, it may turn out that I had already
  28. patented all algorithms herein, and I would then throw a "Unisys" at you!
  29.  
  30. The information herein is Copyright -95 Hakan 'Zap' Andersson
  31.  
  32.  
  33.                                  PART I
  34.                     "Texturing in a skewed universe"
  35.  
  36. 0. Introduction
  37.  
  38. So, you played Wolfenstein 3D. So you played DOOM. And you played Descent. 
  39. Texturemapping in Real Time has proven to be a reality, even on a tiny old
  40. PC.
  41.  
  42. This document will take you on a Guided Tour through one of my more recent
  43. speculations on the subject. There is no doubt that what I outline here
  44. will WORK. I call it speculations only because I havn't actually imple-
  45. mented or tested it! Sadly, I have a life, and that life prevents me from
  46. luxurios amounts of time at the console hacking for FUN. [It instead
  47. forces me to luxurious amounts at the same console, hacking for my bread].
  48.  
  49. So none would be happier than I if somebody had the time to IMPLEMENT any
  50. of this, and please give me the sourcecode when he is done!!!
  51.  
  52.  
  53. 1. History
  54.  
  55. When I first played Wolfenstein 3D, I was chocked by the "real time textures"
  56. that it could produce on my old 386/20. I thought that stuff was only poss-
  57. ible on SGI Onyx machines and above. I was baffled.
  58.  
  59. But after some months, the "secret" of the method was analyzed to death by 
  60. the net. So, it was just vertical slices of wall being scaled vertically!
  61.  
  62. But still - Wolfenstein proves to be the very basis of the idea that will 
  63. be talked about in this document. Everybody and his sister trying to do a 
  64. similar thing, would have initially tried to scan-convert the polygons
  65. HORIZONTALLY. Mostly because all textbooks on the subject talk about
  66. horizontal scanlines as if they were the only things that exists. [Reading
  67. too many books limits your thinking!] 
  68. Wolfensteins strike of genious was to work VERTICALLY.
  69.  
  70. But everybody "knew" that this only worked for walls. We all "knew" that 
  71. there would NEVER be a game capable of drawing textured floors and ceilings. 
  72. And boy were we wrong.
  73.  
  74. Out came DOOM. As usual, I thought this was something only a SGI Onyx 
  75. could do. Now the FLOOR had textures! How the hell was THIS possible!?
  76.  
  77. As usual, the trusty ol' internet (not the RUSTY old internet :-) provided 
  78. the answer. Naturally, they DID work horizontally on the floor, since 
  79. horizontally meant along the same Z, which meant linearilly in texture 
  80. space.
  81.  
  82. "Of course" we thought. "Ok" we thought. "Now we know. Walls work. Floors 
  83. too. But it is still impossible to work on any orientation of textures. 
  84. That, at least, is something that only an SGI Onyx can do".
  85.  
  86. Then came Descent.
  87.  
  88. Of course, I knew this was not possible, and that this was not happening
  89. in MY tiny computer. (A mere 486/66!). I creapt around the case of my 
  90. computer to see if there wasn't an SGI machine hidden in there after all!
  91.  
  92. This time, I didn't wait for the 'net to think for me. This time, I did the
  93. thinking all on my own. This is the result of that thinking.
  94.  
  95. 2. What Wolfenstein and DOOM taught us
  96.  
  97. The basic principle taught by DOOM (and by Wolfenstein) is this: 
  98.  
  99. TRUTH I:  As long as you are drawing your polygon ALONG LINES WITH EQUAL 
  100.           Z (The Y axis for walls and the X axis for floors/ceilings) THE 
  101.           TEXTURE IS TRAVERSED LINEARILY.
  102.  
  103. Of course, this was all fine and dandy for FLOORS and WALLS. But what the hell
  104. did that help us on that sloping plane we wanted to texture!?
  105.  
  106. The answer is, IT HELPED A LOT. We only have to bang our heads on the walls,
  107. and FORGET ALL THAT NONSENSE ABOUT WALKING ALONG HORIZONTAL OR VERTICAL
  108. SCANLINES!!
  109.  
  110. TRUTH II: ALL polygons drawn on screen has along SOME DIRECTION equal Z
  111.           coordinates along that line.
  112.  
  113. This means, that if we can scanconvert our polygons not horizontally, not
  114. vertically, but ALONG THIS LINE, we can TRAVERSE THE TEXTURE LINEARILLY.
  115. I needn't go in to HOW MUCH THIS WILL SPEED UP EVERYTHING compared to
  116. one division per pixel (a common need in texturing algorithms) or
  117. bicubic approximations.
  118.  
  119. 3. How the hell (whimsical DOOM reference intended) do we do it!?
  120.  
  121. Step one: Find the elusive screen-direction which represents equal-Z.
  122. This turns out to be so trivial it hardly even needs to be mentioned:
  123.  
  124. Take the normal vector, converted to screen-coordinates (as a 2D
  125. vector). Your 'constant Z line' will be that vector rotated 90
  126. degrees. [Alternatively, the cross product between the normal and
  127. the viewing direction will give you the same line in 3D]. 
  128. The special case being that the normal points directly at the screen
  129. (having (0, 0) size in screen space). This simply means that you can
  130. pick ANY line - since the whole POLYGON is on the same Z!
  131.  
  132. Now all you have to do, is to scan-convert your polygon in THIS
  133. DIRECTION ON SCREEN. Calculate what texture-coordinate-span that
  134. line has, and linearily walk the texture as you draw that line in
  135. the polygon.
  136.  
  137. That is "it", really. 
  138.  
  139. 4. How can we make this EFFICIENTLY (and without holes?)
  140.  
  141. Firstly, as with ALL line-drawing algorithms, we need different 'versions'
  142. of it depending on if the lines slope is in the range +-45 degrees, or if
  143. it is closer to the Y direction. I will here only discuss the 'almost
  144. horizontal case', where the line has a direction so that for each pixel 
  145. step along X, Y may OR may NOT increase/decrease one.
  146.  
  147. The algorithm only needs to be "rotated" to work with Y instead of X, and
  148. I leave this as an exercise to the reader. Heh. :-)
  149.  
  150. So, assuming that the polygon we want to draw turns out to fulfill this
  151. need, we can do the following:
  152.  
  153. I am assuming we want to draw this polygon into a palette-device that is
  154. represented in memory as an array of bytes, row by row. This discussion 
  155. does NOT assume any particular hardware. You may use MODE13 on a PC, or a 
  156. WinGBitmap under Windows (NT), or you may use an X bitmap under X. Lets 
  157. have the following C variables:
  158.  
  159.      unsigned char *screen; /* Pointer to screen memory */
  160.      short x_size;          /* Screen X size */
  161.      short y_size;          /* Screen Y size */
  162.  
  163.      /* A macro to reference any given pixel (read OR write) */
  164.      #define PIXEL(x, y) screen[y * x_size + x]
  165.  
  166. Since we are in this example walking along X, we find the 'maximum 
  167. horizontal size' of the polygon: It's minimum X and it's maximum X 
  168. coordinates.
  169.  
  170. Now we get clever. We get ourselves an array of integers containing
  171. 'x_size' elements. [If we are on a PC, or are confined to 320x200,
  172. or any other resolution with less than 64k pixels, a short is 
  173. sufficient. Otherwize, we need a long]
  174.  
  175. This array will store our sloping line. To save time filling in
  176. the array, we only walk it starting in the MINIMUM X of the polygon
  177. and walk towards MAXIMUM X of the polygon. 
  178.  
  179. Into the array we fill in the BYTE OFFSET for that pixel.
  180.  
  181. Meaning, for the purely horizontal line, the array would contain:
  182.  
  183.    0   1   2   3   4   5   6   7   8   9   ....
  184.  
  185. But for a line that looks like this:
  186.  
  187.                X X X
  188.          X X X
  189.    X X X
  190.  
  191. Would, on a 320 x 200 graphics mode contain the following offsets:
  192.  
  193.   0  1  2  -323 -324 -325 -646 -647 -648
  194.  
  195. The reason we store this in an array is threefold:
  196.  
  197. Reason 1: Speed! The line is the same all across the polygon! Why calculate it
  198.           more than once!?
  199.  
  200. Reason 2: Avoid HOLES. If you calculated the line 'on the fly' you could end up
  201.           with results such as this:
  202.  
  203.  
  204.                        2 2 2           1 = Line 1
  205.                  2 2 2 . 1 1 1         2 = Line 2
  206.            2 2 2 . 1 1 1               . = Holes in the texture
  207.              1 1 1 
  208.  
  209.           With a precalculated line, we are guaranteed to get:
  210.  
  211.                          2 2    
  212.                    2 2 2 1 1 1   
  213.              2 2 2 1 1 1         
  214.              1 1 1 
  215.  
  216. Reason 3: By not only storeing the Y coordinate, but the BYTE OFFSET, we save
  217.           ourselves a multiplication!
  218.  
  219.  
  220. 5. How to Scanconvert a polygon along a 'skewed line'
  221.  
  222. But now your real headache starts. How the HELL should I make a polygon scan-
  223. conversion algorithm that works along this 'skewed line'!? Didn't I have 
  224. ENOUGH trouble writing a normal "horizontal" polygon scan converter!? :-)
  225.  
  226. All I say is: Relax, pal. There is hope. There is an easy way:
  227.  
  228. If you have a line that is 'skewed' by a slope of 1:3 (as in the example
  229. above), all you have to do, is this:
  230.  
  231. BEFORE scanconverting your polygon (but AFTER transforming to screen-
  232. space AND clipping), SKEW THE POLYGON VERTICIES by THE SAME AMOUNT 
  233. but in the OPPOSITE DIRECTION (in screen space). 
  234. Then use your NORMAL HORIZONTAL SCAN CONVERSION ALGORIHM. But when you 
  235. DRAW the LINES, DONT draw the HORIZONTALLY! Use the offset vector, and 
  236. draw them in the skewed direction!
  237.  
  238. If our sloping line looks like this:
  239.  
  240.                  X X X
  241.            X X X
  242.      X X X
  243.  
  244. Then if this is the original polygon verticies:
  245.  
  246.    1 .               . 2
  247.  
  248.  
  249.  
  250.    3 .   
  251.              . 4
  252.  
  253. After 'skewing' it would look like this:
  254.  
  255.    1 .              
  256.  
  257.                      . 2    (moved down 2)
  258.  
  259.    3 .   
  260.  
  261.              . 4  (moved down 1)
  262.  
  263.  
  264. To paraphrase: Never "TELL" your scanconverter that you are working
  265. with skewed scanconversion! "Fool" it by feeding it a skewed polygon,
  266. and get the right result by "skewing back" the output!
  267.  
  268. So, what's the catch? Well, using this method ain't "perfect". You can
  269. get seams and holes BETWEEN your polygons, because the outcome of the
  270. edge of the polygon depends a little (but not much) on the angle of
  271. the skewed line. [Maybe there is a way to treat the edges of the polygon 
  272. specially? I have many different ideas on this subject, but I dont know
  273. how "bad" the result will be, since I have yet to implement ANY of this!]
  274.  
  275. Now, keep in mind that each "scan" along this "skewed" line represents 
  276. one Z coordinate. This means that for each "scan" you'll need only ONE 
  277. divide to find out at which point on the TEXTURE your START COORDINATES are. 
  278. You can also obtain the 'step' size and direction to walk the texture
  279. bitmap. Note that the step DIRECTION is the same all over the polygon, but
  280. the step SIZE depends on 1/Z. So the direction only needs to be calculated
  281. once per polygon. The size needs to be calculated once per scan.
  282. (HOW your texture is mapped to the polygon is irrelevant - as long it is
  283. linear. The texture needn't necessarily be mapped flat on the polygon -
  284. it may even be a threedimensional hypertexture!!)
  285.  
  286. This method also lends itself nicely to a Z-buffer! No need to recalculate
  287. Z! It is the same along each "scan"! So only a TEST needs to be done! And
  288. if you use a 16-bit Z-buffer, you can use the 'offset vector' discussed above
  289. multiplied by two (= shifted left once) to get the offset into the Z-buffer!
  290.  
  291. 6. Conclusion on texturing
  292.  
  293. After realizing this, I feel that Descent isn't impossible after all. I doubt
  294. they use exactly THIS technique, but at least it has proven to be theoreti-
  295. cally possible to render free-direction textures in realtime.
  296.  
  297.  
  298.  
  299.                                 PART II
  300.  
  301.                           "Let there be light"
  302.  
  303.  
  304. 0. Some words about lighting
  305.  
  306. OK, so we figured out one way to do quick texturemapping. So we cracked
  307. the secret of Descent? Nope.
  308.  
  309. Instead, one of Descent's MOST impressive effects is now the LIGHTING!
  310. It seems like they have TRUE lightsourcing with light fall-off in the
  311. distance!! And it is not just a "one shade per polygon" thing! It is
  312. a true "one shade per pixel" thing! (Try landing a flare on a polygon
  313. in some dark area! You get a nice pool of light around it!)
  314.  
  315. This is extremely impressing that they can do this AND texturemapping in 
  316. realtime, at the SAME time!
  317.  
  318. Sadly, I havn't got a clue. Anyone?
  319.  
  320. Instead, I have got quite a BIG clue about something completely DIFFERENT:
  321.  
  322. 1. DOOM Lighting basics
  323.  
  324. Instead of talking about how Descent does it's ligting, lets step back
  325. to something a lot less complex: DOOM's lighting.
  326.  
  327. DOOM really doesn't have much of lighting. What you CAN do, is specify a
  328. 'brightness' of a certain area of your 'world'.
  329.  
  330. What DOOM *has* is a 'shade remapping table', and this is what I will use
  331. as a base of my idea. Allow me to explain:
  332.  
  333. DOOM uses a 256 color graphics mode - which means that it uses a palette. 
  334. (Well, actually several palettes that gets exchanged, e.g. a red-ish
  335. palette for when you are hurt, e.t.c, but let's not get picky)
  336.  
  337. When the lighting is 100% the pixel in the texturemap is the same pixel 
  338. value that gets written to screen. But DOOM uses a bunch of (34 i think)
  339. remapping tables for different lighting levels. Such as:
  340.  
  341.    unsigned char LightRemap[34][256];
  342.  
  343. So to find the output pixel, the following algorithm would be used:
  344.  
  345.    output_pixel = LightRemap[light_level][input_pixel];
  346.  
  347. The LightRemap vector would be precalculated (in DOOM's case it is 
  348. actually loaded from the WAD file). So when light_level is max,
  349. and imput_pixel references a white pixel, output_pixel will return
  350. the same white pixel. But when the lighting is 50%, output_pixel will
  351. instead be a gray color.
  352.  
  353. 2. Random dithering to the people!
  354.  
  355. Now one problem that is seen in ALL 3D games is that you can SEE how
  356. their lighting falls off in 'steps'. If the palette only contains three
  357. darkness-levels of purple, then the LightRemap vector would for light-
  358. levels from 0-25% reference BLACK, for 25%-50% the darkest, and so on.
  359. You would VERY EASILY *see* the borders between the different levels,
  360. as the light diminishes in the distance. That looks UGLY.
  361.  
  362. Now if the game programmers had added FOR EACH PIXEL a RANDOM value to
  363. the light. (Quick random values can be gotten from a table. They dont 
  364. need to be superrandom, only chaotic). This would have given us a DITHER
  365. of the screen. And that DITHER would CHANGE for each frame (since it is
  366. RANDOM). This would INCREASE the perceived number of colors greatly!
  367. Sure - EACH FRAME would look like a "snowy mess" of random noise. But
  368. when played quickly, it would look smooth!
  369.  
  370. Compare the perceived color quality of a TV picture from what you get
  371. when pausing a frame on your VCR, and you will understand what I am saying.
  372. You dont see all the noise in the picture, because the average of the noise
  373. over time for each pixel is the intended pixel value. The human eye 'removes'
  374. the noise for you. 
  375.  
  376. Dithering would increase the colorresolution of games such as DOOM and
  377. Descent, and the 'noisy picture' would look MORE REAL than the 'clean'
  378. picture of today. (This is true of all moving graphics/animation)
  379.  
  380.  
  381. 1. Bumpmapping in realtime? Impossible! NOT!
  382.  
  383. Now lets get to the REAL fun!!!
  384.  
  385. One of the Truths I have found in computer graphics is that texture-
  386. mapping can GREATLY reduce the number of polygons you need to make
  387. an object convincing. 
  388.  
  389. But sometimes you still need to have extra polygons, just get away
  390. from the "flat" look of the polygons.
  391.  
  392. Another Truth that I found (while implementing my once commercial
  393. raytracer) is that the REAL fun starts with BUMPMAPPING! That is
  394. when you start talking about REAL decreases in the polygon count!
  395.  
  396. Instead of having hundreds of polygons to make a wobbly mountain
  397. side, have ONE polygon, and add the wobblyness of the surface as 
  398. a bumpmap!
  399.  
  400. The basic idea behind bumpmapping:
  401.  
  402. To do bumpmapping, you need to be doing SOME kind of "real" lighting. But
  403. the lighting does NOT need to be ANY MORE COMPLEX than simple cosine
  404. lighting. We dont even need point lightsources! Directional light is OK.
  405.  
  406. To get the light-level of a polygon, we simply take the dot-product 
  407. between the light-direction and the surface normal vector! That's it!
  408.  
  409. If we use directional light, we can assume that light is coming from
  410. the direction Lx, Ly, Lz. (This should be a normalized vector: The
  411. 'length' should be 1.0) and the polygon normal is Nx, Ny, Nz, the
  412. light level is:
  413.  
  414.     Light = Lx * Nx + Ly * Ny + Lz * Nz
  415.  
  416. What could be simpler!?
  417.  
  418. Now, Bumpmapping means VARYING the normal vector over the surface, and
  419. recalculating a NEW lightlevel for each pixel. For instance, lets assume
  420. a surface that is flat in the X-Y plane. If we vary the X component of 
  421. the surface normal with a sinus function along X, it will look like the 
  422. surface was rippled with waves in the X direction!
  423.  
  424. The shading of these ripples would be "correct": If we the light comes
  425. from the LEFT, the LEFT side of the ripples would be bright and the
  426. right side would be dark. if the light came from the RIGHT, the reverse
  427. would be true.
  428.  
  429. Now compare games like DOOM, where they "fake" bumpmapping by simply 
  430. PAINTING light and dark edges on stuff like doors and similar. 
  431. This looks horrible when two doors opposite eachother in a corridor both
  432. have their "bright" edges on their upper and LEFT sides!
  433.  
  434. And trust me, the eye/brain is REALLY good at picking out these
  435. inconsitensies. The eye/brain uses shading as its PRIMARY CUE to the
  436. real nature of the surface! Yes! The PRIMARY cue! The whole human
  437. optical subsystem is oriented towards recognizing shades as being
  438. surface variations! A warning-this-is-not-real flag is IMMEDIATELY
  439. raised when the 'bright edge' of a door doesn't match the intended
  440. light direction!
  441.  
  442. This is where even Descent falls apart!
  443.  
  444. 2. How can we get this into games such as DOOM?
  445.  
  446. Well, first of all SOME kind of 'directional light' must exist. But 
  447. experience tells me that even a hardcoded directional light, where
  448. the light comes from the SAME direction all over the 'world', can
  449. increase the realism. And we need to be doing AT LEAST cosine shading.
  450.  
  451. Above I said that to do bumpmapping, we must RECALCULATE THE SHADING
  452. FOR EACH PIXEL. Now that doesn't sound very fast, does it? Well, the
  453. thing is, we dont need to do that! But first let me explain:
  454.  
  455. In advanced rendering systems you normally have one bitmap as texture-
  456. map, and another bitmap as the bump-map. The bumpmap usually defines the
  457. simulated 'height' of the surface as the brightness of the bitmap. But
  458. HEIGHT in ITSELF is not interesting! (If the surface is flat - it has the
  459. same height. Only where the height CHANGES the surface isn't flat, and
  460. the normal is affected); HEIGHT is not interesting, CHANGE of height is.
  461.  
  462. So a traditional renderer will have to sample at least FOUR adjacent
  463. pixels in the bump-map bitmap, and calculate the 'slope' in that part
  464. of the bitmap based on their RELATIVE brightness. That 'slope' is then
  465. transformed into a deformation of the normal vector - which in turn
  466. (via the shading algorithm) yields another shade at that point (phew!).
  467.  
  468. HOW THE HELL DO I INTEND TO DO SOMETHING LIKE THAT IN REALTIME!?
  469.  
  470. Now, lets assume that we want to make a 'groove' along the Y axis
  471. in the middle of our polygon. Lets say the polygon is 64x64 units
  472. large, is flat in the XY plane, and the texture mapped to the
  473. polygon is also 64x64 in size. 
  474.  
  475. So what we want to do, is at X coordinate 32 we want to make a 'groove',
  476. so the polygon will LOOK as if it's profile was this:
  477.  
  478. The 'intended' surface seen from negative Y axis:
  479.  
  480.  
  481.   --------------\_/---------------
  482.  
  483.  
  484.   |             |||              |
  485. X 0            / | \           X 64
  486.            X 31  32 33
  487.  
  488. Since we are using "flat shading", we will only calculate one brightness
  489. level for the whole polygon: The dot-product between the normal vector
  490. and the light direction.
  491.  
  492. Lets say that the result is 80%. So, the overall lighting of the polygon
  493. is 80%! And 80% is the lightlevel we use on all pixels of the polygon
  494. EXCEPT those at X=31 and X=33! Because all pixels at X=31 should look
  495. as if they were going 'into' the surface (the normal vector displaced
  496. to the right), and those at X=33 should look as coming 'out of' the
  497. surface (normal vector displaced to the LEFT).
  498.  
  499. Lets say the lighting level for a normal displaced a little to the
  500. left is 95%, and a normal vector displaced a little to the right is 50%.
  501.  
  502. As you can see, we then have three different shadings for this polygon
  503. with the current lighting conditions:
  504. 80% for most of it, 50% for column 31, and 95% for column 33.
  505.  
  506. As you can see, we do NOT need to recalculate the shading for each pixel.
  507.  
  508. We only need to recalculate the shading level AS MANY TIMES AS WE HAVE
  509. DIFFERENT DISPLACEMENTS OF THE NORMAL VECTOR.
  510.  
  511.  
  512. 3. How to implement this:
  513.  
  514. We can let the normal texture bitmap that you use for texturing contain
  515. additional data: Any number of 'normal displacement' structures.
  516.  
  517.    struct normal_displacement {
  518.        int palette_index;
  519.        real normal_displace_x, normal_displace_y;
  520.        int color_to_use;  
  521.    };
  522.  
  523. Any number of these structures may be attached to a bitmap. Lets say we
  524. have the following bitmap. Our goal is to depict a flat gray surface with
  525. a raised gray square in the middle. (Each number represents the palette 
  526. index for that pixel:)
  527.  
  528.   Y
  529.     11111111111111111111111111111
  530.     11111111111111111111111111111
  531.     11111222222222222222222111111
  532.     11111311111111111111114111111
  533.     11111311111111111111114111111
  534.     11111311111111111111114111111
  535.     11111311111111111111114111111
  536.     11111311111111111111114111111
  537.     11111311111111111111114111111
  538.     11111311111111111111114111111
  539.     11111355555555555555554111111
  540.     11111111111111111111111111111
  541.     11111111111111111111111111111
  542. (0,0)                             X
  543.  
  544. Attach to this bitmap we have the following four normal_displacement
  545. structures:
  546.     {
  547.        palette_index      = 2;
  548.        normal_displace_x  = 0;
  549.        normal_displace_y  = 0.5;
  550.        color_to_use       = 1;
  551.     }
  552.     {
  553.        palette_index      = 3;
  554.        normal_displace_x  = -0.5;
  555.        normal_displace_y  = 0;
  556.        color_to_use       = 1;
  557.     }
  558.     {
  559.        palette_index      = 4;
  560.        normal_displace_x  = 0.5;
  561.        normal_displace_y  = 0;
  562.        color_to_use       = 1;
  563.     }
  564.     {
  565.        palette_index      = 5;
  566.        normal_displace_x  = 0;
  567.        normal_displace_y  = -0.5;
  568.        color_to_use       = 1;
  569.     }
  570.  
  571. Now what does this mean? 
  572. Let's say that color index 1 is just medium gray. So all pixels with
  573. index 1 will simply be medium gray.
  574. The structures appended means that color index 2 *IN THE BITMAP*
  575. should represent an edge pointing 'upwards' (we displace the normal
  576. vector's Y by 0.5 (normally this displacement would need to be trans-
  577. formed into the space of the polygon, but for our example, this is
  578. sufficient)). 
  579. Now since color index 2 maybe normally be GREEN, PURPLE or any other
  580. undesired color, the structure contains the member color_to_use. In
  581. our example, this is 1. This means that this pixel will ALSO be 
  582. medium gray - but with a DIFFERENT LIGHTING LEVEL.
  583. Similarily, color index 3 is an edge pointing 'to the left', 4 is an
  584. edge pointing 'to the right', and 5 is an edge 'pointing down'.
  585.  
  586. If we would have wanted another COLOR, but the same DISPLACEMENT, we
  587. would have needed another structure for that, e.g. if the lower half 
  588. of the bitmap would have been GREEN, we would have needed a few 
  589. different displacement-structures for green pixels as well.
  590.  
  591. How how should we make this efficiently? Well, remember the LightRemap
  592. vector we talked about earlier. This comes into play for us.
  593.  
  594. The overall color level of the polygon is 80%, remember?
  595.  
  596. So, lets make a COPY of the LightRemap vector for light level 80%. Lets
  597. put this into vector LightRemapCopy:
  598.  
  599.     unsigned char LightRemapCopy[256];
  600.     memcpy(LightRemapCopy, LightRemap[light_level]);
  601.  
  602. Now, lets walk through the normal_displacement structures. For each
  603. structure:
  604.  
  605.  
  606.     struct normal_displacement nrm;
  607.  
  608.     /* Displace the normal */
  609.  
  610.     displace_normal(normal, &new_normal, nrm);
  611.  
  612.     /* Calculate a new light level */
  613.  
  614.     new_light = calculate_light(new_normal);
  615.  
  616.     /* Now plug this NEW stuff into the REMAPPING VECTOR FOR THAT PALETTE
  617.        INDEX! */
  618.  
  619.     LightRemapCopy[nrm.palette_index] = LightRemap[new_lightlevel][nrm.color_to_use];
  620.  
  621.  
  622. After this is done, you simply SPLASH the polygon ONTO the screen, and use
  623. the 'LightRemapCopy' vector as your color-remapping vector! This will give
  624. you the correct bump-mapping shading for the whole bitmap WITHOUT ADDING
  625. ONCE SIGLE PROCESSOR CYCLE TO THE ACTUAL DRAWING OF THE TEXTURE ON SCREEN!
  626.  
  627. [To speed this even further one can skip the copy step, and make these
  628. changes directly into the LightRemap vector - and remember the original
  629. values and plug them back after the polygon is rendered!]    
  630.  
  631.  
  632. HOW ABOUT IT PEOPLE!? WHEN DO WE SEE THE FIRST DOOM-LIKE FREE-DIRECTION
  633. TEXTUREMAPPING GAME WITH BUMPMAPPING!? WHEN DO WE GET A VERSION OF
  634. DESCENT WHERE THE MINE *REALLY* LOOKS LIKE A MINE - WITH WOBBLY WALLS!!!
  635. HOW ABOUT TOMORROW!? I CANT WAIT!!!!
  636.  
  637.  
  638. Hope this wasn't completely unreadable!
  639.  
  640.  
  641. /Z
  642.  
  643. ! Hakan "Zap" Andersson ! Cheif software developer        ! Q: "0x2b | ~0x2b"!
  644. ! zap@lysator.liu.se    ! Genius Cad Software Scandinavia ! A: 42            !
  645. ! Phone: +46 16 96460   ! Fax: +46 16 96014               ! Whirled Peas.    !
  646.  
  647. --
  648. ! Hakan "Zap" Andersson ! Cheif software developer        ! Q: "0x2b | ~0x2b"!
  649. ! zap@lysator.liu.se    ! Genius Cad Software Scandinavia ! A: 42            !
  650. ! Phone: +46 16 96460   ! Fax: +46 16 96014               ! Whirled Peas.    !
  651.  
  652. Path: bcc.ac.uk!uknet!strath-cs!nntp0.brunel.ac.uk!sunsite.doc.ic.ac.uk!agate!newsxfer.itd.umich.edu!umcc.umich.edu!not-for-mail
  653. From: pnettle@umcc.umcc.umich.edu (Paul Nettle)
  654. Newsgroups: comp.graphics.algorithms
  655. Subject: S-Buffer FAQ is here!
  656. Date: 12 Mar 1995 05:26:53 -0500
  657. Organization: UMCC, Ann Arbor, MI
  658. Lines: 944
  659. Message-ID: <3jui9e$9ni@umcc.umcc.umich.edu>
  660. NNTP-Posting-Host: umcc.umcc.umich.edu
  661. Summary: Here's the s-Buffer FAQ
  662. Keywords: s-buffer
  663. X-Newsreader: NN version 6.5.0 #1 (NOV)
  664.  
  665. ~Newsgroups: rec.games.programmer
  666. ~Subject: S-Buffer FAQ is here!
  667. Summary: Here's the s-Buffer FAQ you've all been waiting for...
  668. Keywords: s-buffer faq
  669.  
  670. It's late, and I just finished the first pass.  I wasn't expecting to 
  671. have it done until late next week, but the response has been 
  672. unforgettable.  So, finally (yeah, right, only 2 days) here it is.  :)
  673.  
  674. Your comments are welcome.  If you have any, please leave them in my e-mail
  675. box (contact information enclosed in the FAQ).  Be careful, please make your 
  676. comments HELPFUL and POSITIVE.
  677.  
  678. - Paul
  679. ----------------------------------------------------------------------------
  680.           SS       BBBBBB   UU    UU FFFFFFFF FFFFFFFF EEEEEEEE RRRRRR  
  681.          SS        BB    BB UU    UU FF       FF       EE       RR    RR
  682.         SS         BB    BB UU    UU FF       FF       EE       RR    RR
  683.        SS          BB    BB UU    UU FF       FF       EE       RR    RR
  684.         SS    ---- BBBBBB   UU    UU FFFFF    FFFFF    EEEEE    RRRRRR  
  685.          SS   ---- BB    BB UU    UU FF       FF       EE       RR    RR
  686.           SS       BB    BB UU    UU FF       FF       EE       RR    RR
  687.          SS        BB    BB UU    UU FF       FF       EE       RR    RR
  688.         SS         BB    BB  UUUUUU  FF       FF       EE       RR    RR
  689.        SS          BBBBBBB    UUUU   FF       FF       EEEEEEEE RR    RR
  690.  
  691.  
  692.                          FFFFFFFF    AAAA      QQQQ      Original Creation:
  693.                          FF         AAAAAA    Q    Q        Mar. 10, 1995
  694.                          FF        AA    AA  QQ    QQ
  695.                          FF        AA    AA  QQ    QQ    Written By:
  696.                          FFFFF     AAAAAAAA  QQ    QQ       Paul Nettle
  697.                          FF        AA    AA  QQ    QQ
  698.                          FF        AA    AA  QQ    QQ    Version:
  699.                          FF        AA    AA  QQ  Q QQ       1.0.0
  700.                          FF        AA    AA   QQQQQQ 
  701.                          FF        AA    AA    QQQQQ 
  702.                                                     QQ
  703. -------------------------------------------------------------------------------
  704.  
  705. A note from the author:
  706.  
  707. This may be a bit wordy at times, so I'll try to keep it light with
  708. some of my humor -- you may not call it humor, but at least *I* think
  709. it's funny. :)
  710.  
  711. Where did they come from?  s-Buffers were developed for my game
  712. engine.  Though, since I don't believe that FAQs should have anything
  713. to do with commercialization, I won't even mention the name of this
  714. product.  Let's keep FAQs for their purpose -- FACTS.
  715.  
  716. The technique described in the next few hundred lines of text bears a
  717. close resemblance to some scanline rendering methods of which I
  718. believe span-buffering is one of them.
  719.  
  720. Actually, I had no idea what span-buffering was until I spoke to a
  721. gentleman named Chris Babcock who read the original posts about
  722. s-buffering.  He explained to me what span-buffering was, and as it
  723. turns out, the two techniques are similar, but do have some major
  724. differences (mainly the Insertion routine and segment manager).
  725.  
  726. The major difference is that s-Buffers are more so meant for use in
  727. game engines.  Engines that are meant to produce a high frame rate.
  728.  
  729. When I posted a note to the rec.games.programmer newsgroup about the
  730. fact that I had discovered a technique that would perform z-Buffers
  731. in software faster than hardware could, the response was, quite
  732. literally, overwhelming.  Most of which was plagued with polite
  733. suspicion and skepticism.  This alone told me that, at least, in the
  734. gaming community, this technique was unfamiliar territory.  So, I
  735. propose to you, the game developer, a new technique, called
  736. s-Buffering.
  737.  
  738. The particular implementation of this technique and it's name are
  739. both my own original creation, sparked by my own, sometimes wry
  740. thoughts.  This FAQ and the techniques contained within are also all
  741. my own creation.  This isn't to say that there isn't somebody else
  742. out there that has once thought about these techniques or variations
  743. thereof.  I have no control over their thoughts (and wouldn't want to
  744. -- Ewwwwww!).  I am quite confident, however, that this technique is
  745. not popular, not documented, and not in wide use within games.
  746.  
  747. If you find the information contained within these words, diagrams,
  748. and psudo-codes useful to any degree, please contact me.  If you use
  749. this technique in a product (commercial or otherwise) that you would
  750. not have done so if it had not been for this FAQ, please just drop
  751. me a line and let me know.  It's nice to hear those things. Hearing
  752. good things about efforts like this can only spark more.
  753.  
  754. I would also encourage anybody and everybody to dig in.  Play with
  755. this new technique.  It's not perfect, and the "Insertion" routine
  756. can be it's own line of discussion -- inventing many new algorithms
  757. for insertion can very possibly improve the performance of this
  758. technique in quantum leaps.  I would expect that if this technique
  759. gains any popularity, you'll see new insertion algorithms popping up
  760. in the next Siggraph Proceedings.
  761.  
  762. I'll be the caretaker of this document.  Please make all corrections
  763. through me by contribution (internet e-mail preferred).
  764.  
  765. If you do have other useful information related to this FAQ, !PLEASE!
  766. contact me.  I may very well want to include it in future versions of
  767. this FAQ.  Lets keep the InfoBahn alive, let's contribute to it, and
  768. share ideas.  We've got a hell of a lot of brains out there to draw
  769. from.
  770.  
  771.                                        Striving for speeeeed,
  772.  
  773.                                               Paul Nettle
  774.                                               Hot Wax Software
  775.  
  776.  
  777.  
  778.   "And now for something completely different.  A man with six-hundred
  779.         thirty-two-thousand four-hundred and forty-two big toes."
  780.  
  781. -------------------------------------------------------------------------------
  782. Q:  "How do I contact Paul?"
  783. -------------------------------------------------------------------------------
  784.  
  785. My vitals are:
  786. --------------
  787.  
  788. ADR:  7054 Foxthorn Dr.
  789.       Canton, MI  48187-3073
  790.  
  791. PH#:  (313) 981-6143 anytime
  792.  
  793. NET:  pnettle@umcc.umich.edu
  794. CIS:  72163,2442
  795.  
  796. Please, the name's "Paul", not "Mr. Nettle" :)  I hear "Mr. Nettle",
  797. and I turn around looking for an older man.
  798.  
  799. -------------------------------------------------------------------------------
  800. Q:  "OK, so what the heck is this all about?!!"
  801. -------------------------------------------------------------------------------
  802.  
  803. The movie "Weekend at Bernies" quotes this line:
  804.  
  805.     "He's got good form!"
  806.  
  807. I think that's one of my favorite quotes because, to me, it has a serious
  808. side other than it's humorous description of a corpse being dragged behind
  809. a moving boat by a rope, braining itself on buoys along the way.
  810.  
  811. "Good form" is "Elegance."
  812.  
  813. Webster defines "Elegant" as:
  814.  
  815.              "Marked by concision, incisiveness, and
  816.               ingenuity; cleverly apt and simple"
  817.  
  818. I couldn't have said it better, Noah.  Since elegance is a goal and
  819. not a destination, it's something that's to be strived for.  You
  820. can't reach pure elegance since there is always *something* that's
  821. more elegant (the human body, for example).
  822.  
  823. I hope you find s-Buffers to be "simply brilliant."  Not that I'm the
  824. brilliant one, mind you, I only discovered the technique.  The
  825. technique then just started to jump up and down, wave it's arms,
  826. blare sirens, and was later found to be screaming out it's advantages
  827. at me.  I'm just writing them down.
  828.  
  829. So, back to the question.  What's this all about?  Elegance,
  830. Simplicity and, of course, Speeeeeeeeeeeeeeeeeeeeeeed!
  831.  
  832. -------------------------------------------------------------------------------
  833. Q:  "Why s-Buffers?"
  834. -------------------------------------------------------------------------------
  835.  
  836. First, consider z-Buffers.  If you're not sure EXACTLY how they work,
  837. I'll explain briefly:
  838.  
  839.     A z-Buffer is a `second copy' of the screen-image.  It usually has the
  840.     same resolution.  This z-Buffer is initialized to the largest possible
  841.     value held by an UNSIGNED INT (for 16-bit INTs, that's 0xFFFF, and for
  842.     32-bit INTs that's 0xFFFFFFFF).
  843.  
  844.     During the drawing of polygons, the depth-value for each pixel
  845.     written has to be kept track of.  As you draw each pixel in a
  846.     polygon, you compare the depth-value of that pixel with the value in
  847.     the z-Buffer's corresponding pixel location.  If the z-Buffer value is
  848.     larger (farther away from the camera), then the pixel is plotted, and
  849.     the z-Buffer is updated with the new pixel's depth.  If the z-Buffer
  850.     value is lower (closer to the camera), then the pixel is not drawn
  851.     since it is hidden behind the existing pixel.
  852.  
  853. This is a very simple approach, and, depending on implementation, can
  854. offer pixel-perfect output.  However it is very slow and cumbersome.
  855.  
  856. Your code's inner-most loop (the code that's doing the drawing of the
  857. pixels) -- the same code you've hand-assembled for speed is being
  858. used to waste clock cycles as if they're as rich a commodity as
  859. Madonna CDs.  Even if that mean-ol-nasty z-Buffer says the pixel is
  860. hidden, you still need to track your deltas.  You still have to keep
  861. moving through your shades, texture maps, bump maps, colors, etc. 
  862.  
  863. So, you've had to add depth-tracking as well as a slow check to
  864. another buffer (possibly outside of your CPU's cache), and a
  865. conditional update to that buffer.  Whew...what a waste.  Such a
  866. waste in fact, that I plan to tell you how s-Buffers are not only
  867. FAST, but FASTER than z-Buffering in HARDWARE.  Keep reading, it's
  868. all in here.
  869.  
  870. Is there a more elegant solution?  I believe I have found one. I call
  871. it, simply, "s-Buffering."
  872.  
  873. -------------------------------------------------------------------------------
  874. Q:  "What are s-Buffers?"
  875. -------------------------------------------------------------------------------
  876.  
  877. `S-Buffers' or `segmented-Buffers' are the segmented representation
  878. of a z-Buffer.  They have three key elements:
  879.  
  880. 1.  An array of linked-lists (for this example)
  881. 2.  An insertion routine for inserting segments into the s-buffer
  882. 3.  A segment manager
  883.  
  884. These key Elements are outlined in the next few questions.  To
  885. continue with the answer to this question, you'll need to read the
  886. next three Qs.
  887.  
  888. -------------------------------------------------------------------------------
  889. Q:  "What is...KEY ELEMENT #1:  The s-Buffer itself"
  890. -------------------------------------------------------------------------------
  891.     
  892. For the sake of simplicity, I'll just use a linked list, later, this
  893. can be expanded upon.  It's the concept I'm trying to get across
  894. here.
  895.  
  896. Lets consider our trusty friend, the z-Buffer.  Pull out a single
  897. scanline from the z-Buffer and set it's representation on the kitchen
  898. table (watch out, don't set it in the little puddle of spaghetti
  899. sauce).
  900.  
  901. You now have a single plane.  Across the front is the x-direction
  902. through a scanline.  As you run your finger from the front to the
  903. back you're moving it along the depth axis (z-axis).  Now go wipe
  904. that spaghetti off your finger.
  905.  
  906.                                     Kitchen-table representation
  907.   x     z-Buffer                (Dots represent the depth of a pixel)
  908.  +-------------------+                 _______________________  
  909. y|-------------------|                /   (far away)         / n
  910.  |-------------------|               /                      / o
  911.  |-------------------|              /       ..             / i
  912.  |-------------------|             /       .  .           / t
  913.  |---------------------\          / ...  ..    .         / c
  914.  |-------------------| |         / .   ..               / e
  915.  |-------------------| |        /..            .       / r
  916.  |-------------------| |       /                      / i
  917.  |-------------------| |      /                . ..../ d
  918.  |-------------------| |     /    (close)       .   / /
  919.  +-------------------+ \--->/______________________/ Z
  920.                              (front -- x-direction)
  921.  
  922. When you look at the entire z-buffer, you're looking at a stack of
  923. these planes (that's not true at all, but I want you to think of it
  924. that way for now).  So, lets just consider the kitchen-table
  925. representation.
  926.  
  927. For each scanline in the z-Buffer, what you have is a list of depth
  928. values for each pixel.  Even though you may only have a single
  929. polygon on the screen, and the depth values in the z-Buffer that the
  930. polygon occupies are all the same (it's orientation is perpendicular
  931. to the z-axis), you still have to check each pixel, right?  Not any
  932. more.
  933.  
  934.