I am using motion tracker data which is lovingly supplied to me in X-forward, Y-right, Z-up coordinate space. However, Epic’s third person hero character skeleton does not use this. So, I convert the data. No problem.
This struct contains all the data I need to make a conversion.
struct FQuatConversion
{
int32 X;
int32 Y;
int32 Z;
bool bXNeg;
bool bYNeg;
bool bZNeg;
FRotator Offset;
FQuatConversion(int32 x, int32 y, int32 z, bool bx, bool by, bool bz, FRotator off)
{
X = x;
Y = y;
Z = z;
bXNeg = bx;
bYNeg = by;
bZNeg = bz;
Offset = off;
}
FQuatConversion()
{
X = 1;
Y = 2;
Z = 3;
bXNeg = false;
bYNeg = false;
bZNeg = false;
Offset = FRotator(0.f, 0.f, 0.f);
}
};
Here are some examples for use with Epic’s skeleton
//X Forawrd, Y Left, Z Down hand_l
LHOffset = FQuatConversion(1, 2, 3, false, true, true, FRotator(0.f, 90.f, 180.f));
//X Back, Y Left, Z Up hand_r
RHOffset = FQuatConversion(1, 2, 3, true, true, false, FRotator(0.f, -90.f, 0.f));
//X Up, Y Forward, Z Right pelvis
HipsOffset = FQuatConversion(3, 1, 2, false, false, false, FRotator(90.f, 0.f, 0.f));
//X Up, Y Forward, Z Right spine_3
TorsoOffset = FQuatConversion(3, 1, 2, false, false, false, FRotator(90.f, 0.f, 0.f));
//X Up, Y Forward, Z Right head
HeadOffset = FQuatConversion(3, 1, 2, false, false, false, FRotator(90.f, 0.f, 0.f));
Then this function can carry out the conversion
FQuat AProtoCharacter::CharacterToMeshQuat(FQuat InQuat, FQuatConversion InCon)
{
float NX, NY, NZ, NW;
switch (InCon.X)
{
case 1:
NX = InQuat.X;
break;
case 2:
NX = InQuat.Y;
break;
case 3:
NX = InQuat.Z;
break;
default:
NX = 0.f;
break;
}
switch (InCon.Y)
{
case 1:
NY = InQuat.X;
break;
case 2:
NY = InQuat.Y;
break;
case 3:
NY = InQuat.Z;
break;
default:
NY = 0.f;
break;
}
switch (InCon.Z)
{
case 1:
NZ = InQuat.X;
break;
case 2:
NZ = InQuat.Y;
break;
case 3:
NZ = InQuat.Z;
break;
default:
NZ = 0.f;
break;
}
NW = InQuat.W;
if (InCon.bXNeg)
{
NX *= -1;
}
if (InCon.bYNeg)
{
NY *= -1;
}
if (InCon.bZNeg)
{
NZ *= -1;
}
FQuat NewQuat = FQuat(NX, NY, NZ, NW);
//Add the Offset
NewQuat = InCon.Offset.Quaternion() * NewQuat;
return NewQuat;
}
It works great. Perfect mapping of motion to the skeleton. No gimbal lock.
Now my problem is, there are scenarios where I need to work backward from the converted data.
So, I try to reverse the order of operations. Unfortunately, I get a crash when I try to use the following function.
FQuat AProtoCharacter::MeshToCharacterQuat(FQuat InQuat, FQuatConversion InCon)
{
float NX = 0.f, NY = 0.f, NZ = 0.f, NW = 0.f;
float IX = 0.f, IY = 0.f, IZ = 0.f, IW = 0.f;
FQuat NewQuat;
FQuat NQuat = InCon.Offset.Quaternion().Inverse();
//First, remove that offset;
FQuat IQuat = NQuat * InQuat;
//Second unflip the axis
if (InCon.bXNeg)
{
IX = IQuat.X * -1;
}
else
{
IX = IQuat.X;
}
if (InCon.bYNeg)
{
IY = IQuat.Y * -1;
}
else
{
IY = IQuat.Y;
}
if (InCon.bZNeg)
{
IZ = IQuat.Z * -1;
}
else
{
IZ = IQuat.Z;
}
IW = InQuat.W;
IQuat = FQuat(IX, IY, IZ, IW);
//Third unrearrange the axis
switch (InCon.X)
{
case 1:
NX = IQuat.X;
break;
case 2:
NY = IQuat.X;
break;
case 3:
NZ = IQuat.X;
break;
default:
break;
}
switch (InCon.Y)
{
case 1:
NX = IQuat.Y;
break;
case 2:
NY = IQuat.Y;
break;
case 3:
NZ = IQuat.Y;
break;
default:
break;
}
switch (InCon.Z)
{
case 1:
NX = IQuat.Z;
break;
case 2:
NY = IQuat.Z;
break;
case 3:
NZ = IQuat.Z;
break;
default:
break;
}
NW = IQuat.W;
//Fourth, put everything together
NewQuat = FQuat(NX, NY, NZ, NW);
return NewQuat;
}
Access violation - code c0000005 (first/second chance not available)
UE4Editor_Proto!AProtoCharacter::MeshToCharacterQuat() + 112 bytes
Looks like the line FQuat IQuat = NQuat * InQuat; is what is causing the crash. No idea why.
Any help is much appreciated.