GR2 File Structure - SWTOR-Slicers/WikiPedia GitHub Wiki

The structure of a .gr2 file will vary slightly depending on which of the 3 variations it is (listed above). The structure of a .gr2 is useful if you are looking to create something such as a model viewer, or are just interested in the unique file format!

Header

position: 0x00

  • byte[4]: file identifier, always GAWB (47 41 57 42), which stands for GAWB = BWAG = BioWare Austin Geometry
  • uint32: major version, always 4
  • uint32: minor version, always 3
  • uint32 offsetBNRY: offset of the BNRY / LTLE section

position: 0x10

  • uint32 numCachedOffsets: The number of offsets in the Cached offsets section
  • uint32: type of .gr2 file:
    • 00 = default .gr2 file
    • 01 = model has a .clo file. (refer to above definitions)
    • 02 = this is a skeleton file (see /resources/art/dynamic/spec/)
  • uint16 numMeshes: The number of meshes that the model is made up of
  • uint16 numMaterials: The number of materials referenced by all meshes of the model
  • uint16 numBones: if it is a skeleton files, read the number of skeleton bones here
  • uint16 numAttachs: The number of objects that are attached to bones
  • byte[16]: 16 zero bytes

position: 0x30

BoundingBox The global bounding box, the whole model is contained inside it

position: 0x50

  • uint32 offsetCachedOffsets: Offset of the Cached offsets section
  • uint32 offsetMeshHeader: Offset of the Mesh information section, usually 0x70, or zero, if there are no meshes.
  • uint32 offsetMaterialNameOffsets: Offset of the Material offsets section where the offsets of the material names are given, or zero if there are no materials.
  • uint32 offsetBoneStructure: Offset of the Skeleton structure section (usually 0x70), only when this file is a skeleton file, otherwise zero
  • uint32 offsetAttachments: Offset of the Attached objects section, or zero, if there are not attached objects.
  • (zero padding bytes to next 16-byte block)

Skeleton structure

This section is only contained if the file is a skeleton file (the number of bones is given in numBones). Each bone takes up 0x88 = 136 bytes.

position: offsetBoneStructure = 0x70

  • LOOP (FOR EACH bone IN numBones) {
    • uint32 offsetBoneName: The offset of the bone name, stored as string and terminated by a 00 byte
    • int32 parentBoneIndex: The index of the parent bone, or -1 if it is the root. Indices start at zero.
    • float32[32]: 32 unknown floats
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

Mesh information

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 mesh IN numMeshes) {
    • uint32 offsetMeshName: The offset of the mesh name, stored as string and terminated by a 00 byte
    • float32: unknown
    • uint16 numPieces: The number of pieces this mesh is made up, equal to the number of materials used by this mesh (required for reading mesh piece information section)
    • uint16 numUsedBones: The number of bones from the whole skeleton that are connected to this mesh
    • uint16: bitFlag1: value is 0 if this mesh has bone names, otherwise value is 128
    • uint16 vertexSize: The number of bytes used for storing a vertex (required for reading vertex section)
      • 12 = just the X/Y/Z coordinates of this vertex (e.g. for collision meshes)
      • 24 = X/Y/Z coordinates and texture positions of this vertex (e.g. for static models)
      • 32 = X/Y/Z coordinates, texture positions and bone connections of this vertex (e.g. for dynamic, animated models)
      • 36 = X/Y/Z coordinates, texture positions and bone connections of this vertex (e.g. for dynamic, animated models), plus second set of UVs
      • 40 = X/Y/Z coordinates, texture positions and bone connections of this vertex (e.g. for dynamic, animated models), plus second and third set of UVs
    • uint32 numVertices: The total number of vertices used by this mesh (not required for reading model since this information is also given in mesh piece information)
    • uint32 numIndices: The total number of vertex indices used by this mesh. Divide this number by 3 to get number of faces (not required for reading model since this information is also given in mesh piece information)
    • uint32 offsetMeshVertices: The start address (offset) of the vertices of this mesh (required for reading vertices)
    • uint32 offsetMeshPieces: The start address (offset) of the Mesh piece information of this mesh (required for reading models with textures)
    • uint32 offsetIndices: The start address (offset) of the indices of this mesh (required for reading face indices)
    • uint32 offsetBones: The start address (offset) of the bone list of this mesh
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

Mesh piece 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 mesh IN numMeshes) { position: mesh.offsetMeshPieces
    • LOOP (FOR EACH piece IN mesh.numPieces) {
      • uint32: The starting index where the faces for this piece start, values range from 0 to mesh.numFaces/3 - 1
      • uint32: The number of faces that this piece is made up of
      • int32: If this piece is textured, this field specifies the id (related to the Material offsets section) for this material, otherwise -1
      • int32: This field is just an enumerator/loop index. It starts with 0 for each mesh and increases by 1 for each piece, or is set to -1, if it is not textured
      • BoundingBox: The bounding box for this mesh piece
    • } END LOOP
  • } END LOOP

Material name offsets

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

position: offsetMaterialNameOffsets

  • LOOP (FOR EACH texture IN numTextures) {
    • uint32 offsetMaterialName: The offset where the material name is stored, terminated by a 00 byte
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

Attached objects

This section is optional. It contains a list of objects that are attached to certain bones.

position: offsetAttachments

  • LOOP (FOR EACH attachedObject IN numAttachs) {
    • uint32: offset where the name of the attached object is stored, string of variable length, terminated by a 00 byte
    • uint32: offset where the name of the bone the object attaches to is stored, string of variable length, terminated by a 00 byte
    • float32[16]: 16 floats for a 4x4 matrix
  • } 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 mesh IN numMeshes) {
    • position: mesh.offsetVertices
    • LOOP (FOR EACH vertex IN mesh.numVertices) {
      • IF (mesh.vertexSize == 12) {
        • float32: a float value specifying the x coordinate of the vertex
        • float32: a float value specifying the y coordinate of the vertex
        • float32: a float value specifying the z coordinate of the vertex
      • } ELSE IF (mesh.vertexSize == 24) {
        • float32: a float value specifying the x coordinate of the vertex
        • float32: a float value specifying the y coordinate of the vertex
        • float32: a float value specifying the z coordinate of the vertex
        • byte[8]: possibly normals and tangents in an unknown encoding
        • float16: specifying the u coordinate for the texture
        • float16: specifying the v coordinate for the texture
      • } ELSE IF (mesh.vertexSize == 32) {
        • float32: a float value specifying the x coordinate of the vertex
        • float32: a float value specifying the y coordinate of the vertex
        • float32: a float value specifying the z coordinate of the vertex
        • byte: weight of the first bone joint (0 to 255)
        • byte: weight of the second bone joint (0 to 255)
        • byte: weight of the third bone joint (0 to 255)
        • byte: weight of the fourth bone joint (0 to 255)
        • byte: index of the first bone joint that influences the vertex's position
        • byte: index of the second bone joint that influences the vertex's position
        • byte: index of the third bone joint that influences the vertex's position
        • byte: index of the fourth bone joint that influences the vertex's position
        • uint8: normals (4 values, X, Y, Z, W)
        • uint8: tangents (4 values, X, Y, Z, W)
        • float16: specifying the u coordinate for the texture
        • float16: specifying the v coordinate for the texture
      • } ELSE IF (mesh.vertexSize == 36) {
        • float32: a float value specifying the x coordinate of the vertex
        • float32: a float value specifying the y coordinate of the vertex
        • float32: a float value specifying the z coordinate of the vertex
        • byte: weight of the first bone joint (0 to 255)
        • byte: weight of the second bone joint (0 to 255)
        • byte: weight of the third bone joint (0 to 255)
        • byte: weight of the fourth bone joint (0 to 255)
        • byte: index of the first bone joint that influences the vertex's position
        • byte: index of the second bone joint that influences the vertex's position
        • byte: index of the third bone joint that influences the vertex's position
        • byte: index of the fourth bone joint that influences the vertex's position
        • uint8: normals (4 values, X, Y, Z, W)
        • uint8: tangents (4 values, X, Y, Z, W)
        • float16: specifying the u coordinate for the texture
        • float16: specifying the v coordinate for the texture
        • float16: specifying the second u coordinate for the texture
        • float16: specifying the second v coordinate for the texture
      • } ELSE IF (mesh.vertexSize == 40) {
        • float32: a float value specifying the x coordinate of the vertex
        • float32: a float value specifying the y coordinate of the vertex
        • float32: a float value specifying the z coordinate of the vertex
        • byte: weight of the first bone joint (0 to 255)
        • byte: weight of the second bone joint (0 to 255)
        • byte: weight of the third bone joint (0 to 255)
        • byte: weight of the fourth bone joint (0 to 255)
        • byte: index of the first bone joint that influences the vertex's position
        • byte: index of the second bone joint that influences the vertex's position
        • byte: index of the third bone joint that influences the vertex's position
        • byte: index of the fourth bone joint that influences the vertex's position
        • uint8: normals (4 values, X, Y, Z, W)
        • uint8: tangents (4 values, X, Y, Z, W)
        • float16: specifying the u coordinate for the texture
        • float16: specifying the v coordinate for the texture
        • float16: specifying the second u coordinate for the texture
        • float16: specifying the second v coordinate for the texture
        • float16: specifying the third u coordinate for the texture
        • float16: specifying the third v coordinate for the texture
      • } END IF
    • } END LOOP
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

Faces

This section contains the faces, that is the number of surfaces of the model. Three vertices (= three point) generate one face, so for each face you will have to read three integers to get the index for each point.

Note that mesh.numFaces given under Mesh information stands for the number of indices, so you'll have to divide it by 3 if you want to get the number of faces. In the Mesh piece information section howver, mesh.piece.numFaces stands for the number of the faces. To get the number of indices, multiply it by three.

Please note that the 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 the index at 1, so you'll have to increase the indices by one if you are using such a program.

  • LOOP (FOR EACH mesh IN numMeshes) {
    • position: mesh.offsetFaces
    • LOOP (FOR EACH face in mesh.numFaces/3) {
      • uint16: index of first vertex
      • uint16: index of second vertex
      • uint16: index of third vertex
    • } END LOOP
    • (zero padding bytes to next 16-byte block)
  • } END LOOP

Bone list

If this file is a normal file, not a skeleton file, but is animated (like humans or creatures), the file will contain a list of the bones used by the current model.

  • position: offsetBones
  • LOOP (FOR EACH bone IN numBones) {
    • uint32: offset of bone name as string, terminated by 00
    • float32[6]: six unknown floats, maybe related to position/rotation of bone
  • } END LOOP
  • (zero padding bytes to next 16-byte block)

List of strings

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

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

Cached offsets 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: offsetCachedOffsets
  • LOOP (for each tmpOffset in numCachedOffsets) {
    • 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 binary / little but I am not sure about that.

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 byte[4]: four zero bytes 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]

Custom structures BoundingBox 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: Always 1.0 (00 00 80 3F) 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 float32: Always 1.0 (00 00 80 3F)