Weapons/ToolGun/Modes/Hydraulic/BallSocketPairEntity.cs

A component that aligns two ball model renderers to face each other and orients a line renderer (shaft) between their "end" bones each update. It computes direction, picks an up vector to avoid gimbal when nearly vertical, sets world rotations, and updates the shaft's points.

Native Interop

/// <summary>
/// A pair of ball sockets, we try to align the balls towards eachother.
/// </summary>
public sealed class BallSocketPairEntity : Component
{
	[Property]
	public SkinnedModelRenderer BallModelA { get; set; }

	[Property]
	public SkinnedModelRenderer BallModelB { get; set; }

	[Property]
	public LineRenderer ShaftRenderer { get; set; }

	protected override void OnUpdate()
	{
		if ( !BallModelA.IsValid() || !BallModelB.IsValid() ) return;

		var ballDir = (BallModelB.GameObject.WorldPosition - BallModelA.GameObject.WorldPosition).Normal;
		var ballUp = MathF.Abs( Vector3.Dot( ballDir, Vector3.Up ) ) > 0.99f ? Vector3.Forward : Vector3.Up;

		BallModelA.GameObject.WorldRotation = Rotation.LookAt( -ballDir, ballUp ) * Rotation.FromPitch( -90f );
		BallModelB.GameObject.WorldRotation = Rotation.LookAt( ballDir, ballUp ) * Rotation.FromPitch( -90f );

		if ( ShaftRenderer.IsValid() )
		{
			var endA = BallModelA.GetBoneObject( "end" );
			var endB = BallModelB.GetBoneObject( "end" );

			if ( endA.IsValid() && endB.IsValid() )
			{
				ShaftRenderer.Points = [endA, endB];
			}
		}
	}
}