Battle Isle 2

Fight 3D resources

FGT/*.LIB, FGT/*.DAT

Everything that draws a close-combat scene: 3D meshes, scenery lists, palettes, movement and camera tables, and the sine table.

Close-combat scenes in Battle Isle 2 are drawn with small 3D meshes rather than sprites. Everything that draws them lives in the FGT/ directory: the meshes (three archives sharing a 3DOB magic), the scenery and ground lists, the palettes, the backdrops, the movement and camera tables, a sine/tangent table and the sound banks. This page describes each in turn.

Status: header, vertices, polygon topology, per-face normals, per-face colour and the moving-part table recovered and validated against all 520 records - enough to render the meshes as the game does, including the per-face shade bias, and to know which polygons are a turret and where it pivots. Every byte of the file is now accounted for. The 3DLD scene lists that place the scenery are decoded too, and groupSize records a unit's strategic member count. The conversion from strength to visible members still needs a runtime trace. The layout is accounted for; the light term, some angle drivers and three inline polygon fields remain unresolved.

ObjectHeader66 bytes3DOBchar[4]vertex_offsetu16slot_offsetu16face_offsetu16trailing_offsetu16reserved_0cu16[5]vertex_count_minus_oneu16face_count_minus_oneu16part_countu16reserved_leadu8[38]Polygonsvariable sizePolygonRecord × face_countvariable sizeLayout2 BVertices4 BColour4 BReserved10 BUnknown6 BVertices6 × vertex_count bytesVertex × vertex_count6 bytes eachPosition6 BFace normals6 × face_count bytesFaceNormal × face_count6 bytes eachNormal6 BParts32 × part_count + listsPartRecord × part_count32 bytes eachGeometry8 BPivot6 BRuntime12 BReserved6 Bvertex listsu16[]Vertex slots10 × vertex_count bytes010 bytes each

Each box is one table of a 3DOB object, in file order. The polygons start right after the header; the other tables are found by the four offsets in the header, and each line runs from an offset to the table it locates. A stack stands for the records of a table, with its count in the label. Cut blocks are not drawn to scale. Dashed blocks hold the same value in every object.

Archives

ArchiveRecordsContents
FGT/UN3D000.LIB240units (UNIT###), animations (ANIT###)
FGT/LAND000.LIB288terrain objects (LAND###)
FGT/LAND300.LIB288terrain objects
FGT/PAL000.LIB41the fight palettes the meshes are coloured from
FGT/LAYR000.LIB, LAYR300.LIB96 each3DLD scenery layer lists
FGT/GRND000.LIB, GRND300.LIB24 each3DLD ground lists
FGT/BACK000.LIB20the raster horizon backdrops and their moons - not meshes; see Where the mountains go

520 of the entries carry the 3DOB magic; the remaining 296 are 8-byte LEER__ (German *leer*, "empty") placeholders for unused slots.

FGT/SCHOSS.DAT (German *Schuss*, "shot") is a twelfth archive in a container of its own, and it holds the ammunition. Its own layout is simple: a u32 at 0x00 gives the offset of the directory, TCT and an entry count follow at 0x04, and the directory is 40 records of an 8-byte name and a u32 offset. The 40 slots are ten AMMO### groups in A/B/C/S form, of which 11 hold real 3DOB objects - the ten A models plus AMMO005S - and the other 29 are LEER__.

They obey the object layout above (face_offset - vertex_offset == vertex_count * 6 and so on) and none has parts:

ObjectSizeReads as
AMMO001A2 vertices, 1 two-vertex face, steela tracer: a bare 320-unit line
AMMO002A7 vertices, 8 faces, steel and olivea shell, 420 units long and 30 wide
AMMO005A9 vertices, 2 faces, all sky, y = 0 throughouta flat water splash
AMMO006A16 vertices, 10 faces, steel and golda 110-unit ball with gold octagon caps
AMMO050A12 vertices, 8 faces, entirely golda 120-unit ball of flame - a muzzle or impact flash

The rest of FGT/ is not 3D. FGTEXPLO.LIB and FGTSHOOT.LIB are Creative Voice (.VOC) samples, named by where the explosion happens (LAND, WATER, UWATER, AIR) and by weapon type (MG, KAN, ARI, RAK, BOMBE, WBOMB, TORP, GTORP, with LEER in the unused slots); FGTWIN.LIB and FGTLOS.LIB are eight XMIDI stings each for winning and losing a fight.

Object layout

All multi-byte integers are little-endian.

66 bytes10 fields● 8 ◐ 2 ○ 0
0x00char[4]tag ●Literal 3DOB (0x424f4433).

Tables

4 fields · 8 bytes
0x04u16vertex_offset ●byte offset of the vertex table
0x06u16slot_offset ●byte offset of the runtime vertex slots; equals trailing_offset on the 410 objects that have no parts
0x08u16face_offset ●byte offset of the face-normal table
0x0au16trailing_offset ●byte offset of the part table

Counts

3 fields · 6 bytes
0x16u16vertex_count_minus_one ●vertex count is this + 1
0x18u16face_count_minus_one ●face count is this + 1
0x1au16part_count ●first word of the 40-byte object lead; see Moving parts

Reserved

2 fields · 48 bytes
0x0cu16[5]reserved_0c ◐0 across the corpus
0x1cu8[38]reserved_lead ◐rest of the object lead; 0 across the corpus
6 bytes3 fields● 3 ◐ 0 ○ 0
0x00
Point at the bar to name a field; select it to jump to its row.
knownpartly knownunknown: investigatenamed, never setalways one valueunused bits

Position

3 fields · 6 bytes
0x00i16x ●model space
0x02i16y ●+Y is down, so up is -Y
0x04i16z ●model space
6 bytes3 fields● 3 ◐ 0 ○ 0
0x00
Point at the bar to name a field; select it to jump to its row.
knownpartly knownunknown: investigatenamed, never setalways one valueunused bits

Normal

3 fields · 6 bytes
0x00i16x ●one per polygon; a unit normal in 14-bit fixed point (16384 = 1.0)
0x02i16y ●as x
0x04i16z ●as x
variable size11 fields● 6 ◐ 2 ○ 3

Layout

1 field · 2 bytes
0x00u16record_size ●total bytes of this record; also 26 + 2*vertex_count - the fixed fields run +0x00..+0x19, then one word per vertex.

Vertices

3 fields · 4 bytes
0x02u16first_vertex_ptr ●equals the first entry of vertex_ptrs
0x0cu16vertex_count_minus_one ●polygon has this + 1 vertices
tailu16[vertex_count]vertex_ptrs ●the last vertex_count words

Colour

2 fields · 4 bytes
0x0eu16shade ●0, 32, 64, 96, 128, 160 - the polygon's colour: a base index into the fight palette, see Colour
0x14i16bias ●-8..+8, the polygon's baked shade step, see Colour

Reserved

2 fields · 10 bytes
0x04u8[8]reserved_04 ◐zero on disk
0x16u16reserved_16 ◐0 across the corpus

Unknown

3 fields · 6 bytes
0x10u16unknown_10 ○packed small byte pair; undecoded
0x12u16unknown_12 ○packed small byte pair; undecoded
0x18u16unknown_18 ○0 on 92% of faces; undecoded
32 bytes10 fields● 9 ◐ 1 ○ 0
0x00
Point at the bar to name a field; select it to jump to its row.
knownpartly knownunknown: investigatenamed, never setalways one valueunused bits

Geometry

4 fields · 8 bytes
0x00u16polygon_ptr ●file offset of the part's first polygon record
0x02u16polygon_count_minus_one ●the part owns this + 1 consecutive polygons
0x04u16vertex_list_ptr ●file offset of the part's vertex-index list
0x06u16vertex_count_minus_one ●the list holds this + 1 entries

Pivot

3 fields · 6 bytes
0x08i16pivot_x ●the axis the part turns about, in model space
0x0ai16pivot_y ●up is -Y, as everywhere else in the format
0x0ci16pivot_z ●

Runtime

2 fields · 12 bytes
0x0ei16[3]translation ●0 on disk; runtime X/Y/Z translation added after pivot rotation
0x14i16[3]angles ●runtime X/Y/Z angles; applied in Z, X, Y order; 0 on disk.
slottranslationangles
A0x0e0x14
B0x100x16
C0x120x18
Where each slot's values sit: slot k is element k of every array above.

Reserved

1 field · 6 bytes
0x1au8[6]reserved_1a ◐0 on disk; runtime role unresolved
variable size5 fields● 4 ◐ 1 ○ 0
0x00char[4]tag ●Literal 3DLD.

Objects

1 field · 12 bytes
0x04u16[6]object_ids ●ids of the LAND objects this record may place; 0xffff/0xfffe mark unused slots

Placements

2 fields · 2 bytes
0x20u16count ●placements
0x22..Placement[]placements ●16 bytes each

Reserved

1 field · 16 bytes
0x10u8[16]padding ◐padding and, in a few records, a leftover source filename such as LDF005G.3DL
16 bytes5 fields● 3 ◐ 1 ○ 1
0x00
Point at the bar to name a field; select it to jump to its row.
knownpartly knownunknown: investigatenamed, never setalways one valueunused bits

Position

3 fields · 6 bytes
0x00u16slot ●index into object_ids
0x04i16x ●always a multiple of 1000
0x06i16z ●always a multiple of 1000

Unknown

1 field · 2 bytes
0x02u16unknown_02 ○0, 0xffff or 0xfffe; undecoded

Reserved

1 field · 8 bytes
0x08u8[8]reserved_08 ◐zero on disk

Still to investigate

Every field marked unknown or partial above, and every bit that is set without a name or named without ever being set.

  • unknown_10○packed small byte pair; undecoded PolygonRecord
  • unknown_12○packed small byte pair; undecoded PolygonRecord
  • unknown_18○0 on 92% of faces; undecoded PolygonRecord
  • unknown_02○0, 0xffff or 0xfffe; undecoded Placement