Difference between revisions of "Medusa effect"

From DoomWiki.org

[unchecked revision][checked revision]
(Why medusa occurs)
m (Fix up some minor issues.)
Line 1: Line 1:
 
The '''Medusa effect''' occurs when there is more than one [[wall patch|patch]] occupying the same column in any middle [[wall texture|texture]] of a two-sided [[linedef]] that is visible in the display window. Due to a game engine limitation, these will not display properly. The area where the middle texture would be displayed is instead a series of multicolored horizontal lines. This 'wall' of multicolored lines appears to extend infinitely into the floor. Moving close to the wall will make the framerate in [[vanilla Doom]] slow to a crawl and make play nearly impossible until the offending wall is out of view.
 
The '''Medusa effect''' occurs when there is more than one [[wall patch|patch]] occupying the same column in any middle [[wall texture|texture]] of a two-sided [[linedef]] that is visible in the display window. Due to a game engine limitation, these will not display properly. The area where the middle texture would be displayed is instead a series of multicolored horizontal lines. This 'wall' of multicolored lines appears to extend infinitely into the floor. Moving close to the wall will make the framerate in [[vanilla Doom]] slow to a crawl and make play nearly impossible until the offending wall is out of view.
  
The effect is due to the way multi-patch textures are loaded in memory. The game renders all middle textures on two sided walls with the drawing function as sprites, which is R_DrawMaskedColumn(). This function correctly handles each column and the posts that are contained within the column. The problem does not exist in this function, but instead exists in the code that loads the texture into memory.
+
To understand why this occurs, a basic understanding of the [[Doom rendering engine]] is required. The effect is due to the way multi-patch textures are loaded in memory. The game renders all middle textures on two sided walls with the same drawing function as sprites, which is R_DrawMaskedColumn(). This function correctly handles each of the columns along with the posts that make up the column. The problem does not exist in this function, but instead exists in the code that loads the texture into memory.
  
When a normal wall is drawn, the function R_DrawColumn() is used. This column drawer is only capable of drawing single post patches. When drawing it takes the height of the actual texture and uses that instead of the column data. This limitation also causes the [[Tutti-frutti effect]]. Whereas R_DrawMaskedColumn() takes into consideration the column data.
+
When a normal wall is drawn, the function R_DrawColumn() is used. When drawing it uses the height of the texture and disregards any offset and length information contained in posts and does not even bother to check for other posts to draw. This limitation also causes the [[Tutti-frutti effect]]. However, R_DrawMaskedColumn() takes into consideration the post offset and length.
  
When a texture needs to be loaded into memory, the function R_GenerateComposite() is called. This function loads the texture data from the WAD. When loading a texture, it runs through a list that was determined at load time. The list contains the number of patches that exist on each column, or rather the number of lumps for each column. When a column that has multiple patches for a column is encountered, the game then calls R_DrawColumnInCache(). The problem arises in this function. The function R_DrawColumnInCache() takes a shortcut, it renders the column of the patch on top of the existing cache without checking the end of the column and does not update the existing column data. Thus, the height of the column is never changed nor is the offset. Then once the game draws the texture with R_DrawMaskedColumn() it uses the invalid column height. In short, after drawing a column the function checks to see if there are no more columns afterwards. It will continue to draw columns of varying length until the byte marker 255 is hit, which it will then terminate drawing. Since patches vary in actual data this marker may be hit early reducing the slow down or it may be hit later thus causing extreme slow down. The drawing function eventually would exceeds the bound of the texture data and start to draw data from other memory positions until it reaches the end condition. Due to this, the distorted texture that extends through the floor may animate. It is possible for the game to enter an infinite loop or eventually crash provided all of the conditions are perfect and memory is laid out in a way to cause such an event.
+
When a texture needs to be loaded into memory, the function R_GenerateComposite() is called. This function loads the texture data from the WAD. When loading a texture, it runs through a list that was determined at load time. The list contains the number of patches that exist on each column, or rather the number of lumps for each column. When a column that has multiple patches is encountered, the game then calls R_DrawColumnInCache(). The problem arises in this function. The function R_DrawColumnInCache() takes a shortcut, it renders the column of the patch on top of the existing cache without checking the end of the column and does not update the existing post (offset and length) data. Thus, the height nor the offset of the column/post is never updated. R_DrawMaskedColumn() then uses the invalidated information. In short, after drawing a post the function checks to see if there are more posts afterwards (the value 255 means there are no more). It will continue to draw post of varying length and offsets until the byte marker 255 is hit, which it will then terminate drawing.
 +
 
 +
Since the actual data for a texture varies, the end marker may be found early thus causing little or no slow down, or it could be found late where it causes an extreme slow down to occur. It is theoretically possible for the game to crash or freeze indefinitely if the conditions are correct, however there is a 0.39% chance of landing on an end marker.
  
 
The effect is named for [[Wikipedia:Medusa|Medusa]], the creature in Greek mythology who had live snakes for hair and was so ugly that a single glance at her would turn the beholder to stone.
 
The effect is named for [[Wikipedia:Medusa|Medusa]], the creature in Greek mythology who had live snakes for hair and was so ugly that a single glance at her would turn the beholder to stone.

Revision as of 22:21, 21 February 2012

The Medusa effect occurs when there is more than one patch occupying the same column in any middle texture of a two-sided linedef that is visible in the display window. Due to a game engine limitation, these will not display properly. The area where the middle texture would be displayed is instead a series of multicolored horizontal lines. This 'wall' of multicolored lines appears to extend infinitely into the floor. Moving close to the wall will make the framerate in vanilla Doom slow to a crawl and make play nearly impossible until the offending wall is out of view.

To understand why this occurs, a basic understanding of the Doom rendering engine is required. The effect is due to the way multi-patch textures are loaded in memory. The game renders all middle textures on two sided walls with the same drawing function as sprites, which is R_DrawMaskedColumn(). This function correctly handles each of the columns along with the posts that make up the column. The problem does not exist in this function, but instead exists in the code that loads the texture into memory.

When a normal wall is drawn, the function R_DrawColumn() is used. When drawing it uses the height of the texture and disregards any offset and length information contained in posts and does not even bother to check for other posts to draw. This limitation also causes the Tutti-frutti effect. However, R_DrawMaskedColumn() takes into consideration the post offset and length.

When a texture needs to be loaded into memory, the function R_GenerateComposite() is called. This function loads the texture data from the WAD. When loading a texture, it runs through a list that was determined at load time. The list contains the number of patches that exist on each column, or rather the number of lumps for each column. When a column that has multiple patches is encountered, the game then calls R_DrawColumnInCache(). The problem arises in this function. The function R_DrawColumnInCache() takes a shortcut, it renders the column of the patch on top of the existing cache without checking the end of the column and does not update the existing post (offset and length) data. Thus, the height nor the offset of the column/post is never updated. R_DrawMaskedColumn() then uses the invalidated information. In short, after drawing a post the function checks to see if there are more posts afterwards (the value 255 means there are no more). It will continue to draw post of varying length and offsets until the byte marker 255 is hit, which it will then terminate drawing.

Since the actual data for a texture varies, the end marker may be found early thus causing little or no slow down, or it could be found late where it causes an extreme slow down to occur. It is theoretically possible for the game to crash or freeze indefinitely if the conditions are correct, however there is a 0.39% chance of landing on an end marker.

The effect is named for Medusa, the creature in Greek mythology who had live snakes for hair and was so ugly that a single glance at her would turn the beholder to stone.

Sources

This article is based on information in the Unofficial Doom Specs.