-
Notifications
You must be signed in to change notification settings - Fork 1
Dungeon Graphics Renderer
This page describes the SPS 439 source data used to turn dungeon map locations into the 128×76 first-person display. It is concerned with the game data and drawing routines, rather than any particular editor or preview implementation.
The renderer does not calculate dungeon perspective from a general 3D formula. It uses two fixed coordinate systems:
- 19 player-relative view cells describe the map locations which may contribute to the current view;
- 28 wall-face slots describe the possible projected wall surfaces within the 128×76 viewport.
Dungeon_ViewCell_RelativeCoordinates.positions contains four groups of 19 signed X/Y pairs, one group for each player facing. Each word stores signed X in its high byte and signed Y in its low byte.
For each visible map cell, Dungeon_ViewCell_WallFaces.lookup supplies four bytes. These are the N/E/S/W candidate wall-face slots for that cell; $FF means that the face is unavailable from that view cell.
The source first scans all 19 view cells. It then traverses the cells from farthest to nearest to combine two 32-bit masks per cell:
-
Dungeon_ViewCell_VisibleFaces.flagscontributes wall faces which the cell can make visible; -
Dungeon_ViewCell_Occlusion.flagsremoves faces hidden by an opaque location.
The accumulated visible mask is finally ANDed with the accumulated occlusion mask. The surviving view cells are then drawn in the source traversal order. This is why a collection of individually plausible wall sprites can still be wrong if the view-cell masks or face order are ignored.
Dungeon_ViewCell_CentredSlots.lookup provides one centred projected slot for features such as pillars, beds, pits and pads. Nineteen bytes are used; the twentieth byte is spare/padding. $FF means that no centred sprite is available in that view cell.
A type-1 stone-wall location tests all four of its candidate faces. The visibility and occlusion masks decide which faces survive. Its direction field does not limit the stone block to one wall surface; that direction selects the one face on which a shelf, sign, switch or socket can appear.
A type-2 wooden-wall location is different. Its first map byte contains four independent two-bit N/E/S/W states:
| Value | Result |
|---|---|
| 0 | no wooden wall or door |
| 1 | solid wooden wall |
| 2 | open wooden doorway |
| 3 | closed wooden door |
The renderer therefore draws only the sides actually present in those four fields.
The 28 wall-face slots are translated to source pictures by two byte tables:
-
GFX_Main_Wall_SpriteTableselects the stone-wall picture for each slot; -
GFX_WallComponent_SpriteMirrorTableselects wall-component, wooden-wall, door and stair pictures. Bit 7 requests the mirrored path and the low seven bits select the picture.
Centred dungeon features use GFX_CentredDungeonComponent_SpriteMirrorTable. Its first 19 entries correspond to the 19 view cells; bit 7 again selects the mirrored path. The twentieth byte is not read by the 19-cell traversal.
Main stone-wall rows always pass through the game's bit-reversal lookup and are written right-to-left. The raw ST-format artwork therefore needs this horizontal reversal even though GFX_Main_Wall_SpriteTable contains ordinary picture numbers. Omitting it reverses the masonry perspective on the near left and right walls.
Two separate 28-byte flag tables select the ordinary writer or the perspective edge/cropping path:
-
GFX_Main_Wall_DrawTransformFlagsis used for stone-wall faces; -
GFX_WallComponent_DrawTransformFlagsis used for wall components, wooden walls, doors and stairs.
The tables are nearly, but not completely, identical: slots 6 and 18 differ. They should therefore remain separate resources. Bits 0 and 2 request the extra edge pass before or after the centre, while bit 1 selects the perspective centre path. The low three-bit value indexes the corresponding GFX_*_PerspectiveTrimLookup; that lookup returns 0, 1 or 2 source word-columns to trim while drawing the perspective edge.
For the nearest central component slots, source pictures 12–15 represent one half of the finished image. The renderer draws that half and then its reflected partner at 128 - x - width.
Dungeon artwork is not self-describing. A drawable resource commonly consists of:
| Resource | Purpose |
|---|---|
.gfx |
Raw Atari ST four-plane pixels. |
.offsets |
Big-endian byte offsets selecting pictures within the .gfx block. |
.positions |
Packed X, Y, width and height records. For these wall resources each four-byte record is x / 2, y, width in 16-pixel words minus 1, height minus 1. |
.colours |
Optional palette/remapping data used by the component. |
Gem sockets are a useful example. Main_Slots.gfx supplies the pixels, Main_Slots.offsets selects the socket picture, Main_Slots.positions positions and sizes it for each wall-face view, and Main_Slots.colours supplies its colour choices. All four resources are already identified in segments.xlsx.
The shared face tables described above do not replace the socket position table. They determine which projected wall face is being drawn and which transformation that face uses; Main_Slots.positions then provides the component's exact rectangle within that projected face. The same arrangement applies to signs and switches.
These shared rules belong under gfx-data/ and should initially use extract_only. They are useful for decoding, validation and future renderer expansion, but they are not ordinary editable artwork.
| Memory address | SPS 439 file position | Size | Proposed label | Extracted file |
|---|---|---|---|---|
$0095C0 |
$0923C |
$14 |
GFX_CentredDungeonComponent_SpriteMirrorTable |
gfx-data/Dungeon_CentredComponents.lookup |
$00B2BA |
$0AF36 |
$1C |
GFX_Main_Wall_SpriteTable |
gfx-data/Dungeon_MainWall_SpriteSelection.lookup |
$00B43C |
$0B0B8 |
$1C |
GFX_WallComponent_SpriteMirrorTable |
gfx-data/Dungeon_WallComponents.lookup |
$00B4C4 |
$0B140 |
$1C |
GFX_WallComponent_DrawTransformFlags |
gfx-data/Dungeon_WallComponent_DrawTransform.flags |
$00B558 |
$0B1D4 |
$08 |
GFX_WallComponent_PerspectiveTrimLookup |
gfx-data/Dungeon_WallComponent_PerspectiveTrim.lookup |
$00B64A |
$0B2C6 |
$1C |
GFX_Main_Wall_DrawTransformFlags |
gfx-data/Dungeon_MainWall_DrawTransform.flags |
$00B6F2 |
$0B36E |
$08 |
GFX_Main_Wall_PerspectiveTrimLookup |
gfx-data/Dungeon_MainWall_PerspectiveTrim.lookup |
$00B8AE |
$0B52A |
$98 |
Dungeon_ViewCell_RelativeCoordinates |
gfx-data/Dungeon_ViewCell_RelativeCoordinates.positions |
$00B946 |
$0B5C2 |
$4C |
Dungeon_ViewCell_OcclusionMasks |
gfx-data/Dungeon_ViewCell_Occlusion.flags |
$00B992 |
$0B60E |
$4C |
Dungeon_ViewCell_VisibleFaceMasks |
gfx-data/Dungeon_ViewCell_VisibleFaces.flags |
$00B9DE |
$0B65A |
$14 |
Dungeon_ViewCell_CentredSlots |
gfx-data/Dungeon_ViewCell_CentredSlots.lookup |
$00B9F2 |
$0B66E |
$4C |
Dungeon_ViewCell_WallFaceSlots |
gfx-data/Dungeon_ViewCell_WallFaces.lookup |
The original disassembly has no labels at the true starts $00B946 and $00B992. Existing labels $00B98E and $00B9DA identify the final longword of those two blocks, not their starts. Numeric extraction is safe, but an INCBIN source replacement should not be enabled until exact labels have been inserted at the two true starts.
Door_Lock_Colours at memory $00B2D6 / SPS 439 file position $0AF52 is already extracted as data/Door_Lock.colours; it is adjacent renderer data but does not need a duplicate row.
The proposed routine names describe responsibilities confirmed from the SPS 439 code:
| Source address | Proposed label | Responsibility |
|---|---|---|
$0090D4 |
Draw_DungeonViewport |
Scans the 19 relative cells, builds the global masks and draws the surviving cells. |
$00921E |
Draw_DungeonViewCell |
Resolves and draws one player-relative view cell. |
$009388 |
Draw_DungeonLocation_ByType |
Dispatches map types and iterates their candidate wall faces. |
$0095D4 |
Resolve_DungeonWallFaceDirection |
Converts the current face slot and player facing to N/E/S/W. |
$00B2A4 |
Draw_MainWallFace |
Selects and draws one stone-wall face. |
$00B3D8 |
Draw_WoodenWallOrDoorFace |
Selects solid wall, open frame and optional closed-door overlay. |
$00B410 |
Draw_WallComponentFace |
Selects the wall-component picture and normal, mirrored or two-half path. |
$00B458 |
Draw_WallComponent_TwoHalves |
Draws one source half plus its reflected partner. |
$00B474 |
Select_MainWallGraphicTables |
Selects the main-wall .gfx, .offsets and .positions tables. |
$00B486 |
Prepare_WallSpriteDraw |
Resolves picture offset and packed position into source/destination pointers and dimensions. |
$00B4E0 |
Draw_WallComponent_Transformed |
Applies wall-component transform flags and centre trimming. |
$00B560 |
Draw_WallComponent_EdgeTransform |
Draws the component's perspective edge pass. |
$00B666 |
Draw_MainWall_Transformed |
Applies main-wall transform flags, reversal and centre trimming. |
$00B6FA |
Draw_MainWall_EdgeTransform |
Draws the main wall's reversed perspective edge pass. |
$00B76E |
Draw_WallSprite_BitReversed |
Writes normal wall rows after horizontal bit reversal. |
See also: