This is a JavaScript plugin for reading/writing .IAN files: a simple proprietary 3D model format used for the 1998 video game Incoming.
This format was reverse engineered without a debugger/disassembler; as a result there are still values whose purpose is unknown. These values aren't included in the data returned by the plugin but guesses as to what they are can be found in the source code.
A Python script is also included for batch exporting to OBJ format.
- File data can be used with three.js to render models in-browser:
- Convert between IAN and OBJ, allowing for custom in-game models and model export to programs such as Blender, Maya, etc.
- OBJ files can be converted to IAN format allowing custom models in-game:
Include incoming-reader.js
in the page and pass an ArrayBuffer
of the .IAN file to the global IncomingReader
function:
<input type="file" id="file-input" />
<script src="incoming-reader.js"></script>
<script>
document.getElementById("file-input").addEventListener("input", function() {
for (const file of this.files) {
const fileReader = new FileReader();
fileReader.onload = function() {
const incomingReader = new IncomingReader(this.result);
}
fileReader.readAsArrayBuffer(file);
}
})
</script>
Returns an object containing the following:
Name | Type | Description |
---|---|---|
mesh_info |
Array | Contains four identical objects, each containing:
|
unknown_end_data_count |
Number | Appears to be a count of data at the end of the file. These values may be bounding box vertices, but I'm not sure. The count also appears to be occasionally incorrect, e.g. tree7.ian reads 18, but the actual count is 16. |
unknown_end_data_offset |
Number | Offset to unknown end data. |
faces_offset |
Number | Offset to face indices (same value as repeated four times in mesh_info ). |
name |
String | Model name. Not always descriptive (e.g. "Object01"). |
faces |
Array | An array of objects, each containing:
indices is an array of three indices into the vertices data. The two unknown values are usually 3, or 3 and 7. They do not appear to affect the model if set to null. |
vertices |
Array | An array of objects, each containing:
normal is an array containing 3 values, e.g. [0, -1, 0] . |
unknown_end_data |
Array | See above. |
Converts an OBJ file to IAN format.
Pass an OBJ file ArrayBuffer
to the plugin and call incomingReader.writeIAN("my_model")
. This will prompt a download from a Blob
object URL.
It is recommended to export a default IAN file and keep your model to roughly the same size.
Converts an IAN file to OBJ format.
Pass an IAN file ArrayBuffer
to the plugin and call incomingReader.writeOBJ("my_model")
. This will prompt a download from a Blob
object URL.
The game assets are contained in the following folders (typically located at C:\Program Files (x86)\Incoming
):
Incoming/
├── asc/
├── pcobject/
├── ppm/
└── wavs/
See the sections below for further details on each folder.
Contains several files related to the levels of the game, each contained in its own folder. The folder for the first level, Africa, contains:
Incoming/
└── asc/
└── africa/
├── africa.mdl
├── africa.odl
├── africa.wdl
├── africa_action.mdl
├── africa_virus.mdl
├── africa_virus.odl
├── city2tc.bin
└── tland1.bin
A brief overview of the file types:
File | Description |
---|---|
*.mdl |
A series of procedures describing each mission, including spawn positions of enemies, camera positions, and which speech files to play. |
*.odl |
A list of permitted models (vehicles and scenery) for this level. Basic values for the environment are also present such as RGB and fog values for the sky, as well as a list of terrain textures. |
*.wdl |
A list of level scenery and where to spawn the models. |
city2tc.bin |
Texture positions/properties for each square tile of the level (see below). |
tland1.bin |
A heightmap for the terrain (see below). |
These files consist of 128 blocks of 256 bytes. Each block corresponds to one column of the terrain, starting from north west, going across. Two bytes are used to describe the texture information for each square, which means each level is a 128×128 grid.
An excerpt from Africa's file, split into rows of two bytes:
85 00
05 00
86 00
06 00
The first byte contains two values: the texture index, and its position. Levels use eight textures for terrain which are specified inside the .odl
file.
The texture index is determined by the first four bits. One way to obtain the index is to use a bitwise AND:
0x85 & 0xF // 5
0x05 & 0xF // 5
0x86 & 0xF // 6
0x06 & 0xF // 6
Textures are zero-indexed.
The texture position is determined by the last four bits. A bitwise AND can be used here again:
0x85 & 0xF0 // 8
0x05 & 0xF0 // 0
0x86 & 0xF0 // 8
0x06 & 0xF0 // 0
Terrain textures are 256×256px, however only a quarter (128×128px) segment is shown per tile. There are 16 possible positions (highlighted by the red square):
Value | Position |
---|---|
0x00 |
|
0x10 |
|
0x20 |
|
0x30 |
|
0x40 |
|
0x50 |
|
0x60 |
|
0x70 |
|
0x80 |
|
0x90 |
|
0xA0 |
|
0xB0 |
|
0xC0 |
|
0xD0 |
|
0xE0 |
|
0xF0 |
The second byte is a bitmask of texture properties, such as rotation/mirroring:
Value | Description |
---|---|
0x01 |
Flip horizontally. |
0x02 |
Flip vertically. |
0x04 |
Rotate 90° clockwise. |
0x08 |
Rotate 180° clockwise. |
0x10 |
Unknown; may be used for tiles which should contain water in the oceanic level. |
0x20 |
Unknown; used heavily in the oceanic level. |
0x40 |
No visible effect. |
0x80 |
No visible effect. |
As mentioned, this file solely consist of height values for the terrain which is used to modify a flat plane. Each height value is a 16-bit signed integer, resulting in a 513×513 grid of values. Height values seem to be negative in Incoming, so these values would need to be inverted if used for drawing terrain.
An example applying the Moon level's values to a plane using three.js is shown below:
Contains all model files. Alien models are stored in "cweapons" whereas allied models can be found in "pweapons".
LOD meshes can be found within "low" folders in each directory.
Models displaying different levels of damage are separated into folders corresponding to the amount of damage (ranging from 1-3, with 3 being the most damaged).
Contains all texture files. Textures are encoded as PPM files, an open-source bitmap format.
Contains all sound files. Sounds are stored in WAV format. All sounds appear to be monophonic 22.05 kHz / 16-bit.
Yet to be figured out is the purpose of the .ctl
files inside the Incoming\asc\pads\
folder. As the name indicates, these may be related to healing pads.