Sphinx and the Cursed Mummy Wiki
Sphinx and the Cursed Mummy Wiki
Advertisement
Farmer Brothers This article is under construction.
You can help the Sphinx and the Cursed Mummy Wiki by editing it.

EIF seems to stand for Eurocom Interchange Format. Together with ESE, it is one of the original internal 3D workhorse formats used to move the solid geometry of maps and static entities and their materials from 3ds Max into Euroland, and with it any linked texture. Internally, it is just a plain text file organized in simple sections. The original plugins were lost to time, but we still have reference files and access to EuroLand. This format seems to have been at least partially superseded by ESE, as it supports skinned meshes/skeletal animations/cameras while for Maya RTG was used instead.

Here are some notes transplanted from Discord conversations[1] that will do the trick until we have something more formal. There is also a complete schema with every known used token.

A difference EIF files have versus other DCC formats is that each 3ds Max object node is a separate entity when imported. The other importers merge all objects into a single entity.

File format specification[]

There are several EIF files with original levels included with the Authoring Tools that can be used as reference. From what I see, the EIF importer supports these basic blocks/sections, tokens can be separated either by spaces or tab characters, unrecognized blocks (like *COMMENT) are generally ignored:

*EUROCOM_INTERCHANGE_FILE 100
*COMMENT Eurocom Interchange File Version 1.00 Monday January 06 2003 12:13
*SCENE {
*FIRSTFRAME u32
*LASTFRAME u32
*FRAMESPEED u32
*STATICFRAME u32
*AMBIENTSTATIC f64/r f64/g f64/b # note: rgb; the final color gets computed like this: r * 255 * color scale in settings
}
*MATERIALS {
*MATERIAL u32 { # note: index, starts at zero
*NAME string
*COL_DIFFUSE f64/r f64/g f64/b # note: rgb; the final color gets computed like this: r * 255 * color scale in settings
*MAP_DIFFUSE string/texture-path
*TWOSIDED # note: optional, works as a boolean, if present, then TRUE, missing is FALSE
}
}
*MESH {
*NAME string
*FACELAYERSCOUNT u32
*FACESHADERCOUNT u32
*VERTEX_LIST {
f64/x f64/y f64/z
...
}
*UV_LIST {
f64/u f64/v
...
}
*VERTCOL_LIST {
f64/r f64/g f64/b # note: rgb only, when there are three components the fourth is set to 1.0
f64/r f64/g f64/b f64/a # note: rgba, both modes can coexist
...
}
*FACESHADERS {
*SHADER u32/mat-index string/blend-rule # note: these are the available blend types: Non (no blending), HPH (half plus half), OPO (one plus one), OMO (one minus one), OPQ (one plus quart), Alp (alpha)
}
*FACEFORMAT VTCNMFS
*FACE_LIST {
u32/num-verts [ u32/vert-uv-idx-a ... ] [ u32/vert-pos-idx-a ... ] [ u32/vert-color-idx-a ... ] [ u32/vert-normal-idx-a ... ] u32/face-material-idx u32/face-shader-idx u32/face-flags # note: face normals are unsupported/skipped when present. also, the shader seems to override the plain material, i think. sets it to the first one and deletes any other layers
}
}

*GEOMNODE { # swy: adds a mesh into the map entities when loading a map
*NAME string
*MESH string/mesh-name
*USER_FLAGS_COUNT u32
*WORLD_TM { # note: homogeneous 3x3 transformation/rotation matrix with appended translation, row-major; first three are rotation, last row is the position/translation vector
*TMROW0 f64/rx f64/ry f64/rz
*TMROW1 f64/rx f64/ry f64/rz
*TMROW2 f64/rx f64/ry f64/rz
*TMROW3 f64/tx f64/ty f64/tz
} *USER_FLAGS {
*SET u32/hex-flags-value # note: uses strtoul() and supports non-decimal numbers
}
}

*DUMMYNODE { # note: can be used to wrap around other valid blocks to skip them
}

*PLACENODE { # swy: stripped-down version of a GEOMNODE, no USER_FLAGS, it's used to stamp clones of a GEOMNODE in a map, these clones/instances are the same but in a different place. set the same name and the importer will find them, the GEOMNODE must exist
*NAME string/geomnode-name
*MESH string/mesh-name
*WORLD_TM { # note: homogeneous 3x3 transformation/rotation matrix with appended translation, row-major; first three are rotation, last row is the position/translation vector
*TMROW0 f64/rx f64/ry f64/rz
*TMROW1 f64/rx f64/ry f64/rz
*TMROW2 f64/rx f64/ry f64/rz
*TMROW3 f64/tx f64/ty f64/tz
}
}

Ignored tokens[]

Keep in mind that some keywords and blocks like...

*OPTIONS {
    *COORD_SYSTEM LH
}

...and other random keys like MAP_HASALPHA are completely ignored by this version of the importer and silently discarded.

Mesh faces explained in detail[]

Then, the most complex sub-block is per-face properties and enumeration, which is described below:

*FACEFORMAT VTCMF

This is important and goes right before *FACE_LIST because it tells the importer which fields it actually has, the thing is pretty flexible. V (vertex count for these faces, this one is obligatory and the rest are optional, 4 is the usual value), T (has UV coordinates), C (has vertex colors), N (has normals), M (has materials), F (has face flags), S (has shader index).

Another thing that is also important and affects how the thing is parsed is setting the *FACELAYERSCOUNT at the beginning of the mesh. Remember that one entity/face can have various layers, think terrain blending/fade-off effects and things like that, where various textures get mixed in what looks like the same geometry/triangle.

But that's a thing for later.

  *FACEFORMAT VTCMF
  *FACE_LIST {
    3 1 2 3 0 1 2 1 2 3 8 65536
    
  V -
  V   -----
  T         -----
  C               -----
  M                     -
  F                       -----

You can see that in this case the vertex count for the face is 3, then after that there are three indexes, each of them point to a member of the *VERTEX_LIST table. So with that you can tell it which XYZ entry to use for each vertex.

Then the same thing for UV coordinates (T) and vertex colors. Each of them have three members because of the initial 3.

After that you have the material index (the 8th entry in the *MATERIALS block), and after that the special flags (65536 is the decimal version of 0x10000; only one bit is set there), which can be used to toggle stuff like if the face is part of a portal, of if it's water or invisible. Things like that.

But yeah, you can trim down all the optional parts except the V stuff.

  *FACEFORMAT V
  *FACE_LIST {
    3 1 2 3

This should work just like Wavefront OBJ, pretty much. You can keep adding letters as you need. Just with the 3 (the count) at the start.

Diagram[]

  *FACEFORMAT VTCNMSF
  *FACE_LIST {
    3 F1 F2 F3 T1 T2 T3 T1 T2 T3 C1 C2 C3 C1 C2 C3 N1 N2 N3 M1 M1 S1 F1
     
  V -                                                                                                     <- Number of vertices
  V   --------                                                          [vertex count                   ] <- Index of the XYZ position
                                                                                                             for each vertex
  T            ---L1--- ---L2---                                        [vertex count * number of layers] <- UV coordinate index
                                                                                                             (per layer)
  C                              ---L1--- ---L2---                      [vertex count * number of layers] <- Face colors (per layer)
  N                                                --------             [vertex count                   ] <- Face normals
  M                                                         L1 L2       [           1 * number of layers] <- Material index (per layer)
  S                                                               --    [    always 1                   ] <- Shader index
  F                                                                  -- [    always 1                   ] <- Face flags/bitfield
  

Notes[]

  • Only the V part is mandatory, you can pick and choose as long as you set the *FACEFORMAT.
    Probably a good idea to keep the same line ordering, though.
  • The indexed lists start at index zero, so 1 is the second one. To leave the index as invalid so that the importer picks the default value (for things like vertex colors, if you do this to materials it will error out) make it less than zero, generally -1.
  • The importer uses atoi(), so it will only accept base-10 integers. That's why flags don't use hex numbers. Probably, haven't tested it.
  • For the global .EIF material index see the common *MATERIALS block.
  • For the shader index see the current mesh's *FACESHADERS block.

References[]

Advertisement