Merge branch 'experimental'

This commit is contained in:
SDraw 2024-03-08 01:40:58 +03:00
commit 26257ab886
No known key found for this signature in database
GPG key ID: BB95B4DAB2BB8BB5
85 changed files with 3029 additions and 2372 deletions

View file

@ -8,7 +8,6 @@ namespace ml_amt
{
public enum ParameterType
{
GroundedRaw,
Moving
}
@ -42,10 +41,6 @@ namespace ml_amt
{
switch(m_type)
{
case ParameterType.GroundedRaw:
SetBoolean(p_tweaker.GetGroundedRaw());
break;
case ParameterType.Moving:
SetBoolean(p_tweaker.GetMoving());
break;

View file

@ -1,4 +1,5 @@
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems;
using System;
using System.Collections;
@ -30,9 +31,9 @@ namespace ml_amt
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(BodySystem).GetMethod(nameof(BodySystem.Calibrate)),
typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public),
null,
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnCalibrate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod("SetPlaySpaceScale", BindingFlags.NonPublic | BindingFlags.Instance),
@ -89,17 +90,17 @@ namespace ml_amt
}
}
static void OnCalibrate_Postfix() => ms_instance?.OnCalibrate();
void OnCalibrate()
static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize();
void OnAvatarReinitialize()
{
try
{
if(m_localTweaker != null)
m_localTweaker.OnCalibrate();
m_localTweaker.OnAvatarReinitialize();
}
catch(Exception l_exception)
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(l_exception);
MelonLoader.MelonLogger.Error(e);
}
}

View file

@ -1,7 +1,7 @@
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.MovementSystem;
using ABI_RC.Systems.Movement;
using RootMotion.FinalIK;
using System.Collections.Generic;
using UnityEngine;
@ -28,11 +28,9 @@ namespace ml_amt
int m_locomotionLayer = 0;
float m_avatarScale = 1f;
Vector3 m_locomotionOffset = Vector3.zero; // Original locomotion offset
bool m_inVR = false;
bool m_avatarReady = false;
bool m_grounded = false;
bool m_groundedRaw = false;
bool m_moving = false;
bool m_locomotionOverride = false;
@ -56,8 +54,6 @@ namespace ml_amt
// Unity events
void Start()
{
m_inVR = Utils.IsInVR();
SetCrouchLimit(Settings.CrouchLimit);
SetProneLimit(Settings.ProneLimit);
SetIKOverrideFly(Settings.IKOverrideFly);
@ -90,9 +86,8 @@ namespace ml_amt
{
if(m_avatarReady)
{
m_grounded = MovementSystem.Instance.IsGrounded();
m_groundedRaw = MovementSystem.Instance.IsGroundedRaw();
m_moving = !Mathf.Approximately(MovementSystem.Instance.movementVector.magnitude, 0f);
m_grounded = BetterBetterCharacterController.Instance.IsGrounded();
m_moving = BetterBetterCharacterController.Instance.IsMoving();
UpdateIKLimits();
@ -103,11 +98,8 @@ namespace ml_amt
m_emoteActive = (l_animState.tagHash == ms_emoteHash);
}
if(m_parameters.Count > 0)
{
foreach(AvatarParameter l_param in m_parameters)
l_param.Update(this);
}
foreach(AvatarParameter l_param in m_parameters)
l_param.Update(this);
}
}
@ -117,7 +109,6 @@ namespace ml_amt
m_vrIk = null;
m_locomotionLayer = -1;
m_grounded = false;
m_groundedRaw = false;
m_avatarReady = false;
m_avatarScale = 1f;
m_locomotionOffset = Vector3.zero;
@ -128,19 +119,17 @@ namespace ml_amt
m_ikLimits = null;
m_parameters.Clear();
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(Settings.CrouchLimit);
PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(Settings.ProneLimit);
BetterBetterCharacterController.Instance.avatarCrouchLimit = Mathf.Clamp01(Settings.CrouchLimit);
BetterBetterCharacterController.Instance.avatarProneLimit = Mathf.Clamp01(Settings.ProneLimit);
}
internal void OnSetupAvatar()
{
m_inVR = Utils.IsInVR();
m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
m_locomotionLayer = PlayerSetup.Instance._animator.GetLayerIndex("Locomotion/Emotes");
m_avatarScale = Mathf.Abs(PlayerSetup.Instance._avatar.transform.localScale.y);
// Parse animator parameters
m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.GroundedRaw, PlayerSetup.Instance.animatorManager));
m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.Moving, PlayerSetup.Instance.animatorManager));
m_parameters.RemoveAll(p => !p.IsValid());
@ -181,34 +170,25 @@ namespace ml_amt
m_avatarReady = true;
}
internal void OnCalibrate()
{
if(m_avatarReady && (m_vrIk != null) && (m_vrIk.solver.spine.pelvisTarget != null) && (m_vrIk.solver.leftLeg.target == null) && (m_vrIk.solver.rightLeg.target == null))
{
// Do not consider 4PT as FBT (!!!)
m_vrIk.solver.spine.bodyPosStiffness = 0.55f;
m_vrIk.solver.spine.bodyRotStiffness = 0.1f;
m_vrIk.solver.spine.neckStiffness = 0.5f;
m_vrIk.solver.spine.chestClampWeight = 0.55f;
m_vrIk.solver.spine.moveBodyBackWhenCrouching = 0.5f;
m_vrIk.solver.spine.maxRootAngle = 25f;
m_vrIk.fixTransforms = false;
BodySystem.isCalibratedAsFullBody = false;
BodySystem.TrackingLeftLegEnabled = false;
BodySystem.TrackingRightLegEnabled = false;
BodySystem.TrackingLocomotionEnabled = true;
IKSystem.Instance.applyOriginalHipRotation = true;
}
}
internal void OnPlayspaceScale()
{
if((m_vrIk != null) && Settings.MassCenter)
m_vrIk.solver.locomotion.offset = m_massCenter * GetRelativeScale();
}
internal void OnAvatarReinitialize()
{
// Old VRIK is destroyed by game
m_vrIk = PlayerSetup.Instance._animator.GetComponent<VRIK>();
if(m_vrIk != null)
{
m_vrIk.solver.locomotion.offset = (Settings.MassCenter ? m_massCenter : m_locomotionOffset);
m_vrIk.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
m_vrIk.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
}
}
// IK events
void OnIKPreUpdate()
{
@ -225,21 +205,20 @@ namespace ml_amt
if(!BodySystem.isCalibratedAsFullBody)
{
if(PlayerSetup.Instance.avatarUpright <= PlayerSetup.Instance.avatarCrouchLimit)
if(BetterBetterCharacterController.Instance.AvatarUpright <= BetterBetterCharacterController.Instance.avatarCrouchLimit)
{
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
l_locomotionOverride = true;
}
if(m_ikOverrideFly && MovementSystem.Instance.flying)
if(m_ikOverrideFly && BetterBetterCharacterController.Instance.IsFlying())
{
m_vrIk.solver.locomotion.weight = 0f;
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
l_locomotionOverride = true;
}
if(m_ikOverrideJump && !m_grounded && !MovementSystem.Instance.flying)
if(m_ikOverrideJump && !m_grounded && !BetterBetterCharacterController.Instance.IsFlying())
{
m_vrIk.solver.locomotion.weight = 0f;
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
@ -266,12 +245,12 @@ namespace ml_amt
internal void SetCrouchLimit(float p_value)
{
if(m_ikLimits == null)
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(p_value);
BetterBetterCharacterController.Instance.avatarCrouchLimit = Mathf.Clamp01(p_value);
}
internal void SetProneLimit(float p_value)
{
if(m_ikLimits == null)
PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(p_value);
BetterBetterCharacterController.Instance.avatarProneLimit = Mathf.Clamp01(p_value);
}
internal void SetIKOverrideFly(bool p_state)
{
@ -302,14 +281,12 @@ namespace ml_amt
if(m_ikLimits != null)
{
Vector3 l_values = m_ikLimits.localPosition;
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(l_values.x);
PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(l_values.y);
BetterBetterCharacterController.Instance.avatarCrouchLimit = Mathf.Clamp01(l_values.x);
BetterBetterCharacterController.Instance.avatarProneLimit = Mathf.Clamp01(l_values.y);
}
}
// Parameters access
public float GetUpright() => PlayerSetup.Instance.avatarUpright;
public bool GetGroundedRaw() => m_groundedRaw;
public bool GetMoving() => m_moving;
}
}

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.3.6", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.3.7-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -1,6 +1,6 @@
using ABI.CCK.Components;
using ABI_RC.Core.Savior;
using ABI_RC.Core.UI;
using ABI_RC.Systems.MovementSystem;
using RootMotion.FinalIK;
using System.Reflection;
using UnityEngine;
@ -9,14 +9,10 @@ namespace ml_amt
{
static class Utils
{
static readonly FieldInfo ms_grounded = typeof(MovementSystem).GetField("_isGrounded", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_groundedRaw = typeof(MovementSystem).GetField("_isGroundedRaw", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_hasToes = typeof(IKSolverVR).GetField("hasToes", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsInVR() => ((ABI_RC.Core.Savior.CheckVR.Instance != null) && ABI_RC.Core.Savior.CheckVR.Instance.hasVrDeviceLoaded);
public static bool IsGroundedRaw(this MovementSystem p_instance) => (bool)ms_groundedRaw.GetValue(MovementSystem.Instance);
public static bool IsInVR() => ((MetaPort.Instance != null) && MetaPort.Instance.isUsingVr);
public static bool HasToes(this IKSolverVR p_instance) => (bool)ms_hasToes.GetValue(p_instance);

View file

@ -6,7 +6,7 @@
<Company>None</Company>
<Product>AvatarMotionTweaker</Product>
<PackageId>AvatarMotionTweaker</PackageId>
<Version>1.3.6</Version>
<Version>1.3.7</Version>
<Platforms>x64</Platforms>
<AssemblyName>ml_amt</AssemblyName>
</PropertyGroup>
@ -63,6 +63,11 @@
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="ECM2">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ECM2.dll</HintPath>
<SpecificVersion>false</SpecificVersion>
<Private>false</Private>
</Reference>
<Reference Include="MelonLoader">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
<Private>false</Private>

View file

@ -18,7 +18,7 @@ namespace ml_asl
static void OnPlayerAvatarMovementDataUpdate_Postfix(ref PlayerSetup __instance, PlayerAvatarMovementData ____playerAvatarMovementData)
{
if(Settings.Enabled && (__instance.eyeMovement != null))
if(Settings.Enabled && (__instance.EyeMovementController != null))
____playerAvatarMovementData.EyeTrackingOverride = true;
}
}

View file

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

View file

@ -7,6 +7,7 @@
<Authors>SDraw</Authors>
<Company>None</Company>
<Product>AvatarSyncedLook</Product>
<Version>1.0.1</Version>
</PropertyGroup>
<ItemGroup>

29
ml_dht/DataParser.cs Normal file
View file

@ -0,0 +1,29 @@
namespace ml_dht
{
class DataParser
{
MemoryMapReader m_mapReader = null;
byte[] m_buffer = null;
TrackingData m_trackingData;
public DataParser()
{
m_buffer = new byte[1024];
m_mapReader = new MemoryMapReader();
m_mapReader.Open("head/data");
}
~DataParser()
{
m_mapReader.Close();
m_mapReader = null;
}
public void Update()
{
if(m_mapReader.Read(ref m_buffer))
m_trackingData = TrackingData.ToObject(m_buffer);
}
public ref TrackingData GetLatestTrackingData() => ref m_trackingData;
}
}

View file

@ -1,6 +1,10 @@
using ABI.CCK.Components;
using ABI_RC.Core.Player;
using ABI_RC.Core.Player.EyeMovement;
using ABI_RC.Systems.FaceTracking;
using ABI_RC.Systems.VRModeSwitch;
using RootMotion.FinalIK;
using System;
using System.Reflection;
using UnityEngine;
using ViveSR.anipal.Lip;
@ -25,10 +29,22 @@ namespace ml_dht
Quaternion m_headRotation;
Vector2 m_gazeDirection;
float m_blinkProgress = 0f;
LipData_v2 m_lipData;
bool m_lipDataSent = false;
Quaternion m_bindRotation;
Quaternion m_lastHeadRotation;
internal HeadTracked()
{
m_lipData = new LipData_v2();
m_lipData.frame = 0;
m_lipData.time = 0;
m_lipData.image = IntPtr.Zero;
m_lipData.prediction_data = new PredictionData_v2();
m_lipData.prediction_data.blend_shape_weight = new float[(int)LipShape_v2.Max];
}
// Unity events
void Start()
{
@ -48,6 +64,12 @@ namespace ml_dht
Settings.SmoothingChange -= this.SetSmoothing;
}
void Update()
{
if(m_lipDataSent)
m_lipDataSent = false;
}
// Tracking updates
public void UpdateTrackingData(ref TrackingData p_data)
{
@ -55,6 +77,12 @@ namespace ml_dht
m_headRotation.Set(p_data.m_headRotationX, p_data.m_headRotationY * (Settings.Mirrored ? -1f : 1f), p_data.m_headRotationZ * (Settings.Mirrored ? -1f : 1f), p_data.m_headRotationW);
m_gazeDirection.Set(Settings.Mirrored ? (1f - p_data.m_gazeX) : p_data.m_gazeX, p_data.m_gazeY);
m_blinkProgress = p_data.m_blink;
float l_weight = Mathf.Clamp01(Mathf.InverseLerp(0.25f, 1f, Mathf.Abs(p_data.m_mouthShape)));
m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Jaw_Open] = p_data.m_mouthOpen;
m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Pout] = ((p_data.m_mouthShape > 0f) ? l_weight : 0f);
m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Smile_Left] = ((p_data.m_mouthShape < 0f) ? l_weight : 0f);
m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Smile_Right] = ((p_data.m_mouthShape < 0f) ? l_weight : 0f);
}
void OnLookIKPostUpdate()
@ -80,7 +108,7 @@ namespace ml_dht
m_bindRotation = (m_avatarDescriptor.transform.GetMatrix().inverse * m_headBone.GetMatrix()).rotation;
if(m_lookIK != null)
m_lookIK.solver.OnPostUpdate += this.OnLookIKPostUpdate;
m_lookIK.onPostSolverUpdate.AddListener(this.OnLookIKPostUpdate);
}
internal void OnAvatarClear()
@ -91,8 +119,15 @@ namespace ml_dht
m_lastHeadRotation = Quaternion.identity;
m_bindRotation = Quaternion.identity;
}
internal void OnAvatarReinitialize()
{
m_camera = PlayerSetup.Instance.GetActiveCamera().transform;
m_lookIK = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
if(m_lookIK != null)
m_lookIK.onPostSolverUpdate.AddListener(this.OnLookIKPostUpdate);
}
internal void OnEyeControllerUpdate(CVREyeController p_component)
internal void OnEyeControllerUpdate(EyeMovementController p_component)
{
if(m_enabled)
{
@ -112,6 +147,24 @@ namespace ml_dht
}
}
internal bool UpdateFaceTracking(CVRFaceTracking p_component)
{
bool l_result = false;
if(m_enabled && Settings.FaceTracking)
{
if(!m_lipDataSent)
{
FaceTrackingManager.Instance.SubmitNewFacialData(m_lipData);
m_lipDataSent = true;
}
p_component.LipSyncWasUpdated = true;
p_component.UpdateShapesLocal_Private();
l_result = true;
}
return l_result;
}
// Settings
void SetEnabled(bool p_state)
{

View file

@ -1,6 +1,8 @@
using ABI.CCK.Components;
using ABI_RC.Core.Player;
using ABI_RC.Core.Player.EyeMovement;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.FaceTracking;
using ABI_RC.Systems.IK;
using System.Reflection;
namespace ml_dht
@ -9,7 +11,7 @@ namespace ml_dht
{
static DesktopHeadTracking ms_instance = null;
TrackingModule m_trackingModule = null;
DataParser m_dataParser = null;
HeadTracked m_localTracked = null;
public override void OnInitializeMelon()
@ -19,8 +21,6 @@ namespace ml_dht
Settings.Init();
m_trackingModule = new TrackingModule();
// Patches
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
@ -32,6 +32,11 @@ namespace ml_dht
null,
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public),
null,
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
MelonLoader.MelonCoroutines.Start(WaitForInstances());
}
@ -44,15 +49,19 @@ namespace ml_dht
while(PlayerSetup.Instance == null)
yield return null;
m_dataParser = new DataParser();
m_localTracked = PlayerSetup.Instance.gameObject.AddComponent<HeadTracked>();
FaceTrackingManager.Instance.RegisterModule(m_trackingModule);
// If you think it's a joke to put patch here, go on, try to put it in OnInitializeMelon, you melon :>
HarmonyInstance.Patch(
typeof(CVREyeController).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic),
typeof(EyeMovementController).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic),
null,
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnEyeControllerUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(CVRFaceTracking).GetMethod("UpdateLocalData", BindingFlags.Instance | BindingFlags.NonPublic),
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnFaceTrackingLocalUpdate_Prefix), BindingFlags.Static | BindingFlags.NonPublic))
);
}
public override void OnDeinitializeMelon()
@ -60,17 +69,17 @@ namespace ml_dht
if(ms_instance == this)
ms_instance = null;
m_trackingModule = null;
m_dataParser = null;
m_localTracked = null;
}
public override void OnUpdate()
{
if(Settings.Enabled && (m_trackingModule != null))
if(Settings.Enabled && (m_dataParser != null))
{
m_trackingModule.Update();
m_dataParser.Update();
if(m_localTracked != null)
m_localTracked.UpdateTrackingData(ref m_trackingModule.GetLatestTrackingData());
m_localTracked.UpdateTrackingData(ref m_dataParser.GetLatestTrackingData());
}
}
@ -102,12 +111,26 @@ namespace ml_dht
}
}
static void OnEyeControllerUpdate_Postfix(ref CVREyeController __instance) => ms_instance?.OnEyeControllerUpdate(__instance);
void OnEyeControllerUpdate(CVREyeController p_component)
static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize();
void OnAvatarReinitialize()
{
try
{
if(p_component.isLocal && (m_localTracked != null))
if(m_localTracked != null)
m_localTracked.OnAvatarReinitialize();
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnEyeControllerUpdate_Postfix(ref EyeMovementController __instance) => ms_instance?.OnEyeControllerUpdate(__instance);
void OnEyeControllerUpdate(EyeMovementController p_component)
{
try
{
if(p_component.IsLocal && (m_localTracked != null))
m_localTracked.OnEyeControllerUpdate(p_component);
}
catch(System.Exception e)
@ -115,5 +138,18 @@ namespace ml_dht
MelonLoader.MelonLogger.Error(e);
}
}
static bool OnFaceTrackingLocalUpdate_Prefix(ref CVRFaceTracking __instance)
{
bool? l_result = ms_instance?.OnFaceTrackingLocalUpdate(__instance);
return l_result.GetValueOrDefault(true);
}
bool OnFaceTrackingLocalUpdate(CVRFaceTracking p_component)
{
bool l_result = true;
if(p_component.UseFacialTracking && (m_localTracked != null))
l_result = !m_localTracked.UpdateFaceTracking(p_component);
return l_result;
}
}
}

View file

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

View file

@ -21,7 +21,6 @@ Available mod's settings in `Settings - Implementation - Desktop Head Tracking`:
* **Use head tracking:** enables head tracking; default value - `true`.
* **Use eyes tracking:** enables eyes tracking; default value - `true`.
* **Use face tracking:** enables mouth shapes tracking; default value - `true`.
* **Note:** You need to enable desktop tracking of `Vive Face tracking` in `Settings - Implementation` menu page.
* **Note:** Your avatar should have configured `CVR Face Tracking` component.
* **Use blinking:** uses blinking from data; default value - `true`.
* **Mirrored movement:** mirrors movement and gaze along 0YZ plane; default value - `false`.

View file

@ -1,70 +0,0 @@
using ABI_RC.Systems.FaceTracking;
using System;
using UnityEngine;
using ViveSR.anipal.Lip;
namespace ml_dht
{
class TrackingModule : ITrackingModule
{
bool m_registered = false;
bool m_activeAsModule = false;
MemoryMapReader m_mapReader = null;
byte[] m_buffer = null;
TrackingData m_trackingData;
LipData_v2 m_lipData;
public TrackingModule()
{
m_lipData = new LipData_v2();
m_lipData.frame = 0;
m_lipData.time = 0;
m_lipData.image = IntPtr.Zero;
m_lipData.prediction_data = new PredictionData_v2();
m_lipData.prediction_data.blend_shape_weight = new float[(int)LipShape_v2.Max];
m_buffer = new byte[1024];
m_mapReader = new MemoryMapReader();
m_mapReader.Open("head/data");
}
~TrackingModule()
{
m_mapReader.Close();
m_mapReader = null;
}
public (bool, bool) Initialize(bool useEye, bool useLip)
{
m_registered = true;
m_activeAsModule = true;
return (false, true);
}
public void Shutdown()
{
m_activeAsModule = false;
}
public bool IsEyeDataAvailable() => false;
public bool IsLipDataAvailable() => true;
internal void Update()
{
if(m_mapReader.Read(ref m_buffer))
{
m_trackingData = TrackingData.ToObject(m_buffer);
float l_weight = Mathf.Clamp01(Mathf.InverseLerp(0.25f, 1f, Mathf.Abs(m_trackingData.m_mouthShape)));
m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Jaw_Open] = m_trackingData.m_mouthOpen;
m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Pout] = ((m_trackingData.m_mouthShape > 0f) ? l_weight : 0f);
m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Smile_Left] = ((m_trackingData.m_mouthShape < 0f) ? l_weight : 0f);
m_lipData.prediction_data.blend_shape_weight[(int)LipShape_v2.Mouth_Smile_Right] = ((m_trackingData.m_mouthShape < 0f) ? l_weight : 0f);
if(m_registered && m_activeAsModule && Settings.FaceTracking)
FaceTrackingManager.Instance.SubmitNewFacialData(m_lipData);
}
}
internal ref TrackingData GetLatestTrackingData() => ref m_trackingData;
}
}

View file

@ -1,4 +1,5 @@
using ABI_RC.Core.UI;
using ABI.CCK.Components;
using ABI_RC.Core.UI;
using System.Reflection;
using UnityEngine;
@ -6,10 +7,14 @@ namespace ml_dht
{
static class Utils
{
static readonly object[] ms_emptyArray = new object[0];
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 public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);
static public void UpdateShapesLocal_Private(this CVRFaceTracking p_instance) => ms_updateShapesLocal?.Invoke(p_instance, ms_emptyArray);
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

@ -6,7 +6,7 @@
<Authors>SDraw</Authors>
<Company>None</Company>
<Product>DesktopHeadTracking</Product>
<Version>1.2.0</Version>
<Version>1.2.1</Version>
<Platforms>x64</Platforms>
</PropertyGroup>

129
ml_lme/LeapHand.cs Normal file
View file

@ -0,0 +1,129 @@
using UnityEngine;
namespace ml_lme
{
class LeapHand
{
public enum FingerBone
{
ThumbMetacarpal = 0,
ThumbProximal,
ThumbIntermediate,
ThumbDistal,
IndexMetacarpal,
IndexProximal,
IndexIntermediate,
IndexDistal,
MiddleMetacarpal,
MiddleProximal,
MiddleIntermediate,
MiddleDistal,
RingMetacarpal,
RingProximal,
RingIntermediate,
RingDistal,
PinkyMetacarpal,
PinkyProximal,
PinkyIntermediate,
PinkyDistal
};
readonly Transform m_root = null;
readonly Transform m_wrist = null;
readonly GameObject m_mesh = null;
readonly Transform[] m_fingersBones = null;
readonly Quaternion[] m_initialRotations = null;
public LeapHand(Transform p_root, bool p_left)
{
m_fingersBones = new Transform[20];
m_initialRotations = new Quaternion[20];
m_root = p_root;
if(m_root != null)
{
m_mesh = m_root.Find(p_left ? "GenericHandL" : "GenericHandR")?.gameObject;
m_wrist = m_root.Find(p_left ? "LeftHand/Wrist" : "RightHand/Wrist");
if(m_wrist != null)
{
m_fingersBones[0] = null; // Actual thumb-meta, look at Leap Motion docs, dummy, it's zero point
m_fingersBones[1] = m_wrist.Find("thumb_meta");
m_fingersBones[2] = m_wrist.Find("thumb_meta/thumb_a");
m_fingersBones[3] = m_wrist.Find("thumb_meta/thumb_a/thumb_b");
m_fingersBones[4] = m_wrist.Find("index_meta");
m_fingersBones[5] = m_wrist.Find("index_meta/index_a");
m_fingersBones[6] = m_wrist.Find("index_meta/index_a/index_b");
m_fingersBones[7] = m_wrist.Find("index_meta/index_a/index_b/index_c");
m_fingersBones[8] = m_wrist.Find("middle_meta");
m_fingersBones[9] = m_wrist.Find("middle_meta/middle_a");
m_fingersBones[10] = m_wrist.Find("middle_meta/middle_a/middle_b");
m_fingersBones[11] = m_wrist.Find("middle_meta/middle_a/middle_b/middle_c");
m_fingersBones[12] = m_wrist.Find("ring_meta");
m_fingersBones[13] = m_wrist.Find("ring_meta/ring_a");
m_fingersBones[14] = m_wrist.Find("ring_meta/ring_a/ring_b");
m_fingersBones[15] = m_wrist.Find("ring_meta/ring_a/ring_b/ring_c");
m_fingersBones[16] = m_wrist.Find("pinky_meta");
m_fingersBones[17] = m_wrist.Find("pinky_meta/pinky_a");
m_fingersBones[18] = m_wrist.Find("pinky_meta/pinky_a/pinky_b");
m_fingersBones[19] = m_wrist.Find("pinky_meta/pinky_a/pinky_b/pinky_c");
}
}
for(int i = 0; i < 20; i++)
{
if(m_fingersBones[i] != null)
m_initialRotations[i] = m_fingersBones[i].localRotation;
}
}
public void Update(LeapParser.HandData p_data)
{
if(m_wrist != null)
{
m_wrist.position = p_data.m_position;
m_wrist.rotation = p_data.m_rotation;
for(int i = 0; i < 20; i++)
{
if(m_fingersBones[i] != null)
{
//m_fingers[i].position = p_data.m_fingerPosition[i];
m_fingersBones[i].rotation = p_data.m_fingerRotation[i];
}
}
m_wrist.localPosition = Vector3.zero;
m_wrist.localRotation = Quaternion.identity;
}
}
public void Reset()
{
if(m_wrist != null)
{
m_wrist.localPosition = Vector3.zero;
m_wrist.localRotation = Quaternion.identity;
}
for(int i = 0; i < 20; i++)
{
if(m_fingersBones[i] != null)
m_fingersBones[i].localRotation = m_initialRotations[i];
}
}
public Transform GetRoot() => m_root;
public Transform GetWrist() => m_wrist;
public Transform GetFingersBone(FingerBone p_bone) => m_fingersBones[(int)p_bone];
public void SetMeshActive(bool p_state)
{
if(m_mesh != null)
m_mesh.SetActive(p_state);
}
}
}

View file

@ -1,8 +1,10 @@
using ABI_RC.Core.InteractionSystem;
using ABI.CCK.Components;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.InputManagement;
using ABI_RC.Systems.VRModeSwitch;
using System.Collections;
using UnityEngine;
@ -32,13 +34,15 @@ namespace ml_lme
m_inVR = Utils.IsInVR();
m_handRayLeft = LeapTracking.Instance.GetLeftHand().gameObject.AddComponent<ControllerRay>();
m_handRayLeft = LeapTracking.Instance.GetLeftHand().GetRoot().gameObject.AddComponent<ControllerRay>();
m_handRayLeft.hand = true;
m_handRayLeft.generalMask = -1485;
m_handRayLeft.generalMask = -269;
m_handRayLeft.isInteractionRay = true;
m_handRayLeft.triggerGazeEvents = false;
m_handRayLeft.holderRoot = m_handRayLeft.gameObject;
m_handRayLeft.attachmentDistance = 0f;
m_handRayLeft.uiMask = 32;
m_handRayLeft.isDesktopRay = !m_inVR;
m_lineLeft = m_handRayLeft.gameObject.AddComponent<LineRenderer>();
m_lineLeft.endWidth = 1f;
@ -52,13 +56,15 @@ namespace ml_lme
m_lineLeft.receiveShadows = false;
m_handRayLeft.lineRenderer = m_lineLeft;
m_handRayRight = LeapTracking.Instance.GetRightHand().gameObject.AddComponent<ControllerRay>();
m_handRayRight = LeapTracking.Instance.GetRightHand().GetRoot().gameObject.AddComponent<ControllerRay>();
m_handRayRight.hand = false;
m_handRayRight.generalMask = -1485;
m_handRayRight.generalMask = -269;
m_handRayRight.isInteractionRay = true;
m_handRayRight.triggerGazeEvents = false;
m_handRayRight.holderRoot = m_handRayRight.gameObject;
m_handRayRight.attachmentDistance = 0f;
m_handRayRight.uiMask = 32;
m_handRayRight.isDesktopRay = !m_inVR;
m_lineRight = m_handRayRight.gameObject.AddComponent<LineRenderer>();
m_lineRight.endWidth = 1f;
@ -72,6 +78,9 @@ namespace ml_lme
m_lineRight.receiveShadows = false;
m_handRayRight.lineRenderer = m_lineRight;
m_handRayLeft.otherRay = m_handRayRight;
m_handRayRight.otherRay = m_handRayLeft;
Settings.EnabledChange += this.OnEnableChange;
Settings.InteractionChange += this.OnInteractionChange;
Settings.GesturesChange += this.OnGesturesChange;
@ -84,6 +93,9 @@ namespace ml_lme
MelonLoader.MelonCoroutines.Start(WaitForSettings());
MelonLoader.MelonCoroutines.Start(WaitForMaterial());
VRModeSwitchEvents.OnInitializeXR.AddListener(OnModeSwitch);
VRModeSwitchEvents.OnDeinitializeXR.AddListener(OnModeSwitch);
}
IEnumerator WaitForSettings()
@ -108,8 +120,13 @@ namespace ml_lme
m_lineLeft.material = PlayerSetup.Instance.vrRayLeft.lineRenderer.material;
m_lineLeft.gameObject.layer = PlayerSetup.Instance.vrRayLeft.gameObject.layer;
m_handRayLeft.highlightMaterial = PlayerSetup.Instance.vrRayLeft.highlightMaterial;
m_handRayLeft.SetVRActive(m_inVR);
m_lineRight.material = PlayerSetup.Instance.vrRayLeft.lineRenderer.material;
m_lineRight.gameObject.layer = PlayerSetup.Instance.vrRayLeft.gameObject.layer;
m_handRayRight.highlightMaterial = PlayerSetup.Instance.vrRayLeft.highlightMaterial;
m_handRayRight.SetVRActive(m_inVR);
}
public override void ModuleDestroyed()
@ -138,6 +155,8 @@ namespace ml_lme
Settings.FingersOnlyChange -= this.OnFingersOnlyChange;
MetaPort.Instance.settings.settingBoolChanged.RemoveListener(this.OnGameSettingBoolChange);
VRModeSwitchEvents.OnInitializeXR.RemoveListener(OnModeSwitch);
VRModeSwitchEvents.OnDeinitializeXR.RemoveListener(OnModeSwitch);
}
public override void UpdateInput()
@ -157,36 +176,36 @@ namespace ml_lme
base._inputManager.gestureLeftRaw = 0f;
// Finger Point & Finger Gun
if((base._inputManager.fingerCurlLeftIndex < 0.2f) && (base._inputManager.fingerCurlLeftMiddle > 0.75f) &&
(base._inputManager.fingerCurlLeftRing > 0.75f) && (base._inputManager.fingerCurlLeftPinky > 0.75f))
if((base._inputManager.fingerFullCurlNormalizedLeftIndex < 0.2f) && (base._inputManager.fingerFullCurlNormalizedLeftMiddle > 0.75f) &&
(base._inputManager.fingerFullCurlNormalizedLeftRing > 0.75f) && (base._inputManager.fingerFullCurlNormalizedLeftPinky > 0.75f))
{
base._inputManager.gestureLeftRaw = (base._inputManager.fingerCurlLeftThumb >= 0.5f) ? 4f : 3f;
base._inputManager.gestureLeftRaw = (base._inputManager.fingerFullCurlNormalizedLeftThumb >= 0.5f) ? 4f : 3f;
}
// Peace Sign
if((base._inputManager.fingerCurlLeftIndex < 0.2f) && (base._inputManager.fingerCurlLeftMiddle < 0.2f) &&
(base._inputManager.fingerCurlLeftRing > 0.75f) && (base._inputManager.fingerCurlLeftPinky > 0.75f))
if((base._inputManager.fingerFullCurlNormalizedLeftIndex < 0.2f) && (base._inputManager.fingerFullCurlNormalizedLeftMiddle < 0.2f) &&
(base._inputManager.fingerFullCurlNormalizedLeftRing > 0.75f) && (base._inputManager.fingerFullCurlNormalizedLeftPinky > 0.75f))
{
base._inputManager.gestureLeftRaw = 5f;
}
// Rock and Roll
if((base._inputManager.fingerCurlLeftIndex < 0.2f) && (base._inputManager.fingerCurlLeftMiddle > 0.75f) &&
(base._inputManager.fingerCurlLeftRing > 0.75f) && (base._inputManager.fingerCurlLeftPinky < 0.5f))
if((base._inputManager.fingerFullCurlNormalizedLeftIndex < 0.2f) && (base._inputManager.fingerFullCurlNormalizedLeftMiddle > 0.75f) &&
(base._inputManager.fingerFullCurlNormalizedLeftRing > 0.75f) && (base._inputManager.fingerFullCurlNormalizedLeftPinky < 0.5f))
{
base._inputManager.gestureLeftRaw = 6f;
}
// Fist & Thumbs Up
if((base._inputManager.fingerCurlLeftIndex > 0.5f) && (base._inputManager.fingerCurlLeftMiddle > 0.5f) &&
(base._inputManager.fingerCurlLeftRing > 0.5f) && (base._inputManager.fingerCurlLeftPinky > 0.5f))
if((base._inputManager.fingerFullCurlNormalizedLeftIndex > 0.5f) && (base._inputManager.fingerFullCurlNormalizedLeftMiddle > 0.5f) &&
(base._inputManager.fingerFullCurlNormalizedLeftRing > 0.5f) && (base._inputManager.fingerFullCurlNormalizedLeftPinky > 0.5f))
{
base._inputManager.gestureLeftRaw = (base._inputManager.fingerCurlLeftThumb >= 0.5f) ? ((l_data.m_leftHand.m_grabStrength - 0.5f) * 2f) : 2f;
base._inputManager.gestureLeftRaw = (base._inputManager.fingerFullCurlNormalizedLeftThumb >= 0.5f) ? ((l_data.m_leftHand.m_grabStrength - 0.5f) * 2f) : 2f;
}
// Open Hand
if((base._inputManager.fingerCurlLeftIndex < 0.2f) && (base._inputManager.fingerCurlLeftMiddle < 0.2f) &&
(base._inputManager.fingerCurlLeftRing < 0.2f) && (base._inputManager.fingerCurlLeftPinky < 0.2f))
if((base._inputManager.fingerFullCurlNormalizedLeftIndex < 0.2f) && (base._inputManager.fingerFullCurlNormalizedLeftMiddle < 0.2f) &&
(base._inputManager.fingerFullCurlNormalizedLeftRing < 0.2f) && (base._inputManager.fingerFullCurlNormalizedLeftPinky < 0.2f))
{
base._inputManager.gestureLeftRaw = -1f;
}
@ -217,36 +236,36 @@ namespace ml_lme
base._inputManager.gestureRightRaw = 0f;
// Finger Point & Finger Gun
if((base._inputManager.fingerCurlRightIndex < 0.2f) && (base._inputManager.fingerCurlRightMiddle > 0.75f) &&
(base._inputManager.fingerCurlRightRing > 0.75f) && (base._inputManager.fingerCurlRightPinky > 0.75f))
if((base._inputManager.fingerFullCurlNormalizedRightIndex < 0.2f) && (base._inputManager.fingerFullCurlNormalizedRightMiddle > 0.75f) &&
(base._inputManager.fingerFullCurlNormalizedRightRing > 0.75f) && (base._inputManager.fingerFullCurlNormalizedRightPinky > 0.75f))
{
base._inputManager.gestureRightRaw = (base._inputManager.fingerCurlRightThumb >= 0.5f) ? 4f : 3f;
base._inputManager.gestureRightRaw = (base._inputManager.fingerFullCurlNormalizedRightThumb >= 0.5f) ? 4f : 3f;
}
// Peace Sign
if((base._inputManager.fingerCurlRightIndex < 0.2f) && (base._inputManager.fingerCurlRightMiddle < 0.2f) &&
(base._inputManager.fingerCurlRightRing > 0.75f) && (base._inputManager.fingerCurlRightPinky > 0.75f))
if((base._inputManager.fingerFullCurlNormalizedRightIndex < 0.2f) && (base._inputManager.fingerFullCurlNormalizedRightMiddle < 0.2f) &&
(base._inputManager.fingerFullCurlNormalizedRightRing > 0.75f) && (base._inputManager.fingerFullCurlNormalizedRightPinky > 0.75f))
{
base._inputManager.gestureRightRaw = 5f;
}
// Rock and Roll
if((base._inputManager.fingerCurlRightIndex < 0.2f) && (base._inputManager.fingerCurlRightMiddle > 0.75f) &&
(base._inputManager.fingerCurlRightRing > 0.75f) && (base._inputManager.fingerCurlRightPinky < 0.5f))
if((base._inputManager.fingerFullCurlNormalizedRightIndex < 0.2f) && (base._inputManager.fingerFullCurlNormalizedRightMiddle > 0.75f) &&
(base._inputManager.fingerFullCurlNormalizedRightRing > 0.75f) && (base._inputManager.fingerFullCurlNormalizedRightPinky < 0.5f))
{
base._inputManager.gestureRightRaw = 6f;
}
// Fist & Thumbs Up
if((base._inputManager.fingerCurlRightIndex > 0.5f) && (base._inputManager.fingerCurlRightMiddle > 0.5f) &&
(base._inputManager.fingerCurlRightRing > 0.5f) && (base._inputManager.fingerCurlRightPinky > 0.5f))
if((base._inputManager.fingerFullCurlNormalizedRightIndex > 0.5f) && (base._inputManager.fingerFullCurlNormalizedRightMiddle > 0.5f) &&
(base._inputManager.fingerFullCurlNormalizedRightRing > 0.5f) && (base._inputManager.fingerFullCurlNormalizedRightPinky > 0.5f))
{
base._inputManager.gestureRightRaw = (base._inputManager.fingerCurlRightThumb >= 0.5f) ? ((l_data.m_rightHand.m_grabStrength - 0.5f) * 2f) : 2f;
base._inputManager.gestureRightRaw = (base._inputManager.fingerFullCurlNormalizedRightThumb >= 0.5f) ? ((l_data.m_rightHand.m_grabStrength - 0.5f) * 2f) : 2f;
}
// Open Hand
if((base._inputManager.fingerCurlRightIndex < 0.2f) && (base._inputManager.fingerCurlRightMiddle < 0.2f) &&
(base._inputManager.fingerCurlRightRing < 0.2f) && (base._inputManager.fingerCurlRightPinky < 0.2f))
if((base._inputManager.fingerFullCurlNormalizedRightIndex < 0.2f) && (base._inputManager.fingerFullCurlNormalizedRightMiddle < 0.2f) &&
(base._inputManager.fingerFullCurlNormalizedRightRing < 0.2f) && (base._inputManager.fingerFullCurlNormalizedRightPinky < 0.2f))
{
base._inputManager.gestureRightRaw = -1f;
}
@ -279,7 +298,7 @@ namespace ml_lme
{
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
if(m_handVisibleLeft && (!m_inVR || !Utils.IsLeftHandTracked()) && !Settings.FingersOnly)
if(m_handVisibleLeft && !Settings.FingersOnly)
{
float l_strength = l_data.m_leftHand.m_grabStrength;
@ -307,7 +326,7 @@ namespace ml_lme
}
}
if(m_handVisibleRight && (!m_inVR || !Utils.IsRightHandTracked()) && !Settings.FingersOnly)
if(m_handVisibleRight && !Settings.FingersOnly)
{
float l_strength = l_data.m_rightHand.m_grabStrength;
@ -335,8 +354,8 @@ namespace ml_lme
}
}
ToggleHandRay(m_handVisibleLeft && (!m_inVR || !Utils.IsLeftHandTracked()) && !Settings.FingersOnly, true);
ToggleHandRay(m_handVisibleRight && (!m_inVR || !Utils.IsRightHandTracked()) && !Settings.FingersOnly, false);
ToggleHandRay(m_handVisibleLeft && !Settings.FingersOnly, true);
ToggleHandRay(m_handVisibleRight && !Settings.FingersOnly, false);
}
}
@ -402,47 +421,116 @@ namespace ml_lme
}
// Game events
internal void OnAvatarSetup()
{
m_inVR = Utils.IsInVR();
}
internal void OnRayScale(float p_scale)
{
m_handRayLeft.SetRayScale(p_scale);
m_handRayRight.SetRayScale(p_scale);
}
internal void OnPickupGrab(CVRPickupObject p_pickup)
{
if(p_pickup.gripType == CVRPickupObject.GripType.Origin)
{
if(p_pickup._controllerRay == m_handRayLeft)
{
m_handRayLeft.attachmentPoint.localPosition = Vector3.zero;
m_handRayLeft.attachmentPoint.localRotation = Quaternion.Euler(0f, 0f, 270f);
}
if(p_pickup._controllerRay == m_handRayRight)
{
m_handRayRight.attachmentPoint.localPosition = Vector3.zero;
m_handRayRight.attachmentPoint.localRotation = Quaternion.Euler(0f, 0f, 90f);
}
}
}
void OnModeSwitch()
{
m_inVR = Utils.IsInVR();
base._inputManager.SetModuleAsLast(this);
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);
}
OnEnableChange(Settings.Enabled);
}
// Arbitrary
void SetFingersInput(LeapParser.HandData p_hand, bool p_left)
{
// Game has spreads in range of [0;1], but mod now operates in range of [-1;1]
// So spreads will be normalized towards game's range
if(p_left)
{
base._inputManager.fingerCurlLeftThumb = p_hand.m_bends[0];
base._inputManager.fingerCurlLeftIndex = p_hand.m_bends[1];
base._inputManager.fingerCurlLeftMiddle = p_hand.m_bends[2];
base._inputManager.fingerCurlLeftRing = p_hand.m_bends[3];
base._inputManager.fingerCurlLeftPinky = p_hand.m_bends[4];
base._inputManager.fingerSpreadLeftThumb = 1f - (p_hand.m_spreads[0] * 0.5f + 0.5f);
base._inputManager.fingerSpreadLeftIndex = 1f - (p_hand.m_spreads[1] * 0.5f + 0.5f);
base._inputManager.fingerSpreadLeftMiddle = 1f - (p_hand.m_spreads[2] * 0.5f + 0.5f);
base._inputManager.fingerSpreadLeftRing = 1f - (p_hand.m_spreads[3] * 0.5f + 0.5f);
base._inputManager.fingerSpreadLeftPinky = 1f - (p_hand.m_spreads[4] * 0.5f + 0.5f);
base._inputManager.finger1StretchedLeftThumb = LeapTracked.ms_lastLeftFingerBones[0];
base._inputManager.finger2StretchedLeftThumb = LeapTracked.ms_lastLeftFingerBones[1];
base._inputManager.finger3StretchedLeftThumb = LeapTracked.ms_lastLeftFingerBones[2];
base._inputManager.fingerSpreadLeftThumb = LeapTracked.ms_lastLeftFingerBones[3];
base._inputManager.finger1StretchedLeftIndex = LeapTracked.ms_lastLeftFingerBones[4];
base._inputManager.finger2StretchedLeftIndex = LeapTracked.ms_lastLeftFingerBones[5];
base._inputManager.finger3StretchedLeftIndex = LeapTracked.ms_lastLeftFingerBones[6];
base._inputManager.fingerSpreadLeftIndex = LeapTracked.ms_lastLeftFingerBones[7];
base._inputManager.finger1StretchedLeftMiddle = LeapTracked.ms_lastLeftFingerBones[8];
base._inputManager.finger2StretchedLeftMiddle = LeapTracked.ms_lastLeftFingerBones[9];
base._inputManager.finger3StretchedLeftMiddle = LeapTracked.ms_lastLeftFingerBones[10];
base._inputManager.fingerSpreadLeftMiddle = LeapTracked.ms_lastLeftFingerBones[11];
base._inputManager.finger1StretchedLeftRing = LeapTracked.ms_lastLeftFingerBones[12];
base._inputManager.finger2StretchedLeftRing = LeapTracked.ms_lastLeftFingerBones[13];
base._inputManager.finger3StretchedLeftRing = LeapTracked.ms_lastLeftFingerBones[14];
base._inputManager.fingerSpreadLeftRing = LeapTracked.ms_lastLeftFingerBones[15];
base._inputManager.finger1StretchedLeftPinky = LeapTracked.ms_lastLeftFingerBones[16];
base._inputManager.finger2StretchedLeftPinky = LeapTracked.ms_lastLeftFingerBones[17];
base._inputManager.finger3StretchedLeftPinky = LeapTracked.ms_lastLeftFingerBones[18];
base._inputManager.fingerSpreadLeftPinky = LeapTracked.ms_lastLeftFingerBones[19];
base._inputManager.fingerFullCurlNormalizedLeftThumb = p_hand.m_bends[0];
base._inputManager.fingerFullCurlNormalizedLeftIndex = p_hand.m_bends[1];
base._inputManager.fingerFullCurlNormalizedLeftMiddle = p_hand.m_bends[2];
base._inputManager.fingerFullCurlNormalizedLeftRing = p_hand.m_bends[3];
base._inputManager.fingerFullCurlNormalizedLeftPinky = p_hand.m_bends[4];
}
else
{
base._inputManager.fingerCurlRightThumb = p_hand.m_bends[0];
base._inputManager.fingerCurlRightIndex = p_hand.m_bends[1];
base._inputManager.fingerCurlRightMiddle = p_hand.m_bends[2];
base._inputManager.fingerCurlRightRing = p_hand.m_bends[3];
base._inputManager.fingerCurlRightPinky = p_hand.m_bends[4];
base._inputManager.fingerSpreadRightThumb = 1f - (p_hand.m_spreads[0] * 0.5f + 0.5f);
base._inputManager.fingerSpreadRightIndex = 1f - (p_hand.m_spreads[1] * 0.5f + 0.5f);
base._inputManager.fingerSpreadRightMiddle = 1f - (p_hand.m_spreads[2] * 0.5f + 0.5f);
base._inputManager.fingerSpreadRightRing = 1f - (p_hand.m_spreads[3] * 0.5f + 0.5f);
base._inputManager.fingerSpreadRightPinky = 1f - (p_hand.m_spreads[4] * 0.5f + 0.5f);
base._inputManager.finger1StretchedRightThumb = LeapTracked.ms_lastRightFingerBones[0];
base._inputManager.finger2StretchedRightThumb = LeapTracked.ms_lastRightFingerBones[1];
base._inputManager.finger3StretchedRightThumb = LeapTracked.ms_lastRightFingerBones[2];
base._inputManager.fingerSpreadRightThumb = LeapTracked.ms_lastRightFingerBones[3];
base._inputManager.finger1StretchedRightIndex = LeapTracked.ms_lastRightFingerBones[4];
base._inputManager.finger2StretchedRightIndex = LeapTracked.ms_lastRightFingerBones[5];
base._inputManager.finger3StretchedRightIndex = LeapTracked.ms_lastRightFingerBones[6];
base._inputManager.fingerSpreadRightIndex = LeapTracked.ms_lastRightFingerBones[7];
base._inputManager.finger1StretchedRightMiddle = LeapTracked.ms_lastRightFingerBones[8];
base._inputManager.finger2StretchedRightMiddle = LeapTracked.ms_lastRightFingerBones[9];
base._inputManager.finger3StretchedRightMiddle = LeapTracked.ms_lastRightFingerBones[10];
base._inputManager.fingerSpreadRightMiddle = LeapTracked.ms_lastRightFingerBones[11];
base._inputManager.finger1StretchedRightRing = LeapTracked.ms_lastRightFingerBones[12];
base._inputManager.finger2StretchedRightRing = LeapTracked.ms_lastRightFingerBones[13];
base._inputManager.finger3StretchedRightRing = LeapTracked.ms_lastRightFingerBones[14];
base._inputManager.fingerSpreadRightRing = LeapTracked.ms_lastRightFingerBones[15];
base._inputManager.finger1StretchedRightPinky = LeapTracked.ms_lastRightFingerBones[16];
base._inputManager.finger2StretchedRightPinky = LeapTracked.ms_lastRightFingerBones[17];
base._inputManager.finger3StretchedRightPinky = LeapTracked.ms_lastRightFingerBones[18];
base._inputManager.fingerSpreadRightPinky = LeapTracked.ms_lastRightFingerBones[19];
base._inputManager.fingerFullCurlNormalizedRightThumb = p_hand.m_bends[0];
base._inputManager.fingerFullCurlNormalizedRightIndex = p_hand.m_bends[1];
base._inputManager.fingerFullCurlNormalizedRightMiddle = p_hand.m_bends[2];
base._inputManager.fingerFullCurlNormalizedRightRing = p_hand.m_bends[3];
base._inputManager.fingerFullCurlNormalizedRightPinky = p_hand.m_bends[4];
}
}
@ -450,29 +538,69 @@ namespace ml_lme
{
if(p_left)
{
base._inputManager.fingerCurlLeftThumb = 0f;
base._inputManager.fingerCurlLeftIndex = 0f;
base._inputManager.fingerCurlLeftMiddle = 0f;
base._inputManager.fingerCurlLeftRing = 0f;
base._inputManager.fingerCurlLeftPinky = 0f;
base._inputManager.fingerSpreadLeftThumb = 0.5f;
base._inputManager.fingerSpreadLeftIndex = 0.5f;
base._inputManager.fingerSpreadLeftMiddle = 0.5f;
base._inputManager.fingerSpreadLeftRing = 0.5f;
base._inputManager.fingerSpreadLeftPinky = 0.5f;
base._inputManager.finger1StretchedLeftThumb = -0.5f;
base._inputManager.finger2StretchedLeftThumb = 0.7f;
base._inputManager.finger3StretchedLeftThumb = 0.7f;
base._inputManager.fingerSpreadLeftThumb = 0f;
base._inputManager.finger1StretchedLeftIndex = 0.5f;
base._inputManager.finger2StretchedLeftIndex = 0.7f;
base._inputManager.finger3StretchedLeftIndex = 0.7f;
base._inputManager.fingerSpreadLeftIndex = 0f;
base._inputManager.finger1StretchedLeftMiddle = 0.5f;
base._inputManager.finger2StretchedLeftMiddle = 0.7f;
base._inputManager.finger3StretchedLeftMiddle = 0.7f;
base._inputManager.fingerSpreadLeftMiddle = 0f;
base._inputManager.finger1StretchedLeftRing = 0.5f;
base._inputManager.finger2StretchedLeftRing = 0.7f;
base._inputManager.finger3StretchedLeftRing = 0.7f;
base._inputManager.fingerSpreadLeftRing = 0f;
base._inputManager.finger1StretchedLeftPinky = 0.5f;
base._inputManager.finger2StretchedLeftPinky = 0.7f;
base._inputManager.finger3StretchedLeftPinky = 0.7f;
base._inputManager.fingerSpreadLeftPinky = 0f;
base._inputManager.fingerFullCurlNormalizedLeftThumb = 0f;
base._inputManager.fingerFullCurlNormalizedLeftIndex = 0f;
base._inputManager.fingerFullCurlNormalizedLeftMiddle = 0f;
base._inputManager.fingerFullCurlNormalizedLeftRing = 0f;
base._inputManager.fingerFullCurlNormalizedLeftPinky = 0f;
}
else
{
base._inputManager.fingerCurlRightThumb = 0f;
base._inputManager.fingerCurlRightIndex = 0f;
base._inputManager.fingerCurlRightMiddle = 0f;
base._inputManager.fingerCurlRightRing = 0f;
base._inputManager.fingerCurlRightPinky = 0f;
base._inputManager.fingerSpreadRightThumb = 0.5f;
base._inputManager.fingerSpreadRightIndex = 0.5f;
base._inputManager.fingerSpreadRightMiddle = 0.5f;
base._inputManager.fingerSpreadRightRing = 0.5f;
base._inputManager.fingerSpreadRightPinky = 0.5f;
base._inputManager.finger1StretchedRightThumb = -0.5f;
base._inputManager.finger2StretchedRightThumb = 0.7f;
base._inputManager.finger3StretchedRightThumb = 0.7f;
base._inputManager.fingerSpreadRightThumb = 0f;
base._inputManager.finger1StretchedRightIndex = 0.5f;
base._inputManager.finger2StretchedRightIndex = 0.7f;
base._inputManager.finger3StretchedRightIndex = 0.7f;
base._inputManager.fingerSpreadRightIndex = 0f;
base._inputManager.finger1StretchedRightMiddle = 0.5f;
base._inputManager.finger2StretchedRightMiddle = 0.7f;
base._inputManager.finger3StretchedRightMiddle = 0.7f;
base._inputManager.fingerSpreadRightMiddle = 0f;
base._inputManager.finger1StretchedRightRing = 0.5f;
base._inputManager.finger2StretchedRightRing = 0.7f;
base._inputManager.finger3StretchedRightRing = 0.7f;
base._inputManager.fingerSpreadRightRing = 0f;
base._inputManager.finger1StretchedRightPinky = 0.5f;
base._inputManager.finger2StretchedRightPinky = 0.7f;
base._inputManager.finger3StretchedRightPinky = 0.7f;
base._inputManager.fingerSpreadRightPinky = 0f;
base._inputManager.fingerFullCurlNormalizedRightThumb = 0f;
base._inputManager.fingerFullCurlNormalizedRightIndex = 0f;
base._inputManager.fingerFullCurlNormalizedRightMiddle = 0f;
base._inputManager.fingerFullCurlNormalizedRightRing = 0f;
base._inputManager.fingerFullCurlNormalizedRightPinky = 0f;
}
}

View file

@ -1,216 +1,226 @@
using ABI_RC.Core.Player;
using ABI_RC.Systems.InputManagement;
using System.Collections;
using UnityEngine;
namespace ml_lme
{
[DisallowMultipleComponent]
class LeapManager : MonoBehaviour
{
public static LeapManager Instance { get; private set; } = null;
Leap.Controller m_leapController = null;
LeapParser.LeapData m_leapData = null;
LeapTracking m_leapTracking = null;
LeapTracked m_leapTracked = null;
LeapInput m_leapInput = null;
void Awake()
{
if(Instance == null)
Instance = this;
m_leapController = new Leap.Controller();
m_leapData = new LeapParser.LeapData();
DontDestroyOnLoad(this);
m_leapController.Device += this.OnLeapDeviceInitialized;
m_leapController.DeviceFailure += this.OnLeapDeviceFailure;
m_leapController.DeviceLost += this.OnLeapDeviceLost;
m_leapController.Connect += this.OnLeapServiceConnect;
m_leapController.Disconnect += this.OnLeapServiceDisconnect;
Settings.EnabledChange += this.OnEnableChange;
Settings.TrackingModeChange += this.OnTrackingModeChange;
m_leapTracking = new GameObject("[LeapTrackingRoot]").AddComponent<LeapTracking>();
m_leapTracking.transform.parent = this.transform;
OnEnableChange(Settings.Enabled);
OnTrackingModeChange(Settings.TrackingMode);
MelonLoader.MelonCoroutines.Start(WaitForObjects());
}
void OnDestroy()
{
if(Instance == this)
Instance = null;
m_leapController.StopConnection();
m_leapController.Device -= this.OnLeapDeviceInitialized;
m_leapController.DeviceFailure -= this.OnLeapDeviceFailure;
m_leapController.DeviceLost -= this.OnLeapDeviceLost;
m_leapController.Connect -= this.OnLeapServiceConnect;
m_leapController.Disconnect -= this.OnLeapServiceDisconnect;
m_leapController.Dispose();
m_leapController = null;
if(m_leapTracking != null)
Object.Destroy(m_leapTracking);
m_leapTracking = null;
if(m_leapTracked != null)
Object.Destroy(m_leapTracked);
m_leapTracked = null;
if(m_leapInput != null)
{
if(CVRInputManager.Instance != null)
CVRInputManager.Instance.DestroyInputModule(m_leapInput);
else
m_leapInput.ModuleDestroyed();
}
m_leapInput = null;
Settings.EnabledChange -= this.OnEnableChange;
Settings.TrackingModeChange -= this.OnTrackingModeChange;
}
IEnumerator WaitForObjects()
{
while(CVRInputManager.Instance == null)
yield return null;
while(PlayerSetup.Instance == null)
yield return null;
while(LeapTracking.Instance == null)
yield return null;
m_leapInput = new LeapInput();
CVRInputManager.Instance.AddInputModule(m_leapInput);
m_leapTracked = PlayerSetup.Instance.gameObject.AddComponent<LeapTracked>();
}
void Update()
{
if(Settings.Enabled)
{
m_leapData.Reset();
if(m_leapController.IsConnected)
{
Leap.Frame l_frame = m_leapController.Frame();
LeapParser.ParseFrame(l_frame, m_leapData);
}
}
}
public LeapParser.LeapData GetLatestData() => m_leapData;
// Device events
void OnLeapDeviceInitialized(object p_sender, Leap.DeviceEventArgs p_args)
{
if(Settings.Enabled)
{
m_leapController.SubscribeToDeviceEvents(p_args.Device);
UpdateDeviceTrackingMode();
}
Utils.ShowHUDNotification("Leap Motion Extension", "Device initialized");
}
void OnLeapDeviceFailure(object p_sender, Leap.DeviceFailureEventArgs p_args)
{
Utils.ShowHUDNotification("Leap Motion Extension", "Device failure", "Code " + p_args.ErrorCode + ": " + p_args.ErrorMessage);
}
void OnLeapDeviceLost(object p_sender, Leap.DeviceEventArgs p_args)
{
m_leapController.UnsubscribeFromDeviceEvents(p_args.Device);
Utils.ShowHUDNotification("Leap Motion Extension", "Device lost");
}
void OnLeapServiceConnect(object p_sender, Leap.ConnectionEventArgs p_args)
{
Utils.ShowHUDNotification("Leap Motion Extension", "Service connected");
}
void OnLeapServiceDisconnect(object p_sender, Leap.ConnectionLostEventArgs p_args)
{
Utils.ShowHUDNotification("Leap Motion Extension", "Service disconnected");
}
// Settings
void OnEnableChange(bool p_state)
{
if(p_state)
{
m_leapController.StartConnection();
UpdateDeviceTrackingMode();
}
else
m_leapController.StopConnection();
}
void OnTrackingModeChange(Settings.LeapTrackingMode p_mode)
{
if(Settings.Enabled)
UpdateDeviceTrackingMode();
}
// Game events
internal void OnAvatarClear()
{
if(m_leapTracking != null)
m_leapTracking.OnAvatarClear();
if(m_leapTracked != null)
m_leapTracked.OnAvatarClear();
}
internal void OnAvatarSetup()
{
if(m_leapTracking != null)
m_leapTracking.OnAvatarSetup();
m_leapInput?.OnAvatarSetup();
if(m_leapTracked != null)
m_leapTracked.OnAvatarSetup();
}
internal void OnRayScale(float p_scale)
{
m_leapInput?.OnRayScale(p_scale);
}
internal void OnPlayspaceScale(float p_relation)
{
if(m_leapTracking != null)
m_leapTracking.OnPlayspaceScale(p_relation);
}
// Arbitrary
void UpdateDeviceTrackingMode()
{
m_leapController.ClearPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP, null);
m_leapController.ClearPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_HMD, null);
switch(Settings.TrackingMode)
{
case Settings.LeapTrackingMode.Screentop:
m_leapController.SetPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP, null);
break;
case Settings.LeapTrackingMode.HMD:
m_leapController.SetPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_HMD, null);
break;
}
}
}
}
using ABI.CCK.Components;
using ABI_RC.Core.Player;
using ABI_RC.Systems.InputManagement;
using System.Collections;
using UnityEngine;
namespace ml_lme
{
[DisallowMultipleComponent]
class LeapManager : MonoBehaviour
{
public static LeapManager Instance { get; private set; } = null;
Leap.Controller m_leapController = null;
LeapParser.LeapData m_leapData = null;
LeapTracking m_leapTracking = null;
LeapTracked m_leapTracked = null;
LeapInput m_leapInput = null;
void Awake()
{
if(Instance == null)
Instance = this;
m_leapController = new Leap.Controller();
m_leapData = new LeapParser.LeapData();
DontDestroyOnLoad(this);
m_leapController.Device += this.OnLeapDeviceInitialized;
m_leapController.DeviceFailure += this.OnLeapDeviceFailure;
m_leapController.DeviceLost += this.OnLeapDeviceLost;
m_leapController.Connect += this.OnLeapServiceConnect;
m_leapController.Disconnect += this.OnLeapServiceDisconnect;
Settings.EnabledChange += this.OnEnableChange;
Settings.TrackingModeChange += this.OnTrackingModeChange;
m_leapTracking = new GameObject("[LeapTrackingRoot]").AddComponent<LeapTracking>();
m_leapTracking.transform.parent = this.transform;
OnEnableChange(Settings.Enabled);
OnTrackingModeChange(Settings.TrackingMode);
MelonLoader.MelonCoroutines.Start(WaitForObjects());
}
void OnDestroy()
{
if(Instance == this)
Instance = null;
m_leapController.StopConnection();
m_leapController.Device -= this.OnLeapDeviceInitialized;
m_leapController.DeviceFailure -= this.OnLeapDeviceFailure;
m_leapController.DeviceLost -= this.OnLeapDeviceLost;
m_leapController.Connect -= this.OnLeapServiceConnect;
m_leapController.Disconnect -= this.OnLeapServiceDisconnect;
m_leapController.Dispose();
m_leapController = null;
if(m_leapTracking != null)
Object.Destroy(m_leapTracking);
m_leapTracking = null;
if(m_leapTracked != null)
Object.Destroy(m_leapTracked);
m_leapTracked = null;
if(m_leapInput != null)
{
if(CVRInputManager.Instance != null)
CVRInputManager.Instance.DestroyInputModule(m_leapInput);
else
m_leapInput.ModuleDestroyed();
}
m_leapInput = null;
Settings.EnabledChange -= this.OnEnableChange;
Settings.TrackingModeChange -= this.OnTrackingModeChange;
}
IEnumerator WaitForObjects()
{
while(CVRInputManager.Instance == null)
yield return null;
while(PlayerSetup.Instance == null)
yield return null;
while(LeapTracking.Instance == null)
yield return null;
m_leapInput = new LeapInput();
CVRInputManager.Instance.AddInputModule(m_leapInput);
m_leapTracked = PlayerSetup.Instance.gameObject.AddComponent<LeapTracked>();
}
void Update()
{
if(Settings.Enabled)
{
m_leapData.Reset();
if(m_leapController.IsConnected)
{
Leap.Frame l_frame = m_leapController.Frame();
LeapParser.ParseFrame(l_frame, m_leapData);
}
}
}
public LeapParser.LeapData GetLatestData() => m_leapData;
// Device events
void OnLeapDeviceInitialized(object p_sender, Leap.DeviceEventArgs p_args)
{
if(Settings.Enabled)
{
m_leapController.SubscribeToDeviceEvents(p_args.Device);
UpdateDeviceTrackingMode();
}
Utils.ShowHUDNotification("Leap Motion Extension", "Device initialized");
}
void OnLeapDeviceFailure(object p_sender, Leap.DeviceFailureEventArgs p_args)
{
Utils.ShowHUDNotification("Leap Motion Extension", "Device failure", "Code " + p_args.ErrorCode + ": " + p_args.ErrorMessage);
}
void OnLeapDeviceLost(object p_sender, Leap.DeviceEventArgs p_args)
{
m_leapController.UnsubscribeFromDeviceEvents(p_args.Device);
Utils.ShowHUDNotification("Leap Motion Extension", "Device lost");
}
void OnLeapServiceConnect(object p_sender, Leap.ConnectionEventArgs p_args)
{
Utils.ShowHUDNotification("Leap Motion Extension", "Service connected");
}
void OnLeapServiceDisconnect(object p_sender, Leap.ConnectionLostEventArgs p_args)
{
Utils.ShowHUDNotification("Leap Motion Extension", "Service disconnected");
}
// Settings
void OnEnableChange(bool p_state)
{
if(p_state)
{
m_leapController.StartConnection();
UpdateDeviceTrackingMode();
}
else
m_leapController.StopConnection();
}
void OnTrackingModeChange(Settings.LeapTrackingMode p_mode)
{
if(Settings.Enabled)
UpdateDeviceTrackingMode();
}
// Game events
internal void OnAvatarClear()
{
if(m_leapTracking != null)
m_leapTracking.OnAvatarClear();
if(m_leapTracked != null)
m_leapTracked.OnAvatarClear();
}
internal void OnAvatarSetup()
{
if(m_leapTracking != null)
m_leapTracking.OnAvatarSetup();
if(m_leapTracked != null)
m_leapTracked.OnAvatarSetup();
}
internal void OnAvatarReinitialize()
{
if(m_leapTracked != null)
m_leapTracked.OnAvatarReinitialize();
}
internal void OnRayScale(float p_scale)
{
m_leapInput?.OnRayScale(p_scale);
}
internal void OnPlayspaceScale(float p_relation)
{
if(m_leapTracking != null)
m_leapTracking.OnPlayspaceScale(p_relation);
}
internal void OnPickupGrab(CVRPickupObject p_pickup)
{
m_leapInput?.OnPickupGrab(p_pickup);
}
// Arbitrary
void UpdateDeviceTrackingMode()
{
m_leapController.ClearPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP, null);
m_leapController.ClearPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_HMD, null);
switch(Settings.TrackingMode)
{
case Settings.LeapTrackingMode.Screentop:
m_leapController.SetPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_SCREENTOP, null);
break;
case Settings.LeapTrackingMode.HMD:
m_leapController.SetPolicy(Leap.Controller.PolicyFlag.POLICY_OPTIMIZE_HMD, null);
break;
}
}
}
}

View file

@ -1,12 +1,14 @@
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using RootMotion.FinalIK;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ml_lme
{
[DisallowMultipleComponent]
[DefaultExecutionOrder(999999)]
class LeapTracked : MonoBehaviour
{
struct IKInfo
@ -19,18 +21,64 @@ namespace ml_lme
public Transform m_rightElbowTarget;
}
static readonly float[] ms_tposeMuscles = typeof(ABI_RC.Systems.IK.SubSystems.BodySystem).GetField("TPoseMuscles", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as float[];
struct FingerBoneInfo
{
public LeapHand.FingerBone m_bone;
public Transform m_targetBone;
public Transform m_sourceBone;
public Quaternion m_offset;
}
static readonly Quaternion ms_offsetLeft = Quaternion.Euler(0f, 90f, 0f);
static readonly Quaternion ms_offsetRight = Quaternion.Euler(0f, 270f, 0f);
static readonly (HumanBodyBones, LeapHand.FingerBone, bool)[] ms_fingerBonesLinks =
{
(HumanBodyBones.LeftThumbProximal, LeapHand.FingerBone.ThumbProximal, true),
(HumanBodyBones.LeftThumbIntermediate, LeapHand.FingerBone.ThumbIntermediate, true),
(HumanBodyBones.LeftThumbDistal, LeapHand.FingerBone.ThumbDistal, true),
(HumanBodyBones.LeftIndexProximal, LeapHand.FingerBone.IndexProximal, true),
(HumanBodyBones.LeftIndexIntermediate, LeapHand.FingerBone.IndexIntermediate, true),
(HumanBodyBones.LeftIndexDistal, LeapHand.FingerBone.IndexDistal, true),
(HumanBodyBones.LeftMiddleProximal, LeapHand.FingerBone.MiddleProximal, true),
(HumanBodyBones.LeftMiddleIntermediate, LeapHand.FingerBone.MiddleIntermediate, true),
(HumanBodyBones.LeftMiddleDistal, LeapHand.FingerBone.MiddleDistal, true),
(HumanBodyBones.LeftRingProximal, LeapHand.FingerBone.RingProximal, true),
(HumanBodyBones.LeftRingIntermediate, LeapHand.FingerBone.RingIntermediate, true),
(HumanBodyBones.LeftRingDistal, LeapHand.FingerBone.RingDistal, true),
(HumanBodyBones.LeftLittleProximal, LeapHand.FingerBone.PinkyProximal, true),
(HumanBodyBones.LeftLittleIntermediate, LeapHand.FingerBone.PinkyIntermediate, true),
(HumanBodyBones.LeftLittleDistal, LeapHand.FingerBone.PinkyDistal, true),
(HumanBodyBones.RightThumbProximal, LeapHand.FingerBone.ThumbProximal, false),
(HumanBodyBones.RightThumbIntermediate, LeapHand.FingerBone.ThumbIntermediate, false),
(HumanBodyBones.RightThumbDistal, LeapHand.FingerBone.ThumbDistal, false),
(HumanBodyBones.RightIndexProximal, LeapHand.FingerBone.IndexProximal, false),
(HumanBodyBones.RightIndexIntermediate, LeapHand.FingerBone.IndexIntermediate, false),
(HumanBodyBones.RightIndexDistal, LeapHand.FingerBone.IndexDistal, false),
(HumanBodyBones.RightMiddleProximal, LeapHand.FingerBone.MiddleProximal, false),
(HumanBodyBones.RightMiddleIntermediate, LeapHand.FingerBone.MiddleIntermediate, false),
(HumanBodyBones.RightMiddleDistal, LeapHand.FingerBone.MiddleDistal, false),
(HumanBodyBones.RightRingProximal, LeapHand.FingerBone.RingProximal, false),
(HumanBodyBones.RightRingIntermediate, LeapHand.FingerBone.RingIntermediate, false),
(HumanBodyBones.RightRingDistal, LeapHand.FingerBone.RingDistal, false),
(HumanBodyBones.RightLittleProximal, LeapHand.FingerBone.PinkyProximal, false),
(HumanBodyBones.RightLittleIntermediate, LeapHand.FingerBone.PinkyIntermediate, false),
(HumanBodyBones.RightLittleDistal, LeapHand.FingerBone.PinkyDistal, false),
};
public static readonly float[] ms_lastLeftFingerBones = new float[20];
public static readonly float[] ms_lastRightFingerBones = new float[20];
bool m_inVR = false;
VRIK m_vrIK = null;
Transform m_hips = null;
bool m_enabled = true;
bool m_fingersOnly = false;
bool m_trackElbows = true;
Transform m_leftHand = null;
Transform m_rightHand = null;
IKInfo m_vrIKInfo;
ArmIK m_leftArmIK = null;
ArmIK m_rightArmIK = null;
@ -41,18 +89,30 @@ namespace ml_lme
bool m_leftTargetActive = false; // VRIK only
bool m_rightTargetActive = false; // VRIK only
readonly List<FingerBoneInfo> m_leftFingerBones = null;
readonly List<FingerBoneInfo> m_rightFingerBones = null;
Quaternion m_leftWristOffset;
Quaternion m_rightWristOffset;
internal LeapTracked()
{
m_leftFingerBones = new List<FingerBoneInfo>();
m_rightFingerBones = new List<FingerBoneInfo>();
}
// Unity events
void Start()
{
m_inVR = Utils.IsInVR();
m_leftHandTarget = new GameObject("RotationTarget").transform;
m_leftHandTarget.parent = LeapTracking.Instance.GetLeftHand();
m_leftHandTarget.parent = LeapTracking.Instance.GetLeftHand().GetRoot();
m_leftHandTarget.localPosition = Vector3.zero;
m_leftHandTarget.localRotation = Quaternion.identity;
m_rightHandTarget = new GameObject("RotationTarget").transform;
m_rightHandTarget.parent = LeapTracking.Instance.GetRightHand();
m_rightHandTarget.parent = LeapTracking.Instance.GetRightHand().GetRoot();
m_rightHandTarget.localPosition = Vector3.zero;
m_rightHandTarget.localRotation = Quaternion.identity;
@ -67,13 +127,7 @@ namespace ml_lme
void OnDestroy()
{
if(m_leftArmIK != null)
Destroy(m_leftArmIK);
m_leftArmIK = null;
if(m_rightArmIK != null)
Destroy(m_rightArmIK);
m_rightArmIK = null;
RemoveArmIK();
if(m_leftHandTarget != null)
Destroy(m_leftHandTarget);
@ -122,19 +176,47 @@ namespace ml_lme
void LateUpdate()
{
if(m_enabled && !m_inVR && (m_poseHandler != null))
if(m_enabled && (m_poseHandler != null))
{
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
Vector3 l_hipsLocalPos = m_hips.localPosition;
Quaternion l_hipsLocalRot = m_hips.localRotation;
if(l_data.m_leftHand.m_present)
{
Transform l_leapWrist = LeapTracking.Instance.GetLeftHand().GetWrist();
Quaternion l_turnBack = (m_leftHand.rotation * m_leftWristOffset) * Quaternion.Inverse(l_leapWrist.rotation);
foreach(var l_info in m_leftFingerBones)
l_info.m_targetBone.rotation = l_turnBack * (l_info.m_sourceBone.rotation * l_info.m_offset);
}
if(l_data.m_rightHand.m_present)
{
Transform l_leapWrist = LeapTracking.Instance.GetRightHand().GetWrist();
Quaternion l_turnBack = (m_rightHand.rotation * m_rightWristOffset) * Quaternion.Inverse(l_leapWrist.rotation);
foreach(var l_info in m_rightFingerBones)
l_info.m_targetBone.rotation = l_turnBack * (l_info.m_sourceBone.rotation * l_info.m_offset);
}
m_poseHandler.GetHumanPose(ref m_pose);
UpdateFingers(l_data);
m_poseHandler.SetHumanPose(ref m_pose);
m_hips.localPosition = l_hipsLocalPos;
m_hips.localRotation = l_hipsLocalRot;
if(l_data.m_leftHand.m_present)
{
for(int i = 0; i < 5; i++)
{
int l_offset = i * 4;
ms_lastLeftFingerBones[l_offset] = m_pose.muscles[(int)MuscleIndex.LeftThumb1Stretched + l_offset];
ms_lastLeftFingerBones[l_offset + 1] = m_pose.muscles[(int)MuscleIndex.LeftThumb2Stretched + l_offset];
ms_lastLeftFingerBones[l_offset + 2] = m_pose.muscles[(int)MuscleIndex.LeftThumb3Stretched + l_offset];
ms_lastLeftFingerBones[l_offset + 3] = m_pose.muscles[(int)MuscleIndex.LeftThumbSpread + l_offset];
}
}
if(l_data.m_rightHand.m_present)
{
for(int i = 0; i < 5; i++)
{
int l_offset = i * 4;
ms_lastRightFingerBones[l_offset] = m_pose.muscles[(int)MuscleIndex.RightThumb1Stretched + l_offset];
ms_lastRightFingerBones[l_offset + 1] = m_pose.muscles[(int)MuscleIndex.RightThumb2Stretched + l_offset];
ms_lastRightFingerBones[l_offset + 2] = m_pose.muscles[(int)MuscleIndex.RightThumb3Stretched + l_offset];
ms_lastRightFingerBones[l_offset + 3] = m_pose.muscles[(int)MuscleIndex.RightThumbSpread + l_offset];
}
}
}
}
@ -142,20 +224,26 @@ namespace ml_lme
internal void OnAvatarClear()
{
m_vrIK = null;
m_hips = null;
m_leftArmIK = null;
m_rightArmIK = null;
m_leftTargetActive = false;
m_rightTargetActive = false;
if(!m_inVR)
m_poseHandler?.Dispose();
m_poseHandler?.Dispose();
m_poseHandler = null;
m_leftHandTarget.localPosition = Vector3.zero;
m_leftHandTarget.localRotation = Quaternion.identity;
m_rightHandTarget.localPosition = Vector3.zero;
m_rightHandTarget.localRotation = Quaternion.identity;
m_leftFingerBones.Clear();
m_rightFingerBones.Clear();
m_leftHand = null;
m_rightHand = null;
m_leftWristOffset = Quaternion.identity;
m_rightWristOffset = Quaternion.identity;
}
internal void OnAvatarSetup()
@ -165,84 +253,53 @@ namespace ml_lme
if(PlayerSetup.Instance._animator.isHuman)
{
Vector3 l_hipsPos = Vector3.zero;
m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
if(m_hips != null)
l_hipsPos = m_hips.localPosition;
m_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._animator.transform);
m_poseHandler.GetHumanPose(ref m_pose);
if(!m_inVR)
if(m_inVR)
{
// Force desktop avatar into T-Pose
m_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._avatar.transform);
m_poseHandler.GetHumanPose(ref m_pose);
HumanPose l_tPose = new HumanPose
{
bodyPosition = m_pose.bodyPosition,
bodyRotation = m_pose.bodyRotation,
muscles = new float[m_pose.muscles.Length]
};
for(int i = 0; i < l_tPose.muscles.Length; i++)
l_tPose.muscles[i] = ms_tposeMuscles[i];
m_poseHandler.SetHumanPose(ref l_tPose);
}
Transform l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
if(l_hand != null)
m_leftHandTarget.localRotation = ms_offsetLeft * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand);
if(l_hand != null)
m_rightHandTarget.localRotation = ms_offsetRight * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
if(m_vrIK == null)
{
Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest);
if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest);
if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine);
m_leftArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_leftArmIK.solver.isLeft = true;
m_leftArmIK.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand),
PlayerSetup.Instance._animator.transform
);
m_leftArmIK.solver.arm.target = m_leftHandTarget;
m_leftArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetLeftElbow();
m_leftArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_leftArmIK.enabled = (m_enabled && !m_fingersOnly);
m_rightArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_rightArmIK.solver.isLeft = false;
m_rightArmIK.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand),
PlayerSetup.Instance._animator.transform
);
m_rightArmIK.solver.arm.target = m_rightHandTarget;
m_rightArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetRightElbow();
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_rightArmIK.enabled = (m_enabled && !m_fingersOnly);
m_poseHandler?.SetHumanPose(ref m_pose);
PlayerSetup.Instance._avatar.transform.localPosition = Vector3.zero;
PlayerSetup.Instance._avatar.transform.localRotation = Quaternion.identity;
}
else
{
m_vrIK.solver.OnPreUpdate += this.OnIKPreUpdate;
m_vrIK.solver.OnPostUpdate += this.OnIKPostUpdate;
}
PoseHelper.ForceTPose(PlayerSetup.Instance._animator);
if(m_hips != null)
m_hips.localPosition = l_hipsPos;
m_leftHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
m_leftHandTarget.localRotation = ms_offsetLeft * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_leftHand.rotation);
m_rightHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand);
m_rightHandTarget.localRotation = ms_offsetRight * (Quaternion.Inverse(PlayerSetup.Instance._avatar.transform.rotation) * m_rightHand.rotation);
ParseFingersBones();
if(m_vrIK != null)
{
m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
}
else
SetupArmIK();
}
}
internal void OnAvatarReinitialize()
{
// Old VRIK is destroyed by game
m_inVR = Utils.IsInVR();
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
if(m_inVR)
RemoveArmIK();
if(m_vrIK != null)
{
m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
}
else
{
PoseHelper.ForceTPose(PlayerSetup.Instance._animator);
SetupArmIK();
}
}
@ -335,6 +392,56 @@ namespace ml_lme
m_rightTargetActive = false;
}
void SetupArmIK()
{
Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest);
if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest);
if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine);
m_leftArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_leftArmIK.solver.isLeft = true;
m_leftArmIK.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand),
PlayerSetup.Instance._animator.transform
);
m_leftArmIK.solver.arm.target = m_leftHandTarget;
m_leftArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetLeftElbow();
m_leftArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_leftArmIK.enabled = (m_enabled && !m_fingersOnly);
m_rightArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_rightArmIK.solver.isLeft = false;
m_rightArmIK.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand),
PlayerSetup.Instance._animator.transform
);
m_rightArmIK.solver.arm.target = m_rightHandTarget;
m_rightArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetRightElbow();
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_rightArmIK.enabled = (m_enabled && !m_fingersOnly);
}
void RemoveArmIK()
{
if(m_leftArmIK != null)
Object.Destroy(m_leftArmIK);
m_leftArmIK = null;
if(m_rightArmIK != null)
Object.Destroy(m_rightArmIK);
m_rightArmIK = null;
}
void RefreshArmIK()
{
if((m_leftArmIK != null) && (m_rightArmIK != null))
@ -344,69 +451,33 @@ namespace ml_lme
}
}
void UpdateFingers(LeapParser.LeapData p_data)
void ParseFingersBones()
{
if(p_data.m_leftHand.m_present)
LeapTracking.Instance.GetLeftHand().Reset();
LeapTracking.Instance.GetLeftHand().GetWrist().rotation = PlayerSetup.Instance.transform.rotation * ms_offsetRight; // Weird, but that's how it works
m_leftWristOffset = Quaternion.Inverse(m_leftHand.rotation) * LeapTracking.Instance.GetLeftHand().GetWrist().rotation;
LeapTracking.Instance.GetRightHand().Reset();
LeapTracking.Instance.GetRightHand().GetWrist().rotation = PlayerSetup.Instance.transform.rotation * ms_offsetLeft; // Weird, but that's how it works
m_rightWristOffset = Quaternion.Inverse(m_rightHand.rotation) * LeapTracking.Instance.GetRightHand().GetWrist().rotation;
foreach(var l_link in ms_fingerBonesLinks)
{
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb1Stretched, -0.5f - p_data.m_leftHand.m_bends[0]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb2Stretched, 0.7f - p_data.m_leftHand.m_bends[0] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb3Stretched, 0.7f - p_data.m_leftHand.m_bends[0] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumbSpread, -p_data.m_leftHand.m_spreads[0]);
Transform l_transform = PlayerSetup.Instance._animator.GetBoneTransform(l_link.Item1);
if(l_transform != null)
{
FingerBoneInfo l_info = new FingerBoneInfo();
l_info.m_bone = l_link.Item2;
l_info.m_targetBone = l_transform;
l_info.m_sourceBone = (l_link.Item3 ? LeapTracking.Instance.GetLeftHand().GetFingersBone(l_link.Item2) : LeapTracking.Instance.GetRightHand().GetFingersBone(l_link.Item2));
l_info.m_offset = Quaternion.Inverse(l_info.m_sourceBone.rotation) * l_info.m_targetBone.rotation;
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex1Stretched, 0.5f - p_data.m_leftHand.m_bends[1]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex2Stretched, 0.7f - p_data.m_leftHand.m_bends[1] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex3Stretched, 0.7f - p_data.m_leftHand.m_bends[1] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndexSpread, p_data.m_leftHand.m_spreads[1]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle1Stretched, 0.5f - p_data.m_leftHand.m_bends[2]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle2Stretched, 0.7f - p_data.m_leftHand.m_bends[2] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle3Stretched, 0.7f - p_data.m_leftHand.m_bends[2] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddleSpread, p_data.m_leftHand.m_spreads[2]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing1Stretched, 0.5f - p_data.m_leftHand.m_bends[3]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing2Stretched, 0.7f - p_data.m_leftHand.m_bends[3] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing3Stretched, 0.7f - p_data.m_leftHand.m_bends[3] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRingSpread, -p_data.m_leftHand.m_spreads[3]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle1Stretched, 0.5f - p_data.m_leftHand.m_bends[4]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle2Stretched, 0.7f - p_data.m_leftHand.m_bends[4] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle3Stretched, 0.7f - p_data.m_leftHand.m_bends[4] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittleSpread, -p_data.m_leftHand.m_spreads[4]);
if(l_link.Item3)
m_leftFingerBones.Add(l_info);
else
m_rightFingerBones.Add(l_info);
}
}
if(p_data.m_rightHand.m_present)
{
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb1Stretched, -0.5f - p_data.m_rightHand.m_bends[0]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb2Stretched, 0.7f - p_data.m_rightHand.m_bends[0] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb3Stretched, 0.7f - p_data.m_rightHand.m_bends[0] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumbSpread, -p_data.m_rightHand.m_spreads[0]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex1Stretched, 0.5f - p_data.m_rightHand.m_bends[1]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex2Stretched, 0.7f - p_data.m_rightHand.m_bends[1] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex3Stretched, 0.7f - p_data.m_rightHand.m_bends[1] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndexSpread, p_data.m_rightHand.m_spreads[1]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle1Stretched, 0.5f - p_data.m_rightHand.m_bends[2]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle2Stretched, 0.7f - p_data.m_rightHand.m_bends[2] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle3Stretched, 0.7f - p_data.m_rightHand.m_bends[2] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddleSpread, p_data.m_rightHand.m_spreads[2]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing1Stretched, 0.5f - p_data.m_rightHand.m_bends[3]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing2Stretched, 0.7f - p_data.m_rightHand.m_bends[3] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing3Stretched, 0.7f - p_data.m_rightHand.m_bends[3] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRingSpread, -p_data.m_rightHand.m_spreads[3]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle1Stretched, 0.5f - p_data.m_rightHand.m_bends[4]);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle2Stretched, 0.7f - p_data.m_rightHand.m_bends[4] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle3Stretched, 0.7f - p_data.m_rightHand.m_bends[4] * 2f);
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittleSpread, -p_data.m_rightHand.m_spreads[4]);
}
}
static void UpdatePoseMuscle(ref HumanPose p_pose, int p_index, float p_value)
{
if(p_pose.muscles.Length > p_index)
p_pose.muscles[p_index] = p_value;
}
}
}

View file

@ -1,288 +1,280 @@
using ABI_RC.Core.Player;
using System.Collections;
using UnityEngine;
namespace ml_lme
{
[DisallowMultipleComponent]
class LeapTracking : MonoBehaviour
{
public static LeapTracking Instance { get; private set; } = null;
static Quaternion ms_dummyRotation = Quaternion.identity;
static readonly Quaternion ms_hmdRotation = new Quaternion(0f, 0.7071068f, 0.7071068f, 0f);
static readonly Quaternion ms_screentopRotation = new Quaternion(0f, 0f, -1f, 0f);
bool m_inVR = false;
GameObject m_leapHandLeft = null;
GameObject m_leapHandRight = null;
GameObject m_leapElbowLeft = null;
GameObject m_leapElbowRight = null;
GameObject m_leapControllerModel = null;
GameObject m_visualHands = null;
VisualHand m_visualHandLeft = null;
VisualHand m_visualHandRight = null;
float m_scaleRelation = 1f;
void Start()
{
if(Instance == null)
Instance = this;
m_inVR = Utils.IsInVR();
m_leapHandLeft = new GameObject("LeapHandLeft");
m_leapHandLeft.transform.parent = this.transform;
m_leapHandLeft.transform.localPosition = Vector3.zero;
m_leapHandLeft.transform.localRotation = Quaternion.identity;
m_leapHandRight = new GameObject("LeapHandRight");
m_leapHandRight.transform.parent = this.transform;
m_leapHandRight.transform.localPosition = Vector3.zero;
m_leapHandRight.transform.localRotation = Quaternion.identity;
m_leapElbowLeft = new GameObject("LeapElbowLeft");
m_leapElbowLeft.transform.parent = this.transform;
m_leapElbowLeft.transform.localPosition = Vector3.zero;
m_leapElbowLeft.transform.localRotation = Quaternion.identity;
m_leapElbowRight = new GameObject("LeapElbowRight");
m_leapElbowRight.transform.parent = this.transform;
m_leapElbowRight.transform.localPosition = Vector3.zero;
m_leapElbowRight.transform.localRotation = Quaternion.identity;
m_leapControllerModel = AssetsHandler.GetAsset("assets/models/leapmotion/leap_motion_1_0.obj");
if(m_leapControllerModel != null)
{
m_leapControllerModel.name = "LeapModel";
m_leapControllerModel.transform.parent = this.transform;
m_leapControllerModel.transform.localPosition = Vector3.zero;
m_leapControllerModel.transform.localRotation = Quaternion.identity;
}
m_visualHands = AssetsHandler.GetAsset("assets/models/hands/leaphands.prefab");
if(m_visualHands != null)
{
m_visualHands.name = "VisualHands";
m_visualHands.transform.parent = this.transform;
m_visualHands.transform.localPosition = Vector3.zero;
m_visualHands.transform.localRotation = Quaternion.identity;
m_visualHandLeft = new VisualHand(m_visualHands.transform.Find("HandL"), true);
m_visualHandRight = new VisualHand(m_visualHands.transform.Find("HandR"), false);
}
Settings.DesktopOffsetChange += this.OnDesktopOffsetChange;
Settings.ModelVisibilityChange += this.OnModelVisibilityChange;
Settings.VisualHandsChange += this.OnVisualHandsChange;
Settings.TrackingModeChange += this.OnTrackingModeChange;
Settings.RootAngleChange += this.OnRootAngleChange;
Settings.HeadAttachChange += this.OnHeadAttachChange;
Settings.HeadOffsetChange += this.OnHeadOffsetChange;
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
OnModelVisibilityChange(Settings.ModelVisibility);
OnVisualHandsChange(Settings.VisualHands);
OnTrackingModeChange(Settings.TrackingMode);
OnRootAngleChange(Settings.RootAngle);
}
IEnumerator WaitForLocalPlayer()
{
while(PlayerSetup.Instance == null)
yield return null;
OnHeadAttachChange(Settings.HeadAttach);
}
void OnDestroy()
{
if(Instance == this)
Instance = null;
if(m_leapHandLeft != null)
Object.Destroy(m_leapHandLeft);
m_leapHandLeft = null;
if(m_leapHandRight != null)
Object.Destroy(m_leapHandRight);
m_leapHandRight = null;
if(m_leapElbowLeft != null)
Object.Destroy(m_leapElbowLeft);
m_leapElbowLeft = null;
if(m_leapElbowRight != null)
Object.Destroy(m_leapElbowRight);
m_leapElbowRight = null;
if(m_leapControllerModel != null)
Object.Destroy(m_leapControllerModel);
m_leapControllerModel = null;
if(m_visualHands != null)
Object.Destroy(m_visualHands);
m_visualHands = null;
m_visualHandLeft = null;
m_visualHandRight = null;
Settings.DesktopOffsetChange -= this.OnDesktopOffsetChange;
Settings.ModelVisibilityChange -= this.OnModelVisibilityChange;
Settings.VisualHandsChange -= this.OnVisualHandsChange;
Settings.TrackingModeChange -= this.OnTrackingModeChange;
Settings.RootAngleChange -= this.OnRootAngleChange;
Settings.HeadAttachChange -= this.OnHeadAttachChange;
Settings.HeadOffsetChange -= this.OnHeadOffsetChange;
}
void Update()
{
if(Settings.Enabled)
{
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
if(l_data.m_leftHand.m_present)
{
OrientationAdjustment(ref l_data.m_leftHand.m_position, ref l_data.m_leftHand.m_rotation, Settings.TrackingMode);
for(int i = 0; i < 20; i++)
OrientationAdjustment(ref l_data.m_leftHand.m_fingerPosition[i], ref l_data.m_leftHand.m_fingerRotation[i], Settings.TrackingMode);
m_leapHandLeft.transform.localPosition = l_data.m_leftHand.m_position;
m_leapHandLeft.transform.localRotation = l_data.m_leftHand.m_rotation;
OrientationAdjustment(ref l_data.m_leftHand.m_elbowPosition, ref ms_dummyRotation, Settings.TrackingMode);
m_leapElbowLeft.transform.localPosition = l_data.m_leftHand.m_elbowPosition;
if(Settings.VisualHands)
m_visualHandLeft?.Update(l_data.m_leftHand);
}
if(l_data.m_rightHand.m_present)
{
OrientationAdjustment(ref l_data.m_rightHand.m_position, ref l_data.m_rightHand.m_rotation, Settings.TrackingMode);
for(int i = 0; i < 20; i++)
OrientationAdjustment(ref l_data.m_rightHand.m_fingerPosition[i], ref l_data.m_rightHand.m_fingerRotation[i], Settings.TrackingMode);
m_leapHandRight.transform.localPosition = l_data.m_rightHand.m_position;
m_leapHandRight.transform.localRotation = l_data.m_rightHand.m_rotation;
OrientationAdjustment(ref l_data.m_rightHand.m_elbowPosition, ref ms_dummyRotation, Settings.TrackingMode);
m_leapElbowRight.transform.localPosition = l_data.m_rightHand.m_elbowPosition;
if(Settings.VisualHands)
m_visualHandRight?.Update(l_data.m_rightHand);
}
}
}
public Transform GetLeftHand() => m_leapHandLeft.transform;
public Transform GetRightHand() => m_leapHandRight.transform;
public Transform GetLeftElbow() => m_leapElbowLeft.transform;
public Transform GetRightElbow() => m_leapElbowRight.transform;
// Settings
void OnDesktopOffsetChange(Vector3 p_offset)
{
if(!Settings.HeadAttach)
this.transform.localPosition = p_offset * (!m_inVR ? m_scaleRelation : 1f);
}
void OnModelVisibilityChange(bool p_state)
{
m_leapControllerModel.SetActive(p_state);
}
void OnVisualHandsChange(bool p_state)
{
m_visualHands.SetActive(p_state);
}
void OnTrackingModeChange(Settings.LeapTrackingMode p_mode)
{
switch(p_mode)
{
case Settings.LeapTrackingMode.Screentop:
m_leapControllerModel.transform.localRotation = Quaternion.Euler(0f, 0f, 180f);
break;
case Settings.LeapTrackingMode.Desktop:
m_leapControllerModel.transform.localRotation = Quaternion.identity;
break;
case Settings.LeapTrackingMode.HMD:
m_leapControllerModel.transform.localRotation = Quaternion.Euler(270f, 180f, 0f);
break;
}
}
void OnRootAngleChange(Vector3 p_angle)
{
this.transform.localRotation = Quaternion.Euler(p_angle);
}
void OnHeadAttachChange(bool p_state)
{
if(!m_inVR)
{
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 OnHeadOffsetChange(Vector3 p_offset)
{
if(Settings.HeadAttach)
this.transform.localPosition = p_offset * (!m_inVR ? m_scaleRelation : 1f);
}
// Game events
internal void OnAvatarClear()
{
m_scaleRelation = 1f;
OnHeadAttachChange(Settings.HeadAttach);
}
internal void OnAvatarSetup()
{
m_inVR = Utils.IsInVR();
OnHeadAttachChange(Settings.HeadAttach);
}
internal void OnPlayspaceScale(float p_relation)
{
m_scaleRelation = p_relation;
OnHeadAttachChange(Settings.HeadAttach);
}
static void OrientationAdjustment(ref Vector3 p_pos, ref Quaternion p_rot, Settings.LeapTrackingMode p_mode)
{
switch(p_mode)
{
case Settings.LeapTrackingMode.Screentop:
{
p_pos.x *= -1f;
p_pos.y *= -1f;
p_rot = (ms_screentopRotation * p_rot);
}
break;
case Settings.LeapTrackingMode.HMD:
{
p_pos.x *= -1f;
Utils.Swap(ref p_pos.y, ref p_pos.z);
p_rot = (ms_hmdRotation * p_rot);
}
break;
}
}
}
}
using ABI_RC.Core.Player;
using ABI_RC.Systems.VRModeSwitch;
using System.Collections;
using UnityEngine;
namespace ml_lme
{
[DisallowMultipleComponent]
class LeapTracking : MonoBehaviour
{
public static LeapTracking Instance { get; private set; } = null;
static Quaternion ms_dummyRotation = Quaternion.identity;
static readonly Quaternion ms_hmdRotation = new Quaternion(0f, 0.7071068f, 0.7071068f, 0f);
static readonly Quaternion ms_screentopRotation = new Quaternion(0f, 0f, -1f, 0f);
bool m_inVR = false;
GameObject m_leapHands = null;
LeapHand m_leapHandLeft = null;
LeapHand m_leapHandRight = null;
GameObject m_leapElbowLeft = null;
GameObject m_leapElbowRight = null;
GameObject m_leapControllerModel = null;
float m_scaleRelation = 1f;
void Start()
{
if(Instance == null)
Instance = this;
m_inVR = Utils.IsInVR();
m_leapElbowLeft = new GameObject("LeapElbowLeft");
m_leapElbowLeft.transform.parent = this.transform;
m_leapElbowLeft.transform.localPosition = Vector3.zero;
m_leapElbowLeft.transform.localRotation = Quaternion.identity;
m_leapElbowRight = new GameObject("LeapElbowRight");
m_leapElbowRight.transform.parent = this.transform;
m_leapElbowRight.transform.localPosition = Vector3.zero;
m_leapElbowRight.transform.localRotation = Quaternion.identity;
m_leapControllerModel = AssetsHandler.GetAsset("assets/models/leapmotion/leap_motion_1_0.obj");
if(m_leapControllerModel != null)
{
m_leapControllerModel.name = "LeapModel";
m_leapControllerModel.transform.parent = this.transform;
m_leapControllerModel.transform.localPosition = Vector3.zero;
m_leapControllerModel.transform.localRotation = Quaternion.identity;
}
m_leapHands = AssetsHandler.GetAsset("assets/models/leaphands/leaphands.prefab");
if(m_leapHands != null)
{
m_leapHands.name = "LeapHands";
m_leapHands.transform.parent = this.transform;
m_leapHands.transform.localPosition = Vector3.zero;
m_leapHands.transform.localRotation = Quaternion.identity;
m_leapHandLeft = new LeapHand(m_leapHands.transform.Find("HandL"), true);
m_leapHandRight = new LeapHand(m_leapHands.transform.Find("HandR"), false);
}
Settings.DesktopOffsetChange += this.OnDesktopOffsetChange;
Settings.ModelVisibilityChange += this.OnModelVisibilityChange;
Settings.VisualHandsChange += this.OnVisualHandsChange;
Settings.TrackingModeChange += this.OnTrackingModeChange;
Settings.RootAngleChange += this.OnRootAngleChange;
Settings.HeadAttachChange += this.OnHeadAttachChange;
Settings.HeadOffsetChange += this.OnHeadOffsetChange;
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
OnModelVisibilityChange(Settings.ModelVisibility);
OnVisualHandsChange(Settings.VisualHands);
OnTrackingModeChange(Settings.TrackingMode);
OnRootAngleChange(Settings.RootAngle);
VRModeSwitchEvents.OnInitializeXR.AddListener(this.OnModeSwitch);
VRModeSwitchEvents.OnDeinitializeXR.AddListener(this.OnModeSwitch);
}
IEnumerator WaitForLocalPlayer()
{
while(PlayerSetup.Instance == null)
yield return null;
OnHeadAttachChange(Settings.HeadAttach);
}
void OnDestroy()
{
if(Instance == this)
Instance = null;
if(m_leapHands != null)
Object.Destroy(m_leapHands);
m_leapHands = null;
m_leapHandLeft = null;
m_leapHandRight = null;
if(m_leapElbowLeft != null)
Object.Destroy(m_leapElbowLeft);
m_leapElbowLeft = null;
if(m_leapElbowRight != null)
Object.Destroy(m_leapElbowRight);
m_leapElbowRight = null;
if(m_leapControllerModel != null)
Object.Destroy(m_leapControllerModel);
m_leapControllerModel = null;
Settings.DesktopOffsetChange -= this.OnDesktopOffsetChange;
Settings.ModelVisibilityChange -= this.OnModelVisibilityChange;
Settings.VisualHandsChange -= this.OnVisualHandsChange;
Settings.TrackingModeChange -= this.OnTrackingModeChange;
Settings.RootAngleChange -= this.OnRootAngleChange;
Settings.HeadAttachChange -= this.OnHeadAttachChange;
Settings.HeadOffsetChange -= this.OnHeadOffsetChange;
VRModeSwitchEvents.OnInitializeXR.RemoveListener(this.OnModeSwitch);
VRModeSwitchEvents.OnDeinitializeXR.RemoveListener(this.OnModeSwitch);
}
void Update()
{
if(Settings.Enabled)
{
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
if(l_data.m_leftHand.m_present)
{
OrientationAdjustment(ref l_data.m_leftHand.m_position, ref l_data.m_leftHand.m_rotation, Settings.TrackingMode);
for(int i = 0; i < 20; i++)
OrientationAdjustment(ref l_data.m_leftHand.m_fingerPosition[i], ref l_data.m_leftHand.m_fingerRotation[i], Settings.TrackingMode);
m_leapHandLeft.GetRoot().localPosition = l_data.m_leftHand.m_position;
m_leapHandLeft.GetRoot().localRotation = l_data.m_leftHand.m_rotation;
OrientationAdjustment(ref l_data.m_leftHand.m_elbowPosition, ref ms_dummyRotation, Settings.TrackingMode);
m_leapElbowLeft.transform.localPosition = l_data.m_leftHand.m_elbowPosition;
m_leapHandLeft?.Update(l_data.m_leftHand);
}
if(l_data.m_rightHand.m_present)
{
OrientationAdjustment(ref l_data.m_rightHand.m_position, ref l_data.m_rightHand.m_rotation, Settings.TrackingMode);
for(int i = 0; i < 20; i++)
OrientationAdjustment(ref l_data.m_rightHand.m_fingerPosition[i], ref l_data.m_rightHand.m_fingerRotation[i], Settings.TrackingMode);
m_leapHandRight.GetRoot().localPosition = l_data.m_rightHand.m_position;
m_leapHandRight.GetRoot().localRotation = l_data.m_rightHand.m_rotation;
OrientationAdjustment(ref l_data.m_rightHand.m_elbowPosition, ref ms_dummyRotation, Settings.TrackingMode);
m_leapElbowRight.transform.localPosition = l_data.m_rightHand.m_elbowPosition;
m_leapHandRight?.Update(l_data.m_rightHand);
}
}
}
public LeapHand GetLeftHand() => m_leapHandLeft;
public LeapHand GetRightHand() => m_leapHandRight;
public Transform GetLeftElbow() => m_leapElbowLeft.transform;
public Transform GetRightElbow() => m_leapElbowRight.transform;
// Settings
void OnDesktopOffsetChange(Vector3 p_offset)
{
if(!Settings.HeadAttach)
this.transform.localPosition = p_offset * (!m_inVR ? m_scaleRelation : 1f);
}
void OnModelVisibilityChange(bool p_state)
{
m_leapControllerModel.SetActive(p_state);
}
void OnVisualHandsChange(bool p_state)
{
m_leapHandLeft?.SetMeshActive(p_state);
m_leapHandRight?.SetMeshActive(p_state);
}
void OnTrackingModeChange(Settings.LeapTrackingMode p_mode)
{
switch(p_mode)
{
case Settings.LeapTrackingMode.Screentop:
m_leapControllerModel.transform.localRotation = Quaternion.Euler(0f, 0f, 180f);
break;
case Settings.LeapTrackingMode.Desktop:
m_leapControllerModel.transform.localRotation = Quaternion.identity;
break;
case Settings.LeapTrackingMode.HMD:
m_leapControllerModel.transform.localRotation = Quaternion.Euler(270f, 180f, 0f);
break;
}
}
void OnRootAngleChange(Vector3 p_angle)
{
this.transform.localRotation = Quaternion.Euler(p_angle);
}
void OnHeadAttachChange(bool p_state)
{
if(!m_inVR)
{
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 OnHeadOffsetChange(Vector3 p_offset)
{
if(Settings.HeadAttach)
this.transform.localPosition = p_offset * (!m_inVR ? m_scaleRelation : 1f);
}
// Game events
internal void OnAvatarClear()
{
m_scaleRelation = 1f;
OnHeadAttachChange(Settings.HeadAttach);
}
internal void OnAvatarSetup()
{
m_inVR = Utils.IsInVR();
OnHeadAttachChange(Settings.HeadAttach);
}
internal void OnPlayspaceScale(float p_relation)
{
m_scaleRelation = p_relation;
OnHeadAttachChange(Settings.HeadAttach);
}
void OnModeSwitch()
{
m_inVR = Utils.IsInVR();
OnHeadAttachChange(Settings.HeadAttach);
}
// Utils
static void OrientationAdjustment(ref Vector3 p_pos, ref Quaternion p_rot, Settings.LeapTrackingMode p_mode)
{
switch(p_mode)
{
case Settings.LeapTrackingMode.Screentop:
{
p_pos.x *= -1f;
p_pos.y *= -1f;
p_rot = (ms_screentopRotation * p_rot);
}
break;
case Settings.LeapTrackingMode.HMD:
{
p_pos.x *= -1f;
Utils.Swap(ref p_pos.y, ref p_pos.z);
p_rot = (ms_hmdRotation * p_rot);
}
break;
}
}
}
}

View file

@ -1,4 +1,6 @@
using ABI_RC.Core.Player;
using ABI.CCK.Components;
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using System.Collections;
using System.Reflection;
using UnityEngine;
@ -32,6 +34,11 @@ namespace ml_lme
null,
new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public),
null,
new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetControllerRayScale)),
null,
@ -42,6 +49,12 @@ namespace ml_lme
null,
new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.Grab), BindingFlags.Public | BindingFlags.Instance),
null,
new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnPickupGrab_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
ModSupporter.Init();
MelonLoader.MelonCoroutines.Start(WaitForRootLogic());
@ -94,6 +107,20 @@ namespace ml_lme
}
}
static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize();
void OnAvatarReinitialize()
{
try
{
if(m_leapManager != null)
m_leapManager.OnAvatarReinitialize();
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnRayScale_Postfix(float __0) => ms_instance?.OnRayScale(__0);
void OnRayScale(float p_scale)
{
@ -121,5 +148,19 @@ namespace ml_lme
MelonLoader.MelonLogger.Error(e);
}
}
static void OnPickupGrab_Postfix(ref CVRPickupObject __instance) => ms_instance?.OnPickupGrab(__instance);
void OnPickupGrab(CVRPickupObject p_pickup)
{
try
{
if(m_leapManager != null)
m_leapManager.OnPickupGrab(p_pickup);
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
}
}

26
ml_lme/PoseHelper.cs Normal file
View file

@ -0,0 +1,26 @@
using UnityEngine;
using ABI_RC.Systems.IK;
namespace ml_lme
{
static class PoseHelper
{
public static void ForceTPose(Animator p_animator)
{
if(p_animator.isHuman)
{
HumanPoseHandler l_handler = new HumanPoseHandler(p_animator.avatar, p_animator.transform);
HumanPose l_pose = new HumanPose();
l_handler.GetHumanPose(ref l_pose);
for(int i=0, j = Mathf.Min(l_pose.muscles.Length,MusclePoses.TPoseMuscles.Length); i < j; i++)
l_pose.muscles[i] = MusclePoses.TPoseMuscles[i];
l_pose.bodyPosition = Vector3.up;
l_pose.bodyRotation = Quaternion.identity;
l_handler.SetHumanPose(ref l_pose);
l_handler.Dispose();
}
}
}
}

View file

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

View file

@ -1,346 +1,346 @@
using ABI_RC.Core.InteractionSystem;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace ml_lme
{
static class Settings
{
public enum LeapTrackingMode
{
Screentop = 0,
Desktop,
HMD
}
enum ModSetting
{
Enabled,
DesktopX,
DesktopY,
DesktopZ,
FingersOnly,
Model,
Mode,
AngleX,
AngleY,
AngleZ,
Head,
HeadX,
HeadY,
HeadZ,
TrackElbows,
Interaction,
Gestures,
InteractThreadhold,
GripThreadhold,
VisualHands
};
public static bool Enabled { get; private set; } = false;
public static Vector3 DesktopOffset { get; private set; } = new Vector3(0f, -0.45f, 0.3f);
public static bool FingersOnly { get; private set; } = false;
public static bool ModelVisibility { get; private set; } = false;
public static LeapTrackingMode TrackingMode { get; private set; } = LeapTrackingMode.Desktop;
public static Vector3 RootAngle { get; private set; } = Vector3.zero;
public static bool HeadAttach { get; private set; } = false;
public static Vector3 HeadOffset { get; private set; } = new Vector3(0f, -0.3f, 0.15f);
public static bool TrackElbows { get; private set; } = true;
public static bool Interaction { get; private set; } = true;
public static bool Gestures { get; private set; } = false;
public static float InteractThreadhold { get; private set; } = 0.8f;
public static float GripThreadhold { get; private set; } = 0.4f;
public static bool VisualHands { get; private set; } = false;
static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
static public event Action<bool> EnabledChange;
static public event Action<Vector3> DesktopOffsetChange;
static public event Action<bool> FingersOnlyChange;
static public event Action<bool> ModelVisibilityChange;
static public event Action<LeapTrackingMode> TrackingModeChange;
static public event Action<Vector3> RootAngleChange;
static public event Action<bool> HeadAttachChange;
static public event Action<Vector3> HeadOffsetChange;
static public event Action<bool> TrackElbowsChange;
static public event Action<bool> InteractionChange;
static public event Action<bool> GesturesChange;
static public event Action<float> InteractThreadholdChange;
static public event Action<float> GripThreadholdChange;
static public event Action<bool> VisualHandsChange;
internal static void Init()
{
ms_category = MelonLoader.MelonPreferences.CreateCategory("LME", null, true);
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{
ms_category.CreateEntry(ModSetting.Enabled.ToString(), Enabled),
ms_category.CreateEntry(ModSetting.DesktopX.ToString(), (int)(DesktopOffset.x * 100f)),
ms_category.CreateEntry(ModSetting.DesktopY.ToString(), (int)(DesktopOffset.y * 100f)),
ms_category.CreateEntry(ModSetting.DesktopZ.ToString(), (int)(DesktopOffset.z * 100f)),
ms_category.CreateEntry(ModSetting.FingersOnly.ToString(), FingersOnly),
ms_category.CreateEntry(ModSetting.Model.ToString(), ModelVisibility),
ms_category.CreateEntry(ModSetting.Mode.ToString(), (int)TrackingMode),
ms_category.CreateEntry(ModSetting.AngleX.ToString(), (int)(RootAngle.x * 100f)),
ms_category.CreateEntry(ModSetting.AngleY.ToString(), (int)(RootAngle.y * 100f)),
ms_category.CreateEntry(ModSetting.AngleZ.ToString(), (int)(RootAngle.z * 100f)),
ms_category.CreateEntry(ModSetting.Head.ToString(), HeadAttach),
ms_category.CreateEntry(ModSetting.HeadX.ToString(), (int)(HeadOffset.x * 100f)),
ms_category.CreateEntry(ModSetting.HeadY.ToString(), (int)(HeadOffset.y * 100f)),
ms_category.CreateEntry(ModSetting.HeadZ.ToString(), (int)(HeadOffset.z * 100f)),
ms_category.CreateEntry(ModSetting.TrackElbows.ToString(), TrackElbows),
ms_category.CreateEntry(ModSetting.Interaction.ToString(), Interaction),
ms_category.CreateEntry(ModSetting.Gestures.ToString(), Gestures),
ms_category.CreateEntry(ModSetting.InteractThreadhold.ToString(), (int)(InteractThreadhold * 100f)),
ms_category.CreateEntry(ModSetting.GripThreadhold.ToString(), (int)(GripThreadhold * 100f)),
ms_category.CreateEntry(ModSetting.VisualHands.ToString(), VisualHands)
};
Load();
MelonLoader.MelonCoroutines.Start(WaitMainMenuUi());
}
static System.Collections.IEnumerator WaitMainMenuUi()
{
while(ViewManager.Instance == null)
yield return null;
while(ViewManager.Instance.gameMenuView == null)
yield return null;
while(ViewManager.Instance.gameMenuView.Listener == null)
yield return null;
ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
{
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("OnDropdownUpdate_" + ms_category.Identifier, new Action<string, string>(OnDropdownUpdate));
};
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
{
ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js"));
ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js"));
foreach(var l_entry in ms_entries)
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString());
};
}
static void Load()
{
Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue;
DesktopOffset = new Vector3(
(int)ms_entries[(int)ModSetting.DesktopX].BoxedValue,
(int)ms_entries[(int)ModSetting.DesktopY].BoxedValue,
(int)ms_entries[(int)ModSetting.DesktopZ].BoxedValue
) * 0.01f;
FingersOnly = (bool)ms_entries[(int)ModSetting.FingersOnly].BoxedValue;
ModelVisibility = (bool)ms_entries[(int)ModSetting.Model].BoxedValue;
TrackingMode = (LeapTrackingMode)(int)ms_entries[(int)ModSetting.Mode].BoxedValue;
RootAngle = new Vector3(
(int)ms_entries[(int)ModSetting.AngleX].BoxedValue,
(int)ms_entries[(int)ModSetting.AngleY].BoxedValue,
(int)ms_entries[(int)ModSetting.AngleZ].BoxedValue
);
HeadAttach = (bool)ms_entries[(int)ModSetting.Head].BoxedValue;
HeadOffset = new Vector3(
(int)ms_entries[(int)ModSetting.HeadX].BoxedValue,
(int)ms_entries[(int)ModSetting.HeadY].BoxedValue,
(int)ms_entries[(int)ModSetting.HeadZ].BoxedValue
) * 0.01f;
TrackElbows = (bool)ms_entries[(int)ModSetting.TrackElbows].BoxedValue;
Interaction = (bool)ms_entries[(int)ModSetting.Interaction].BoxedValue;
Gestures = (bool)ms_entries[(int)ModSetting.Gestures].BoxedValue;
InteractThreadhold = (int)ms_entries[(int)ModSetting.InteractThreadhold].BoxedValue * 0.01f;
GripThreadhold = (int)ms_entries[(int)ModSetting.GripThreadhold].BoxedValue * 0.01f;
VisualHands = (bool)ms_entries[(int)ModSetting.VisualHands].BoxedValue;
}
static void OnToggleUpdate(string p_name, string p_value)
{
if(Enum.TryParse(p_name, out ModSetting l_setting))
{
switch(l_setting)
{
case ModSetting.Enabled:
{
Enabled = bool.Parse(p_value);
EnabledChange?.Invoke(Enabled);
}
break;
case ModSetting.FingersOnly:
{
FingersOnly = bool.Parse(p_value);
FingersOnlyChange?.Invoke(FingersOnly);
}
break;
case ModSetting.Model:
{
ModelVisibility = bool.Parse(p_value);
ModelVisibilityChange?.Invoke(ModelVisibility);
}
break;
case ModSetting.Head:
{
HeadAttach = bool.Parse(p_value);
HeadAttachChange?.Invoke(HeadAttach);
}
break;
case ModSetting.TrackElbows:
{
TrackElbows = bool.Parse(p_value);
TrackElbowsChange?.Invoke(TrackElbows);
}
break;
case ModSetting.Interaction:
{
Interaction = bool.Parse(p_value);
InteractionChange?.Invoke(Interaction);
}
break;
case ModSetting.Gestures:
{
Gestures = bool.Parse(p_value);
GesturesChange?.Invoke(Gestures);
}
break;
case ModSetting.VisualHands:
{
VisualHands = bool.Parse(p_value);
VisualHandsChange?.Invoke(VisualHands);
}
break;
}
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
}
}
static void OnSliderUpdate(string p_name, string p_value)
{
if(Enum.TryParse(p_name, out ModSetting l_setting))
{
switch(l_setting)
{
case ModSetting.DesktopX:
{
Vector3 l_current = DesktopOffset;
l_current.x = int.Parse(p_value) * 0.01f;
DesktopOffset = l_current;
DesktopOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.DesktopY:
{
Vector3 l_current = DesktopOffset;
l_current.y = int.Parse(p_value) * 0.01f;
DesktopOffset = l_current;
DesktopOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.DesktopZ:
{
Vector3 l_current = DesktopOffset;
l_current.z = int.Parse(p_value) * 0.01f;
DesktopOffset = l_current;
DesktopOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.AngleX:
{
Vector3 l_current = RootAngle;
l_current.x = int.Parse(p_value);
RootAngle = l_current;
RootAngleChange?.Invoke(l_current);
}
break;
case ModSetting.AngleY:
{
Vector3 l_current = RootAngle;
l_current.y = int.Parse(p_value);
RootAngle = l_current;
RootAngleChange?.Invoke(l_current);
}
break;
case ModSetting.AngleZ:
{
Vector3 l_current = RootAngle;
l_current.z = int.Parse(p_value);
RootAngle = l_current;
RootAngleChange?.Invoke(l_current);
}
break;
case ModSetting.HeadX:
{
Vector3 l_current = HeadOffset;
l_current.x = int.Parse(p_value) * 0.01f;
HeadOffset = l_current;
HeadOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.HeadY:
{
Vector3 l_current = HeadOffset;
l_current.y = int.Parse(p_value) * 0.01f;
HeadOffset = l_current;
HeadOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.HeadZ:
{
Vector3 l_current = HeadOffset;
l_current.z = int.Parse(p_value) * 0.01f;
HeadOffset = l_current;
HeadOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.InteractThreadhold:
{
InteractThreadhold = int.Parse(p_value) * 0.01f;
InteractThreadholdChange?.Invoke(InteractThreadhold);
}
break;
case ModSetting.GripThreadhold:
{
GripThreadhold = int.Parse(p_value) * 0.01f;
GripThreadholdChange?.Invoke(GripThreadhold);
}
break;
}
ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value);
}
}
static void OnDropdownUpdate(string p_name, string p_value)
{
if(Enum.TryParse(p_name, out ModSetting l_setting))
{
switch(l_setting)
{
case ModSetting.Mode:
{
TrackingMode = (LeapTrackingMode)int.Parse(p_value);
TrackingModeChange?.Invoke(TrackingMode);
}
break;
}
ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value);
}
}
}
}
using ABI_RC.Core.InteractionSystem;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace ml_lme
{
static class Settings
{
public enum LeapTrackingMode
{
Screentop = 0,
Desktop,
HMD
}
enum ModSetting
{
Enabled,
DesktopX,
DesktopY,
DesktopZ,
FingersOnly,
Model,
Mode,
AngleX,
AngleY,
AngleZ,
Head,
HeadX,
HeadY,
HeadZ,
TrackElbows,
Interaction,
Gestures,
InteractThreadhold,
GripThreadhold,
VisualHands
};
public static bool Enabled { get; private set; } = false;
public static Vector3 DesktopOffset { get; private set; } = new Vector3(0f, -0.45f, 0.3f);
public static bool FingersOnly { get; private set; } = false;
public static bool ModelVisibility { get; private set; } = false;
public static LeapTrackingMode TrackingMode { get; private set; } = LeapTrackingMode.Desktop;
public static Vector3 RootAngle { get; private set; } = Vector3.zero;
public static bool HeadAttach { get; private set; } = false;
public static Vector3 HeadOffset { get; private set; } = new Vector3(0f, -0.3f, 0.15f);
public static bool TrackElbows { get; private set; } = true;
public static bool Interaction { get; private set; } = true;
public static bool Gestures { get; private set; } = false;
public static float InteractThreadhold { get; private set; } = 0.8f;
public static float GripThreadhold { get; private set; } = 0.4f;
public static bool VisualHands { get; private set; } = false;
static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
static public event Action<bool> EnabledChange;
static public event Action<Vector3> DesktopOffsetChange;
static public event Action<bool> FingersOnlyChange;
static public event Action<bool> ModelVisibilityChange;
static public event Action<LeapTrackingMode> TrackingModeChange;
static public event Action<Vector3> RootAngleChange;
static public event Action<bool> HeadAttachChange;
static public event Action<Vector3> HeadOffsetChange;
static public event Action<bool> TrackElbowsChange;
static public event Action<bool> InteractionChange;
static public event Action<bool> GesturesChange;
static public event Action<float> InteractThreadholdChange;
static public event Action<float> GripThreadholdChange;
static public event Action<bool> VisualHandsChange;
internal static void Init()
{
ms_category = MelonLoader.MelonPreferences.CreateCategory("LME", null, true);
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{
ms_category.CreateEntry(ModSetting.Enabled.ToString(), Enabled),
ms_category.CreateEntry(ModSetting.DesktopX.ToString(), (int)(DesktopOffset.x * 100f)),
ms_category.CreateEntry(ModSetting.DesktopY.ToString(), (int)(DesktopOffset.y * 100f)),
ms_category.CreateEntry(ModSetting.DesktopZ.ToString(), (int)(DesktopOffset.z * 100f)),
ms_category.CreateEntry(ModSetting.FingersOnly.ToString(), FingersOnly),
ms_category.CreateEntry(ModSetting.Model.ToString(), ModelVisibility),
ms_category.CreateEntry(ModSetting.Mode.ToString(), (int)TrackingMode),
ms_category.CreateEntry(ModSetting.AngleX.ToString(), (int)(RootAngle.x * 100f)),
ms_category.CreateEntry(ModSetting.AngleY.ToString(), (int)(RootAngle.y * 100f)),
ms_category.CreateEntry(ModSetting.AngleZ.ToString(), (int)(RootAngle.z * 100f)),
ms_category.CreateEntry(ModSetting.Head.ToString(), HeadAttach),
ms_category.CreateEntry(ModSetting.HeadX.ToString(), (int)(HeadOffset.x * 100f)),
ms_category.CreateEntry(ModSetting.HeadY.ToString(), (int)(HeadOffset.y * 100f)),
ms_category.CreateEntry(ModSetting.HeadZ.ToString(), (int)(HeadOffset.z * 100f)),
ms_category.CreateEntry(ModSetting.TrackElbows.ToString(), TrackElbows),
ms_category.CreateEntry(ModSetting.Interaction.ToString(), Interaction),
ms_category.CreateEntry(ModSetting.Gestures.ToString(), Gestures),
ms_category.CreateEntry(ModSetting.InteractThreadhold.ToString(), (int)(InteractThreadhold * 100f)),
ms_category.CreateEntry(ModSetting.GripThreadhold.ToString(), (int)(GripThreadhold * 100f)),
ms_category.CreateEntry(ModSetting.VisualHands.ToString(), VisualHands)
};
Load();
MelonLoader.MelonCoroutines.Start(WaitMainMenuUi());
}
static System.Collections.IEnumerator WaitMainMenuUi()
{
while(ViewManager.Instance == null)
yield return null;
while(ViewManager.Instance.gameMenuView == null)
yield return null;
while(ViewManager.Instance.gameMenuView.Listener == null)
yield return null;
ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
{
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("OnDropdownUpdate_" + ms_category.Identifier, new Action<string, string>(OnDropdownUpdate));
};
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
{
ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mods_extension.js"));
ViewManager.Instance.gameMenuView.View.ExecuteScript(ResourcesHandler.GetEmbeddedResource("mod_menu.js"));
foreach(var l_entry in ms_entries)
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString());
};
}
static void Load()
{
Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue;
DesktopOffset = new Vector3(
(int)ms_entries[(int)ModSetting.DesktopX].BoxedValue,
(int)ms_entries[(int)ModSetting.DesktopY].BoxedValue,
(int)ms_entries[(int)ModSetting.DesktopZ].BoxedValue
) * 0.01f;
FingersOnly = (bool)ms_entries[(int)ModSetting.FingersOnly].BoxedValue;
ModelVisibility = (bool)ms_entries[(int)ModSetting.Model].BoxedValue;
TrackingMode = (LeapTrackingMode)(int)ms_entries[(int)ModSetting.Mode].BoxedValue;
RootAngle = new Vector3(
(int)ms_entries[(int)ModSetting.AngleX].BoxedValue,
(int)ms_entries[(int)ModSetting.AngleY].BoxedValue,
(int)ms_entries[(int)ModSetting.AngleZ].BoxedValue
);
HeadAttach = (bool)ms_entries[(int)ModSetting.Head].BoxedValue;
HeadOffset = new Vector3(
(int)ms_entries[(int)ModSetting.HeadX].BoxedValue,
(int)ms_entries[(int)ModSetting.HeadY].BoxedValue,
(int)ms_entries[(int)ModSetting.HeadZ].BoxedValue
) * 0.01f;
TrackElbows = (bool)ms_entries[(int)ModSetting.TrackElbows].BoxedValue;
Interaction = (bool)ms_entries[(int)ModSetting.Interaction].BoxedValue;
Gestures = (bool)ms_entries[(int)ModSetting.Gestures].BoxedValue;
InteractThreadhold = (int)ms_entries[(int)ModSetting.InteractThreadhold].BoxedValue * 0.01f;
GripThreadhold = (int)ms_entries[(int)ModSetting.GripThreadhold].BoxedValue * 0.01f;
VisualHands = (bool)ms_entries[(int)ModSetting.VisualHands].BoxedValue;
}
static void OnToggleUpdate(string p_name, string p_value)
{
if(Enum.TryParse(p_name, out ModSetting l_setting))
{
switch(l_setting)
{
case ModSetting.Enabled:
{
Enabled = bool.Parse(p_value);
EnabledChange?.Invoke(Enabled);
}
break;
case ModSetting.FingersOnly:
{
FingersOnly = bool.Parse(p_value);
FingersOnlyChange?.Invoke(FingersOnly);
}
break;
case ModSetting.Model:
{
ModelVisibility = bool.Parse(p_value);
ModelVisibilityChange?.Invoke(ModelVisibility);
}
break;
case ModSetting.Head:
{
HeadAttach = bool.Parse(p_value);
HeadAttachChange?.Invoke(HeadAttach);
}
break;
case ModSetting.TrackElbows:
{
TrackElbows = bool.Parse(p_value);
TrackElbowsChange?.Invoke(TrackElbows);
}
break;
case ModSetting.Interaction:
{
Interaction = bool.Parse(p_value);
InteractionChange?.Invoke(Interaction);
}
break;
case ModSetting.Gestures:
{
Gestures = bool.Parse(p_value);
GesturesChange?.Invoke(Gestures);
}
break;
case ModSetting.VisualHands:
{
VisualHands = bool.Parse(p_value);
VisualHandsChange?.Invoke(VisualHands);
}
break;
}
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
}
}
static void OnSliderUpdate(string p_name, string p_value)
{
if(Enum.TryParse(p_name, out ModSetting l_setting))
{
switch(l_setting)
{
case ModSetting.DesktopX:
{
Vector3 l_current = DesktopOffset;
l_current.x = int.Parse(p_value) * 0.01f;
DesktopOffset = l_current;
DesktopOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.DesktopY:
{
Vector3 l_current = DesktopOffset;
l_current.y = int.Parse(p_value) * 0.01f;
DesktopOffset = l_current;
DesktopOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.DesktopZ:
{
Vector3 l_current = DesktopOffset;
l_current.z = int.Parse(p_value) * 0.01f;
DesktopOffset = l_current;
DesktopOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.AngleX:
{
Vector3 l_current = RootAngle;
l_current.x = int.Parse(p_value);
RootAngle = l_current;
RootAngleChange?.Invoke(l_current);
}
break;
case ModSetting.AngleY:
{
Vector3 l_current = RootAngle;
l_current.y = int.Parse(p_value);
RootAngle = l_current;
RootAngleChange?.Invoke(l_current);
}
break;
case ModSetting.AngleZ:
{
Vector3 l_current = RootAngle;
l_current.z = int.Parse(p_value);
RootAngle = l_current;
RootAngleChange?.Invoke(l_current);
}
break;
case ModSetting.HeadX:
{
Vector3 l_current = HeadOffset;
l_current.x = int.Parse(p_value) * 0.01f;
HeadOffset = l_current;
HeadOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.HeadY:
{
Vector3 l_current = HeadOffset;
l_current.y = int.Parse(p_value) * 0.01f;
HeadOffset = l_current;
HeadOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.HeadZ:
{
Vector3 l_current = HeadOffset;
l_current.z = int.Parse(p_value) * 0.01f;
HeadOffset = l_current;
HeadOffsetChange?.Invoke(l_current);
}
break;
case ModSetting.InteractThreadhold:
{
InteractThreadhold = int.Parse(p_value) * 0.01f;
InteractThreadholdChange?.Invoke(InteractThreadhold);
}
break;
case ModSetting.GripThreadhold:
{
GripThreadhold = int.Parse(p_value) * 0.01f;
GripThreadholdChange?.Invoke(GripThreadhold);
}
break;
}
ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value);
}
}
static void OnDropdownUpdate(string p_name, string p_value)
{
if(Enum.TryParse(p_name, out ModSetting l_setting))
{
switch(l_setting)
{
case ModSetting.Mode:
{
TrackingMode = (LeapTrackingMode)int.Parse(p_value);
TrackingModeChange?.Invoke(TrackingMode);
}
break;
}
ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value);
}
}
}
}

View file

@ -1,6 +1,8 @@
using ABI_RC.Core.Savior;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Savior;
using ABI_RC.Core.UI;
using ABI_RC.Systems.InputManagement;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
@ -9,8 +11,10 @@ namespace ml_lme
static class Utils
{
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_vrActive = typeof(ControllerRay).GetField("vrActive", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_inputModules = typeof(CVRInputManager).GetField("_inputModules", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded);
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 IsLeftHandTracked() => (CVRInputManager.Instance._leftController != ABI_RC.Systems.InputManagement.XR.eXRControllerType.None);
public static bool IsRightHandTracked() => (CVRInputManager.Instance._rightController != ABI_RC.Systems.InputManagement.XR.eXRControllerType.None);
@ -31,6 +35,20 @@ namespace ml_lme
}
}
public static void SetVRActive(this ControllerRay p_instance, bool p_state) => ms_vrActive?.SetValue(p_instance, p_state);
public static void SetModuleAsLast(this CVRInputManager p_instance, CVRInputModule p_module)
{
List<CVRInputModule> l_modules = ms_inputModules.GetValue(p_instance) as List<CVRInputModule>;
int l_lastIndex = l_modules.Count - 1;
int l_index = l_modules.FindIndex(p => p == p_module);
if((l_index != -1) && (l_index != l_lastIndex))
{
l_modules[l_index] = l_modules[l_lastIndex];
l_modules[l_lastIndex] = p_module;
}
}
static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);
public static void Swap<T>(ref T lhs, ref T rhs)

View file

@ -1,68 +0,0 @@
using UnityEngine;
namespace ml_lme
{
class VisualHand
{
readonly Transform m_wrist = null;
readonly Transform[] m_fingers = null;
public VisualHand(Transform p_root, bool p_left)
{
if(p_root != null)
{
m_wrist = p_root.Find(p_left ? "LeftHand/Wrist" : "RightHand/Wrist");
if(m_wrist != null)
{
m_fingers = new Transform[20];
m_fingers[0] = null; // Actual thumb-meta, look at Leap Motion docs, dummy
m_fingers[1] = m_wrist.Find("thumb_meta");
m_fingers[2] = m_wrist.Find("thumb_meta/thumb_a");
m_fingers[3] = m_wrist.Find("thumb_meta/thumb_a/thumb_b");
m_fingers[4] = m_wrist.Find("index_meta");
m_fingers[5] = m_wrist.Find("index_meta/index_a");
m_fingers[6] = m_wrist.Find("index_meta/index_a/index_b");
m_fingers[7] = m_wrist.Find("index_meta/index_a/index_b/index_c");
m_fingers[8] = m_wrist.Find("middle_meta");
m_fingers[9] = m_wrist.Find("middle_meta/middle_a");
m_fingers[10] = m_wrist.Find("middle_meta/middle_a/middle_b");
m_fingers[11] = m_wrist.Find("middle_meta/middle_a/middle_b/middle_c");
m_fingers[12] = m_wrist.Find("ring_meta");
m_fingers[13] = m_wrist.Find("ring_meta/ring_a");
m_fingers[14] = m_wrist.Find("ring_meta/ring_a/ring_b");
m_fingers[15] = m_wrist.Find("ring_meta/ring_a/ring_b/ring_c");
m_fingers[16] = m_wrist.Find("pinky_meta");
m_fingers[17] = m_wrist.Find("pinky_meta/pinky_a");
m_fingers[18] = m_wrist.Find("pinky_meta/pinky_a/pinky_b");
m_fingers[19] = m_wrist.Find("pinky_meta/pinky_a/pinky_b/pinky_c");
}
}
}
public void Update(LeapParser.HandData p_data)
{
if(m_wrist != null)
{
m_wrist.position = p_data.m_position;
m_wrist.rotation = p_data.m_rotation;
for(int i = 0; i < 20; i++)
{
if(m_fingers[i] != null)
{
//m_fingers[i].position = p_data.m_fingerPosition[i];
m_fingers[i].rotation = p_data.m_fingerRotation[i];
}
}
m_wrist.localPosition = p_data.m_position;
m_wrist.localRotation = p_data.m_rotation;
}
}
}
}

View file

@ -4,15 +4,15 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Platforms>x64</Platforms>
<PackageId>LeapMotionExtension</PackageId>
<Version>1.4.5</Version>
<Version>1.4.6</Version>
<Authors>SDraw</Authors>
<Company>None</Company>
<Product>LeapMotionExtension</Product>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>

View file

@ -1,162 +1,162 @@
{
let l_block = document.createElement('div');
l_block.innerHTML = `
<div class ="settings-subcategory">
<div class ="subcategory-name">Leap Motion tracking</div>
<div class ="subcategory-description"></div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Enable tracking: </div>
<div class ="option-input">
<div id="Enabled" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Tracking mode: </div>
<div class ="option-input">
<div id="Mode" class ="inp_dropdown no-scroll" data-options="0:Screentop,1:Desktop,2:HMD" data-current="1"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Desktop offset X: </div>
<div class ="option-input">
<div id="DesktopX" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Desktop offset Y: </div>
<div class ="option-input">
<div id="DesktopY" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="-45"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Desktop offset Z: </div>
<div class ="option-input">
<div id="DesktopZ" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="30"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Attach to head: </div>
<div class ="option-input">
<div id="Head" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Head offset X: </div>
<div class ="option-input">
<div id="HeadX" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Head offset Y: </div>
<div class ="option-input">
<div id="HeadY" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="-30"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Head offset Z: </div>
<div class ="option-input">
<div id="HeadZ" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="15"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Offset angle X: </div>
<div class ="option-input">
<div id="AngleX" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Offset angle Y: </div>
<div class ="option-input">
<div id="AngleY" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Offset angle Z: </div>
<div class ="option-input">
<div id="AngleZ" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Track elbows: </div>
<div class ="option-input">
<div id="TrackElbows" class ="inp_toggle no-scroll" data-current="true"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Fingers tracking only: </div>
<div class ="option-input">
<div id="FingersOnly" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Model visibility: </div>
<div class ="option-input">
<div id="Model" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Visualize hands: </div>
<div class ="option-input">
<div id="VisualHands" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Interaction input: </div>
<div class ="option-input">
<div id="Interaction" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Recognize gestures: </div>
<div class ="option-input">
<div id="Gestures" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Interact gesture threadhold: </div>
<div class ="option-input">
<div id="InteractThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="80"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Grip gesture threadhold: </div>
<div class ="option-input">
<div id="GripThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div>
</div>
</div>
`;
document.getElementById('settings-implementation').appendChild(l_block);
// Toggles
for (let l_toggle of l_block.querySelectorAll('.inp_toggle'))
modsExtension.addSetting('LME', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_LME'));
// Sliders
for (let l_slider of l_block.querySelectorAll('.inp_slider'))
modsExtension.addSetting('LME', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_LME'));
// Dropdowns
for (let l_dropdown of l_block.querySelectorAll('.inp_dropdown'))
modsExtension.addSetting('LME', l_dropdown.id, modsExtension.createDropdown(l_dropdown, 'OnDropdownUpdate_LME'));
}
{
let l_block = document.createElement('div');
l_block.innerHTML = `
<div class ="settings-subcategory">
<div class ="subcategory-name">Leap Motion tracking</div>
<div class ="subcategory-description"></div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Enable tracking: </div>
<div class ="option-input">
<div id="Enabled" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Tracking mode: </div>
<div class ="option-input">
<div id="Mode" class ="inp_dropdown no-scroll" data-options="0:Screentop,1:Desktop,2:HMD" data-current="1"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Desktop offset X: </div>
<div class ="option-input">
<div id="DesktopX" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Desktop offset Y: </div>
<div class ="option-input">
<div id="DesktopY" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="-45"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Desktop offset Z: </div>
<div class ="option-input">
<div id="DesktopZ" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="30"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Attach to head: </div>
<div class ="option-input">
<div id="Head" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Head offset X: </div>
<div class ="option-input">
<div id="HeadX" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Head offset Y: </div>
<div class ="option-input">
<div id="HeadY" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="-30"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Head offset Z: </div>
<div class ="option-input">
<div id="HeadZ" class ="inp_slider no-scroll" data-min="-100" data-max="100" data-current="15"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Offset angle X: </div>
<div class ="option-input">
<div id="AngleX" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Offset angle Y: </div>
<div class ="option-input">
<div id="AngleY" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Offset angle Z: </div>
<div class ="option-input">
<div id="AngleZ" class ="inp_slider no-scroll" data-min="-180" data-max="180" data-current="0"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Track elbows: </div>
<div class ="option-input">
<div id="TrackElbows" class ="inp_toggle no-scroll" data-current="true"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Fingers tracking only: </div>
<div class ="option-input">
<div id="FingersOnly" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Model visibility: </div>
<div class ="option-input">
<div id="Model" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Visualize hands: </div>
<div class ="option-input">
<div id="VisualHands" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Interaction input: </div>
<div class ="option-input">
<div id="Interaction" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Recognize gestures: </div>
<div class ="option-input">
<div id="Gestures" class ="inp_toggle no-scroll" data-current="false"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Interact gesture threadhold: </div>
<div class ="option-input">
<div id="InteractThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="80"></div>
</div>
</div>
<div class ="row-wrapper">
<div class ="option-caption">Grip gesture threadhold: </div>
<div class ="option-input">
<div id="GripThreadhold" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div>
</div>
</div>
`;
document.getElementById('settings-implementation').appendChild(l_block);
// Toggles
for (let l_toggle of l_block.querySelectorAll('.inp_toggle'))
modsExtension.addSetting('LME', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_LME'));
// Sliders
for (let l_slider of l_block.querySelectorAll('.inp_slider'))
modsExtension.addSetting('LME', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_LME'));
// Dropdowns
for (let l_dropdown of l_block.querySelectorAll('.inp_dropdown'))
modsExtension.addSetting('LME', l_dropdown.id, modsExtension.createDropdown(l_dropdown, 'OnDropdownUpdate_LME'));
}

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2023. *
* Copyright (C) Ultraleap, Inc. 2011-2024. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *

View file

@ -83,13 +83,7 @@ namespace ml_pam
void OnDestroy()
{
if(m_armIKLeft != null)
Destroy(m_armIKLeft);
m_armIKLeft = null;
if(m_armIKRight != null)
Destroy(m_armIKRight);
m_armIKRight = null;
RemoveArmIK();
if(m_rootLeft != null)
Destroy(m_rootLeft);
@ -331,26 +325,15 @@ namespace ml_pam
internal void OnAvatarSetup()
{
// Recheck if user could switch to VR
if(m_inVR != Utils.IsInVR())
{
m_rootLeft.parent = PlayerSetup.Instance.GetActiveCamera().transform;
m_rootLeft.localPosition = Vector3.zero;
m_rootLeft.localRotation = Quaternion.identity;
m_rootRight.parent = PlayerSetup.Instance.GetActiveCamera().transform;
m_rootRight.localPosition = Vector3.zero;
m_rootRight.localRotation = Quaternion.identity;
}
m_inVR = Utils.IsInVR();
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
if(!m_inVR && PlayerSetup.Instance._animator.isHuman)
ReparentRoots();
if(PlayerSetup.Instance._animator.isHuman)
{
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
TPoseHelper l_tpHelper = new TPoseHelper();
l_tpHelper.Assign(PlayerSetup.Instance._animator);
l_tpHelper.Apply();
if(!m_inVR)
PoseHelper.ForceTPose(PlayerSetup.Instance._animator);
Transform l_leftHand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
if(l_leftHand != null)
@ -359,64 +342,41 @@ namespace ml_pam
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_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest);
if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest);
if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine);
m_armIKLeft = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_armIKLeft.solver.isLeft = true;
m_armIKLeft.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
l_leftHand,
PlayerSetup.Instance._animator.transform
);
m_armIKLeft.solver.arm.target = m_leftTarget;
m_armIKLeft.solver.arm.positionWeight = 1f;
m_armIKLeft.solver.arm.rotationWeight = 1f;
m_armIKLeft.solver.IKPositionWeight = 0f;
m_armIKLeft.solver.IKRotationWeight = 0f;
m_armIKLeft.enabled = false;
m_armLength = m_armIKLeft.solver.arm.mag * 1.25f;
m_armIKRight = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_armIKRight.solver.isLeft = false;
m_armIKRight.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
l_rightHand,
PlayerSetup.Instance._animator.transform
);
m_armIKRight.solver.arm.target = m_rightTarget;
m_armIKRight.solver.arm.positionWeight = 1f;
m_armIKRight.solver.arm.rotationWeight = 1f;
m_armIKRight.solver.IKPositionWeight = 0f;
m_armIKRight.solver.IKRotationWeight = 0f;
m_armIKRight.enabled = false;
}
else
if(m_vrIK != null)
{
m_armLength = m_vrIK.solver.leftArm.mag * 1.25f;
m_vrIK.solver.OnPreUpdate += this.OnIKPreUpdate;
m_vrIK.solver.OnPostUpdate += this.OnIKPostUpdate;
m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
}
l_tpHelper.Restore();
l_tpHelper.Unassign();
else if(!m_inVR)
SetupArmIK();
}
SetEnabled(m_enabled);
}
internal void OnAvatarReinitialize()
{
// Old VRIK is destroyed by game
m_inVR = Utils.IsInVR();
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
ReparentRoots();
if(m_inVR)
RemoveArmIK();
if(m_vrIK != null)
{
m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
}
else if(!m_inVR)
SetupArmIK();
SetEnabled(m_enabled);
}
internal void OnPickupGrab(CVRPickupObject p_pickup, ControllerRay p_ray, Vector3 p_hit)
{
if(p_ray == ViewManager.Instance.desktopControllerRay)
@ -488,6 +448,62 @@ namespace ml_pam
}
// Arbitrary
void SetupArmIK()
{
Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest);
if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest);
if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine);
m_armIKLeft = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_armIKLeft.solver.isLeft = true;
m_armIKLeft.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand),
PlayerSetup.Instance._animator.transform
);
m_armIKLeft.solver.arm.target = m_leftTarget;
m_armIKLeft.solver.arm.positionWeight = 1f;
m_armIKLeft.solver.arm.rotationWeight = 1f;
m_armIKLeft.solver.IKPositionWeight = 0f;
m_armIKLeft.solver.IKRotationWeight = 0f;
m_armIKLeft.enabled = false;
m_armLength = m_armIKLeft.solver.arm.mag * 1.25f;
m_armIKRight = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_armIKRight.solver.isLeft = false;
m_armIKRight.solver.SetChain(
l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand),
PlayerSetup.Instance._animator.transform
);
m_armIKRight.solver.arm.target = m_rightTarget;
m_armIKRight.solver.arm.positionWeight = 1f;
m_armIKRight.solver.arm.rotationWeight = 1f;
m_armIKRight.solver.IKPositionWeight = 0f;
m_armIKRight.solver.IKRotationWeight = 0f;
m_armIKRight.enabled = false;
}
void RemoveArmIK()
{
if(m_armIKLeft != null)
Object.Destroy(m_armIKLeft);
m_armIKLeft = null;
if(m_armIKRight != null)
Object.Destroy(m_armIKRight);
m_armIKRight = null;
}
void SetArmActive(Settings.LeadHand p_hand, bool p_state, bool p_forced = false)
{
if(m_enabled || p_forced)
@ -506,5 +522,16 @@ namespace ml_pam
}
}
}
void ReparentRoots()
{
m_rootLeft.parent = PlayerSetup.Instance.GetActiveCamera().transform;
m_rootLeft.localPosition = Vector3.zero;
m_rootLeft.localRotation = Quaternion.identity;
m_rootRight.parent = PlayerSetup.Instance.GetActiveCamera().transform;
m_rootRight.localPosition = Vector3.zero;
m_rootRight.localRotation = Quaternion.identity;
}
}
}

View file

@ -1,6 +1,7 @@
using ABI.CCK.Components;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using System;
using System.Reflection;
using UnityEngine;
@ -30,6 +31,11 @@ namespace ml_pam
null,
new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public),
null,
new HarmonyLib.HarmonyMethod(typeof(PickupArmMovement).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(CVRPickupObject).GetMethod(nameof(CVRPickupObject.Grab)),
null,
@ -95,6 +101,20 @@ namespace ml_pam
}
}
static void OnAvatarReinitialize_Postfix() => ms_instance?.OnAvatarReinitialize();
void OnAvatarReinitialize()
{
try
{
if(m_localMover != null)
m_localMover.OnAvatarReinitialize();
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnCVRPickupObjectGrab_Postfix(ref CVRPickupObject __instance, ControllerRay __1, Vector3 __2) => ms_instance?.OnCVRPickupObjectGrab(__instance, __1, __2);
void OnCVRPickupObjectGrab(CVRPickupObject p_pickup, ControllerRay p_ray, Vector3 p_hit)
{

26
ml_pam/PoseHelper.cs Normal file
View file

@ -0,0 +1,26 @@
using UnityEngine;
using ABI_RC.Systems.IK;
namespace ml_pam
{
static class PoseHelper
{
public static void ForceTPose(Animator p_animator)
{
if(p_animator.isHuman)
{
HumanPoseHandler l_handler = new HumanPoseHandler(p_animator.avatar, p_animator.transform);
HumanPose l_pose = new HumanPose();
l_handler.GetHumanPose(ref l_pose);
for(int i = 0, j = Mathf.Min(l_pose.muscles.Length, MusclePoses.TPoseMuscles.Length); i < j; i++)
l_pose.muscles[i] = MusclePoses.TPoseMuscles[i];
l_pose.bodyPosition = Vector3.up;
l_pose.bodyRotation = Quaternion.identity;
l_handler.SetHumanPose(ref l_pose);
l_handler.Dispose();
}
}
}
}

View file

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

View file

@ -1,63 +0,0 @@
using System.Reflection;
using UnityEngine;
using ABI_RC.Systems.IK.SubSystems;
namespace ml_pam
{
class TPoseHelper
{
static readonly float[] ms_tposeMuscles = typeof(BodySystem).GetField("TPoseMuscles", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as float[];
HumanPoseHandler m_poseHandler = null;
HumanPose m_oldPose;
HumanPose m_newPose;
Vector3 m_hipsLocalPos = Vector3.zero;
Transform m_hips = null;
public void Assign(Animator p_animator)
{
if(m_poseHandler != null)
{
m_poseHandler = new HumanPoseHandler(p_animator.avatar, p_animator.transform);
m_hips = p_animator.GetBoneTransform(HumanBodyBones.Hips);
}
}
public void Unassign()
{
m_poseHandler?.Dispose();
m_poseHandler = null;
m_oldPose = new HumanPose();
m_newPose = new HumanPose();
m_hips = null;
m_hipsLocalPos = Vector3.zero;
}
public void Apply()
{
if(m_hips != null)
m_hipsLocalPos = m_hips.localPosition;
if(m_poseHandler != null)
{
m_poseHandler.GetHumanPose(ref m_oldPose);
m_newPose.bodyPosition = m_oldPose.bodyPosition;
m_newPose.bodyRotation = m_oldPose.bodyRotation;
m_newPose.muscles = new float[m_oldPose.muscles.Length];
for(int i = 0, j = m_newPose.muscles.Length; i < j; i++)
m_newPose.muscles[i] = ms_tposeMuscles[i];
m_poseHandler.SetHumanPose(ref m_newPose);
}
}
public void Restore()
{
if(m_poseHandler != null)
m_poseHandler.SetHumanPose(ref m_oldPose);
if(m_hips != null)
m_hips.localPosition = m_hipsLocalPos;
}
}
}

View file

@ -1,4 +1,5 @@
using ABI_RC.Core.UI;
using ABI_RC.Core.Savior;
using ABI_RC.Core.UI;
using System.Reflection;
using UnityEngine;
@ -8,7 +9,7 @@ namespace ml_pam
{
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsInVR() => ((ABI_RC.Core.Savior.CheckVR.Instance != null) && ABI_RC.Core.Savior.CheckVR.Instance.hasVrDeviceLoaded);
public static bool IsInVR() => ((MetaPort.Instance != null) && MetaPort.Instance.isUsingVr);
static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);

View file

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

View file

@ -41,25 +41,28 @@ namespace ml_pin
{
try
{
bool l_isFriend = Friends.FriendsWith(p_player.ownerId);
bool l_notify = false;
switch(Settings.NotifyType)
if(p_player != null) // This happens sometimes, no idea why
{
case Settings.NotificationType.None:
l_notify = false;
break;
case Settings.NotificationType.Friends:
l_notify = (l_isFriend && ShouldNotifyInCurrentInstance());
break;
case Settings.NotificationType.All:
l_notify = ShouldNotifyInCurrentInstance();
break;
}
l_notify |= (l_isFriend && Settings.FriendsAlways);
bool l_isFriend = Friends.FriendsWith(p_player.ownerId);
bool l_notify = false;
if(l_notify)
m_soundManager?.PlaySound(l_isFriend ? SoundManager.SoundType.FriendJoin : SoundManager.SoundType.PlayerJoin);
switch(Settings.NotifyType)
{
case Settings.NotificationType.None:
l_notify = false;
break;
case Settings.NotificationType.Friends:
l_notify = (l_isFriend && ShouldNotifyInCurrentInstance());
break;
case Settings.NotificationType.All:
l_notify = ShouldNotifyInCurrentInstance();
break;
}
l_notify |= (l_isFriend && Settings.FriendsAlways);
if(l_notify)
m_soundManager?.PlaySound(l_isFriend ? SoundManager.SoundType.FriendJoin : SoundManager.SoundType.PlayerJoin);
}
}
catch(Exception e)
{
@ -70,25 +73,28 @@ namespace ml_pin
{
try
{
bool l_isFriend = Friends.FriendsWith(p_player.ownerId);
bool l_notify = false;
switch(Settings.NotifyType)
if(p_player != null) // This happens sometimes, no idea why
{
case Settings.NotificationType.None:
l_notify = false;
break;
case Settings.NotificationType.Friends:
l_notify = (l_isFriend && ShouldNotifyInCurrentInstance());
break;
case Settings.NotificationType.All:
l_notify = ShouldNotifyInCurrentInstance();
break;
}
l_notify |= (l_isFriend && Settings.FriendsAlways);
bool l_isFriend = Friends.FriendsWith(p_player.ownerId);
bool l_notify = false;
if(l_notify)
m_soundManager?.PlaySound(l_isFriend ? SoundManager.SoundType.FriendLeave : SoundManager.SoundType.PlayerLeave);
switch(Settings.NotifyType)
{
case Settings.NotificationType.None:
l_notify = false;
break;
case Settings.NotificationType.Friends:
l_notify = (l_isFriend && ShouldNotifyInCurrentInstance());
break;
case Settings.NotificationType.All:
l_notify = ShouldNotifyInCurrentInstance();
break;
}
l_notify |= (l_isFriend && Settings.FriendsAlways);
if(l_notify)
m_soundManager?.PlaySound(l_isFriend ? SoundManager.SoundType.FriendLeave : SoundManager.SoundType.PlayerLeave);
}
}
catch(Exception e)
{

View file

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

View file

@ -7,7 +7,7 @@
<Authors>SDraw</Authors>
<Company>None</Company>
<Product>PlayersInstanceNotifier</Product>
<Version>1.0.1</Version>
<Version>1.0.2</Version>
</PropertyGroup>
<ItemGroup>

View file

@ -1,137 +1,171 @@
using ABI_RC.Core.Networking.IO.Social;
using ABI_RC.Core.Player;
using ABI_RC.Systems.MovementSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace ml_pmc
{
public class PlayerMovementCopycat : MelonLoader.MelonMod
{
static PlayerMovementCopycat ms_instance = null;
PoseCopycat m_localCopycat = null;
public override void OnInitializeMelon()
{
if(ms_instance == null)
ms_instance = this;
Settings.Init();
ModUi.Init();
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
null,
new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
);
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
null,
new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
}
public override void OnDeinitializeMelon()
{
if(ms_instance == this)
ms_instance = null;
ModUi.CopySwitch -= this.OnTargetSelect;
if(m_localCopycat != null)
UnityEngine.Object.Destroy(m_localCopycat);
m_localCopycat = null;
}
System.Collections.IEnumerator WaitForLocalPlayer()
{
while(PlayerSetup.Instance == null)
yield return null;
m_localCopycat = PlayerSetup.Instance.gameObject.AddComponent<PoseCopycat>();
ModUi.CopySwitch += this.OnTargetSelect;
}
void OnTargetSelect(string p_id)
{
if(m_localCopycat != null)
{
if(m_localCopycat.IsActive())
m_localCopycat.SetTarget(null);
else
{
if(Friends.FriendsWith(p_id))
{
if(CVRPlayerManager.Instance.GetPlayerPuppetMaster(p_id, out PuppetMaster l_puppetMaster))
{
if(IsInSight(MovementSystem.Instance.proxyCollider, l_puppetMaster.GetComponent<CapsuleCollider>(), Utils.GetWorldMovementLimit()))
m_localCopycat.SetTarget(l_puppetMaster);
else
ModUi.ShowAlert("Selected player is too far away or obstructed");
}
else
ModUi.ShowAlert("Selected player isn't connected or ready yet");
}
else
ModUi.ShowAlert("Selected player isn't your friend");
}
}
}
static bool IsInSight(CapsuleCollider p_source, CapsuleCollider p_target, float p_limit)
{
bool l_result = false;
if((p_source != null) && (p_target != null))
{
Ray l_ray = new Ray();
l_ray.origin = p_source.bounds.center;
l_ray.direction = p_target.bounds.center - l_ray.origin;
List<RaycastHit> l_hits = Physics.RaycastAll(l_ray, p_limit).ToList();
if(l_hits.Count > 0)
{
l_hits.RemoveAll(hit => hit.collider.gameObject.layer == LayerMask.NameToLayer("UI Internal")); // Somehow layer mask in RaycastAll just ignores players entirely
l_hits.RemoveAll(hit => hit.collider.gameObject.layer == LayerMask.NameToLayer("PlayerLocal"));
l_hits.RemoveAll(hit => hit.collider.gameObject.layer == LayerMask.NameToLayer("PlayerClone"));
l_hits.Sort((a, b) => ((a.distance < b.distance) ? -1 : 1));
l_result = (l_hits.First().collider.gameObject.transform.root == p_target.transform.root);
}
}
return l_result;
}
// Patches
static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
void OnAvatarClear()
{
try
{
if(m_localCopycat != null)
m_localCopycat.OnAvatarClear();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
void OnSetupAvatar()
{
try
{
if(m_localCopycat != null)
m_localCopycat.OnAvatarSetup();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
}
}
using ABI_RC.Core.Networking.IO.Social;
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.Movement;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace ml_pmc
{
public class PlayerMovementCopycat : MelonLoader.MelonMod
{
static PlayerMovementCopycat ms_instance = null;
PoseCopycat m_localCopycat = null;
public override void OnInitializeMelon()
{
if(ms_instance == null)
ms_instance = this;
Settings.Init();
ModUi.Init();
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
null,
new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
);
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
null,
new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public),
new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnAvatarReinitialize_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
}
public override void OnDeinitializeMelon()
{
if(ms_instance == this)
ms_instance = null;
ModUi.CopySwitch -= this.OnTargetSelect;
if(m_localCopycat != null)
UnityEngine.Object.Destroy(m_localCopycat);
m_localCopycat = null;
}
System.Collections.IEnumerator WaitForLocalPlayer()
{
while(PlayerSetup.Instance == null)
yield return null;
m_localCopycat = PlayerSetup.Instance.gameObject.AddComponent<PoseCopycat>();
ModUi.CopySwitch += this.OnTargetSelect;
}
void OnTargetSelect(string p_id)
{
if(m_localCopycat != null)
{
if(m_localCopycat.IsActive())
m_localCopycat.SetTarget(null);
else
{
if(Friends.FriendsWith(p_id))
{
if(CVRPlayerManager.Instance.GetPlayerPuppetMaster(p_id, out PuppetMaster l_puppetMaster))
{
if(IsInSight(BetterBetterCharacterController.Instance.KinematicTriggerProxy.Collider, l_puppetMaster.GetComponent<CapsuleCollider>(), Utils.GetWorldMovementLimit()))
m_localCopycat.SetTarget(l_puppetMaster);
else
ModUi.ShowAlert("Selected player is too far away or obstructed");
}
else
ModUi.ShowAlert("Selected player isn't connected or ready yet");
}
else
ModUi.ShowAlert("Selected player isn't your friend");
}
}
}
static bool IsInSight(CapsuleCollider p_source, CapsuleCollider p_target, float p_limit)
{
bool l_result = false;
if((p_source != null) && (p_target != null))
{
Ray l_ray = new Ray();
l_ray.origin = p_source.bounds.center;
l_ray.direction = p_target.bounds.center - l_ray.origin;
List<RaycastHit> l_hits = Physics.RaycastAll(l_ray, p_limit).ToList();
if(l_hits.Count > 0)
{
l_hits.RemoveAll(hit => hit.collider.gameObject.layer == LayerMask.NameToLayer("UI Internal")); // Somehow layer mask in RaycastAll just ignores players entirely
l_hits.RemoveAll(hit => hit.collider.gameObject.layer == LayerMask.NameToLayer("PlayerLocal"));
l_hits.RemoveAll(hit => hit.collider.gameObject.layer == LayerMask.NameToLayer("PlayerClone"));
l_hits.Sort((a, b) => ((a.distance < b.distance) ? -1 : 1));
l_result = (l_hits.First().collider.gameObject.transform.root == p_target.transform.root);
}
}
return l_result;
}
// Patches
static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
void OnAvatarClear()
{
try
{
if(m_localCopycat != null)
m_localCopycat.OnAvatarClear();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
void OnSetupAvatar()
{
try
{
if(m_localCopycat != null)
m_localCopycat.OnAvatarSetup();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnAvatarReinitialize_Prefix() => ms_instance?.OnPreAvatarReinitialize();
void OnPreAvatarReinitialize()
{
try
{
if(m_localCopycat != null)
m_localCopycat.OnPreAvatarReinitialize();
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnAvatarReinitialize_Postfix() => ms_instance?.OnPostAvatarReinitialize();
void OnPostAvatarReinitialize()
{
try
{
if(m_localCopycat != null)
m_localCopycat.OnPostAvatarReinitialize();
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
}
}

View file

@ -1,342 +1,440 @@
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.InputManagement;
using ABI_RC.Systems.MovementSystem;
using RootMotion.FinalIK;
using UnityEngine;
namespace ml_pmc
{
[DisallowMultipleComponent]
public class PoseCopycat : MonoBehaviour
{
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
static public PoseCopycat Instance { get; private set; } = null;
static internal System.Action<bool> OnActivityChange;
Animator m_animator = null;
VRIK m_vrIk = null;
float m_ikWeight = 1f;
LookAtIK m_lookAtIk = null;
float m_lookIkWeight = 1f;
bool m_sitting = false;
bool m_inVr = false;
bool m_active = false;
float m_distanceLimit = float.MaxValue;
bool m_fingerTracking = false;
HumanPoseHandler m_poseHandler = null;
HumanPose m_pose;
PuppetParser m_puppetParser = null;
void Start()
{
if(Instance == null)
Instance = this;
}
void OnDestroy()
{
if(Instance == this)
Instance = null;
m_poseHandler?.Dispose();
m_poseHandler = null;
if(m_puppetParser != null)
Object.Destroy(m_puppetParser);
m_puppetParser = null;
m_animator = null;
m_vrIk = null;
m_lookAtIk = null;
}
// Unity events
void Update()
{
m_sitting = (MovementSystem.Instance.lastSeat != null);
if(m_active)
{
if(m_puppetParser != null)
{
OverrideIK();
bool l_mirror = Settings.MirrorPose;
if(Settings.Gestures)
{
CVRInputManager.Instance.gestureLeft = (l_mirror ? m_puppetParser.GetRightGesture() : m_puppetParser.GetLeftGesture());
CVRInputManager.Instance.gestureRight = (l_mirror ? m_puppetParser.GetLeftGesture() : m_puppetParser.GetRightGesture());
}
if(m_puppetParser.HasFingerTracking())
{
m_fingerTracking = true;
CVRInputManager.Instance.individualFingerTracking = true;
IKSystem.Instance.FingerSystem.controlActive = true;
ref readonly float[] l_curls = ref m_puppetParser.GetFingerCurls();
ref readonly float[] l_spreads = ref m_puppetParser.GetFingerSpreads();
CVRInputManager.Instance.fingerCurlLeftThumb = l_curls[l_mirror ? 5 : 0];
CVRInputManager.Instance.fingerCurlLeftIndex = l_curls[l_mirror ? 6 : 1];
CVRInputManager.Instance.fingerCurlLeftMiddle = l_curls[l_mirror ? 7 : 2];
CVRInputManager.Instance.fingerCurlLeftRing = l_curls[l_mirror ? 8 : 3];
CVRInputManager.Instance.fingerCurlLeftPinky = l_curls[l_mirror ? 9 : 4];
CVRInputManager.Instance.fingerCurlRightThumb = l_curls[l_mirror ? 0 : 5];
CVRInputManager.Instance.fingerCurlRightIndex = l_curls[l_mirror ? 1 : 6];
CVRInputManager.Instance.fingerCurlRightMiddle = l_curls[l_mirror ? 2 : 7];
CVRInputManager.Instance.fingerCurlRightRing = l_curls[l_mirror ? 3 : 8];
CVRInputManager.Instance.fingerCurlRightPinky = l_curls[l_mirror ? 4 : 9];
CVRInputManager.Instance.fingerSpreadLeftThumb = l_spreads[l_mirror ? 5 : 0];
CVRInputManager.Instance.fingerSpreadLeftIndex = l_spreads[l_mirror ? 6 : 1];
CVRInputManager.Instance.fingerSpreadLeftMiddle = l_spreads[l_mirror ? 7 : 2];
CVRInputManager.Instance.fingerSpreadLeftRing = l_spreads[l_mirror ? 8 : 3];
CVRInputManager.Instance.fingerSpreadLeftPinky = l_spreads[l_mirror ? 9 : 4];
CVRInputManager.Instance.fingerSpreadRightThumb = l_spreads[l_mirror ? 0 : 5];
CVRInputManager.Instance.fingerSpreadRightIndex = l_spreads[l_mirror ? 1 : 6];
CVRInputManager.Instance.fingerSpreadRightMiddle = l_spreads[l_mirror ? 2 : 7];
CVRInputManager.Instance.fingerSpreadRightRing = l_spreads[l_mirror ? 3 : 8];
CVRInputManager.Instance.fingerSpreadRightPinky = l_spreads[l_mirror ? 4 : 9];
}
else
{
if(m_fingerTracking)
{
RestoreFingerTracking();
m_fingerTracking = false;
}
}
Matrix4x4 l_offset = m_puppetParser.GetLastOffset();
Vector3 l_pos = l_offset * ms_pointVector;
Quaternion l_rot = l_offset.rotation;
l_pos.y = 0f;
if(Settings.MirrorPosition)
l_pos.x *= -1f;
l_pos = Vector3.ClampMagnitude(l_pos, m_distanceLimit);
l_rot = Quaternion.Euler(0f, l_rot.eulerAngles.y * (Settings.MirrorRotation ? -1f : 1f), 0f);
Matrix4x4 l_result = PlayerSetup.Instance.transform.GetMatrix() * Matrix4x4.TRS(l_pos, l_rot, Vector3.one);
if(Settings.Position && !m_sitting && !m_puppetParser.IsSitting() && Utils.IsWorldSafe() && Utils.IsCombatSafe())
PlayerSetup.Instance.transform.position = l_result * ms_pointVector;
if(Settings.Rotation && !m_sitting && !m_puppetParser.IsSitting() && Utils.IsCombatSafe())
{
if(m_inVr)
{
Vector3 l_avatarPos = PlayerSetup.Instance._avatar.transform.position;
PlayerSetup.Instance.transform.rotation = l_result.rotation;
Vector3 l_dif = l_avatarPos - PlayerSetup.Instance._avatar.transform.position;
PlayerSetup.Instance.transform.position += l_dif;
}
else
PlayerSetup.Instance.transform.rotation = l_result.rotation;
}
if(Vector3.Distance(this.transform.position, m_puppetParser.transform.position) > m_distanceLimit)
SetTarget(null);
}
else
SetTarget(null);
}
}
void LateUpdate()
{
if(m_active && (m_animator != null) && (m_puppetParser != null))
{
OverrideIK();
m_puppetParser.GetPose().CopyTo(ref m_pose);
if(Settings.MirrorPose)
Utils.MirrorPose(ref m_pose);
m_poseHandler.SetHumanPose(ref m_pose);
}
}
// Patches
internal void OnAvatarClear()
{
if(m_active)
{
RestoreIK();
RestoreFingerTracking();
OnActivityChange?.Invoke(false);
}
m_active = false;
m_inVr = Utils.IsInVR();
if(m_puppetParser != null)
Object.Destroy(m_puppetParser);
m_puppetParser = null;
m_animator = null;
m_vrIk = null;
m_lookAtIk = null;
m_poseHandler?.Dispose();
m_poseHandler = null;
m_distanceLimit = float.MaxValue;
m_fingerTracking = false;
m_pose = new HumanPose();
}
internal void OnAvatarSetup()
{
m_animator = PlayerSetup.Instance._animator;
m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
m_lookAtIk = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
if((m_animator != null) && m_animator.isHuman)
{
m_poseHandler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);
m_poseHandler.GetHumanPose(ref m_pose);
if(m_vrIk != null)
{
m_vrIk.onPreSolverUpdate.AddListener(this.OnVRIKPreUpdate);
m_vrIk.onPostSolverUpdate.AddListener(this.OnVRIKPostUpdate);
}
if(m_lookAtIk != null)
{
m_lookAtIk.onPreSolverUpdate.AddListener(this.OnLookAtIKPreUpdate);
m_lookAtIk.onPostSolverUpdate.AddListener(this.OnLookAtIKPostUpdate);
}
}
else
m_animator = null;
}
// IK updates
void OnVRIKPreUpdate()
{
if(m_active)
{
m_ikWeight = m_vrIk.solver.IKPositionWeight;
m_vrIk.solver.IKPositionWeight = 0f;
}
}
void OnVRIKPostUpdate()
{
if(m_active)
m_vrIk.solver.IKPositionWeight = m_ikWeight;
}
void OnLookAtIKPreUpdate()
{
if(m_active && !Settings.LookAtMix)
{
m_lookIkWeight = m_lookAtIk.solver.IKPositionWeight;
m_lookAtIk.solver.IKPositionWeight = 0f;
}
}
void OnLookAtIKPostUpdate()
{
if(m_active && !Settings.LookAtMix)
m_lookAtIk.solver.IKPositionWeight = m_lookIkWeight;
}
// Arbitrary
public void SetTarget(PuppetMaster p_target)
{
if(m_animator != null)
{
if(!m_active)
{
if((p_target != null) && (p_target.animatorManager != null) && (p_target.animatorManager.animator != null) && p_target.animatorManager.animator.isHuman)
{
m_puppetParser = p_target.animatorManager.animator.gameObject.AddComponent<PuppetParser>();
m_puppetParser.m_puppetMaster = p_target;
m_distanceLimit = Utils.GetWorldMovementLimit();
m_active = true;
OnActivityChange?.Invoke(m_active);
}
}
else
{
if(p_target == null)
{
if(m_puppetParser != null)
Object.Destroy(m_puppetParser);
m_puppetParser = null;
if(!m_sitting)
{
Quaternion l_rot = PlayerSetup.Instance.transform.rotation;
PlayerSetup.Instance.transform.rotation = Quaternion.Euler(0f, l_rot.eulerAngles.y, 0f);
}
RestoreIK();
RestoreFingerTracking();
m_fingerTracking = false;
m_active = false;
OnActivityChange?.Invoke(m_active);
}
}
}
}
public bool IsActive() => m_active;
public bool IsFingerTrackingActive() => m_fingerTracking;
void OverrideIK()
{
if(!BodySystem.isCalibrating)
BodySystem.TrackingPositionWeight = 0f;
}
void RestoreIK()
{
if(!BodySystem.isCalibrating)
BodySystem.TrackingPositionWeight = 1f;
if(m_vrIk != null)
m_vrIk.solver.Reset();
}
void RestoreFingerTracking()
{
CVRInputManager.Instance.individualFingerTracking = (m_inVr && Utils.AreKnucklesInUse() && !CVRInputManager._moduleXR.GestureToggleValue);
IKSystem.Instance.FingerSystem.controlActive = CVRInputManager.Instance.individualFingerTracking;
if(!CVRInputManager.Instance.individualFingerTracking)
{
CVRInputManager.Instance.fingerCurlLeftThumb = 0f;
CVRInputManager.Instance.fingerCurlLeftIndex = 0f;
CVRInputManager.Instance.fingerCurlLeftMiddle = 0f;
CVRInputManager.Instance.fingerCurlLeftRing = 0f;
CVRInputManager.Instance.fingerCurlLeftPinky = 0f;
CVRInputManager.Instance.fingerCurlRightThumb = 0f;
CVRInputManager.Instance.fingerCurlRightIndex = 0f;
CVRInputManager.Instance.fingerCurlRightMiddle = 0f;
CVRInputManager.Instance.fingerCurlRightRing = 0f;
CVRInputManager.Instance.fingerCurlRightPinky = 0f;
CVRInputManager.Instance.fingerSpreadLeftThumb = 0.5f;
CVRInputManager.Instance.fingerSpreadLeftIndex = 0.5f;
CVRInputManager.Instance.fingerSpreadLeftMiddle = 0.5f;
CVRInputManager.Instance.fingerSpreadLeftRing = 0.5f;
CVRInputManager.Instance.fingerSpreadLeftPinky = 0.5f;
CVRInputManager.Instance.fingerSpreadRightThumb = 0.5f;
CVRInputManager.Instance.fingerSpreadRightIndex = 0.5f;
CVRInputManager.Instance.fingerSpreadRightMiddle = 0.5f;
CVRInputManager.Instance.fingerSpreadRightRing = 0.5f;
CVRInputManager.Instance.fingerSpreadRightPinky = 0.5f;
}
}
}
}
using ABI_RC.Core.Player;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.InputManagement;
using ABI_RC.Systems.Movement;
using ABI_RC.Systems.VRModeSwitch;
using RootMotion.FinalIK;
using UnityEngine;
namespace ml_pmc
{
[DisallowMultipleComponent]
public class PoseCopycat : MonoBehaviour
{
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
static public PoseCopycat Instance { get; private set; } = null;
static internal System.Action<bool> OnActivityChange;
Animator m_animator = null;
VRIK m_vrIk = null;
float m_ikWeight = 1f;
LookAtIK m_lookAtIk = null;
float m_lookIkWeight = 1f;
bool m_sitting = false;
bool m_inVr = false;
bool m_active = false;
float m_distanceLimit = float.MaxValue;
bool m_fingerTracking = false;
HumanPoseHandler m_poseHandler = null;
HumanPose m_pose;
PuppetParser m_puppetParser = null;
void Start()
{
if(Instance == null)
Instance = this;
}
void OnDestroy()
{
if(Instance == this)
Instance = null;
m_poseHandler?.Dispose();
m_poseHandler = null;
if(m_puppetParser != null)
Object.Destroy(m_puppetParser);
m_puppetParser = null;
m_animator = null;
m_vrIk = null;
m_lookAtIk = null;
}
// Unity events
void Update()
{
m_sitting = BetterBetterCharacterController.Instance.IsSitting();
if(m_active)
{
if(m_puppetParser != null)
{
OverrideIK();
bool l_mirror = Settings.MirrorPose;
if(Settings.Gestures)
{
CVRInputManager.Instance.gestureLeft = (l_mirror ? m_puppetParser.GetRightGesture() : m_puppetParser.GetLeftGesture());
CVRInputManager.Instance.gestureRight = (l_mirror ? m_puppetParser.GetLeftGesture() : m_puppetParser.GetRightGesture());
}
if(m_puppetParser.HasFingerTracking())
{
m_fingerTracking = true;
CVRInputManager.Instance.individualFingerTracking = true;
IKSystem.Instance.FingerSystem.controlActive = true;
ref readonly float[] l_curls = ref m_puppetParser.GetFingerCurls();
ref readonly float[] l_spreads = ref m_puppetParser.GetFingerSpreads();
// Left hand
CVRInputManager.Instance.finger1StretchedLeftThumb = l_curls[l_mirror ? 15 : 0];
CVRInputManager.Instance.finger2StretchedLeftThumb = l_curls[l_mirror ? 16 : 1];
CVRInputManager.Instance.finger3StretchedLeftThumb = l_curls[l_mirror ? 17 : 2];
CVRInputManager.Instance.fingerSpreadLeftThumb = l_spreads[l_mirror ? 5 : 0];
CVRInputManager.Instance.finger1StretchedLeftIndex = l_curls[l_mirror ? 18 : 3];
CVRInputManager.Instance.finger2StretchedLeftIndex = l_curls[l_mirror ? 19 : 4];
CVRInputManager.Instance.finger3StretchedLeftIndex = l_curls[l_mirror ? 20 : 5];
CVRInputManager.Instance.fingerSpreadLeftIndex = l_spreads[l_mirror ? 6 : 1];
CVRInputManager.Instance.finger1StretchedLeftMiddle = l_curls[l_mirror ? 21 : 6];
CVRInputManager.Instance.finger2StretchedLeftMiddle = l_curls[l_mirror ? 22 : 7];
CVRInputManager.Instance.finger3StretchedLeftMiddle = l_curls[l_mirror ? 23 : 8];
CVRInputManager.Instance.fingerSpreadLeftMiddle = l_spreads[l_mirror ? 7 : 2];
CVRInputManager.Instance.finger1StretchedLeftRing = l_curls[l_mirror ? 24 : 9];
CVRInputManager.Instance.finger2StretchedLeftRing = l_curls[l_mirror ? 25 : 10];
CVRInputManager.Instance.finger3StretchedLeftRing = l_curls[l_mirror ? 26 : 11];
CVRInputManager.Instance.fingerSpreadLeftRing = l_spreads[l_mirror ? 8 : 3];
CVRInputManager.Instance.finger1StretchedLeftPinky = l_curls[l_mirror ? 27 : 12];
CVRInputManager.Instance.finger2StretchedLeftPinky = l_curls[l_mirror ? 28 : 13];
CVRInputManager.Instance.finger3StretchedLeftPinky = l_curls[l_mirror ? 29 : 14];
CVRInputManager.Instance.fingerSpreadLeftPinky = l_spreads[l_mirror ? 9 : 4];
// Right hand
CVRInputManager.Instance.finger1StretchedRightThumb = l_curls[l_mirror ? 0 : 15];
CVRInputManager.Instance.finger2StretchedRightThumb = l_curls[l_mirror ? 1 : 16];
CVRInputManager.Instance.finger3StretchedRightThumb = l_curls[l_mirror ? 2 : 17];
CVRInputManager.Instance.fingerSpreadRightThumb = l_spreads[l_mirror ? 0 : 5];
CVRInputManager.Instance.finger1StretchedRightIndex = l_curls[l_mirror ? 3 : 18];
CVRInputManager.Instance.finger2StretchedRightIndex = l_curls[l_mirror ? 4 : 19];
CVRInputManager.Instance.finger3StretchedRightIndex = l_curls[l_mirror ? 5 : 20];
CVRInputManager.Instance.fingerSpreadRightIndex = l_spreads[l_mirror ? 1 : 6];
CVRInputManager.Instance.finger1StretchedRightMiddle = l_curls[l_mirror ? 6 : 21];
CVRInputManager.Instance.finger2StretchedRightMiddle = l_curls[l_mirror ? 7 : 22];
CVRInputManager.Instance.finger3StretchedRightMiddle = l_curls[l_mirror ? 8 : 23];
CVRInputManager.Instance.fingerSpreadRightMiddle = l_spreads[l_mirror ? 2 : 7];
CVRInputManager.Instance.finger1StretchedRightRing = l_curls[l_mirror ? 9 : 24];
CVRInputManager.Instance.finger2StretchedRightRing = l_curls[l_mirror ? 10 : 25];
CVRInputManager.Instance.finger3StretchedRightRing = l_curls[l_mirror ? 11 : 26];
CVRInputManager.Instance.fingerSpreadRightRing = l_spreads[l_mirror ? 3 : 8];
CVRInputManager.Instance.finger1StretchedRightPinky = l_curls[l_mirror ? 12 : 27];
CVRInputManager.Instance.finger2StretchedRightPinky = l_curls[l_mirror ? 13 : 28];
CVRInputManager.Instance.finger3StretchedRightPinky = l_curls[l_mirror ? 14 : 29];
CVRInputManager.Instance.fingerSpreadRightPinky = l_spreads[l_mirror ? 4 : 9];
}
else
{
if(m_fingerTracking)
{
RestoreFingerTracking();
m_fingerTracking = false;
}
}
Matrix4x4 l_offset = m_puppetParser.GetLastOffset();
Vector3 l_pos = l_offset * ms_pointVector;
Quaternion l_rot = l_offset.rotation;
l_pos.y = 0f;
if(Settings.MirrorPosition)
l_pos.x *= -1f;
l_pos = Vector3.ClampMagnitude(l_pos, m_distanceLimit);
l_rot = Quaternion.Euler(0f, l_rot.eulerAngles.y * (Settings.MirrorRotation ? -1f : 1f), 0f);
Matrix4x4 l_result = PlayerSetup.Instance.transform.GetMatrix() * Matrix4x4.TRS(l_pos, l_rot, Vector3.one);
if(Settings.Position && !m_sitting && !m_puppetParser.IsSitting() && Utils.IsWorldSafe() && Utils.IsCombatSafe())
PlayerSetup.Instance.transform.position = l_result * ms_pointVector;
if(Settings.Rotation && !m_sitting && !m_puppetParser.IsSitting() && Utils.IsCombatSafe())
{
if(m_inVr)
{
Vector3 l_avatarPos = PlayerSetup.Instance._avatar.transform.position;
PlayerSetup.Instance.transform.rotation = l_result.rotation;
Vector3 l_dif = l_avatarPos - PlayerSetup.Instance._avatar.transform.position;
PlayerSetup.Instance.transform.position += l_dif;
}
else
PlayerSetup.Instance.transform.rotation = l_result.rotation;
}
if(Vector3.Distance(this.transform.position, m_puppetParser.transform.position) > m_distanceLimit)
SetTarget(null);
}
else
SetTarget(null);
}
}
void LateUpdate()
{
if(m_active && (m_animator != null) && (m_puppetParser != null))
{
OverrideIK();
m_puppetParser.GetPose().CopyTo(ref m_pose);
if(Settings.MirrorPose)
Utils.MirrorPose(ref m_pose);
m_poseHandler.SetHumanPose(ref m_pose);
}
}
// Patches
internal void OnAvatarClear()
{
if(m_active)
{
RestoreIK();
RestoreFingerTracking();
OnActivityChange?.Invoke(false);
}
m_active = false;
if(m_puppetParser != null)
Object.Destroy(m_puppetParser);
m_puppetParser = null;
m_animator = null;
m_vrIk = null;
m_lookAtIk = null;
m_poseHandler?.Dispose();
m_poseHandler = null;
m_distanceLimit = float.MaxValue;
m_fingerTracking = false;
m_pose = new HumanPose();
}
internal void OnAvatarSetup()
{
m_inVr = Utils.IsInVR();
m_animator = PlayerSetup.Instance._animator;
m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
m_lookAtIk = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
if((m_animator != null) && m_animator.isHuman)
{
m_poseHandler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);
m_poseHandler.GetHumanPose(ref m_pose);
if(m_vrIk != null)
{
m_vrIk.onPreSolverUpdate.AddListener(this.OnVRIKPreUpdate);
m_vrIk.onPostSolverUpdate.AddListener(this.OnVRIKPostUpdate);
}
if(m_lookAtIk != null)
{
m_lookAtIk.onPreSolverUpdate.AddListener(this.OnLookAtIKPreUpdate);
m_lookAtIk.onPostSolverUpdate.AddListener(this.OnLookAtIKPostUpdate);
}
}
else
m_animator = null;
}
internal void OnPreAvatarReinitialize()
{
if(m_active)
SetTarget(null);
}
internal void OnPostAvatarReinitialize()
{
m_inVr = Utils.IsInVR();
// Old VRIK and LookAtIK are destroyed by game
m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
m_lookAtIk = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
if(m_vrIk != null)
{
m_vrIk.onPreSolverUpdate.AddListener(this.OnVRIKPreUpdate);
m_vrIk.onPostSolverUpdate.AddListener(this.OnVRIKPostUpdate);
}
if(m_lookAtIk != null)
{
m_lookAtIk.onPreSolverUpdate.AddListener(this.OnLookAtIKPreUpdate);
m_lookAtIk.onPostSolverUpdate.AddListener(this.OnLookAtIKPostUpdate);
}
}
// IK updates
void OnVRIKPreUpdate()
{
if(m_active)
{
m_ikWeight = m_vrIk.solver.IKPositionWeight;
m_vrIk.solver.IKPositionWeight = 0f;
}
}
void OnVRIKPostUpdate()
{
if(m_active)
m_vrIk.solver.IKPositionWeight = m_ikWeight;
}
void OnLookAtIKPreUpdate()
{
if(m_active && !Settings.LookAtMix)
{
m_lookIkWeight = m_lookAtIk.solver.IKPositionWeight;
m_lookAtIk.solver.IKPositionWeight = 0f;
}
}
void OnLookAtIKPostUpdate()
{
if(m_active && !Settings.LookAtMix)
m_lookAtIk.solver.IKPositionWeight = m_lookIkWeight;
}
// Arbitrary
public void SetTarget(PuppetMaster p_target)
{
if(m_animator != null)
{
if(!m_active)
{
if((p_target != null) && (p_target.animatorManager != null) && (p_target.animatorManager.animator != null) && p_target.animatorManager.animator.isHuman)
{
m_puppetParser = p_target.animatorManager.animator.gameObject.AddComponent<PuppetParser>();
m_puppetParser.m_puppetMaster = p_target;
m_distanceLimit = Utils.GetWorldMovementLimit();
m_active = true;
OnActivityChange?.Invoke(m_active);
}
}
else
{
if(p_target == null)
{
if(m_puppetParser != null)
Object.Destroy(m_puppetParser);
m_puppetParser = null;
if(!m_sitting)
{
Quaternion l_rot = PlayerSetup.Instance.transform.rotation;
PlayerSetup.Instance.transform.rotation = Quaternion.Euler(0f, l_rot.eulerAngles.y, 0f);
}
RestoreIK();
RestoreFingerTracking();
m_fingerTracking = false;
m_active = false;
OnActivityChange?.Invoke(m_active);
}
}
}
}
public bool IsActive() => m_active;
public bool IsFingerTrackingActive() => m_fingerTracking;
void OverrideIK()
{
if(!BodySystem.isCalibrating)
BodySystem.TrackingPositionWeight = 0f;
}
void RestoreIK()
{
if(!BodySystem.isCalibrating)
BodySystem.TrackingPositionWeight = 1f;
if(m_vrIk != null)
m_vrIk.solver.Reset();
}
void RestoreFingerTracking()
{
CVRInputManager.Instance.individualFingerTracking = (m_inVr && Utils.AreKnucklesInUse() && !CVRInputManager._moduleXR.GestureToggleValue);
IKSystem.Instance.FingerSystem.controlActive = CVRInputManager.Instance.individualFingerTracking;
if(!CVRInputManager.Instance.individualFingerTracking)
{
// Left hand
CVRInputManager.Instance.finger1StretchedLeftThumb = -0.5f;
CVRInputManager.Instance.finger2StretchedLeftThumb = 0.7f;
CVRInputManager.Instance.finger3StretchedLeftThumb = 0.7f;
CVRInputManager.Instance.fingerSpreadLeftThumb = 0f;
CVRInputManager.Instance.finger1StretchedLeftIndex = 0.5f;
CVRInputManager.Instance.finger2StretchedLeftIndex = 0.7f;
CVRInputManager.Instance.finger3StretchedLeftIndex = 0.7f;
CVRInputManager.Instance.fingerSpreadLeftIndex = 0f;
CVRInputManager.Instance.finger1StretchedLeftMiddle = 0.5f;
CVRInputManager.Instance.finger2StretchedLeftMiddle = 0.7f;
CVRInputManager.Instance.finger3StretchedLeftMiddle = 0.7f;
CVRInputManager.Instance.fingerSpreadLeftMiddle = 0f;
CVRInputManager.Instance.finger1StretchedLeftRing = 0.5f;
CVRInputManager.Instance.finger2StretchedLeftRing = 0.7f;
CVRInputManager.Instance.finger3StretchedLeftRing = 0.7f;
CVRInputManager.Instance.fingerSpreadLeftRing = 0f;
CVRInputManager.Instance.finger1StretchedLeftPinky = 0.5f;
CVRInputManager.Instance.finger2StretchedLeftPinky = 0.7f;
CVRInputManager.Instance.finger3StretchedLeftPinky = 0.7f;
CVRInputManager.Instance.fingerSpreadLeftPinky = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedLeftThumb = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedLeftIndex = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedLeftMiddle = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedLeftRing = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedLeftPinky = 0f;
// Right hand
CVRInputManager.Instance.finger1StretchedRightThumb = -0.5f;
CVRInputManager.Instance.finger2StretchedRightThumb = 0.7f;
CVRInputManager.Instance.finger3StretchedRightThumb = 0.7f;
CVRInputManager.Instance.fingerSpreadRightThumb = 0f;
CVRInputManager.Instance.finger1StretchedRightIndex = 0.5f;
CVRInputManager.Instance.finger2StretchedRightIndex = 0.7f;
CVRInputManager.Instance.finger3StretchedRightIndex = 0.7f;
CVRInputManager.Instance.fingerSpreadRightIndex = 0f;
CVRInputManager.Instance.finger1StretchedRightMiddle = 0.5f;
CVRInputManager.Instance.finger2StretchedRightMiddle = 0.7f;
CVRInputManager.Instance.finger3StretchedRightMiddle = 0.7f;
CVRInputManager.Instance.fingerSpreadRightMiddle = 0f;
CVRInputManager.Instance.finger1StretchedRightRing = 0.5f;
CVRInputManager.Instance.finger2StretchedRightRing = 0.7f;
CVRInputManager.Instance.finger3StretchedRightRing = 0.7f;
CVRInputManager.Instance.fingerSpreadRightRing = 0f;
CVRInputManager.Instance.finger1StretchedRightPinky = 0.5f;
CVRInputManager.Instance.finger2StretchedRightPinky = 0.7f;
CVRInputManager.Instance.finger3StretchedRightPinky = 0.7f;
CVRInputManager.Instance.fingerSpreadRightPinky = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedRightThumb = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedRightIndex = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedRightMiddle = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedRightRing = 0f;
CVRInputManager.Instance.fingerFullCurlNormalizedRightPinky = 0f;
}
}
}
}

View file

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

View file

@ -1,108 +1,136 @@
using ABI_RC.Core.Player;
using UnityEngine;
namespace ml_pmc
{
[DisallowMultipleComponent]
class PuppetParser : MonoBehaviour
{
internal PuppetMaster m_puppetMaster = null;
Animator m_animator = null;
AnimatorCullingMode m_cullMode;
HumanPoseHandler m_poseHandler = null;
HumanPose m_pose;
Matrix4x4 m_matrix = Matrix4x4.identity;
Matrix4x4 m_offset = Matrix4x4.identity;
bool m_sitting = false;
float m_leftGesture = 0f;
float m_rightGesture = 0f;
bool m_fingerTracking = false;
readonly float[] m_fingerCurls = null;
readonly float[] m_fingerSpreads = null;
internal PuppetParser()
{
m_fingerCurls = new float[10];
m_fingerSpreads = new float[10];
}
// Unity events
void Start()
{
m_animator = this.GetComponent<Animator>();
m_cullMode = m_animator.cullingMode;
m_animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
m_poseHandler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);
m_poseHandler.GetHumanPose(ref m_pose);
m_matrix = this.transform.GetMatrix();
}
void OnDestroy()
{
m_puppetMaster = null;
if(m_animator != null)
m_animator.cullingMode = m_cullMode;
m_animator = null;
m_poseHandler?.Dispose();
m_poseHandler = null;
}
void Update()
{
if(m_puppetMaster != null)
{
m_sitting = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorSitting;
m_leftGesture = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorGestureLeft;
m_rightGesture = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorGestureRight;
m_fingerTracking = m_puppetMaster.PlayerAvatarMovementDataInput.IndexUseIndividualFingers;
if(m_fingerTracking)
{
m_fingerCurls[0] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftThumbCurl;
m_fingerCurls[1] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftIndexCurl;
m_fingerCurls[2] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftMiddleCurl;
m_fingerCurls[3] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftRingCurl;
m_fingerCurls[4] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftPinkyCurl;
m_fingerCurls[5] = m_puppetMaster.PlayerAvatarMovementDataInput.RightThumbCurl;
m_fingerCurls[6] = m_puppetMaster.PlayerAvatarMovementDataInput.RightIndexCurl;
m_fingerCurls[7] = m_puppetMaster.PlayerAvatarMovementDataInput.RightMiddleCurl;
m_fingerCurls[8] = m_puppetMaster.PlayerAvatarMovementDataInput.RightRingCurl;
m_fingerCurls[9] = m_puppetMaster.PlayerAvatarMovementDataInput.RightPinkyCurl;
m_fingerSpreads[0] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftThumbSpread;
m_fingerSpreads[1] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftIndexSpread;
m_fingerSpreads[2] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftMiddleSpread;
m_fingerSpreads[3] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftRingSpread;
m_fingerSpreads[4] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftPinkySpread;
m_fingerSpreads[5] = m_puppetMaster.PlayerAvatarMovementDataInput.RightThumbSpread;
m_fingerSpreads[6] = m_puppetMaster.PlayerAvatarMovementDataInput.RightIndexSpread;
m_fingerSpreads[7] = m_puppetMaster.PlayerAvatarMovementDataInput.RightMiddleSpread;
m_fingerSpreads[8] = m_puppetMaster.PlayerAvatarMovementDataInput.RightRingSpread;
m_fingerSpreads[9] = m_puppetMaster.PlayerAvatarMovementDataInput.RightPinkySpread;
}
Matrix4x4 l_current = this.transform.GetMatrix();
m_offset = m_matrix.inverse * l_current;
m_matrix = l_current;
}
}
void LateUpdate()
{
if((m_animator != null) && (m_poseHandler != null))
m_poseHandler.GetHumanPose(ref m_pose);
}
public ref HumanPose GetPose() => ref m_pose;
public ref Matrix4x4 GetLastOffset() => ref m_offset;
public bool IsSitting() => m_sitting;
public float GetLeftGesture() => m_leftGesture;
public float GetRightGesture() => m_rightGesture;
public bool HasFingerTracking() => m_fingerTracking;
public ref readonly float[] GetFingerCurls() => ref m_fingerCurls;
public ref readonly float[] GetFingerSpreads() => ref m_fingerSpreads;
}
}
using ABI_RC.Core.Player;
using UnityEngine;
namespace ml_pmc
{
[DisallowMultipleComponent]
class PuppetParser : MonoBehaviour
{
internal PuppetMaster m_puppetMaster = null;
Animator m_animator = null;
AnimatorCullingMode m_cullMode;
HumanPoseHandler m_poseHandler = null;
HumanPose m_pose;
Matrix4x4 m_matrix = Matrix4x4.identity;
Matrix4x4 m_offset = Matrix4x4.identity;
bool m_sitting = false;
float m_leftGesture = 0f;
float m_rightGesture = 0f;
bool m_fingerTracking = false;
readonly float[] m_fingerCurls = null;
readonly float[] m_fingerSpreads = null;
internal PuppetParser()
{
m_fingerCurls = new float[30];
m_fingerSpreads = new float[10];
}
// Unity events
void Start()
{
m_animator = this.GetComponent<Animator>();
m_cullMode = m_animator.cullingMode;
m_animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
m_poseHandler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);
m_poseHandler.GetHumanPose(ref m_pose);
m_matrix = this.transform.GetMatrix();
}
void OnDestroy()
{
m_puppetMaster = null;
if(m_animator != null)
m_animator.cullingMode = m_cullMode;
m_animator = null;
m_poseHandler?.Dispose();
m_poseHandler = null;
}
void Update()
{
if(m_puppetMaster != null)
{
m_sitting = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorSitting;
m_leftGesture = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorGestureLeft;
m_rightGesture = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorGestureRight;
m_fingerTracking = m_puppetMaster.PlayerAvatarMovementDataInput.UseIndividualFingers;
if(m_fingerTracking)
{
m_fingerCurls[0] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftThumb1Stretched;
m_fingerCurls[1] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftThumb2Stretched;
m_fingerCurls[2] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftThumb3Stretched;
m_fingerSpreads[0] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftThumbSpread;
m_fingerCurls[3] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftIndex1Stretched;
m_fingerCurls[4] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftIndex2Stretched;
m_fingerCurls[5] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftIndex3Stretched;
m_fingerSpreads[1] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftIndexSpread;
m_fingerCurls[6] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftMiddle1Stretched;
m_fingerCurls[7] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftMiddle2Stretched;
m_fingerCurls[8] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftMiddle3Stretched;
m_fingerSpreads[2] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftMiddleSpread;
m_fingerCurls[9] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftRing1Stretched;
m_fingerCurls[10] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftRing2Stretched;
m_fingerCurls[11] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftRing3Stretched;
m_fingerSpreads[3] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftRingSpread;
m_fingerCurls[12] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftPinky1Stretched;
m_fingerCurls[13] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftPinky2Stretched;
m_fingerCurls[14] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftPinky3Stretched;
m_fingerSpreads[4] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftPinkySpread;
m_fingerCurls[15] = m_puppetMaster.PlayerAvatarMovementDataInput.RightThumb1Stretched;
m_fingerCurls[16] = m_puppetMaster.PlayerAvatarMovementDataInput.RightThumb2Stretched;
m_fingerCurls[17] = m_puppetMaster.PlayerAvatarMovementDataInput.RightThumb3Stretched;
m_fingerSpreads[5] = m_puppetMaster.PlayerAvatarMovementDataInput.RightThumbSpread;
m_fingerCurls[18] = m_puppetMaster.PlayerAvatarMovementDataInput.RightIndex1Stretched;
m_fingerCurls[19] = m_puppetMaster.PlayerAvatarMovementDataInput.RightIndex2Stretched;
m_fingerCurls[20] = m_puppetMaster.PlayerAvatarMovementDataInput.RightIndex3Stretched;
m_fingerSpreads[6] = m_puppetMaster.PlayerAvatarMovementDataInput.RightIndexSpread;
m_fingerCurls[21] = m_puppetMaster.PlayerAvatarMovementDataInput.RightMiddle1Stretched;
m_fingerCurls[22] = m_puppetMaster.PlayerAvatarMovementDataInput.RightMiddle2Stretched;
m_fingerCurls[23] = m_puppetMaster.PlayerAvatarMovementDataInput.RightMiddle3Stretched;
m_fingerSpreads[7] = m_puppetMaster.PlayerAvatarMovementDataInput.RightMiddleSpread;
m_fingerCurls[24] = m_puppetMaster.PlayerAvatarMovementDataInput.RightRing1Stretched;
m_fingerCurls[25] = m_puppetMaster.PlayerAvatarMovementDataInput.RightRing2Stretched;
m_fingerCurls[26] = m_puppetMaster.PlayerAvatarMovementDataInput.RightRing3Stretched;
m_fingerSpreads[8] = m_puppetMaster.PlayerAvatarMovementDataInput.RightRingSpread;
m_fingerCurls[27] = m_puppetMaster.PlayerAvatarMovementDataInput.RightPinky1Stretched;
m_fingerCurls[28] = m_puppetMaster.PlayerAvatarMovementDataInput.RightPinky2Stretched;
m_fingerCurls[29] = m_puppetMaster.PlayerAvatarMovementDataInput.RightPinky3Stretched;
m_fingerSpreads[9] = m_puppetMaster.PlayerAvatarMovementDataInput.RightPinkySpread;
}
Matrix4x4 l_current = this.transform.GetMatrix();
m_offset = m_matrix.inverse * l_current;
m_matrix = l_current;
}
}
void LateUpdate()
{
if((m_animator != null) && (m_poseHandler != null))
m_poseHandler.GetHumanPose(ref m_pose);
}
public ref HumanPose GetPose() => ref m_pose;
public ref Matrix4x4 GetLastOffset() => ref m_offset;
public bool IsSitting() => m_sitting;
public float GetLeftGesture() => m_leftGesture;
public float GetRightGesture() => m_rightGesture;
public bool HasFingerTracking() => m_fingerTracking;
public ref readonly float[] GetFingerCurls() => ref m_fingerCurls;
public ref readonly float[] GetFingerSpreads() => ref m_fingerSpreads;
}
}

View file

@ -16,7 +16,7 @@ namespace ml_pmc
};
static readonly int[] ms_centralMuscles = new int[] { 1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 18, 20 };
public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded);
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 IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);

View file

@ -7,7 +7,7 @@
<Authors>SDraw</Authors>
<Company>None</Company>
<Product>PlayerMovementCopycat</Product>
<Version>1.0.4</Version>
<Version>1.0.5</Version>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
@ -45,6 +45,11 @@
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="ECM2">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ECM2.dll</HintPath>
<SpecificVersion>false</SpecificVersion>
<Private>false</Private>
</Reference>
<Reference Include="MelonLoader">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
<Private>false</Private>

View file

@ -0,0 +1,30 @@
using ABI.CCK.Components;
using ABI_RC.Systems.Gravity;
using ABI_RC.Systems.Movement;
using UnityEngine;
namespace ml_prm
{
[DisallowMultipleComponent]
class GravityInfluencer : MonoBehaviour
{
Rigidbody m_rigidBody = null;
PhysicsInfluencer m_physicsInfluencer = null;
bool m_activeGravity = true;
void Start()
{
m_rigidBody = this.GetComponent<Rigidbody>();
m_physicsInfluencer = this.GetComponent<PhysicsInfluencer>();
}
void FixedUpdate()
{
m_rigidBody.useGravity = false;
if(m_activeGravity && ((m_physicsInfluencer == null) || !m_physicsInfluencer.enableInfluence || !m_physicsInfluencer.GetSubmerged()))
m_rigidBody.AddForce(BetterBetterCharacterController.Instance.GravityResult.AppliedGravity * m_rigidBody.mass);
}
public void SetActiveGravity(bool p_state) => m_activeGravity = p_state;
}
}

View file

@ -4,8 +4,9 @@ using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Core.Util.AssetFiltering;
using ABI_RC.Systems.Camera.VisualMods;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.MovementSystem;
using ABI_RC.Systems.Movement;
using System;
using System.Collections.Generic;
using System.Linq;
@ -15,6 +16,8 @@ namespace ml_prm
{
public class PlayerRagdollMod : MelonLoader.MelonMod
{
static readonly Type[] ms_teleportTypes = { typeof(UnityEngine.Vector3), typeof(bool), typeof(bool), typeof(UnityEngine.Quaternion?) };
static PlayerRagdollMod ms_instance = null;
RagdollController m_localController = null;
@ -37,6 +40,11 @@ namespace ml_prm
null,
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(IKSystem).GetMethod(nameof(IKSystem.ReinitializeAvatar), BindingFlags.Instance | BindingFlags.Public),
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnAvatarReinitialize_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnAvatarReinitialize_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.NonPublic | BindingFlags.Instance),
null,
@ -63,19 +71,13 @@ namespace ml_prm
null
);
HarmonyInstance.Patch(
typeof(MovementSystem).GetMethod(nameof(MovementSystem.ChangeFlight)),
typeof(BetterBetterCharacterController).GetMethod(nameof(BetterBetterCharacterController.ChangeFlight)),
null,
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnChangeFlight_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(MovementSystem).GetMethod(nameof(MovementSystem.TeleportToPosRot)),
null,
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnPlayerTeleport_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
HarmonyInstance.Patch(
typeof(DroneMode).GetMethod(nameof(DroneMode.Disable)),
null,
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnDroneModeDisable_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
typeof(IKSystem).GetMethod("OnPreSolverUpdateActiveOffset", BindingFlags.Instance | BindingFlags.NonPublic),
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnOffsetUpdate_Prefix), BindingFlags.Static | BindingFlags.NonPublic))
);
// Whitelist the toggle script
@ -140,6 +142,34 @@ namespace ml_prm
}
}
static void OnAvatarReinitialize_Prefix() => ms_instance?.OnPreAvatarReinitialize();
void OnPreAvatarReinitialize()
{
try
{
if(m_localController != null)
m_localController.OnPreAvatarReinitialize();
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnAvatarReinitialize_Postfix() => ms_instance?.OnPostAvatarReinitialize();
void OnPostAvatarReinitialize()
{
try
{
if(m_localController != null)
m_localController.OnPostAvatarReinitialize();
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnSetupIKScaling_Postfix(ref UnityEngine.Vector3 ___scaleDifference) => ms_instance?.OnSetupIKScaling(___scaleDifference.y);
void OnSetupIKScaling(float p_scaleDifference)
{
@ -228,32 +258,20 @@ namespace ml_prm
}
}
static void OnPlayerTeleport_Postfix() => ms_instance?.OnPlayerTeleport();
void OnPlayerTeleport()
static bool OnOffsetUpdate_Prefix(ref IKSystem __instance) => ms_instance.OnOffsetUpdate(__instance);
bool OnOffsetUpdate(IKSystem p_instance)
{
bool l_result = true;
try
{
if(m_localController != null)
m_localController.OnPlayerTeleport();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
static void OnDroneModeDisable_Postfix() => ms_instance?.OnDroneModeDisable();
void OnDroneModeDisable()
{
try
{
if(m_localController != null)
m_localController.OnDroneModeDisable();
l_result = !m_localController.ShoudlDisableHeadOffset();
}
catch(Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
return l_result;
}
}
}

View file

@ -90,19 +90,19 @@ namespace ml_prm
ms_uiElements.Add(l_modCategory.AddToggle("Fall damage", "Enable ragdoll when falling from height", Settings.FallDamage));
(ms_uiElements[(int)UiIndex.FallDamage] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.FallDamage, state);
ms_uiElements.Add(l_modRoot.AddSlider("Velocity multiplier", "Velocity multiplier upon entering ragdoll state", Settings.VelocityMultiplier, 1f, 50f));
ms_uiElements.Add(l_modCategory.AddSlider("Velocity multiplier", "Velocity multiplier upon entering ragdoll state", Settings.VelocityMultiplier, 1f, 50f));
(ms_uiElements[(int)UiIndex.VelocityMultiplier] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.VelocityMultiplier, value);
ms_uiElements.Add(l_modRoot.AddSlider("Movement drag", "Movement resistance", Settings.MovementDrag, 0f, 50f));
ms_uiElements.Add(l_modCategory.AddSlider("Movement drag", "Movement resistance", Settings.MovementDrag, 0f, 50f));
(ms_uiElements[(int)UiIndex.MovementDrag] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.MovementDrag, value);
ms_uiElements.Add(l_modRoot.AddSlider("Angular movement drag", "Rotation movement resistance", Settings.AngularDrag, 0f, 50f));
ms_uiElements.Add(l_modCategory.AddSlider("Angular movement drag", "Rotation movement resistance", Settings.AngularDrag, 0f, 50f));
(ms_uiElements[(int)UiIndex.AngularDrag] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.AngularDrag, value);
ms_uiElements.Add(l_modRoot.AddSlider("Recover delay (seconds)", "Recover delay for automatic recover", Settings.RecoverDelay, 1f, 10f));
ms_uiElements.Add(l_modCategory.AddSlider("Recover delay (seconds)", "Recover delay for automatic recover", Settings.RecoverDelay, 1f, 10f));
(ms_uiElements[(int)UiIndex.RecoverDelay] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.RecoverDelay, value);
ms_uiElements.Add(l_modRoot.AddSlider("Fall limit", "Height limit for fall damage", Settings.FallLimit, 0f, 100f));
ms_uiElements.Add(l_modCategory.AddSlider("Fall limit", "Height limit for fall damage", Settings.FallLimit, 0f, 100f));
(ms_uiElements[(int)UiIndex.FallLimit] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.FallLimit, value);
l_modCategory.AddButton("Reset settings", "", "Reset mod settings to default").OnPress += Reset;

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.1.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.1.3-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPriority(2)]
[assembly: MelonLoader.MelonOptionalDependencies("BTKUILib")]

View file

@ -1,11 +1,10 @@
using ABI.CCK.Components;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using ABI_RC.Systems.Camera;
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.InputManagement;
using ABI_RC.Systems.MovementSystem;
using ABI_RC.Systems.Movement;
using RootMotion.Dynamics;
using RootMotion.FinalIK;
using System.Collections;
@ -21,6 +20,7 @@ namespace ml_prm
public static RagdollController Instance { get; private set; } = null;
bool m_inVR = false;
VRIK m_vrIK = null;
bool m_applyHipsPosition = false;
bool m_applyHipsRotation = false;
@ -36,12 +36,14 @@ namespace ml_prm
readonly List<System.Tuple<Transform, Transform>> m_boneLinks = null;
readonly List<System.Tuple<CharacterJoint, Vector3>> m_jointAnchors = null;
readonly List<PhysicsInfluencer> m_physicsInfluencers = null;
readonly List<GravityInfluencer> m_gravityInfluencers = null;
bool m_avatarReady = false;
Coroutine m_initCoroutine = null;
Vector3 m_lastPosition = Vector3.zero;
Vector3 m_velocity = Vector3.zero;
Vector3 m_ragdollLastPos = Vector3.zero;
bool m_wasSwimming = false;
RagdollToggle m_avatarRagdollToggle = null;
RagdollTrigger m_ragdollTrigger = null;
@ -62,6 +64,7 @@ namespace ml_prm
m_boneLinks = new List<System.Tuple<Transform, Transform>>();
m_jointAnchors = new List<System.Tuple<CharacterJoint, Vector3>>();
m_physicsInfluencers = new List<PhysicsInfluencer>();
m_gravityInfluencers = new List<GravityInfluencer>();
}
// Unity events
@ -82,7 +85,7 @@ namespace ml_prm
m_puppetRoot.localPosition = Vector3.zero;
m_puppetRoot.localRotation = Quaternion.identity;
m_ragdollTrigger = MovementSystem.Instance.proxyCollider.gameObject.AddComponent<RagdollTrigger>();
m_ragdollTrigger = BetterBetterCharacterController.Instance.NonKinematicProxy.gameObject.AddComponent<RagdollTrigger>();
m_ragdollTrigger.enabled = false;
Settings.MovementDragChange += this.OnMovementDragChange;
@ -92,6 +95,8 @@ namespace ml_prm
Settings.BouncinessChange += this.OnPhysicsMaterialChange;
Settings.BuoyancyChange += this.OnBuoyancyChange;
Settings.FallDamageChange += this.OnFallDamageChange;
BetterBetterCharacterController.OnTeleport.AddListener(this.OnPlayerTeleport);
}
void OnDestroy()
@ -106,6 +111,7 @@ namespace ml_prm
if(m_puppetRoot != null)
Object.Destroy(m_puppetRoot);
m_puppetRoot = null;
m_puppet = null;
m_rigidBodies.Clear();
m_colliders.Clear();
@ -129,14 +135,16 @@ namespace ml_prm
Settings.BouncinessChange -= this.OnPhysicsMaterialChange;
Settings.BuoyancyChange -= this.OnBuoyancyChange;
Settings.FallDamageChange -= this.OnFallDamageChange;
BetterBetterCharacterController.OnTeleport.RemoveListener(this.OnPlayerTeleport);
}
void Update()
{
if(m_avatarReady && !m_enabled && Settings.FallDamage && !MovementSystem.Instance.flying)
if(m_avatarReady && !m_enabled && Settings.FallDamage && !BetterBetterCharacterController.Instance.IsFlying())
{
bool l_grounded = MovementSystem.Instance.IsGroundedRaw();
bool l_inWater = MovementSystem.Instance.GetSubmerged();
bool l_grounded = BetterBetterCharacterController.Instance.IsGrounded();
bool l_inWater = BetterBetterCharacterController.Instance.IsInWater();
if(m_inAir && l_grounded && !l_inWater && (m_inAirDistance > Settings.FallLimit))
{
m_inAirDistance = 0f;
@ -151,12 +159,6 @@ namespace ml_prm
if(m_avatarReady && m_enabled)
{
m_inAirDistance = 0f;
Vector3 l_dif = m_puppetReferences.hips.position - m_ragdollLastPos;
PlayerSetup.Instance.transform.position += l_dif;
m_puppetReferences.hips.position -= l_dif;
m_ragdollLastPos = m_puppetReferences.hips.position;
BodySystem.TrackingPositionWeight = 0f;
}
@ -166,13 +168,13 @@ namespace ml_prm
m_velocity = (m_velocity + (l_pos - m_lastPosition) / Time.deltaTime) * 0.5f;
if(m_inAir)
{
m_inAirDistance += (m_lastPosition - l_pos).y;
m_inAirDistance += (Quaternion.Inverse(PlayerSetup.Instance.transform.rotation) * (m_lastPosition - l_pos)).y;
m_inAirDistance = Mathf.Clamp(m_inAirDistance, 0f, float.MaxValue);
}
m_lastPosition = l_pos;
if(!m_reachedGround && MovementSystem.Instance.IsGrounded())
if(!m_reachedGround && BetterBetterCharacterController.Instance.IsOnGround())
{
m_groundedTime += Time.deltaTime;
if(m_groundedTime >= 0.25f)
@ -196,13 +198,24 @@ namespace ml_prm
if((m_ragdollTrigger != null) && m_ragdollTrigger.GetStateWithReset() && m_avatarReady && !m_enabled && Settings.PointersReaction)
SwitchRagdoll();
if(Settings.Hotkey && Input.GetKeyDown(Settings.HotkeyKey) && !ViewManager.Instance.isGameMenuOpen())
if(Settings.Hotkey && Input.GetKeyDown(Settings.HotkeyKey) && !ViewManager.Instance.IsAnyMenuOpen)
SwitchRagdoll();
if(m_avatarReady && m_enabled && CVRInputManager.Instance.jump && Settings.JumpRecover)
SwitchRagdoll();
}
void FixedUpdate()
{
if(m_avatarReady && m_enabled)
{
Vector3 l_dif = m_puppetReferences.hips.position - m_ragdollLastPos;
PlayerSetup.Instance.transform.position += l_dif;
m_puppetReferences.hips.position -= l_dif;
m_ragdollLastPos = m_puppetReferences.hips.position;
}
}
void LateUpdate()
{
if(m_avatarReady)
@ -223,6 +236,8 @@ namespace ml_prm
// Game events
internal void OnAvatarClear()
{
m_inVR = Utils.IsInVR();
if(m_initCoroutine != null)
{
StopCoroutine(m_initCoroutine);
@ -257,16 +272,20 @@ namespace ml_prm
m_boneLinks.Clear();
m_jointAnchors.Clear();
m_physicsInfluencers.Clear();
m_gravityInfluencers.Clear();
m_reachedGround = true;
m_groundedTime = 0f;
m_downTime = float.MinValue;
m_puppetRoot.localScale = Vector3.one;
m_inAir = false;
m_inAirDistance = 0f;
m_wasSwimming = false;
}
internal void OnAvatarSetup()
{
m_inVR = Utils.IsInVR();
if(PlayerSetup.Instance._animator.isHuman)
{
BipedRagdollReferences l_avatarReferences = BipedRagdollReferences.FromAvatar(PlayerSetup.Instance._animator);
@ -322,9 +341,13 @@ namespace ml_prm
l_body.isKinematic = true;
l_body.angularDrag = Settings.AngularDrag;
l_body.drag = (Utils.IsWorldSafe() ? Settings.MovementDrag : 1f);
l_body.useGravity = (!Utils.IsWorldSafe() || Settings.Gravity);
l_body.useGravity = false;
l_body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
l_body.gameObject.layer = LayerMask.NameToLayer("PlayerLocal");
GravityInfluencer l_gravInfluencer = l_body.gameObject.AddComponent<GravityInfluencer>();
l_gravInfluencer.SetActiveGravity((!Utils.IsWorldSafe() || Settings.Gravity));
m_gravityInfluencers.Add(l_gravInfluencer);
}
CharacterJoint l_joint = l_puppetTransforms[i].GetComponent<CharacterJoint>();
@ -338,9 +361,12 @@ namespace ml_prm
Collider l_collider = l_puppetTransforms[i].GetComponent<Collider>();
if(l_collider != null)
{
Physics.IgnoreCollision(l_collider, MovementSystem.Instance.controller, true);
Physics.IgnoreCollision(l_collider, MovementSystem.Instance.proxyCollider, true);
Physics.IgnoreCollision(l_collider, MovementSystem.Instance.forceCollider, true);
Physics.IgnoreCollision(l_collider, BetterBetterCharacterController.Instance.Collider, true);
Physics.IgnoreCollision(l_collider, BetterBetterCharacterController.Instance.KinematicTriggerProxy.Collider, true);
Physics.IgnoreCollision(l_collider, BetterBetterCharacterController.Instance.NonKinematicProxy.Collider, true);
Physics.IgnoreCollision(l_collider, BetterBetterCharacterController.Instance.SphereProxy.Collider, true);
BetterBetterCharacterController.Instance.IgnoreCollision(l_collider, true);
l_collider.sharedMaterial = m_physicsMaterial;
l_collider.material = m_physicsMaterial;
m_colliders.Add(l_collider);
@ -355,6 +381,7 @@ namespace ml_prm
l_physicsInfluencer.fluidAngularDrag = 1f;
l_physicsInfluencer.enableBuoyancy = true;
l_physicsInfluencer.enableInfluence = false;
l_physicsInfluencer.forceAlignUpright = false;
float mass = l_body.mass;
l_physicsInfluencer.UpdateDensity();
l_body.mass = mass;
@ -402,6 +429,24 @@ namespace ml_prm
OnAngularDragChange(Settings.AngularDrag);
}
internal void OnPreAvatarReinitialize()
{
if(m_avatarReady && m_enabled)
{
m_forcedSwitch = true;
SwitchRagdoll();
m_forcedSwitch = false;
}
}
internal void OnPostAvatarReinitialize()
{
m_inVR = Utils.IsInVR();
m_vrIK = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
if(m_vrIK != null)
m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
}
internal void OnAvatarScaling(float p_scaleDifference)
{
if(m_puppetRoot != null)
@ -460,7 +505,7 @@ namespace ml_prm
internal void OnChangeFlight()
{
if(m_avatarReady && m_enabled && MovementSystem.Instance.flying)
if(m_avatarReady && m_enabled && BetterBetterCharacterController.Instance.IsFlying())
{
m_forcedSwitch = true;
SwitchRagdoll();
@ -470,21 +515,22 @@ namespace ml_prm
}
}
internal void OnPlayerTeleport()
void OnPlayerTeleport(BetterBetterCharacterController.PlayerMoveOffset p_offset)
{
ResetStates();
try
{
ResetStates();
if(m_avatarReady && m_enabled)
m_ragdollLastPos = m_puppetReferences.hips.position;
if(m_avatarReady && m_enabled)
m_ragdollLastPos = m_puppetReferences.hips.position;
}
catch(System.Exception e)
{
MelonLoader.MelonLogger.Error(e);
}
}
internal void OnDroneModeDisable()
{
if(m_avatarReady && m_enabled)
MovementSystem.Instance.canRot = false;
}
// IK updates
// VRIK updates
void OnIKPostUpdate()
{
if(!m_enabled)
@ -529,10 +575,10 @@ namespace ml_prm
if(m_avatarReady)
{
bool l_gravity = (!Utils.IsWorldSafe() || p_state);
foreach(Rigidbody l_body in m_rigidBodies)
l_body.useGravity = l_gravity;
foreach(PhysicsInfluencer l_influencer in m_physicsInfluencers)
l_influencer.enabled = l_gravity;
foreach(GravityInfluencer l_influencer in m_gravityInfluencers)
l_influencer.SetActiveGravity(l_gravity);
if(!l_gravity)
{
@ -584,10 +630,12 @@ namespace ml_prm
{
if(CanRagdoll())
{
if(MovementSystem.Instance.flying)
MovementSystem.Instance.ChangeFlight(false);
MovementSystem.Instance.SetImmobilized(true);
MovementSystem.Instance.ClearFluidVolumes();
m_wasSwimming = BetterBetterCharacterController.Instance.IsSwimming();
if(BetterBetterCharacterController.Instance.IsFlying())
BetterBetterCharacterController.Instance.ChangeFlight(false,true);
BetterBetterCharacterController.Instance.SetImmobilized(true);
BetterBetterCharacterController.Instance.ClearFluidVolumes();
BodySystem.TrackingPositionWeight = 0f;
m_applyHipsPosition = IKSystem.Instance.applyOriginalHipPosition;
IKSystem.Instance.applyOriginalHipPosition = true;
@ -631,13 +679,13 @@ namespace ml_prm
{
if(CanUnragdoll())
{
MovementSystem.Instance.TeleportTo(m_puppetReferences.hips.position, new Vector3(0f, PlayerSetup.Instance.GetActiveCamera().transform.rotation.eulerAngles.y, 0f));
BetterBetterCharacterController.Instance.TeleportPlayerTo(m_puppetReferences.hips.position, PlayerSetup.Instance.GetPlayerRotation().eulerAngles, false, false);
TryRestoreMovement();
if(!Utils.IsWorldSafe())
{
Vector3 l_vec = MovementSystem.Instance.GetAppliedGravity();
Vector3 l_vec = BetterBetterCharacterController.Instance.GetVelocity();
l_vec.y = Mathf.Clamp(l_vec.y, float.MinValue, 0f);
MovementSystem.Instance.SetAppliedGravity(l_vec);
BetterBetterCharacterController.Instance.SetVelocity(l_vec);
}
BodySystem.TrackingPositionWeight = 1f;
IKSystem.Instance.applyOriginalHipPosition = m_applyHipsPosition;
@ -666,6 +714,10 @@ namespace ml_prm
OnMovementDragChange(Settings.MovementDrag);
OnAngularDragChange(Settings.AngularDrag);
// Restore movement if was ragdolled in water and left it
if(m_wasSwimming)
BetterBetterCharacterController.Instance.SetMovementMode(ECM2.Character.MovementMode.Swimming);
m_enabled = false;
}
}
@ -698,7 +750,7 @@ namespace ml_prm
{
bool l_result = m_reachedGround;
l_result &= !BodySystem.isCalibrating;
l_result &= (MovementSystem.Instance.lastSeat == null);
l_result &= !BetterBetterCharacterController.Instance.IsSitting();
l_result &= ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown);
return (l_result || m_forcedSwitch);
}
@ -710,18 +762,19 @@ namespace ml_prm
return (l_result || m_forcedSwitch);
}
internal bool ShoudlDisableHeadOffset()
{
return (m_enabled && (m_vrIK != null));
}
static void TryRestoreMovement()
{
bool l_state = true;
l_state &= ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown);
l_state &= (MovementSystem.Instance.lastSeat == null);
l_state &= !BetterBetterCharacterController.Instance.IsSitting();
if(l_state)
{
MovementSystem.Instance.SetImmobilized(false);
if(PortableCamera.Instance.CheckModActive(typeof(ABI_RC.Systems.Camera.VisualMods.DroneMode)))
MovementSystem.Instance.canRot = false;
}
BetterBetterCharacterController.Instance.SetImmobilized(false);
}
}
}

View file

@ -1,6 +1,6 @@
using ABI.CCK.Components;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.MovementSystem;
using ABI_RC.Systems.Movement;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
@ -9,14 +9,12 @@ namespace ml_prm
{
static class Utils
{
static readonly FieldInfo ms_groundedRaw = typeof(MovementSystem).GetField("_isGroundedRaw", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_appliedGravity = typeof(MovementSystem).GetField("_appliedGravity", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_touchingVolumes = typeof(MovementSystem).GetField("_touchingVolumes", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_touchingVolumes = typeof(BetterBetterCharacterController).GetField("_currentlyTouchingFluidVolumes", 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_influencerSubmergedColliders = typeof(PhysicsInfluencer).GetField("_submergedColliders", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded);
public static bool IsInVR() => ((MetaPort.Instance != null) && MetaPort.Instance.isUsingVr);
public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
public static float GetWorldMovementLimit()
{
@ -31,10 +29,7 @@ namespace ml_prm
return l_result;
}
public static bool IsGroundedRaw(this MovementSystem p_instance) => (bool)ms_groundedRaw.GetValue(p_instance);
public static Vector3 GetAppliedGravity(this MovementSystem p_instance) => (Vector3)ms_appliedGravity.GetValue(p_instance);
public static void SetAppliedGravity(this MovementSystem p_instance, Vector3 p_vec) => ms_appliedGravity.SetValue(p_instance, p_vec);
public static void ClearFluidVolumes(this MovementSystem 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();
public static void CopyGlobal(this Transform p_source, Transform p_target)
{

View file

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<Platforms>x64</Platforms>
<PackageId>PlayerRagdollMod</PackageId>
<Version>1.1.2</Version>
<Version>1.1.3</Version>
<Authors>SDraw</Authors>
<Company>None</Company>
<Product>PlayerRagdollMod</Product>
@ -41,6 +41,11 @@
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="ECM2">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ECM2.dll</HintPath>
<SpecificVersion>false</SpecificVersion>
<Private>false</Private>
</Reference>
<Reference Include="MelonLoader">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
<Private>false</Private>

View file

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

View file

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