Saturday, 3 September 2016

And yet more I76 - Roads, Objects and Related Guesswork



Early on I mentioned that we would need to decode the RDEF section of the mission file to get more detail on the roads.

This turns out to be fairly straightforward for the basics. There are a couple of loose ends around the details which I've yet to decode, however here's the progress so far.

Parsing the RDEF

The RDEF section is another nested BWD2-style tag/length/data section, it opens with the size of the field and a revision tag, RREV, which for the I76 I have here is always “1”.

Following this there are a number of “RSEG” declarations, each of which contains a declaration for a piece of road, and has the data:
  • 4 byte “RSEG” label
  • 4 byte Data Length (uint32_t)
  • 4 byte Segment Type
  • 4 byte Segment Pieces Count
  • Followed by a number of 24 byte segment pieces.

The segment type tells you the type of road, 0 is “paved highway”, 1 is “dirt track” and 2 is (I believe) the rarer “river bed” type. T05 also has a segment of type 3072, but that has no actual pieces associated with it (so I'm being optimistic and assuming that's an artefact of some four lane highway stuff that was cut from the game, as opposed to a bug on my part).

The segment count tells you the number of 24 byte segment pieces remaining in this block of data, which describe this section of road. (So the overall RSEG data size will be ((24*'segment pieces count')+16).

Each of these 24 byte entries is 6 floats, arranged as two 3 byte vertex co-ordinates, and these outline the road edges. One thing to note is that these values are in absolute game co-ordinates, in meters (so they run from 0,0 to  51995,51995). We can treat these values as “XZY” triplets (for the convention of X vs Y on a plane and Z as vertical), and by placing vertices we end up with a road path which we can simply mesh directly:


One oddity is the 'Z' values – they are almost all zero or near zero, which makes sense since the road will follow the terrain height map for vertical values, however the end of some paths, particularly those at junctions have high values.


However I have no idea quite why these values are high – there may be some hint to the render engine, and the actual Z value groups around particular junctions and junction objects in a suspiciously deliberate way, but I have no idea why the values do what they do. It may be they're masked values, or not actually floats, but for looking at imported road meshes I simply ignore them for now.

Also I think the road definition is just a texturing cue to the render engine, since the actual behaviour of the roads depends on the values in the terrain height field higher order bits, but that's also something of a guess at the moment.

Objects

Just a quick note on the objects: these are in the ODEF section of the mission file – again this is a nested BWD2-style section of the file, and has an OREV revision tag of “3” in my case.

Each object starts with the tag “OBJ “ and a 4 byte field length and is always 108 bytes long. The data is
  • 8 byte raw label
  • 2 byte integer
  • 2 byte integer
  • Followed by “at least” 11 bytes of floating point value
The object label is an odd mash of the Class Name string (as per the asset bible) masked with a unique id value to prevent collisions – if you mask out the high bit with a piece of code like
    for (int i=0; i < 8; i++)
    {
      unsigned char v = object.rawlabel.at(i);
      if (v > 0x7f)
      {
        labelhigh = (labelhigh <<1) |0x01;
      }
      else
      {
        labelhigh = (labelhigh <<1) & 0xfe;
      }
      v = v &0x7f;
      if (v != 0)
        object.label += v;
    }
Then you wind up with a set of ASCII strings and associated ID values, i.e. from M01 separating the labels and labelhigh values of the spawn points gets:
Object Label "spawn" High Bits "0"  Of length  "108"
Object Label "spawn" High Bits "16"  Of length  "108"
Object Label "spawn" High Bits "15"  Of length  "108"
Object Label "spawn" High Bits "14"  Of length  "108"
Object Label "spawn" High Bits "13"  Of length  "108"
etc. Quite why this wasn't just split down into a label with a trailing ID number originally isn't entirely clear, although it could be that parts of the engine had to work inside the 8 byte label restriction (another guess).

Although I haven't gone into too much detail on what the object fields actually do (i.e. how ClassID, size and rotation is encoded) the X & Y co-ordinates appear to be from the 9th and 11th floating point value, and we can place them directly on the road render – just dropping these on as simple planes around the target point we get:
t01 roads and objects
To help decode what's being displayed we can use the .obj format to add names to the objects – before declaring the face simply add a line beginning with “o” and followed by the name string, which we can from from the base label and the ID value we extracted above e.g.:
  s = "o " + obj.label +"_"+ QString::number(obj.id);
  fnew.write(s.toLocal8Bit());
  fnew.write("\r\n",2);
And you get this:
Highlighting the Red Deacon Fireworks stand from t01