Terminology
i call your "left/right rotation point of turret" joint "horizontal joint" and your "Up/down rotation point of barrel" joint "vertical joint".
Calculate with Forward kinematic (transformations leading to (previously unknown) orientation/position) the Position and Upvector and Forwardvector (this vector points from the horizontal joint forward).
Inverse Part After that use the Position and the Upvector and the Forwardvector to calculate the vertical and horizontal target rotations.
Formulas for the inverse Part of the case when the horizontal joint is directly on the vertical joint
(Pseudocode - untested)
// input
Vector3f UpVector; // upvector (normalized)
Vector3f ForwardVector; // (normalized)
Vector3f TargetPosition; // (absolute target position
Vector3f FKPosition; // forward kinematic Position of the turret
// is the absolute position in worldspace of the turret head
// temp
Vector3f SideVector; (normalized)
Vector3f TargetDiffUnnormalized; // difference to the target
Vector3f TargetDiff; // (normalized)
// output
float HorizontalRotationRawRad;
float VerticalRotationRawRad;
// calculation
// first we calculate the sidevector which is perpendicular to the up and ForwardVector
SideVector = UpVector.Cross(ForwardVector).normalized();
TargetDiffUnnormalized = TargetPosition - FKPosition;
TargetDiff = TargetDiffUnnormalized.normalize();
// we "project" the TargetDiff vector to the Plane which is described by the ForwardVector and SideVector
float ProjectedForward = ForwardVector.dot(TargetDiff)
float ProjectedSide = SideVector.dot(TargetDiff)
// now we need to normalize the 2d Vector which is described by ProjectedForward and ProjectedSide because the length of it is not 1.0 and we would get wrong results
Vector2f ProjectedHorizontal = new Vector2f(ProjectedForward, ProjectedSide);
ProjectedHorizontal = ProjectedHorizontal.normalized();
// NOTE< here the components _could_ be wrong and the multiplication _could_ be wrong, depending on the direction of rotation and so on >
HorizontalRotationRawRad = acos(ProjectedHorizontal.Y);
if( ProjectedHorizontal.X < 0.0f )
{
HorizontalRotationRawRad *= -1.0f;
}
//-----
Vector3f HorizontalDirection = ForwardVector.scale(ProjectedHorizontal.X) + SideVector.scale(ProjectedHorizontal.Y)
// NOTE< HorizontalDirection should be normalized, check if wanted with assertion to make sure >
VerticalRotationRawRad = acos(HorizontalDirection.dot(TargetDiff));
Edit 1
The math and code and the description for the case if the horizontal joint is not on the vertical joint (the more general case) follows here.
If you look at the 2d case from above (so to say on the plane of the horizontal joint in the 3d world) you notice that when you rotate your horizontal joint the vertical joint spins on a circle.
So now we search the rotation offset, because you need to add the rotation to the HorizontalRotationRawRad
variable to get the real rotation.
To archive this you need to project the target point in the 3d space to the horizontal joint plane (you can archive this with the calculation of the intersection between the plane and a ray which starts at the target point and points in the direction of the normal of the plane).
I call this point you get as the result of the intersection PlaneTarget
.
Now you can calculate the distance from FKPosition
to PlaneTarget
and i call it c
.
For the following calculation you need to calculate only once on gameloading or on the construction time of the tank or whenever only the distance from the horizontal joint to the vertical joint and the angle of the two (from 90 to 180 degrees) and the side of the vertical joint.
To calculate the offset angle we use the law of cosines:
We know c and b and gamma and we search alpha.
after some math...
c² = a² +b² - 2ab cos(gamma)
rearanging->
c² = a² + b² - a * 2b cos(gamma)
this is a quadratic equation, solve it with some math or a solver which you wrote to get c
we know a, notice to catch the cases if the target is inside the circle (c < b) because it wouldn't make much sense.
now we need to calculate the angle alpha with some more math...
a² = b² + c² - 2bc cos(alpha) | + 2bc cos(alpha) | - a²
2bc cos(alpha) = b² + c² - a²
alpha = acos(b² + c² - a² / 2bc)
now we can add/subtract alpha from/to VerticalRotationRawRad to get the correct angle for the horizontal joint.
The calculation of the vertical joint is a bit easier, use the angle of the horizontal joint to rotate the point of the vertical joint in worldspace.
Then just do a dot product between the normalized direction of the barrel and the difference to the target position (of course the result doesn't have a sign, if it should have one just use the horizontal plane and look if the target position is above or below it).