mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-02 19:38:24 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
129 lines
3.9 KiB
C#
129 lines
3.9 KiB
C#
namespace Sandbox.VR;
|
|
|
|
/// <summary>
|
|
/// Updates the parameters on an <see cref="SkinnedModelRenderer"/> on this GameObject based on the skeletal data from SteamVR.
|
|
/// Useful for quick hand posing based on controller input.
|
|
/// </summary>
|
|
[Title( "VR Hand" )]
|
|
[Category( "VR" )]
|
|
[EditorHandle( "materials/gizmo/hand.png" )]
|
|
[Icon( "waving_hand" )]
|
|
public class VRHand : Component
|
|
{
|
|
/// <summary>
|
|
/// Which <see cref="SkinnedModelRenderer"/> to use when updating this component
|
|
/// </summary>
|
|
[RequireComponent]
|
|
public SkinnedModelRenderer SkinnedModelComponent { get; set; }
|
|
|
|
/// <summary>
|
|
/// Represents a controller to use when fetching skeletal data (finger curl/splay values)
|
|
/// </summary>
|
|
public enum HandSources
|
|
{
|
|
/// <summary>
|
|
/// The left controller
|
|
/// </summary>
|
|
Left,
|
|
|
|
/// <summary>
|
|
/// The right controller
|
|
/// </summary>
|
|
Right
|
|
}
|
|
|
|
/// <summary>
|
|
/// Which hand should we use to update the parameters?
|
|
/// </summary>
|
|
[Property]
|
|
public HandSources HandSource { get; set; } = HandSources.Left;
|
|
|
|
/// <summary>
|
|
/// What motion range should we use to update the parameters?
|
|
/// </summary>
|
|
[Property]
|
|
public MotionRange MotionRange { get; set; } = MotionRange.Hand;
|
|
|
|
private static readonly Dictionary<VRHandJoint, string> JointNameMap = new Dictionary<VRHandJoint, string>
|
|
{
|
|
{ VRHandJoint.Wrist, "hand_{0}" },
|
|
|
|
{ VRHandJoint.ThumbMetacarpal, "finger_thumb_0_{0}" },
|
|
{ VRHandJoint.ThumbProximal, "finger_thumb_1_{0}" },
|
|
{ VRHandJoint.ThumbDistal, "finger_thumb_2_{0}" },
|
|
|
|
{ VRHandJoint.IndexMetacarpal, "finger_index_meta_{0}" },
|
|
{ VRHandJoint.IndexProximal, "finger_index_0_{0}" },
|
|
{ VRHandJoint.IndexIntermediate, "finger_index_1_{0}" },
|
|
{ VRHandJoint.IndexDistal, "finger_index_2_{0}" },
|
|
|
|
{ VRHandJoint.MiddleMetacarpal, "finger_middle_meta_{0}" },
|
|
{ VRHandJoint.MiddleProximal, "finger_middle_0_{0}" },
|
|
{ VRHandJoint.MiddleIntermediate, "finger_middle_1_{0}" },
|
|
{ VRHandJoint.MiddleDistal, "finger_middle_2_{0}" },
|
|
|
|
{ VRHandJoint.RingMetacarpal, "finger_ring_meta_{0}" },
|
|
{ VRHandJoint.RingProximal, "finger_ring_0_{0}" },
|
|
{ VRHandJoint.RingIntermediate, "finger_ring_1_{0}" },
|
|
{ VRHandJoint.RingDistal, "finger_ring_2_{0}" },
|
|
|
|
{ VRHandJoint.LittleMetacarpal, "finger_pinky_meta_{0}" },
|
|
{ VRHandJoint.LittleProximal, "finger_pinky_0_{0}" },
|
|
{ VRHandJoint.LittleIntermediate, "finger_pinky_1_{0}" },
|
|
{ VRHandJoint.LittleDistal, "finger_pinky_2_{0}" },
|
|
};
|
|
|
|
private static string GetBoneName( VRHandJoint joint, HandSources hand )
|
|
{
|
|
if ( JointNameMap.TryGetValue( joint, out string jointNameFormat ) )
|
|
{
|
|
string handSide = hand == HandSources.Left ? "L" : "R";
|
|
return string.Format( jointNameFormat, handSide );
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
private void UpdatePose()
|
|
{
|
|
if ( SkinnedModelComponent?.Model is null )
|
|
return;
|
|
|
|
var source = (HandSource == HandSources.Left) ? Input.VR.LeftHand : Input.VR.RightHand;
|
|
var offset = (HandSource == HandSources.Left) ? new Transform( Vector3.Zero, Rotation.From( 0, 0, 180 ) ) : new Transform( Vector3.Zero, Rotation.From( 0, 0, 0 ) );
|
|
|
|
foreach ( var entry in source.GetJoints( MotionRange ) )
|
|
{
|
|
var boneName = GetBoneName( entry.Joint, HandSource );
|
|
if ( string.IsNullOrEmpty( boneName ) )
|
|
continue;
|
|
|
|
var bone = SkinnedModelComponent.Model.Bones.AllBones.FirstOrDefault( x => x.Name.Equals( boneName, StringComparison.OrdinalIgnoreCase ) );
|
|
|
|
if ( bone is null )
|
|
continue;
|
|
|
|
var tx = GameObject.WorldTransform.ToLocal( entry.Transform );
|
|
tx = tx.WithPosition( tx.Position + offset.Position );
|
|
tx = tx.WithRotation( tx.Rotation * offset.Rotation );
|
|
|
|
SkinnedModelComponent.SceneModel?.SetBoneOverride( bone.Index, tx );
|
|
}
|
|
}
|
|
|
|
protected override void OnUpdate()
|
|
{
|
|
if ( !Enabled || Scene.IsEditor || !Game.IsRunningInVR )
|
|
return;
|
|
|
|
UpdatePose();
|
|
}
|
|
|
|
protected override void OnPreRender()
|
|
{
|
|
if ( !Enabled || Scene.IsEditor || !Game.IsRunningInVR )
|
|
return;
|
|
|
|
UpdatePose();
|
|
}
|
|
}
|