-
Notifications
You must be signed in to change notification settings - Fork 42
Habitat Object image format
These notes were written by Aric Wilmunder, original creator of Habitats animation engine. They were written on 06/16/2023 based on looking at the following image from Habitat:
Let’s start with the cel data itself. This cels can contain one of four colors, so they are represented in base 4, so each byte contains two bit pairs with values of 0, 1, 2 or 3. Below shows decimal and binary values, and the pixel color they represent
0 or 0b00 - Transparent
1 or 0b01 - Blue
2 or 0b10 - Black
3 or 0b11 - Pink
Same info in Hex where each Hex value represents two colors and Binary:
Hex | Binary | Color |
---|---|---|
0x0 | 0b 00 00 | Transparent, Transparent |
0x1 | 0b 00 01 | Transparent, Blue |
0x2 | 0b 00 10 | Transparent, Black |
0x3 | 0b 00 11 | Transparent, Pink |
0x4 | 0b 01 00 | Blue, Transparent |
0x5 | 0b 01 01 | Blue, Blue |
0x6 | 0b 01 10 | Blue Black |
0x7 | 0b 01 11 | Blue Pink |
0x8 | 0b 10 00 | Black Transparent |
0x9 | 0b 10 01 | Black Blue |
0xA | 0b 10 10 | Black Black |
0xB | 0b 10 11 | Black Pink |
0xC | 0b 11 00 | Pink Transparent |
0xD | 0b 11 01 | Pink Blue |
0xE | 0b 11 10 | Pink Black |
0xF | 0b 11 11 | Pink Pink |
So a Byte containing Hex 0x75
would be Blue Pink for the upper 0x70
hex value, and Blue Blue for the lower 0x05
hex value.
The cel compression format is very simplistic.
A non-zero value indicates raw data, so just convert the hex values into the pattern using the table above. A 0xA7
would represent a pattern of Black Black Blue Pink.
A zero value (0x00
) indicates a run will follow. A run is a repeated pattern.
If the byte following the run indicator has the high bit set (0x80
) this is a transparent run, and the low 7 bits are the number of repeats of transparency.
A 0x00
(run) followed by 0x80
+ 4 indicates four rows of transparency four pixels wide.
If the high bit is NOT set, the next byte represents the length of the run, and the third byte indicates the pattern of the run.
A 0x00
(run) followed by 14 (the run length) followed by the pattern 149 (0x95
) means the pattern of Black Blue Blue Blue repeated 14 times.
A few more points. The cels are drawn from the bottom up. The first byte in the cel data header is the width (a 0x04 indicates 4 bytes wide) and the next value indicates the height (a 0x1e
indicates the cel is 30 pixels high) When you hit the height of the cel, move over four pixels and start back at the bottom of the cel.
The object image data is not well documented within the file. Inside the ‘Heads’ folder I found a file named Object_data. It appears to contain just the header info from most of the head files along with some info on what the header bytes contain. Here’s a bit of that file…
Object | x_fix | y_fix | how held | disk | width | walkto location | container? | gr_states | /expres/nobend/rght/frnt/down/back |
---|---|---|---|---|---|---|---|---|---|
afro0 | 0 | 63 | swing | 11000000 | 3 | 244+right,28+left,255 | no_cont | 0,0,1,1,0,0,2,2 | 10000000 01000000 01100000 |
X_fix
and y_fix
may be the position of the start of the cel in relation to the base of the object. Honestly, I’m not really sure.
How Held
indicates how the object is held when the Avatar is walking. Do your arms swing when carrying it, or do you need to hold your arms straight out, like when carrying a box.
Not sure about disk
or width
.
Walkto Location
I believe indicates where the Avatar will walk to in relation to the object when it is on the ground. This depends on the direction the Avatar is facing.
The container
flag is 0 if this is object is no_cont
or not a container, and presumably non-zero if it is.
Gr_states
I think should be right/left/down/back. This must control what animation is done based on the orientation of the object.
I have tried to map that info over the afro0 file. Not perfect, but it is a close match.
;---------------------------------------
;
; animation cel data
;
afro0_data::
byte swing + 2 // How object is held (arms swinging or straight out?)
byte 0b11000000 // Unknown
byte afro0_start_end - afro0_data // offset to end of header
byte no_cont // Not a container (0) I’m presuming objects that are containers are set true
byte 244+right,28+left,255 // Walk to location and face (where to stand when picking up right=1, left=0)
byte 0b10000000 // Unknown at this time
byte 0b01000000
byte 0b01100000
word afro0_data_a - afro0_data // Offsets to the start of each cel
word afro0_data_b - afro0_data
word afro0_data_c - afro0_data
afro0_start_end:
byte 0,0,1,1,0,0,2,2 // Graphical states? Animation data? right, left, down, back?
Here is the data for the first cel
afro0_data_a:
byte
Cel Width | Height | Horizontal Run Index | Vert Run Index | Cel X Offset | Cel Y Offset |
---|---|---|---|---|---|
0x04 | 0x1e | 0x00 | 0x08 | 0x00 | 0x00 |
The first two bytes of the header indicate that this cel is 4 strips wide, and 0x1e or 30 pixels tall.
byte run,0x80+4 // a run (0x00) followed by High bit=indicates transparent + length of four bytes
This is the start of literal or raw data:
byte 2 // 0x02 transparent transparent transparent black
byte 10 // 0x0a transparent transparent black black
byte 9
byte 9
byte 41
byte 165
byte run,14,149 // a run (0x00) followed by a run length of 14 of the pattern 149 (0x95) or Black Blue Blue Blue
byte 165
byte 41
byte 10
byte 2
byte 2
byte run,0x80+2
byte 47
byte 42
byte 47
byte 175
byte 175
byte 106
byte 106
byte 85
byte 86
byte 91
byte 91
byte 90
byte 86
byte run,15,85
byte 170
byte 40
byte 184
byte 248
byte 250
byte 239
byte 239
byte run,3,191
byte run,3,255
byte 239
byte 175
byte 111
byte 107
byte run,3,91
byte 90
byte run,8,85
byte 86
byte 170
byte run,0x80+3
byte 128
byte run,3,224
byte run,4,248
byte run,3,254
byte 248
byte run,4,224
byte 168
byte 90
byte run,4,86
byte 90
byte 104
byte 160
byte 128
byte run,0x80+1
I had to flip the image because I forgot that Cels start at the bottom. Also C64 pixels are not square, so adding the empty columns helps correct the aspect ratio. Eyes and mouths are added at a second pass to give the character expressions.
This is how the head actually looks when rendered ingame with the facial features.