Chocks Away 3D model file format

Christopher Bazley, April 2018

Introduction

Chocks Away is a flight simulator for Acorn Archimedes computers, written by Andrew Hutchings (interviewed here). It was published in 1990 by The Fourth Dimension (and followed by Extra Missions in 1991). It is fondly remembered by many because of its smoothly animated 3D graphics, simple controls and inclusion of a split-screen dogfight mode. The game world is drawn using a combination of flat-shaded polygon meshes, lines and points. Its 3D models of aircraft, buildings, bridges and other structures are primitive but I think they have a certain charm.

Some time in December 2017, I decided to reverse-engineer the 3D model file format for Chocks Away, starting from a disassembly produced by ARMalyser. I had already written a program to convert the 3D models for Star Fighter 3000 to Wavefront .OBJ format. I knew that Chocks Away had been written by the same author and used the same compression code (by Gordon Key) so I thought it might be possible to reuse a lot of my existing code. It turned out that Chocks Away had a lot of interesting and unique features.

Required files

The 3D models for Chocks Away are stored in a file named 'Land' (probably to hide them). The model data is indexed by an array of 200 (four byte, little-endian) addresses stored in a second file named 'Obj3D'. Consequently, it's possible for several indices to alias the same model. Strictly, one would need to know the address at which the model data will be loaded in order to correctly interpret the index file. In practice, the lowest address in the 'Obj3D' file is always the address of the start of the model data, which also coincides with the start of the 'Land' file.

Chocks Away: Extra Missions requires additional models for some maps. Instead of a single 'Obj3D' file, alternative index files numbered from 'Obj3D0' to 'Obj3DF' store between 109 and 159 model addresses. Model data in common between all maps is still stored in a single 'Land' file and any extra model data is stored in files numbered 'LandEx0' to 'LandExF'. Many of these files have length zero.

Model data

The rendering engine supports two levels of detail for every model, selected according to its distance from the camera. The distance beyond which a simplified model is used is part of the model data. Two models can be created from each set of data; vertices and geometric primitives defined nearer the start of the file are always rendered whereas those defined later in the file are only rendered at the high detail level. The vertex and primitive counts for the simplified model are stored separately from the counts for the complex model and should always be smaller.

If the chosen primitive count has the special value of 255 then the first vertex of the model is rendered as a point and any other vertices are ignored. It's unclear what use this is. Attempting to use it would probably crash the map view.

The vertex and primitive counts for the simplified model are ignored when drawing models on the map.

The near clip plane is an xz plane at a y position specified as part of the model data. If any vertex of one of the primitives comprising the model is closer than this plane (i.e. has a smaller y value) then that primitive will be clipped.

The last part of the per-model data controls plotting style. Lines can be drawn one or two pixels thick and polygons can be drawn with or without a blue or black outline. One word is overloaded to control both attributes: models with outlined polygons must have thick lines and models with plain polygons must have thin lines. Plotting style doesn't affect procedurally-generated primitives.

By default, thick lines and polygon outlines are disabled globally; enabling them (by pressing '5' in Extra Missions) only affects models configured to have those attributes in the first place. This attribute seems to have been set in a rather arbitrary way. For example, patrol boats can have outlined polygons and thick masts but gunboats cannot!

Plotting style is ignored when drawing the map: lines are thin and polygons have no outlines.

Model data format

Offset Size Data
0 4 Model simplification distance
4 4 Number of primitives -1, or 255 to draw 1st vertex
8 4 Number of vertices -1
12 4 Simplified number of primitives -1, or 255
16 4 Simplified number of vertices -1
20 4 Ignored
24 4 Near clip plane distance
28 4 Plot style (0, 1 or 2)

Plot styles

Value Line style Polygon style
0 Thin No outline
1 Thick Black outline
2 Thick Royal azure outline

Vertices

An array of vertex definitions follows the per-model data. 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. Any vertices required by a simplified model should be defined before those only required by the high-detail version of the same model, to minimize the number of coordinates processed.

It probably isn't practical to define more than 200 vertices in a single model because higher vertex indices cannot be used in a model that is drawn on the map (and some high indices trigger procedural generation when a model is rendered in 3D).

A right-hand coordinate system is used to specify vertex positions. Ground level is the xy plane with z=0 and all negative 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 vertex definitions. Each primitive definition is 16 bytes long, half of which is used to store vertex indices. Primitives are not shared between models.

The order in which primitives are defined is significant for several reasons:

  1. Primitives belonging to both simplified and full versions of a model must precede those only required for the full model.
  2. Primitives are rendered in the order they are defined regardless of their depth within a scene. Consequently every primitive should be defined before any others that could (partially) obscure it when projected into two dimensions. This is impossible for complex models, including some of the aircraft in the game.
  3. Only the first primitive of most models is drawn on the map. This explains why square brown field boundaries are drawn as straight lines (annoying for navigation). A small list of exceptions is hardwired into the game to allow certain models to be drawn on the map without simplification — for example cross-shaped aerodromes, which would otherwise appear to be missing a runway.

Each vertex index occupies one byte. Vertices are indexed by their position in the preceding vertex array, starting at 1. A primitive must have at least two vertices and can have up to eight (an octagon). Primitives with fewer than eight vertices are terminated by a zero byte. Polygons with vertices specified in anti-clockwise direction face the camera.

Some models are sufficiently complex that back-face culling is required to prevent near polygons being overdrawn by far polygons; others require polygons to be visible from both sides. For example, one airfield has two runways and one taxiway defined anti-clockwise (facing up) but two other taxiways defined clockwise! No switch to disable back-face culling is incorporated in the model data.

Beyond a specified distance, a polygon is replaced with a line between its first two vertices (as if the third byte of the definition were zero). This threshold applies to the distance of the whole model from the camera, not the distance of any individual vertex. A side-effect is to disable procedural generation (e.g. stripes or dashes) and force the specified colour and plotting style to be used.

The simplified version of a model may be selected for an object receding into the distance before its constituent polygons are simplified, or vice versa. A simplified model's vertex count need not include any vertices not required by polygons that are always simplified when that version of the model is selected.

The polygon simplification distance is ignored when drawing models on the map: if a polygon is drawn at all then all of its edges are drawn.

Primitive data format

Offset Size Data
0 1 1st vertex index
1 1 2nd vertex index
2 1 3rd vertex index (or 0 to terminate a line, 253..255 for patterns)
3 1 4th vertex index (or 0 to terminate a triangle, 248..255 for patterns)
4 1 5th vertex index (or 0 to terminate a quadrilateral)
5 1 6th vertex index (or 0 to terminate a pentagon)
6 1 7th vertex index (or 0 to terminate a hexagon)
7 1 8th vertex index (or 0 to terminate a heptagon)
8 1 Colour number
9 3 Ignored
12 4 Polygon simplification distance

Procedural generation

The game uses procedural generation to create parts of its 3D models. Road and runway markings, battlements and trusses follow a regular pattern and can be described more accurately and concisely by algorithm instead of defining them by hand. Procedural generation is triggered by terminating a line or triangle with a special vertex number instead of with zero. This encoding scheme effectively limits the maximum number of vertices in a polygon mesh to 252, unless care is taken to ensure that vertices numbered 253..255 never appear as the third vertex of a triangle. (A stricter constraint applies to the fourth vertex of quadrilaterals and other complex polygons.)

Different special vertex numbers invoke different algorithms, using the previous vertices as their input. Special lines can be drawn with different thicknesses and numbers of dashes. Special triangles define parallelogram-shaped regions to be filled with parallel lines (for railway sleepers), zigzag lines (for trusses) or parallelograms (for stripes). Special triangles are also used to draw evenly-spaced points (for landing lights).

When drawing procedurally-generated primitives, the colour of the original primitive is ignored in favour of a special colour. Similarly, the plotting style of the model is ignored: polygons are drawn without outlines and line thickness depends on the chosen algorithm. Line thickness and colour may vary according to distance.

Back-face culling is not applied to procedurally-generated polygons so they effectively face both ways.

Procedural generation is disabled when drawing the map. Special primitives are drawn as ordinary triangles or lines, using the vertices and colour of the original primitive.

Patterns triggered on the third vertex

Vertex no. Description Colour No. of primitives Illustration
253 Thin dashed line White 8
254 Thin dashed line White 16
255 Thick dashed line White 32

Patterns triggered on the fourth vertex

Vertex no. Description Colour No. of primitives Illustration
248 Dotted line (ignoring 3rd vertex) Orange 32
249 Parallelograms Dark grey 16
250 Thick parallel lines Peru 64
251 Thin zigzag lines Black 16
252 Parallelograms if camera z <= -400 Peridot 8
253 Parallelograms if camera z <= -400 White 16
254 Parallelograms Peridot 8
255 Parallelograms White 16

Bugs

Thick lines are not clipped correctly at the righthand edge of the screen, which means that up to one pixel can spill from the end of each raster line to the start of the next.

A subroutine used to do perspective projection when generating thick patterned lines (for roads and railways) corrupts the input depth value when the point to be projected is distant. The effect is to make distant lines thicker than those in the middle distance (and also brighter, in the case of parallel lines).

A patch to fix these bugs is available.

Magic numbers

The following table summarizes the various magic numbers used in Chocks Away: Extra Missions.

HIT = byte offset in hitlist (from the 'PlaneNames' file).
VIE = value poked into viewdata%!4 in PROCshowtargets.
MSG = message number.
TGT = target object type (word at offset 24 in data).
PLN = plane object type (word at offset 140 in data).
GFX = plane model index (in 'Obj3D0' file).
SHD = ground shadow index (in 'Obj3D0' file).

VIE HIT MSG TGT PLN GFX SHD Name
0 0 0 0 18 23 TIGER MOTH
1 1 [[[[[ HELLO ERROR ]]]]]
2 2 2 2 19 24 FOKKER V7 TWIN
3 6 3 3 29 26 FOKKER EINDECKER IV
4 4 4 4 31 27 ALBATROS DIII SCOUT
5 5 5 5 22 25 GOTHA G IV BOMBER
6 6 6 6 30 28 FOKKER VIII TRIPLANE
7 7 7 7 77 102 FOKKER DE5 BIPLANE
8 8 8 8 75 103 FOKKER V3 TRIENGINE
9 9 9 9 74 104 CARGO AIRCRAFT
10 10 10 108 103 JET FIGHTER
100 50 11 0 0 GROUND GUN BASE
101 51 12 1 1 STORE BUILDING
102 52 13 2 2 TANK
103 53 14 3 3 HEAD QUARTERS
104 54 15 4 4 CONTROL TOWER
105 55 16 5 5 PATROL BOAT
106 56 17 6 72 AIRSHIP
107 57 18 7 73 BARRAGE BALLOON
108 58 19 8 78 CONTROL TERMINAL
109 59 20 9 79 OIL TANKER
110 60 21 10 81 GUN BOAT
111 61 22 11 85 TRAIN
62 23 12 22 GOTHA G IV BOMBER
63 24 13 29 FOKKER EINDECKER IV
64 25 14 19 FOKKER V7 TWIN
65 26 15 75 FOKKER V3 TRIENGINE
66 27 16 74 CARGO AIRCRAFT
67 28 17 54 YACHT
68 29 18 87 RAILWAY STATION
69 30 19 46 BRIDGE
70 31 20 68 FACTORY
71 32 21 52 AIRCRAFT CARRIER
72 33 22 107 JET FIGHTER

List of models

The following table summarizes the 3D models stored in the 'Land' file for Chocks Away: Extra Missions (i.e. the model data in common between all maps). Many of these models are not named targets or otherwise special to the game engine, therefore their appearance is subject to interpretation.

Number ranges ('..') indicate that more than one index aliases the same model data.

Number Special? On map Appearance
0 Yes Hidden Ground gun base
1 Yes Hidden Store building
2 Yes Hidden Tank
3 Yes Hidden Head quarters
4 Yes Hidden Control tower
5 Yes Hidden Patrol boat
6 No Partial Aerodrome
7 No Partial Aerodrome
8 No Partial Dark olive field
9 No Partial Dark green field
10 No Partial Avocado field
11 No Partial Peridot field
12 No Partial Harvest gold field
13 Yes Hidden Light grey cloud
14 Yes Hidden White cloud
15 No Partial Brown field
16 No Partial Royal azure lake
17 No Partial Aerodrome
18 Yes Partial Tiger Moth
19 Yes Partial Fokker V7 twin
20 No Partial Aerodrome
21 No Partial Aerodrome
22 Yes Partial Gotha G IV bomber
23 Yes Partial Tiger Moth shadow
24 Yes Partial Fokker V7 twin shadow
25 Yes Partial Gotha G IV bomber shadow
26 Yes Partial Fokker Eindecker IV shadow
27 Yes Partial Albatros DIII scout shadow
28 Yes Partial Fokker VIII triplane shadow
29 Yes Partial Fokker Eindecker IV
30 Yes Partial Fokker VIII triplane
31 Yes Partial Albatros DIII scout
32 No Partial Tree
33 No Partial Aerodrome
34 Yes Full Aerodrome
35 No Partial Rugby field
36 No Partial Low factory
37..38 Yes Hidden Rubbish (-ve primitive count)
39 No Partial Church
40 No Partial Terraced houses
41 No Partial Bridge
42..43 No Partial Grey block
44 No Partial Dark grey block
45 No Partial Church
46 Yes Partial Bridge
47 No Partial White house and outbuilding
48 Yes Full Island
49 No Partial Aircraft carrier
50..51 Yes Full Aircraft carrier
52 Yes Partial Aircraft carrier (canonical)
53 No Partial Lighthouse
54 Yes Partial Yacht
55 No Partial Blue block
56 No Partial Blue block
57 Yes Full Aerodrome
58 Yes Full Dark olive field
59 Yes Full Dark olive field
60 No Partial Dark olive field
61..62 No Partial Avocado square field
63 No Partial Black tower
64 No Partial Hangar
65 No Partial Grey house
66 No Partial Grey house with lean-to
68 Yes Partial Factory
69 No Partial Hexagonal road
70 No Partial Wall with battlements
71 Yes Hidden Airship
72 Yes Hidden Airship (canonical)
73 Yes Hidden Barrage balloon
74 Yes Partial Cargo aircraft
75 Yes Partial Fokker V3 triengine
76 Yes Hidden Dark olive field boundary
77 Yes Partial Fokker DE5 biplane
78 Yes Partial Control terminal
79 Yes Hidden Oil tanker
80 Yes Partial Oil rig
81 Yes Hidden Gun boat
82 No Partial Grey house
83 No Partial White house with outbuilding
84 No Partial Pier
85 Yes Hidden Train
86 No Partial Railway track
87 Yes Partial Railway station
88 No Partial Railway bridge side
89 No Partial Railway signal box
90 No Partial Railway signal
91 No Partial Level crossing
92 No Partial Street light
93 No Partial Rectangular signboard
94 No Partial Triangular signboard
95 No Partial Triangular signboard
98 No Partial Street light
99 Yes Partial Sea
100 No Partial Road
101 No Partial Road
102 Yes Partial Fokker DE5 biplane shadow
103 Yes Partial Fokker V3 triengine shadow
104 Yes Partial Cargo aircraft shadow
105 No Partial Brown field boundary
106 No Partial Runway lights
107..108 Yes Partial Jet fighter
108 Yes Partial Jet fighter