solved it using iteration.
Also, had to project weapon forward onto spines forward plane as the origin.
added clamping and smoothing too: :)
Transform s = spine;
Transform w = weapon.transform;
float distance = float.PositiveInfinity;
float minDistance = 0.01f;
int iteration = 30;
Quaternion oldRotation = lastAimRotation;
while (distance > minDistance && iteration > 0) {
Vector3 aimOrigin = w.TransformPoint(weapon.attackPoint);
Vector3 origin;
if (!LinePlaneIntersection(out origin, aimOrigin, -w.forward, s.forward, s.position)) {
//if for some readon the weapon cannot intersect with spine forward plane, just use the aim origin
//hopefully this never happens as weapon should always be in front of spine
origin = aimOrigin;
}
Vector3 weaponForward = w.forward;
Vector3 aimForward = target - origin;
Quaternion rot = Quaternion.FromToRotation(weaponForward, aimForward);
distance = rot.eulerAngles.sqrMagnitude;
iteration--;
s.rotation = rot * s.rotation;
}
//clamp angles
Vector3 angles = s.localEulerAngles;
//offset the clamped angles by any upper body offset
//we may need this if characters aren't aligned forward when rotation is identity
angles.x = ClampAngle(angles.x, minUpperBodyAngles.x + upperBodyAngleOffset.x, maxUpperBodyAngles.x + upperBodyAngleOffset.x);
angles.y = ClampAngle(angles.y, minUpperBodyAngles.y + upperBodyAngleOffset.y, maxUpperBodyAngles.y + upperBodyAngleOffset.y);
angles.z = ClampAngle(angles.z, minUpperBodyAngles.z + upperBodyAngleOffset.z, maxUpperBodyAngles.z + upperBodyAngleOffset.z);
s.localEulerAngles = angles;
//smooth if we have easing
if (aimRotationEasing > 0) {
s.rotation = Quaternion.Slerp(lastAimRotation, s.rotation, Time.deltaTime * aimRotationEasing);
lastAimRotation = s.rotation;
}
private static float ClampAngle(float ang, float min, float max) {
if (ang > 180) ang -= 360;
if (max > 180) max -= 360;
if (min > 180) min -= 360;
ang = Mathf.Clamp(ang, min, max);
return ang < 0 ? ang + 360 : ang;
}
private static bool LinePlaneIntersection(out Vector3 intersection, Vector3 linePoint, Vector3 lineVec, Vector3 planeNormal, Vector3 planePoint) {
float dotNumerator = Vector3.Dot(planePoint - linePoint, planeNormal);
float dotDenominator = Vector3.Dot(lineVec, planeNormal);
if (dotDenominator != 0.0f) {
intersection = linePoint + lineVec.normalized * (dotNumerator / dotDenominator);
return true;
}
else {
intersection = Vector3.zero;
return false;
}
}