...
i16 offset of skin Sn-1
SKINDEF skinDef [ Sn ]
;
};
struct SKINDEF
{
i16 floor graphic ID
i16 middle graphic ID
i16 ceiling graphic ID
i16 wall graphic ID
i16 floor mask ID
i16 middle mask ID
i16 ceiling mask ID
i16 wall mask ID
i16 code ID
i16 number of floor
decorations = Fn
i16 floor decoration graphic ID [
Fn ]
i16 number of wall decorations =
Wn
i16 wall decoration graphic ID [
Wn ]
};
The graphics themselves (other than the skin definition) can have any ID you
choose although it is wasteful to use very large graphic numbers because it
causes memory to be wasted at runtime. Also,
at runtime you can generate 'Virtual' graphics such as mirrored floors,
shifted, and scaled graphics
The graphics and the masks are in the Atari 'Bit-Plane' format in which four
16-bit words are used to represent 16 pixels. The first of the four words
contains the least significant bit of each of the 16 pixels. The second
16-bit word contains the next least significant bits, etc. The most
significant bit in each of the four words provide the four bits for the first
pixel, etc. In this way the four words define 16 pixels. The mask need only
contain one word for the 16 pixels because the masks for each of the four
words are identical and can be constructed at runtime by replicating just one
word. So, in the mask graphic 16 pixels are represented by one 16-bit word
and in the 'picture' graphic 16 pixels are represented by four words.
The Mask graphic has the following format:
struct index
{
i32 number of masks = Mn
i32 maskOffset [ Mn
]
};
maskOffset is the byte offset within the file of the start of each of the
masks used by the skin. If any of these is zero then any code using that
mask will not be drawn. Each mask (pointed at by the index entry) is of the
following format:
struct maskEntry
{
ui16 sourceX;
ui16 sourceY;
ui16 destinationX;
ui16 dstinationY;
ui16 width;
ui16 height;
ui16
pixels[width*height/16];
};
The 'sourceX', 'sourceY', 'destinationWidth', 'destinationHeight', 'width'
and 'height' are all in units of one pixel. Therefore, 'sourceX' and 'width'
must be multiples of 16; 'destinationX need not be a multiple of 16 although
it is more efficient if it is. 'destinationX' and 'destinationY' are
relative to the top left of the viewport. As a result of this, you can draw
floor graphics on the ceiling or anywhere else in the viewport that you
choose. Moreover, the graphics can be any size. So if you want to draw
trees or columns you can do so.
The 'picture' graphic has the following format:
struct picture
{
i32 width; //In units of
pixels. Multiple of 16;
i16 pixels[#pixels in
graphic/4]
};
*******************************************************************************************************
*****
***** A DECORATION GRAPHIC ( WALL OR FLOOR ) LOOKS LIKE
THIS
*****
*******************************************************************************************************
i16 number of decorationPictures =
Pn
i16 offset of decorationPicture 0
i16 offset of decorationPicture 1
.....
i16 offset of decorationPicture Pn-1
i16 number of decorationLocations =
Ln
i16 decorationLocation 0
i16 decorationLocation 1
...
i16 decorationLocation Ln-1
Here starts the actual bitmap data and mask data.
Each picture is an 'interleaved' bitmap with the mask interleaved in the
pixel data.
struct decorationPicture
{
i8 width; // In units of pixels.
Multiple of 16.
i8 height;
PICTUREGROUP pictureData[width/4 *
height];
};
struct PICTUREGROUP
{
i16 mask;
i16 bitplanes[4];
};
struct decorationLocation
{
i8 destinationX;
i8 destinationY;
};
Notice that the pictures have no sourceX nor sourceY. The source of bitmaps
all start at ( 0, 0 );
Like other graphics, the destinationX must be a multiple of 16 and the width
must be a multiple of 16.