Too many changes

This commit is contained in:
SDraw 2024-10-05 15:42:32 +03:00
parent 45557943c4
commit a22e5992d0
No known key found for this signature in database
GPG key ID: BB95B4DAB2BB8BB5
72 changed files with 1064 additions and 927 deletions

View file

@ -1,25 +1,15 @@
Merged set of MelonLoader mods for ChilloutVR. Merged set of MelonLoader mods for ChilloutVR.
**Table for game build 2023r175:** **Table for game build 2023r177:**
| Full name | Short name | Latest version | | Full name | Latest version |
|:---------:|:----------:|:--------------:| |:---------:|:--------------:|
| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.4.1 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| |[Avatar Motion Tweaker](/ml_amt/README.md)|1.5.0 [:arrow_down:](../../releases/latest/download/ml_amt.dll)|
| [Avatar Synced Look](/ml_asl/README.md) | ml_asl | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_asl.dll)| |[Avatar Synced Look](/ml_asl/README.md)|1.1.0 [:arrow_down:](../../releases/latest/download/ml_asl.dll)|
| [Better Fingers Tracking](/ml_bft/README.md) | ml_bft | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_bft.dll)| |[Better Fingers Tracking](/ml_bft/README.md)|1.1.0 [:arrow_down:](../../releases/latest/download/ml_bft.dll)|
| [Desktop Head Tracking](/ml_dht/README.md) | ml_dht | 1.2.5 [:arrow_down:](../../releases/latest/download/ml_dht.dll)| |[Desktop Head Tracking](/ml_dht/README.md)|1.3.0 [:arrow_down:](../../releases/latest/download/ml_dht.dll)|
| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.5.2 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| |[Leap Motion Extension](/ml_lme/README.md)| 1.6.0 [:arrow_down:](../../releases/latest/download/ml_lme.dll)|
| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.1.4 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| |[Pickup Arm Movement](/ml_pam/README.md)|1.2.0 [:arrow_down:](../../releases/latest/download/ml_pam.dll)|
| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.8 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| |[Player Movement Copycat](/ml_pmc/README.md)|1.1.0 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)|
| [Player Ragdoll Mod](/ml_prm/README.md) | ml_prm | 1.1.9 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| |[Player Ragdoll Mod](/ml_prm/README.md)|1.2.0 [:arrow_down:](../../releases/latest/download/ml_prm.dll)|
| [Players Instance Notifier](/ml_pin/README.md) | ml_pin | 1.0.8 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)| |[Players Instance Notifier](/ml_pin/README.md)|1.1.0 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)|
| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| |[Vive Extended Input](/ml_vei/README.md)|1.1.0 [:arrow_down:](../../releases/latest/download/ml_vei.dll)|
**Archived mods:**
| Full name | Short name | Notes |
|:---------:|:----------:|-------|
| Avatar Change Info | ml_aci | Superseded by `Extended Game Notifications` |
| Desktop Reticle Switch | ml_drs | Boring functionality |
| Extended Game Notifications | ml_egn | In-game feature since 2023r172 update |
| Four Point Tracking | ml_fpt | In-game feature since 2022r170 update |
| Game Main Fixes | ml_gmf | In-game feature since 2023r172 update |
| Server Connection Info | ml_sci | Superseded by `Extended Game Notifications`

View file

@ -11,7 +11,7 @@ namespace ml_amt
[DisallowMultipleComponent] [DisallowMultipleComponent]
class MotionTweaker : MonoBehaviour class MotionTweaker : MonoBehaviour
{ {
struct IKState struct IKInfo
{ {
public float m_weight; public float m_weight;
public float m_locomotionWeight; public float m_locomotionWeight;
@ -20,7 +20,9 @@ namespace ml_amt
public bool m_bendNormalRight; public bool m_bendNormalRight;
} }
IKState m_ikState; static MotionTweaker ms_instance = null;
IKInfo m_ikInfo;
VRIK m_vrIk = null; VRIK m_vrIk = null;
float m_avatarScale = 1f; float m_avatarScale = 1f;
Vector3 m_locomotionOffset = Vector3.zero; // Original locomotion offset Vector3 m_locomotionOffset = Vector3.zero; // Original locomotion offset
@ -37,10 +39,19 @@ namespace ml_amt
} }
// Unity events // Unity events
void Awake()
{
if(ms_instance != null)
{
DestroyImmediate(this);
return;
}
ms_instance = this;
DontDestroyOnLoad(this);
}
void Start() void Start()
{ {
DontDestroyOnLoad(this);
OnCrouchLimitChanged(Settings.CrouchLimit); OnCrouchLimitChanged(Settings.CrouchLimit);
OnProneLimitChanged(Settings.ProneLimit); OnProneLimitChanged(Settings.ProneLimit);
@ -56,6 +67,9 @@ namespace ml_amt
void OnDestroy() void OnDestroy()
{ {
if(ms_instance == this)
ms_instance = null;
m_vrIk = null; m_vrIk = null;
m_ikLimits = null; m_ikLimits = null;
m_parameters.Clear(); m_parameters.Clear();
@ -170,11 +184,11 @@ namespace ml_amt
// IK events // IK events
void OnIKPreSolverUpdate() void OnIKPreSolverUpdate()
{ {
m_ikState.m_weight = m_vrIk.solver.IKPositionWeight; m_ikInfo.m_weight = m_vrIk.solver.IKPositionWeight;
m_ikState.m_locomotionWeight = m_vrIk.solver.locomotion.weight; m_ikInfo.m_locomotionWeight = m_vrIk.solver.locomotion.weight;
m_ikState.m_plantFeet = m_vrIk.solver.plantFeet; m_ikInfo.m_plantFeet = m_vrIk.solver.plantFeet;
m_ikState.m_bendNormalLeft = m_vrIk.solver.leftLeg.useAnimatedBendNormal; m_ikInfo.m_bendNormalLeft = m_vrIk.solver.leftLeg.useAnimatedBendNormal;
m_ikState.m_bendNormalRight = m_vrIk.solver.rightLeg.useAnimatedBendNormal; m_ikInfo.m_bendNormalRight = m_vrIk.solver.rightLeg.useAnimatedBendNormal;
if(!BodySystem.isCalibratedAsFullBody) if(!BodySystem.isCalibratedAsFullBody)
{ {
@ -200,11 +214,11 @@ namespace ml_amt
void OnIKPostSolverUpdate() void OnIKPostSolverUpdate()
{ {
m_vrIk.solver.IKPositionWeight = m_ikState.m_weight; m_vrIk.solver.IKPositionWeight = m_ikInfo.m_weight;
m_vrIk.solver.locomotion.weight = m_ikState.m_locomotionWeight; m_vrIk.solver.locomotion.weight = m_ikInfo.m_locomotionWeight;
m_vrIk.solver.plantFeet = m_ikState.m_plantFeet; m_vrIk.solver.plantFeet = m_ikInfo.m_plantFeet;
m_vrIk.solver.leftLeg.useAnimatedBendNormal = m_ikState.m_bendNormalLeft; m_vrIk.solver.leftLeg.useAnimatedBendNormal = m_ikInfo.m_bendNormalLeft;
m_vrIk.solver.rightLeg.useAnimatedBendNormal = m_ikState.m_bendNormalRight; m_vrIk.solver.rightLeg.useAnimatedBendNormal = m_ikInfo.m_bendNormalRight;
} }
// Settings // Settings

View file

@ -6,15 +6,16 @@ namespace ml_amt
{ {
static class ResourcesHandler static class ResourcesHandler
{ {
readonly static string ms_namespace = typeof(ResourcesHandler).Namespace;
public static string GetEmbeddedResource(string p_name) public static string GetEmbeddedResource(string p_name)
{ {
string l_result = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
StreamReader l_streadReader = new StreamReader(l_libraryStream); StreamReader l_streadReader = new StreamReader(l_libraryStream);
l_result = l_streadReader.ReadToEnd(); l_result = l_streadReader.ReadToEnd();
} }

View file

@ -18,20 +18,6 @@ namespace ml_amt
public static bool HasToes(this IKSolverVR p_instance) => (bool)ms_hasToes.GetValue(p_instance); public static bool HasToes(this IKSolverVR p_instance) => (bool)ms_hasToes.GetValue(p_instance);
public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
public static float GetWorldMovementLimit()
{
float l_result = 1f;
if(CVRWorld.Instance != null)
{
l_result = CVRWorld.Instance.baseMovementSpeed;
l_result *= CVRWorld.Instance.sprintMultiplier;
l_result *= CVRWorld.Instance.inAirMovementMultiplier;
l_result *= CVRWorld.Instance.flyMultiplier;
}
return l_result;
}
public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);
public static void SetAvatarTPose() public static void SetAvatarTPose()

View file

@ -6,7 +6,7 @@
<Company>SDraw</Company> <Company>SDraw</Company>
<Product>AvatarMotionTweaker</Product> <Product>AvatarMotionTweaker</Product>
<PackageId>AvatarMotionTweaker</PackageId> <PackageId>AvatarMotionTweaker</PackageId>
<Version>1.4.2</Version> <Version>1.5.0</Version>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<AssemblyName>AvatarMotionTweaker</AssemblyName> <AssemblyName>AvatarMotionTweaker</AssemblyName>
</PropertyGroup> </PropertyGroup>

View file

@ -1,10 +1,13 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using System.Reflection; using System.Reflection;
using UnityEngine;
namespace ml_asl namespace ml_asl
{ {
public class AvatarSyncedLook : MelonLoader.MelonMod public class AvatarSyncedLook : MelonLoader.MelonMod
{ {
readonly static Matrix4x4 ms_back = Matrix4x4.Translate(Vector3.back);
public override void OnInitializeMelon() public override void OnInitializeMelon()
{ {
Settings.Init(); Settings.Init();
@ -19,7 +22,14 @@ namespace ml_asl
static void OnPlayerAvatarMovementDataUpdate_Postfix(ref PlayerSetup __instance, PlayerAvatarMovementData ____playerAvatarMovementData) static void OnPlayerAvatarMovementDataUpdate_Postfix(ref PlayerSetup __instance, PlayerAvatarMovementData ____playerAvatarMovementData)
{ {
if(Settings.Enabled && (__instance.EyeMovementController != null)) if(Settings.Enabled && (__instance.EyeMovementController != null))
{
____playerAvatarMovementData.EyeTrackingOverride = true; ____playerAvatarMovementData.EyeTrackingOverride = true;
if(__instance.EyeMovementController.CurrentTarget != null)
____playerAvatarMovementData.EyeTrackingPosition = __instance.EyeMovementController.CurrentTarget.GetPosition();
else
____playerAvatarMovementData.EyeTrackingPosition = (__instance.transform.GetMatrix() * ms_back).GetPosition();
}
} }
} }
} }

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_asl.AvatarSyncedLook), "AvatarSyncedLook", "1.0.4", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_asl.AvatarSyncedLook), "AvatarSyncedLook", "1.1.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -1,14 +1,11 @@
# Avatar Synced Look # Avatar Synced Look
This mod Forces local player's eyes look direction to be synced for remote players. This mod forces local player's eyes look direction to be synced for remote players.
# Installation # Installation
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
* Get [latest release DLL](../../../releases/latest): * Get [latest release DLL](../../../releases/latest):
* Put `ml_asl.dll` in `Mods` folder of game * Put `AvatarSyncedLook.dll` in `Mods` folder of game
# Usage # Usage
Available mod's settings in `Settings - Interactions - Avatar Synced Look`: Available mod's settings in `Settings - Interactions - Avatar Synced Look`:
* **Enabled:** sets eyes look direction to be synced or locally generated on remote users side; `true` by default. * **Enabled:** sets eyes look direction to be synced or locally generated on remote users side; `true` by default.
# Notes
* Remote users with [EyeMovementFix](https://github.com/kafeijao/Kafe_CVR_Mods/tree/master/EyeMovementFix) installed can't see synced look direction.

View file

@ -6,15 +6,16 @@ namespace ml_asl
{ {
static class ResourcesHandler static class ResourcesHandler
{ {
readonly static string ms_namespace = typeof(ResourcesHandler).Namespace;
public static string GetEmbeddedResource(string p_name) public static string GetEmbeddedResource(string p_name)
{ {
string l_result = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
StreamReader l_streadReader = new StreamReader(l_libraryStream); StreamReader l_streadReader = new StreamReader(l_libraryStream);
l_result = l_streadReader.ReadToEnd(); l_result = l_streadReader.ReadToEnd();
} }

View file

@ -1,5 +1,6 @@
using ABI_RC.Core.UI; using ABI_RC.Core.UI;
using System.Reflection; using System.Reflection;
using UnityEngine;
namespace ml_asl namespace ml_asl
{ {
@ -8,5 +9,11 @@ namespace ml_asl
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);
// Extensions
public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false)
{
return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one);
}
} }
} }

View file

@ -5,9 +5,10 @@
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<PackageId>AvatarSyncedLook</PackageId> <PackageId>AvatarSyncedLook</PackageId>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>None</Company> <Company>SDraw</Company>
<Product>AvatarSyncedLook</Product> <Product>AvatarSyncedLook</Product>
<Version>1.0.4</Version> <Version>1.1.0</Version>
<AssemblyName>AvatarSyncedLook</AssemblyName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -50,6 +51,11 @@
<Private>false</Private> <Private>false</Private>
<SpecificVersion>false</SpecificVersion> <SpecificVersion>false</SpecificVersion>
</Reference> </Reference>
<Reference Include="UnityEngine">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
<SpecificVersion>false</SpecificVersion>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule"> <Reference Include="UnityEngine.CoreModule">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath> <HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>

View file

@ -7,10 +7,11 @@ namespace ml_bft
{ {
static class AssetsHandler static class AssetsHandler
{ {
readonly static string ms_namespace = typeof(AssetsHandler).Namespace;
static readonly List<string> ms_assets = new List<string>() static readonly List<string> ms_assets = new List<string>()
{ {
"ovr_fingers.asset", "ovr_fingers.asset"
"oxr_fingers.asset"
}; };
static readonly Dictionary<string, AssetBundle> ms_loadedAssets = new Dictionary<string, AssetBundle>(); static readonly Dictionary<string, AssetBundle> ms_loadedAssets = new Dictionary<string, AssetBundle>();
@ -19,13 +20,12 @@ namespace ml_bft
public static void Load() public static void Load()
{ {
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
foreach(string l_assetName in ms_assets) foreach(string l_assetName in ms_assets)
{ {
try try
{ {
Stream l_assetStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + l_assetName); Stream l_assetStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + l_assetName);
if(l_assetStream != null) if(l_assetStream != null)
{ {
MemoryStream l_memorySteam = new MemoryStream((int)l_assetStream.Length); MemoryStream l_memorySteam = new MemoryStream((int)l_assetStream.Length);

View file

@ -45,18 +45,28 @@ namespace ml_bft
HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal, HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,
HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal
}; };
static readonly (HumanBodyBones, HumanBodyBones, bool)[] ms_rotationFixChains = static readonly (HumanBodyBones, HumanBodyBones, bool)[] ms_fingersChains =
{ {
(HumanBodyBones.LeftThumbProximal,HumanBodyBones.LeftThumbIntermediate,true), (HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,true), (HumanBodyBones.LeftThumbProximal,HumanBodyBones.LeftThumbIntermediate,true), (HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,true), (HumanBodyBones.LeftThumbDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.LeftIndexProximal,HumanBodyBones.LeftIndexIntermediate,true), (HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,true), (HumanBodyBones.LeftIndexProximal,HumanBodyBones.LeftIndexIntermediate,true), (HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,true), (HumanBodyBones.LeftIndexDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.LeftMiddleProximal,HumanBodyBones.LeftMiddleIntermediate,true), (HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,true), (HumanBodyBones.LeftMiddleProximal,HumanBodyBones.LeftMiddleIntermediate,true), (HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,true), (HumanBodyBones.LeftMiddleDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.LeftRingProximal,HumanBodyBones.LeftRingIntermediate,true), (HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,true), (HumanBodyBones.LeftRingProximal,HumanBodyBones.LeftRingIntermediate,true), (HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,true), (HumanBodyBones.LeftRingDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.LeftLittleProximal,HumanBodyBones.LeftLittleIntermediate,true), (HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal,true), (HumanBodyBones.LeftLittleProximal,HumanBodyBones.LeftLittleIntermediate,true), (HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal,true), (HumanBodyBones.LeftLittleDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.RightThumbProximal,HumanBodyBones.RightThumbIntermediate,false), (HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,false), (HumanBodyBones.RightThumbProximal,HumanBodyBones.RightThumbIntermediate,false), (HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,false), (HumanBodyBones.RightThumbDistal,HumanBodyBones.LastBone,false),
(HumanBodyBones.RightIndexProximal,HumanBodyBones.RightIndexIntermediate,false), (HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,false), (HumanBodyBones.RightIndexProximal,HumanBodyBones.RightIndexIntermediate,false), (HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,false), (HumanBodyBones.RightIndexDistal,HumanBodyBones.LastBone,false),
(HumanBodyBones.RightMiddleProximal,HumanBodyBones.RightMiddleIntermediate,false), (HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,false), (HumanBodyBones.RightMiddleProximal,HumanBodyBones.RightMiddleIntermediate,false), (HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,false), (HumanBodyBones.RightMiddleDistal,HumanBodyBones.LastBone,false),
(HumanBodyBones.RightRingProximal,HumanBodyBones.RightRingIntermediate,false), (HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,false), (HumanBodyBones.RightRingProximal,HumanBodyBones.RightRingIntermediate,false), (HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,false), (HumanBodyBones.RightRingDistal,HumanBodyBones.LastBone,false),
(HumanBodyBones.RightLittleProximal,HumanBodyBones.RightLittleIntermediate,false), (HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal,false) (HumanBodyBones.RightLittleProximal,HumanBodyBones.RightLittleIntermediate,false), (HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal,false),(HumanBodyBones.RightLittleDistal,HumanBodyBones.LastBone,false),
};
static readonly Vector3[] ms_directions =
{
Vector3.forward,
Vector3.back,
Vector3.left,
Vector3.right,
Vector3.up,
Vector3.down,
}; };
public static FingerSystem Instance { get; private set; } = null; public static FingerSystem Instance { get; private set; } = null;
@ -104,39 +114,39 @@ namespace ml_bft
internal void OnAvatarSetup() internal void OnAvatarSetup()
{ {
if(PlayerSetup.Instance._animator.isHuman) Animator l_animator = PlayerSetup.Instance._animator;
if(l_animator.isHuman)
{ {
Utils.SetAvatarTPose(); Utils.SetAvatarTPose();
InputHandler.Instance.Rebind(PlayerSetup.Instance.transform.rotation); InputHandler.Instance.Rebind(PlayerSetup.Instance.transform.rotation);
if(Settings.FixFingers) foreach(var l_tuple in ms_fingersChains)
{ {
foreach(var l_tuple in ms_rotationFixChains) ReorientateTowards(
{ PlayerSetup.Instance.transform,
ReorientateTowards( l_animator.GetBoneTransform(l_tuple.Item1),
PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1), (l_tuple.Item2 != HumanBodyBones.LastBone) ? l_animator.GetBoneTransform(l_tuple.Item2) : null,
PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2), InputHandler.Instance.GetSourceForBone(l_tuple.Item1, l_tuple.Item3),
InputHandler.Instance.GetSourceForBone(l_tuple.Item1, l_tuple.Item3), InputHandler.Instance.GetSourceForBone(l_tuple.Item2, l_tuple.Item3),
InputHandler.Instance.GetSourceForBone(l_tuple.Item2, l_tuple.Item3), PlaneType.OXZ
PlaneType.OXZ );
); ReorientateTowards(
ReorientateTowards( PlayerSetup.Instance.transform,
PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1), l_animator.GetBoneTransform(l_tuple.Item1),
PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2), (l_tuple.Item2 != HumanBodyBones.LastBone) ? l_animator.GetBoneTransform(l_tuple.Item2) : null,
InputHandler.Instance.GetSourceForBone(l_tuple.Item1, l_tuple.Item3), InputHandler.Instance.GetSourceForBone(l_tuple.Item1, l_tuple.Item3),
InputHandler.Instance.GetSourceForBone(l_tuple.Item2, l_tuple.Item3), InputHandler.Instance.GetSourceForBone(l_tuple.Item2, l_tuple.Item3),
PlaneType.OYX PlaneType.OYX
); );
}
} }
// Bind hands // Bind hands
m_leftHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand); m_leftHandOffset.m_source = l_animator.GetBoneTransform(HumanBodyBones.LeftHand);
m_leftHandOffset.m_target = InputHandler.Instance.GetSourceForBone(HumanBodyBones.LeftHand, true); m_leftHandOffset.m_target = InputHandler.Instance.GetSourceForBone(HumanBodyBones.LeftHand, true);
if((m_leftHandOffset.m_source != null) && (m_leftHandOffset.m_target != null)) 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_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_source = l_animator.GetBoneTransform(HumanBodyBones.RightHand);
m_rightHandOffset.m_target = InputHandler.Instance.GetSourceForBone(HumanBodyBones.RightHand, false); m_rightHandOffset.m_target = InputHandler.Instance.GetSourceForBone(HumanBodyBones.RightHand, false);
if((m_rightHandOffset.m_source != null) && (m_rightHandOffset.m_target != null)) 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; m_rightHandOffset.m_offset = Quaternion.Inverse(m_rightHandOffset.m_source.rotation) * m_rightHandOffset.m_target.rotation;
@ -144,7 +154,7 @@ namespace ml_bft
// Bind fingers // Bind fingers
foreach(HumanBodyBones p_bone in ms_leftFingerBones) foreach(HumanBodyBones p_bone in ms_leftFingerBones)
{ {
Transform l_avatarBone = PlayerSetup.Instance._animator.GetBoneTransform(p_bone); Transform l_avatarBone = l_animator.GetBoneTransform(p_bone);
Transform l_controllerBone = InputHandler.Instance.GetSourceForBone(p_bone, true); Transform l_controllerBone = InputHandler.Instance.GetSourceForBone(p_bone, true);
if((l_avatarBone != null) && (l_controllerBone != null)) if((l_avatarBone != null) && (l_controllerBone != null))
{ {
@ -157,7 +167,7 @@ namespace ml_bft
} }
foreach(HumanBodyBones p_bone in ms_rightFingerBones) foreach(HumanBodyBones p_bone in ms_rightFingerBones)
{ {
Transform l_avatarBone = PlayerSetup.Instance._animator.GetBoneTransform(p_bone); Transform l_avatarBone = l_animator.GetBoneTransform(p_bone);
Transform l_controllerBone = InputHandler.Instance.GetSourceForBone(p_bone, false); Transform l_controllerBone = InputHandler.Instance.GetSourceForBone(p_bone, false);
if((l_avatarBone != null) && (l_controllerBone != null)) if((l_avatarBone != null) && (l_controllerBone != null))
{ {
@ -262,13 +272,13 @@ namespace ml_bft
} }
} }
void ReorientateTowards(Transform p_target, Transform p_targetEnd, Transform p_source, Transform p_sourceEnd, PlaneType p_plane) static void ReorientateTowards(Transform root, Transform p_source, Transform p_sourceEnd, Transform p_target, Transform p_targetEnd, PlaneType p_plane)
{ {
if((p_target != null) && (p_targetEnd != null) && (p_source != null) && (p_sourceEnd != null)) if((root != null) && (p_target != null) && (p_source != null))
{ {
Quaternion l_playerInv = Quaternion.Inverse(PlayerSetup.Instance.transform.rotation); Quaternion l_rootInv = Quaternion.Inverse(root.rotation);
Vector3 l_targetDir = l_playerInv * (p_targetEnd.position - p_target.position); Vector3 l_targetDir = l_rootInv * (((p_targetEnd != null) ? p_targetEnd.position : GuessEnd(p_target)) - p_target.position);
Vector3 l_sourceDir = l_playerInv * (p_sourceEnd.position - p_source.position); Vector3 l_sourceDir = l_rootInv * (((p_sourceEnd != null) ? p_sourceEnd.position : GuessEnd(p_source)) - p_source.position);
switch(p_plane) switch(p_plane)
{ {
case PlaneType.OXZ: case PlaneType.OXZ:
@ -301,9 +311,30 @@ namespace ml_bft
if(p_plane == PlaneType.OYX) if(p_plane == PlaneType.OYX)
l_diff = Quaternion.Euler(0f, 0f, l_diff.eulerAngles.y); l_diff = Quaternion.Euler(0f, 0f, l_diff.eulerAngles.y);
Quaternion l_adjusted = l_diff * (l_playerInv * p_target.rotation); Quaternion l_adjusted = l_diff * (l_rootInv * p_target.rotation);
p_target.rotation = PlayerSetup.Instance.transform.rotation * l_adjusted; p_target.rotation = root.rotation * l_adjusted;
} }
} }
static Vector3 GuessEnd(Transform p_target)
{
Vector3 l_result = p_target.position;
if(p_target.parent != null)
{
float l_dot = -1f;
Vector3 l_axisDir = p_target.position - p_target.parent.position;
foreach(Vector3 l_dir in ms_directions)
{
Vector3 l_rotDir = p_target.rotation * l_dir;
float l_stepDot = Vector3.Dot(l_rotDir, l_axisDir);
if(l_stepDot >= l_dot)
{
l_dot = l_stepDot;
l_result = p_target.position + l_rotDir;
}
}
}
return l_result;
}
} }
} }

View file

@ -90,6 +90,7 @@ namespace ml_bft
OnShowHandsChanged(Settings.ShowHands); OnShowHandsChanged(Settings.ShowHands);
OnMotionRangeChanged(Settings.MotionRange); OnMotionRangeChanged(Settings.MotionRange);
Settings.OnSkeletalInputChanged.AddListener(this.OnSkeletalInputChanged);
Settings.OnShowHandsChanged.AddListener(this.OnShowHandsChanged); Settings.OnShowHandsChanged.AddListener(this.OnShowHandsChanged);
Settings.OnMotionRangeChanged.AddListener(this.OnMotionRangeChanged); Settings.OnMotionRangeChanged.AddListener(this.OnMotionRangeChanged);
} }
@ -106,6 +107,7 @@ namespace ml_bft
m_skeletonAction = null; m_skeletonAction = null;
Settings.OnSkeletalInputChanged.RemoveListener(this.OnSkeletalInputChanged);
Settings.OnShowHandsChanged.RemoveListener(this.OnShowHandsChanged); Settings.OnShowHandsChanged.RemoveListener(this.OnShowHandsChanged);
Settings.OnMotionRangeChanged.RemoveListener(this.OnMotionRangeChanged); Settings.OnMotionRangeChanged.RemoveListener(this.OnMotionRangeChanged);
} }
@ -254,12 +256,17 @@ namespace ml_bft
} }
// Settings // Settings
void OnSkeletalInputChanged(bool p_state)
{
OnShowHandsChanged(Settings.ShowHands);
}
void OnShowHandsChanged(bool p_state) void OnShowHandsChanged(bool p_state)
{ {
foreach(var l_render in m_renderers) foreach(var l_render in m_renderers)
{ {
if(l_render != null) if(l_render != null)
l_render.enabled = p_state; l_render.enabled = (Settings.SkeletalInput && p_state);
} }
} }

View file

@ -25,8 +25,7 @@ namespace ml_bft
if(MetaPort.Instance.isUsingVr) if(MetaPort.Instance.isUsingVr)
SetupHandlers(); SetupHandlers();
VRModeSwitchEvents.OnInitializeXR.AddListener(this.OnSwitchToVR); VRModeSwitchEvents.OnCompletedVRModeSwitch.AddListener(this.OnVRModeSwitch);
VRModeSwitchEvents.OnDeinitializeXR.AddListener(this.OnSwitchToDesktop);
Settings.OnSkeletalInputChanged.AddListener(this.OnSkeletalInputChanged); Settings.OnSkeletalInputChanged.AddListener(this.OnSkeletalInputChanged);
@ -39,6 +38,8 @@ namespace ml_bft
RemoveHandlers(); RemoveHandlers();
VRModeSwitchEvents.OnCompletedVRModeSwitch.RemoveListener(this.OnVRModeSwitch);
Settings.OnSkeletalInputChanged.RemoveListener(this.OnSkeletalInputChanged); Settings.OnSkeletalInputChanged.RemoveListener(this.OnSkeletalInputChanged);
GameEvents.OnInputUpdate.RemoveListener(this.OnInputUpdate); GameEvents.OnInputUpdate.RemoveListener(this.OnInputUpdate);
@ -133,23 +134,14 @@ namespace ml_bft
} }
} }
void OnSwitchToVR() void OnVRModeSwitch(bool p_state)
{ {
try try
{ {
SetupHandlers(); if(Utils.IsInVR())
} SetupHandlers();
catch(System.Exception e) else
{ RemoveHandlers();
MelonLoader.MelonLogger.Error(e);
}
}
void OnSwitchToDesktop()
{
try
{
RemoveHandlers();
} }
catch(System.Exception e) catch(System.Exception e)
{ {

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_bft.BetterFingersTracking), "BetterFingersTracking", "1.0.5", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_bft.BetterFingersTracking), "BetterFingersTracking", "1.1.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -13,7 +13,7 @@ Available mod's settings in `Settings - Input & Key-Bindings - Better Fingers Tr
* **Filter humanoid limits:** Limits fingers rotations to be valid for Unity's Mechanim; `true` by default * **Filter humanoid limits:** Limits fingers rotations to be valid for Unity's Mechanim; `true` by default
* Note: Enabling this option ensures that visual representation of your fingers will be same for you and remote players, but it cancels out additional finger segments rotations that can be better visually in most cases. * Note: Enabling this option ensures that visual representation of your fingers will be same for you and remote players, but it cancels out additional finger segments rotations that can be better visually in most cases.
* **Show hands model:** shows transparent hands model (mostly as debug option); `false` by default * **Show hands model:** shows transparent hands model (mostly as debug option); `false` by default
* **Change fingers direction at bind:** tries to allign avatar's fingers for more accurate poses * **Change fingers direction at bind:** tries to allign avatar's fingers for more accurate poses; `true` by default
# Notes # Notes
* Currently supports only SteamVR environment, OpenXR support is planned. * Currently supports only SteamVR environment, OpenXR support is planned.

View file

@ -6,15 +6,16 @@ namespace ml_bft
{ {
static class ResourcesHandler static class ResourcesHandler
{ {
readonly static string ms_namespace = typeof(ResourcesHandler).Namespace;
public static string GetEmbeddedResource(string p_name) public static string GetEmbeddedResource(string p_name)
{ {
string l_result = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
StreamReader l_streadReader = new StreamReader(l_libraryStream); StreamReader l_streadReader = new StreamReader(l_libraryStream);
l_result = l_streadReader.ReadToEnd(); l_result = l_streadReader.ReadToEnd();
} }

View file

@ -32,7 +32,6 @@ namespace ml_bft
public static MotionRangeType MotionRange { get; private set; } = MotionRangeType.WithController; public static MotionRangeType MotionRange { get; private set; } = MotionRangeType.WithController;
public static bool ShowHands { get; private set; } = false; public static bool ShowHands { get; private set; } = false;
public static bool MechanimFilter { get; private set; } = true; public static bool MechanimFilter { get; private set; } = true;
public static bool FixFingers { get; private set; } = true;
static MelonLoader.MelonPreferences_Category ms_category = null; static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null; static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
@ -41,7 +40,6 @@ namespace ml_bft
public static readonly SettingEvent<MotionRangeType> OnMotionRangeChanged = new SettingEvent<MotionRangeType>(); public static readonly SettingEvent<MotionRangeType> OnMotionRangeChanged = new SettingEvent<MotionRangeType>();
public static readonly SettingEvent<bool> OnShowHandsChanged = new SettingEvent<bool>(); public static readonly SettingEvent<bool> OnShowHandsChanged = new SettingEvent<bool>();
public static readonly SettingEvent<bool> OnMechanimFilterChanged = new SettingEvent<bool>(); public static readonly SettingEvent<bool> OnMechanimFilterChanged = new SettingEvent<bool>();
public static readonly SettingEvent<bool> OnFixFingersChanged = new SettingEvent<bool>();
internal static void Init() internal static void Init()
{ {
@ -52,14 +50,12 @@ namespace ml_bft
ms_category.CreateEntry(ModSetting.SkeletalInput.ToString(), SkeletalInput), ms_category.CreateEntry(ModSetting.SkeletalInput.ToString(), SkeletalInput),
ms_category.CreateEntry(ModSetting.MotionRange.ToString(), (int)MotionRange), ms_category.CreateEntry(ModSetting.MotionRange.ToString(), (int)MotionRange),
ms_category.CreateEntry(ModSetting.ShowHands.ToString(), ShowHands), ms_category.CreateEntry(ModSetting.ShowHands.ToString(), ShowHands),
ms_category.CreateEntry(ModSetting.MechanimFilter.ToString(), MechanimFilter), ms_category.CreateEntry(ModSetting.MechanimFilter.ToString(), MechanimFilter)
ms_category.CreateEntry(ModSetting.FixFingers.ToString(), FixFingers)
}; };
SkeletalInput = (bool)ms_entries[(int)ModSetting.SkeletalInput].BoxedValue; SkeletalInput = (bool)ms_entries[(int)ModSetting.SkeletalInput].BoxedValue;
MotionRange = (MotionRangeType)(int)ms_entries[(int)ModSetting.MotionRange].BoxedValue; MotionRange = (MotionRangeType)(int)ms_entries[(int)ModSetting.MotionRange].BoxedValue;
ShowHands = (bool)ms_entries[(int)ModSetting.ShowHands].BoxedValue; ShowHands = (bool)ms_entries[(int)ModSetting.ShowHands].BoxedValue;
FixFingers = (bool)ms_entries[(int)ModSetting.FixFingers].BoxedValue;
MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); MelonLoader.MelonCoroutines.Start(WaitMainMenuUi());
} }
@ -115,13 +111,6 @@ namespace ml_bft
OnMechanimFilterChanged.Invoke(MechanimFilter); OnMechanimFilterChanged.Invoke(MechanimFilter);
} }
break; break;
case ModSetting.FixFingers:
{
FixFingers = l_value;
OnFixFingersChanged.Invoke(FixFingers);
}
break;
} }
ms_entries[(int)l_setting].BoxedValue = l_value; ms_entries[(int)l_setting].BoxedValue = l_value;

View file

@ -1,4 +1,5 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Core.UI; using ABI_RC.Core.UI;
using ABI_RC.Systems.IK; using ABI_RC.Systems.IK;
using ABI_RC.Systems.InputManagement; using ABI_RC.Systems.InputManagement;
@ -13,6 +14,8 @@ namespace ml_bft
public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);
public static bool IsInVR() => ((MetaPort.Instance != null) && MetaPort.Instance.isUsingVr);
public static bool AreKnucklesInUse() => ((CVRInputManager.Instance._leftController == ABI_RC.Systems.InputManagement.XR.eXRControllerType.Index) || (CVRInputManager.Instance._rightController == ABI_RC.Systems.InputManagement.XR.eXRControllerType.Index)); public static bool AreKnucklesInUse() => ((CVRInputManager.Instance._leftController == ABI_RC.Systems.InputManagement.XR.eXRControllerType.Index) || (CVRInputManager.Instance._rightController == ABI_RC.Systems.InputManagement.XR.eXRControllerType.Index));
public static void SetAvatarTPose() public static void SetAvatarTPose()

View file

@ -7,7 +7,7 @@
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>SDraw</Company> <Company>SDraw</Company>
<Product>BetterFingersTracking</Product> <Product>BetterFingersTracking</Product>
<Version>1.0.5</Version> <Version>1.1.0</Version>
<AssemblyName>BetterFingersTracking</AssemblyName> <AssemblyName>BetterFingersTracking</AssemblyName>
</PropertyGroup> </PropertyGroup>

View file

@ -19,13 +19,6 @@
<div id="MotionRange" class ="inp_dropdown no-scroll" data-options="0:With controller,1:Without controller" data-current="0"></div> <div id="MotionRange" class ="inp_dropdown no-scroll" data-options="0:With controller,1:Without controller" data-current="0"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper">
<div class ="option-caption">Change fingers direction at bind: </div>
<div class ="option-input">
<div id="FixFingers" class ="inp_toggle no-scroll" data-current="true"></div>
</div>
</div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Filter humanoid limits: </div> <div class ="option-caption">Filter humanoid limits: </div>

View file

@ -2,9 +2,9 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Core.Player.EyeMovement; using ABI_RC.Core.Player.EyeMovement;
using ABI_RC.Systems.FaceTracking; using ABI_RC.Systems.FaceTracking;
using ABI_RC.Systems.VRModeSwitch;
using RootMotion.FinalIK; using RootMotion.FinalIK;
using System; using System;
using System.Reflection;
using UnityEngine; using UnityEngine;
using ViveSR.anipal.Lip; using ViveSR.anipal.Lip;
@ -13,11 +13,7 @@ namespace ml_dht
[DisallowMultipleComponent] [DisallowMultipleComponent]
class HeadTracked : MonoBehaviour class HeadTracked : MonoBehaviour
{ {
static FieldInfo ms_emotePlaying = typeof(PlayerSetup).GetField("_emotePlaying", BindingFlags.NonPublic | BindingFlags.Instance); static HeadTracked ms_instance = null;
bool m_enabled = false;
bool m_headTracking = true;
float m_smoothing = 0.5f;
CVRAvatar m_avatarDescriptor = null; CVRAvatar m_avatarDescriptor = null;
Transform m_camera = null; Transform m_camera = null;
@ -34,6 +30,9 @@ namespace ml_dht
Quaternion m_bindRotation; Quaternion m_bindRotation;
Quaternion m_lastHeadRotation; Quaternion m_lastHeadRotation;
DataParser m_dataParser = null;
float m_smoothing = 0.5f;
internal HeadTracked() internal HeadTracked()
{ {
m_lipData = new LipData_v2(); m_lipData = new LipData_v2();
@ -45,14 +44,28 @@ namespace ml_dht
} }
// Unity events // Unity events
void Awake()
{
if(ms_instance != null)
{
DestroyImmediate(this);
return;
}
DontDestroyOnLoad(this);
ms_instance = this;
m_dataParser = new DataParser();
}
void Start() void Start()
{ {
OnEnabledChanged(Settings.Enabled);
OnHeadTrackingChanged(Settings.HeadTracking);
OnSmoothingChanged(Settings.Smoothing); OnSmoothingChanged(Settings.Smoothing);
Settings.OnEnabledChanged.AddListener(this.OnEnabledChanged); OnVRModeSwitch(true);
Settings.OnHeadTrackingChanged.AddListener(this.OnHeadTrackingChanged);
Settings.OnEnabledChanged.AddListener(this.OnEnabledOrHeadTrackingChanged);
Settings.OnHeadTrackingChanged.AddListener(this.OnEnabledOrHeadTrackingChanged);
Settings.OnSmoothingChanged.AddListener(this.OnSmoothingChanged); Settings.OnSmoothingChanged.AddListener(this.OnSmoothingChanged);
GameEvents.OnAvatarClear.AddListener(this.OnAvatarClear); GameEvents.OnAvatarClear.AddListener(this.OnAvatarClear);
@ -60,12 +73,19 @@ namespace ml_dht
GameEvents.OnAvatarReuse.AddListener(this.OnAvatarReuse); GameEvents.OnAvatarReuse.AddListener(this.OnAvatarReuse);
GameEvents.OnEyeControllerUpdate.AddListener(this.OnEyeControllerUpdate); GameEvents.OnEyeControllerUpdate.AddListener(this.OnEyeControllerUpdate);
GameEvents.OnFaceTrackingUpdate.AddListener(this.UpdateFaceTracking); GameEvents.OnFaceTrackingUpdate.AddListener(this.UpdateFaceTracking);
VRModeSwitchEvents.OnCompletedVRModeSwitch.AddListener(this.OnVRModeSwitch);
} }
void OnDestroy() void OnDestroy()
{ {
Settings.OnEnabledChanged.RemoveListener(this.OnEnabledChanged); if(ms_instance == this)
Settings.OnHeadTrackingChanged.RemoveListener(this.OnHeadTrackingChanged); ms_instance = null;
m_dataParser = null;
Settings.OnEnabledChanged.RemoveListener(this.OnEnabledOrHeadTrackingChanged);
Settings.OnHeadTrackingChanged.RemoveListener(this.OnEnabledOrHeadTrackingChanged);
Settings.OnSmoothingChanged.RemoveListener(this.OnSmoothingChanged); Settings.OnSmoothingChanged.RemoveListener(this.OnSmoothingChanged);
GameEvents.OnAvatarClear.RemoveListener(this.OnAvatarClear); GameEvents.OnAvatarClear.RemoveListener(this.OnAvatarClear);
@ -73,12 +93,20 @@ namespace ml_dht
GameEvents.OnAvatarReuse.RemoveListener(this.OnAvatarReuse); GameEvents.OnAvatarReuse.RemoveListener(this.OnAvatarReuse);
GameEvents.OnEyeControllerUpdate.RemoveListener(this.OnEyeControllerUpdate); GameEvents.OnEyeControllerUpdate.RemoveListener(this.OnEyeControllerUpdate);
GameEvents.OnFaceTrackingUpdate.RemoveListener(this.UpdateFaceTracking); GameEvents.OnFaceTrackingUpdate.RemoveListener(this.UpdateFaceTracking);
VRModeSwitchEvents.OnCompletedVRModeSwitch.RemoveListener(this.OnVRModeSwitch);
} }
void Update() void Update()
{ {
if(m_lipDataSent) if(m_lipDataSent)
m_lipDataSent = false; m_lipDataSent = false;
if(Settings.Enabled && (m_dataParser != null))
{
m_dataParser.Update();
UpdateTrackingData(ref m_dataParser.GetLatestTrackingData());
}
} }
// Tracking updates // Tracking updates
@ -98,11 +126,11 @@ namespace ml_dht
void OnLookIKPostUpdate() void OnLookIKPostUpdate()
{ {
if(m_enabled && m_headTracking && (m_headBone != null)) if(Settings.Enabled && Settings.HeadTracking && (m_headBone != null))
{ {
m_lastHeadRotation = Quaternion.Slerp(m_lastHeadRotation, m_avatarDescriptor.transform.rotation * (m_headRotation * m_bindRotation), m_smoothing); m_lastHeadRotation = Quaternion.Slerp(m_lastHeadRotation, m_avatarDescriptor.transform.rotation * (m_headRotation * m_bindRotation), m_smoothing);
if(!(bool)ms_emotePlaying.GetValue(PlayerSetup.Instance)) if(!PlayerSetup.Instance.IsEmotePlaying())
m_headBone.rotation = m_lastHeadRotation; m_headBone.rotation = m_lastHeadRotation;
} }
} }
@ -142,7 +170,7 @@ namespace ml_dht
void OnEyeControllerUpdate(EyeMovementController p_component) void OnEyeControllerUpdate(EyeMovementController p_component)
{ {
if(m_enabled) if(this.enabled && Settings.Enabled)
{ {
// Gaze // Gaze
if(Settings.EyeTracking && (m_camera != null)) if(Settings.EyeTracking && (m_camera != null))
@ -162,7 +190,7 @@ namespace ml_dht
void UpdateFaceTracking(CVRFaceTracking p_component, GameEvents.EventResult p_result) void UpdateFaceTracking(CVRFaceTracking p_component, GameEvents.EventResult p_result)
{ {
if(p_component.isLocal && p_component.UseFacialTracking && m_enabled && Settings.FaceTracking) if(this.enabled && Settings.Enabled && Settings.FaceTracking && p_component.isLocal && p_component.UseFacialTracking )
{ {
if(!m_lipDataSent) if(!m_lipDataSent)
{ {
@ -176,33 +204,27 @@ namespace ml_dht
} }
} }
// Settings void OnVRModeSwitch(bool p_state)
void OnEnabledChanged(bool p_state)
{ {
if(m_enabled != p_state) try
{ {
m_enabled = p_state; this.enabled = !Utils.IsInVR();
TryRestoreHeadRotation(); }
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
} }
} }
void OnHeadTrackingChanged(bool p_state)
// Settings
void OnEnabledOrHeadTrackingChanged(bool p_state)
{ {
if(m_headTracking != p_state) if(Settings.Enabled && Settings.HeadTracking)
{ m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation);
m_headTracking = p_state;
TryRestoreHeadRotation();
}
} }
void OnSmoothingChanged(float p_value) void OnSmoothingChanged(float p_value)
{ {
m_smoothing = 1f - Mathf.Clamp(p_value, 0f, 0.99f); m_smoothing = 1f - Mathf.Clamp(p_value, 0f, 0.99f);
} }
// Arbitrary
void TryRestoreHeadRotation()
{
if(m_enabled && m_headTracking)
m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation);
}
} }
} }

View file

@ -1,12 +1,13 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Core.Savior; using ABI_RC.Core.Savior;
using UnityEngine;
namespace ml_dht namespace ml_dht
{ {
public class DesktopHeadTracking : MelonLoader.MelonMod public class DesktopHeadTracking : MelonLoader.MelonMod
{ {
DataParser m_dataParser = null;
HeadTracked m_localTracked = null; HeadTracked m_tracked = null;
public override void OnInitializeMelon() public override void OnInitializeMelon()
{ {
@ -26,24 +27,14 @@ namespace ml_dht
GameEvents.InitB(HarmonyInstance); GameEvents.InitB(HarmonyInstance);
m_dataParser = new DataParser(); m_tracked = new GameObject("[DesktopHeadTracking]").AddComponent<HeadTracked>();
m_localTracked = PlayerSetup.Instance.gameObject.AddComponent<HeadTracked>();
} }
public override void OnDeinitializeMelon() public override void OnDeinitializeMelon()
{ {
m_dataParser = null; if(m_tracked != null)
m_localTracked = null; Object.Destroy(m_tracked.gameObject);
} m_tracked = null;
public override void OnUpdate()
{
if(Settings.Enabled && (m_dataParser != null))
{
m_dataParser.Update();
if(m_localTracked != null)
m_localTracked.UpdateTrackingData(ref m_dataParser.GetLatestTrackingData());
}
} }
} }
} }

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.2.5", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.3.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -13,7 +13,7 @@ Refer to `TrackingData.cs` for reference in case of implementing own software.
# Installation # Installation
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
* Get [latest release DLL](../../../releases/latest): * Get [latest release DLL](../../../releases/latest):
* Put `ml_dht.dll` in `Mods` folder of game * Put `DesktopHeadTracking.dll` in `Mods` folder of game
# Usage # Usage
Available mod's settings in `Settings - Implementation - Desktop Head Tracking`: Available mod's settings in `Settings - Implementation - Desktop Head Tracking`:

View file

@ -6,15 +6,16 @@ namespace ml_dht
{ {
static class ResourcesHandler static class ResourcesHandler
{ {
readonly static string ms_namespace = typeof(ResourcesHandler).Namespace;
public static string GetEmbeddedResource(string p_name) public static string GetEmbeddedResource(string p_name)
{ {
string l_result = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
StreamReader l_streadReader = new StreamReader(l_libraryStream); StreamReader l_streadReader = new StreamReader(l_libraryStream);
l_result = l_streadReader.ReadToEnd(); l_result = l_streadReader.ReadToEnd();
} }

View file

@ -1,5 +1,6 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Core.UI; using ABI_RC.Core.UI;
using ABI_RC.Systems.IK; using ABI_RC.Systems.IK;
using System.Reflection; using System.Reflection;
@ -13,6 +14,8 @@ namespace ml_dht
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly MethodInfo ms_updateShapesLocal = typeof(CVRFaceTracking).GetMethod("UpdateShapesLocal", BindingFlags.NonPublic | BindingFlags.Instance); static readonly MethodInfo ms_updateShapesLocal = typeof(CVRFaceTracking).GetMethod("UpdateShapesLocal", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsInVR() => ((MetaPort.Instance != null) && MetaPort.Instance.isUsingVr);
public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); public static void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);
public static void UpdateShapesLocal_Private(this CVRFaceTracking p_instance) => ms_updateShapesLocal?.Invoke(p_instance, ms_emptyArray); public static void UpdateShapesLocal_Private(this CVRFaceTracking p_instance) => ms_updateShapesLocal?.Invoke(p_instance, ms_emptyArray);

View file

@ -4,10 +4,11 @@
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<PackageId>DesktopHeadTracking</PackageId> <PackageId>DesktopHeadTracking</PackageId>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>None</Company> <Company>SDraw</Company>
<Product>DesktopHeadTracking</Product> <Product>DesktopHeadTracking</Product>
<Version>1.2.5</Version> <Version>1.3.0</Version>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<AssemblyName>DesktopHeadTracking</AssemblyName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

View file

@ -7,6 +7,8 @@ namespace ml_lme
{ {
static class AssetsHandler static class AssetsHandler
{ {
readonly static string ms_namespace = typeof(AssetsHandler).Namespace;
static readonly List<string> ms_assets = new List<string>() static readonly List<string> ms_assets = new List<string>()
{ {
"leapmotion_controller.asset", "leapmotion_controller.asset",
@ -19,13 +21,12 @@ namespace ml_lme
public static void Load() public static void Load()
{ {
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
foreach(string l_assetName in ms_assets) foreach(string l_assetName in ms_assets)
{ {
try try
{ {
Stream l_assetStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + l_assetName); Stream l_assetStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + l_assetName);
if(l_assetStream != null) if(l_assetStream != null)
{ {
MemoryStream l_memorySteam = new MemoryStream((int)l_assetStream.Length); MemoryStream l_memorySteam = new MemoryStream((int)l_assetStream.Length);

View file

@ -8,6 +8,8 @@ namespace ml_lme
{ {
static class DependenciesHandler static class DependenciesHandler
{ {
readonly static string ms_namespace = typeof(DependenciesHandler).Namespace;
static readonly List<string> ms_libraries = new List<string>() static readonly List<string> ms_libraries = new List<string>()
{ {
"LeapC.dll" "LeapC.dll"
@ -16,11 +18,10 @@ namespace ml_lme
public static void ExtractDependencies() public static void ExtractDependencies()
{ {
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
foreach(string l_library in ms_libraries) foreach(string l_library in ms_libraries)
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + l_library); Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + l_library);
if(!File.Exists(l_library)) if(!File.Exists(l_library))
{ {

View file

@ -123,107 +123,89 @@ namespace ml_lme
} }
public Transform GetRoot() => m_root; public Transform GetRoot() => m_root;
public Transform GetBone(HumanBodyBones p_bone) public Transform GetLinkedBone(HumanBodyBones p_bone)
{ {
Transform l_result = null; Transform l_result = null;
switch(p_bone) switch(p_bone)
{ {
case HumanBodyBones.LeftHand: case HumanBodyBones.LeftHand:
l_result = (m_left ? m_wrist : null); case HumanBodyBones.RightHand:
break; l_result = m_wrist;
case HumanBodyBones.LeftThumbProximal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.ThumbProximal] : null);
break;
case HumanBodyBones.LeftThumbIntermediate:
l_result = (m_left ? m_fingersBones[(int)FingerBone.ThumbIntermediate] : null);
break;
case HumanBodyBones.LeftThumbDistal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.ThumbDistal] : null);
break;
case HumanBodyBones.LeftIndexProximal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.IndexProximal] : null);
break;
case HumanBodyBones.LeftIndexIntermediate:
l_result = (m_left ? m_fingersBones[(int)FingerBone.IndexIntermediate] : null);
break;
case HumanBodyBones.LeftIndexDistal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.IndexDistal] : null);
break;
case HumanBodyBones.LeftMiddleProximal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.MiddleProximal] : null);
break;
case HumanBodyBones.LeftMiddleIntermediate:
l_result = (m_left ? m_fingersBones[(int)FingerBone.MiddleIntermediate] : null);
break;
case HumanBodyBones.LeftMiddleDistal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.MiddleDistal] : null);
break;
case HumanBodyBones.LeftRingProximal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.RingProximal] : null);
break;
case HumanBodyBones.LeftRingIntermediate:
l_result = (m_left ? m_fingersBones[(int)FingerBone.RingIntermediate] : null);
break;
case HumanBodyBones.LeftRingDistal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.RingDistal] : null);
break;
case HumanBodyBones.LeftLittleProximal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.PinkyProximal] : null);
break;
case HumanBodyBones.LeftLittleIntermediate:
l_result = (m_left ? m_fingersBones[(int)FingerBone.PinkyIntermediate] : null);
break;
case HumanBodyBones.LeftLittleDistal:
l_result = (m_left ? m_fingersBones[(int)FingerBone.PinkyDistal] : null);
break; break;
case HumanBodyBones.RightHand: case HumanBodyBones.LeftThumbProximal:
l_result = (!m_left ? m_wrist : null);
break;
case HumanBodyBones.RightThumbProximal: case HumanBodyBones.RightThumbProximal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.ThumbProximal] : null); l_result = m_fingersBones[(int)FingerBone.ThumbProximal];
break; break;
case HumanBodyBones.LeftThumbIntermediate:
case HumanBodyBones.RightThumbIntermediate: case HumanBodyBones.RightThumbIntermediate:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.ThumbIntermediate] : null); l_result = m_fingersBones[(int)FingerBone.ThumbIntermediate];
break; break;
case HumanBodyBones.LeftThumbDistal:
case HumanBodyBones.RightThumbDistal: case HumanBodyBones.RightThumbDistal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.ThumbDistal] : null); l_result = m_fingersBones[(int)FingerBone.ThumbDistal];
break; break;
case HumanBodyBones.LeftIndexProximal:
case HumanBodyBones.RightIndexProximal: case HumanBodyBones.RightIndexProximal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.IndexProximal] : null); l_result = m_fingersBones[(int)FingerBone.IndexProximal];
break; break;
case HumanBodyBones.LeftIndexIntermediate:
case HumanBodyBones.RightIndexIntermediate: case HumanBodyBones.RightIndexIntermediate:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.IndexIntermediate] : null); l_result = m_fingersBones[(int)FingerBone.IndexIntermediate];
break; break;
case HumanBodyBones.LeftIndexDistal:
case HumanBodyBones.RightIndexDistal: case HumanBodyBones.RightIndexDistal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.IndexDistal] : null); l_result = m_fingersBones[(int)FingerBone.IndexDistal];
break; break;
case HumanBodyBones.LeftMiddleProximal:
case HumanBodyBones.RightMiddleProximal: case HumanBodyBones.RightMiddleProximal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.MiddleProximal] : null); l_result = m_fingersBones[(int)FingerBone.MiddleProximal];
break; break;
case HumanBodyBones.LeftMiddleIntermediate:
case HumanBodyBones.RightMiddleIntermediate: case HumanBodyBones.RightMiddleIntermediate:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.MiddleIntermediate] : null); l_result = m_fingersBones[(int)FingerBone.MiddleIntermediate];
break; break;
case HumanBodyBones.LeftMiddleDistal:
case HumanBodyBones.RightMiddleDistal: case HumanBodyBones.RightMiddleDistal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.MiddleDistal] : null); l_result = m_fingersBones[(int)FingerBone.MiddleDistal];
break; break;
case HumanBodyBones.LeftRingProximal:
case HumanBodyBones.RightRingProximal: case HumanBodyBones.RightRingProximal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.RingProximal] : null); l_result = m_fingersBones[(int)FingerBone.RingProximal];
break; break;
case HumanBodyBones.LeftRingIntermediate:
case HumanBodyBones.RightRingIntermediate: case HumanBodyBones.RightRingIntermediate:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.RingIntermediate] : null); l_result = m_fingersBones[(int)FingerBone.RingIntermediate];
break; break;
case HumanBodyBones.LeftRingDistal:
case HumanBodyBones.RightRingDistal: case HumanBodyBones.RightRingDistal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.RingDistal] : null); l_result = m_fingersBones[(int)FingerBone.RingDistal];
break; break;
case HumanBodyBones.LeftLittleProximal:
case HumanBodyBones.RightLittleProximal: case HumanBodyBones.RightLittleProximal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.PinkyProximal] : null); l_result = m_fingersBones[(int)FingerBone.PinkyProximal];
break; break;
case HumanBodyBones.LeftLittleIntermediate:
case HumanBodyBones.RightLittleIntermediate: case HumanBodyBones.RightLittleIntermediate:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.PinkyIntermediate] : null); l_result = m_fingersBones[(int)FingerBone.PinkyIntermediate];
break; break;
case HumanBodyBones.LeftLittleDistal:
case HumanBodyBones.RightLittleDistal: case HumanBodyBones.RightLittleDistal:
l_result = (!m_left ? m_fingersBones[(int)FingerBone.PinkyDistal] : null); l_result = m_fingersBones[(int)FingerBone.PinkyDistal];
break; break;
} }
return l_result; return l_result;
@ -234,5 +216,7 @@ namespace ml_lme
if(m_mesh != null) if(m_mesh != null)
m_mesh.SetActive(p_state); m_mesh.SetActive(p_state);
} }
public bool IsLeft() => m_left;
} }
} }

View file

@ -89,8 +89,7 @@ namespace ml_lme
MelonLoader.MelonCoroutines.Start(WaitForSettings()); MelonLoader.MelonCoroutines.Start(WaitForSettings());
MelonLoader.MelonCoroutines.Start(WaitForMaterial()); MelonLoader.MelonCoroutines.Start(WaitForMaterial());
VRModeSwitchEvents.OnInitializeXR.AddListener(OnModeSwitch); VRModeSwitchEvents.OnCompletedVRModeSwitch.AddListener(OnVRModeSwitch);
VRModeSwitchEvents.OnDeinitializeXR.AddListener(OnModeSwitch);
Settings.OnEnabledChanged.AddListener(this.OnEnableChanged); Settings.OnEnabledChanged.AddListener(this.OnEnableChanged);
Settings.OnInteractionChanged.AddListener(this.OnInteractionChanged); Settings.OnInteractionChanged.AddListener(this.OnInteractionChanged);
@ -153,8 +152,7 @@ namespace ml_lme
m_lineRight = null; m_lineRight = null;
MetaPort.Instance.settings.settingBoolChanged.RemoveListener(this.OnGameSettingBoolChange); MetaPort.Instance.settings.settingBoolChanged.RemoveListener(this.OnGameSettingBoolChange);
VRModeSwitchEvents.OnInitializeXR.RemoveListener(OnModeSwitch); VRModeSwitchEvents.OnCompletedVRModeSwitch.RemoveListener(OnVRModeSwitch);
VRModeSwitchEvents.OnDeinitializeXR.RemoveListener(OnModeSwitch);
Settings.OnEnabledChanged.RemoveListener(this.OnEnableChanged); Settings.OnEnabledChanged.RemoveListener(this.OnEnableChanged);
Settings.OnInteractionChanged.RemoveListener(this.OnInteractionChanged); Settings.OnInteractionChanged.RemoveListener(this.OnInteractionChanged);
@ -291,7 +289,7 @@ namespace ml_lme
m_handVisibleRight = false; m_handVisibleRight = false;
} }
if(!ModSupporter.SkipFingersOverride() && (!m_inVR || !Utils.AreKnucklesInUse())) if(!m_inVR || !Utils.AreKnucklesInUse())
SetGameFingersTracking(m_handVisibleRight || m_handVisibleLeft); SetGameFingersTracking(m_handVisibleRight || m_handVisibleLeft);
base.UpdateInput(); base.UpdateInput();
@ -384,7 +382,7 @@ namespace ml_lme
ResetGestures(false); ResetGestures(false);
} }
// Reset to default, FreedomFingers can go brrr, player should press funny controller button two times // Reset to default
SetGameFingersTracking(m_inVR && Utils.AreKnucklesInUse() && !CVRInputManager._moduleXR.GestureToggleValue); SetGameFingersTracking(m_inVR && Utils.AreKnucklesInUse() && !CVRInputManager._moduleXR.GestureToggleValue);
} }
@ -450,23 +448,30 @@ namespace ml_lme
} }
} }
void OnModeSwitch() void OnVRModeSwitch(bool p_state)
{ {
m_inVR = Utils.IsInVR(); try
base._inputManager.SetModuleAsLast(this);
if(m_handRayLeft != null)
{ {
m_handRayLeft.isDesktopRay = !m_inVR; m_inVR = Utils.IsInVR();
m_handRayLeft.SetVRActive(m_inVR); base._inputManager.SetModuleAsLast(this);
}
if(m_handRayRight != null)
{
m_handRayRight.isDesktopRay = !m_inVR;
m_handRayRight.SetVRActive(m_inVR);
}
OnEnableChanged(Settings.Enabled); if(m_handRayLeft != null)
{
m_handRayLeft.isDesktopRay = !m_inVR;
m_handRayLeft.SetVRActive(m_inVR);
}
if(m_handRayRight != null)
{
m_handRayRight.isDesktopRay = !m_inVR;
m_handRayRight.SetVRActive(m_inVR);
}
OnEnableChanged(Settings.Enabled);
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
} }
// Arbitrary // Arbitrary

View file

@ -19,9 +19,9 @@ namespace ml_lme
void Awake() void Awake()
{ {
if((Instance != null) && (Instance != this)) if(Instance != null)
{ {
Object.DestroyImmediate(this); DestroyImmediate(this);
return; return;
} }
@ -44,8 +44,7 @@ namespace ml_lme
Settings.OnEnabledChanged.AddListener(this.OnEnableChanged); Settings.OnEnabledChanged.AddListener(this.OnEnableChanged);
Settings.OnTrackingModeChanged.AddListener(this.OnTrackingModeChanged); Settings.OnTrackingModeChanged.AddListener(this.OnTrackingModeChanged);
m_leapTracking = new GameObject("[LeapTrackingRoot]").AddComponent<LeapTracking>(); m_leapTracking = this.gameObject.AddComponent<LeapTracking>();
m_leapTracking.transform.parent = this.transform;
OnEnableChanged(Settings.Enabled); OnEnableChanged(Settings.Enabled);
OnTrackingModeChanged(Settings.TrackingMode); OnTrackingModeChanged(Settings.TrackingMode);
@ -102,7 +101,7 @@ namespace ml_lme
m_leapInput = new LeapInput(); m_leapInput = new LeapInput();
CVRInputManager.Instance.AddInputModule(m_leapInput); CVRInputManager.Instance.AddInputModule(m_leapInput);
m_leapTracked = PlayerSetup.Instance.gameObject.AddComponent<LeapTracked>(); m_leapTracked = this.gameObject.AddComponent<LeapTracked>();
} }
void Update() void Update()

View file

@ -77,18 +77,28 @@ namespace ml_lme
(HumanBodyBones.RightLittleIntermediate, false), (HumanBodyBones.RightLittleIntermediate, false),
(HumanBodyBones.RightLittleDistal, false), (HumanBodyBones.RightLittleDistal, false),
}; };
static readonly (HumanBodyBones, HumanBodyBones, bool)[] ms_rotationFixChains = static readonly (HumanBodyBones, HumanBodyBones, bool)[] ms_fingersChains =
{ {
(HumanBodyBones.LeftThumbProximal,HumanBodyBones.LeftThumbIntermediate,true), (HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,true), (HumanBodyBones.LeftThumbProximal,HumanBodyBones.LeftThumbIntermediate,true), (HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal,true), (HumanBodyBones.LeftThumbDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.LeftIndexProximal,HumanBodyBones.LeftIndexIntermediate,true), (HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,true), (HumanBodyBones.LeftIndexProximal,HumanBodyBones.LeftIndexIntermediate,true), (HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal,true), (HumanBodyBones.LeftIndexDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.LeftMiddleProximal,HumanBodyBones.LeftMiddleIntermediate,true), (HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,true), (HumanBodyBones.LeftMiddleProximal,HumanBodyBones.LeftMiddleIntermediate,true), (HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal,true), (HumanBodyBones.LeftMiddleDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.LeftRingProximal,HumanBodyBones.LeftRingIntermediate,true), (HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,true), (HumanBodyBones.LeftRingProximal,HumanBodyBones.LeftRingIntermediate,true), (HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal,true), (HumanBodyBones.LeftRingDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.LeftLittleProximal,HumanBodyBones.LeftLittleIntermediate,true), (HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal,true), (HumanBodyBones.LeftLittleProximal,HumanBodyBones.LeftLittleIntermediate,true), (HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal,true), (HumanBodyBones.LeftLittleDistal,HumanBodyBones.LastBone,true),
(HumanBodyBones.RightThumbProximal,HumanBodyBones.RightThumbIntermediate,false), (HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,false), (HumanBodyBones.RightThumbProximal,HumanBodyBones.RightThumbIntermediate,false), (HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal,false), (HumanBodyBones.RightThumbDistal,HumanBodyBones.LastBone,false),
(HumanBodyBones.RightIndexProximal,HumanBodyBones.RightIndexIntermediate,false), (HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,false), (HumanBodyBones.RightIndexProximal,HumanBodyBones.RightIndexIntermediate,false), (HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal,false), (HumanBodyBones.RightIndexDistal,HumanBodyBones.LastBone,false),
(HumanBodyBones.RightMiddleProximal,HumanBodyBones.RightMiddleIntermediate,false), (HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,false), (HumanBodyBones.RightMiddleProximal,HumanBodyBones.RightMiddleIntermediate,false), (HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal,false), (HumanBodyBones.RightMiddleDistal,HumanBodyBones.LastBone,false),
(HumanBodyBones.RightRingProximal,HumanBodyBones.RightRingIntermediate,false), (HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,false), (HumanBodyBones.RightRingProximal,HumanBodyBones.RightRingIntermediate,false), (HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal,false), (HumanBodyBones.RightRingDistal,HumanBodyBones.LastBone,false),
(HumanBodyBones.RightLittleProximal,HumanBodyBones.RightLittleIntermediate,false), (HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal,false) (HumanBodyBones.RightLittleProximal,HumanBodyBones.RightLittleIntermediate,false), (HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal,false),(HumanBodyBones.RightLittleDistal,HumanBodyBones.LastBone,false),
};
static readonly Vector3[] ms_directions =
{
Vector3.forward,
Vector3.back,
Vector3.left,
Vector3.right,
Vector3.up,
Vector3.down,
}; };
public static readonly float[] ms_lastLeftFingerBones = new float[20]; public static readonly float[] ms_lastLeftFingerBones = new float[20];
@ -97,10 +107,6 @@ namespace ml_lme
VRIK m_vrIK = null; VRIK m_vrIK = null;
Transform m_hips = null; Transform m_hips = null;
bool m_enabled = true;
bool m_fingersOnly = false;
bool m_trackElbows = true;
IKInfo m_vrIKInfo; IKInfo m_vrIKInfo;
ArmIK m_leftArmIK = null; ArmIK m_leftArmIK = null;
ArmIK m_rightArmIK = null; ArmIK m_rightArmIK = null;
@ -135,12 +141,11 @@ namespace ml_lme
m_rightHandTarget.localPosition = Vector3.zero; m_rightHandTarget.localPosition = Vector3.zero;
m_rightHandTarget.localRotation = Quaternion.identity; m_rightHandTarget.localRotation = Quaternion.identity;
OnEnabledChanged(Settings.Enabled); OnEnabledOrFingersOnlyChanged(Settings.Enabled || Settings.FingersOnly);
OnFingersOnlyChanged(Settings.FingersOnly);
OnTrackElbowsChanged(Settings.TrackElbows); OnTrackElbowsChanged(Settings.TrackElbows);
Settings.OnEnabledChanged.AddListener(this.OnEnabledChanged); Settings.OnEnabledChanged.AddListener(this.OnEnabledOrFingersOnlyChanged);
Settings.OnFingersOnlyChanged.AddListener(this.OnFingersOnlyChanged); Settings.OnFingersOnlyChanged.AddListener(this.OnEnabledOrFingersOnlyChanged);
Settings.OnTrackElbowsChanged.AddListener(this.OnTrackElbowsChanged); Settings.OnTrackElbowsChanged.AddListener(this.OnTrackElbowsChanged);
GameEvents.OnAvatarClear.AddListener(this.OnAvatarClear); GameEvents.OnAvatarClear.AddListener(this.OnAvatarClear);
@ -165,8 +170,8 @@ namespace ml_lme
m_vrIK = null; m_vrIK = null;
Settings.OnEnabledChanged.RemoveListener(this.OnEnabledChanged); Settings.OnEnabledChanged.RemoveListener(this.OnEnabledOrFingersOnlyChanged);
Settings.OnFingersOnlyChanged.RemoveListener(this.OnFingersOnlyChanged); Settings.OnFingersOnlyChanged.RemoveListener(this.OnEnabledOrFingersOnlyChanged);
Settings.OnTrackElbowsChanged.RemoveListener(this.OnTrackElbowsChanged); Settings.OnTrackElbowsChanged.RemoveListener(this.OnTrackElbowsChanged);
GameEvents.OnAvatarClear.RemoveListener(this.OnAvatarClear); GameEvents.OnAvatarClear.RemoveListener(this.OnAvatarClear);
@ -176,24 +181,24 @@ namespace ml_lme
void Update() void Update()
{ {
if(m_enabled) if(Settings.Enabled)
{ {
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData(); LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
if((m_leftArmIK != null) && (m_rightArmIK != null)) if((m_leftArmIK != null) && (m_rightArmIK != null))
{ {
m_leftArmIK.solver.IKPositionWeight = Mathf.Lerp(m_leftArmIK.solver.IKPositionWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f); m_leftArmIK.solver.IKPositionWeight = Mathf.Lerp(m_leftArmIK.solver.IKPositionWeight, (l_data.m_leftHand.m_present && !Settings.FingersOnly) ? 1f : 0f, 0.25f);
m_leftArmIK.solver.IKRotationWeight = Mathf.Lerp(m_leftArmIK.solver.IKRotationWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f); m_leftArmIK.solver.IKRotationWeight = Mathf.Lerp(m_leftArmIK.solver.IKRotationWeight, (l_data.m_leftHand.m_present && !Settings.FingersOnly) ? 1f : 0f, 0.25f);
if(m_trackElbows) if(Settings.TrackElbows)
m_leftArmIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_leftArmIK.solver.arm.bendGoalWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f); m_leftArmIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_leftArmIK.solver.arm.bendGoalWeight, (l_data.m_leftHand.m_present && !Settings.FingersOnly) ? 1f : 0f, 0.25f);
m_rightArmIK.solver.IKPositionWeight = Mathf.Lerp(m_rightArmIK.solver.IKPositionWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f); m_rightArmIK.solver.IKPositionWeight = Mathf.Lerp(m_rightArmIK.solver.IKPositionWeight, (l_data.m_rightHand.m_present && !Settings.FingersOnly) ? 1f : 0f, 0.25f);
m_rightArmIK.solver.IKRotationWeight = Mathf.Lerp(m_rightArmIK.solver.IKRotationWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f); m_rightArmIK.solver.IKRotationWeight = Mathf.Lerp(m_rightArmIK.solver.IKRotationWeight, (l_data.m_rightHand.m_present && !Settings.FingersOnly) ? 1f : 0f, 0.25f);
if(m_trackElbows) if(Settings.TrackElbows)
m_rightArmIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_rightArmIK.solver.arm.bendGoalWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f); m_rightArmIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_rightArmIK.solver.arm.bendGoalWeight, (l_data.m_rightHand.m_present && !Settings.FingersOnly) ? 1f : 0f, 0.25f);
} }
if((m_vrIK != null) && !m_fingersOnly) if((m_vrIK != null) && !Settings.FingersOnly)
{ {
m_leftTargetActive = l_data.m_leftHand.m_present; m_leftTargetActive = l_data.m_leftHand.m_present;
m_rightTargetActive = l_data.m_rightHand.m_present; m_rightTargetActive = l_data.m_rightHand.m_present;
@ -203,7 +208,7 @@ namespace ml_lme
void LateUpdate() void LateUpdate()
{ {
if(m_enabled && (m_poseHandler != null)) if(Settings.Enabled && (m_poseHandler != null))
{ {
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData(); LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
if(l_data.m_leftHand.m_present) if(l_data.m_leftHand.m_present)
@ -276,24 +281,25 @@ namespace ml_lme
void OnAvatarSetup() void OnAvatarSetup()
{ {
if(PlayerSetup.Instance._animator.isHuman) Animator l_animator = PlayerSetup.Instance._animator;
if(l_animator.isHuman)
{ {
Utils.SetAvatarTPose(); Utils.SetAvatarTPose();
m_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._animator.transform); m_poseHandler = new HumanPoseHandler(l_animator.avatar, l_animator.transform);
m_poseHandler.GetHumanPose(ref m_pose); m_poseHandler.GetHumanPose(ref m_pose);
m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips); m_hips = l_animator.GetBoneTransform(HumanBodyBones.Hips);
m_leftHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand); m_leftHandOffset.m_source = l_animator.GetBoneTransform(HumanBodyBones.LeftHand);
m_leftHandTarget.localRotation = ms_offsetLeft * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_leftHandOffset.m_source.rotation); m_leftHandTarget.localRotation = ms_offsetLeft * (Quaternion.Inverse(l_animator.transform.rotation) * m_leftHandOffset.m_source.rotation);
m_rightHandOffset.m_source = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand); m_rightHandOffset.m_source = l_animator.GetBoneTransform(HumanBodyBones.RightHand);
m_rightHandTarget.localRotation = ms_offsetRight * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_rightHandOffset.m_source.rotation); m_rightHandTarget.localRotation = ms_offsetRight * (Quaternion.Inverse(l_animator.transform.rotation) * m_rightHandOffset.m_source.rotation);
ParseFingersBones(); ParseFingersBones();
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>(); m_vrIK = l_animator.GetComponent<VRIK>();
if(m_vrIK != null) if(m_vrIK != null)
{ {
m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreSolverUpdate); m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreSolverUpdate);
@ -339,7 +345,7 @@ namespace ml_lme
m_vrIK.solver.leftArm.positionWeight = 1f; m_vrIK.solver.leftArm.positionWeight = 1f;
m_vrIK.solver.leftArm.rotationWeight = 1f; m_vrIK.solver.leftArm.rotationWeight = 1f;
m_vrIK.solver.leftArm.bendGoal = LeapTracking.Instance.GetLeftElbow(); m_vrIK.solver.leftArm.bendGoal = LeapTracking.Instance.GetLeftElbow();
m_vrIK.solver.leftArm.bendGoalWeight = (m_trackElbows ? 1f : 0f); m_vrIK.solver.leftArm.bendGoalWeight = (Settings.TrackElbows ? 1f : 0f);
} }
if(m_rightTargetActive) if(m_rightTargetActive)
{ {
@ -353,7 +359,7 @@ namespace ml_lme
m_vrIK.solver.rightArm.positionWeight = 1f; m_vrIK.solver.rightArm.positionWeight = 1f;
m_vrIK.solver.rightArm.rotationWeight = 1f; m_vrIK.solver.rightArm.rotationWeight = 1f;
m_vrIK.solver.rightArm.bendGoal = LeapTracking.Instance.GetRightElbow(); m_vrIK.solver.rightArm.bendGoal = LeapTracking.Instance.GetRightElbow();
m_vrIK.solver.rightArm.bendGoalWeight = (m_trackElbows ? 1f : 0f); m_vrIK.solver.rightArm.bendGoalWeight = (Settings.TrackElbows ? 1f : 0f);
} }
} }
void OnIKPostSolverUpdate() void OnIKPostSolverUpdate()
@ -377,30 +383,18 @@ namespace ml_lme
} }
// Settings // Settings
void OnEnabledChanged(bool p_state) void OnEnabledOrFingersOnlyChanged(bool p_state)
{ {
m_enabled = p_state;
RefreshArmIK();
ResetTargetsStates();
}
void OnFingersOnlyChanged(bool p_state)
{
m_fingersOnly = p_state;
RefreshArmIK(); RefreshArmIK();
ResetTargetsStates(); ResetTargetsStates();
} }
void OnTrackElbowsChanged(bool p_state) void OnTrackElbowsChanged(bool p_state)
{ {
m_trackElbows = p_state;
if((m_leftArmIK != null) && (m_rightArmIK != null)) if((m_leftArmIK != null) && (m_rightArmIK != null))
{ {
m_leftArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f); m_leftArmIK.solver.arm.bendGoalWeight = (p_state ? 1f : 0f);
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f); m_rightArmIK.solver.arm.bendGoalWeight = (p_state ? 1f : 0f);
} }
ResetTargetsStates(); ResetTargetsStates();
@ -415,41 +409,42 @@ namespace ml_lme
void SetupArmIK() void SetupArmIK()
{ {
Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest); Animator l_animator = PlayerSetup.Instance._animator;
Transform l_chest = l_animator.GetBoneTransform(HumanBodyBones.UpperChest);
if(l_chest == null) if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest); l_chest = l_animator.GetBoneTransform(HumanBodyBones.Chest);
if(l_chest == null) if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine); l_chest = l_animator.GetBoneTransform(HumanBodyBones.Spine);
m_leftArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>(); m_leftArmIK = l_animator.gameObject.AddComponent<ArmIK>();
m_leftArmIK.solver.isLeft = true; m_leftArmIK.solver.isLeft = true;
m_leftArmIK.solver.SetChain( m_leftArmIK.solver.SetChain(
l_chest, l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftShoulder), l_animator.GetBoneTransform(HumanBodyBones.LeftShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftUpperArm), l_animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftLowerArm), l_animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand), l_animator.GetBoneTransform(HumanBodyBones.LeftHand),
PlayerSetup.Instance._animator.transform l_animator.transform
); );
m_leftArmIK.solver.arm.target = m_leftHandTarget; m_leftArmIK.solver.arm.target = m_leftHandTarget;
m_leftArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetLeftElbow(); m_leftArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetLeftElbow();
m_leftArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f); m_leftArmIK.solver.arm.bendGoalWeight = (Settings.TrackElbows ? 1f : 0f);
m_leftArmIK.enabled = (m_enabled && !m_fingersOnly); m_leftArmIK.enabled = (Settings.Enabled && !Settings.FingersOnly);
m_rightArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>(); m_rightArmIK = l_animator.gameObject.AddComponent<ArmIK>();
m_rightArmIK.solver.isLeft = false; m_rightArmIK.solver.isLeft = false;
m_rightArmIK.solver.SetChain( m_rightArmIK.solver.SetChain(
l_chest, l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder), l_animator.GetBoneTransform(HumanBodyBones.RightShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm), l_animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm), l_animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand), l_animator.GetBoneTransform(HumanBodyBones.RightHand),
PlayerSetup.Instance._animator.transform l_animator.transform
); );
m_rightArmIK.solver.arm.target = m_rightHandTarget; m_rightArmIK.solver.arm.target = m_rightHandTarget;
m_rightArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetRightElbow(); m_rightArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetRightElbow();
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f); m_rightArmIK.solver.arm.bendGoalWeight = (Settings.TrackElbows ? 1f : 0f);
m_rightArmIK.enabled = (m_enabled && !m_fingersOnly); m_rightArmIK.enabled = (Settings.Enabled && !Settings.FingersOnly);
} }
void RemoveArmIK() void RemoveArmIK()
@ -467,8 +462,8 @@ namespace ml_lme
{ {
if((m_leftArmIK != null) && (m_rightArmIK != null)) if((m_leftArmIK != null) && (m_rightArmIK != null))
{ {
m_leftArmIK.enabled = (m_enabled && !m_fingersOnly); m_leftArmIK.enabled = (Settings.Enabled && !Settings.FingersOnly);
m_rightArmIK.enabled = (m_enabled && !m_fingersOnly); m_rightArmIK.enabled = (Settings.Enabled && !Settings.FingersOnly);
} }
} }
@ -476,42 +471,48 @@ namespace ml_lme
{ {
LeapTracking.Instance.Rebind(PlayerSetup.Instance.transform.rotation); LeapTracking.Instance.Rebind(PlayerSetup.Instance.transform.rotation);
// Align rotations of leap fingers to avatar fingers
Animator l_animator = PlayerSetup.Instance._animator;
LeapHand l_leapLeft = LeapTracking.Instance.GetLeftHand();
LeapHand l_leapRight = LeapTracking.Instance.GetRightHand();
// Try to "fix" rotations, slightly inaccurate after 0YX plane rotation // Try to "fix" rotations, slightly inaccurate after 0YX plane rotation
foreach(var l_tuple in ms_rotationFixChains) foreach(var l_tuple in ms_fingersChains)
{ {
ReorientateTowards( ReorientateTowards(
PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1), PlayerSetup.Instance.transform,
PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2), l_animator.GetBoneTransform(l_tuple.Item1),
l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item1) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item1), (l_tuple.Item2 != HumanBodyBones.LastBone) ? l_animator.GetBoneTransform(l_tuple.Item2) : null,
l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item2) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item2), l_tuple.Item3 ? l_leapLeft.GetLinkedBone(l_tuple.Item1) : l_leapRight.GetLinkedBone(l_tuple.Item1),
l_tuple.Item3 ? l_leapLeft.GetLinkedBone(l_tuple.Item2) : l_leapRight.GetLinkedBone(l_tuple.Item2),
PlaneType.OXZ PlaneType.OXZ
); );
ReorientateTowards( ReorientateTowards(
PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item1), PlayerSetup.Instance.transform,
PlayerSetup.Instance._animator.GetBoneTransform(l_tuple.Item2), l_animator.GetBoneTransform(l_tuple.Item1),
l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item1) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item1), (l_tuple.Item2 != HumanBodyBones.LastBone) ? l_animator.GetBoneTransform(l_tuple.Item2) : null,
l_tuple.Item3 ? LeapTracking.Instance.GetLeftHand().GetBone(l_tuple.Item2) : LeapTracking.Instance.GetRightHand().GetBone(l_tuple.Item2), l_tuple.Item3 ? l_leapLeft.GetLinkedBone(l_tuple.Item1) : l_leapRight.GetLinkedBone(l_tuple.Item1),
l_tuple.Item3 ? l_leapLeft.GetLinkedBone(l_tuple.Item2) : l_leapRight.GetLinkedBone(l_tuple.Item2),
PlaneType.OYX PlaneType.OYX
); );
} }
// Bind // Bind
m_leftHandOffset.m_target = LeapTracking.Instance.GetLeftHand().GetBone(HumanBodyBones.LeftHand); m_leftHandOffset.m_target = l_leapLeft.GetLinkedBone(HumanBodyBones.LeftHand);
if((m_leftHandOffset.m_source != null) && (m_leftHandOffset.m_target != null)) 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_leftHandOffset.m_offset = Quaternion.Inverse(m_leftHandOffset.m_source.rotation) * m_leftHandOffset.m_target.rotation;
m_rightHandOffset.m_target = LeapTracking.Instance.GetRightHand().GetBone(HumanBodyBones.RightHand); m_rightHandOffset.m_target = l_leapRight.GetLinkedBone(HumanBodyBones.RightHand);
if((m_rightHandOffset.m_source != null) && (m_rightHandOffset.m_target != null)) 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; m_rightHandOffset.m_offset = Quaternion.Inverse(m_rightHandOffset.m_source.rotation) * m_rightHandOffset.m_target.rotation;
foreach(var l_link in ms_fingers) foreach(var l_link in ms_fingers)
{ {
Transform l_transform = PlayerSetup.Instance._animator.GetBoneTransform(l_link.Item1); Transform l_transform = l_animator.GetBoneTransform(l_link.Item1);
if(l_transform != null) if(l_transform != null)
{ {
RotationOffset l_offset = new RotationOffset(); RotationOffset l_offset = new RotationOffset();
l_offset.m_target = l_transform; l_offset.m_target = l_transform;
l_offset.m_source = (l_link.Item2 ? LeapTracking.Instance.GetLeftHand().GetBone(l_link.Item1) : LeapTracking.Instance.GetRightHand().GetBone(l_link.Item1)); l_offset.m_source = (l_link.Item2 ? l_leapLeft.GetLinkedBone(l_link.Item1) : l_leapRight.GetLinkedBone(l_link.Item1));
l_offset.m_offset = Quaternion.Inverse(l_offset.m_source.rotation) * l_offset.m_target.rotation; l_offset.m_offset = Quaternion.Inverse(l_offset.m_source.rotation) * l_offset.m_target.rotation;
if(l_link.Item2) if(l_link.Item2)
@ -522,13 +523,13 @@ namespace ml_lme
} }
} }
void ReorientateTowards(Transform p_target, Transform p_targetEnd, Transform p_source, Transform p_sourceEnd, PlaneType p_plane) static void ReorientateTowards(Transform root, Transform p_source, Transform p_sourceEnd, Transform p_target, Transform p_targetEnd, PlaneType p_plane)
{ {
if((p_target != null) && (p_targetEnd != null) && (p_source != null) && (p_sourceEnd != null)) if((root != null) && (p_target != null) && (p_source != null))
{ {
Quaternion l_playerInv = Quaternion.Inverse(PlayerSetup.Instance.transform.rotation); Quaternion l_rootInv = Quaternion.Inverse(root.rotation);
Vector3 l_targetDir = l_playerInv * (p_targetEnd.position - p_target.position); Vector3 l_targetDir = l_rootInv * (((p_targetEnd != null) ? p_targetEnd.position : GuessEnd(p_target)) - p_target.position);
Vector3 l_sourceDir = l_playerInv * (p_sourceEnd.position - p_source.position); Vector3 l_sourceDir = l_rootInv * (((p_sourceEnd != null) ? p_sourceEnd.position : GuessEnd(p_source)) - p_source.position);
switch(p_plane) switch(p_plane)
{ {
case PlaneType.OXZ: case PlaneType.OXZ:
@ -561,9 +562,30 @@ namespace ml_lme
if(p_plane == PlaneType.OYX) if(p_plane == PlaneType.OYX)
l_diff = Quaternion.Euler(0f, 0f, l_diff.eulerAngles.y); l_diff = Quaternion.Euler(0f, 0f, l_diff.eulerAngles.y);
Quaternion l_adjusted = l_diff * (l_playerInv * p_target.rotation); Quaternion l_adjusted = l_diff * (l_rootInv * p_target.rotation);
p_target.rotation = PlayerSetup.Instance.transform.rotation * l_adjusted; p_target.rotation = root.rotation * l_adjusted;
} }
} }
static Vector3 GuessEnd(Transform p_target)
{
Vector3 l_result = p_target.position;
if(p_target.parent != null)
{
float l_dot = -1f;
Vector3 l_axisDir = p_target.position - p_target.parent.position;
foreach(Vector3 l_dir in ms_directions)
{
Vector3 l_rotDir = p_target.rotation * l_dir;
float l_stepDot = Vector3.Dot(l_rotDir, l_axisDir);
if(l_stepDot >= l_dot)
{
l_dot = l_stepDot;
l_result = p_target.position + l_rotDir;
}
}
}
return l_result;
}
} }
} }

View file

@ -1,6 +1,4 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Systems.VRModeSwitch;
using System.Collections;
using UnityEngine; using UnityEngine;
namespace ml_lme namespace ml_lme
@ -13,8 +11,8 @@ namespace ml_lme
static readonly Quaternion ms_hmdRotation = new Quaternion(0f, 0.7071068f, 0.7071068f, 0f); static readonly Quaternion ms_hmdRotation = new Quaternion(0f, 0.7071068f, 0.7071068f, 0f);
static readonly Quaternion ms_screentopRotation = new Quaternion(0f, 0f, -1f, 0f); static readonly Quaternion ms_screentopRotation = new Quaternion(0f, 0f, -1f, 0f);
bool m_inVR = false; Transform m_root = null;
Transform m_offsetPoint = null;
GameObject m_leapHands = null; GameObject m_leapHands = null;
LeapHand m_leapHandLeft = null; LeapHand m_leapHandLeft = null;
LeapHand m_leapHandRight = null; LeapHand m_leapHandRight = null;
@ -22,26 +20,33 @@ namespace ml_lme
Transform m_leapElbowRight = null; Transform m_leapElbowRight = null;
GameObject m_leapControllerModel = null; GameObject m_leapControllerModel = null;
float m_scaleRelation = 1f;
void Start() void Start()
{ {
if((Instance != null) && (Instance != this)) if(Instance != null)
{ {
Object.DestroyImmediate(this); Object.DestroyImmediate(this);
return; return;
} }
Instance = this; Instance = this;
m_inVR = Utils.IsInVR();
m_root = new GameObject("Root").transform;
m_root.parent = this.transform;
m_root.localPosition = Vector3.zero;
m_root.localRotation = Quaternion.identity;
m_offsetPoint = new GameObject("OffsetPoint").transform;
m_offsetPoint.parent = m_root;
m_offsetPoint.localPosition = Vector3.zero;
m_offsetPoint.localRotation = Quaternion.identity;
m_leapElbowLeft = new GameObject("LeapElbowLeft").transform; m_leapElbowLeft = new GameObject("LeapElbowLeft").transform;
m_leapElbowLeft.parent = this.transform; m_leapElbowLeft.parent = m_offsetPoint;
m_leapElbowLeft.localPosition = Vector3.zero; m_leapElbowLeft.localPosition = Vector3.zero;
m_leapElbowLeft.localRotation = Quaternion.identity; m_leapElbowLeft.localRotation = Quaternion.identity;
m_leapElbowRight = new GameObject("LeapElbowRight").transform; m_leapElbowRight = new GameObject("LeapElbowRight").transform;
m_leapElbowRight.parent = this.transform; m_leapElbowRight.parent = m_offsetPoint;
m_leapElbowRight.localPosition = Vector3.zero; m_leapElbowRight.localPosition = Vector3.zero;
m_leapElbowRight.localRotation = Quaternion.identity; m_leapElbowRight.localRotation = Quaternion.identity;
@ -49,7 +54,7 @@ namespace ml_lme
if(m_leapControllerModel != null) if(m_leapControllerModel != null)
{ {
m_leapControllerModel.name = "LeapModel"; m_leapControllerModel.name = "LeapModel";
m_leapControllerModel.transform.parent = this.transform; m_leapControllerModel.transform.parent = m_offsetPoint;
m_leapControllerModel.transform.localPosition = Vector3.zero; m_leapControllerModel.transform.localPosition = Vector3.zero;
m_leapControllerModel.transform.localRotation = Quaternion.identity; m_leapControllerModel.transform.localRotation = Quaternion.identity;
} }
@ -58,7 +63,7 @@ namespace ml_lme
if(m_leapHands != null) if(m_leapHands != null)
{ {
m_leapHands.name = "LeapHands"; m_leapHands.name = "LeapHands";
m_leapHands.transform.parent = this.transform; m_leapHands.transform.parent = m_offsetPoint;
m_leapHands.transform.localPosition = Vector3.zero; m_leapHands.transform.localPosition = Vector3.zero;
m_leapHands.transform.localRotation = Quaternion.identity; m_leapHands.transform.localRotation = Quaternion.identity;
@ -69,34 +74,21 @@ namespace ml_lme
OnModelVisibilityChanged(Settings.ModelVisibility); OnModelVisibilityChanged(Settings.ModelVisibility);
OnVisualHandsChanged(Settings.VisualHands); OnVisualHandsChanged(Settings.VisualHands);
OnTrackingModeChanged(Settings.TrackingMode); OnTrackingModeChanged(Settings.TrackingMode);
OnHeadAttachChanged(Settings.HeadAttach);
OnRootAngleChanged(Settings.RootAngle); OnRootAngleChanged(Settings.RootAngle);
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); Settings.OnEnabledChanged.AddListener(this.OnEnabledChanged);
VRModeSwitchEvents.OnInitializeXR.AddListener(this.OnAvatarSetup);
VRModeSwitchEvents.OnDeinitializeXR.AddListener(this.OnAvatarSetup);
Settings.OnDesktopOffsetChanged.AddListener(this.OnDesktopOffsetChanged);
Settings.OnModelVisibilityChanged.AddListener(this.OnModelVisibilityChanged); Settings.OnModelVisibilityChanged.AddListener(this.OnModelVisibilityChanged);
Settings.OnVisualHandsChanged.AddListener(this.OnVisualHandsChanged); Settings.OnVisualHandsChanged.AddListener(this.OnVisualHandsChanged);
Settings.OnTrackingModeChanged.AddListener(this.OnTrackingModeChanged); Settings.OnTrackingModeChanged.AddListener(this.OnTrackingModeChanged);
Settings.OnRootAngleChanged.AddListener(this.OnRootAngleChanged);
Settings.OnHeadAttachChanged.AddListener(this.OnHeadAttachChanged); Settings.OnHeadAttachChanged.AddListener(this.OnHeadAttachChanged);
Settings.OnHeadOffsetChanged.AddListener(this.OnHeadOffsetChanged); Settings.OnHeadOffsetChanged.AddListener(this.OnHeadOffsetChanged);
Settings.OnDesktopOffsetChanged.AddListener(this.OnDesktopOffsetChanged);
Settings.OnRootAngleChanged.AddListener(this.OnRootAngleChanged);
GameEvents.OnAvatarClear.AddListener(this.OnAvatarClear);
GameEvents.OnAvatarSetup.AddListener(this.OnAvatarSetup);
GameEvents.OnPlayspaceScale.AddListener(this.OnPlayspaceScale); GameEvents.OnPlayspaceScale.AddListener(this.OnPlayspaceScale);
} }
IEnumerator WaitForLocalPlayer()
{
while(PlayerSetup.Instance == null)
yield return null;
OnHeadAttachChanged(Settings.HeadAttach);
}
void OnDestroy() void OnDestroy()
{ {
if(Instance == this) if(Instance == this)
@ -120,19 +112,23 @@ namespace ml_lme
Object.Destroy(m_leapControllerModel); Object.Destroy(m_leapControllerModel);
m_leapControllerModel = null; m_leapControllerModel = null;
VRModeSwitchEvents.OnInitializeXR.RemoveListener(this.OnAvatarSetup); if(m_offsetPoint != null)
VRModeSwitchEvents.OnDeinitializeXR.RemoveListener(this.OnAvatarSetup); Destroy(m_offsetPoint.gameObject);
m_offsetPoint = null;
Settings.OnDesktopOffsetChanged.RemoveListener(this.OnDesktopOffsetChanged); if(m_root != null)
Destroy(m_root.gameObject);
m_root = null;
Settings.OnEnabledChanged.RemoveListener(this.OnEnabledChanged);
Settings.OnModelVisibilityChanged.RemoveListener(this.OnModelVisibilityChanged); Settings.OnModelVisibilityChanged.RemoveListener(this.OnModelVisibilityChanged);
Settings.OnVisualHandsChanged.RemoveListener(this.OnVisualHandsChanged); Settings.OnVisualHandsChanged.RemoveListener(this.OnVisualHandsChanged);
Settings.OnTrackingModeChanged.RemoveListener(this.OnTrackingModeChanged); Settings.OnTrackingModeChanged.RemoveListener(this.OnTrackingModeChanged);
Settings.OnRootAngleChanged.RemoveListener(this.OnRootAngleChanged);
Settings.OnHeadAttachChanged.RemoveListener(this.OnHeadAttachChanged); Settings.OnHeadAttachChanged.RemoveListener(this.OnHeadAttachChanged);
Settings.OnHeadOffsetChanged.RemoveListener(this.OnHeadOffsetChanged); Settings.OnHeadOffsetChanged.RemoveListener(this.OnHeadOffsetChanged);
Settings.OnDesktopOffsetChanged.RemoveListener(this.OnDesktopOffsetChanged);
Settings.OnRootAngleChanged.RemoveListener(this.OnRootAngleChanged);
GameEvents.OnAvatarClear.RemoveListener(this.OnAvatarClear);
GameEvents.OnAvatarSetup.RemoveListener(this.OnAvatarSetup);
GameEvents.OnPlayspaceScale.RemoveListener(this.OnPlayspaceScale); GameEvents.OnPlayspaceScale.RemoveListener(this.OnPlayspaceScale);
} }
@ -140,6 +136,10 @@ namespace ml_lme
{ {
if(Settings.Enabled) if(Settings.Enabled)
{ {
Transform l_camera = PlayerSetup.Instance.GetActiveCamera().transform;
m_root.position = l_camera.position;
m_root.rotation = (Settings.HeadAttach ? l_camera.rotation : PlayerSetup.Instance.GetPlayerRotation());
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData(); LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
if(l_data.m_leftHand.m_present) if(l_data.m_leftHand.m_present)
@ -185,21 +185,22 @@ namespace ml_lme
} }
// Settings // Settings
void OnDesktopOffsetChanged(Vector3 p_offset) void OnEnabledChanged(bool p_state)
{ {
if(!Settings.HeadAttach) OnModelVisibilityChanged(Settings.ModelVisibility);
this.transform.localPosition = p_offset * (!m_inVR ? m_scaleRelation : 1f); OnVisualHandsChanged(Settings.VisualHands);
} }
void OnModelVisibilityChanged(bool p_state) void OnModelVisibilityChanged(bool p_state)
{ {
m_leapControllerModel.SetActive(p_state); if(m_leapControllerModel != null)
m_leapControllerModel.SetActive(Settings.Enabled && p_state);
} }
void OnVisualHandsChanged(bool p_state) void OnVisualHandsChanged(bool p_state)
{ {
m_leapHandLeft?.SetMeshActive(p_state); m_leapHandLeft?.SetMeshActive(Settings.Enabled && p_state);
m_leapHandRight?.SetMeshActive(p_state); m_leapHandRight?.SetMeshActive(Settings.Enabled && p_state);
} }
void OnTrackingModeChanged(Settings.LeapTrackingMode p_mode) void OnTrackingModeChanged(Settings.LeapTrackingMode p_mode)
@ -218,51 +219,42 @@ namespace ml_lme
} }
} }
void OnRootAngleChanged(Vector3 p_angle)
{
this.transform.localRotation = Quaternion.Euler(p_angle);
}
void OnHeadAttachChanged(bool p_state) void OnHeadAttachChanged(bool p_state)
{ {
if(!m_inVR) if(m_offsetPoint != null)
{ m_offsetPoint.localPosition = (p_state ? Settings.HeadOffset : Settings.DesktopOffset);
this.transform.parent = (p_state ? PlayerSetup.Instance.desktopCamera.transform : PlayerSetup.Instance.desktopCameraRig.transform);
this.transform.localPosition = (p_state ? Settings.HeadOffset : Settings.DesktopOffset) * m_scaleRelation;
}
else
{
this.transform.parent = (p_state ? PlayerSetup.Instance.vrCamera.transform : PlayerSetup.Instance.vrCameraRig.transform);
this.transform.localPosition = (p_state ? Settings.HeadOffset : Settings.DesktopOffset);
}
this.transform.localScale = Vector3.one * (!m_inVR ? m_scaleRelation : 1f);
this.transform.localRotation = Quaternion.Euler(Settings.RootAngle);
} }
void OnHeadOffsetChanged(Vector3 p_offset) void OnHeadOffsetChanged(Vector3 p_offset)
{ {
if(Settings.HeadAttach) if(Settings.HeadAttach && (m_offsetPoint != null))
this.transform.localPosition = p_offset * (!m_inVR ? m_scaleRelation : 1f); m_offsetPoint.localPosition = p_offset;
}
void OnDesktopOffsetChanged(Vector3 p_offset)
{
if(!Settings.HeadAttach && (m_offsetPoint != null))
m_offsetPoint.localPosition = p_offset;
}
void OnRootAngleChanged(Vector3 p_angle)
{
if(m_offsetPoint != null)
m_offsetPoint.localRotation = Quaternion.Euler(p_angle);
} }
// Game events // Game events
void OnAvatarClear()
{
m_scaleRelation = 1f;
OnHeadAttachChanged(Settings.HeadAttach);
}
void OnAvatarSetup()
{
m_inVR = Utils.IsInVR();
OnHeadAttachChanged(Settings.HeadAttach);
}
void OnPlayspaceScale(float p_relation) void OnPlayspaceScale(float p_relation)
{ {
m_scaleRelation = p_relation; try
OnHeadAttachChanged(Settings.HeadAttach); {
if(m_root != null)
m_root.localScale = Vector3.one * p_relation;
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
} }
// Utils // Utils

View file

@ -14,7 +14,6 @@ namespace ml_lme
Settings.Init(); Settings.Init();
AssetsHandler.Load(); AssetsHandler.Load();
GameEvents.Init(HarmonyInstance); GameEvents.Init(HarmonyInstance);
ModSupporter.Init();
MelonLoader.MelonCoroutines.Start(WaitForRootLogic()); MelonLoader.MelonCoroutines.Start(WaitForRootLogic());
} }
@ -31,7 +30,7 @@ namespace ml_lme
while(ABI_RC.Core.RootLogic.Instance == null) while(ABI_RC.Core.RootLogic.Instance == null)
yield return null; yield return null;
m_leapManager = new GameObject("LeapMotionManager").AddComponent<LeapManager>(); m_leapManager = new GameObject("[LeapMotionExtension]").AddComponent<LeapManager>();
} }
} }
} }

View file

@ -1,33 +0,0 @@
using System.Collections;
using System.Linq;
namespace ml_lme
{
static class ModSupporter
{
static bool ms_copycatMod = false;
public static void Init()
{
if(MelonLoader.MelonMod.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "PlayerMovementCopycat") != null)
MelonLoader.MelonCoroutines.Start(WaitForCopycatInstance());
}
// PlayerMovementCopycat support
static IEnumerator WaitForCopycatInstance()
{
while(ml_pmc.PoseCopycat.Instance == null)
yield return null;
ms_copycatMod = true;
}
static bool IsCopycating() => (ml_pmc.PoseCopycat.Instance.IsActive() && ml_pmc.PoseCopycat.Instance.IsFingerTrackingActive());
public static bool SkipFingersOverride()
{
bool l_result = false;
l_result |= (ms_copycatMod && IsCopycating());
return l_result;
}
}
}

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.5.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.6.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonOptionalDependencies("ml_pmc")] [assembly: MelonLoader.MelonOptionalDependencies("ml_pmc")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]

View file

@ -1,13 +1,13 @@
# Leap Motion Extension # Leap Motion Extension
This mod allows you to use your Leap Motion controller for hands and fingers tracking. This mod allows you to use your Leap Motion controller for hands and fingers tracking.
[![](.github/img_01.png)](https://youtu.be/nak1C8uibgc) ![](.github/img_01.png)
# Installation # Installation
* Install [latest Ultraleap Gemini tracking software](https://developer.leapmotion.com/tracking-software-download) * Install [latest Ultraleap Gemini tracking software](https://developer.leapmotion.com/tracking-software-download)
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
* Get [latest release DLL](../../../releases/latest): * Get [latest release DLL](../../../releases/latest):
* Put `ml_lme.dll` in `Mods` folder of game * Put `LeapMotionExtension.dll` in `Mods` folder of game
# Usage # Usage
## Settings ## Settings
@ -26,4 +26,4 @@ Available mod's settings in `Settings - Implementation - Leap Motion Tracking`:
* **Interact gesture threadhold:** activation limit for interaction based on hand gesture; 80 by default. * **Interact gesture threadhold:** activation limit for interaction based on hand gesture; 80 by default.
* **Grip gesture threadhold:** activation limit for grip based on hand gesture; 40 by default. * **Grip gesture threadhold:** activation limit for grip based on hand gesture; 40 by default.
* **Filter humanoid limits:** Limits fingers rotations to be valid for Unity's Mechanim; `true` by default * **Filter humanoid limits:** Limits fingers rotations to be valid for Unity's Mechanim; `true` by default
* Note: Enabling this option ensures that visual representation of your fingers will be same for you and remote players, but it cancels out additional finger segments rotations that can be better visually in most cases. * Note: Enabling this option ensures that visual representation of your fingers will be same for you and remote players, but it cancels out additional finger segments rotations that can be better visually in some cases.

View file

@ -6,15 +6,16 @@ namespace ml_lme
{ {
static class ResourcesHandler static class ResourcesHandler
{ {
readonly static string ms_namespace = typeof(ResourcesHandler).Namespace;
public static string GetEmbeddedResource(string p_name) public static string GetEmbeddedResource(string p_name)
{ {
string l_result = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
StreamReader l_streadReader = new StreamReader(l_libraryStream); StreamReader l_streadReader = new StreamReader(l_libraryStream);
l_result = l_streadReader.ReadToEnd(); l_result = l_streadReader.ReadToEnd();
} }

View file

@ -31,9 +31,9 @@ namespace ml_lme
if(CohtmlHud.Instance != null) if(CohtmlHud.Instance != null)
{ {
if(p_immediate) if(p_immediate)
CohtmlHud.Instance.ViewDropTextImmediate(p_title, p_message, p_small); CohtmlHud.Instance.ViewDropTextImmediate(p_title, p_message, p_small, "", false);
else else
CohtmlHud.Instance.ViewDropText(p_title, p_message, p_small); CohtmlHud.Instance.ViewDropText(p_title, p_message, p_small, "", false);
} }
} }

View file

@ -4,10 +4,11 @@
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<PackageId>LeapMotionExtension</PackageId> <PackageId>LeapMotionExtension</PackageId>
<Version>1.5.2</Version> <Version>1.6.0</Version>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>None</Company> <Company>SDraw</Company>
<Product>LeapMotionExtension</Product> <Product>LeapMotionExtension</Product>
<AssemblyName>LeapMotionExtension</AssemblyName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -68,11 +69,6 @@
<Private>false</Private> <Private>false</Private>
<SpecificVersion>false</SpecificVersion> <SpecificVersion>false</SpecificVersion>
</Reference> </Reference>
<Reference Include="ml_pmc">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_pmc.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="UnityEngine"> <Reference Include="UnityEngine">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
<Private>false</Private> <Private>false</Private>

View file

@ -1,7 +1,9 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Systems.VRModeSwitch;
using RootMotion.FinalIK; using RootMotion.FinalIK;
using System.Collections;
using UnityEngine; using UnityEngine;
namespace ml_pam namespace ml_pam
@ -22,61 +24,79 @@ namespace ml_pam
public Transform m_rightHandTarget; public Transform m_rightHandTarget;
} }
const float c_offsetLimit = 0.5f;
const KeyCode c_leftKey = KeyCode.Q; const KeyCode c_leftKey = KeyCode.Q;
const KeyCode c_rightKey = KeyCode.E; const KeyCode c_rightKey = KeyCode.E;
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f); static ArmMover ms_instance = null;
static readonly Quaternion ms_offsetLeft = Quaternion.Euler(270f, 90f, 0f); static readonly Quaternion ms_offsetLeft = Quaternion.Euler(270f, 90f, 0f);
static readonly Quaternion ms_offsetRight = Quaternion.Euler(270f, 270f, 0f); static readonly Quaternion ms_offsetRight = Quaternion.Euler(270f, 270f, 0f);
bool m_inVR = false;
VRIK m_vrIK = null; VRIK m_vrIK = null;
float m_armLength = 0f; Vector4 m_armsLength; // x,y - from upper arm to hand; z,w - from center to upper arm
float m_playspaceScale = 1f; Transform m_camera = null;
IKInfo m_ikInfo;
bool m_enabled = true; Transform m_root = null;
IKInfo m_vrIKInfo;
Transform m_rootLeft = null;
Transform m_rootRight = null;
Transform m_leftTarget = null; Transform m_leftTarget = null;
Transform m_rightTarget = null; Transform m_rightTarget = null;
Transform m_leftRotationTarget = null;
Transform m_rightRotationTarget = null;
ArmIK m_armIKLeft = null; ArmIK m_armIKLeft = null;
ArmIK m_armIKRight = null; ArmIK m_armIKRight = null;
CVRPickupObject m_pickup = null; CVRPickupObject m_pickup = null;
Matrix4x4 m_offset; Matrix4x4 m_offset;
HandState m_leftHandState = HandState.Empty; HandState m_leftHandState = HandState.Empty;
HandState m_rightHandState = HandState.Empty; HandState m_rightHandState = HandState.Empty;
Vector2 m_handsWeights;
AvatarBoolParameter m_leftHandParameter = null; AvatarBoolParameter m_leftHandParameter = null;
AvatarBoolParameter m_rightHandParameter = null; AvatarBoolParameter m_rightHandParameter = null;
Coroutine m_disableTask = null;
// Unity events // Unity events
void Awake()
{
if(ms_instance != null)
{
DestroyImmediate(this);
return;
}
ms_instance = this;
DontDestroyOnLoad(this);
}
void Start() void Start()
{ {
m_inVR = Utils.IsInVR(); m_camera = PlayerSetup.Instance.GetActiveCamera().transform;
m_rootLeft = new GameObject("[ArmPickupLeft]").transform; m_root = new GameObject("Root").transform;
m_rootLeft.parent = PlayerSetup.Instance.GetActiveCamera().transform; m_root.parent = this.transform;
m_rootLeft.localPosition = Vector3.zero; m_root.localPosition = Vector3.zero;
m_rootLeft.localRotation = Quaternion.identity; m_root.localRotation = Quaternion.identity;
m_leftTarget = new GameObject("Target").transform; m_leftTarget = new GameObject("TargetLeft").transform;
m_leftTarget.parent = m_rootLeft; m_leftTarget.parent = m_root;
m_leftTarget.localPosition = new Vector3(c_offsetLimit * -Settings.GrabOffset, 0f, 0f); m_leftTarget.localPosition = Vector3.zero;
m_leftTarget.localRotation = Quaternion.identity; m_leftTarget.localRotation = Quaternion.identity;
m_rootRight = new GameObject("[ArmPickupRight]").transform; m_leftRotationTarget = new GameObject("RotationTarget").transform;
m_rootRight.parent = PlayerSetup.Instance.GetActiveCamera().transform; m_leftRotationTarget.parent = m_leftTarget;
m_rootRight.localPosition = Vector3.zero; m_leftRotationTarget.localPosition = Vector3.zero;
m_rootRight.localRotation = Quaternion.identity; m_leftRotationTarget.localRotation = Quaternion.identity;
m_rightTarget = new GameObject("Target").transform; m_rightTarget = new GameObject("TargetRight").transform;
m_rightTarget.parent = m_rootRight; m_rightTarget.parent = m_root;
m_rightTarget.localPosition = new Vector3(c_offsetLimit * Settings.GrabOffset, 0f, 0f); m_rightTarget.localPosition = Vector3.zero;
m_rightTarget.localRotation = Quaternion.identity; m_rightTarget.localRotation = Quaternion.identity;
m_enabled = Settings.Enabled; m_rightRotationTarget = new GameObject("RotationTarget").transform;
m_rightRotationTarget.parent = m_rightTarget;
m_rightRotationTarget.localPosition = Vector3.zero;
m_rightRotationTarget.localRotation = Quaternion.identity;
Settings.OnEnabledChanged.AddListener(this.OnEnabledChanged); Settings.OnEnabledChanged.AddListener(this.OnEnabledChanged);
Settings.OnGrabOffsetChanged.AddListener(this.OnGrabOffsetChanged); Settings.OnGrabOffsetChanged.AddListener(this.OnGrabOffsetChanged);
@ -86,25 +106,44 @@ namespace ml_pam
GameEvents.OnAvatarClear.AddListener(this.OnAvatarClear); GameEvents.OnAvatarClear.AddListener(this.OnAvatarClear);
GameEvents.OnAvatarSetup.AddListener(this.OnAvatarSetup); GameEvents.OnAvatarSetup.AddListener(this.OnAvatarSetup);
GameEvents.OnAvatarReuse.AddListener(this.OnAvatarReuse); GameEvents.OnAvatarReuse.AddListener(this.OnAvatarReuse);
GameEvents.OnPlayspaceScale.AddListener(this.OnPlayspaceScale); GameEvents.OnIKScaling.AddListener(this.OnIKScaling);
GameEvents.OnPickupGrab.AddListener(this.OnPickupGrab); GameEvents.OnPickupGrab.AddListener(this.OnPickupGrab);
GameEvents.OnPickupDrop.AddListener(this.OnPickupDrop); GameEvents.OnPickupDrop.AddListener(this.OnPickupDrop);
VRModeSwitchEvents.OnCompletedVRModeSwitch.AddListener(this.OnVRModeSwitch);
} }
void OnDestroy() void OnDestroy()
{ {
if(ms_instance == this)
ms_instance = null;
if(m_disableTask != null)
StopCoroutine(m_disableTask);
m_disableTask = null;
RemoveArmIK(); RemoveArmIK();
if(m_rootLeft != null) if(m_leftRotationTarget != null)
Destroy(m_rootLeft); Destroy(m_leftRotationTarget.gameObject);
m_rootLeft = null; m_leftRotationTarget = null;
if(m_leftTarget != null)
Destroy(m_leftTarget.gameObject);
m_leftTarget = null; m_leftTarget = null;
if(m_rootRight != null) if(m_rightRotationTarget != null)
Destroy(m_rootRight); Destroy(m_rightRotationTarget.gameObject);
m_rootRight = null; m_rightRotationTarget = null;
if(m_rightTarget != null)
Destroy(m_rightTarget.gameObject);
m_rightTarget = null; m_rightTarget = null;
if(m_root != null)
Destroy(m_root.gameObject);
m_root = null;
m_pickup = null; m_pickup = null;
m_vrIK = null; m_vrIK = null;
@ -116,13 +155,21 @@ namespace ml_pam
GameEvents.OnAvatarClear.RemoveListener(this.OnAvatarClear); GameEvents.OnAvatarClear.RemoveListener(this.OnAvatarClear);
GameEvents.OnAvatarSetup.RemoveListener(this.OnAvatarSetup); GameEvents.OnAvatarSetup.RemoveListener(this.OnAvatarSetup);
GameEvents.OnAvatarReuse.RemoveListener(this.OnAvatarReuse); GameEvents.OnAvatarReuse.RemoveListener(this.OnAvatarReuse);
GameEvents.OnPlayspaceScale.RemoveListener(this.OnPlayspaceScale); GameEvents.OnIKScaling.AddListener(this.OnIKScaling);
GameEvents.OnPickupGrab.RemoveListener(this.OnPickupGrab); GameEvents.OnPickupGrab.RemoveListener(this.OnPickupGrab);
GameEvents.OnPickupDrop.RemoveListener(this.OnPickupDrop); GameEvents.OnPickupDrop.RemoveListener(this.OnPickupDrop);
VRModeSwitchEvents.OnCompletedVRModeSwitch.RemoveListener(this.OnVRModeSwitch);
} }
void Update() void Update()
{ {
if((m_root != null) && (m_camera != null))
{
m_root.position = m_camera.position;
m_root.rotation = m_camera.rotation;
}
if(!ReferenceEquals(m_pickup, null) && (m_pickup == null)) if(!ReferenceEquals(m_pickup, null) && (m_pickup == null))
OnPickupDrop(m_pickup); OnPickupDrop(m_pickup);
@ -130,205 +177,170 @@ namespace ml_pam
{ {
case HandState.Empty: case HandState.Empty:
{ {
if(Settings.HandsExtension && Input.GetKeyDown(c_leftKey)) if(Settings.Enabled && Settings.HandsExtension && Input.GetKey(c_leftKey) && !ViewManager.Instance.IsAnyMenuOpen)
{
m_leftHandState = HandState.Extended; m_leftHandState = HandState.Extended;
m_rootLeft.localPosition = new Vector3(0f, 0f, m_armLength * m_playspaceScale);
SetArmActive(Settings.LeadHand.Left, true);
}
} }
break; break;
case HandState.Extended: case HandState.Extended:
{ {
if(Input.GetKeyUp(c_leftKey)) if(!Input.GetKey(c_leftKey))
{
m_leftHandState = HandState.Empty; m_leftHandState = HandState.Empty;
SetArmActive(Settings.LeadHand.Left, false);
}
}
break;
case HandState.Pickup:
{
if(m_pickup != null)
{
Matrix4x4 l_result = m_pickup.transform.GetMatrix() * m_offset;
m_rootLeft.position = l_result * ms_pointVector;
}
} }
break; break;
} }
switch(m_rightHandState) switch(m_rightHandState)
{ {
case HandState.Empty: case HandState.Empty:
{ {
if(Settings.HandsExtension && Input.GetKeyDown(c_rightKey)) if(Settings.Enabled && Settings.HandsExtension && Input.GetKey(c_rightKey) && !ViewManager.Instance.IsAnyMenuOpen)
{
m_rightHandState = HandState.Extended; m_rightHandState = HandState.Extended;
m_rootRight.localPosition = new Vector3(0f, 0f, m_armLength * m_playspaceScale);
SetArmActive(Settings.LeadHand.Right, true);
}
} }
break; break;
case HandState.Extended: case HandState.Extended:
{ {
if(Input.GetKeyUp(c_rightKey)) if(!Input.GetKey(c_rightKey))
{
m_rightHandState = HandState.Empty; m_rightHandState = HandState.Empty;
SetArmActive(Settings.LeadHand.Right, false);
}
} }
break; break;
case HandState.Pickup: }
m_handsWeights.x = Mathf.Clamp01(m_handsWeights.x + ((m_leftHandState != HandState.Empty) ? 1f : -1f) * Time.unscaledDeltaTime * Settings.ExtensionSpeed);
m_handsWeights.y = Mathf.Clamp01(m_handsWeights.y + ((m_rightHandState != HandState.Empty) ? 1f : -1f) * Time.unscaledDeltaTime * Settings.ExtensionSpeed);
UpdateArmIK(m_armIKLeft, m_handsWeights.x);
UpdateArmIK(m_armIKRight, m_handsWeights.y);
m_leftHandParameter?.SetValue(m_leftHandState != HandState.Empty);
m_rightHandParameter?.SetValue(m_rightHandState != HandState.Empty);
if(m_leftHandState != HandState.Empty)
{
if(m_pickup != null)
{ {
if(m_pickup != null) Matrix4x4 l_result = m_pickup.transform.GetMatrix() * m_offset;
{ m_leftTarget.position = l_result.GetPosition();
Matrix4x4 l_result = m_pickup.transform.GetMatrix() * m_offset; m_leftTarget.rotation = l_result.rotation;
m_rootRight.position = l_result * ms_pointVector; m_leftTarget.localPosition = Vector3.ClampMagnitude(m_leftTarget.localPosition, m_armsLength.x);
}
} }
break; else
{
m_leftTarget.localPosition = new Vector3(0f, 0f, m_armsLength.x);
m_leftTarget.localRotation = Quaternion.identity;
}
}
if(m_rightHandState != HandState.Empty)
{
if(m_pickup != null)
{
Matrix4x4 l_result = m_pickup.transform.GetMatrix() * m_offset;
m_rightTarget.position = l_result.GetPosition();
m_rightTarget.rotation = l_result.rotation;
m_rightTarget.localPosition = Vector3.ClampMagnitude(m_rightTarget.localPosition, m_armsLength.y);
}
else
{
m_rightTarget.localPosition = new Vector3(0f, 0f, m_armsLength.y);
m_rightTarget.localRotation = Quaternion.identity;
}
}
}
void LateUpdate()
{
if((m_root != null) && (m_camera != null))
{
m_root.position = m_camera.position;
m_root.rotation = m_camera.rotation;
} }
} }
// VRIK updates // VRIK updates
void OnIKPreUpdate() void OnIKPreUpdate()
{ {
if(m_enabled) if(!Mathf.Approximately(m_handsWeights.x, 0f))
{ {
if(m_leftHandState != HandState.Empty) m_ikInfo.m_leftHandTarget = m_vrIK.solver.leftArm.target;
{ m_ikInfo.m_armsWeights.x = m_vrIK.solver.leftArm.positionWeight;
m_vrIKInfo.m_leftHandTarget = m_vrIK.solver.leftArm.target; m_ikInfo.m_armsWeights.y = m_vrIK.solver.leftArm.rotationWeight;
m_vrIKInfo.m_armsWeights.x = m_vrIK.solver.leftArm.positionWeight;
m_vrIKInfo.m_armsWeights.y = m_vrIK.solver.leftArm.rotationWeight;
m_vrIK.solver.leftArm.positionWeight = 1f; m_vrIK.solver.leftArm.positionWeight = m_handsWeights.x;
m_vrIK.solver.leftArm.rotationWeight = 1f; m_vrIK.solver.leftArm.rotationWeight = m_handsWeights.x;
m_vrIK.solver.leftArm.target = m_leftTarget; m_vrIK.solver.leftArm.target = m_leftRotationTarget;
} }
if(m_rightHandState != HandState.Empty) if(!Mathf.Approximately(m_handsWeights.y, 0f))
{ {
m_vrIKInfo.m_rightHandTarget = m_vrIK.solver.rightArm.target; m_ikInfo.m_rightHandTarget = m_vrIK.solver.rightArm.target;
m_vrIKInfo.m_armsWeights.z = m_vrIK.solver.rightArm.positionWeight; m_ikInfo.m_armsWeights.z = m_vrIK.solver.rightArm.positionWeight;
m_vrIKInfo.m_armsWeights.w = m_vrIK.solver.rightArm.rotationWeight; m_ikInfo.m_armsWeights.w = m_vrIK.solver.rightArm.rotationWeight;
m_vrIK.solver.rightArm.positionWeight = 1f; m_vrIK.solver.rightArm.positionWeight = m_handsWeights.y;
m_vrIK.solver.rightArm.rotationWeight = 1f; m_vrIK.solver.rightArm.rotationWeight = m_handsWeights.y;
m_vrIK.solver.rightArm.target = m_rightTarget; m_vrIK.solver.rightArm.target = m_rightRotationTarget;
}
} }
} }
void OnIKPostUpdate() void OnIKPostUpdate()
{ {
if(m_enabled) if(!Mathf.Approximately(m_handsWeights.x, 0f))
{ {
if(m_leftHandState != HandState.Empty) m_vrIK.solver.leftArm.target = m_ikInfo.m_leftHandTarget;
{ m_vrIK.solver.leftArm.positionWeight = m_ikInfo.m_armsWeights.x;
m_vrIK.solver.leftArm.target = m_vrIKInfo.m_leftHandTarget; m_vrIK.solver.leftArm.rotationWeight = m_ikInfo.m_armsWeights.y;
m_vrIK.solver.leftArm.positionWeight = m_vrIKInfo.m_armsWeights.x; }
m_vrIK.solver.leftArm.rotationWeight = m_vrIKInfo.m_armsWeights.y; if(!Mathf.Approximately(m_handsWeights.y, 0f))
} {
if(m_rightHandState != HandState.Empty) m_vrIK.solver.rightArm.target = m_ikInfo.m_rightHandTarget;
{ m_vrIK.solver.rightArm.positionWeight = m_ikInfo.m_armsWeights.z;
m_vrIK.solver.rightArm.target = m_vrIKInfo.m_rightHandTarget; m_vrIK.solver.rightArm.rotationWeight = m_ikInfo.m_armsWeights.w;
m_vrIK.solver.rightArm.positionWeight = m_vrIKInfo.m_armsWeights.z;
m_vrIK.solver.rightArm.rotationWeight = m_vrIKInfo.m_armsWeights.w;
}
} }
} }
// Settings // Settings
void OnEnabledChanged(bool p_state) void OnEnabledChanged(bool p_state)
{ {
m_enabled = p_state;
if(p_state) if(p_state)
{ {
if(m_leftHandState != HandState.Empty) if(this.enabled)
SetArmActive(Settings.LeadHand.Left, true); {
if(m_rightHandState != HandState.Empty) if(m_disableTask != null)
SetArmActive(Settings.LeadHand.Right, true); {
StopCoroutine(m_disableTask);
m_disableTask = null;
}
}
else
this.enabled = true;
OnHandsExtensionChanged(Settings.HandsExtension); OnLeadingHandChanged(Settings.LeadingHand);
} }
else else
SetArmActive(Settings.LeadHand.Both, false, true); {
m_leftHandState = HandState.Empty;
m_rightHandState = HandState.Empty;
m_disableTask = StartCoroutine(WaitToDisable());
}
} }
void OnGrabOffsetChanged(float p_value) void OnGrabOffsetChanged(float p_value)
{ {
if(m_leftTarget != null) if(m_leftRotationTarget != null)
m_leftTarget.localPosition = new Vector3(c_offsetLimit * m_playspaceScale * -p_value, 0f, 0f); m_leftRotationTarget.localPosition = new Vector3(-m_armsLength.z * p_value * 2f, 0f, 0f);
if(m_rightTarget != null) if(m_rightRotationTarget != null)
m_rightTarget.localPosition = new Vector3(c_offsetLimit * m_playspaceScale * p_value, 0f, 0f); m_rightRotationTarget.localPosition = new Vector3(m_armsLength.w * p_value * 2f, 0f, 0f);
} }
void OnLeadingHandChanged(Settings.LeadHand p_hand) void OnLeadingHandChanged(Settings.LeadHand p_hand)
{ {
if(m_pickup != null) SetLeadingHandState((m_pickup != null) ? HandState.Pickup : HandState.Empty);
{ SetUnleadingHandState(HandState.Empty);
if(m_leftHandState == HandState.Pickup)
{
m_leftHandState = HandState.Empty;
SetArmActive(Settings.LeadHand.Left, false);
}
if(m_rightHandState == HandState.Pickup)
{
m_rightHandState = HandState.Empty;
SetArmActive(Settings.LeadHand.Right, false);
}
switch(p_hand)
{
case Settings.LeadHand.Left:
m_leftHandState = HandState.Pickup;
break;
case Settings.LeadHand.Right:
m_rightHandState = HandState.Pickup;
break;
case Settings.LeadHand.Both:
{
m_leftHandState = HandState.Pickup;
m_rightHandState = HandState.Pickup;
}
break;
}
SetArmActive(p_hand, true);
}
} }
void OnHandsExtensionChanged(bool p_state) void OnHandsExtensionChanged(bool p_state)
{ {
if(m_enabled) if(m_leftHandState == HandState.Extended)
{ m_leftHandState = HandState.Empty;
if(p_state) if(m_rightHandState == HandState.Extended)
{ m_rightHandState = HandState.Empty;
if((m_leftHandState == HandState.Empty) && Input.GetKey(c_leftKey))
{
m_leftHandState = HandState.Extended;
SetArmActive(Settings.LeadHand.Left, true);
}
if((m_rightHandState == HandState.Empty) && Input.GetKey(c_rightKey))
{
m_rightHandState = HandState.Extended;
SetArmActive(Settings.LeadHand.Right, true);
}
}
else
{
if(m_leftHandState == HandState.Extended)
{
m_leftHandState = HandState.Empty;
SetArmActive(Settings.LeadHand.Left, false);
}
if(m_rightHandState == HandState.Extended)
{
m_rightHandState = HandState.Empty;
SetArmActive(Settings.LeadHand.Right, false);
}
}
}
} }
// Game events // Game events
@ -337,65 +349,81 @@ namespace ml_pam
m_vrIK = null; m_vrIK = null;
m_armIKLeft = null; m_armIKLeft = null;
m_armIKRight = null; m_armIKRight = null;
m_armLength = 0f; m_armsLength.Set(0f, 0f, 0f, 0f);
m_leftHandParameter = null; m_leftHandParameter = null;
m_rightHandParameter = null; m_rightHandParameter = null;
} }
void OnAvatarSetup() void OnAvatarSetup()
{ {
m_inVR = Utils.IsInVR(); m_camera = PlayerSetup.Instance.GetActiveCamera().transform;
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
ReparentRoots();
if(PlayerSetup.Instance._animator.isHuman) if(PlayerSetup.Instance._animator.isHuman)
{ {
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
Utils.SetAvatarTPose(); Utils.SetAvatarTPose();
Transform l_leftHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand); Animator l_animator = PlayerSetup.Instance._animator;
if(l_leftHand != null) Matrix4x4 l_avatarMatrixInv = l_animator.transform.GetMatrix().inverse; // Animator and avatar are on same game object
m_leftTarget.localRotation = ms_offsetLeft * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_leftHand.GetMatrix()).rotation;
Transform l_rightHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand);
if(l_rightHand != null)
m_rightTarget.localRotation = ms_offsetRight * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_rightHand.GetMatrix()).rotation;
if(m_vrIK != null) Transform l_leftHand = l_animator.GetBoneTransform(HumanBodyBones.LeftHand);
if(l_leftHand != null)
m_leftRotationTarget.localRotation = ms_offsetLeft * (l_avatarMatrixInv * l_leftHand.GetMatrix()).rotation;
Transform l_rightHand = l_animator.GetBoneTransform(HumanBodyBones.RightHand);
if(l_rightHand != null)
m_rightRotationTarget.localRotation = ms_offsetRight * (l_avatarMatrixInv * l_rightHand.GetMatrix()).rotation;
m_armsLength.x = GetChainLength(new Transform[]{
l_animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
l_animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
l_animator.GetBoneTransform(HumanBodyBones.LeftHand)
});
m_armsLength.y = GetChainLength(new Transform[]{
l_animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
l_animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
l_animator.GetBoneTransform(HumanBodyBones.RightHand)
});
m_armsLength.z = Mathf.Abs((l_avatarMatrixInv * l_animator.GetBoneTransform(HumanBodyBones.LeftUpperArm).GetMatrix()).GetPosition().x);
m_armsLength.w = Mathf.Abs((l_avatarMatrixInv * l_animator.GetBoneTransform(HumanBodyBones.RightUpperArm).GetMatrix()).GetPosition().x);
if(!Utils.IsInVR())
{ {
m_armLength = m_vrIK.solver.leftArm.mag * 1.25f; if(m_vrIK != null)
m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate); {
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate); m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
}
else
SetupArmIK();
} }
else if(!m_inVR)
SetupArmIK();
} }
m_leftHandParameter = new AvatarBoolParameter("LeftHandExtended", PlayerSetup.Instance.animatorManager); m_leftHandParameter = new AvatarBoolParameter("LeftHandExtended", PlayerSetup.Instance.animatorManager);
m_rightHandParameter = new AvatarBoolParameter("RightHandExtended", PlayerSetup.Instance.animatorManager); m_rightHandParameter = new AvatarBoolParameter("RightHandExtended", PlayerSetup.Instance.animatorManager);
OnEnabledChanged(m_enabled); OnEnabledChanged(Settings.Enabled);
OnGrabOffsetChanged(Settings.GrabOffset);
} }
void OnAvatarReuse() void OnAvatarReuse()
{ {
// Old VRIK is destroyed by game // Old VRIK is destroyed by game
m_inVR = Utils.IsInVR();
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>(); m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
ReparentRoots(); if(Utils.IsInVR())
if(m_inVR)
RemoveArmIK(); RemoveArmIK();
else
if(m_vrIK != null)
{ {
m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate); if(m_vrIK != null)
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate); {
m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
}
else
SetupArmIK();
} }
else if(!m_inVR)
SetupArmIK();
OnEnabledChanged(m_enabled); OnEnabledChanged(Settings.Enabled);
} }
void OnPickupGrab(CVRPickupObject p_pickup, Vector3 p_hit) void OnPickupGrab(CVRPickupObject p_pickup, Vector3 p_hit)
@ -416,97 +444,82 @@ namespace ml_pam
} }
} }
else else
m_offset = m_pickup.transform.GetMatrix().inverse * Matrix4x4.Translate(p_hit); m_offset = m_pickup.transform.GetMatrix().inverse * Matrix4x4.TRS(p_hit, m_camera.rotation, Vector3.one);
switch(Settings.LeadingHand) if(Settings.Enabled)
{ OnLeadingHandChanged(Settings.LeadingHand);
case Settings.LeadHand.Left:
m_leftHandState = HandState.Pickup;
break;
case Settings.LeadHand.Right:
m_rightHandState = HandState.Pickup;
break;
case Settings.LeadHand.Both:
{
m_leftHandState = HandState.Pickup;
m_rightHandState = HandState.Pickup;
}
break;
}
SetArmActive(Settings.LeadingHand, true);
} }
} }
void OnPickupDrop(CVRPickupObject p_pickup) void OnPickupDrop(CVRPickupObject p_pickup)
{ {
if(m_pickup == p_pickup) if(ReferenceEquals(m_pickup, p_pickup) || (m_pickup == p_pickup))
{ {
m_pickup = null; m_pickup = null;
switch(Settings.LeadingHand)
{ if(Settings.Enabled)
case Settings.LeadHand.Left: SetLeadingHandState(HandState.Empty);
m_leftHandState = HandState.Empty;
break;
case Settings.LeadHand.Right:
m_rightHandState = HandState.Empty;
break;
case Settings.LeadHand.Both:
{
m_leftHandState = HandState.Empty;
m_rightHandState = HandState.Empty;
}
break;
}
SetArmActive(Settings.LeadingHand, false);
} }
} }
void OnPlayspaceScale(float p_relation) void OnIKScaling(float p_relation)
{ {
m_playspaceScale = p_relation; if(m_root != null)
OnGrabOffsetChanged(Settings.GrabOffset); m_root.localScale = Vector3.one * p_relation;
}
void OnVRModeSwitch(bool p_state)
{
try
{
m_camera = PlayerSetup.Instance.GetActiveCamera().transform;
this.enabled = !Utils.IsInVR();
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
} }
// Arbitrary // Arbitrary
void SetupArmIK() void SetupArmIK()
{ {
Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest); Animator l_animator = PlayerSetup.Instance._animator;
Transform l_chest = l_animator.GetBoneTransform(HumanBodyBones.UpperChest);
if(l_chest == null) if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest); l_chest = l_animator.GetBoneTransform(HumanBodyBones.Chest);
if(l_chest == null) if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine); l_chest = l_animator.GetBoneTransform(HumanBodyBones.Spine);
m_armIKLeft = PlayerSetup.Instance._avatar.AddComponent<ArmIK>(); m_armIKLeft = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_armIKLeft.solver.isLeft = true; m_armIKLeft.solver.isLeft = true;
m_armIKLeft.solver.SetChain( m_armIKLeft.solver.SetChain(
l_chest, l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftShoulder), l_animator.GetBoneTransform(HumanBodyBones.LeftShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftUpperArm), l_animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftLowerArm), l_animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand), l_animator.GetBoneTransform(HumanBodyBones.LeftHand),
PlayerSetup.Instance._animator.transform l_animator.transform
); );
m_armIKLeft.solver.arm.target = m_leftTarget; m_armIKLeft.solver.arm.target = m_leftRotationTarget;
m_armIKLeft.solver.arm.positionWeight = 1f; m_armIKLeft.solver.arm.positionWeight = 1f;
m_armIKLeft.solver.arm.rotationWeight = 1f; m_armIKLeft.solver.arm.rotationWeight = 1f;
m_armIKLeft.solver.IKPositionWeight = 0f; m_armIKLeft.solver.IKPositionWeight = 0f;
m_armIKLeft.solver.IKRotationWeight = 0f; m_armIKLeft.solver.IKRotationWeight = 0f;
m_armIKLeft.enabled = false; m_armIKLeft.enabled = false;
m_armLength = m_armIKLeft.solver.arm.mag * 1.25f;
m_armIKRight = PlayerSetup.Instance._avatar.AddComponent<ArmIK>(); m_armIKRight = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_armIKRight.solver.isLeft = false; m_armIKRight.solver.isLeft = false;
m_armIKRight.solver.SetChain( m_armIKRight.solver.SetChain(
l_chest, l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder), l_animator.GetBoneTransform(HumanBodyBones.RightShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm), l_animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm), l_animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand), l_animator.GetBoneTransform(HumanBodyBones.RightHand),
PlayerSetup.Instance._animator.transform l_animator.transform
); );
m_armIKRight.solver.arm.target = m_rightTarget; m_armIKRight.solver.arm.target = m_rightRotationTarget;
m_armIKRight.solver.arm.positionWeight = 1f; m_armIKRight.solver.arm.positionWeight = 1f;
m_armIKRight.solver.arm.rotationWeight = 1f; m_armIKRight.solver.arm.rotationWeight = 1f;
m_armIKRight.solver.IKPositionWeight = 0f; m_armIKRight.solver.IKPositionWeight = 0f;
@ -525,39 +538,66 @@ namespace ml_pam
m_armIKRight = null; m_armIKRight = null;
} }
void SetArmActive(Settings.LeadHand p_hand, bool p_state, bool p_forced = false) void UpdateArmIK(ArmIK p_ik, float p_weight)
{ {
if(m_enabled || p_forced) if(p_ik != null)
{ {
if(((p_hand == Settings.LeadHand.Left) || (p_hand == Settings.LeadHand.Both)) && (m_armIKLeft != null)) bool l_state = !Mathf.Approximately(p_weight, 0f);
{ p_ik.solver.IKPositionWeight = p_weight;
m_armIKLeft.enabled = m_enabled; p_ik.solver.IKRotationWeight = p_weight;
m_armIKLeft.solver.IKPositionWeight = (p_state ? 1f : 0f); p_ik.enabled = l_state;
m_armIKLeft.solver.IKRotationWeight = (p_state ? 1f : 0f);
}
if(((p_hand == Settings.LeadHand.Right) || (p_hand == Settings.LeadHand.Both)) && (m_armIKRight != null))
{
m_armIKRight.enabled = m_enabled;
m_armIKRight.solver.IKPositionWeight = (p_state ? 1f : 0f);
m_armIKRight.solver.IKRotationWeight = (p_state ? 1f : 0f);
}
if((p_hand == Settings.LeadHand.Left) || (p_hand == Settings.LeadHand.Both))
m_leftHandParameter?.SetValue(p_state);
if((p_hand == Settings.LeadHand.Right) || (p_hand == Settings.LeadHand.Both))
m_rightHandParameter?.SetValue(p_state);
} }
} }
void ReparentRoots() void SetLeadingHandState(HandState p_state)
{ {
m_rootLeft.parent = PlayerSetup.Instance.GetActiveCamera().transform; switch(Settings.LeadingHand)
m_rootLeft.localPosition = Vector3.zero; {
m_rootLeft.localRotation = Quaternion.identity; case Settings.LeadHand.Left:
m_leftHandState = p_state;
break;
case Settings.LeadHand.Right:
m_rightHandState = p_state;
break;
case Settings.LeadHand.Both:
{
m_leftHandState = p_state;
m_rightHandState = p_state;
}
break;
}
}
void SetUnleadingHandState(HandState p_state)
{
switch(Settings.LeadingHand)
{
case Settings.LeadHand.Left:
m_rightHandState = p_state;
break;
case Settings.LeadHand.Right:
m_leftHandState = p_state;
break;
}
}
m_rootRight.parent = PlayerSetup.Instance.GetActiveCamera().transform; IEnumerator WaitToDisable()
m_rootRight.localPosition = Vector3.zero; {
m_rootRight.localRotation = Quaternion.identity; while(!Mathf.Approximately(m_handsWeights.x + m_handsWeights.y, 0f))
yield return null;
this.enabled = false;
m_disableTask = null;
}
static float GetChainLength(Transform[] p_chain)
{
float l_result = 0f;
for(int i = 0, j = p_chain.Length - 1; i < j; i++)
{
if((p_chain[i] != null) && (p_chain[i + 1] != null))
l_result += Vector3.Distance(p_chain[i].position, p_chain[i + 1].position);
}
return l_result;
} }
} }
} }

View file

@ -34,7 +34,7 @@ namespace ml_pam
public static readonly GameEvent OnAvatarSetup = new GameEvent(); public static readonly GameEvent OnAvatarSetup = new GameEvent();
public static readonly GameEvent OnAvatarClear = new GameEvent(); public static readonly GameEvent OnAvatarClear = new GameEvent();
public static readonly GameEvent OnAvatarReuse = new GameEvent(); public static readonly GameEvent OnAvatarReuse = new GameEvent();
public static readonly GameEvent<float> OnPlayspaceScale = new GameEvent<float>(); public static readonly GameEvent<float> OnIKScaling = new GameEvent<float>();
public static readonly GameEvent<CVRPickupObject, Vector3> OnPickupGrab = new GameEvent<CVRPickupObject, Vector3>(); public static readonly GameEvent<CVRPickupObject, Vector3> OnPickupGrab = new GameEvent<CVRPickupObject, Vector3>();
public static readonly GameEvent<CVRPickupObject> OnPickupDrop = new GameEvent<CVRPickupObject>(); public static readonly GameEvent<CVRPickupObject> OnPickupDrop = new GameEvent<CVRPickupObject>();
@ -61,9 +61,9 @@ namespace ml_pam
); );
p_instance.Patch( p_instance.Patch(
typeof(PlayerSetup).GetMethod("SetPlaySpaceScale", BindingFlags.NonPublic | BindingFlags.Instance), typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.NonPublic | BindingFlags.Instance),
null, null,
new HarmonyLib.HarmonyMethod(typeof(GameEvents).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) new HarmonyLib.HarmonyMethod(typeof(GameEvents).GetMethod(nameof(OnSetupIKScaling_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
); );
p_instance.Patch( p_instance.Patch(
@ -120,11 +120,11 @@ namespace ml_pam
} }
} }
static void OnPlayspaceScale_Postfix(float ____avatarScaleRelation) static void OnSetupIKScaling_Postfix(ref UnityEngine.Vector3 ___scaleDifference)
{ {
try try
{ {
OnPlayspaceScale.Invoke(____avatarScaleRelation); OnIKScaling.Invoke(1f + ___scaleDifference.y);
} }
catch(Exception e) catch(Exception e)
{ {

View file

@ -1,32 +1,34 @@
using ABI_RC.Core.Player; using UnityEngine;
namespace ml_pam namespace ml_pam
{ {
public class PickupArmMovement : MelonLoader.MelonMod public class PickupArmMovement : MelonLoader.MelonMod
{ {
ArmMover m_localMover = null; ArmMover m_mover = null;
public override void OnInitializeMelon() public override void OnInitializeMelon()
{ {
Settings.Init(); Settings.Init();
GameEvents.Init(HarmonyInstance); GameEvents.Init(HarmonyInstance);
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); MelonLoader.MelonCoroutines.Start(WaitForRootLogic());
} }
System.Collections.IEnumerator WaitForLocalPlayer() System.Collections.IEnumerator WaitForRootLogic()
{ {
while(PlayerSetup.Instance == null) while(ABI_RC.Core.RootLogic.Instance == null)
yield return null;
while(ABI_RC.Core.Player.PlayerSetup.Instance == null)
yield return null; yield return null;
m_localMover = PlayerSetup.Instance.gameObject.AddComponent<ArmMover>(); m_mover = new GameObject("[PickupArmMovement]").AddComponent<ArmMover>();
} }
public override void OnDeinitializeMelon() public override void OnDeinitializeMelon()
{ {
if(m_localMover != null) if(m_mover != null)
UnityEngine.Object.Destroy(m_localMover); Object.Destroy(m_mover.gameObject);
m_localMover = null; m_mover = null;
} }
} }
} }

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.1.4", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.2.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPriority(1)] [assembly: MelonLoader.MelonPriority(1)]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]

View file

@ -4,14 +4,15 @@ This mod adds arm tracking upon holding pickup in desktop mode.
# Installation # Installation
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
* Get [latest release DLL](../../../releases/latest): * Get [latest release DLL](../../../releases/latest):
* Put `ml_pam.dll` in `Mods` folder of game * Put `PickupArmMovement.dll` in `Mods` folder of game
# Usage # Usage
Available mod's settings in `Settings - Interactions - Pickup Arm Movement`: Available mod's settings in `Settings - Input & Key-Bindings - Pickup Arm Movement`:
* **Enable hand movement:** enables/disables arm tracking; default value - `true`. * **Enable hand movement:** enables/disables arm tracking; default value - `true`.
* **Grab offset:** offset from pickup grab point; default value - `25`. * **Grab offset:** offset from pickup grab point; default value - `25`.
* **Leading hand:** hand that will be extended when gragging pickup; available values: `Left`, `Right`, `Both`; default value - `Right`. * **Leading hand:** hand that will be extended when gragging pickup; available values: `Left`, `Right`, `Both`; default value - `Right`.
* **Hands extension (Q\E):** extend left and right hand if `Q` and `E` keys are pressed; default value - `true`. * **Hands extension (Q\E):** extend left and right hand if `Q` and `E` keys are pressed; default value - `true`.
* **Hand extension speed::** smoothing speed multiplier between extended and animated hands; default value - `25`.
Available animator boolean parameters: Available animator boolean parameters:
* **LeftHandExtended:`` indicates if left hand is extended. * **LeftHandExtended:`` indicates if left hand is extended.

View file

@ -6,15 +6,16 @@ namespace ml_pam
{ {
static class ResourcesHandler static class ResourcesHandler
{ {
readonly static string ms_namespace = typeof(ResourcesHandler).Namespace;
public static string GetEmbeddedResources(string p_name) public static string GetEmbeddedResources(string p_name)
{ {
string l_result = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
StreamReader l_streadReader = new StreamReader(l_libraryStream); StreamReader l_streadReader = new StreamReader(l_libraryStream);
l_result = l_streadReader.ReadToEnd(); l_result = l_streadReader.ReadToEnd();
} }

View file

@ -19,7 +19,8 @@ namespace ml_pam
Enabled = 0, Enabled = 0,
GrabOffset, GrabOffset,
LeadHand, LeadHand,
HandsExtension HandsExtension,
ExtensionSpeed
} }
public enum LeadHand public enum LeadHand
{ {
@ -29,9 +30,10 @@ namespace ml_pam
} }
public static bool Enabled { get; private set; } = true; public static bool Enabled { get; private set; } = true;
public static float GrabOffset { get; private set; } = 0.25f; public static float GrabOffset { get; private set; } = 0.5f;
public static LeadHand LeadingHand { get; private set; } = LeadHand.Right; public static LeadHand LeadingHand { get; private set; } = LeadHand.Right;
public static bool HandsExtension { get; private set; } = true; public static bool HandsExtension { get; private set; } = true;
public static float ExtensionSpeed { get; private set; } = 10f;
static MelonLoader.MelonPreferences_Category ms_category = null; static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null; static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
@ -40,6 +42,7 @@ namespace ml_pam
public static readonly SettingEvent<float> OnGrabOffsetChanged = new SettingEvent<float>(); public static readonly SettingEvent<float> OnGrabOffsetChanged = new SettingEvent<float>();
public static readonly SettingEvent<LeadHand> OnLeadingHandChanged = new SettingEvent<LeadHand>(); public static readonly SettingEvent<LeadHand> OnLeadingHandChanged = new SettingEvent<LeadHand>();
public static readonly SettingEvent<bool> OnHandsExtensionChanged = new SettingEvent<bool>(); public static readonly SettingEvent<bool> OnHandsExtensionChanged = new SettingEvent<bool>();
public static readonly SettingEvent<float> OnExtensionSpeedChanged = new SettingEvent<float>();
internal static void Init() internal static void Init()
{ {
@ -51,12 +54,14 @@ namespace ml_pam
ms_category.CreateEntry(ModSetting.GrabOffset.ToString(), (int)(GrabOffset * 100f)), ms_category.CreateEntry(ModSetting.GrabOffset.ToString(), (int)(GrabOffset * 100f)),
ms_category.CreateEntry(ModSetting.LeadHand.ToString(), (int)LeadHand.Right), ms_category.CreateEntry(ModSetting.LeadHand.ToString(), (int)LeadHand.Right),
ms_category.CreateEntry(ModSetting.HandsExtension.ToString(), HandsExtension), ms_category.CreateEntry(ModSetting.HandsExtension.ToString(), HandsExtension),
ms_category.CreateEntry(ModSetting.ExtensionSpeed.ToString(), (int)ExtensionSpeed),
}; };
Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue; Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue;
GrabOffset = (int)ms_entries[(int)ModSetting.GrabOffset].BoxedValue * 0.01f; GrabOffset = (int)ms_entries[(int)ModSetting.GrabOffset].BoxedValue * 0.01f;
LeadingHand = (LeadHand)(int)ms_entries[(int)ModSetting.LeadHand].BoxedValue; LeadingHand = (LeadHand)(int)ms_entries[(int)ModSetting.LeadHand].BoxedValue;
HandsExtension = (bool)ms_entries[(int)ModSetting.HandsExtension].BoxedValue; HandsExtension = (bool)ms_entries[(int)ModSetting.HandsExtension].BoxedValue;
ExtensionSpeed = Math.Clamp((int)ms_entries[(int)ModSetting.ExtensionSpeed].BoxedValue, 1f, 50f);
MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); MelonLoader.MelonCoroutines.Start(WaitMainMenuUi());
} }
@ -72,6 +77,7 @@ namespace ml_pam
ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () => ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
{ {
ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action<string, string>(OnToggleUpdate)); ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action<string, string>(OnToggleUpdate));
ViewManager.Instance.gameMenuView.View.BindCall("OnSliderUpdate_" + ms_category.Identifier, new Action<string, string>(OnSliderUpdate)); ViewManager.Instance.gameMenuView.View.BindCall("OnSliderUpdate_" + ms_category.Identifier, new Action<string, string>(OnSliderUpdate));
ViewManager.Instance.gameMenuView.View.BindCall("OnDropdownUpdate_" + ms_category.Identifier, new Action<string, string>(OnDropdownUpdate)); ViewManager.Instance.gameMenuView.View.BindCall("OnDropdownUpdate_" + ms_category.Identifier, new Action<string, string>(OnDropdownUpdate));
@ -131,6 +137,13 @@ namespace ml_pam
OnGrabOffsetChanged.Invoke(GrabOffset); OnGrabOffsetChanged.Invoke(GrabOffset);
} }
break; break;
case ModSetting.ExtensionSpeed:
{
ExtensionSpeed = l_value;
OnExtensionSpeedChanged.Invoke(ExtensionSpeed);
}
break;
} }
ms_entries[(int)l_setting].BoxedValue = l_value; ms_entries[(int)l_setting].BoxedValue = l_value;

View file

@ -4,10 +4,11 @@
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<PackageId>PickupArmMovement</PackageId> <PackageId>PickupArmMovement</PackageId>
<Version>1.1.4</Version> <Version>1.2.0</Version>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>None</Company> <Company>SDraw</Company>
<Product>PickupArmMovement</Product> <Product>PickupArmMovement</Product>
<AssemblyName>PickupArmMovement</AssemblyName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

View file

@ -16,7 +16,7 @@
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Grab offset: </div> <div class ="option-caption">Grab offset: </div>
<div class ="option-input"> <div class ="option-input">
<div id="GrabOffset" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="25"></div> <div id="GrabOffset" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="50"></div>
</div> </div>
</div> </div>
@ -33,8 +33,15 @@
<div id="HandsExtension" class ="inp_toggle no-scroll" data-current="true"></div> <div id="HandsExtension" class ="inp_toggle no-scroll" data-current="true"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper">
<div class ="option-caption">Hand extension speed: </div>
<div class ="option-input">
<div id="ExtensionSpeed" class ="inp_slider no-scroll" data-min="1" data-max="50" data-current="10"></div>
</div>
</div>
`; `;
document.getElementById('settings-interaction').appendChild(l_block); document.getElementById('settings-input').appendChild(l_block);
// Toggles // Toggles
for (let l_toggle of l_block.querySelectorAll('.inp_toggle')) for (let l_toggle of l_block.querySelectorAll('.inp_toggle'))

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_pin.PlayersInstanceNotifier), "PlayersInstanceNotifier", "1.0.9", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_pin.PlayersInstanceNotifier), "PlayersInstanceNotifier", "1.1.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -8,6 +8,7 @@ namespace ml_pin
static class ResourcesHandler static class ResourcesHandler
{ {
const string c_modName = "PlayersInstanceNotifier"; const string c_modName = "PlayersInstanceNotifier";
readonly static string ms_namespace = typeof(ResourcesHandler).Namespace;
static readonly List<string> ms_audioResources = new List<string>() static readonly List<string> ms_audioResources = new List<string>()
{ {
@ -45,11 +46,10 @@ namespace ml_pin
static void ExtractAudioFile(string p_name, string p_path) static void ExtractAudioFile(string p_name, string p_path)
{ {
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_resourceStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_resourceStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
Stream l_fileStream = File.Create(p_path); Stream l_fileStream = File.Create(p_path);
l_resourceStream.CopyTo(l_fileStream); l_resourceStream.CopyTo(l_fileStream);
l_fileStream.Flush(); l_fileStream.Flush();
@ -66,11 +66,10 @@ namespace ml_pin
{ {
string l_result = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
StreamReader l_streadReader = new StreamReader(l_libraryStream); StreamReader l_streadReader = new StreamReader(l_libraryStream);
l_result = l_streadReader.ReadToEnd(); l_result = l_streadReader.ReadToEnd();
} }

View file

@ -5,9 +5,9 @@
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<PackageId>PlayersInstanceNotifier</PackageId> <PackageId>PlayersInstanceNotifier</PackageId>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>None</Company> <Company>SDraw</Company>
<Product>PlayersInstanceNotifier</Product> <Product>PlayersInstanceNotifier</Product>
<Version>1.0.9</Version> <Version>1.1.0</Version>
<AssemblyName>PlayersInstanceNotifier</AssemblyName> <AssemblyName>PlayersInstanceNotifier</AssemblyName>
</PropertyGroup> </PropertyGroup>

View file

@ -1,10 +1,11 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using UnityEngine;
namespace ml_pmc namespace ml_pmc
{ {
public class PlayerMovementCopycat : MelonLoader.MelonMod public class PlayerMovementCopycat : MelonLoader.MelonMod
{ {
PoseCopycat m_localCopycat = null; PoseCopycat m_poseCopycat = null;
public override void OnInitializeMelon() public override void OnInitializeMelon()
{ {
@ -17,9 +18,9 @@ namespace ml_pmc
public override void OnDeinitializeMelon() public override void OnDeinitializeMelon()
{ {
if(m_localCopycat != null) if(m_poseCopycat != null)
UnityEngine.Object.Destroy(m_localCopycat); Object.Destroy(m_poseCopycat.gameObject);
m_localCopycat = null; m_poseCopycat = null;
} }
System.Collections.IEnumerator WaitForLocalPlayer() System.Collections.IEnumerator WaitForLocalPlayer()
@ -27,7 +28,7 @@ namespace ml_pmc
while(PlayerSetup.Instance == null) while(PlayerSetup.Instance == null)
yield return null; yield return null;
m_localCopycat = PlayerSetup.Instance.gameObject.AddComponent<PoseCopycat>(); m_poseCopycat = new GameObject("[PlayerMovementCopycat]").AddComponent<PoseCopycat>();
} }
} }
} }

View file

@ -10,7 +10,7 @@ using UnityEngine;
namespace ml_pmc namespace ml_pmc
{ {
[DisallowMultipleComponent] [DisallowMultipleComponent]
public class PoseCopycat : MonoBehaviour class PoseCopycat : MonoBehaviour
{ {
public class CopycatEvent<T1> public class CopycatEvent<T1>
{ {
@ -22,7 +22,7 @@ namespace ml_pmc
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f); static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
public static PoseCopycat Instance { get; private set; } = null; static PoseCopycat ms_instance = null;
internal static readonly CopycatEvent<bool> OnCopycatChanged = new CopycatEvent<bool>(); internal static readonly CopycatEvent<bool> OnCopycatChanged = new CopycatEvent<bool>();
Animator m_animator = null; Animator m_animator = null;
@ -43,10 +43,14 @@ namespace ml_pmc
void Awake() void Awake()
{ {
if((Instance != null) && (Instance != this)) if(ms_instance != null)
Object.Destroy(this); {
else DestroyImmediate(this);
Instance = this; return;
}
ms_instance = this;
DontDestroyOnLoad(this);
} }
void Start() void Start()
@ -60,8 +64,8 @@ namespace ml_pmc
} }
void OnDestroy() void OnDestroy()
{ {
if(Instance == this) if(ms_instance == this)
Instance = null; ms_instance = null;
m_poseHandler?.Dispose(); m_poseHandler?.Dispose();
m_poseHandler = null; m_poseHandler = null;
@ -201,7 +205,7 @@ namespace ml_pmc
PlayerSetup.Instance.transform.rotation = l_result.rotation; PlayerSetup.Instance.transform.rotation = l_result.rotation;
} }
if(Vector3.Distance(this.transform.position, m_puppetParser.transform.position) > m_distanceLimit) if(Vector3.Distance(PlayerSetup.Instance.GetPlayerPosition(), m_puppetParser.transform.position) > m_distanceLimit)
SetTarget(null); SetTarget(null);
} }
else else
@ -364,7 +368,7 @@ namespace ml_pmc
} }
// Arbitrary // Arbitrary
public void SetTarget(PuppetMaster p_target) void SetTarget(PuppetMaster p_target)
{ {
if(m_animator != null) if(m_animator != null)
{ {
@ -405,9 +409,6 @@ namespace ml_pmc
} }
} }
public bool IsActive() => m_active;
public bool IsFingerTrackingActive() => m_fingerTracking;
void OverrideIK() void OverrideIK()
{ {
if(!BodySystem.isCalibrating) if(!BodySystem.isCalibrating)

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_pmc.PlayerMovementCopycat), "PlayerMovementCopycat", "1.0.8", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_pmc.PlayerMovementCopycat), "PlayerMovementCopycat", "1.1.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPriority(3)] [assembly: MelonLoader.MelonPriority(3)]
[assembly: MelonLoader.MelonAdditionalDependencies("BTKUILib")] [assembly: MelonLoader.MelonAdditionalDependencies("BTKUILib")]

View file

@ -5,7 +5,7 @@ Allows to copy pose, gestures and movement of your friends.
* Install [BTKUILib](https://github.com/BTK-Development/BTKUILib) * Install [BTKUILib](https://github.com/BTK-Development/BTKUILib)
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
* Get [latest release DLL](../../../releases/latest): * Get [latest release DLL](../../../releases/latest):
* Put `ml_pmc.dll` in `Mods` folder of game * Put `PlayerMovementCopycat.dll` in `Mods` folder of game
# Usage # Usage
Available options in BTKUILib players list upon player selection: Available options in BTKUILib players list upon player selection:

View file

@ -5,9 +5,10 @@
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<PackageId>PlayerMovementCopycat</PackageId> <PackageId>PlayerMovementCopycat</PackageId>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>None</Company> <Company>SDraw</Company>
<Product>PlayerMovementCopycat</Product> <Product>PlayerMovementCopycat</Product>
<Version>1.0.8</Version> <Version>1.1.0</Version>
<AssemblyName>PlayerMovementCopycat</AssemblyName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

View file

@ -248,6 +248,5 @@ namespace ml_prm
} }
return !ms_result.m_result; return !ms_result.m_result;
} }
} }
} }

View file

@ -42,14 +42,9 @@ namespace ml_prm
System.Collections.IEnumerator WaitForWhitelist() System.Collections.IEnumerator WaitForWhitelist()
{ {
// Whitelist the toggle script // Whitelist the toggle script
FieldInfo l_field = typeof(SharedFilter).GetField("_localComponentWhitelist", BindingFlags.NonPublic | BindingFlags.Static); while(SharedFilter.LocalComponentWhitelist == null)
HashSet<Type> l_hashSet = l_field?.GetValue(null) as HashSet<Type>;
while(l_hashSet == null)
{
l_hashSet = l_field?.GetValue(null) as HashSet<Type>;
yield return null; yield return null;
} SharedFilter.LocalComponentWhitelist.Add(typeof(RagdollToggle));
l_hashSet.Add(typeof(RagdollToggle));
} }
} }
} }

View file

@ -43,6 +43,7 @@ namespace ml_prm
const string c_ragdollKeyTooltip = "Switch ragdoll mode with '{0}' key"; const string c_ragdollKeyTooltip = "Switch ragdoll mode with '{0}' key";
const string c_fallLimitTooltip = "Fall limit based on impact velocity<p>Current value corresponds to drop from {0} units with default gravity</p>"; const string c_fallLimitTooltip = "Fall limit based on impact velocity<p>Current value corresponds to drop from {0} units with default gravity</p>";
readonly static string ms_namespace = typeof(ModUi).Namespace;
internal static readonly UiEvent OnSwitchChanged = new UiEvent(); internal static readonly UiEvent OnSwitchChanged = new UiEvent();
static Page ms_page = null; static Page ms_page = null;
@ -345,7 +346,7 @@ namespace ml_prm
{ {
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name; string l_assemblyName = l_assembly.GetName().Name;
return l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); return l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
} }
static float GetDropHeight(float p_speed, float p_gravity = 9.8f) static float GetDropHeight(float p_speed, float p_gravity = 9.8f)

View file

@ -65,7 +65,7 @@ Now you can animate both parameters available:
![](.github/img_01.png) ![](.github/img_01.png)
Note: In order to work the game object needs to be active and the component enabled. Note: In order to work the game object needs to be active and the component enabled.
# Mods Integration # Mods integration
You can use this mod's functions within your mod. To do this you need: You can use this mod's functions within your mod. To do this you need:
* Add mod's dll as reference in your project * Add mod's dll as reference in your project
* Access ragdoll controller with `ml_prm.RagdollController.Instance` * Access ragdoll controller with `ml_prm.RagdollController.Instance`
@ -75,9 +75,3 @@ Available methods:
* ```void SwitchRagdoll()``` * ```void SwitchRagdoll()```
* ```void Ragdoll()``` * ```void Ragdoll()```
* ```void Unragdoll()``` * ```void Unragdoll()```
# Notes
* If ragdoll state is enabled during emote, remote players see whole emote playing while local player sees ragdolling. It's tied to how game handles remote players, currently can be prevented with (choose one):
* Renaming avatar emote animations to not have default name or containing `Emote` substring.
* Holding any movement key right after activating ragdoll state.
* Add transition from `Any state` to state with A-pose/T-pose animation and condition with `Ragdolled` parameter on main avatar's animator layer.

View file

@ -26,8 +26,10 @@ namespace ml_prm
bool m_applyHipsPosition = false; bool m_applyHipsPosition = false;
bool m_applyHipsRotation = false; bool m_applyHipsRotation = false;
bool m_avatarReady = false;
bool m_ragdolled = false; bool m_ragdolled = false;
bool m_forcedSwitch = false; bool m_forcedSwitch = false;
Coroutine m_initTask = null;
Transform m_puppet = null; Transform m_puppet = null;
Transform m_puppetRoot = null; Transform m_puppetRoot = null;
@ -36,10 +38,6 @@ namespace ml_prm
readonly List<System.Tuple<Transform, Transform>> m_boneLinks = null; readonly List<System.Tuple<Transform, Transform>> m_boneLinks = null;
readonly List<System.Tuple<CharacterJoint, Vector3>> m_jointAnchors = null; readonly List<System.Tuple<CharacterJoint, Vector3>> m_jointAnchors = null;
bool m_avatarReady = false;
Coroutine m_initCoroutine = null;
Vector3 m_ragdollLastPos = Vector3.zero;
RagdollToggle m_avatarRagdollToggle = null; // Custom component available for editor RagdollToggle m_avatarRagdollToggle = null; // Custom component available for editor
AvatarBoolParameter m_ragdolledParameter = null; AvatarBoolParameter m_ragdolledParameter = null;
PhysicMaterial m_physicsMaterial = null; PhysicMaterial m_physicsMaterial = null;
@ -49,6 +47,11 @@ namespace ml_prm
float m_groundedTime = 0f; float m_groundedTime = 0f;
float m_downTime = float.MinValue; float m_downTime = float.MinValue;
Vector3 m_lastRagdollPosition;
Vector3 m_lastSeatPositon;
Vector3 m_seatVelocity;
Plane m_playerPlane;
internal RagdollController() internal RagdollController()
{ {
m_ragdollBodyHandlers = new List<RagdollBodypartHandler>(); m_ragdollBodyHandlers = new List<RagdollBodypartHandler>();
@ -59,10 +62,14 @@ namespace ml_prm
// Unity events // Unity events
void Awake() void Awake()
{ {
if((Instance != null) && (Instance != this)) if(Instance != null)
Object.Destroy(this); {
else DestroyImmediate(this);
Instance = this; return;
}
Instance = this;
DontDestroyOnLoad(this);
} }
void Start() void Start()
@ -112,9 +119,9 @@ namespace ml_prm
if(Instance == this) if(Instance == this)
Instance = null; Instance = null;
if(m_initCoroutine != null) if(m_initTask != null)
StopCoroutine(m_initCoroutine); StopCoroutine(m_initTask);
m_initCoroutine = null; m_initTask = null;
if(m_puppet != null) if(m_puppet != null)
Object.Destroy(m_puppet); Object.Destroy(m_puppet);
@ -160,7 +167,7 @@ namespace ml_prm
{ {
if(m_avatarReady) if(m_avatarReady)
{ {
if(!m_ragdolled && Settings.FallDamage && !BetterBetterCharacterController.Instance.IsFlying()) if(!m_ragdolled && Settings.FallDamage && !BetterBetterCharacterController.Instance.IsFlying() && !BetterBetterCharacterController.Instance.IsSitting())
{ {
bool l_grounded = BetterBetterCharacterController.Instance.IsGrounded(); bool l_grounded = BetterBetterCharacterController.Instance.IsGrounded();
bool l_inWater = BetterBetterCharacterController.Instance.IsInWater(); bool l_inWater = BetterBetterCharacterController.Instance.IsInWater();
@ -170,6 +177,17 @@ namespace ml_prm
m_inAir = !(l_grounded || l_inWater); m_inAir = !(l_grounded || l_inWater);
} }
if(!m_ragdolled && BetterBetterCharacterController.Instance.IsSitting()) // Those seats without velocity, smh
{
CVRSeat l_seat = BetterBetterCharacterController.Instance.GetCurrentSeat();
if(l_seat != null)
{
Vector3 l_pos = l_seat.transform.position;
m_seatVelocity = (l_pos - m_lastSeatPositon) / Time.deltaTime;
m_lastSeatPositon = l_pos;
}
}
if(m_ragdolled) if(m_ragdolled)
{ {
BodySystem.TrackingPositionWeight = 0f; BodySystem.TrackingPositionWeight = 0f;
@ -178,7 +196,7 @@ namespace ml_prm
PlayerSetup.Instance.animatorManager.CancelEmote = true; PlayerSetup.Instance.animatorManager.CancelEmote = true;
} }
if(!m_ragdolled && !m_reachedGround && (BetterBetterCharacterController.Instance.IsOnGround() || BetterBetterCharacterController.Instance.IsInWater())) if(!m_ragdolled && !m_reachedGround && (BetterBetterCharacterController.Instance.IsOnGround() || BetterBetterCharacterController.Instance.IsInWater() || BetterBetterCharacterController.Instance.IsSitting()))
{ {
m_groundedTime += Time.unscaledDeltaTime; m_groundedTime += Time.unscaledDeltaTime;
if(m_groundedTime >= 0.25f) if(m_groundedTime >= 0.25f)
@ -210,9 +228,18 @@ namespace ml_prm
{ {
if(m_avatarReady && m_ragdolled) if(m_avatarReady && m_ragdolled)
{ {
Vector3 l_currentPos = m_puppetReferences.hips.position; Vector3 l_diff = m_puppetReferences.hips.position - m_lastRagdollPosition;
PlayerSetup.Instance.transform.position += (l_currentPos - m_ragdollLastPos); m_playerPlane.SetNormalAndPosition(PlayerSetup.Instance.transform.rotation * Vector3.up, PlayerSetup.Instance.transform.position);
m_ragdollLastPos = l_currentPos;
PlayerSetup.Instance.transform.position += l_diff;
m_lastRagdollPosition = m_puppetReferences.hips.position;
// Project on plane and fix our position if we under previous plane
if(m_playerPlane.GetDistanceToPoint(m_lastRagdollPosition) < 0f)
m_playerPlane.Flip();
float l_distance = m_playerPlane.GetDistanceToPoint(PlayerSetup.Instance.transform.position);
if(l_distance < 0f)
PlayerSetup.Instance.transform.position = m_playerPlane.ClosestPointOnPlane(PlayerSetup.Instance.transform.position);
} }
} }
@ -242,10 +269,10 @@ namespace ml_prm
// Game events // Game events
void OnAvatarClear() void OnAvatarClear()
{ {
if(m_initCoroutine != null) if(m_initTask != null)
{ {
StopCoroutine(m_initCoroutine); StopCoroutine(m_initTask);
m_initCoroutine = null; m_initTask = null;
} }
if(m_ragdolled) if(m_ragdolled)
@ -365,7 +392,7 @@ namespace ml_prm
m_avatarRagdollToggle = PlayerSetup.Instance._avatar.GetComponentInChildren<RagdollToggle>(true); m_avatarRagdollToggle = PlayerSetup.Instance._avatar.GetComponentInChildren<RagdollToggle>(true);
m_ragdolledParameter = new AvatarBoolParameter("Ragdolled", PlayerSetup.Instance.animatorManager); m_ragdolledParameter = new AvatarBoolParameter("Ragdolled", PlayerSetup.Instance.animatorManager);
m_initCoroutine = StartCoroutine(WaitForBodyHandlers()); m_initTask = StartCoroutine(WaitForBodyHandlers());
} }
} }
@ -381,7 +408,7 @@ namespace ml_prm
} }
m_avatarReady = true; m_avatarReady = true;
m_initCoroutine = null; m_initTask = null;
OnMovementDragChanged(Settings.MovementDrag); OnMovementDragChanged(Settings.MovementDrag);
OnAngularDragChanged(Settings.AngularDrag); OnAngularDragChanged(Settings.AngularDrag);
@ -414,6 +441,8 @@ namespace ml_prm
void OnSeatPreSit(CVRSeat p_seat) void OnSeatPreSit(CVRSeat p_seat)
{ {
m_lastSeatPositon = p_seat.transform.position;
if(!p_seat.occupied) if(!p_seat.occupied)
{ {
m_forcedSwitch = true; m_forcedSwitch = true;
@ -473,9 +502,8 @@ namespace ml_prm
if(m_avatarReady && m_ragdolled) if(m_avatarReady && m_ragdolled)
{ {
Vector3 l_pos = m_hips.position; m_puppetReferences.hips.position = m_hips.position;
m_puppetReferences.hips.position = l_pos; m_lastRagdollPosition = m_puppetReferences.hips.position;
m_ragdollLastPos = l_pos;
} }
} }
catch(System.Exception e) catch(System.Exception e)
@ -600,7 +628,7 @@ namespace ml_prm
void OnGestureGrabChanged(bool p_state) void OnGestureGrabChanged(bool p_state)
{ {
if(m_avatarReady && m_ragdolled & !p_state) if(m_avatarReady && m_ragdolled && !p_state)
{ {
foreach(var l_hanlder in m_ragdollBodyHandlers) foreach(var l_hanlder in m_ragdollBodyHandlers)
l_hanlder.Detach(); l_hanlder.Detach();
@ -622,13 +650,24 @@ namespace ml_prm
{ {
if(m_avatarReady && !m_ragdolled && CanRagdoll()) if(m_avatarReady && !m_ragdolled && CanRagdoll())
{ {
Vector3 l_velocity = Vector3.ClampMagnitude(BetterBetterCharacterController.Instance.velocity * (WorldManager.IsSafeWorld() ? Settings.VelocityMultiplier : 1f), WorldManager.GetMovementLimit()); Vector3 l_velocity = (BetterBetterCharacterController.Instance.IsSitting() ? m_seatVelocity : BetterBetterCharacterController.Instance.velocity);
l_velocity *= (WorldManager.IsSafeWorld() ? Settings.VelocityMultiplier : 1f);
l_velocity = Vector3.ClampMagnitude(l_velocity, WorldManager.GetMovementLimit());
if(Settings.ViewVelocity && WorldManager.IsSafeWorld()) if(Settings.ViewVelocity && WorldManager.IsSafeWorld())
{ {
float l_mag = l_velocity.magnitude; float l_mag = l_velocity.magnitude;
l_velocity = PlayerSetup.Instance.GetActiveCamera().transform.forward * l_mag; l_velocity = PlayerSetup.Instance.GetActiveCamera().transform.forward * l_mag;
} }
Vector3 l_playerPos = PlayerSetup.Instance.transform.position;
Quaternion l_playerRot = PlayerSetup.Instance.transform.rotation;
bool l_wasSitting = BetterBetterCharacterController.Instance.IsSitting();
if(BetterBetterCharacterController.Instance.IsSitting())
{
BetterBetterCharacterController.Instance.SetSitting(false);
l_wasSitting = true;
}
if(BetterBetterCharacterController.Instance.IsFlying()) if(BetterBetterCharacterController.Instance.IsFlying())
BetterBetterCharacterController.Instance.ChangeFlight(false, true); BetterBetterCharacterController.Instance.ChangeFlight(false, true);
BetterBetterCharacterController.Instance.SetImmobilized(true); BetterBetterCharacterController.Instance.SetImmobilized(true);
@ -662,7 +701,12 @@ namespace ml_prm
l_handler.SetAngularVelocity(Vector3.zero); l_handler.SetAngularVelocity(Vector3.zero);
} }
m_ragdollLastPos = m_puppetReferences.hips.position; if(l_wasSitting)
{
PlayerSetup.Instance.transform.position = l_playerPos;
PlayerSetup.Instance.transform.rotation = l_playerRot;
}
m_lastRagdollPosition = m_puppetReferences.hips.position;
m_downTime = 0f; m_downTime = 0f;
m_ragdolled = true; m_ragdolled = true;
@ -675,12 +719,6 @@ namespace ml_prm
{ {
BetterBetterCharacterController.Instance.TeleportPlayerTo(m_puppetReferences.hips.position, PlayerSetup.Instance.GetPlayerRotation().eulerAngles, false, false); BetterBetterCharacterController.Instance.TeleportPlayerTo(m_puppetReferences.hips.position, PlayerSetup.Instance.GetPlayerRotation().eulerAngles, false, false);
TryRestoreMovement(); TryRestoreMovement();
if(!WorldManager.IsSafeWorld())
{
Vector3 l_vec = BetterBetterCharacterController.Instance.GetVelocity();
l_vec.y = Mathf.Clamp(l_vec.y, float.MinValue, 0f);
BetterBetterCharacterController.Instance.SetVelocity(l_vec);
}
BodySystem.TrackingPositionWeight = 1f; BodySystem.TrackingPositionWeight = 1f;
IKSystem.Instance.applyOriginalHipPosition = m_applyHipsPosition; IKSystem.Instance.applyOriginalHipPosition = m_applyHipsPosition;
IKSystem.Instance.applyOriginalHipRotation = m_applyHipsRotation; IKSystem.Instance.applyOriginalHipRotation = m_applyHipsRotation;
@ -725,7 +763,6 @@ namespace ml_prm
bool l_result = m_reachedGround; bool l_result = m_reachedGround;
l_result &= !BodySystem.isCalibrating; l_result &= !BodySystem.isCalibrating;
l_result &= !BetterBetterCharacterController.Instance.IsSitting();
l_result &= ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown); l_result &= ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown);
return (l_result || m_forcedSwitch); return (l_result || m_forcedSwitch);
} }

View file

@ -22,6 +22,7 @@ namespace ml_prm
public void RemoveListener(Action<T1, T2, T3> p_listener) => m_action -= p_listener; public void RemoveListener(Action<T1, T2, T3> p_listener) => m_action -= p_listener;
public void Invoke(T1 p_objA, T2 p_objB, T3 p_objC) => m_action?.Invoke(p_objA, p_objB, p_objC); public void Invoke(T1 p_objA, T2 p_objB, T3 p_objC) => m_action?.Invoke(p_objA, p_objB, p_objC);
} }
public static readonly GestureEvent<PuppetMaster, GestureHand, bool> OnGestureState = new GestureEvent<PuppetMaster, GestureHand, bool>(); public static readonly GestureEvent<PuppetMaster, GestureHand, bool> OnGestureState = new GestureEvent<PuppetMaster, GestureHand, bool>();
class PlayerEntry class PlayerEntry
@ -32,6 +33,8 @@ namespace ml_prm
public bool m_stateRight = false; public bool m_stateRight = false;
} }
static RemoteGesturesManager ms_instance = null;
readonly List<PlayerEntry> m_entries = null; readonly List<PlayerEntry> m_entries = null;
internal RemoteGesturesManager() internal RemoteGesturesManager()
@ -39,10 +42,20 @@ namespace ml_prm
m_entries = new List<PlayerEntry>(); m_entries = new List<PlayerEntry>();
} }
void Awake()
{
if(ms_instance != null)
{
DestroyImmediate(this);
return;
}
ms_instance = this;
DontDestroyOnLoad(this);
}
void Start() void Start()
{ {
DontDestroyOnLoad(this);
CVRGameEventSystem.Player.OnJoinEntity.AddListener(OnRemotePlayerCreated); CVRGameEventSystem.Player.OnJoinEntity.AddListener(OnRemotePlayerCreated);
CVRGameEventSystem.Player.OnLeaveEntity.AddListener(OnRemotePlayerDestroyed); CVRGameEventSystem.Player.OnLeaveEntity.AddListener(OnRemotePlayerDestroyed);
Settings.OnGestureGrabChanged.AddListener(OnGestureGrabChanged); Settings.OnGestureGrabChanged.AddListener(OnGestureGrabChanged);
@ -50,6 +63,9 @@ namespace ml_prm
void OnDestroy() void OnDestroy()
{ {
if(ms_instance == this)
ms_instance = null;
m_entries.Clear(); m_entries.Clear();
CVRGameEventSystem.Player.OnJoinEntity.RemoveListener(OnRemotePlayerCreated); CVRGameEventSystem.Player.OnJoinEntity.RemoveListener(OnRemotePlayerCreated);

View file

@ -8,6 +8,7 @@ using System.Reflection;
using UnityEngine; using UnityEngine;
using System.Linq; using System.Linq;
using ABI_RC.Systems.IK.SubSystems; using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Core.InteractionSystem;
namespace ml_prm namespace ml_prm
{ {
@ -17,6 +18,7 @@ namespace ml_prm
static readonly FieldInfo ms_referencePoints = typeof(PhysicsInfluencer).GetField("_referencePoints", BindingFlags.NonPublic | BindingFlags.Instance); static readonly FieldInfo ms_referencePoints = typeof(PhysicsInfluencer).GetField("_referencePoints", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_influencerTouchingVolumes = typeof(PhysicsInfluencer).GetField("_touchingVolumes", BindingFlags.NonPublic | BindingFlags.Instance); static readonly FieldInfo ms_influencerTouchingVolumes = typeof(PhysicsInfluencer).GetField("_touchingVolumes", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_influencerSubmergedColliders = typeof(PhysicsInfluencer).GetField("_submergedColliders", BindingFlags.NonPublic | BindingFlags.Instance); static readonly FieldInfo ms_influencerSubmergedColliders = typeof(PhysicsInfluencer).GetField("_submergedColliders", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_lastCVRSeat = typeof(BetterBetterCharacterController).GetField("_lastCvrSeat", BindingFlags.NonPublic | BindingFlags.Instance);
public static void ClearFluidVolumes(this BetterBetterCharacterController p_instance) => (ms_touchingVolumes.GetValue(p_instance) as List<FluidVolume>)?.Clear(); public static void ClearFluidVolumes(this BetterBetterCharacterController p_instance) => (ms_touchingVolumes.GetValue(p_instance) as List<FluidVolume>)?.Clear();
@ -58,5 +60,9 @@ namespace ml_prm
l_result |= ((FingerSystem.GetCurlNormalized(p_source._playerAvatarMovementDataCurrent.RightMiddle1Stretched) >= 0.5f) && (FingerSystem.GetCurlNormalized(p_source._playerAvatarMovementDataCurrent.RightMiddle2Stretched) >= 0.5f) && (FingerSystem.GetCurlNormalized(p_source._playerAvatarMovementDataCurrent.RightMiddle3Stretched) >= 0.5f)); l_result |= ((FingerSystem.GetCurlNormalized(p_source._playerAvatarMovementDataCurrent.RightMiddle1Stretched) >= 0.5f) && (FingerSystem.GetCurlNormalized(p_source._playerAvatarMovementDataCurrent.RightMiddle2Stretched) >= 0.5f) && (FingerSystem.GetCurlNormalized(p_source._playerAvatarMovementDataCurrent.RightMiddle3Stretched) >= 0.5f));
return l_result; return l_result;
} }
public static CVRSeat GetCurrentSeat(this BetterBetterCharacterController p_instance) => (ms_lastCVRSeat?.GetValue(p_instance) as CVRSeat);
public static bool IsInRange(float p_value, float p_min, float p_max) => ((p_min <= p_value) && (p_value <= p_max));
} }
} }

View file

@ -1,5 +1,6 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI_RC.Systems.GameEventSystem; using ABI_RC.Systems.GameEventSystem;
using System;
using UnityEngine; using UnityEngine;
namespace ml_prm namespace ml_prm
@ -22,18 +23,25 @@ namespace ml_prm
static void OnWorldLoad(string p_id) static void OnWorldLoad(string p_id)
{ {
ms_safeWorld = ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying); try
ms_movementLimit = 1f;
GameObject l_restrictObj = GameObject.Find("[RagdollRestriction]");
ms_restrictedWorld = ((l_restrictObj == null) ? false : (l_restrictObj.scene.name != "DontDestroyOnLoad"));
if(CVRWorld.Instance != null)
{ {
ms_movementLimit = CVRWorld.Instance.baseMovementSpeed; ms_safeWorld = ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
ms_movementLimit *= CVRWorld.Instance.sprintMultiplier; ms_movementLimit = 1f;
ms_movementLimit *= CVRWorld.Instance.inAirMovementMultiplier;
ms_movementLimit *= CVRWorld.Instance.flyMultiplier; GameObject l_restrictObj = GameObject.Find("[RagdollRestriction]");
ms_restrictedWorld = ((l_restrictObj == null) ? false : (l_restrictObj.scene.name != "DontDestroyOnLoad"));
if(CVRWorld.Instance != null)
{
ms_movementLimit = CVRWorld.Instance.baseMovementSpeed;
ms_movementLimit *= CVRWorld.Instance.sprintMultiplier;
ms_movementLimit *= CVRWorld.Instance.inAirMovementMultiplier;
ms_movementLimit *= CVRWorld.Instance.flyMultiplier;
}
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
} }
} }

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_vei.ViveExtendedInput), "ViveExtendedInput", "1.0.5", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_vei.ViveExtendedInput), "ViveExtendedInput", "1.1.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -1,5 +1,5 @@
# Vive Extended Input # Vive Extended Input
This mod changes input behaviour for Vive controllers. This mod adds few changes of input behaviour for Vive controllers.
# Installation # Installation
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)

View file

@ -6,15 +6,16 @@ namespace ml_vei
{ {
static class ResourcesHandler static class ResourcesHandler
{ {
readonly static string ms_namespace = typeof(ResourcesHandler).Namespace;
public static string GetEmbeddedResource(string p_name) public static string GetEmbeddedResource(string p_name)
{ {
string l_result = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_libraryStream = l_assembly.GetManifestResourceStream(ms_namespace + ".resources." + p_name);
StreamReader l_streadReader = new StreamReader(l_libraryStream); StreamReader l_streadReader = new StreamReader(l_libraryStream);
l_result = l_streadReader.ReadToEnd(); l_result = l_streadReader.ReadToEnd();
} }

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<PackageId>ViveExtendedInput</PackageId> <PackageId>ViveExtendedInput</PackageId>
<Version>1.0.5</Version> <Version>1.1.0</Version>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>SDraw</Company> <Company>SDraw</Company>
<Product>ViveExtendedInput</Product> <Product>ViveExtendedInput</Product>