home *** CD-ROM | disk | FTP | other *** search
/ Chip 2003 August / Chip_2003-08_cd1.bin / oddech / maelstrom / maelstrom.exe / Maelstrom / Docs / Technical_Notes-v1.0 < prev    next >
Text File  |  1999-12-07  |  15KB  |  261 lines

  1.  
  2. The first challenge was figuring out how to get the Macintosh resources
  3. into a form that could be dissected from within UNIX.  First, I tried
  4. copying files over from "Executor", a Linux based X11 Macintosh emulator.
  5. Unfortunately, Executor wraps the resource fork in a special header, 
  6. rendering it difficult to decipher.  The solution was to BinHex the files
  7. on a Macintosh, and then use the Linux utility 'mcvert' to extract the
  8. resource fork from the archive.  I also used the ALPHA 'hfs' filesystem
  9. for Linux, but got a file that was different than the one 'mcvert' unwrapped,
  10. so I didn't trust it.
  11.  
  12. The second problem was finding out the format of the Macintosh resource
  13. fork, so that the resources could be extracted.  This was solved by going
  14. to the bookstore and looking it up in the "Inside Macintosh" series 
  15. published by Addison and Wesley.  This series has detailed specifications
  16. on the internals of the Macintosh operating system.  This was pointed out
  17. by Andrew Welch, the author of Maelstrom.
  18.  
  19. The next tricky part was after I had written code to look at the various
  20. parts of the resource fork of the Maelstrom game, the values seemed way
  21. too large to be correct.  The offset for the Resource Map was 4 MegaBytes
  22. into the file, when the file was only .5 Megabyte large.  This was caused
  23. by the fact that Linux is little-endian, while Macintoshes are big-endian.
  24. This was solved by running ntohl() and ntohs() on the offset/value fields 
  25. of the resource fork.
  26.  
  27. I wrote a utility to list and extract all resources from a Macintosh binary 
  28. resource fork.
  29.  
  30. I extracted the sprites from the Maelstrom resources, and attempted to view
  31. them in a VGA console, using custom-crafted bitmap viewing utilities. 
  32. The sprites appeared, but appeared in psychedelic colors.  Evidently, 
  33. the colormap was wrong.  I played with the colormap, setting it to the 
  34. standard colormap, which didn't help.  I used ResEdit on the Macintosh
  35. to look into the ColorMap resources of Maelstrom.  ResEdit has a colormap
  36. editor that can display and set the colormap of a Macintosh application.
  37. I used it to find out the proper colormap for displaying Maelstrom sprites.
  38.  
  39. Next was the sounds.  I used the resource parsing code to extract the
  40. sound resources, and then used "Inside Macintosh: Sounds" to look at
  41. the format of the sounds used in Maelstrom.  It turns out they are all
  42. either format 1 or format 2 sampled sound bites which can be directly
  43. played on /dev/dsp under Linux.
  44.  
  45. Then, I worked with the colormap resources.  I found the format for the
  46. colormap resources in "Inside Macintosh: Imaging with QuickDraw"
  47. I used this description to write a utility that converts a binary clut
  48. resource into a C header file describing the colormap.
  49.  
  50. Next was the PICT resources: the title screen, credits, and info screens.
  51. I looked at the "Inside Macintosh: Imaging with QuickDraw" for the PICT
  52. resource format, and freaked when I saw it was a collection of opcodes 
  53. for the QuickDraw routines.  Then I found that the first part of the 
  54. Maelstrom PICTs seemed to be a colormap.
  55.  
  56. The solution to the problem of understanding PICT resources was going
  57. to     ftp.sunet.se:/pub/mac/mirror-umich/graphics/graphicsutil
  58. and retrieving the programs "GraphicConverter" and "pictmanagementutils"
  59. I used these programs to extract the PICT resources and save them
  60. as raw PPM files.  Then I wrote a simple program to display raw PPM
  61. files on the VGA console.
  62.  
  63. I copied the raw PPM files from the macintosh disk to the Linux system
  64. by mounting it with the 'hfs' module loaded, and then doing a straight
  65. copy of the data portions of the files.  The 'hfs' module for Linux
  66. was written by Paul H. Hargrove, hargrove@sccm.stanford.edu
  67.  
  68. I then translated PPM files to XPM format with xv, performing image 
  69. enhancement.  I then wrote a utility to merge the colormaps of the
  70. pixmaps and perform a check to see if all the colors are in the 
  71. standard colormap of Maelstrom.  They are, so I can convert them all
  72. into raw pixel data -- a form more suitable for blitting to the screen.
  73. This form also takes less disk-storage than either the raw PPM data
  74. or the textual XPM data.
  75.  
  76. When loading up Maelstrom, I thought it might be nice to create a color
  77. icon for the window manager to use.  I created it by resizing and 
  78. simplifying the main title image of Maelstrom with xv and then turning
  79. it into a standard colormap xpm icon.  The program 'xboing', written by
  80. Justin C. Kibell, jck@citri.edu.au, creates its own color icon.  I looked
  81. at the code for this, and used the technique with Maelstrom.
  82. The technique is basically to create a pixmap on the X server and then
  83. tell the window manager to use it for an icon.  This must be done before
  84. the window is mapped on the screen, otherwise the window manager ignores
  85. its hint.
  86.  
  87. Then, I tried allocating a private colormap for displaying Maelstrom
  88. graphics.  Well, that works, and the code is still in place to support
  89. that, but the Maelstrom colormap turns the rest of the screen flourescent
  90. colors.  I looked at the code for 'xv', written by John Bradley, and
  91. found a good algorithm for mapping the colors needed to the existing 
  92. color palette.  I have ideas for more advanced algorithms of color 
  93. allocation, but the direct mapping method seems to work well enough for
  94. me.  One problem with this approach is that once Maelstrom is started, 
  95. it locks all the colors in the colormap, preventing applications from 
  96. allocating new shared color cells.
  97.  
  98. One of the primary factors affecting game playability is the speed of
  99. real-time animation.  One of the best ways to achieve high-speed animation
  100. in the X11 environment is to use the MITSHM extension.  The standard 
  101. method of working with X shared memory is to create a small (300x200) 
  102. shared image, manipulate the image and then "put" it on the server 
  103. with XShmPutImage().  Reasonable speed can be achieved with this method, 
  104. however this is not fast enough for the needs of Maelstrom.  Maelstrom 
  105. is played in a 640x480 window, and copying a 2.5 megabyte image into 
  106. the screen contents would take way too long.  I get around this problem 
  107. by creating a shared Pixmap, and making it the background of the window.  
  108. Background pixmaps are tiled, so the background Pixmap has to be exactly 
  109. the same size as the window.  Now, the background of the window can be 
  110. directly manipulated, and refreshed with XClearArea().  This approach seems 
  111. to be much faster than working with an XImage, and blitting it to the window.
  112. One side-advantage of this approach is that graphics can be drawn in
  113. the foreground of the window, without affecting the animated background.
  114. Another benefit of this approach is that a redraw on an expose event 
  115. simply consists of an XClearWindow() call, without having to redraw
  116. the entire contents of the graphics window.
  117.  
  118. Next was sound mixing.  I used a simplified version of the sound mixing
  119. code in 'sfxserver', written by Terry Evans, tevans@cs.utah.edu, for
  120. sound mixing.  Each loop of the mixer compiles multiple channels of
  121. the sound mixer into a single chunk of sampled data which is sent to 
  122. the audio device.  The original idea was to have a continuous loop,
  123. first checking for input, and then writing a chunk of sound (or silence)
  124. to the sound device.  This resulted in slow response time, because the
  125. a sound event had to wait the entire cycle of compiling the sound channels
  126. until it could be acted upon.  I modified the original concept to support
  127. asynchronous input.  Now, the loop has been simplified to a simple continuous
  128. play of the sound channels, but at any time during the compilation of a
  129. sound chunk, new sound data can be placed into channels, or removed from 
  130. the mixing channels.  This allows nearly instantaneous mixing of sound effects,
  131. in response to sound events.
  132.  
  133. I decided to run the sound server as a separate process from Maelstrom.
  134. The sound server has to continuously play sampled data (sound or silence)
  135. and respond instantly to sound events.  Maelstrom has to continuously
  136. update animated graphics.  I thought the best way to perform both of these
  137. functions simultaneously was to do them in separately running processes,
  138. communicating through a private UNIX domain socket.
  139.  
  140. PROBLEM: After we allocate a full colormap, Maelstrom wants to allocate
  141. other colors.  We have completely filled the colormap, yet... what can
  142. we do?  We can completely take over the root window colormap...
  143. That's not polite, but what else can we do?
  144.  
  145. PROBLEM: After we allocate a full colormap, Maelstrom wants to allocate
  146. other colors.  We have completely filled the colormap, yet... what can
  147. we do?  We can completely take over the root window colormap...
  148. That's not polite, but what else can we do?
  149. ... Try to allocate the closest color in the colormap and see what we
  150. come up with...
  151.  
  152. The best thing might be to grab a copy of the current screen and see 
  153. what colors are actually in use, and reserve all the rest.  Hmmm??
  154.  
  155. Mapping to the closest color already in the colormap seems like it
  156. works.  It's not perfect, but it is polite, and prevents lots of 
  157. gyrations trying to blend a private and public colormap.
  158.  
  159. I tried to allocate all the colors, and then map the ones that were
  160. left.  That didn't work.  The colormap was left full, and the colors
  161. that needed to be mapped didn't have any colors to map to.
  162.  
  163. I wrote a routine to convert the Macintosh color icon resource into 
  164. both XPM and Linux-Maelstrom sprite format.
  165.  
  166. The next challenge is the fonts...
  167.  
  168. I found a program called 'mac2bdf' that converts Macintosh FONT resources
  169. into UNIX bdf fonts.  I'm using the information here to learn about the
  170. Macintosh font format.  My plan is to write a "Font Server" that can 
  171. translate text, given font and pointsize, into a blittable Sprite.
  172.  
  173. The font server works nicely. :)
  174.  
  175. Next challenge is how to blit the sprites.  The best way is probably to 
  176. keep an off-screen buffer and save the area behind the sprites to this
  177. buffer.  When the sprite is removed, the screen is restored from this
  178. off-screen buffer.  If this "back-buffer" is too big, we can possibly
  179. save a shred of the background in the Sprite data structure and use that
  180. to restore the background.  The off-screen buffer is a more robust
  181. implementation in that it allows graphics pen styles, such as XOR, OR,
  182. etc.
  183.  
  184. Saving the back-buffer in the sprites is a bad idea because two or
  185. three sprites may go over single point, and each would have a different
  186. idea of what the background looks like.
  187.  
  188. The standard way of blitting sprites is to cycle through the sprite,
  189. checking against the mask, and put the pixel if it is "live".  This 
  190. requires a loop iteration for each pixel in each scanline.  This can
  191. be fairly time consuming.  A much faster method was outlined by Andrew
  192. Welch, and that is to compile the sprite into a continuous stream of
  193. pixels, arranged by a series of opcodes describing where the next pixel
  194. should go.
  195.  
  196.     By compiling the sprites, I achieved a speed up of 45% 
  197. over the old sprite/mask method.  An additional advantage is that
  198. the compiled sprites take much less storage space -- an average of
  199. 50% less space.
  200.  
  201. Sound:  Under certain conditions, Maelstrom wants to wait until a
  202. sound is completed.  In the original code, Maelstrom looped, polling
  203. the server, waiting for the sound to finish playing.  Unfortunately,
  204. polling the server interrupts the write() which must be restarted.
  205. In continuous polling, the write keeps having to be restarted and
  206. the sound never finishes playing.  If the sound status is kept by
  207. the Maelstrom process, with the sound server sending periodic updates,
  208. then the sound server can be greatly simplified and will not be
  209. continuously polled.  This is the new approach I will take.
  210.  
  211. This approach works well enough.  The sound server takes very little
  212. CPU time, as most of it's time is spent waiting for device writes to
  213. complete.  One of the challenges I ran into, in writing interactive
  214. sound code, is you need quick response time to user events.  For example,
  215. if the player presses the "Thrust" key, you need to be able to start the
  216. thrust sound, and stop it almost immediately.  One of the parameters 
  217. you can tune to get this is the fragment size of the sound-card writes.
  218. If you have a large fragment size, then large amounts of sound are
  219. quickly processed and sent to the sound device.  Once it is in the 
  220. sound device buffer, the sound cannot be cancelled.  However, as the
  221. size of the audio-fragment decreases, you begin to have better control
  222. over the timing, starts and stops of the interractive sound.
  223.  
  224.     For Maelstrom, I found that a fragment size of 1024 (2^10)
  225. was about right for good response time without instantaneous response.
  226.  
  227.     Instantaneous response is not, in general, a good way to
  228. program an interactive game, such as Asteroids or Maelstrom.  The
  229. player expects to see an organic, analog universe, not one that 
  230. reacts instantly to conditions.  In the real world, cause and effect
  231. relationships are not instantaneous or even immediately obvious.
  232. For example, when a jet passes overhead, the sound of the jet trails
  233. the sight of the jet by a good distance.  Another example is acceleration.
  234. When gravity takes hold of a body, the body gradually accelerates to a 
  235. maximum velocity -- it doesn't immediately reach its terminal velocity.
  236. These organic and semi-random natural conditions can be simulated in
  237. the computer using random vectors and calculated delays to produce an 
  238. environment more suitable for interactive play.
  239.  
  240.     Timing is a crucial part of interactive games.  In Maelstrom,
  241. large portions of code are devoted to boundary condition detection and
  242. timing conventions.  At each frame update, all sorts of time-dependent
  243. conditions are checked -- how much time has it been since the last
  244. screen update, is it time for a keyboard check, has the shield ran out,
  245. etc, etc, all these are crucial components to a well-rounded game.
  246. If things are handled too quickly, the player no longer enjoys playing
  247. the game -- there is no time to repond.  If things are handled too 
  248. slowly, the game appears sluggish and is no longer fast-action fun.
  249.  
  250.     I'm working on clipping.  Right now, clipping is handled by
  251. dynamically recompiling the compiled sprite when it's near the edge of
  252. the clipping rectangle.  This is okay when there are only a few sprites
  253. on the screen, but when there are more than about ten or twenty, this
  254. becomes too slow.
  255.     I'm going to try to add code that will detect if the sprite
  256. is near the edge of the clipping rectangle.  If it is, it will call
  257. the Blit_Sprite() function, instead of the Blit_CSprite() function.
  258. It is faster to clip a pixmap than it is to recompile a compiled
  259. sprite, I think.
  260.  
  261.