.XG

From Gitaroo Pals
Jump to navigation Jump to search

.XG is the 3D model file format used in Gitaroo Man and Gitaroo Man Wan.

Data types used in this article (all little-endian):

  • PString: a Pascal string, i.e. a unsigned byte (0 to 255) followed by that many bytes of text (Shift-JIS encoding)
  • int32: signed 4-byte integer
  • uint8: unsigned 1-byte integer
  • uint32: unsigned 4-byte integer
  • float32: 4-byte floating-point decimal

All xgNode types and names and stuff are stored as Pstrings.

  • In particular, node names occasionally contain Shift-JIS encoded Japanese text. For XG files from the original game, it is safe to assume all node names are Shift-JIS encoded.


Todo:

  • add note on how to correct winding order (for stuff like Blender)
  • add section on how to convert stuff to a different coordinate system (XG has left-handed Y-up, stuff like Blender has right-handed Z-up)

File structure

Header

8 bytes XGBv1.00

(In addition to XGB, XGA also exists, presumably some sort of human-readable ascii version, but the format is unknown.)

Node declarations

Every node that is used in the model needs to be declared beforehand.

The declarations are made of 3 Pstrings:

  • Pstring xgNode_type
  • Pstring xgNode_name (a unique identifier for this node)
  • Pstring ";"


Example: xgDagTransform BaNorenBip ;

Node definitions

After a node is declared, it can be filled with data.

The definitions are made of:

  • PString xgNode_type (same as in the declaration)
  • Pstring xgNode_name (same as in the declaration)
  • Pstring"{"
  • properties of this xgNode (see Node Types for the properties each xgNode type can have, their data types, and their expected formats)
  • Pstring "}"


Examples: (newlines and indents are added for readability)

xgDagTransform BaNorenBip
{
    inputMatrix BaNorenBip$matrix_0 outputMatrix
}
xgTexture puma_nt$texture_0
{
    url puma_nt.imx
    mipmap_depth <uint32: 127>
}

DAG setup

The DAG setup determines which nodes actually show up visibly when the model is loaded. Only DAG nodes (i.e. xgDagMesh and xgDagTransform nodes) can be added here; any other nodes have to be part of a DAG node's hierarchy (connected as an input attribute, like inputGeometry or inputTexture)

The DAG setup is made of:

  • Pstring "dag"
  • Pstring "{"
  • Any number of DAG setup entries. A single entry can be either:
    • PStrings xgDagMesh_name, "[", "]"
    • PStrings xgDagTransform_name, "[", any number of DAGNode_name, "]"
  • Pstring "}"


Example: (newlines and indentation are added for readability)

dag
{
    T_puma_parts01$mesh_0 [ ]
    T_puma_speakerLB [ T_puma_speakerLB$mesh_0 ]
    T_puma_speakerRB [ T_puma_speakerRB$mesh_0 ]
    T_puma_speakerRF [ T_puma_speakerRF$mesh_0 ]
    T_puma_speakerLF [ T_puma_speakerLF$mesh_0 ]
}

A single model's DAG setup can contain multiple chains of nested xgDagTransform nodes.

Node Types

xgBgGeometry

density

float32 density; //always 1000.0?

vertices

uint32 vertexFlags; //bitwise combination of vertex attributes

uint32 numVerts;

float vertices[numVerts][?];

vertexFlags: Indicates the contents of each vertex (and thereby its size). Only the first byte (LSB) is used. If the bits of that byte go 87654321:

  • 1: enable position (x, y, z, w_unused*)
  • 2: enable normal (x, y, z)
  • 3: enable color (r, g, b, a)
  • 4: enable texture coordinate (s, t)

numVerts: number of vertices in the vertex array

vertices: a list of numVerts vertices. Contents of each vertex depends on vertexType. The order is always position, normal, color, texcoord. The values are all float32s. Colors and texcoords are from 0.0 to 1.0.

*w_unused: The 4th coordinate/position appears to be unused. Replacing all instances in a model with 0, random values, negative and positive infinity, and NaN had no visible effect when testing in an emulator.

inputGeometry

(optional, and there can be more than 1)

Syntax:

inputGeometry <name of a xgEnvelope or xgVertexInterpolator/xgNormalInterpolator/xgTexCoordInterpolator/xgShapeInterpolator> outputGeometry


The cases I've seen have been:

  • multiple xgEnvelopes (any time a mesh is deformed by multiple bones)
  • xgVertexInterpolator + xgNormalInterpolator + xgTexCoordInterpolator (FLYING_O.XG)
  • just one xgShapeInterpolator (Stage 2 U1_FIN_A.XG)


(I may have missed some interpolator types. such as xgColorInterpolator which I don't think any model uses)

xgBgMatrix

An xgBgMatrix is used to transform an xgBone or another xgBgMatrix.

  • It is used to set a pose (position, rotation, and scale). This pose deviates from the xgBone's rest pose and will therefore deform the mesh.
  • position/rotation/scale and inputPosition/Rotation/Scale both set the pose in a similar manner.
    • inputPosition/Rotation/Scale: Made of keyframes (list of values), so it does an animated pose over time. Overrides respective position/rotation/scale if both exist.
    • position/rotation/scale: Has a single value, so it sets a still, unanimated pose. If not overridden by a respective inputPosition/Rotation/Scale, it will hold the same value for the duration of the animation (e.g. some bones of BGC3_3PCO.XG).
    • ("respective" meaning an inputPosition overrides a position and so on)

position

float position[3]; //xyz

rotation

float rotation[4]; //quaternion XYZW

scale

float scale[3]; //xyz

inputPosition

(optional)

inputPosition <name of a xgVec3Interpolato> outputVec3

inputRotation

(optional)

inputRotation <name of a xgQuatInterpolator> outputQuat

inputScale

(optional)

inputScale <name of a xgVec3Interpolato> outputVec3

inputParentMatrix

(optional) Used if this matrix is the child of (i.e. is transformed by) another xgBgMatrix.

inputParentMatrix <name of a xgBgMatrix> outputMatrix

xgBone

restMatrix

float restMatrix[16];

inputMatrix

inputMatrix <name of a xgBgMatrix> outputMatrix

xgDagMesh

primType

uint32 primType;

  • 4 = kick verts separately
  • 5 = kick verts as group

This affects what triListData/triStripData/triFanData contains:

  • (kick separately) for each triList/Strip/Fan, there is a u32 value, followed by that number of vertex indices.
  • (kick as groups) first u32 value is a starting vertex index. Then, for each triList/Strip/Fan, a u32 value for the number of vertices to use (starting from the last index of the previous triList/Strip/Fan).

primCount

uint32 primCount; //always 0 in the game's models

primData

uint32 size; //always 0

uint32 primData[size]; //always 0

triFanCount

uint32 triFanCount; //number of triFans

triFanData

uint32 size;

uint32 triFanData[size];


size: equal to 0, unless triFanCount > 0

  • (kick separately) triFanCount + the sum of the vertex counts for each triFan
  • (kick as groups) triFanCount + 1

triFanData: see primType

triStripCount

uint32 triStripCount; //number of triStrips

triStripData

uint32 size;

uint32 triStripData[size];


size: equal to 0, unless triStripCount > 0

  • (kick separately) triStripCount + the sum of the vertex counts for each triStrip
  • (kick as groups) triStripCount + 1

triStripData: see primType

triListCount

uint32 triListCount; //number of triLists

triListData

(A triList is a list where every 3 vertices makes a triangle.)

uint32 size;

uint32 triListData[size];


size: equal to 0, unless triListCount > 0

  • (kick separately) triListCount + the sum of the vertex counts for each triList
  • (kick as groups) triListCount + 1

triListData: see primType

cullFunc

uint32 cullFunc;

  • 0 = disable culling (i.e. triangles are double-sided)
  • 1 = cull clockwise-winding triangles (i.e. CCW is front-facing)
  • 2 = cull counterclockwise-winding triangles (i.e. CW is front-facing)

inputGeometry

inputGeometry <name of a xgBgGeometry> outputGeometry

Contains the vertices to make this mesh.

inputMaterial

inputMaterial <name of a xgMaterial or xgMultiPassMaterial> outputMaterial

Contains the material for this mesh.

xgDagTransform

inputMatrix

inputMatrix <name of a xgBgMatrix> outputMatrix

It's optional! (ST00 NOR.XG's xgDagTransforms "band14" & "noren_body" contain nothing at all.)

xgEnvelope

Verts are taken from a xgBgGeometry to form an "envelope", sort of an invisible mesh used to deform a visible mesh.

startVertex

uint32 startVertex;

startVertex: vertex offset when using vertices to make this envelope. (i.e. first vertex to use from the xgBgGeometry)

weights

uint32 numweights;

float32 weights[numweights][4];

numweights is equal to the number of vertices that make up this envelope.

vertexTargets

uint32 numRawValues;

int32 vertexTargetsRaw[numRawValues];

vertexTargetsRaw contains sequences of one or positive integer values, and after each sequence there is a -1 value before the next sequence begins. As such, the final value in vertexTargetsRaw will always be -1.


So by using -1 as a delimiter, you can turn vertexTargetsRaw into the easier-to-understand vertexTargets. For example:

vertexTargetsRaw = [304, -1, 306, 309, -1, 307, -1, ...]

becomes

vertexTargets = [
 [304],
 [306, 309],
 [307],
 ...
]

The purpose of vertexTargets is to use one set of vertices to deform another set of vertices (i.e. to perform animations).

  • The first set of vertices come from this xgEnvelope. The second set of vertices come from another (completely different/separate) xgBgGeometry (which often belongs to a visible mesh, thus the mesh will be animated).
  • The two must be linked together as follows: otherXgBgGeometry.inputGeometry = thisXgEnvelope.
  • Once they are linked, thisXgEnvelope.inputGeometry.vertices will animate otherXgBgGeometry.vertices.


Specifically, each vertex from this xgEnvelope is linked to one or more vertices of the other xgBgGeometry. Take the previous example of:

vertexTargets = [
 [304],       # index 0
 [306, 309],  # index 1
 [307],       # index 2
 ...
]

This means:

  • thisXgEnvelope's vert 0 is linked to otherXgBgGeometry's vert 304
  • thisXgEnvelope's vert 1 is linked to otherXgBgGeometry's verts 306 and 309
  • thisXgEnvelope's vert 2 is linked to otherXgBgGeometry's vert 307
effect of xgEnvelope.weights...

Question: Where do the values from xgEnvelope.weights come into play?

Answer, maybe:

  • Gitaroo Man models only ever use inputMatrix1. However, an xgEnvelope could potentially have any or all of inputMatrix1, inputMatrix2, inputMatrix3, and inputMatrix4.
  • Does that mean the envelope can be deformed by up to 4 different inputMatrixs at once?
    • And is the influence of each inputMatrix determined by 'weights' for each envelope vertex?
      • e.g. Probably a weight entry of (1.0, 0.0, 0.0, 0.0) means the envelope vertex is deformed only by inputMatrix1 and ignores the other 3 matrices.
    • Yes, , it seems plausible that placing a nonzero value in the 2nd/3rd/4th part of a weight entry would cause the weight entry's associated envelope vertex to be deformed by the respective inputMatrix2/3/4.
    • (I'm just not sure if all 4 weight entry values are supposed to add up to 1.0 or not...)

inputMatrix1

inputMatrix1 <name of a xgBone> envelopeMatrix

Contains the matrix that animates this envelope.

inputGeometry

inputGeometry <name of a xgBgGeometry> outputGeometry

Contains the vertices to make this envelope.

xgMaterial

blendType

uint32 blendType;

  • 00 - Normal (aka Mix) **
  • 01 - *Add +a
  • 02 - *Reverse Subtract -a
    • Like Subtract, but (src - dest) instead of (dest - src)
    • (only used once, with flags 02 in ST02 EFF_FOKEMURI.XG)
  • 03 - *Subtract -a
    • (only used once, with flags 02 in ST01 EFEV_F_FADE.XG)
  • 04 - *Unknown **
    • Usually like 00 Normal but much darker, almost black. Same/similar effect as dividing the mesh color by 8?
    • When flags→texalpha is enabled, it's just like 00 Normal, no longer dark
    • (only used once, with flags 03 in ST07 EFEV_KEMURI.XG, so the dark effect is never seen)
  • 05 - *Normal (w/ alpha) [indicates material alpha?] +a

Notes:

  • +a : uses diffuse alpha, texture alpha, and vertex color alpha, regardless of flags value
  • -a : ignores diffuse alpha, texture alpha, and vertex color alpha, regardless of flags value
  • * : Screws up mesh-sorting, just like flags→texalpha
  • ** affected by flags→texalpha

shadingType

uint32 shadingType;

  • 00 - unshaded
  • 01 - shaded
  • 02 - shaded
  • 03 - vertex colors (unshaded)
  • 04 - ambient only? (no diffuse/specular?)

As far as I can tell, 01 is less common than 02 but looks exactly the same. (If there are differences in specular, there's no way of seeing it because Gitaroo Man stages don't really do specular lighting, I think?)

Vertex colors can be used with texture alpha-- vertex colors do not appear on the fully transparent parts. (e.g. ST02BGKA)

diffuse

float32 red;

float32 green;

float32 blue;

float32 alpha;

Effects:

  • if shadingType is 0 (unshaded), the entire mesh is multiplied by the diffuse color regardless of lighting.
  • shadingTypes 1 and 2 (shaded) are lit normally by diffuse RGB.
  • shadingTypes 3 (vertex colors) and 4 (ambient only) ignore diffuse RGBA entirely (ignoring alpha even in the case below).
  • alpha is ignored unless flags→texalpha is enabled or blendType is 5 (normal w/ alpha), in which case the entire mesh is multiplied by the alpha value regardless of lighting.

specular

float32 red;

float32 green;

float32 blue;

float32 exponent;

flags

uint32 flags;

flags: if the bits are 87654321...

  • bit 1: flags→texalpha
Indicates this material's use of texture alpha.
0 = ignore alpha
1 = use alpha
This value is usually redundant.
Only if blendType is 0 or 4 does this value make a difference-- in both cases it forces Normal blending w/ alpha (plus sorting).
Texture alpha is always enabled by blendType 5, regardless of flags (though I think in practice it only gets used for material alpha?). Also regardless of flags, Additive always uses texture alpha, and Multiply & Subtract always ignore i
  • bit 2: unknown, it's always 1
  • bits 3-8: unknown and always 0, probably nothing

textureEnv

uint32: textureEnv

  • 0 = UV coordinates
  • 1 = sphere/environment map

uTile

uint32 uTile; //always 0?

vTile

uint32 vTile; //always 0?

inputTexture

(optional)

Determines the texture this xgMaterial uses. Syntax is:

inputTexture <name of a xgTexture> outputTexture

Note: It appears that when multiple inputTextures are provided, only the final one appears visibly on the model. (Tested with FLYING_O.XG)

xgMultiPassMaterial

Layering multiple materials. Frequently used to give models a reflective glass effect with both a regular and sphere-mapped texture.

inputMaterial

(can be one or more inputMaterials.)

  • Game is a little weird with this sometimes; some xgMultiPassMaterials have only 1 inputMaterial, in which case you may as well just use a regular xgMaterial by itself. Also the racecars in Stage 1 contain a xgMultiPassMaterial with like 6(?) inputMaterials layered which IIRC taxes performance more, but since some are completely opaque only like the top 2 inputMaterials are even visible.

inputMaterial <name of a xgMaterial> outputMaterial

xgNormalInterpolator

type

uint32 type;

Interpolation between keyframes:

  • 0 = no interpolation
  • 1 = interpolation (linear?)
  • 2+ = likely same as 1

times

Todo:

The times property may be a way for an interpolator of any kind to have relatively few keyframes (keys) cover a much larger range of time than is usually possible.

  • For example, FLYING_O.XG has 409 keys in each of its interpolators... except those belonging to UV_ANI_OBJ (the blinking forehead panel). Those interpolators have only 65 keys, but they also have 409 times, presumably so they will still cover the same range of time as the other interpolators.

uint32 numtimes;

float times[numtimes];

keys

uint32 numkeys;

keys[numkeys];

each key is:

  • uint32 numNormals; //equal to number of targets, below
  • float[numNormals][3];

targets

uint32 numtargets;

uint32 targets[numtargets];

Used with xgVertex/Normal/TexCoord/ShapeInterpolator to match keys with the parent xgBgGeometry's vertices. (Much like vertexTargets, but without -1s to separate them into groups.)

inputTime

inputTime <name of a xgTime> outputTime

xgQuatInterpolator

type

uint32 type;

Interpolation between keyframes:

  • 0 = no interpolation
  • 1 = interpolation (linear?)
  • 2+ = likely same as 1

keys

uint32 numKeys; float keys[numKeys][4]; //quaternions XYZW

inputTime

inputTime <name of a xgTime> outputTime

xgShapeInterpolator

//found in: katestill, st02 eff_fokemuri, st02 u1_fin_a, st03, st08, etc?

type

uint32 type;

Interpolation between keyframes:

  • 0 = no interpolation
  • 1 = interpolation (linear?)
  • 2+ = likely same as 1

times

uint32 size;

float times[size];

keys

uint32 numkeys; //same as number of times?

keys[numkeys];

each key is, um...:

  • uint32 vertexType;
  • uint32 numVerts;
  • float vertices[numVerts][vertSize]; //vertSize is gotten from vertexType


inputTime

inputTime <name of a xgTime> outputTime

xgTexCoordInterpolator

type

uint32 type;

Interpolation between keyframes:

  • 0 = no interpolation
  • 1 = interpolation (linear?)
  • 2+ = likely same as 1

times

uint32 numtimes;

float times[numtimes];

keys

uint32 numkeys;

keys[numkeys]; //same as numtargets, below?

each key is:

  • uint32 numVerts
  • float texcoords[numVerts][2];

targets

uint32 numtargets; uint32 targets[numtargets];

inputTime

inputTime <name of a xgTime> outputTime

xgTexture

url

PString url; //name of the .IMX file use

mipmap_depth

uint32 mipmap_depth; // always 127?

xgTime

This node appears once per file, and only when necessary i.e. in XG files containing animation data. So it's not present in files like Stage 1's U1ROOM.XG.

numFrames

float numFrames;

This value encompasses the sum of the length of all animations. (The XG file contains just 1 long combined animation, which will get split into separate animations via extra data in the XGM file)

time

float32 time;

Always 0. This value is used by interpolators as the means of knowing what frame to draw. (It is always zero inside a written file, but it will change in memory as a chart progresses.)

xgVec3Interpolator

type

uint32 type;

Interpolation between keyframes:

  • 0 = no interpolation
  • 1 = interpolation (linear?)
  • 2+ = likely same as 1

keys

uint32 numkeys; float keys[numkeys][3]; //position or scale

inputTime

inputTime <name of a xgTime> outputTime

xgVertexInterpolator

Only affects vertex positioning - contrary to its name.

type

uint32 type;

Interpolation between keyframes:

  • 0 = no interpolation
  • 1 = interpolation (linear?)
  • 2+ = likely same as 1

times

uint32 numtimes;

float times[numtimes];

keys

uint32 numkeys;

each key goes like:

  • uint32 numPositions;
  • float32 [numPositions][3];

targets

like with xgTexCoordInterpolator and xgNormalInterpolator...

Unused node types

Node types that exist in the executable but aren't used in any .XG files:

  • xgAnimation
  • xgBaseMaterial
  • xgBgColor
  • xgColor
  • xgColorArray
  • xgColorInterpolator
  • xgDagGroup
  • xgDagToonMesh
  • xgEnvVertex
  • xgEnvVertexArray
  • xgFloat
  • xgFloatArray
  • xgFloatInterpolator
  • xgGeometry
  • xgGeometryArray
  • xgInt
  • xgIntArray
  • xgMatrix
  • xgMatrix3
  • xgNodeRef
  • xgParticleEmitter
  • xgQuat
  • xgQuatArray
  • xgStringArray
  • xgToonEdge
  • xgToonEdgeArray
  • xgVec2
  • xgVec2Array
  • xgVec2ArrayArray
  • xgVec3
  • xgVec3Array
  • xgVec3ArrayArray
  • xgVec4
  • xgVec4Array