Determine the RLE format for .obj file sprite pixeldata
This commit is contained in:
BIN
doc/formats/img/altar.obj.me-2018-09-07.png
Normal file
BIN
doc/formats/img/altar.obj.me-2018-09-07.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
doc/formats/img/altar.obj.original.png
Normal file
BIN
doc/formats/img/altar.obj.original.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
BIN
doc/formats/img/chapter01_rendered_2018-09-08.png
Normal file
BIN
doc/formats/img/chapter01_rendered_2018-09-08.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 MiB |
@@ -85,8 +85,8 @@ The `type` field **may** tell us what format each sprite is in.
|
||||
`.obj` files represent visual data. They contain a number of sprites, which are
|
||||
assigned attributes in `.asn` files and referenced from `.map` files.
|
||||
|
||||
The container format is worked out, but the per-sprite data is still unknown, so
|
||||
I'm documenting the former here while still investigating the latter.
|
||||
The container format is worked out, but the per-sprite data is still WIP, so I'm
|
||||
documenting the former here while still investigating the latter.
|
||||
|
||||
The file begins with a header, with all values 32-bit little-endians:
|
||||
|
||||
@@ -357,6 +357,149 @@ following pixeldata, and then a trailing pair of bytes with the same value.
|
||||
|
||||
For these tiles, the central (widest) row has 0x7f instead of 0x80.
|
||||
|
||||
|
||||
## Comparative rendering
|
||||
|
||||
Here, I'm focusing on the `altar.obj` file. It has just two sprites and is a
|
||||
`CENTER` object. Here's how it looks when rendered by `WH40K_TD.exe` (there's
|
||||
a cloud of uncertainty around the skulls at present):
|
||||
|
||||

|
||||
|
||||
And here's the rendering of the first sprite, done by my code as of 2018-09-07:
|
||||
|
||||

|
||||
|
||||
Initial impressions:
|
||||
|
||||
* The original clearly has some transparent pixels
|
||||
* Skulls are scrambled, more so to the right
|
||||
* Starting X-axis along the top half is well out
|
||||
* The tabletop has become basically invisible
|
||||
|
||||
I think some sort of RLE *must* be in place for repeated pixels in a line.
|
||||
|
||||
Digging into the pixeldata, we have 3 distinct stages in the first 16 records
|
||||
(which are null-terminated):
|
||||
|
||||
1. Top of skull
|
||||
1. Right hand side is marked by a vertical antenna-like thing
|
||||
1. The stepped altar resumes on the right.
|
||||
|
||||
Header:
|
||||
|
||||
```
|
||||
------------------
|
||||
0 1 2 3
|
||||
------------------
|
||||
0x0000 d6 00 16 01
|
||||
0x0004 74 00 67 00 (x = 116, y = 103)
|
||||
0x0008 00 00 00 00
|
||||
0x000c 11 1d 00 00
|
||||
0x0010 00 00 00 00
|
||||
0x0014 00 00 00 00
|
||||
```
|
||||
|
||||
|
||||
Here's the first row of pixeldata:
|
||||
|
||||
```
|
||||
80 3d 82 65 1d 80 35 00 (0)
|
||||
```
|
||||
|
||||
There are just two visible pixels in this row, and they are specified correctly
|
||||
by the middle two bytes.
|
||||
|
||||
The 3 on either side are framing data, for certain. It's not clear exactly what
|
||||
they specify though.
|
||||
|
||||
Onto the next row:
|
||||
|
||||
```
|
||||
80 38 87 97 19 17 18 17 18 16 80 35 00 (1)
|
||||
```
|
||||
|
||||
These pixels end in the same position on the right, but start 5 pixels earlier.
|
||||
So there are 7 pixels total, specified by the middle bytes again.
|
||||
|
||||
The RHS bytes are identical. The LHS bytes are 5 lower and higher, respectively.
|
||||
|
||||
Hmm. More rows, not analyzed in depth yet:
|
||||
|
||||
```
|
||||
80 37 82 17 19 03 17 04 16 80 34 00 ( 2)
|
||||
80 36 84 18 16 15 15 04 16 83 14 14 16 80 33 00 ( 3)
|
||||
80 35 83 17 17 16 03 17 81 18 03 17 83 a8 16 17 80 32 00 ( 4)
|
||||
|
||||
80 35 8e 18 19 aa 18 17 17 18 18 17 a8 a8 17 18 18 80 02 81 ad 80 2e 00 ( 5) - right vertical bar starts
|
||||
80 34 8f 18 ab 19 18 aa 19 19 ab ab 19 aa aa 17 17 19 80 02 81 ad 80 2e 00 ( 6)
|
||||
80 34 8c 19 ab 19 19 1a ad ad 1b ad ad 1a ab 03 17 81 19 80 01 81 ad 80 2e 00 ( 7)
|
||||
80 34 90 ac 19 18 18 ab 1b ae 1c ae 1c ad ab 17 16 aa 1b 80 01 81 ae 80 2e 00 ( 8)
|
||||
80 33 91 1a 1c ac 18 19 1a 1b 1c ae 1c 1c ad 1a 19 17 19 ae 80 01 81 ae 80 2e 00 ( 9)
|
||||
80 33 91 1a 1c ad 1a ad 1c ae 1c ae 1c 1c ad 1c ad 1a ab ad 80 01 81 ae 80 2e 00 (10)
|
||||
|
||||
80 31 81 8b 80 01 91 19 1c 1a ad ae 1d 1c af 1d 1c af 1c ae ae 1b ab ad 80 01 81 af 80 2e 00 (11) - left vertical bar starts
|
||||
80 31 81 ab 80 02 86 1c ad 1b 1c 1c af 04 1d 86 1c ae 1b ad 1a ae 80 01 81 af 80 2e 00 (12)
|
||||
80 31 81 ad 80 01 93 1f 1c 1c 1b 1c 1b ae 1d 1c 1d ae 1c ad ad 1b ae 1c 1f ad 80 2e 00 (13)
|
||||
```
|
||||
|
||||
Now for something more interesting. At line 14, an antenna starts to sprout on
|
||||
the far left. It is one pixel wide, followed by 19 transparent pixels, followed
|
||||
by another antenna and the skull.
|
||||
|
||||
```
|
||||
1 2 3 4
|
||||
80 1d 81 ad 80 13 85 ac 1f 1f af 1d 08 1c 89 1b 1b 1c ae 1c 1a 1f af 1f 80 2d 00 (14) - right vertical bar is no longer the last thing
|
||||
80 1d 81 ac 80 11 83 1f 1f ac 03 1f 81 af 09 1d 87 1c 1d ae 1d 1f 1f ae 03 1f 80 2b 00 (15)
|
||||
80 1d 81 ac 80 0f 04 1f 81 ab 03 1f 88 1c 1d 17 15 15 1d 1e 1c 05 1d 03 1f 81 af 05 1f 80 29 00
|
||||
|
||||
```
|
||||
|
||||
Looking at `y=14`, the first couplet *must* define where we start, and
|
||||
that all bytes before then are transparent. Looks like `0x80 <count>`
|
||||
|
||||
The second couplet writes 1 byte in a defined colour. Palette indices 0xad and
|
||||
0xac match the pixels rendered by `WH40K_TD.exe`.
|
||||
|
||||
The third couplet defines the transparent section between the far left antenna
|
||||
and the left antenna or table. Like the first couplet, it is `0x80 <count>`.
|
||||
|
||||
At 4, the couplet idea starts to break down, but this is definitely starting to
|
||||
look RLE-ish.
|
||||
|
||||
Let's go back to line 2 with the knowledge above. We have 9 literal bytes to
|
||||
display. Decoded, they should equal:
|
||||
|
||||
```
|
||||
17 19 17 17 17 16 16 16 16
|
||||
```
|
||||
|
||||
(these are the indexes for the colours each pixel is, as rendered by
|
||||
`WH40K_TD.EXE`).
|
||||
|
||||
The encoded bytes:
|
||||
|
||||
```
|
||||
80 37 82 17 19 03 17 04 16 80 34 00 ( 2)
|
||||
```
|
||||
|
||||
We can get to the output by applying these rules to each byte of encoded data:
|
||||
|
||||
* `< 0x80`: repeat the next byte that many times
|
||||
* `= 0x80`: skip `<count>` bytes
|
||||
* `> 0x80`: take literal - 0x80 bytes
|
||||
|
||||
Doing so, the altar displays, but more importantly, so does all of
|
||||
`Chapter01.MAP`!
|
||||
|
||||

|
||||
|
||||
The annoying "static" is gone, and the only rendering issue visible is that the
|
||||
objects from different Z levels don't stack correctly. Hooray for progress!
|
||||
|
||||
I haven't tried any of the other objects in different chapters yet. Perhaps some
|
||||
of them will fail to render?
|
||||
|
||||
## Debugger
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user