The issue is that a player could be turning their head to look over their left or right shoulder, but if the player wants to walk forward, they should walk in the direction their torso faces, not the direction their head faces. This way, you can be looking over your shoulder as you’re running away from a monster.
I’m wondering if anyone has figured out a way to derive the torso orientation of the player based off of the motion controller positions and orientations and the HMD position and orientations?
I’ve got a 70% working solution at the moment, but the missing 30% bothers me a lot because there’s a lot of edge cases I don’t account for.
I’ve started looking at the extremes of physical movement to get an idea on the torso direction. For example, your head can’t yaw more than about 90 degrees left or right, so the torso yaw will always be within 90 degrees of the head yaw. I also looked at the hand position extremes of the left and right hands, but couldn’t really make any generalized inferences about that. Elbows cause complexity because you can reach your hand behind your back by bending your elbow. However, at the far extremes, a hand can trace out a hemisphere with the center located at the shoulder socket. There are some physical limitations though, for example, you can’t touch your elbows behind your back, and your left arm can’t reach across further than your right arm, etc. I don’t know how to make use of these facts to derive torso rotation however (why can’t VR just come with a belt or something?)
Here’s my current code implementation.
void AWizard::GetPlayerTorso(FVector& WorldLocation, FVector& DirVec, FRotator& Rotation)
{
FVector BHForward = GetActorRotation().Vector();
BHForward.Z = 0;
FVector Gaze = GazeYaw();
//if we're not tracking both hands, we can't get a hand midpoint!
if (LeftMC->IsTracked() && RightMC->IsTracked())
{
FVector LMC = GetMCPos(true);
FVector RMC = GetMCPos(false);
//We draw a line from one hand to the other and then divide it in half to get the midpoint on that line.
FVector HandMidPos = (LMC + RMC) / 2.0f;
BHForward = GetActorLocation() - HandMidPos;
BHForward.Z = 0;
BHForward.Normalize();
//the hands are behind the actor center point
if (FVector::DotProduct(BHForward, Gaze) < 0)
{
BHForward *= -1;
}
}
//get the forward facing vector for the motion controller. Note that if you flip the controller upside down, we have to flip the forward vector.
FVector LMCForward = FVector();
if (LeftMC->IsTracked()) //if we lost tracking, we ignore this hand until it comes back
{
LMCForward = LeftMC->GetForwardVector();
if (FVector::DotProduct(LMCForward, Gaze) < 0) LMCForward *= -1;
LMCForward.Z = 0; //remove pitch
LMCForward.Normalize();
}
FVector RMCForward = FVector();
if (RightMC->IsTracked())
{
RMCForward = RightMC->GetForwardVector();
if (FVector::DotProduct(RMCForward, Gaze) < 0) RMCForward *= -1;
RMCForward.Z = 0; //remove pitch
RMCForward.Normalize();
}
//FVector TorsoVec = RMCForward + LMCForward + BHForward + Gaze + LastTorso;
//FVector TorsoVec = BHForward * 3 + Gaze;
FVector TorsoVec = BHForward * 0.1f + Gaze + RMCForward * 0.1f + LMCForward * 0.1f + LastTorso * 5.f;
TorsoVec.Z = 0; //remove any pitch
TorsoVec.Normalize();
//run a sanity check against the gaze yaw vector: We know it can't be more than 90 degrees due to human physical constraints
if (FVector::DotProduct(Gaze, TorsoVec) < 0)
{
//houston, we got a problem... either the player became an owl or our math is wrong
//let's just take the vector average between the gaze vector and the last good torso vector
TorsoVec = Gaze + LastTorso;
TorsoVec.Z = 0;
TorsoVec.Normalize();
}
LastTorso = TorsoVec;
DirVec = TorsoVec;
Rotation = FRotator(0, UKismetMathLibrary::MakeRotFromX(TorsoVec).Yaw, 0);
}