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

  1. you to think in terms of the segments that make up that polygon.  In
  2. our case, we'll stick with horizontal segments (though orientation
  3. isn't important).
  4.  
  5. When you scan convert a polygon, you break it up into horizontal
  6. SEGMENTS:
  7.  
  8.               /\             /--\            /----\           /------\          /--------\         /----------\         ------------\              --------\                   ----\
  9.  
  10. This is the key to s-Buffers.  Now you have a list of segments. 
  11. Rather that calling any low-level drawing routines to draw these
  12. segments, just simply call the s-buffer "insertion" routine.
  13.  
  14. No, this isn't going to be easy.  The "insertion" routine can be a
  15. beast all by itself.  But, there is a simple `starter' algorithm that
  16. I've included here.
  17.  
  18. When you're all done scan-converting your polygons and adding your
  19. segments to the s-Buffer, what do you have?
  20.  
  21. You have the ENTIRE screen in a well-formatted, nicely laid out
  22. fashion.  All the data is in one place, too.  The screen is laid
  23. out top-to-bottom and each scanline is left-to-right.  Can you think
  24. of any possible optimizations to take advantage of here?  I sure can.
  25.  
  26. Now it's time to call your 'modified' low-level drawing routine.  But
  27. first, a glance in the past.  In a lot of applications, the low-level
  28. drawing routine might look something like this:
  29.  
  30. void LowLevelDrawer( int x1, int x2, int y, int shade1, int shade1, int Color)
  31. {   [lots of setup code, clipping, etc.]   .   .   .   [draw the scanline]   .   .   .   [any exit code]   .   .   .   [return]
  32. }
  33.  
  34. This is for a single horizontal line of a polygon.  This routine will
  35. be getting called for each of the scanlines in a polygon, for every
  36. polygon.  Hmmm...I wonder how many times that startup code is gunna
  37. get run through...
  38.  
  39. See all them parameters (I count 6)?  That's just for a single piece
  40. of a single polygon for a simple gouraud shader.  What about a
  41. bump-mapper and texture-mapper?  Your stack will hate you for that,
  42. you're driving it nuts!  Push, Pop, Push, Pop, Push, Pop....
  43.  
  44. Here's a peek at your NEW and IMPROVED s-Buffer drawing routine.  It
  45. draws the ENTIRE screen:
  46.  
  47. void LowLevelDrawer( SBUFFER *Sbuf )
  48. {   while( !Done )   {      [do a little setup for this scanline]      .      .      .      [draw all segments for a single scanline (optimized code goes here)]      .      .      .      [next scanline]   }   [any exit code]   .   .   .   [return]
  49. }
  50.  
  51. When you leave here, you have a new screen, drawn to completion, ready
  52. for display.
  53.  
  54. One thing to note is that as you're drawing, if you watch it in slow-
  55. motion, you'll see that you're drawing each scanline from
  56. left-to-right, top-down.  Nice and clean.  I'll address this cool
  57. feature later...
  58.  
  59. -------------------------------------------------------------------------------
  60. Q:  "What is...KEY ELEMENT #2:  The insertion routine"
  61. -------------------------------------------------------------------------------
  62.  
  63. Due to the nature of it's possible complexity, the Insertion routine
  64. is the most important key element of the s-Buffer algorithm.  As
  65. such, it's also the most confusing routine to write.
  66.  
  67. Like some compression routines, any version of the de-compression
  68. routine can de-compress the data stored in compressed form.  However,
  69. each version of the compressing code is different.  Better
  70. compression can be obtained by better analysis.  The format of the
  71. output is still the same, but substantial gains can be made by better
  72. analysis in the compressing code.  I believe that the Insertion
  73. routine can also have this trait.  A better analysis on the part of
  74. the Insertion routine can simplify the job of the low-level drawing
  75. routines.
  76.  
  77. My point is that there is ALWAYS room for improvements to be made to
  78. any insertion routine.  The closer to perfect elegance, the better.
  79.  
  80. Take my rudimentary psudo-code for example.  The code relies on
  81. splitting segments that may not need splitting simply to keep the
  82. algorithm simple for explanation's sake.  It does the job, but it is
  83. wasteful.
  84.  
  85. Granted, the segments won't overlap (that would defeat the purpose of
  86. s-Buffering all together).  But on many occasions, a segment may be
  87. split into two segments side-by-side, causing the low-level drawing
  88. routines more effort, and wasting memory by using up more segments
  89. than are necessary.  And ultimately more time is spent in the drawing
  90. phase.
  91.  
  92. Please note that the following code may not be satisfactory for real
  93. use, but will certainly improve the performance of your application
  94. regardless.
  95.  
  96. You'll need these defined before we can start...
  97.  
  98.    Cur = Start of the existing segment list   New = Segment to be added to the list
  99.  
  100. First, the possible cases used in the psudo-code.  In the following
  101. cases, I use characters to represent pixels of the New segment being
  102. added (`n') and the current segment in the list being compared against
  103. (`c').  So in the following example:
  104.  
  105.          cccccccc            nnnnn
  106.  
  107. The Cur's right-half is overlapping with the entire New segment, and
  108. an intersection must be tested for.  These examples only represent
  109. where the start x coordinates and ending X coordinates fall, they in
  110. no way represent the depth values.
  111.  
  112. -----------------------------------------------------------
  113. Possible cases:
  114. -----------------------------------------------------------
  115.  
  116.       Case 1:         -> cccccc         ->        nnnnnn
  117.  
  118.       Case 2:         ->          cccccccc         -> nnnnnnnn
  119.  
  120.       Case 3a:         ->         ccccccccc         -> nnnnnnnnn
  121.  
  122.       Case 3b:         ->        cccccccccc         -> nnnnnnnnnn
  123.  
  124.       Case 3c:         ->         ccccccccc         -> nnnnnnnnnnnnnnnnn
  125.  
  126.       Case 3d:         ->    ccccccccccc         -> nnnnnnnnnnnnnnnnn
  127.  
  128.       Case 3e:         ->       c         -> nnnnnnnnnnnnn
  129.  
  130.       Case 3f:         ->             c         -> nnnnnnnnnnnnn
  131.  
  132.       Case 4a:         -> ccccccccccccc         ->   nnnnnnnn
  133.  
  134.       Case 4b:         -> ccccccccccccc         ->   nnnnnnnnnnn
  135.  
  136.       Case 4c:         -> ccccccccccccc         ->             n
  137.  
  138.       Case 4d:         -> ccccccccccc         ->   nnnnnnnnnnn
  139.  
  140.       Case 4e:         -> ccccccc         ->       nnnnnnn
  141.  
  142.       Case 5:         -> cccccccccccccc         -> n
  143.  
  144.       Case 6:         -> cccccccccccccc         -> nnnnnnn
  145.  
  146.       Case 7:         -> c         -> nnnnnnnnnnnnn
  147.  
  148.       Case 8:         -> ccccccc         -> nnnnnnnnnnnnnn
  149.  
  150.       Case 9:         ->       c         ->       n
  151.  
  152.       Case 10:         -> cccccccccccccc         -> nnnnnnnnnnnnnn
  153.  
  154. -----------------------------------------------------------
  155. Psudo-code for a rudimentary insertion routine begins here
  156. -----------------------------------------------------------
  157.  
  158.    if (New is entirely off-screen)      return;     // Make sure segment is sorted left-right
  159.  
  160.    if (the list is empty)   {      just add it   }
  161.  
  162.    while(we still have entries in the list)   {      // Case 1      if (New.x1 > Cur.x2)      {         // Next Cur         // Continue Checking      }      // Simply off-screen? (do this check again 'cause New is changin')      else if (New.x1 >= ScreenResX || New.x2 < 0)      {         // Return      }      // Case 2      else if (New.x2 < Cur.x1)      {         // Add New before Cur;         // Return      }      // Case 3      else if (New.x1 < Cur.x1)      {         // Split New at Cur's left-most point         // Add New's left-half before Cur         // Replace New with it's right half         // Continue checking      }      // Case 4      else if (Cur.x1 < New.x1)      {         // Split Cur at New's left-most point         // Add Cur's right-half after Cur         // Next Cur         // Continue checking      }      // Cases 5 & 6      else if (Cur.x2 > New.x2)      {         // Case 5         if (New.x1 == New.x2)         {            // If New's starting Z is behind Cur's starting Z, just return            // Split Cur at New's right-most point + 1            // Replace Cur with it's right half            // Add New before Cur            // Return         }         else         // Case 6         {            // Split Cur at New's right-most point + 1            // Replace Cur with it's right half            // Call IntersectSplit for New and Cur's left-half            // Return         }               }      // Cases 7 & 8      else if (Cur.x2 < New.x2 )      {         // Case 7         if (Cur.x1 == Cur.x2)         {            // Split New at Cur's right-most point + 1            // If Cur is behind New's left-half, replace Cur with New's left-half            // Continue checking using New's right half         }         // Case 8         else         {            // Split New at Cur's right-most point + 1            // Remove Cur from the list            // Call IntersectSplit for Cur and New's left-half            // Continue checking using New's right-half         }      }      // Cases 9 & 10      else      {         // Case 9         if (Cur.x1 == Cur.x2)         {            // If Cur is behind New, replace Cur with New            // Return         }         // Case 10         else         {            // Remove Cur from the list            // Call IntersectSplit for Cur and New            // Return         }      }   }
  163.  
  164.    // Just add it to the end   // Return
  165.  
  166. -----------------------------------------------------------
  167. Psudo-code for a rudimentary insertion routine ends here
  168. -----------------------------------------------------------
  169.  
  170. This routine makes use of a few linked-list management routines and a
  171. routine I called IntersectSplit().
  172.  
  173. IntersectSplit() simply finds the intersection of two lines that
  174. start at the same x coordinate, end at the same x coordinate and
  175. returns the order in which the two segments appear from left->right,
  176. unless one segment is totally behind the other, in which case it
  177. simply returns the single segment.
  178.  
  179. Note that your splitting code must be very accurate.  Not making an
  180. accurate splitting routine or an accurate IntersectSplit() routine
  181. may cause artifacting in the foreground polygons caused by polygons
  182. hidden behind them.
  183.  
  184. Besides, if your intersection and splitting routines are accurate,
  185. then you can expect more accurate results than a 16-bit z-Buffer.
  186.  
  187. For more information on inserting segments into the list, see the Q
  188. titled: "What are some ways to improve the Insertion routine?"
  189.  
  190. -------------------------------------------------------------------------------
  191. Q:  "What is...KEY ELEMENT #3:  The segment manager"
  192. -------------------------------------------------------------------------------    
  193. The segment manager is simply a reservoir of pre-allocated segment
  194. structures.  These segment structures hold the actual segments that
  195. are linked together in the s-Buffer's linked lists.
  196.  
  197. If you were to malloc() and free() segments as you needed them, you
  198. run the risk of fragmenting memory, and running out of larger chunks
  199. of memory for other parts of your application.  Not to mention the
  200. extra overhead of the malloc() and free() routines.
  201.  
  202. To solve this problem, a segment manager is needed.  It keeps a
  203. reservoir of segments, and gives you free segments as you ask for
  204. them.  This may be done in any number of ways.  It's also very fast.
  205.  
  206.