SWTOR: .gr2 3D model file

From XentaxWiki
Revision as of 09:15, 6 May 2012 by imported>SWTOR fan (.gr2 file format specification!)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

This article contains a specification for the custom .gr2 file format used by the MMORPG Star Wars: The Old Republic to store the 3D models of the game.

General information

All model files can be found in the .tor archives swtor_main_art_*_1.tor and they are placed in the folder /resources/art/.

There are different kinds of model files:

  • Dynamic files are files that have an animation. They consist of creatures, NPCs, body parts as well as helmets, belts and other clothes.
  • FX (effects) files are used for animations, abilities like detonations or computer screens.
  • Static files are inanimate models. These include weapons, vehicles, buildings, furniture and small items. Static items are grouped by planet and can be divided into:
    • Arch models are big models like models that are used to create the game world.
    • Item models are smaller models. These can include streetlights, tables, benches, drinking cups, etc.
    • Speedtree models are trees or bushes. Only in a few cases (like branches or dead trees) are .gr2 files used; otherwise, .spt Speedtree files are used to describe the trees.

Each .gr2 file that has textures also references one or more Material files (these .mat files can all be found in /resources/art/shaders/materials/). The material files in turn contain the path of the diffuse and normal textures.

Notes on implementation

...

Full file format specification

Header

position: 0x00

  • bytes(4): always GAWB (47 41 57 42)
  • uint32 majorVersion: always 4 (04 00 00 00)
  • uint32 minorVersion: always 3 (03 00 00 00)
  • uint32 bnryOffset: offset of the BNRY / LTLE section


position: 0x0C

  • uint32 num50Offsets: The number of offsets in the 50 offset section
  • uint32 fileDependency:
    • 00 = no file dependency
    • 01 = model has a .clo file. The .clo file has the same path as the .gr2 file, but ends with .clo instead of .gr2
    • 02 = unknown file dependence (related to mags/bones)
  • uint16 numMeshes: The number of meshes that the model is made up of
  • uint16 numTextures: The number of textures referenced by the model
  • 20 zero bytes


position: 0x30

  • float boundingMinX: The minimum X value of the bounding box
  • float boundingMinY: The minimum Y value of the bounding box
  • float boundingMinZ: The minimum Z value of the bounding box
  • float constant1: always 00 00 80 3F = 1.0
  • float boundingMaxX: The maximum X value of the bounding box
  • float boundingMaxY: The maximum Y value of the bounding box
  • float boundingMaxZ: The maximum Z value of the bounding box
  • float constant1: always 00 00 80 3F = 1.0


position: 0x50

  • uint32 offset50offset: Offset of the 50 offset section
  • uint32 offsetMeshHeader: Offset of the mesh header, always 70 00 00 00 = 112
  • uint32 offsetMaterialNameOffsets: Offset of the Material offsets section where the offsets of the material names are given.
  • 4 zero bytes
  • uint32: unknown, nearly always zero (00 00 00 00)
  • 12 zero bytes

Mesh headers

The mesh headers contain information on each mesh (the number of meshes is given in numMeshes). Each mesh takes up 0x28 = 40 bytes.

position: offsetMeshHeader = 0x70

  • LOOP (for each tmpMesh in numMeshes) {
    • uint32 offsetMeshName: The offset of the mesh name, stored as string and terminated by a 00 byte
    • float: unknown
    • uint16 numUsedTextures: The number of textures used by this mesh; required for reading material usage information
    • uint16 numBones: The number of bones in this mesh
    • uint16: unknown, seems to be related to bones
    • uint16 numVertexBytes: The number of bytes used for storing a vertex; required for reading vertex section
    • uint32 numVertices: The total number of vertices used by this mesh; not required for reading model since this information is also given in material usage information
    • uint32 numFaces: The total number of faces (=face indices) used by this mesh; not required for reading model since this information is also given in material usage information
    • uint32: offsetVertices: The start address (offset) of the vertices of this mesh; required for reading vertices
    • uint32: offsetMaterialUsage: The start address (offset) of the Material usage information of this mesh; required for reading model with textures
    • uint32: offsetFaces: The start address (offset) of the faces (vertex indices) of this mesh; required for reading face indices
    • uint32: unknown offset
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

Material usage information

This section contains information on which faces of the mesh display which material/texture. Keep in mind that the number of faces given here stands for the faces; if you need the number of vertices for your model viewer, you'll have to multiple the values by 3. To get the material names, you also need to read the Material offsets section.

  • LOOP (for each tmpMesh in numMeshes) {
  • position: offsetMaterialUsage
    • LOOP (for each tmpTexture in numUsedTextures) {
      • uint32 materialFacesIndex: The starting index
      • uint32 materialNumFaces: The number of faces that use this material
      • uint32 textureId: This field specifies the id (as given in Material offsets) for this material
      • uint32 entryId: This field is just an enumerator/loop index. It starts with 0 for each mesh and increases by 1 for each texture.
      • float boundingMatMinX: The minimum X value of the bounding box of this part of the mesh
      • float boundingMatMinY: The minimum Y value of the bounding box of this part of the mesh
      • float boundingMatMinZ: The minimum Z value of the bounding box of this part of the mesh
      • float constant1: always 00 00 80 3F = 1.0
      • float boundingMatMaxX: The maximum X value of the bounding box of this part of the mesh
      • float boundingMatMaxY: The maximum Y value of the bounding box of this part of the mesh
      • float boundingMatMaxZ: The maximum Z value of the bounding box of this part of the mesh
      • float constant1: always 00 00 80 3F = 1.0
    • } END LOOP
  • } END LOOP

Material name offsets

This section contains a list of the offsets to the material names.

  • position: offsetMaterialNameOffsets
  • LOOP (for each tmpTexture in numTextures) {
    • uint32 textureNameOffset: The offset where the material name is stored, terminated by a 00 byte
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

Vertices

Depending on the vertex length (given in numVertexBytes), each vertex takes up 12, 24 and 32 bytes. This section contains not only the x/y/z positions, but also (for textured meshes) the texture u/v coordinates as well as the normals and tangents.

  • LOOP (for each tmpMesh in numMeshes) {
  • position: offsetVertices
    • LOOP (for each tmpVertex in numVertices) {
      • IF (numVertexBytes==12) {
        • float32 positionX: a float value specifying the x coordinate of the vertex
        • float32 positionY: a float value specifying the y coordinate of the vertex
        • float32 positionZ: a float value specifying the z coordinate of the vertex
      • } ELSE IF (numVertexBytes==24) {
        • float32 positionX: a float value specifying the x coordinate of the vertex
        • float32 positionY: a float value specifying the y coordinate of the vertex
        • float32 positionZ: a float value specifying the z coordinate of the vertex
        • bytes(8): possibly normals and tangents in an unknown encoding
        • float16 textureU: specifying the u coordinate for the texture
        • float16 textureV: specifying the v coordinate for the texture
      • } ELSE IF (numVertexBytes==32) {
        • float32 positionX: a float value specifying the x coordinate of the vertex
        • float32 positionY: a float value specifying the y coordinate of the vertex
        • float32 positionZ: a float value specifying the z coordinate of the vertex
        • bytea(8): unknown
        • bytes(8): possibly normals and tangents in an unknown encoding
        • float16 textureU: specifying the u coordinate for the texture
        • float16 textureV: specifying the v coordinate for the texture
      • } END IF
    • } END LOOP
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

Faces (vertex indices)

... Please note that these vertex indices are zero-based (that is, the first vertex is numbered 0 and not 1). Some programs (for example Wavefront .obj files) start counting at 1, so you'll have to increase the indices by one if you are using such a program.

  • LOOP (for each tmpMesh in numMeshes) {
  • position: offsetFaces
    • LOOP (for each tmpFace in numFaces) {
      • uint16 vertexId1: vertex id of first vertex
      • uint16 vertexId2: vertex id of second vertex
      • uint16 vertexId3: vertex id of third vertex
    • } END LOOP
    • (zero padding bytes to next 16-byte block)
  • } END LOOP

Bone list?

...

List of strings

This section contains multiple strings. It is not necessary trying to read this whole section completely; instead, it is best to just seek here whenever you encounter a string offset.

  • LOOP (for each tmpString) {
    • position: different for each string
    • string of unknown length, but always terminated by the 00 byte
    • 00 byte
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

50 offset section

This section contains a list of offset addresses along with the value that is found at that address. Each of these values is in turn an offset again. The first offset is always 50 00 00 00, which is the offset of this section.

This section may be important for reading the bones, but right now it appears to be redundant to me.

  • position: offset50offset
  • LOOP (for each tmpOffset in num50Offsets) {
    • uint32 offsetAddress: An offset in the value
    • uint32 offsetValue: The value that is stored at that offset, always another offset
  • } END LOOP
    • (zero padding bytes to next 16-byte block)

BNRY / LTLE section

This section is only present if the model contains a collision mesh. To be on the safe side, read the first length field. If it is zero, you can ignore this section, otherwise you can read it.

This section is completely unknown to me. The specification will allow you to parse the BNRY section without errors, but there is not much to do with the data yet, so you can just as well skip this section.

Regarding the BNRY / LTLE acronym, the only meaningful words I could come up with were bone array / little but this does not make much sense considering this section is connected to the collision mesh.

BNRY header

  • position: bnryOffset
  • uint32 bnryLength: length of the whole BNRY section, excluding these four bytes
  • bytes(4): always BNRY (42 4E 52 59)
  • bytes(4): always 00 00 00 02
  • bytes(4): always LTLE (4C 54 4C 45)

  • position: bnryOffset + 16
  • uint32: always 01 00 00 00
  • uint32: always 02 00 00 00
  • uint32 numBNRYsections: number of sections in the BNRY section
  • uint32: some unknown offset/length


  • uint32 numLongLines: the number of long lines in the BNRY section
  • uint32 totalNum21Lines: the total number of lines starting with 0x21/0xA1; this number is the sum of the 0x21 lines from each BNRY section
  • uint32: always 01 00 00 00
  • uint32: always 01 00 00 00


  • float32: unknown, x coordinate?
  • float32: unknown, y coordinate?
  • float32: unknown, z coordinate?
  • uint32: always 01 00 00 00


  • float32: unknown, x coordinate?
  • float32: unknown, y coordinate?
  • float32: unknown, z coordinate?
  • uint32: always 00 00 00 00

  • position: bnryOffset + 80
  • uint32: always 01 00 00 00
  • uint32: always 04 00 00 00
  • uint32: always 01 00 00 00
  • uint32: always 01 00 00 00


  • uint32: unknown
  • uint32 totalNum21Lines: the total number of lines starting with 0x21/0xA1; same as above
  • uint32: always 01 00 00 00
  • uint32: always 01 00 00 00


  • float32: unknown, x coordinate?
  • float32: unknown, y coordinate?
  • float32: unknown, z coordinate?
  • uint32: always 01 00 00 00


  • float32: unknown, x coordinate?
  • float32: unknown, y coordinate?
  • float32: unknown, z coordinate?
  • uint32: numBNRYsections: number of sections in the BNRY section; same as above

  • position: bnryOffset + 128
  • uint32: numBNRYsections: number of sections in the BNRY section; same as above
  • uint32 totalNum21Lines: the total number of lines starting with 0x21/0xA1; same as above
  • uint32 totalNum21Lines: the total number of lines starting with 0x21/0xA1; same as above
  • uint32: always 00 00 00 00


  • uint32: some unknown offset/length
  • uint32: always 10 00 00 00
  • uint16: always 00 00
  • uint16: always 80 00
  • byte: always 01
  • uint32: always 01 00 00 00


  • uint32 numLongLines: the number of long lines in the BNRY section; same as above
  • uint32 totalNum21Lines: the total number of lines starting with 0x21/0xA1; same as above
  • uint32: always 01 00 00 00
  • uint32: always 01 00 00 00


  • float32: unknown, x coordinate?
  • float32: unknown, y coordinate?
  • float32: unknown, z coordinate?
  • uint32: always 01 00 00 00


  • float32: unknown, x coordinate?
  • float32: unknown, y coordinate?
  • float32: unknown, z coordinate?
  • uint32: always 01 00 00 00

long lines

  • position: bnryOffset + 209
  • LOOP (for each tmpLongLine in numLongLines) {
    • uint32: some id
    • uint32: some id or small integer
    • uint32: always 01 00 00 00
    • int32: parent id? either FF FF FF FF or an id
  • uint32: unknown
    • int32: parent id? either FF FF FF FF or an id
  • uint32: unknown
  • float32: unknown
  • float32: unknown
  • } END LOOP

BNRY sections

  • uint32: always 00 00 00 00
  • uint32: always 01 00 00 00


  • LOOP (for each tmpBNRYsection in numBNRYsections) {
    • uint32 tmpBNRYsectionOffset: offset of the BNRY section from the beginning of the following loop
  • } END LOOP


  • LOOP (for each tmpBNRYsection in numBNRYsections) {
    • uint16 curNum21Lines: number of 21/A1 lines in this BNRY section
    • uint16 lengthNum21Lines: length of the 21/A1 lines section in this BNRY section
    • uint16 numVertexLines: number of vertex lines in the BNRY section
    • uint16 numVertexLines: number of vertex lines in the BNRY section; same as above
    • uint16: unknown offset/length
    • byte: always 00
    • uint16 numVertexLines: number of vertex lines in the BNRY section; same as above

    • uint32: always 01 00 00 00
    • LOOP (for each tmpVertexLine in numVertexLines) {
      • float32: x coordinate
      • float32: y coordinate
      • float32: z coordinate
    • } END LOOP

    • uint32: always 01 00 00 00
    • LOOP (for each tmp21Line in curNum21Lines) {
      • byte: either 0x21 or 0xA1
      • IF (first byte == 0x21) {
        • read 6 more unknown bytes
      • } ELSE IF (first byte == 0xA1) {
        • read 7 more unknown bytes
      • } END IF
    • } END LOOP
  • } END LOOP
  • uint32: often, but not always 01 00 00 00

String section

According to the length of the BNRY section, this section is no longer part of the BNRY section. However, this section is only present in the .gr2 file if the BNRY section exists.

  • uint32 numStrings: number of strings in this section
  • LOOP (for each tmpString in numStrings) {
    • uint32: tmpStringLength: length of this string
    • string with given length, terminated by a 00 byte
  • } END LOOP

Unknown mesh floats

Not yet confirmed, but this section seems to contain the bounding box for each mesh, as opposed to the global bounding box given at the beginning of the value.

  • LOOP (for each tmpMesh in numMeshes) {
    • float32: The minimum X value of the bounding box of this mesh
    • float32: The minimum Y value of the bounding box of this mesh
    • float32: The minimum Z value of the bounding box of this mesh
    • float32: The maximum X value of the bounding box of this mesh
    • float32: The maximum Y value of the bounding box of this mesh
    • float32: The maximum Z value of the bounding box of this mesh
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

EGCD section

  • bytes(4): always EGCD (45 47 43 44)
  • uint32: unknown, sometimes 05 00 00 00
  • uint32: offset of BNRY/LTLE section

[END OF FILE]