Apocalypse 3D model file format

Christopher Bazley, April 2020

Introduction

Apocalypse is a single-player 3D shoot 'em up for Acorn Archimedes computers, written by Gordon Key. The game world is drawn using a combination of flat-shaded polygon meshes and sprites. Most of the targets in the game are animated and have amusing names such as 'weirding flasher' and 'proton flapper'.

In March 2020, I decided to reverse-engineer the 3D model file format for Apocalypse, starting from a disassembly produced by ARMalyser. I had already written programs to convert the 3D models for Star Fighter 3000 and Chocks Away to Wavefront .OBJ format. Since Apocalypse also uses flat-shaded polygon meshes, I thought it might be possible to reuse a lot of my existing code. Apocalypse turned out to be a lot simpler than the other two games.

Required files

The 3D models for Apocalypse are stored in the same file as the executable code, so this game isn't easily moddable. The executable file contains two types of interesting data: object models and flats. Flats are individual polygons that have no colour and are defined in only two dimensions. I believe these are used for planet surface detail.

The game's assembled code and data is loaded at address &8F00. Flats are accessed via a table of 26 addresses located at address &18B64. Object models are accessed via a table of 200 addresses located at address &19B6C. Each address occupies 4 bytes, stored in little-endian byte order (i.e. least-significant bits first). This mechanism allows fast random access to data by the game.

Model data

Model data format

Offset Size Data
0 4 Number of vertices (v)
4 12 × v Vertex definitions
4 + (12 × v) 4 Number of primitives (p)
8 + (12 × v) 8 × p Primitive definitions
8 + (12 × v) + (8 × p) p Primitive colours

Vertices

An array of vertex definitions follows the vertex count. Each vertex definition is 12 bytes long, comprising three coordinate values. Vertices are not shared between models.

The order in which vertices are defined is significant because their position in the array is used to refer to them in primitive definitions. It would be pointless to define more than 256 vertices in a single model because each vertex index is encoded as one byte in a primitive definition.

A right-handed coordinate system is used to specify vertex positions. Ground level is the xy plane with z=0 and all positive z coordinate values are above ground. Coordinates are stored as a signed two's complement little-endian number occupying four bytes.

Vertex data format

Offset Size Data
0 4 X coordinate
4 4 Y coordinate
8 4 Z coordinate

Primitives

An array of primitive definitions follows the primitive count. Each primitive definition is 8 bytes long, most of which is used to store vertex indices. Primitives are not shared between models.

Each vertex index occupies one byte. Vertices are indexed by their position in the preceding vertex array, starting at 0. A primitive must have at least three vertices and can have up to seven (a heptagon). Polygons with vertices specified in anti-clockwise direction face the camera.

Primitive data format

Offset Size Data
0 1 Number of edges
1 1 1st vertex index
2 1 2nd vertex index
3 1 3rd vertex index
4 1 4th vertex index
5 1 5th vertex index
6 1 6th vertex index
7 1 7th vertex index

Primitive colours

An array of colours numbers follows the primitive definitions. Each byte specifies the colour of the primitive with the corresponding array index.

Colours are encoded using an additive RGB model with 4 bits per component. However, the 2 least-significant bits of each component (the 'tint' bits) must be equal for all three components.

Bit 7 6 5 4 3 2 1 0
Role Blue high Green high Green low Red high Blue low Red low Tint high Tint low
Red component:
((Red high × 8) + (Red low × 4) + (Tint high × 2) + Tint low) / 15
Green component:
((Green high × 8) + (Green low × 4) + (Tint high × 2) + Tint low) / 15
Blue component:
((Blue high × 8) + (Blue low × 4) + (Tint high × 2) + Tint low) / 15

For example, colour 42 (binary 00101010):

Red:
0010 = 2/15 = 0.13
Green:
0110 = 6/15 = 0.4
Blue:
0110 = 6/15 = 0.4

This is similar to the SVG/HTML/CSS colour named 'Teal'. (0.0, 0.50, 0.50)

Flat data

Flat data format

Offset Size Data
0 4 Number of vertices (v)
4 8 × v Vertex definitions

Vertices

An array of vertex definitions follows the vertex count. Each vertex definition is 8 bytes long, comprising two coordinate values. Vertices are not shared between flats.

The order in which vertices are defined is significant because each vertex is implicitly joined to its predecessor by an edge. There isn't an enforced limit on the number of vertices (or edges).

Coordinates are stored as a signed two's complement little-endian number occupying four bytes.

Vertex data format

Offset Size Data
0 4 X coordinate
4 4 Y coordinate

Magic numbers

The following table summarizes some of the magic numbers used in Apocalypse.

Message Model Name
0 55 Silicon Vat
1 58..61 Pumping Station
2 62..65 Seismic Hammer
3 14 Processing Factory
4 n/a ESCAPE ABORTED
5 66..69 Krypton Breather
6 80..87 Rakonan GomJabba
7 70,71 Ground Transport
8 72..74 Wind Generator
8 75..78 Wind Generator
9 40..42 Ground Wasp
10 n/a ENERGY UNITS RECOVERED
11 n/a NO ENERGY UNITS RECOVERED
12 n/a SHIELD ENERGY REPLENISHED
13 56 Static Release
14 57 Ground Scanner
15 15, 7 Obelisk
15 167..166 Obelisk
16 n/a WITHDRAWAL ON GUILD ORDERS
17 88..95 Proton Flapper
18 96..103 Electron Grinder
19 104..111 Wave Generator
20 112..119 Postal Teleport
21 120..127 Climatic Ticker
22 44..46 Tilexu Floater
23 26 Snail Rider
24 n/a Energy Bank Recharged
25 128..135 Weirding Flasher
26 136..143 Aldebran Linkbat
27 144..151 Argon Storehouse
28 152..159 Thermal Riser
29 1, 6 Guard Tower
30 181..187 Snailherd
31 192..199 Lhaktal Gourd
32 23..25 Simtaal Quak
33 35,36 Rakonan Barftub
34 n/a SHIELD ENERGY OVERBOOSTED