private void SetOnSteeringWheel(Matrix steerMatrix, float swRadius) { // point on the wheel, matrix var l = Matrix.Translation(swRadius * _side, 0, 0) * steerMatrix; var lPos = l.GetTranslationVector(); // vertical offset of point on the wheel after rotation var armUp = Vector3.TransformNormal(Vector3.UnitX, steerMatrix); // approximate direction var approxDirection = lPos - _approxPoint; // move hand var handUp = Vector3.Normalize(Vector3.TransformNormal(Vector3.UnitX * 6f + Vector3.UnitY * _side, l)); var handXFix = Vector3.Normalize(new Vector3(approxDirection.X, Math.Min(approxDirection.Y, 0f) * 5f, approxDirection.Z.Abs() + 0.2f)).X; handXFix = ((handXFix.Abs() - 0.1f) * 6f).Saturate() * handXFix.Sign() * 5f; var handLookAt = Vector3.TransformCoordinate(Vector3.UnitZ * 5f, steerMatrix) + // really far into the wheel Vector3.UnitY * (4f + armUp.Y * 2f) * (armUp.X * 2f).Saturate() + // sort of upwards depending on how high it’s on the wheel Vector3.UnitX * handXFix; // on the side to avoid breaking _hand.LookAt(handLookAt, handUp); var downK = (_side * -armUp.Y / 0.5f - 0.5f).Saturate(); _indexf.Set(0.8f - downK * 0.3f); _middle.Set(0.9f - downK * 0.8f); _ring.Set(1f - downK); _pinkie.Set(1f - downK); if (_side > 0f) { _ds = $"v: {armUp}\r\nn: {handUp}\r\nx fix: {handXFix}\r\na.p.: {_approxPoint}\r\nl.p.: {lPos}\r\na.d.: {approxDirection}"; } // after moved, update target point var targetPoint = _hand.Matrix.GetTranslationVector() + GetThumbsOffset(steerMatrix, swRadius); MoveHand(targetPoint, armUp, handUp); // move hand, again _hand.LookAt(handLookAt, handUp); }
private void MoveHand(Vector3 targetPoint, Vector3 up, Vector3 handUp) { _targetPoint = targetPoint; _up = up; _handUp = handUp; // static stuff var handLength = _armLength + _forearmLength + _forearmEndLength; // approximate direction var approxDirection = targetPoint - _approxPoint; // move shoulder var approxDistance = approxDirection.Length(); var shoulderFix = Smooth(Math.Max(approxDistance - handLength + 0.1f, 0f) / 0.2f); _clave.LocalMatrix = Matrix.RotationYawPitchRoll(_side * 17f.ToRadians(), (-12f - 40f * shoulderFix).ToRadians(), (_side > 0 ? -95f : 95f).ToRadians()) * Matrix.Translation(0.02f * _side, 0.11f + 0.08f * shoulderFix, 0.05f + 0.05f * shoulderFix); var armPoint = _arm.Matrix.GetTranslationVector(); var armDelta = targetPoint - armPoint; // comparing distance to point with hand length var distance = armDelta.Length(); var relative = distance / handLength; if (relative > _smoothFrom) { var leftMax = 1f - _smoothFrom; var left = relative - _smoothFrom; var smooth = left / leftMax; var smoother = -0.5f + MathF.Sqrt(1f + 3f * smooth) / 2f; distance = handLength * (_smoothFrom + smoother * leftMax); } /*if (distance > handLength * _smoothFrom && distance < handLength * _smoothStretch) { * var left = handLength - distance; * var leftK = (left / (1f - _smoothFrom)).Saturate(); * * // leftK = 0 → handLength * _smoothFrom * // leftK + very small x → handLength * _smoothFrom + very small x * // leftK = _smoothFrom - 1 → handLength * _smoothStretch * * // var stretchTo = _smoothStretch; * distance = handLength * _smoothFrom + leftK * (_smoothStretch - _smoothFrom) * handLength; * }*/ var overOffset = MathF.Sqrt(Math.Max(MathF.Pow(handLength, 2f) - MathF.Pow(distance, 2f), 0f)); // normals: direction, bottom normal for elbow offset var direction = Vector3.Normalize(armDelta); var verticalOffsetFixed = up; verticalOffsetFixed.Y = _side * verticalOffsetFixed.Y.Abs(); verticalOffsetFixed.Z += _side; var armBottom = Vector3.Normalize(Vector3.Cross(direction, -Vector3.Normalize(verticalOffsetFixed))); // moving shoulder var armUp = (-armBottom + Vector3.UnitY * 2f) / 3f * _side; var armLookAt = armPoint + Vector3.Normalize(targetPoint - armPoint) * _armLength + armBottom * overOffset * _armLength / handLength; _arm.LookAt(armLookAt, armUp); _forearm.LookAt(targetPoint, Vector3.UnitY * _side); _forearmEnd.LookAt(targetPoint, handUp); }