pghalf stands for half floats with 1 bit sign 6 bit exponent 9 bit significand and a bias of 47.
Edit: added Nier Automata, TRANSFORMERS: Devastation PC, Teenage Mutant Ninja Turtles: Mutants in Manhattan, Astral Chain motion format. Identical to Bayonetta 2 except that for motion type 8 one index is stored in big endian format (WTF...)
Thanks to the administrator that gave me edit rights on my posts.
Bayonetta 1 (PC version):
Code: Select all
LittleEndian();
typedef uint16 pghalf;
struct {
char id[4]; // "mot\0"
uint16 flag; //sometimes seem to indicate that out of bound bone index are used no idea why.
int16 frameCount;
uint32 recordOffset;
uint32 recordNumber;
} header;
FSeek( header.recordOffset );
struct {
int16 boneIndex;
char index;
char flag;
int16 elemNumber;
int16 unknown; //always -1
union {
float flt;
uint32 offset;
} value;
} records[header.recordNumber];
local int i;
for ( i = 0; i < header.recordNumber; i++) {
// 0: constant value for each frame. The value is in records[i].value.flt. -1: only found on terminator (bone index 0x7fff).
if( records[i].flag == 0 || records[i].flag == -1)
continue;
FSeek( records[i].value.offset );
switch ( records[i].flag ) {
// usually one value per frame, if some are missing the last is to be repeated
case 1:
struct {
float values[records[i].elemNumber];
} interpol1;
break;
// quantized hermit spline coeffs values at key point index :
// value: p + dp * cp
// incoming derivative: m0 + dm0 * cm0
// outcoming derivative: m1 + dm1 * cm1
// if some ranges are missing at start or after last index, first value or last value should be repeated for missing values.
case 4:
struct {
struct {
float p;
float dp;
float m0;
float dm0;
float m1;
float dm1;
} values;
struct {
uint16 index; // absolute frame index
uint16 cp;
uint16 cm0;
uint16 cm1;
} keys[records[i].elemNumber];
} interpol4;
break;
// same as 4 with reduced precision and relative frame index encoding
case 6:
struct {
struct {
pghalf p;
pghalf dp;
pghalf m0;
pghalf dm0;
pghalf m1;
pghalf dm1;
} values;
struct {
ubyte index; // frame index relative to previous key
ubyte cp;
ubyte cm0;
ubyte cm1;
} keys[records[i].elemNumber];
} interpol6;
break;
// same as 6 but with absolute frame index (at least one relative frame index would have been > 255)
case 7:
struct {
struct {
pghalf p;
pghalf dp;
pghalf m0;
pghalf dm0;
pghalf m1;
pghalf dm1;
} values;
struct {
uint16 index; // absolute frame index
ubyte dummy;
ubyte cp;
ubyte cm0;
ubyte cm1;
} keys[records[i].elemNumber];
} interpol7;
break;
// unkown flag, I have yet to find another for Bayonetta
default:
break;
}
}
Bayonetta 2 (WiiU version):
Code: Select all
BigEndian();
typedef uint16 pghalf;
struct {
char id[4]; // "mot\0"
uint32 hash;
uint16 flag;
int16 frameCount;
uint32 recordOffset;
uint32 recordNumber;
uint32 unknown; // usually 0 or 0x003c0000, maybe two uint16
string animName; // found at most 12 bytes with terminating 0
} header;
FSeek( header.recordOffset );
struct RECORD{
int16 boneIndex;
char index;
char flag;
int16 elemNumber;
int16 unknown; //always -1
union {
float flt;
uint32 offset;
} value;
} records[header.recordNumber];
local int i;
for ( i = 0; i < header.recordNumber; i++) {
// 0: constant value for each frame. The value is in records[i].value.flt. -1: only found on terminator (bone index 0x7fff).
if( records[i].flag == 0 || records[i].flag == -1)
continue;
FSeek( header.recordOffset + i * sizeof(RECORD) + records[i].value.offset );
switch ( records[i].flag ) {
// usually one value per frame, if some are missing the last is to be repeated
case 1:
struct {
float values[records[i].elemNumber];
} interpol1;
break;
// same as 1 but with quantized data
// value: p + dp * cp;
case 2:
struct {
struct {
float p;
float dp;
} values;
uint16 cp[records[i].elemNumber];
} interpol2;
break;
// same as 2 but with reduced precision
case 3:
struct {
struct {
pghalf p;
pghalf dp;
} values;
ubyte cp[records[i].elemNumber];
} interpol3;
break;
// spline coeffs values at key point index :
// value: p
// incoming derivative: m0
// outcoming derivative: m1
// if some ranges are missing at start or after last index, first value or last value should be repeated for missing values.
case 4:
struct {
struct {
uint16 index; // absolute frame index
uint16 dummy;
float p;
float m0;
float m1;
} keys[records[i].elemNumber];
} interpol4;
break;
// same as 4 but with quantized values:
// value: p + dp * cp
// incoming derivative: m0 + dm0 * cm0
// outcoming derivative: m1 + dm1 * cm1
// if some ranges are missing at start or after last index, first value or last value should be repeated for missing values.
case 5:
struct {
struct {
float p;
float dp;
float m0;
float dm0;
float m1;
float dm1;
} values;
struct {
uint16 index; // absolute frame index
uint16 cp;
uint16 cm0;
uint16 cm1;
} keys[records[i].elemNumber];
} interpol5;
break;
// same as 5 with reduced precision
case 6:
struct {
struct {
pghalf p;
pghalf dp;
pghalf m0;
pghalf dm0;
pghalf m1;
pghalf dm1;
} values;
struct {
ubyte index; // absolute frame index
ubyte cp;
ubyte cm0;
ubyte cm1;
} keys[records[i].elemNumber];
} interpol6;
break;
// same as 6 with relative frame index encoding
case 7:
struct {
struct {
pghalf p;
pghalf dp;
pghalf m0;
pghalf dm0;
pghalf m1;
pghalf dm1;
} values;
struct {
ubyte index; // frame index relative to previous key
ubyte cp;
ubyte cm0;
ubyte cm1;
} keys[records[i].elemNumber];
} interpol7;
break;
// same as 7 but with absolute frame index (at least one relative frame index would have been > 255)
case 8:
struct {
struct {
pghalf p;
pghalf dp;
pghalf m0;
pghalf dm0;
pghalf m1;
pghalf dm1;
} values;
struct {
uint16 index; // absolute frame index
ubyte cp;
ubyte cm0;
ubyte cm1;
} keys[records[i].elemNumber];
} interpol8;
break;
// unkown flag, I have yet to find another for Bayonetta 2
default:
break;
}
}
Nier Automata/TRANSFORMERS: Devastation (PC version)/Bayonetta 2 Swotch/Metal Gear Rising: Revengeance:
Code: Select all
LittleEndian();
typedef uint16 pghalf;
struct {
char id[4]; // "mot\0"
uint32 hash;
uint16 flag;
int16 frameCount;
uint32 recordOffset;
uint32 recordNumber;
uint32 unknown; // usually 0 or 0x003c0000, maybe two uint16
string animName; // found at most 12 bytes with terminating 0
} header;
FSeek( header.recordOffset );
struct RECORD{
int16 boneIndex;
char index;
char flag;
int16 elemNumber;
int16 unknown; //always -1
union {
float flt;
uint32 offset;
} value;
} records[header.recordNumber];
local int i;
for ( i = 0; i < header.recordNumber; i++) {
// 0: constant value for each frame. The value is in records[i].value.flt. -1: only found on terminator (bone index 0x7fff).
if( records[i].flag == 0 || records[i].flag == -1)
continue;
FSeek( header.recordOffset + i * sizeof(RECORD) + records[i].value.offset );
switch ( records[i].flag ) {
// usually one value per frame, if some are missing the last is to be repeated
case 1:
struct {
float values[records[i].elemNumber];
} interpol1;
break;
// same as 1 but with quantized data
// value: p + dp * cp;
case 2:
struct {
struct {
float p;
float dp;
} values;
uint16 cp[records[i].elemNumber];
} interpol2;
break;
// same as 2 but with reduced precision
case 3:
struct {
struct {
pghalf p;
pghalf dp;
} values;
ubyte cp[records[i].elemNumber];
} interpol3;
break;
// spline coeffs values at key point index :
// value: p
// incoming derivative: m0
// outcoming derivative: m1
// if some ranges are missing at start or after last index, first value or last value should be repeated for missing values.
case 4:
struct {
struct {
uint16 index; // absolute frame index
uint16 dummy;
float p;
float m0;
float m1;
} keys[records[i].elemNumber];
} interpol4;
break;
// same as 4 but with quantized values:
// value: p + dp * cp
// incoming derivative: m0 + dm0 * cm0
// outcoming derivative: m1 + dm1 * cm1
// if some ranges are missing at start or after last index, first value or last value should be repeated for missing values.
case 5:
struct {
struct {
float p;
float dp;
float m0;
float dm0;
float m1;
float dm1;
} values;
struct {
uint16 index; // absolute frame index
uint16 cp;
uint16 cm0;
uint16 cm1;
} keys[records[i].elemNumber];
} interpol5;
break;
// same as 5 with reduced precision
case 6:
struct {
struct {
pghalf p;
pghalf dp;
pghalf m0;
pghalf dm0;
pghalf m1;
pghalf dm1;
} values;
struct {
ubyte index; // absolute frame index
ubyte cp;
ubyte cm0;
ubyte cm1;
} keys[records[i].elemNumber];
} interpol6;
break;
// same as 6 with relative frame index encoding
case 7:
struct {
struct {
pghalf p;
pghalf dp;
pghalf m0;
pghalf dm0;
pghalf m1;
pghalf dm1;
} values;
struct {
ubyte index; // frame index relative to previous key
ubyte cp;
ubyte cm0;
ubyte cm1;
} keys[records[i].elemNumber];
} interpol7;
break;
// same as 7 but with absolute frame index (at least one relative frame index would have been > 255)
case 8:
struct {
struct {
pghalf p;
pghalf dp;
pghalf m0;
pghalf dm0;
pghalf m1;
pghalf dm1;
} values;
struct {
BigEndian();
uint16 index; // absolute frame index (in big endian order!!!!)
LittleEndian();
ubyte cp;
ubyte cm0;
ubyte cm1;
} keys[records[i].elemNumber];
} interpol8;
break;
default:
break;
}
}
Using this code in the scripts displays the pgfloats correctly.
Code: Select all
// 1 sign bit, 6 exponent bit, 9 bit significand, 47 bias
typedef uint16 pghalf<read=pghalfRead>;
string pghalfRead( pghalf value )
{
double f = 0.0;
uint32 ui = value;
uint32 sign = ui & 0x8000;
ui = ui ^ sign;
uint32 exponent = ui & 0x7E00;
uint32 significand = ui ^ exponent;
int i;
int bit = 0x1 << 8;
for ( i = 1; i <= 9; i++ ) {
if ( bit & significand ) {
f += Pow(2.0, -i);
}
bit >>= 1;
}
string s;
int32 sexponent;
if ( exponent == 0x7E00 ) {
if (significand) {
SPrintf( s, "NaN" );
} else if (sign) {
SPrintf( s, "-Infinity" );
} else {
SPrintf( s, "+Infinity" );
}
} else if ( exponent != 0 ) {
exponent >>= 9;
sexponent = exponent;
sexponent -= 47;
f += 1.0;
f *= Pow(2.0, sexponent);
if (sign) {
f *= -1.0;
}
SPrintf( s, "%e", f );
} else {
if (significand) { //denorm
if( sign ) {
f *= -1;
}
f *= Pow(2.0, -46.0);
SPrintf( s, "%e", f );
} else if (sign) {
SPrintf( s, "-0.0" );
} else {
SPrintf( s, "0.0" );
}
}
return s;
}
Values Mapping:
for Bayonetta triplets are always fully given, seems to be relaxed in Bayonetta 2
index 0: translation along axis 0
index 1: translation along axis 1
index 2: translation along axis 2
index 3: rotation around axis 0
index 4: rotation around axis 1
index 5: rotation around axis 2
index 7: scaling along axis 0
index 8: scaling along axis 1
index 9: scaling along axis 2
missing tripplets (or values for Bayonetta 2) have default values:
translations have bone relative offset as default values
rotations have 0.0 as default values
scalings have 1.0 as default values
in noesis rotations are applied in this order:
-rotate[2]
+rotate[1]
-rotate[0]
Interpolation Mechanism:
interpolation values are given as:
between keys i and i+1 and thus index between keys[i].index and keys[i+1].index
Code: Select all
float p0 = values.p + values.dp * keys[i].cp
float m0 = values.m1 + values.dm1 * keys[i].cm1
float p1 = values.p + values.dp * keys[i+1].cp
float m1 = values.m0 + values.dm0 * keys[i+1].cm0
float t = (float)(index - keys[i].index)/(keys[i+1].index - keys[i].index)
float val = (2*t^3 - 3*t^2 + 1)*p0 + (t^3 - 2*t^2 + t)*m0 + (-2*t^3 + 3*t^2)*p1 + (t^3 - t^2)*m1;
Bone Index Translation Table:
at offset 0x40 of the wmb model file is a 3 level bone lookup table:
here is the c function I use for conversion (0x0fff is used as forbidden bone value):
Code: Select all
static inline short int Model_Bayo_DecodeMotionIndex(const short int *table, const short int boneIndex) {
short int index = table[(boneIndex >> 8) & 0xf];
if ( index != -1 ) {
index = table[((boneIndex >> 4) & 0xf) + index];
if ( index != -1 ) {
index = table[(boneIndex & 0xf) + index];
return index;
}
}
return 0x0fff;
}