Skip to content

Commit

Permalink
New mod: Better Fingers Tracking
Browse files Browse the repository at this point in the history
Default muscles values to zero
Embedding of debug symbols
  • Loading branch information
SDraw committed Mar 25, 2024
1 parent d6e52fe commit 2aaac8f
Show file tree
Hide file tree
Showing 27 changed files with 1,457 additions and 36 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Merged set of MelonLoader mods for ChilloutVR.
|:---------:|:----------:|:--------------:| :----------------------------------------------------------------|
| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.7 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| ✔ Yes |
| [Avatar Synced Look](/ml_asl/README.md) | ml_asl | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_asl.dll)| ✔ Yes |
| [Better Fingers Tracking](/ml_bft/README.md) | ml_bft | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_bft.dll)| On review |
| [Desktop Head Tracking](/ml_dht/README.md) | ml_dht | 1.2.1 [:arrow_down:](../../releases/latest/download/ml_dht.dll) | ✔ Yes |
| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.6 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes |
| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.1.0 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes |
Expand Down
4 changes: 2 additions & 2 deletions ml_amt/ml_amt.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
Expand Down
5 changes: 5 additions & 0 deletions ml_asl/ml_asl.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
<Version>1.0.1</Version>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>

<ItemGroup>
<None Remove="resources\mod_menu.js" />
</ItemGroup>
Expand Down
89 changes: 89 additions & 0 deletions ml_bft/AssetsHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEngine;

namespace ml_bft
{
static class AssetsHandler
{
static readonly List<string> ms_assets = new List<string>()
{
"ovr_fingers.asset",
"oxr_fingers.asset"
};

static readonly Dictionary<string, AssetBundle> ms_loadedAssets = new Dictionary<string, AssetBundle>();
static readonly Dictionary<string, GameObject> ms_loadedObjects = new Dictionary<string, GameObject>();

public static void Load()
{
Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;

foreach(string l_assetName in ms_assets)
{
try
{
Stream l_assetStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + l_assetName);
if(l_assetStream != null)
{
MemoryStream l_memorySteam = new MemoryStream((int)l_assetStream.Length);
l_assetStream.CopyTo(l_memorySteam);
AssetBundle l_assetBundle = AssetBundle.LoadFromMemory(l_memorySteam.ToArray(), 0);
if(l_assetBundle != null)
{
l_assetBundle.hideFlags |= HideFlags.DontUnloadUnusedAsset;
ms_loadedAssets.Add(l_assetName, l_assetBundle);
}
else
MelonLoader.MelonLogger.Warning("Unable to load bundled '" + l_assetName + "' asset");
}
else
MelonLoader.MelonLogger.Warning("Unable to get bundled '" + l_assetName + "' asset stream");
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Warning("Unable to load bundled '" + l_assetName + "' asset, reason: " + e.Message);
}
}
}

public static GameObject GetAsset(string p_name)
{
GameObject l_result = null;
if(ms_loadedObjects.ContainsKey(p_name))
{
l_result = Object.Instantiate(ms_loadedObjects[p_name]);
l_result.SetActive(true);
l_result.hideFlags |= HideFlags.DontUnloadUnusedAsset;
}
else
{
foreach(var l_pair in ms_loadedAssets)
{
if(l_pair.Value.Contains(p_name))
{
GameObject l_bundledObject = (GameObject)l_pair.Value.LoadAsset(p_name, typeof(GameObject));
if(l_bundledObject != null)
{
ms_loadedObjects.Add(p_name, l_bundledObject);
l_result = Object.Instantiate(l_bundledObject);
l_result.SetActive(true);
l_result.hideFlags |= HideFlags.DontUnloadUnusedAsset;
}
break;
}
}
}
return l_result;
}

public static void Unload()
{
foreach(var l_pair in ms_loadedAssets)
Object.Destroy(l_pair.Value);
ms_loadedAssets.Clear();
}
}
}
199 changes: 199 additions & 0 deletions ml_bft/FingerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.InputManagement;
using System.Collections.Generic;
using UnityEngine;

namespace ml_bft
{
class FingerSystem
{
struct RotationOffset
{
public Transform m_target;
public Transform m_source;
public Quaternion m_offset;
}

static readonly List<HumanBodyBones> ms_leftFingerBones = new List<HumanBodyBones>()
{
HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,
HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,
HumanBodyBones.LeftMiddleProximal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,
HumanBodyBones.LeftRingProximal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,
HumanBodyBones.LeftLittleProximal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal
};
static readonly List<HumanBodyBones> ms_rightFingerBones = new List<HumanBodyBones>()
{
HumanBodyBones.RightThumbProximal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,
HumanBodyBones.RightIndexProximal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,
HumanBodyBones.RightMiddleProximal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,
HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,
HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal
};

public static FingerSystem Instance { get; private set; } = null;

RotationOffset m_leftHandOffset; // From avatar hand to controller wrist
RotationOffset m_rightHandOffset;
readonly List<RotationOffset> m_leftFingerOffsets = null; // From controller finger bone to avatar finger bone
readonly List<RotationOffset> m_rightFingerOffsets = null;

public readonly float[] m_lastValues;

bool m_ready = false;
HumanPose m_pose;

internal FingerSystem()
{
if(Instance == null)
Instance = this;

m_leftFingerOffsets = new List<RotationOffset>();
m_rightFingerOffsets = new List<RotationOffset>();

m_pose = new HumanPose();
m_lastValues = new float[40];
}
internal void Cleanup()
{
if(Instance == this)
Instance = null;

m_leftFingerOffsets.Clear();
m_rightFingerOffsets.Clear();
m_ready = false;
}

internal void OnAvatarSetup()
{
if(PlayerSetup.Instance._animator.isHuman)
{
IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose);
PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero;
PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity;
InputHandler.Instance?.Rebind(PlayerSetup.Instance.transform.rotation);

m_leftHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
m_leftHandOffset.m_target = InputHandler.Instance?.GetSourceForBone(HumanBodyBones.LeftHand, true);
if((m_leftHandOffset.m_source != null) && (m_leftHandOffset.m_target != null))
m_leftHandOffset.m_offset = Quaternion.Inverse(m_leftHandOffset.m_source.rotation) * m_leftHandOffset.m_target.rotation;

m_rightHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand);
m_rightHandOffset.m_target = InputHandler.Instance?.GetSourceForBone(HumanBodyBones.RightHand, false);
if((m_rightHandOffset.m_source != null) && (m_rightHandOffset.m_target != null))
m_rightHandOffset.m_offset = Quaternion.Inverse(m_rightHandOffset.m_source.rotation) * m_rightHandOffset.m_target.rotation;

foreach(HumanBodyBones p_bone in ms_leftFingerBones)
{
Transform l_avatarBone = PlayerSetup.Instance._animator.GetBoneTransform(p_bone);
Transform l_controllerBone = InputHandler.Instance?.GetSourceForBone(p_bone, true);
if((l_avatarBone != null) && (l_controllerBone != null))
{
RotationOffset l_offset = new RotationOffset();
l_offset.m_source = l_controllerBone;
l_offset.m_target = l_avatarBone;
l_offset.m_offset = Quaternion.Inverse(l_controllerBone.rotation) * l_avatarBone.rotation;
m_leftFingerOffsets.Add(l_offset);
}
}

foreach(HumanBodyBones p_bone in ms_rightFingerBones)
{
Transform l_avatarBone = PlayerSetup.Instance._animator.GetBoneTransform(p_bone);
Transform l_controllerBone = InputHandler.Instance?.GetSourceForBone(p_bone, false);
if((l_avatarBone != null) && (l_controllerBone != null))
{
RotationOffset l_offset = new RotationOffset();
l_offset.m_source = l_controllerBone;
l_offset.m_target = l_avatarBone;
l_offset.m_offset = Quaternion.Inverse(l_controllerBone.rotation) * l_avatarBone.rotation;
m_rightFingerOffsets.Add(l_offset);
}
}

m_ready = ((m_leftFingerOffsets.Count > 0) || (m_rightFingerOffsets.Count > 0));
}
}

internal void OnAvatarClear()
{
m_ready = false;
m_pose = new HumanPose();
m_leftFingerOffsets.Clear();
m_rightFingerOffsets.Clear();
}

internal void OnReinitializeAvatar()
{
OnAvatarClear();
OnAvatarSetup();
}

internal void OnIKSystemLateUpdate(HumanPoseHandler p_handler)
{
if(m_ready && MetaPort.Instance.isUsingVr && (p_handler != null) && Settings.SkeletalInput)
{
// Virtually allign controllers wrist bone to avatar hands with offset and apply global rotation to avatar finger bones with individial offset
// This is done to apply rotation changes from controller bones to avatar finger bones as in local space
if(CVRInputManager.Instance._leftController != ABI_RC.Systems.InputManagement.XR.eXRControllerType.None)
{
Quaternion l_turnBack = (m_leftHandOffset.m_source.rotation * m_leftHandOffset.m_offset) * Quaternion.Inverse(m_leftHandOffset.m_target.rotation);
foreach(var l_offset in m_leftFingerOffsets)
l_offset.m_target.rotation = l_turnBack * (l_offset.m_source.rotation * l_offset.m_offset);
}

if(CVRInputManager.Instance._rightController != ABI_RC.Systems.InputManagement.XR.eXRControllerType.None)
{
Quaternion l_turnBack = (m_rightHandOffset.m_source.rotation * m_rightHandOffset.m_offset) * Quaternion.Inverse(m_rightHandOffset.m_target.rotation);
foreach(var l_offset in m_rightFingerOffsets)
l_offset.m_target.rotation = l_turnBack * (l_offset.m_source.rotation * l_offset.m_offset);
}

// No matter if hands are tracked, fill muscles values
p_handler.GetHumanPose(ref m_pose);
m_lastValues[0] = m_pose.muscles[(int)MuscleIndex.LeftThumb1Stretched];
m_lastValues[1] = m_pose.muscles[(int)MuscleIndex.LeftThumb2Stretched];
m_lastValues[2] = m_pose.muscles[(int)MuscleIndex.LeftThumb3Stretched];
m_lastValues[3] = m_pose.muscles[(int)MuscleIndex.LeftThumbSpread];
m_lastValues[4] = m_pose.muscles[(int)MuscleIndex.LeftIndex1Stretched];
m_lastValues[5] = m_pose.muscles[(int)MuscleIndex.LeftIndex2Stretched];
m_lastValues[6] = m_pose.muscles[(int)MuscleIndex.LeftIndex3Stretched];
m_lastValues[7] = m_pose.muscles[(int)MuscleIndex.LeftIndexSpread];
m_lastValues[8] = m_pose.muscles[(int)MuscleIndex.LeftMiddle1Stretched];
m_lastValues[9] = m_pose.muscles[(int)MuscleIndex.LeftMiddle2Stretched];
m_lastValues[10] = m_pose.muscles[(int)MuscleIndex.LeftMiddle3Stretched];
m_lastValues[11] = m_pose.muscles[(int)MuscleIndex.LeftMiddleSpread];
m_lastValues[12] = m_pose.muscles[(int)MuscleIndex.LeftRing1Stretched];
m_lastValues[13] = m_pose.muscles[(int)MuscleIndex.LeftRing2Stretched];
m_lastValues[14] = m_pose.muscles[(int)MuscleIndex.LeftRing3Stretched];
m_lastValues[15] = m_pose.muscles[(int)MuscleIndex.LeftRingSpread];
m_lastValues[16] = m_pose.muscles[(int)MuscleIndex.LeftLittle1Stretched];
m_lastValues[17] = m_pose.muscles[(int)MuscleIndex.LeftLittle2Stretched];
m_lastValues[18] = m_pose.muscles[(int)MuscleIndex.LeftLittle3Stretched];
m_lastValues[19] = m_pose.muscles[(int)MuscleIndex.LeftLittleSpread];
m_lastValues[20] = m_pose.muscles[(int)MuscleIndex.RightThumb1Stretched];
m_lastValues[21] = m_pose.muscles[(int)MuscleIndex.RightThumb2Stretched];
m_lastValues[22] = m_pose.muscles[(int)MuscleIndex.RightThumb3Stretched];
m_lastValues[23] = m_pose.muscles[(int)MuscleIndex.RightThumbSpread];
m_lastValues[24] = m_pose.muscles[(int)MuscleIndex.RightIndex1Stretched];
m_lastValues[25] = m_pose.muscles[(int)MuscleIndex.RightIndex2Stretched];
m_lastValues[26] = m_pose.muscles[(int)MuscleIndex.RightIndex3Stretched];
m_lastValues[27] = m_pose.muscles[(int)MuscleIndex.RightIndexSpread];
m_lastValues[28] = m_pose.muscles[(int)MuscleIndex.RightMiddle1Stretched];
m_lastValues[29] = m_pose.muscles[(int)MuscleIndex.RightMiddle2Stretched];
m_lastValues[30] = m_pose.muscles[(int)MuscleIndex.RightMiddle3Stretched];
m_lastValues[31] = m_pose.muscles[(int)MuscleIndex.RightMiddleSpread];
m_lastValues[32] = m_pose.muscles[(int)MuscleIndex.RightRing1Stretched];
m_lastValues[33] = m_pose.muscles[(int)MuscleIndex.RightRing2Stretched];
m_lastValues[34] = m_pose.muscles[(int)MuscleIndex.RightRing3Stretched];
m_lastValues[35] = m_pose.muscles[(int)MuscleIndex.RightRingSpread];
m_lastValues[36] = m_pose.muscles[(int)MuscleIndex.RightLittle1Stretched];
m_lastValues[37] = m_pose.muscles[(int)MuscleIndex.RightLittle2Stretched];
m_lastValues[38] = m_pose.muscles[(int)MuscleIndex.RightLittle3Stretched];
m_lastValues[39] = m_pose.muscles[(int)MuscleIndex.RightLittleSpread];
}
}
}
}
59 changes: 59 additions & 0 deletions ml_bft/HandHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using UnityEngine;

namespace ml_bft
{
class HandHandler
{
protected bool m_left = false;
protected List<Transform> m_bones = null;
protected List<Quaternion> m_localRotations = null;
protected Transform m_prefabRoot = null;
protected List<Renderer> m_renderers = null;

protected HandHandler(bool p_left)
{
m_left = p_left;
m_bones = new List<Transform>();
m_localRotations = new List<Quaternion>();
m_renderers = new List<Renderer>();

Settings.ShowHandsChange += this.OnShowHandsChange;
}

public virtual void Cleanup()
{
if(m_prefabRoot != null)
Object.Destroy(m_prefabRoot.gameObject);
m_prefabRoot = null;

m_bones.Clear();
m_localRotations.Clear();
m_renderers.Clear();

Settings.ShowHandsChange -= this.OnShowHandsChange;
}

public virtual void Update()
{
}

public virtual Transform GetSourceForBone(HumanBodyBones p_bone)
{
return default;
}

public virtual void Rebind(Quaternion p_base)
{
}

protected void OnShowHandsChange(bool p_state)
{
foreach(var l_render in m_renderers)
{
if(l_render != null)
l_render.enabled = p_state;
}
}
}
}
Loading

0 comments on commit 2aaac8f

Please sign in to comment.