mirror of
https://github.com/hanetzer/sdraw_mods_cvr.git
synced 2025-09-05 11:29:23 +00:00
Merge branch 'master' into experimental
This commit is contained in:
commit
c169c7336a
155 changed files with 16128 additions and 11723 deletions
26
README.md
26
README.md
|
@ -1,20 +1,24 @@
|
|||
Merged set of MelonLoader mods for ChilloutVR.
|
||||
|
||||
**Table for game build 2022r171:**
|
||||
**Table for game build 2023r173:**
|
||||
| Full name | Short name | Latest version | Available in [CVRMA](https://github.com/knah/CVRMelonAssistant) |
|
||||
|:---------:|:----------:|:--------------:| :----------------------------------------------------------------|
|
||||
| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | - | ✔ Yes<br>:warning:Broken |
|
||||
| [Desktop Head Tracking](/ml_dht/README.md)| ml_dht | - | ✔ Yes<br>:warning:Broken |
|
||||
| [Desktop Reticle Switch](/ml_drs/README.md)| ml_drs | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_drs.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Extended Game Notifications](/ml_egn/README.md) | ml_egn | 1.0.3 [:arrow_down:](../../releases/latest/download/ml_egn.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.0 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.0.6 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Player Ragdoll Mod](/ml_prm/README.md)| ml_prm | 1.0.6 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
|
||||
| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.6 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| ✔ Yes<br>:hourglass: Update review |
|
||||
| [Avatar Synced Look](/ml_asl/README.md) | ml_asl | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_asl.dll)| ✔ Yes |
|
||||
| [Desktop Head Tracking](/ml_dht/README.md) | ml_dht | 1.2.0 [:arrow_down:](../../releases/latest/download/ml_dht.dll) | ✔ Yes (`Retired` group)<br>:hourglass: Update review |
|
||||
| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.5 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes |
|
||||
| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.0.9 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes |
|
||||
| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.4 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| ✔ Yes |
|
||||
| [Player Ragdoll Mod](/ml_prm/README.md) | ml_prm | 1.1.2 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| ✔ Yes<br>:hourglass: Update review |
|
||||
| [Players Instance Notifier](/ml_pin/README.md) | ml_pin | 1.0.1 [:arrow_down:](../../releases/latest/download/ml_ml_pin.dll)| ✔ Yes<br>:hourglass: Update review |
|
||||
| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| ✔ Yes |
|
||||
|
||||
**Archived mods:**
|
||||
| Full name | Short name | Notes |
|
||||
|:---------:|:----------:|-------|
|
||||
| Avatar Change Info | ml_aci | Superseded by `Extended Game Notifications`
|
||||
| Four Point Tracking | ml_fpt | In-game feature since 2022r170 update
|
||||
| Avatar Change Info | ml_aci | Superseded by `Extended Game Notifications` |
|
||||
| Desktop Reticle Switch | ml_drs | Boring functionality |
|
||||
| Extended Game Notifications | ml_egn | In-game feature sine 2023r172 update |
|
||||
| Four Point Tracking | ml_fpt | In-game feature since 2022r170 update |
|
||||
| Game Main Fixes | ml_gmf | In-game feature sine 2023r172 update |
|
||||
| Server Connection Info | ml_sci | Superseded by `Extended Game Notifications`
|
||||
|
|
BIN
archived/ml_aci/.github/img_01.png
vendored
Normal file
BIN
archived/ml_aci/.github/img_01.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 170 KiB |
58
archived/ml_aci/Main.cs
Normal file
58
archived/ml_aci/Main.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using ABI_RC.Core.EventSystem;
|
||||
using ABI_RC.Core.InteractionSystem;
|
||||
using ABI_RC.Core.Networking;
|
||||
using ABI_RC.Core.Util;
|
||||
using DarkRift;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_aci
|
||||
{
|
||||
public class AvatarChangeInfo : MelonLoader.MelonMod
|
||||
{
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
HarmonyInstance.Patch(
|
||||
typeof(AssetManagement).GetMethod(nameof(AssetManagement.LoadLocalAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarChangeInfo).GetMethod(nameof(OnLocalAvatarLoad), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
|
||||
HarmonyInstance.Patch(
|
||||
typeof(CVRSyncHelper).GetMethod(nameof(CVRSyncHelper.SpawnProp)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarChangeInfo).GetMethod(nameof(OnPropSpawned), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
}
|
||||
|
||||
static void OnLocalAvatarLoad()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(ViewManager.Instance != null)
|
||||
ViewManager.Instance.TriggerPushNotification("Avatar changed", 1f);
|
||||
}
|
||||
catch(System.Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnPropSpawned()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(ViewManager.Instance != null)
|
||||
{
|
||||
if((NetworkManager.Instance != null) && (NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected))
|
||||
ViewManager.Instance.TriggerPushNotification("Prop spawned", 1f);
|
||||
else
|
||||
ViewManager.Instance.TriggerAlert("Prop Error", "Not connected to live instance", -1, true);
|
||||
}
|
||||
}
|
||||
catch(System.Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
using System.Reflection;
|
||||
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_egn.ExtendedGameNotifications), "ExtendedGameNotifications", "1.0.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: AssemblyTitle("AvatarChangeInfo")]
|
||||
[assembly: AssemblyVersion("1.0.3")]
|
||||
[assembly: AssemblyFileVersion("1.0.3")]
|
||||
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_aci.AvatarChangeInfo), "AvatarChangeInfo", "1.0.3", "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)]
|
9
archived/ml_aci/README.md
Normal file
9
archived/ml_aci/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Avatar Change Info
|
||||
This mod shows simple main menu popup upon local player avatar change and prop spawn.
|
||||
|
||||

|
||||
|
||||
# Installation
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
* Get [latest release DLL](../../../releases/latest):
|
||||
* Put `ml_aci.dll` in `Mods` folder of game
|
77
archived/ml_aci/ml_aci.csproj
Normal file
77
archived/ml_aci/ml_aci.csproj
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{1B5ACA07-6266-4C9A-BA30-D4BBE6634846}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ml_aci</RootNamespace>
|
||||
<AssemblyName>ml_aci</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony, Version=2.9.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>F:\games\Steam\common\ChilloutVR\MelonLoader\0Harmony.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>F:\games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="DarkRift">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader, Version=0.5.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>F:\games\Steam\common\ChilloutVR\MelonLoader\MelonLoader.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy /y "$(TargetPath)" "C:\Games\Steam\common\ChilloutVR\Mods\"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
6
archived/ml_aci/ml_aci.csproj.user
Normal file
6
archived/ml_aci/ml_aci.csproj.user
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ReferencePath>C:\Games\Steam\common\ChilloutVR\MelonLoader\;C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\</ReferencePath>
|
||||
</PropertyGroup>
|
||||
</Project>
|
4
archived/ml_drs/Properties/AssemblyInfo.cs
Normal file
4
archived/ml_drs/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_drs.DesktopReticleSwitch), "DesktopReticleSwitch", "1.0.1", "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)]
|
4
archived/ml_egn/Properties/AssemblyInfo.cs
Normal file
4
archived/ml_egn/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_egn.ExtendedGameNotifications), "ExtendedGameNotifications", "1.0.3", "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)]
|
313
archived/ml_fpt/Main.cs
Normal file
313
archived/ml_fpt/Main.cs
Normal file
|
@ -0,0 +1,313 @@
|
|||
using ABI.CCK.Scripts;
|
||||
using ABI_RC.Core.InteractionSystem;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Core.UI;
|
||||
using ABI_RC.Systems.IK;
|
||||
using ABI_RC.Systems.IK.SubSystems;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_fpt
|
||||
{
|
||||
public class FourPointTracking : MelonLoader.MelonMod
|
||||
{
|
||||
static FourPointTracking ms_instance = null;
|
||||
|
||||
bool m_ready = false;
|
||||
|
||||
IndexIK m_indexIK = null;
|
||||
RootMotion.FinalIK.VRIK m_vrIK = null;
|
||||
RuntimeAnimatorController m_runtimeAnimator = null;
|
||||
List<CVRAdvancedSettingsFileProfileValue> m_aasParameters = null;
|
||||
|
||||
bool m_calibrationActive = false;
|
||||
object m_calibrationTask = null;
|
||||
|
||||
int m_hipsTrackerIndex = -1;
|
||||
Transform m_hips = null;
|
||||
|
||||
Dictionary<string, Tuple<Vector3, Quaternion>> m_avatarCalibrations = null;
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
if(ms_instance == null)
|
||||
ms_instance = this;
|
||||
|
||||
m_avatarCalibrations = new Dictionary<string, Tuple<Vector3, Quaternion>>();
|
||||
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(FourPointTracking).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(FourPointTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(WaitForMainMenuView());
|
||||
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator WaitForMainMenuView()
|
||||
{
|
||||
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.RegisterForEvent("MelonMod_FPT_Action_Calibrate", new Action(this.StartCalibration));
|
||||
};
|
||||
|
||||
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
|
||||
};
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator WaitForLocalPlayer()
|
||||
{
|
||||
while(PlayerSetup.Instance == null)
|
||||
yield return null;
|
||||
|
||||
m_indexIK = PlayerSetup.Instance.gameObject.GetComponent<IndexIK>();
|
||||
|
||||
m_ready = true;
|
||||
}
|
||||
|
||||
public override void OnDeinitializeMelon()
|
||||
{
|
||||
if(ms_instance == this)
|
||||
ms_instance = null;
|
||||
|
||||
m_ready = false;
|
||||
m_aasParameters?.Clear();
|
||||
m_aasParameters = null;
|
||||
m_avatarCalibrations?.Clear();
|
||||
m_avatarCalibrations = null;
|
||||
m_hipsTrackerIndex = -1;
|
||||
|
||||
if(m_calibrationTask != null)
|
||||
MelonLoader.MelonCoroutines.Stop(m_calibrationTask);
|
||||
m_calibrationTask = null;
|
||||
}
|
||||
|
||||
void StartCalibration()
|
||||
{
|
||||
if(m_ready && !m_calibrationActive && PlayerSetup.Instance._inVr && !PlayerSetup.Instance.avatarIsLoading && PlayerSetup.Instance._animator.isHuman && !BodySystem.isCalibrating && !BodySystem.isCalibratedAsFullBody)
|
||||
{
|
||||
m_hipsTrackerIndex = GetHipsTracker();
|
||||
if(m_hipsTrackerIndex != -1)
|
||||
{
|
||||
m_avatarCalibrations.Remove(MetaPort.Instance.currentAvatarGuid);
|
||||
|
||||
m_runtimeAnimator = PlayerSetup.Instance._animator.runtimeAnimatorController;
|
||||
m_aasParameters = PlayerSetup.Instance.animatorManager.GetAdditionalSettingsCurrent();
|
||||
PlayerSetup.Instance._animator.runtimeAnimatorController = PlayerSetup.Instance.tPoseAnimatorController;
|
||||
PlayerSetup.Instance.animatorManager.SetAnimator(PlayerSetup.Instance._animator, PlayerSetup.Instance.tPoseAnimatorController);
|
||||
|
||||
m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
|
||||
m_vrIK = PlayerSetup.Instance._animator.GetComponent<RootMotion.FinalIK.VRIK>();
|
||||
|
||||
if(m_vrIK != null)
|
||||
m_vrIK.solver.OnPreUpdate += this.OverrideIKWeight;
|
||||
|
||||
IKSystem.Instance.leftHandModel.SetActive(true);
|
||||
IKSystem.Instance.rightHandModel.SetActive(true);
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowTracker(true);
|
||||
CVR_InteractableManager.enableInteractions = false;
|
||||
|
||||
m_calibrationActive = true;
|
||||
m_calibrationTask = MelonLoader.MelonCoroutines.Start(CalibrationTask());
|
||||
|
||||
ViewManager.Instance.ForceUiStatus(false);
|
||||
ShowHudNotification("Calibration started");
|
||||
}
|
||||
else
|
||||
ShowMenuAlert("No hips tracker detected. Check if tracker has waist role in SteamVR settings.");
|
||||
}
|
||||
else
|
||||
ShowMenuAlert("Calibraton requirements aren't met: be in VR, be not in FBT or avatar calibration, humanoid avatar");
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator CalibrationTask()
|
||||
{
|
||||
while(m_calibrationActive)
|
||||
{
|
||||
if(m_vrIK != null)
|
||||
m_vrIK.enabled = false;
|
||||
|
||||
m_indexIK.enabled = false;
|
||||
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowLine(true, m_hips);
|
||||
|
||||
if((CVRInputManager.Instance.interactLeftValue > 0.9f) && (CVRInputManager.Instance.interactRightValue > 0.9f))
|
||||
{
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target.transform.position = m_hips.position;
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target.transform.rotation = m_hips.rotation;
|
||||
|
||||
m_avatarCalibrations.Add(
|
||||
MetaPort.Instance.currentAvatarGuid,
|
||||
new Tuple<Vector3, Quaternion>(
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target.transform.localPosition,
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target.transform.localRotation
|
||||
)
|
||||
);
|
||||
|
||||
if(m_vrIK != null)
|
||||
{
|
||||
m_vrIK.solver.spine.pelvisTarget = PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target;
|
||||
m_vrIK.solver.spine.pelvisPositionWeight = 1f;
|
||||
m_vrIK.solver.spine.pelvisRotationWeight = 1f;
|
||||
m_vrIK.solver.OnPreUpdate -= this.OverrideIKWeight;
|
||||
m_vrIK.solver.IKPositionWeight = 1f;
|
||||
m_vrIK.enabled = true;
|
||||
}
|
||||
|
||||
m_indexIK.enabled = true;
|
||||
|
||||
PlayerSetup.Instance._animator.runtimeAnimatorController = m_runtimeAnimator;
|
||||
PlayerSetup.Instance.animatorManager.SetAnimator(PlayerSetup.Instance._animator, m_runtimeAnimator);
|
||||
if(m_aasParameters != null)
|
||||
{
|
||||
foreach(var l_param in m_aasParameters)
|
||||
{
|
||||
PlayerSetup.Instance.animatorManager.SetAnimatorParameter(l_param.name, l_param.value);
|
||||
}
|
||||
}
|
||||
|
||||
IKSystem.Instance.leftHandModel.SetActive(false);
|
||||
IKSystem.Instance.rightHandModel.SetActive(false);
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowTracker(false);
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowLine(false);
|
||||
CVR_InteractableManager.enableInteractions = true;
|
||||
|
||||
Reset();
|
||||
|
||||
ShowHudNotification("Calibration completed");
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
m_calibrationTask = null; // Idk if it's safe or not
|
||||
}
|
||||
|
||||
void OverrideIKWeight()
|
||||
{
|
||||
if(m_calibrationActive)
|
||||
{
|
||||
m_vrIK.solver.IKPositionWeight = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
m_vrIK = null;
|
||||
m_runtimeAnimator = null;
|
||||
m_aasParameters = null;
|
||||
m_calibrationActive = false;
|
||||
m_calibrationTask = null;
|
||||
m_hipsTrackerIndex = -1;
|
||||
m_hips = null;
|
||||
}
|
||||
|
||||
static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
|
||||
void OnAvatarClear()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_calibrationActive)
|
||||
{
|
||||
if(m_calibrationTask != null)
|
||||
MelonLoader.MelonCoroutines.Stop(m_calibrationTask);
|
||||
|
||||
m_indexIK.enabled = true;
|
||||
|
||||
IKSystem.Instance.leftHandModel.SetActive(false);
|
||||
IKSystem.Instance.rightHandModel.SetActive(false);
|
||||
|
||||
if(m_hipsTrackerIndex != -1)
|
||||
{
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowTracker(false);
|
||||
PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowLine(false);
|
||||
}
|
||||
CVR_InteractableManager.enableInteractions = true;
|
||||
|
||||
Reset();
|
||||
|
||||
ShowHudNotification("Calibration canceled");
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
|
||||
void OnSetupAvatar()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_ready && PlayerSetup.Instance._inVr && PlayerSetup.Instance._animator.isHuman && !VRTrackerManager.Instance.CheckFullBody())
|
||||
{
|
||||
int l_hipsTracker = GetHipsTracker();
|
||||
if((l_hipsTracker != -1) && m_avatarCalibrations.TryGetValue(MetaPort.Instance.currentAvatarGuid, out var l_stored))
|
||||
{
|
||||
var l_vrIK = PlayerSetup.Instance._animator.GetComponent<RootMotion.FinalIK.VRIK>();
|
||||
if(l_vrIK != null)
|
||||
{
|
||||
l_vrIK.solver.spine.pelvisTarget = PlayerSetup.Instance._trackerManager.trackers[l_hipsTracker].target;
|
||||
l_vrIK.solver.spine.pelvisPositionWeight = 1f;
|
||||
l_vrIK.solver.spine.pelvisRotationWeight = 1f;
|
||||
|
||||
l_vrIK.solver.spine.pelvisTarget.localPosition = l_stored.Item1;
|
||||
l_vrIK.solver.spine.pelvisTarget.localRotation = l_stored.Item2;
|
||||
|
||||
ShowHudNotification("Applied saved calibration");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(System.Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void ShowHudNotification(string p_message)
|
||||
{
|
||||
if(CohtmlHud.Instance != null)
|
||||
CohtmlHud.Instance.ViewDropText("4-Point Tracking", p_message);
|
||||
}
|
||||
|
||||
static void ShowMenuAlert(string p_message)
|
||||
{
|
||||
if(ViewManager.Instance != null)
|
||||
ViewManager.Instance.TriggerAlert("4-Point Tracking", p_message, 0, false);
|
||||
}
|
||||
|
||||
static int GetHipsTracker()
|
||||
{
|
||||
int l_result = -1;
|
||||
for(int i = 0; i < PlayerSetup.Instance._trackerManager.trackerNames.Length; i++)
|
||||
{
|
||||
if((PlayerSetup.Instance._trackerManager.trackerNames[i] == "vive_tracker_waist") && PlayerSetup.Instance._trackerManager.trackers[i].active)
|
||||
{
|
||||
l_result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return l_result;
|
||||
}
|
||||
}
|
||||
}
|
10
archived/ml_fpt/Properties/AssemblyInfo.cs
Normal file
10
archived/ml_fpt/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyTitle("FourPointTracking")]
|
||||
[assembly: AssemblyVersion("1.0.9")]
|
||||
[assembly: AssemblyFileVersion("1.0.9")]
|
||||
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_fpt.FourPointTracking), "FourPointTracking", "1.0.9", "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)]
|
18
archived/ml_fpt/README.md
Normal file
18
archived/ml_fpt/README.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Four Point Tracking
|
||||
This mod adds ability to use 4-point tracking.
|
||||
|
||||
# Installation
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
* Get [latest release DLL](../../../releases/latest):
|
||||
* Put `ml_fpt.dll` in `Mods` folder of game
|
||||
|
||||
# Usage
|
||||
* Be sure that your tracker role is set to `Hips` in SteamVR
|
||||
* Go to `Settings - Implementation - 4-Point Tracking` and press `Calibrate` button
|
||||
* Adjust your tracker in a similar way as in FBT calibration
|
||||
* Press trigger on both controllers
|
||||
|
||||
# Notes
|
||||
* Will be deprecated soon
|
||||
* Calibration is saved per avatar for game session.
|
||||
* AAS parameters are restored after calibration.
|
|
@ -2,7 +2,7 @@
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_amt
|
||||
namespace ml_fpt
|
||||
{
|
||||
static class Scripts
|
||||
{
|
88
archived/ml_fpt/ml_fpt.csproj
Normal file
88
archived/ml_fpt/ml_fpt.csproj
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{EC0A8C41-A429-42CD-B8FA-401A802D4BA6}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ml_fpt</RootNamespace>
|
||||
<AssemblyName>ml_fpt</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="cohtml.Net">
|
||||
<HintPath>C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Cohtml.Runtime">
|
||||
<HintPath>C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader">
|
||||
<HintPath>C:\Games\Steam\common\ChilloutVR\MelonLoader\MelonLoader.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="UnityEngine.AnimationModule">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Scripts.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy /y "$(TargetPath)" "C:\Games\Steam\common\ChilloutVR\Mods\</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
6
archived/ml_fpt/ml_fpt.csproj.user
Normal file
6
archived/ml_fpt/ml_fpt.csproj.user
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ReferencePath>C:\Games\Steam\common\ChilloutVR\MelonLoader\;C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\</ReferencePath>
|
||||
</PropertyGroup>
|
||||
</Project>
|
12
archived/ml_fpt/resources/menu.js
Normal file
12
archived/ml_fpt/resources/menu.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">4-Point Tracking</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="action-btn button" onclick="engine.trigger('MelonMod_FPT_Action_Calibrate');"><img src="gfx/recalibrate.svg">Calibrate</div>
|
||||
`;
|
||||
document.getElementById('settings-implementation').appendChild(l_block);
|
||||
}
|
34
archived/ml_gmf/Fixes/AnimationOverrides.cs
Normal file
34
archived/ml_gmf/Fixes/AnimationOverrides.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using ABI_RC.Core;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_gmf.Fixes
|
||||
{
|
||||
static class AnimationOverrides
|
||||
{
|
||||
internal static void Init(HarmonyLib.Harmony p_instance)
|
||||
{
|
||||
p_instance.Patch(
|
||||
typeof(CVRAnimatorManager).GetMethod(nameof(CVRAnimatorManager.SetOverrideAnimation)),
|
||||
new HarmonyLib.HarmonyMethod(typeof(AnimationOverrides).GetMethod(nameof(OnOverride_Prefix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
p_instance.Patch(
|
||||
typeof(CVRAnimatorManager).GetMethod(nameof(CVRAnimatorManager.RestoreOverrideAnimation)),
|
||||
new HarmonyLib.HarmonyMethod(typeof(AnimationOverrides).GetMethod(nameof(OnOverride_Prefix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
}
|
||||
|
||||
static void OnOverride_Prefix(ref CVRAnimatorManager __instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(__instance.animator != null)
|
||||
__instance.animator.WriteDefaultValues();
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
archived/ml_gmf/Fixes/AvatarOverrides.cs
Normal file
48
archived/ml_gmf/Fixes/AvatarOverrides.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.Player;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_gmf.Fixes
|
||||
{
|
||||
static class AvatarOverrides
|
||||
{
|
||||
internal static void Init(HarmonyLib.Harmony p_instance)
|
||||
{
|
||||
p_instance.Patch(
|
||||
typeof(PlayerSetup).GetMethod("SetupAvatarGeneral", BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarOverrides).GetMethod(nameof(OnSetupAvatarGeneral_Prefix), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
p_instance.Patch(
|
||||
typeof(PuppetMaster).GetMethod(nameof(PuppetMaster.AvatarInstantiated), BindingFlags.Public | BindingFlags.Instance),
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarOverrides).GetMethod(nameof(OnPuppetAvatarInstantiated_Prefix), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
}
|
||||
|
||||
static void OnSetupAvatarGeneral_Prefix(CVRAvatar ____avatarDescriptor)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(____avatarDescriptor.overrides != null)
|
||||
____avatarDescriptor.overrides = UnityEngine.Object.Instantiate(____avatarDescriptor.overrides);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
static void OnPuppetAvatarInstantiated_Prefix(ref PuppetMaster __instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
CVRAvatar l_avatar = __instance.avatarObject.GetComponent<CVRAvatar>();
|
||||
if((l_avatar != null) && (l_avatar.overrides != null))
|
||||
l_avatar.overrides = UnityEngine.Object.Instantiate(l_avatar.overrides);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
archived/ml_gmf/Fixes/PostProccesVolumes.cs
Normal file
31
archived/ml_gmf/Fixes/PostProccesVolumes.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using System.Collections;
|
||||
|
||||
namespace ml_gmf.Fixes
|
||||
{
|
||||
static class PostProccesVolumes
|
||||
{
|
||||
internal static void Init()
|
||||
{
|
||||
MelonLoader.MelonCoroutines.Start(FixVRCameraVolumeTarget());
|
||||
}
|
||||
|
||||
static IEnumerator FixVRCameraVolumeTarget()
|
||||
{
|
||||
while(PlayerSetup.Instance == null)
|
||||
yield return null;
|
||||
|
||||
while(PlayerSetup.Instance.vrCamera == null)
|
||||
yield return null;
|
||||
|
||||
UnityEngine.Rendering.PostProcessing.PostProcessLayer l_layer = null;
|
||||
while(l_layer == null)
|
||||
{
|
||||
l_layer = PlayerSetup.Instance.vrCamera.GetComponent<UnityEngine.Rendering.PostProcessing.PostProcessLayer>();
|
||||
yield return null;
|
||||
}
|
||||
|
||||
l_layer.volumeTrigger = PlayerSetup.Instance.vrCamera.transform;
|
||||
}
|
||||
}
|
||||
}
|
60
archived/ml_gmf/Fixes/ViveControls.cs
Normal file
60
archived/ml_gmf/Fixes/ViveControls.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using ABI_RC.Systems.InputManagement;
|
||||
using ABI_RC.Systems.InputManagement.XR;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_gmf.Fixes
|
||||
{
|
||||
static class ViveControls
|
||||
{
|
||||
internal static void Init(HarmonyLib.Harmony p_instance)
|
||||
{
|
||||
p_instance.Patch(
|
||||
typeof(CVRXRModule).GetMethod("Update_Gestures_Vive", BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(ViveControls).GetMethod(nameof(OnViveGesturesUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
p_instance.Patch(
|
||||
typeof(CVRXRModule).GetMethod(nameof(CVRXRModule.Reset), BindingFlags.Public | BindingFlags.Instance),
|
||||
new HarmonyLib.HarmonyMethod(typeof(ViveControls).GetMethod(nameof(OnCVRXRModuleReset_Prefix), BindingFlags.NonPublic | BindingFlags.Static)),
|
||||
new HarmonyLib.HarmonyMethod(typeof(ViveControls).GetMethod(nameof(OnCVRXRModuleReset_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
}
|
||||
|
||||
static void OnViveGesturesUpdate_Postfix(ref CVRXRModule __instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
float l_mag = ((!__instance.HasEmoteOverride) ? __instance.Primary2DAxis : __instance.EmoteOverride).magnitude;
|
||||
if(__instance.ViveDirectionPressed && (l_mag >= CVRInputManager.VrViveGestureDeadZone))
|
||||
{
|
||||
if(__instance.Grip > 0.5f)
|
||||
{
|
||||
__instance.GestureRaw = -1f;
|
||||
__instance.Gesture = -1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance.GestureRaw = __instance.Trigger;
|
||||
__instance.Gesture = __instance.Trigger;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCVRXRModuleReset_Prefix(ref CVRXRModule __instance, out bool __state)
|
||||
{
|
||||
__state = __instance.ViveDirectionPressed;
|
||||
}
|
||||
|
||||
static void OnCVRXRModuleReset_Postfix(ref CVRXRModule __instance, bool __state)
|
||||
{
|
||||
if((__instance.Type == EXRControllerType.Vive) && CVRInputManager._moduleXR.ViveAdvancedControls)
|
||||
__instance.ViveDirectionPressed = __state;
|
||||
}
|
||||
}
|
||||
}
|
13
archived/ml_gmf/Main.cs
Normal file
13
archived/ml_gmf/Main.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace ml_gmf
|
||||
{
|
||||
public class GameMainFixes : MelonLoader.MelonMod
|
||||
{
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
Fixes.ViveControls.Init(HarmonyInstance);
|
||||
Fixes.AvatarOverrides.Init(HarmonyInstance);
|
||||
Fixes.PostProccesVolumes.Init();
|
||||
Fixes.AnimationOverrides.Init(HarmonyInstance);
|
||||
}
|
||||
}
|
||||
}
|
4
archived/ml_gmf/Properties/AssemblyInfo.cs
Normal file
4
archived/ml_gmf/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_gmf.GameMainFixes), "GameMainFixes", "1.0.0", "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)]
|
17
archived/ml_gmf/README.md
Normal file
17
archived/ml_gmf/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Game Main Fixes
|
||||
This mod fixes some issues that are present in game
|
||||
|
||||
# Installation
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
* Get [latest release DLL](../../../releases/latest):
|
||||
* Put `ml_gmf.dll` in `Mods` folder of game
|
||||
|
||||
# Implemented fixes
|
||||
* Fix of broken `Vive Advanced Controls` game input option
|
||||
* Additional feature: Disables gestures when moving with Vive controllers
|
||||
* Fix of post-processing layer volume trigger for VR camera ([feedback post](https://feedback.abinteractive.net/p/2023r171ex1-post-process-volume-effects-are-applied-based-on-playspace-center-instead-of-camera-s-in-vr-mode))
|
||||
* Fix of shared `AnimatorOverrideController` between same avatars that leads to broken avatar animator
|
||||
* Fix of animation replacement (chairs, etc.) that leads to broken avatar animator ([feedback post](https://feedback.abinteractive.net/p/gestures-getting-stuck-locally-upon-entering-vehicles-chairs))
|
||||
|
||||
# Notes
|
||||
All these fixes are implemented natively in 2023r172ex4 build.
|
52
archived/ml_gmf/ml_gmf.csproj
Normal file
52
archived/ml_gmf/ml_gmf.csproj
Normal file
|
@ -0,0 +1,52 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PackageId>GameMainFixes</PackageId>
|
||||
<Version>1.0.0</Version>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>GameMainFixes</Product>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="PlayerRagdollMod.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Postprocessing.Runtime">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.Postprocessing.Runtime.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AnimationModule">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
BIN
archived/ml_sci/.github/img_01.png
vendored
Normal file
BIN
archived/ml_sci/.github/img_01.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 184 KiB |
31
archived/ml_sci/Main.cs
Normal file
31
archived/ml_sci/Main.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using ABI_RC.Core.UI;
|
||||
using DarkRift.Client;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_sci
|
||||
{
|
||||
public class ServerConnectionInfo : MelonLoader.MelonMod
|
||||
{
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
HarmonyInstance.Patch(
|
||||
typeof(ABI_RC.Core.Networking.NetworkManager).GetMethod("OnGameNetworkConnectionClosed", BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(ServerConnectionInfo).GetMethod(nameof(OnGameNetworkConnectionClosed), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
}
|
||||
|
||||
static void OnGameNetworkConnectionClosed(object __0, DisconnectedEventArgs __1)
|
||||
{
|
||||
try
|
||||
{
|
||||
if((CohtmlHud.Instance != null) && (__1 != null) && (!__1.LocalDisconnect))
|
||||
CohtmlHud.Instance.ViewDropTextImmediate("(Local) Client", "Connection lost", (__1.Error != System.Net.Sockets.SocketError.Success) ? ("Reason: " + __1.Error.ToString()) : "");
|
||||
}
|
||||
catch(System.Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
using System.Reflection;
|
||||
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_drs.DesktopReticleSwitch), "DesktopReticleSwitch", "1.0.1", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: AssemblyTitle("ServerConnectionInfo")]
|
||||
[assembly: AssemblyVersion("1.0.2")]
|
||||
[assembly: AssemblyFileVersion("1.0.2")]
|
||||
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_sci.ServerConnectionInfo), "ServerConnectionInfo", "1.0.2", "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)]
|
9
archived/ml_sci/README.md
Normal file
9
archived/ml_sci/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Server Connection Info
|
||||
This mod shows HUD notification upon server disconnection.
|
||||
|
||||

|
||||
|
||||
# Installation
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
* Get [latest release DLL](../../../releases/latest):
|
||||
* Put `ml_sci.dll` in `Mods` folder of game
|
79
archived/ml_sci/ml_sci.csproj
Normal file
79
archived/ml_sci/ml_sci.csproj
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{E5481D41-196C-4241-AF26-6595EF1863C1}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ml_sci</RootNamespace>
|
||||
<AssemblyName>ml_sci</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony, Version=2.9.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>F:\games\Steam\common\ChilloutVR\MelonLoader\0Harmony.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>F:\games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="DarkRift.Client, Version=2.4.5.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\DarkRift.Client.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader, Version=0.5.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>F:\games\Steam\common\ChilloutVR\MelonLoader\MelonLoader.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy /y "$(TargetPath)" "C:\Games\Steam\common\ChilloutVR\Mods\"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
6
archived/ml_sci/ml_sci.csproj.user
Normal file
6
archived/ml_sci/ml_sci.csproj.user
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ReferencePath>C:\Games\Steam\common\ChilloutVR\MelonLoader\;C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\</ReferencePath>
|
||||
</PropertyGroup>
|
||||
</Project>
|
283
js/mods_extension.js
Normal file
283
js/mods_extension.js
Normal file
|
@ -0,0 +1,283 @@
|
|||
if (typeof modsExtension === 'undefined') {
|
||||
window.modsExtension = []
|
||||
|
||||
// UI elements, modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
modsExtension.createToggle = function (_obj, _callbackName) {
|
||||
let uiElement = {};
|
||||
|
||||
uiElement.obj = _obj;
|
||||
uiElement.callbackName = _callbackName;
|
||||
uiElement.value = _obj.getAttribute('data-current');
|
||||
uiElement.name = _obj.id;
|
||||
uiElement.type = _obj.getAttribute('data-type');
|
||||
|
||||
var self = uiElement;
|
||||
|
||||
uiElement.mouseDown = function (_e) {
|
||||
self.value = self.value == "True" ? "False" : "True";
|
||||
self.updateState();
|
||||
}
|
||||
|
||||
uiElement.updateState = function () {
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', uiElement.mouseDown);
|
||||
|
||||
uiElement.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
uiElement.updateValue = function (value) {
|
||||
self.value = value;
|
||||
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
}
|
||||
|
||||
uiElement.updateValue(uiElement.value);
|
||||
|
||||
return {
|
||||
name: uiElement.name,
|
||||
value: uiElement.getValue,
|
||||
updateValue: uiElement.updateValue
|
||||
}
|
||||
};
|
||||
|
||||
modsExtension.createSlider = function (_obj, _callbackName) {
|
||||
let uiElement = {};
|
||||
|
||||
uiElement.obj = _obj;
|
||||
uiElement.callbackName = _callbackName;
|
||||
uiElement.minValue = parseFloat(_obj.getAttribute('data-min'));
|
||||
uiElement.maxValue = parseFloat(_obj.getAttribute('data-max'));
|
||||
uiElement.percent = 0;
|
||||
uiElement.value = parseFloat(_obj.getAttribute('data-current'));
|
||||
uiElement.dragActive = false;
|
||||
uiElement.name = _obj.id;
|
||||
uiElement.type = _obj.getAttribute('data-type');
|
||||
uiElement.stepSize = _obj.getAttribute('data-stepSize') || 0;
|
||||
uiElement.format = _obj.getAttribute('data-format') || '{value}';
|
||||
|
||||
var self = uiElement;
|
||||
|
||||
if (uiElement.stepSize != 0)
|
||||
uiElement.value = Math.round(uiElement.value / uiElement.stepSize) * uiElement.stepSize;
|
||||
else
|
||||
uiElement.value = Math.round(uiElement.value);
|
||||
|
||||
uiElement.valueLabelBackground = document.createElement('div');
|
||||
uiElement.valueLabelBackground.className = 'valueLabel background';
|
||||
uiElement.valueLabelBackground.innerHTML = uiElement.format.replace('{value}', uiElement.value);
|
||||
uiElement.obj.appendChild(uiElement.valueLabelBackground);
|
||||
|
||||
uiElement.valueBar = document.createElement('div');
|
||||
uiElement.valueBar.className = 'valueBar';
|
||||
uiElement.valueBar.setAttribute('style', 'width: ' + (((uiElement.value - uiElement.minValue) / (uiElement.maxValue - uiElement.minValue)) * 100) + '%;');
|
||||
uiElement.obj.appendChild(uiElement.valueBar);
|
||||
|
||||
uiElement.valueLabelForeground = document.createElement('div');
|
||||
uiElement.valueLabelForeground.className = 'valueLabel foreground';
|
||||
uiElement.valueLabelForeground.innerHTML = uiElement.format.replace('{value}', uiElement.value);
|
||||
uiElement.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((uiElement.value - uiElement.minValue) / (uiElement.maxValue - uiElement.minValue)) * 100) + '%;');
|
||||
uiElement.valueBar.appendChild(uiElement.valueLabelForeground);
|
||||
|
||||
uiElement.mouseDown = function (_e) {
|
||||
self.dragActive = true;
|
||||
self.mouseMove(_e, false);
|
||||
}
|
||||
|
||||
uiElement.mouseMove = function (_e, _write) {
|
||||
if (self.dragActive) {
|
||||
var rect = _obj.getBoundingClientRect();
|
||||
var start = rect.left;
|
||||
var end = rect.right;
|
||||
self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1);
|
||||
var value = self.percent;
|
||||
value *= (self.maxValue - self.minValue);
|
||||
value += self.minValue;
|
||||
if (self.stepSize != 0) {
|
||||
value = Math.round(value / self.stepSize);
|
||||
self.value = value * self.stepSize;
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
}
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
|
||||
engine.call(self.callbackName, self.name, "" + self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
}
|
||||
|
||||
uiElement.mouseUp = function (_e) {
|
||||
self.mouseMove(_e, true);
|
||||
self.dragActive = false;
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', uiElement.mouseDown);
|
||||
document.addEventListener('mousemove', uiElement.mouseMove);
|
||||
document.addEventListener('mouseup', uiElement.mouseUp);
|
||||
|
||||
uiElement.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
uiElement.updateValue = function (value) {
|
||||
if (self.stepSize != 0)
|
||||
self.value = Math.round(value * self.stepSize) / self.stepSize;
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
|
||||
uiElement.displayImperial = function () {
|
||||
var displays = document.querySelectorAll('.imperialDisplay');
|
||||
for (var i = 0; i < displays.length; i++) {
|
||||
var binding = displays[i].getAttribute('data-binding');
|
||||
if (binding == self.name) {
|
||||
var realFeet = ((self.value * 0.393700) / 12);
|
||||
var feet = Math.floor(realFeet);
|
||||
var inches = Math.floor((realFeet - feet) * 12);
|
||||
displays[i].innerHTML = feet + "'" + inches + '''';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: uiElement.name,
|
||||
value: uiElement.getValue,
|
||||
updateValue: uiElement.updateValue
|
||||
}
|
||||
};
|
||||
|
||||
modsExtension.createDropdown = function (_obj, _callbackName) {
|
||||
let uiElement = {};
|
||||
|
||||
uiElement.obj = _obj;
|
||||
uiElement.callbackName = _callbackName;
|
||||
uiElement.value = _obj.getAttribute('data-current');
|
||||
uiElement.options = _obj.getAttribute('data-options').split(',');
|
||||
uiElement.name = _obj.id;
|
||||
uiElement.opened = false;
|
||||
uiElement.keyValue = [];
|
||||
uiElement.type = _obj.getAttribute('data-type');
|
||||
|
||||
uiElement.optionElements = [];
|
||||
|
||||
var self = uiElement;
|
||||
|
||||
uiElement.SelectValue = function (_e) {
|
||||
self.value = _e.target.getAttribute('data-key');
|
||||
self.valueElement.innerHTML = _e.target.getAttribute('data-value');
|
||||
self.globalClose();
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
uiElement.openClick = function (_e) {
|
||||
if (self.obj.classList.contains('open')) {
|
||||
self.obj.classList.remove('open');
|
||||
self.list.setAttribute('style', 'display: none;');
|
||||
} else {
|
||||
self.obj.classList.add('open');
|
||||
self.list.setAttribute('style', 'display: block;');
|
||||
self.opened = true;
|
||||
window.setTimeout(function () { self.opened = false; }, 10);
|
||||
}
|
||||
}
|
||||
|
||||
uiElement.globalClose = function (_e) {
|
||||
if (self.opened) return;
|
||||
self.obj.classList.remove('open');
|
||||
self.list.setAttribute('style', 'display: none;');
|
||||
}
|
||||
|
||||
uiElement.list = document.createElement('div');
|
||||
uiElement.list.className = 'valueList';
|
||||
|
||||
uiElement.updateOptions = function () {
|
||||
self.list.innerHTML = "";
|
||||
for (var i = 0; i < self.options.length; i++) {
|
||||
self.optionElements[i] = document.createElement('div');
|
||||
self.optionElements[i].className = 'listValue';
|
||||
var valuePair = Array.isArray(self.options[i]) ? self.options[i] : self.options[i].split(':');
|
||||
var key = "";
|
||||
var value = "";
|
||||
if (valuePair.length == 1) {
|
||||
key = valuePair[0];
|
||||
value = valuePair[0];
|
||||
} else {
|
||||
key = valuePair[0];
|
||||
value = valuePair[1];
|
||||
}
|
||||
self.keyValue[key] = value;
|
||||
self.optionElements[i].innerHTML = value;
|
||||
self.optionElements[i].setAttribute('data-value', value);
|
||||
self.optionElements[i].setAttribute('data-key', key);
|
||||
self.list.appendChild(self.optionElements[i]);
|
||||
self.optionElements[i].addEventListener('mousedown', self.SelectValue);
|
||||
}
|
||||
|
||||
self.valueElement.innerHTML = self.keyValue[self.value];
|
||||
}
|
||||
|
||||
uiElement.valueElement = document.createElement('div');
|
||||
uiElement.valueElement.className = 'dropdown-value';
|
||||
|
||||
uiElement.updateOptions();
|
||||
|
||||
uiElement.obj.appendChild(uiElement.valueElement);
|
||||
uiElement.obj.appendChild(uiElement.list);
|
||||
uiElement.valueElement.addEventListener('mousedown', uiElement.openClick);
|
||||
document.addEventListener('mousedown', uiElement.globalClose);
|
||||
|
||||
uiElement.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
uiElement.updateValue = function (value) {
|
||||
self.value = value;
|
||||
self.valueElement.innerHTML = self.keyValue[value];
|
||||
}
|
||||
|
||||
uiElement.setOptions = function (options) {
|
||||
self.options = options;
|
||||
}
|
||||
|
||||
return {
|
||||
name: uiElement.name,
|
||||
value: uiElement.getValue,
|
||||
updateValue: uiElement.updateValue,
|
||||
updateOptions: uiElement.updateOptions,
|
||||
setOptions: uiElement.setOptions
|
||||
}
|
||||
};
|
||||
|
||||
modsExtension.settings = []
|
||||
modsExtension.settings.data = []; // [category] -> [entry]
|
||||
modsExtension.addSetting = function (_category, _entry, _obj) {
|
||||
if (modsExtension.settings.data[_category] === undefined)
|
||||
modsExtension.settings.data[_category] = []
|
||||
modsExtension.settings.data[_category][_entry] = _obj
|
||||
};
|
||||
modsExtension.updateSetting = function (_category, _entry, _value) {
|
||||
if ((modsExtension.settings.data[_category] !== undefined) && (modsExtension.settings.data[_category][_entry] !== undefined))
|
||||
modsExtension.settings.data[_category][_entry].updateValue(_value);
|
||||
};
|
||||
engine.on('updateModSetting', modsExtension.updateSetting);
|
||||
}
|
|
@ -8,7 +8,6 @@ namespace ml_amt
|
|||
{
|
||||
public enum ParameterType
|
||||
{
|
||||
Upright,
|
||||
GroundedRaw,
|
||||
Moving
|
||||
}
|
||||
|
@ -32,7 +31,7 @@ namespace ml_amt
|
|||
if(l_regex.IsMatch(l_param.name))
|
||||
{
|
||||
m_hash = l_param.nameHash;
|
||||
m_sync = (l_param.name[0] != '#');
|
||||
m_sync = !l_param.name.StartsWith('#');
|
||||
m_innerType = l_param.type;
|
||||
break;
|
||||
}
|
||||
|
@ -43,10 +42,6 @@ namespace ml_amt
|
|||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case ParameterType.Upright:
|
||||
SetFloat(p_tweaker.GetUpright());
|
||||
break;
|
||||
|
||||
case ParameterType.GroundedRaw:
|
||||
SetBoolean(p_tweaker.GetGroundedRaw());
|
||||
break;
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_amt.Fixes
|
||||
{
|
||||
class AnimatorAnalyzer
|
||||
{
|
||||
bool m_enabled = true;
|
||||
List<AnimatorControllerParameter> m_parameters = null;
|
||||
|
||||
public void AnalyzeFrom(Animator p_animator)
|
||||
{
|
||||
m_enabled = p_animator.enabled;
|
||||
m_parameters = p_animator.parameters?.ToList();
|
||||
|
||||
if(m_parameters != null)
|
||||
{
|
||||
foreach(var l_param in m_parameters)
|
||||
{
|
||||
switch(l_param.type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Bool:
|
||||
case AnimatorControllerParameterType.Trigger:
|
||||
l_param.defaultBool = p_animator.GetBool(l_param.nameHash);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Float:
|
||||
l_param.defaultFloat = p_animator.GetFloat(l_param.nameHash);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Int:
|
||||
l_param.defaultInt = p_animator.GetInteger(l_param.nameHash);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyTo(Animator p_animator)
|
||||
{
|
||||
p_animator.enabled = m_enabled;
|
||||
|
||||
if(m_parameters != null)
|
||||
{
|
||||
foreach(var l_param in m_parameters)
|
||||
{
|
||||
switch(l_param.type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Bool:
|
||||
p_animator.SetBool(l_param.nameHash, l_param.defaultBool);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Float:
|
||||
p_animator.SetFloat(l_param.nameHash, l_param.defaultFloat);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Int:
|
||||
p_animator.SetInteger(l_param.nameHash, l_param.defaultInt);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Trigger:
|
||||
{
|
||||
if(l_param.defaultBool)
|
||||
p_animator.SetTrigger(l_param.nameHash);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEnabled() => m_enabled;
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
using ABI_RC.Core;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_amt.Fixes
|
||||
{
|
||||
static class AnimatorOverrideControllerFix
|
||||
{
|
||||
internal static void Init(HarmonyLib.Harmony p_instance)
|
||||
{
|
||||
// AAS overriding fix
|
||||
p_instance.Patch(
|
||||
typeof(CVRAnimatorManager).GetMethod(nameof(CVRAnimatorManager.SetOverrideAnimation)),
|
||||
new HarmonyLib.HarmonyMethod(typeof(AnimatorOverrideControllerFix).GetMethod(nameof(OnOverride_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
|
||||
new HarmonyLib.HarmonyMethod(typeof(AnimatorOverrideControllerFix).GetMethod(nameof(OnOverride_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
p_instance.Patch(
|
||||
typeof(CVRAnimatorManager).GetMethod(nameof(CVRAnimatorManager.RestoreOverrideAnimation)),
|
||||
new HarmonyLib.HarmonyMethod(typeof(AnimatorOverrideControllerFix).GetMethod(nameof(OnOverride_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
|
||||
new HarmonyLib.HarmonyMethod(typeof(AnimatorOverrideControllerFix).GetMethod(nameof(OnOverride_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
}
|
||||
|
||||
// AnimatorOverrideController runtime animation replacement fix
|
||||
static void OnOverride_Prefix(ref CVRAnimatorManager __instance, out AnimatorAnalyzer __state)
|
||||
{
|
||||
__state = new AnimatorAnalyzer();
|
||||
try
|
||||
{
|
||||
if(Settings.OverrideFix && (__instance.animator != null))
|
||||
{
|
||||
__state.AnalyzeFrom(__instance.animator);
|
||||
if(__state.IsEnabled())
|
||||
__instance.animator.enabled = false;
|
||||
__instance.animator.WriteDefaultValues();
|
||||
}
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
static void OnOverride_Postfix(ref CVRAnimatorManager __instance, AnimatorAnalyzer __state)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(Settings.OverrideFix && (__instance.animator != null))
|
||||
{
|
||||
__state.ApplyTo(__instance.animator);
|
||||
if(__state.IsEnabled())
|
||||
__instance.animator.Update(0f);
|
||||
}
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Systems.MovementSystem;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_amt.Fixes
|
||||
{
|
||||
static class MovementJumpFix
|
||||
{
|
||||
static FieldInfo ms_avatarHeight = typeof(PlayerSetup).GetField("_avatarHeight", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
static float ms_playerHeight = 1f;
|
||||
|
||||
internal static void Init(HarmonyLib.Harmony p_instance)
|
||||
{
|
||||
p_instance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(MovementJumpFix).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
p_instance.Patch(
|
||||
typeof(CVRWorld).GetMethod("SetupWorldRules", BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(MovementJumpFix).GetMethod(nameof(OnWorldRulesSetup_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
p_instance.Patch(
|
||||
typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(MovementJumpFix).GetMethod(nameof(OnSetupIKScaling_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
|
||||
Settings.ScaledJumpChange += OnScaledJumpChange;
|
||||
MelonLoader.MelonCoroutines.Start(WaitForGameSettings());
|
||||
}
|
||||
|
||||
static IEnumerator WaitForGameSettings()
|
||||
{
|
||||
while(MetaPort.Instance == null)
|
||||
yield return null;
|
||||
while(MetaPort.Instance.settings == null)
|
||||
yield return null;
|
||||
|
||||
ms_playerHeight = MetaPort.Instance.settings.GetSettingInt("GeneralPlayerHeight") * 0.01f;
|
||||
MetaPort.Instance.settings.settingIntChanged.AddListener(OnGameSettingIntChange);
|
||||
}
|
||||
|
||||
// Patches
|
||||
static void OnSetupAvatar_Postfix()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetScaledJump(Settings.ScaledJump);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
static void OnWorldRulesSetup_Postfix()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetScaledJump(Settings.ScaledJump);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnSetupIKScaling_Postfix()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetScaledJump(Settings.ScaledJump);
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
|
||||
// Mod settings
|
||||
static void OnScaledJumpChange(bool p_state)
|
||||
{
|
||||
SetScaledJump(p_state);
|
||||
}
|
||||
|
||||
// Game settings
|
||||
static void OnGameSettingIntChange(string p_name, int p_value)
|
||||
{
|
||||
if(p_name == "GeneralPlayerHeight")
|
||||
{
|
||||
ms_playerHeight = p_value * 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
// Arbitrary
|
||||
static void SetScaledJump(bool p_state)
|
||||
{
|
||||
if(Utils.IsWorldSafe())
|
||||
{
|
||||
if(p_state)
|
||||
MovementSystem.Instance.jumpHeight = Mathf.Clamp(Utils.GetWorldJumpHeight() * ((float)ms_avatarHeight.GetValue(PlayerSetup.Instance) / ms_playerHeight), float.MinValue, Utils.GetWorldMovementLimit());
|
||||
else
|
||||
MovementSystem.Instance.jumpHeight = Utils.GetWorldJumpHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.IK.SubSystems;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
@ -35,12 +34,12 @@ namespace ml_amt
|
|||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnCalibrate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod("SetPlaySpaceScale", BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
|
||||
// Fixes
|
||||
Fixes.AnimatorOverrideControllerFix.Init(HarmonyInstance);
|
||||
Fixes.MovementJumpFix.Init(HarmonyInstance);
|
||||
|
||||
ModSupporter.Init();
|
||||
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
|
||||
}
|
||||
|
||||
|
@ -50,12 +49,6 @@ namespace ml_amt
|
|||
yield return null;
|
||||
|
||||
m_localTweaker = PlayerSetup.Instance.gameObject.AddComponent<MotionTweaker>();
|
||||
m_localTweaker.SetCrouchLimit(Settings.CrouchLimit);
|
||||
m_localTweaker.SetProneLimit(Settings.ProneLimit);
|
||||
m_localTweaker.SetIKOverrideFly(Settings.IKOverrideFly);
|
||||
m_localTweaker.SetIKOverrideJump(Settings.IKOverrideJump);
|
||||
m_localTweaker.SetDetectEmotes(Settings.DetectEmotes);
|
||||
m_localTweaker.SetFollowHips(Settings.FollowHips);
|
||||
}
|
||||
|
||||
public override void OnDeinitializeMelon()
|
||||
|
@ -63,6 +56,8 @@ namespace ml_amt
|
|||
if(ms_instance == this)
|
||||
ms_instance = null;
|
||||
|
||||
if(m_localTweaker != null)
|
||||
UnityEngine.Object.Destroy(m_localTweaker);
|
||||
m_localTweaker = null;
|
||||
}
|
||||
|
||||
|
@ -107,5 +102,19 @@ namespace ml_amt
|
|||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnPlayspaceScale_Postfix() => ms_instance?.OnPlayspaceScale();
|
||||
void OnPlayspaceScale()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_localTweaker != null)
|
||||
m_localTweaker.OnPlayspaceScale();
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Linq;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
static class ModSupporter
|
||||
{
|
||||
static bool ms_ragdollMod = false;
|
||||
static bool ms_copycatMod = false;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if(MelonLoader.MelonMod.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "PlayerRagdollMod") != null)
|
||||
MelonLoader.MelonCoroutines.Start(WaitForRagdollInstance());
|
||||
if(MelonLoader.MelonMod.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "PlayerMovementCopycat") != null)
|
||||
MelonLoader.MelonCoroutines.Start(WaitForCopycatInstance());
|
||||
}
|
||||
|
||||
// PlayerRagdollMod support
|
||||
static IEnumerator WaitForRagdollInstance()
|
||||
{
|
||||
while(ml_prm.RagdollController.Instance == null)
|
||||
yield return null;
|
||||
|
||||
ms_ragdollMod = true;
|
||||
}
|
||||
static bool IsRagdolled() => ml_prm.RagdollController.Instance.IsRagdolled();
|
||||
|
||||
// PlayerMovementCopycat support
|
||||
static IEnumerator WaitForCopycatInstance()
|
||||
{
|
||||
while(ml_pmc.PoseCopycat.Instance == null)
|
||||
yield return null;
|
||||
|
||||
ms_copycatMod = true;
|
||||
}
|
||||
static bool IsCopycating() => ml_pmc.PoseCopycat.Instance.IsActive();
|
||||
|
||||
public static bool SkipHipsOverride()
|
||||
{
|
||||
bool l_result = false;
|
||||
l_result |= (ms_ragdollMod && IsRagdolled());
|
||||
l_result |= (ms_copycatMod && IsCopycating());
|
||||
return l_result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Systems.IK;
|
||||
using ABI_RC.Systems.IK.SubSystems;
|
||||
using ABI_RC.Systems.MovementSystem;
|
||||
|
@ -29,7 +28,6 @@ namespace ml_amt
|
|||
int m_locomotionLayer = 0;
|
||||
float m_avatarScale = 1f;
|
||||
Vector3 m_locomotionOffset = Vector3.zero; // Original locomotion offset
|
||||
Transform m_avatarHips = null;
|
||||
bool m_inVR = false;
|
||||
|
||||
bool m_avatarReady = false;
|
||||
|
@ -44,11 +42,10 @@ namespace ml_amt
|
|||
bool m_detectEmotes = true;
|
||||
bool m_emoteActive = false;
|
||||
|
||||
bool m_followHips = true;
|
||||
Vector3 m_hipsToPlayer = Vector3.zero;
|
||||
|
||||
Vector3 m_massCenter = Vector3.zero;
|
||||
|
||||
Transform m_ikLimits = null;
|
||||
|
||||
readonly List<AvatarParameter> m_parameters = null;
|
||||
|
||||
internal MotionTweaker()
|
||||
|
@ -61,26 +58,31 @@ namespace ml_amt
|
|||
{
|
||||
m_inVR = Utils.IsInVR();
|
||||
|
||||
SetCrouchLimit(Settings.CrouchLimit);
|
||||
SetProneLimit(Settings.ProneLimit);
|
||||
SetIKOverrideFly(Settings.IKOverrideFly);
|
||||
SetIKOverrideJump(Settings.IKOverrideJump);
|
||||
SetDetectEmotes(Settings.DetectEmotes);
|
||||
|
||||
Settings.CrouchLimitChange += this.SetCrouchLimit;
|
||||
Settings.ProneLimitChange += this.SetProneLimit;
|
||||
Settings.IKOverrideFlyChange += this.SetIKOverrideFly;
|
||||
Settings.IKOverrideJumpChange += this.SetIKOverrideJump;
|
||||
Settings.DetectEmotesChange += this.SetDetectEmotes;
|
||||
Settings.FollowHipsChange += this.SetFollowHips;
|
||||
Settings.MassCenterChange += this.OnMassCenterChange;
|
||||
|
||||
SetCrouchLimit(Settings.CrouchLimit);
|
||||
SetProneLimit(Settings.ProneLimit);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
m_vrIk = null;
|
||||
m_ikLimits = null;
|
||||
m_parameters.Clear();
|
||||
|
||||
Settings.CrouchLimitChange -= this.SetCrouchLimit;
|
||||
Settings.ProneLimitChange -= this.SetProneLimit;
|
||||
Settings.IKOverrideFlyChange -= this.SetIKOverrideFly;
|
||||
Settings.IKOverrideJumpChange -= this.SetIKOverrideJump;
|
||||
Settings.DetectEmotesChange -= this.SetDetectEmotes;
|
||||
Settings.FollowHipsChange -= this.SetFollowHips;
|
||||
Settings.MassCenterChange -= this.OnMassCenterChange;
|
||||
}
|
||||
|
||||
|
@ -92,11 +94,7 @@ namespace ml_amt
|
|||
m_groundedRaw = MovementSystem.Instance.IsGroundedRaw();
|
||||
m_moving = !Mathf.Approximately(MovementSystem.Instance.movementVector.magnitude, 0f);
|
||||
|
||||
if(m_avatarHips != null)
|
||||
{
|
||||
Vector4 l_hipsToPoint = (PlayerSetup.Instance.transform.GetMatrix().inverse * m_avatarHips.GetMatrix()) * ms_pointVector;
|
||||
m_hipsToPlayer.Set(l_hipsToPoint.x, 0f, l_hipsToPoint.z);
|
||||
}
|
||||
UpdateIKLimits();
|
||||
|
||||
m_emoteActive = false;
|
||||
if(m_detectEmotes && (m_locomotionLayer >= 0))
|
||||
|
@ -126,10 +124,12 @@ namespace ml_amt
|
|||
m_emoteActive = false;
|
||||
m_moving = false;
|
||||
m_locomotionOverride = false;
|
||||
m_hipsToPlayer = Vector3.zero;
|
||||
m_avatarHips = null;
|
||||
m_massCenter = Vector3.zero;
|
||||
m_ikLimits = null;
|
||||
m_parameters.Clear();
|
||||
|
||||
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(Settings.CrouchLimit);
|
||||
PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(Settings.ProneLimit);
|
||||
}
|
||||
|
||||
internal void OnSetupAvatar()
|
||||
|
@ -137,15 +137,17 @@ namespace ml_amt
|
|||
m_inVR = Utils.IsInVR();
|
||||
m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
|
||||
m_locomotionLayer = PlayerSetup.Instance._animator.GetLayerIndex("Locomotion/Emotes");
|
||||
m_avatarHips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
|
||||
m_avatarScale = Mathf.Abs(PlayerSetup.Instance._avatar.transform.localScale.y);
|
||||
|
||||
// Parse animator parameters
|
||||
m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.Upright, PlayerSetup.Instance.animatorManager));
|
||||
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());
|
||||
|
||||
// Avatar custom IK limits
|
||||
m_ikLimits = PlayerSetup.Instance._avatar.transform.Find("[IKLimits]");
|
||||
UpdateIKLimits();
|
||||
|
||||
// Apply VRIK tweaks
|
||||
if(m_vrIk != null)
|
||||
{
|
||||
|
@ -196,9 +198,17 @@ namespace ml_amt
|
|||
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();
|
||||
}
|
||||
|
||||
// IK events
|
||||
void OnIKPreUpdate()
|
||||
{
|
||||
|
@ -238,14 +248,6 @@ namespace ml_amt
|
|||
}
|
||||
}
|
||||
|
||||
bool l_solverActive = !Mathf.Approximately(m_vrIk.solver.IKPositionWeight, 0f);
|
||||
if(l_locomotionOverride && l_solverActive && m_followHips && (!m_moving || (PlayerSetup.Instance.avatarUpright <= PlayerSetup.Instance.avatarProneLimit)) && m_inVR && !BodySystem.isCalibratedAsFullBody && !ModSupporter.SkipHipsOverride())
|
||||
{
|
||||
m_vrIk.solver.plantFeet = false;
|
||||
IKSystem.VrikRootController.enabled = false;
|
||||
PlayerSetup.Instance._avatar.transform.localPosition = m_hipsToPlayer;
|
||||
}
|
||||
|
||||
if(m_locomotionOverride && !l_locomotionOverride)
|
||||
m_vrIk.solver.Reset();
|
||||
m_locomotionOverride = l_locomotionOverride;
|
||||
|
@ -263,11 +265,13 @@ namespace ml_amt
|
|||
// Settings
|
||||
internal void SetCrouchLimit(float p_value)
|
||||
{
|
||||
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Max(Mathf.Clamp01(p_value), PlayerSetup.Instance.avatarProneLimit);
|
||||
if(m_ikLimits == null)
|
||||
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(p_value);
|
||||
}
|
||||
internal void SetProneLimit(float p_value)
|
||||
{
|
||||
PlayerSetup.Instance.avatarProneLimit = Mathf.Min(Mathf.Clamp01(p_value), PlayerSetup.Instance.avatarCrouchLimit);
|
||||
if(m_ikLimits == null)
|
||||
PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(p_value);
|
||||
}
|
||||
internal void SetIKOverrideFly(bool p_state)
|
||||
{
|
||||
|
@ -281,10 +285,6 @@ namespace ml_amt
|
|||
{
|
||||
m_detectEmotes = p_state;
|
||||
}
|
||||
internal void SetFollowHips(bool p_state)
|
||||
{
|
||||
m_followHips = p_state;
|
||||
}
|
||||
void OnMassCenterChange(bool p_state)
|
||||
{
|
||||
if(m_vrIk != null)
|
||||
|
@ -297,6 +297,16 @@ namespace ml_amt
|
|||
return ((m_avatarScale > 0f) ? (PlayerSetup.Instance._avatar.transform.localScale.y / m_avatarScale) : 0f);
|
||||
}
|
||||
|
||||
void UpdateIKLimits()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Parameters access
|
||||
public float GetUpright() => PlayerSetup.Instance.avatarUpright;
|
||||
public bool GetGroundedRaw() => m_groundedRaw;
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
using System.Reflection;
|
||||
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.2.9", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.3.6", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
|
||||
[assembly: MelonLoader.MelonOptionalDependencies("ml_prm", "ml_pmc")]
|
||||
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
|
@ -14,18 +14,10 @@ Available mod's settings in `Settings - IK - Avatar Motion Tweaker`:
|
|||
* **Prone limit:** defines prone limit; default value - `40`.
|
||||
* **IK override while flying:** disables legs locomotion/autostep in fly mode; default value - `true`.
|
||||
* **IK override while jumping:** disables legs locomotion/autostep in jump; default value - `true`.
|
||||
* **Follow hips on IK override:** adjusts avatar position to overcome animation snapping on IK override; default value - `true`.
|
||||
* Note: Works best with animations that have root transform position (XZ) based on center of mass.
|
||||
* Note: Made for four point tracking (head, hands and hips) in mind.
|
||||
* **Detect animations emote tag:** disables avatar's IK entirely if current animator state has `Emote` tag; default value - `true`.
|
||||
* Note: Created as example for [propoused game feature](https://feedback.abinteractive.net/p/disabling-vr-ik-for-emotes-via-animator-state-tag-7b80d963-053a-41c0-86ac-e3d53c61c1e2).
|
||||
* **Adjusted locomotion mass center:** automatically changes IK locomotion center if avatar has toe bones; default value - `true`.
|
||||
* Note: Compatible with [DesktopVRIK](https://github.com/NotAKidOnSteam/DesktopVRIK) and [FuckToes](https://github.com/NotAKidOnSteam/FuckToes).
|
||||
#### Fixes/overhauls options
|
||||
* **Scaled locomotion jump:** scales locomotion jump according to relation between your player settings height and current avatar height (includes avatar scale); default value - `false`.
|
||||
* Note: Disabled in worlds that don't allow flight.
|
||||
* **Fix animation overrides (chairs, etc.):** fixes animations overriding for avatars with AAS; default value - `true`.
|
||||
* Note: This option is made to address [broken animator in chairs and combat worlds issue](https://feedback.abinteractive.net/p/gestures-getting-stuck-locally-upon-entering-vehicles-chairs).
|
||||
|
||||
Available additional parameters for AAS animator:
|
||||
* **`Upright`:** defines linear coefficient between current viewpoint height and avatar's viewpoint height; float, range - [0.0, 1.0].
|
||||
|
@ -38,6 +30,4 @@ Available additional parameters for AAS animator:
|
|||
|
||||
Additional mod's behaviour:
|
||||
* Overrides and fixes IK behaviour in 4PT mode (head, hands and hips).
|
||||
|
||||
# NOTE
|
||||
This is testing update for game build r171, not ready for massive usage yet!
|
||||
* Avatars can have controlled IK crouch and prone limits. For that create `[IKLimits]` GameObject parented to avatar's root. Its local X and Y positions will be used as crouch and prone limits respectively and can be changed via animations. Values should be in range of [0;1].
|
26
ml_amt/ResourcesHandler.cs
Normal file
26
ml_amt/ResourcesHandler.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
static class ResourcesHandler
|
||||
{
|
||||
public static string GetEmbeddedResource(string p_name)
|
||||
{
|
||||
string l_result = "";
|
||||
Assembly l_assembly = Assembly.GetExecutingAssembly();
|
||||
string l_assemblyName = l_assembly.GetName().Name;
|
||||
|
||||
try
|
||||
{
|
||||
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name);
|
||||
StreamReader l_streadReader = new StreamReader(l_libraryStream);
|
||||
l_result = l_streadReader.ReadToEnd();
|
||||
}
|
||||
catch(Exception) { }
|
||||
|
||||
return l_result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using ABI_RC.Core.InteractionSystem;
|
||||
using cohtml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
@ -14,10 +13,7 @@ namespace ml_amt
|
|||
IKOverrideFly,
|
||||
IKOverrideJump,
|
||||
DetectEmotes,
|
||||
FollowHips,
|
||||
ScaledJump,
|
||||
MassCenter,
|
||||
OverrideFix
|
||||
MassCenter
|
||||
};
|
||||
|
||||
public static float CrouchLimit { get; private set; } = 0.75f;
|
||||
|
@ -25,10 +21,7 @@ namespace ml_amt
|
|||
public static bool IKOverrideFly { get; private set; } = true;
|
||||
public static bool IKOverrideJump { get; private set; } = true;
|
||||
public static bool DetectEmotes { get; private set; } = true;
|
||||
public static bool FollowHips { get; private set; } = true;
|
||||
public static bool MassCenter { get; private set; } = true;
|
||||
public static bool ScaledJump { get; private set; } = false;
|
||||
public static bool OverrideFix { get; private set; } = true;
|
||||
|
||||
static MelonLoader.MelonPreferences_Category ms_category = null;
|
||||
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
|
||||
|
@ -38,10 +31,7 @@ namespace ml_amt
|
|||
static public event Action<bool> IKOverrideFlyChange;
|
||||
static public event Action<bool> IKOverrideJumpChange;
|
||||
static public event Action<bool> DetectEmotesChange;
|
||||
static public event Action<bool> FollowHipsChange;
|
||||
static public event Action<bool> MassCenterChange;
|
||||
static public event Action<bool> ScaledJumpChange;
|
||||
static public event Action<bool> OverrideFixChange;
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
|
@ -54,10 +44,7 @@ namespace ml_amt
|
|||
ms_category.CreateEntry(ModSetting.IKOverrideFly.ToString(), IKOverrideFly),
|
||||
ms_category.CreateEntry(ModSetting.IKOverrideJump.ToString(), IKOverrideJump),
|
||||
ms_category.CreateEntry(ModSetting.DetectEmotes.ToString(), DetectEmotes),
|
||||
ms_category.CreateEntry(ModSetting.FollowHips.ToString(), FollowHips),
|
||||
ms_category.CreateEntry(ModSetting.MassCenter.ToString(), MassCenter),
|
||||
ms_category.CreateEntry(ModSetting.ScaledJump.ToString(), ScaledJump),
|
||||
ms_category.CreateEntry(ModSetting.OverrideFix.ToString(), OverrideFix)
|
||||
ms_category.CreateEntry(ModSetting.MassCenter.ToString(), MassCenter)
|
||||
};
|
||||
|
||||
CrouchLimit = ((int)ms_entries[(int)ModSetting.CrouchLimit].BoxedValue) * 0.01f;
|
||||
|
@ -65,10 +52,7 @@ namespace ml_amt
|
|||
IKOverrideFly = (bool)ms_entries[(int)ModSetting.IKOverrideFly].BoxedValue;
|
||||
IKOverrideJump = (bool)ms_entries[(int)ModSetting.IKOverrideJump].BoxedValue;
|
||||
DetectEmotes = (bool)ms_entries[(int)ModSetting.DetectEmotes].BoxedValue;
|
||||
FollowHips = (bool)ms_entries[(int)ModSetting.FollowHips].BoxedValue;
|
||||
MassCenter = (bool)ms_entries[(int)ModSetting.MassCenter].BoxedValue;
|
||||
ScaledJump = (bool)ms_entries[(int)ModSetting.ScaledJump].BoxedValue;
|
||||
OverrideFix = (bool)ms_entries[(int)ModSetting.OverrideFix].BoxedValue;
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(WaitMainMenuUi());
|
||||
}
|
||||
|
@ -84,14 +68,15 @@ namespace ml_amt
|
|||
|
||||
ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_AMT_Call_InpSlider", new Action<string, string>(OnSliderUpdate));
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_AMT_Call_InpToggle", new Action<string, string>(OnToggleUpdate));
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("OnSliderUpdate_" + ms_category.Identifier, new Action<string, string>(OnSliderUpdate));
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action<string, string>(OnToggleUpdate));
|
||||
};
|
||||
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
|
||||
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("updateModSettingAMT", l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -147,33 +132,12 @@ namespace ml_amt
|
|||
}
|
||||
break;
|
||||
|
||||
case ModSetting.FollowHips:
|
||||
{
|
||||
FollowHips = bool.Parse(p_value);
|
||||
FollowHipsChange?.Invoke(FollowHips);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.MassCenter:
|
||||
{
|
||||
MassCenter = bool.Parse(p_value);
|
||||
MassCenterChange?.Invoke(MassCenter);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.ScaledJump:
|
||||
{
|
||||
ScaledJump = bool.Parse(p_value);
|
||||
ScaledJumpChange?.Invoke(ScaledJump);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.OverrideFix:
|
||||
{
|
||||
OverrideFix = bool.Parse(p_value);
|
||||
OverrideFixChange?.Invoke(OverrideFix);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
|
||||
|
|
|
@ -12,28 +12,15 @@ namespace ml_amt
|
|||
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 MethodInfo ms_getSineKeyframes = typeof(IKSolverVR).GetMethod("GetSineKeyframes", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
static FieldInfo ms_cohtmlView = typeof(CohtmlControlledViewDisposable).GetField("_view", 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 IsGrounded(this MovementSystem p_instance) => (bool)ms_grounded.GetValue(MovementSystem.Instance);
|
||||
public static bool IsGroundedRaw(this MovementSystem p_instance) => (bool)ms_groundedRaw.GetValue(MovementSystem.Instance);
|
||||
|
||||
public static bool HasToes(this IKSolverVR p_instance) => (bool)ms_hasToes.GetValue(p_instance);
|
||||
public static Keyframe[] GetSineKeyframes(float p_mag)
|
||||
{
|
||||
return (Keyframe[])ms_getSineKeyframes.Invoke(null, new object[] { p_mag });
|
||||
}
|
||||
|
||||
public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
|
||||
public static float GetWorldJumpHeight()
|
||||
{
|
||||
float l_result = 1f;
|
||||
if(CVRWorld.Instance != null)
|
||||
l_result = CVRWorld.Instance.jumpHeight;
|
||||
return l_result;
|
||||
}
|
||||
public static float GetWorldMovementLimit()
|
||||
{
|
||||
float l_result = 1f;
|
||||
|
@ -47,7 +34,7 @@ namespace ml_amt
|
|||
return l_result;
|
||||
}
|
||||
|
||||
public static void ExecuteScript(this CohtmlControlledViewDisposable p_instance, string p_script) => ((cohtml.Net.View)ms_cohtmlView.GetValue(p_instance))?.ExecuteScript(p_script);
|
||||
static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);
|
||||
|
||||
// Engine extensions
|
||||
public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
|
@ -6,7 +6,7 @@
|
|||
<Company>None</Company>
|
||||
<Product>AvatarMotionTweaker</Product>
|
||||
<PackageId>AvatarMotionTweaker</PackageId>
|
||||
<Version>1.2.9</Version>
|
||||
<Version>1.3.6</Version>
|
||||
<Platforms>x64</Platforms>
|
||||
<AssemblyName>ml_amt</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
@ -16,6 +16,8 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -24,61 +26,67 @@
|
|||
|
||||
<ItemGroup>
|
||||
<None Remove="AvatarMotionTweaker.json" />
|
||||
<None Remove="resources\menu.js" />
|
||||
<None Remove="resources\mod_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
<EmbeddedResource Include="resources\mod_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-firstpass">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="cohtml.Net">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Cohtml.Runtime">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="ml_pmc">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_pmc.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="ml_prm">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_prm.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AnimationModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.PhysicsModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,261 +0,0 @@
|
|||
// Add settings
|
||||
var g_modSettingsAMT = [];
|
||||
|
||||
engine.on('updateModSettingAMT', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsAMT.length; i++) {
|
||||
if (g_modSettingsAMT[i].name == _name) {
|
||||
g_modSettingsAMT[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
function inp_slider_mod_amt(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.minValue = parseFloat(_obj.getAttribute('data-min'));
|
||||
this.maxValue = parseFloat(_obj.getAttribute('data-max'));
|
||||
this.percent = 0;
|
||||
this.value = parseFloat(_obj.getAttribute('data-current'));
|
||||
this.dragActive = false;
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
this.stepSize = _obj.getAttribute('data-stepSize') || 0;
|
||||
this.format = _obj.getAttribute('data-format') || '{value}';
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this.stepSize != 0)
|
||||
this.value = Math.round(this.value / this.stepSize) * this.stepSize;
|
||||
else
|
||||
this.value = Math.round(this.value);
|
||||
|
||||
this.valueLabelBackground = document.createElement('div');
|
||||
this.valueLabelBackground.className = 'valueLabel background';
|
||||
this.valueLabelBackground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.obj.appendChild(this.valueLabelBackground);
|
||||
|
||||
this.valueBar = document.createElement('div');
|
||||
this.valueBar.className = 'valueBar';
|
||||
this.valueBar.setAttribute('style', 'width: ' + (((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.obj.appendChild(this.valueBar);
|
||||
|
||||
this.valueLabelForeground = document.createElement('div');
|
||||
this.valueLabelForeground.className = 'valueLabel foreground';
|
||||
this.valueLabelForeground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.valueBar.appendChild(this.valueLabelForeground);
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.dragActive = true;
|
||||
self.mouseMove(_e, false);
|
||||
}
|
||||
|
||||
this.mouseMove = function (_e, _write) {
|
||||
if (self.dragActive) {
|
||||
var rect = _obj.getBoundingClientRect();
|
||||
var start = rect.left;
|
||||
var end = rect.right;
|
||||
self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1);
|
||||
var value = self.percent;
|
||||
value *= (self.maxValue - self.minValue);
|
||||
value += self.minValue;
|
||||
if (self.stepSize != 0) {
|
||||
value = Math.round(value / self.stepSize);
|
||||
self.value = value * self.stepSize;
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
}
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
|
||||
engine.call(self.callbackName, self.name, "" + self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
}
|
||||
|
||||
this.mouseUp = function (_e) {
|
||||
self.mouseMove(_e, true);
|
||||
self.dragActive = false;
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
document.addEventListener('mousemove', this.mouseMove);
|
||||
document.addEventListener('mouseup', this.mouseUp);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
if (self.stepSize != 0)
|
||||
self.value = Math.round(value * self.stepSize) / self.stepSize;
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
|
||||
this.displayImperial = function () {
|
||||
var displays = document.querySelectorAll('.imperialDisplay');
|
||||
for (var i = 0; i < displays.length; i++) {
|
||||
var binding = displays[i].getAttribute('data-binding');
|
||||
if (binding == self.name) {
|
||||
var realFeet = ((self.value * 0.393700) / 12);
|
||||
var feet = Math.floor(realFeet);
|
||||
var inches = Math.floor((realFeet - feet) * 12);
|
||||
displays[i].innerHTML = feet + "'" + inches + '''';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
function inp_toggle_mod_amt(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.value = self.value == "True" ? "False" : "True";
|
||||
self.updateState();
|
||||
}
|
||||
|
||||
this.updateState = function () {
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
}
|
||||
|
||||
this.updateValue(this.value);
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Avatar Motion Tweaker</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Crouch limit: </div>
|
||||
<div class ="option-input">
|
||||
<div id="CrouchLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="75"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Prone limit: </div>
|
||||
<div class ="option-input">
|
||||
<div id="ProneLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">IK override while flying: </div>
|
||||
<div class ="option-input">
|
||||
<div id="IKOverrideFly" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">IK override while jumping: </div>
|
||||
<div class ="option-input">
|
||||
<div id="IKOverrideJump" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Follow hips on IK override: </div>
|
||||
<div class ="option-input">
|
||||
<div id="FollowHips" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Detect animations emote tag: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DetectEmotes" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Adjusted locomotion mass center: </div>
|
||||
<div class ="option-input">
|
||||
<div id="MassCenter" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4><p style="color: #7F7F7F">Avatar independent game fixes/overhauls</p></h4><br>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Scaled locomotion jump: </div>
|
||||
<div class ="option-input">
|
||||
<div id="ScaledJump" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Fix animator overrides (chairs, etc.): </div>
|
||||
<div class ="option-input">
|
||||
<div id="OverrideFix" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-ik').appendChild(l_block);
|
||||
|
||||
// Update sliders in new menu block
|
||||
let l_sliders = l_block.querySelectorAll('.inp_slider');
|
||||
for (var i = 0; i < l_sliders.length; i++) {
|
||||
g_modSettingsAMT[g_modSettingsAMT.length] = new inp_slider_mod_amt(l_sliders[i], 'MelonMod_AMT_Call_InpSlider');
|
||||
}
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsAMT[g_modSettingsAMT.length] = new inp_toggle_mod_amt(l_toggles[i], 'MelonMod_AMT_Call_InpToggle');
|
||||
}
|
||||
}
|
54
ml_amt/resources/mod_menu.js
Normal file
54
ml_amt/resources/mod_menu.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Avatar Motion Tweaker</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Crouch limit: </div>
|
||||
<div class ="option-input">
|
||||
<div id="CrouchLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="75"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Prone limit: </div>
|
||||
<div class ="option-input">
|
||||
<div id="ProneLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">IK override while flying: </div>
|
||||
<div class ="option-input">
|
||||
<div id="IKOverrideFly" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Detect animations emote tag: </div>
|
||||
<div class ="option-input">
|
||||
<div id="DetectEmotes" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Adjusted locomotion mass center: </div>
|
||||
<div class ="option-input">
|
||||
<div id="MassCenter" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-ik').appendChild(l_block);
|
||||
|
||||
// Toggles
|
||||
for (let l_toggle of l_block.querySelectorAll('.inp_toggle'))
|
||||
modsExtension.addSetting('AMT', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_AMT'));
|
||||
|
||||
// Sliders
|
||||
for (let l_slider of l_block.querySelectorAll('.inp_slider'))
|
||||
modsExtension.addSetting('AMT', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_AMT'));
|
||||
}
|
25
ml_asl/Main.cs
Normal file
25
ml_asl/Main.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_asl
|
||||
{
|
||||
public class AvatarSyncedLook : MelonLoader.MelonMod
|
||||
{
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
Settings.Init();
|
||||
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod("UpdatePlayerAvatarMovementData", BindingFlags.NonPublic | BindingFlags.Instance),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarSyncedLook).GetMethod(nameof(OnPlayerAvatarMovementDataUpdate_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
}
|
||||
|
||||
static void OnPlayerAvatarMovementDataUpdate_Postfix(ref PlayerSetup __instance, PlayerAvatarMovementData ____playerAvatarMovementData)
|
||||
{
|
||||
if(Settings.Enabled && (__instance.eyeMovement != null))
|
||||
____playerAvatarMovementData.EyeTrackingOverride = true;
|
||||
}
|
||||
}
|
||||
}
|
4
ml_asl/Properties/AssemblyInfo.cs
Normal file
4
ml_asl/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_asl.AvatarSyncedLook), "AvatarSyncedLook", "1.0.0", "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)]
|
14
ml_asl/README.md
Normal file
14
ml_asl/README.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Avatar Synced Look
|
||||
This mod Forces local player's eyes look direction to be synced for remote players.
|
||||
|
||||
# Installation
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
* Get [latest release DLL](../../../releases/latest):
|
||||
* Put `ml_asl.dll` in `Mods` folder of game
|
||||
|
||||
# Usage
|
||||
Available mod's settings in `Settings - Interactions - Avatar Synced Look`:
|
||||
* **Enabled:** sets eyes look direction to be synced or locally generated on remote users side; `true` by default.
|
||||
|
||||
# Notes
|
||||
* Remote users with [EyeMovementFix](https://github.com/kafeijao/Kafe_CVR_Mods/tree/master/EyeMovementFix) installed can't see synced look direction.
|
26
ml_asl/ResourcesHandler.cs
Normal file
26
ml_asl/ResourcesHandler.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_asl
|
||||
{
|
||||
static class ResourcesHandler
|
||||
{
|
||||
public static string GetEmbeddedResource(string p_name)
|
||||
{
|
||||
string l_result = "";
|
||||
Assembly l_assembly = Assembly.GetExecutingAssembly();
|
||||
string l_assemblyName = l_assembly.GetName().Name;
|
||||
|
||||
try
|
||||
{
|
||||
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name);
|
||||
StreamReader l_streadReader = new StreamReader(l_libraryStream);
|
||||
l_result = l_streadReader.ReadToEnd();
|
||||
}
|
||||
catch(Exception) { }
|
||||
|
||||
return l_result;
|
||||
}
|
||||
}
|
||||
}
|
75
ml_asl/Settings.cs
Normal file
75
ml_asl/Settings.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using ABI_RC.Core.InteractionSystem;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ml_asl
|
||||
{
|
||||
static class Settings
|
||||
{
|
||||
public enum ModSetting
|
||||
{
|
||||
Enabled = 0
|
||||
}
|
||||
|
||||
public static bool Enabled { get; private set; } = true;
|
||||
|
||||
static MelonLoader.MelonPreferences_Category ms_category = null;
|
||||
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
|
||||
|
||||
static public event Action<bool> EnabledChange;
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
ms_category = MelonLoader.MelonPreferences.CreateCategory("ASL", null, true);
|
||||
|
||||
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
|
||||
{
|
||||
ms_category.CreateEntry(ModSetting.Enabled.ToString(), Enabled)
|
||||
};
|
||||
|
||||
Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue;
|
||||
|
||||
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.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 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;
|
||||
}
|
||||
|
||||
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
ml_asl/Utils.cs
Normal file
12
ml_asl/Utils.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using ABI_RC.Core.UI;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_asl
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", 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);
|
||||
}
|
||||
}
|
63
ml_asl/ml_asl.csproj
Normal file
63
ml_asl/ml_asl.csproj
Normal file
|
@ -0,0 +1,63 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PackageId>AvatarSyncedLook</PackageId>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>AvatarSyncedLook</Product>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="resources\mod_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-firstpass">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="cohtml.Net">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Cohtml.Runtime">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" />
|
||||
<EmbeddedResource Include="resources\mod_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
22
ml_asl/resources/mod_menu.js
Normal file
22
ml_asl/resources/mod_menu.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Avatar Synced Look</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Enabled: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Enabled" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-interaction').appendChild(l_block);
|
||||
|
||||
// Toggles
|
||||
for (let l_toggle of l_block.querySelectorAll('.inp_toggle'))
|
||||
modsExtension.addSetting('ASL', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_ASL'));
|
||||
}
|
|
@ -14,13 +14,10 @@ namespace ml_dht
|
|||
|
||||
bool m_enabled = false;
|
||||
bool m_headTracking = true;
|
||||
bool m_blinking = true;
|
||||
bool m_eyeTracking = true;
|
||||
float m_smoothing = 0.5f;
|
||||
bool m_mirrored = false;
|
||||
bool m_faceOverride = true;
|
||||
|
||||
CVRAvatar m_avatarDescriptor = null;
|
||||
Transform m_camera = null;
|
||||
LookAtIK m_lookIK = null;
|
||||
Transform m_headBone = null;
|
||||
|
||||
|
@ -28,8 +25,6 @@ namespace ml_dht
|
|||
Quaternion m_headRotation;
|
||||
Vector2 m_gazeDirection;
|
||||
float m_blinkProgress = 0f;
|
||||
Vector2 m_mouthShapes;
|
||||
float m_eyebrowsProgress = 0f;
|
||||
|
||||
Quaternion m_bindRotation;
|
||||
Quaternion m_lastHeadRotation;
|
||||
|
@ -37,35 +32,29 @@ namespace ml_dht
|
|||
// Unity events
|
||||
void Start()
|
||||
{
|
||||
SetEnabled(Settings.Enabled);
|
||||
SetHeadTracking(Settings.HeadTracking);
|
||||
SetSmoothing(Settings.Smoothing);
|
||||
|
||||
Settings.EnabledChange += this.SetEnabled;
|
||||
Settings.HeadTrackingChange += this.SetHeadTracking;
|
||||
Settings.EyeTrackingChange += this.SetEyeTracking;
|
||||
Settings.BlinkingChange += this.SetBlinking;
|
||||
Settings.SmoothingChange += this.SetSmoothing;
|
||||
Settings.MirroredChange += this.SetMirrored;
|
||||
Settings.FaceOverrideChange += this.SetFaceOverride;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
Settings.EnabledChange -= this.SetEnabled;
|
||||
Settings.HeadTrackingChange -= this.SetHeadTracking;
|
||||
Settings.EyeTrackingChange -= this.SetEyeTracking;
|
||||
Settings.BlinkingChange -= this.SetBlinking;
|
||||
Settings.SmoothingChange -= this.SetSmoothing;
|
||||
Settings.MirroredChange -= this.SetMirrored;
|
||||
Settings.FaceOverrideChange -= this.SetFaceOverride;
|
||||
}
|
||||
|
||||
// Tracking updates
|
||||
public void UpdateTrackingData(ref TrackingData p_data)
|
||||
{
|
||||
m_headPosition.Set(p_data.m_headPositionX * (m_mirrored ? -1f : 1f), p_data.m_headPositionY, p_data.m_headPositionZ);
|
||||
m_headRotation.Set(p_data.m_headRotationX, p_data.m_headRotationY * (m_mirrored ? -1f : 1f), p_data.m_headRotationZ * (m_mirrored ? -1f : 1f), p_data.m_headRotationW);
|
||||
m_gazeDirection.Set(m_mirrored ? (1f - p_data.m_gazeX) : p_data.m_gazeX, p_data.m_gazeY);
|
||||
m_headPosition.Set(p_data.m_headPositionX * (Settings.Mirrored ? -1f : 1f), p_data.m_headPositionY, p_data.m_headPositionZ);
|
||||
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;
|
||||
m_mouthShapes.Set(p_data.m_mouthOpen, p_data.m_mouthShape);
|
||||
m_eyebrowsProgress = p_data.m_brows;
|
||||
}
|
||||
|
||||
void OnLookIKPostUpdate()
|
||||
|
@ -80,48 +69,9 @@ namespace ml_dht
|
|||
}
|
||||
|
||||
// Game events
|
||||
internal void OnEyeControllerUpdate(CVREyeController p_component)
|
||||
{
|
||||
if(m_enabled)
|
||||
{
|
||||
// Gaze
|
||||
if(m_eyeTracking)
|
||||
{
|
||||
Transform l_camera = PlayerSetup.Instance.GetActiveCamera().transform;
|
||||
|
||||
p_component.manualViewTarget = true;
|
||||
p_component.targetViewPosition = l_camera.position + l_camera.rotation * new Vector3((m_gazeDirection.x - 0.5f) * 2f, (m_gazeDirection.y - 0.5f) * 2f, 1f);
|
||||
}
|
||||
|
||||
// Blink
|
||||
if(m_blinking)
|
||||
{
|
||||
p_component.manualBlinking = true;
|
||||
p_component.blinkProgress = m_blinkProgress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnFaceTrackingUpdate(CVRFaceTracking p_component)
|
||||
{
|
||||
if(m_enabled && m_faceOverride)
|
||||
{
|
||||
if(m_avatarDescriptor != null)
|
||||
m_avatarDescriptor.useVisemeLipsync = false;
|
||||
|
||||
float l_weight = Mathf.Clamp01(Mathf.InverseLerp(0.25f, 1f, Mathf.Abs(m_mouthShapes.y))) * 100f;
|
||||
|
||||
p_component.BlendShapeValues[(int)LipShape_v2.Jaw_Open] = m_mouthShapes.x * 100f;
|
||||
p_component.BlendShapeValues[(int)LipShape_v2.Mouth_Pout] = ((m_mouthShapes.y > 0f) ? l_weight : 0f);
|
||||
p_component.BlendShapeValues[(int)LipShape_v2.Mouth_Smile_Left] = ((m_mouthShapes.y < 0f) ? l_weight : 0f);
|
||||
p_component.BlendShapeValues[(int)LipShape_v2.Mouth_Smile_Right] = ((m_mouthShapes.y < 0f) ? l_weight : 0f);
|
||||
p_component.LipSyncWasUpdated = true;
|
||||
p_component.UpdateLipShapes();
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnSetupAvatar()
|
||||
{
|
||||
m_camera = PlayerSetup.Instance.GetActiveCamera().transform;
|
||||
m_avatarDescriptor = PlayerSetup.Instance._avatar.GetComponent<CVRAvatar>();
|
||||
m_headBone = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Head);
|
||||
m_lookIK = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
|
||||
|
@ -142,44 +92,53 @@ namespace ml_dht
|
|||
m_bindRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
internal void OnEyeControllerUpdate(CVREyeController p_component)
|
||||
{
|
||||
if(m_enabled)
|
||||
{
|
||||
// Gaze
|
||||
if(Settings.EyeTracking && (m_camera != null))
|
||||
{
|
||||
p_component.manualViewTarget = true;
|
||||
p_component.targetViewPosition = m_camera.position + m_camera.rotation * new Vector3((m_gazeDirection.x - 0.5f) * 2f, (m_gazeDirection.y - 0.5f) * 2f, 1f);
|
||||
}
|
||||
|
||||
// Blink
|
||||
if(Settings.Blinking)
|
||||
{
|
||||
p_component.manualBlinking = true;
|
||||
p_component.blinkProgress = m_blinkProgress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Settings
|
||||
internal void SetEnabled(bool p_state)
|
||||
void SetEnabled(bool p_state)
|
||||
{
|
||||
if(m_enabled != p_state)
|
||||
{
|
||||
m_enabled = p_state;
|
||||
if(m_enabled && m_headTracking)
|
||||
m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation);
|
||||
TryRestoreHeadRotation();
|
||||
}
|
||||
}
|
||||
internal void SetHeadTracking(bool p_state)
|
||||
void SetHeadTracking(bool p_state)
|
||||
{
|
||||
if(m_headTracking != p_state)
|
||||
{
|
||||
m_headTracking = p_state;
|
||||
if(m_enabled && m_headTracking)
|
||||
m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation);
|
||||
TryRestoreHeadRotation();
|
||||
}
|
||||
}
|
||||
internal void SetEyeTracking(bool p_state)
|
||||
{
|
||||
m_eyeTracking = p_state;
|
||||
}
|
||||
internal void SetBlinking(bool p_state)
|
||||
{
|
||||
m_blinking = p_state;
|
||||
}
|
||||
internal void SetSmoothing(float p_value)
|
||||
void SetSmoothing(float p_value)
|
||||
{
|
||||
m_smoothing = 1f - Mathf.Clamp(p_value, 0f, 0.99f);
|
||||
}
|
||||
internal void SetMirrored(bool p_state)
|
||||
|
||||
// Arbitrary
|
||||
void TryRestoreHeadRotation()
|
||||
{
|
||||
m_mirrored = p_state;
|
||||
}
|
||||
internal void SetFaceOverride(bool p_state)
|
||||
{
|
||||
m_faceOverride = p_state;
|
||||
if(m_enabled && m_headTracking)
|
||||
m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Systems.FaceTracking;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_dht
|
||||
|
@ -8,10 +9,7 @@ namespace ml_dht
|
|||
{
|
||||
static DesktopHeadTracking ms_instance = null;
|
||||
|
||||
MemoryMapReader m_mapReader = null;
|
||||
byte[] m_buffer = null;
|
||||
TrackingData m_trackingData;
|
||||
|
||||
TrackingModule m_trackingModule = null;
|
||||
HeadTracked m_localTracked = null;
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
|
@ -21,10 +19,7 @@ namespace ml_dht
|
|||
|
||||
Settings.Init();
|
||||
|
||||
m_mapReader = new MemoryMapReader();
|
||||
m_buffer = new byte[1024];
|
||||
|
||||
m_mapReader.Open("head/data");
|
||||
m_trackingModule = new TrackingModule();
|
||||
|
||||
// Patches
|
||||
HarmonyInstance.Patch(
|
||||
|
@ -37,33 +32,27 @@ namespace ml_dht
|
|||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(WaitForInstances());
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator WaitForInstances()
|
||||
{
|
||||
while(MetaPort.Instance == null)
|
||||
yield return null;
|
||||
|
||||
while(PlayerSetup.Instance == null)
|
||||
yield return null;
|
||||
|
||||
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),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnEyeControllerUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(CVRFaceTracking).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnFaceTrackingUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(WaitForPlayer());
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator WaitForPlayer()
|
||||
{
|
||||
while(PlayerSetup.Instance == null)
|
||||
yield return null;
|
||||
|
||||
m_localTracked = PlayerSetup.Instance.gameObject.AddComponent<HeadTracked>();
|
||||
m_localTracked.SetEnabled(Settings.Enabled);
|
||||
m_localTracked.SetHeadTracking(Settings.HeadTracking);
|
||||
m_localTracked.SetEyeTracking(Settings.EyeTracking);
|
||||
m_localTracked.SetBlinking(Settings.Blinking);
|
||||
m_localTracked.SetMirrored(Settings.Mirrored);
|
||||
m_localTracked.SetSmoothing(Settings.Smoothing);
|
||||
m_localTracked.SetFaceOverride(Settings.FaceOverride);
|
||||
}
|
||||
|
||||
public override void OnDeinitializeMelon()
|
||||
|
@ -71,19 +60,17 @@ namespace ml_dht
|
|||
if(ms_instance == this)
|
||||
ms_instance = null;
|
||||
|
||||
m_mapReader?.Close();
|
||||
m_mapReader = null;
|
||||
m_buffer = null;
|
||||
m_trackingModule = null;
|
||||
m_localTracked = null;
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
if(Settings.Enabled && m_mapReader.Read(ref m_buffer))
|
||||
if(Settings.Enabled && (m_trackingModule != null))
|
||||
{
|
||||
m_trackingData = TrackingData.ToObject(m_buffer);
|
||||
m_trackingModule.Update();
|
||||
if(m_localTracked != null)
|
||||
m_localTracked.UpdateTrackingData(ref m_trackingData);
|
||||
m_localTracked.UpdateTrackingData(ref m_trackingModule.GetLatestTrackingData());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,19 +115,5 @@ namespace ml_dht
|
|||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnFaceTrackingUpdate_Postfix(ref CVRFaceTracking __instance) => ms_instance?.OnFaceTrackingUpdate(__instance);
|
||||
void OnFaceTrackingUpdate(CVRFaceTracking p_component)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(p_component.isLocal && (m_localTracked != null))
|
||||
m_localTracked.OnFaceTrackingUpdate(p_component);
|
||||
}
|
||||
catch(System.Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.1.2-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.2.0", "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)]
|
|
@ -7,8 +7,8 @@ Refer to `TrackingData.cs` for reference in case of implementing own software.
|
|||
# Features
|
||||
* Head rotation
|
||||
* Eyes gaze direction
|
||||
* Basic mouth shapes: open, smile and pout
|
||||
* Blinking
|
||||
* Basic mouth shapes
|
||||
|
||||
# Installation
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
|
@ -19,11 +19,13 @@ Refer to `TrackingData.cs` for reference in case of implementing own software.
|
|||
Available mod's settings in `Settings - Implementation - Desktop Head Tracking`:
|
||||
* **Enabled:** enables mod's activity; default value - `false`.
|
||||
* **Use head tracking:** enables head tracking; default value - `true`.
|
||||
* **Use eyes tracking:** uses eyes tracking from data; 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`.
|
||||
* **Movement smoothing:** smoothing factor between new and old movement data; default value - `50`.
|
||||
* **Override face tracking:** overrides and activates avatar's `VRC Face Tracking` components. List of used shapes: `Jaw_Open`, `Mouth_Pout`, `Mouth_Smile_Left`, `Mouth_Smile_Right`; default value - `true`.
|
||||
|
||||
# Known compatible tracking software
|
||||
* [VSeeFace](https://www.vseeface.icu) with [Tracking Data Parser mod](https://github.com/SDraw/ml_mods_vsf)
|
||||
|
|
|
@ -4,9 +4,9 @@ using System.Reflection;
|
|||
|
||||
namespace ml_dht
|
||||
{
|
||||
static class Scripts
|
||||
static class ResourcesHandler
|
||||
{
|
||||
public static string GetEmbeddedScript(string p_name)
|
||||
public static string GetEmbeddedResource(string p_name)
|
||||
{
|
||||
string l_result = "";
|
||||
Assembly l_assembly = Assembly.GetExecutingAssembly();
|
|
@ -11,19 +11,19 @@ namespace ml_dht
|
|||
Enabled = 0,
|
||||
HeadTracking,
|
||||
EyeTracking,
|
||||
FaceTracking,
|
||||
Blinking,
|
||||
Mirrored,
|
||||
Smoothing,
|
||||
FaceOverride
|
||||
}
|
||||
|
||||
public static bool Enabled { get; private set; } = false;
|
||||
public static bool HeadTracking { get; private set; } = true;
|
||||
public static bool EyeTracking { get; private set; } = true;
|
||||
public static bool FaceTracking { get; private set; } = true;
|
||||
public static bool Blinking { get; private set; } = true;
|
||||
public static bool Mirrored { get; private set; } = false;
|
||||
public static float Smoothing { get; private set; } = 0.5f;
|
||||
public static bool FaceOverride { get; private set; } = true;
|
||||
|
||||
static MelonLoader.MelonPreferences_Category ms_category = null;
|
||||
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
|
||||
|
@ -31,10 +31,11 @@ namespace ml_dht
|
|||
static public event Action<bool> EnabledChange;
|
||||
static public event Action<bool> HeadTrackingChange;
|
||||
static public event Action<bool> EyeTrackingChange;
|
||||
static public event Action<bool> FaceTrackingChange;
|
||||
static public event Action<bool> BlinkingChange;
|
||||
static public event Action<bool> MirroredChange;
|
||||
static public event Action<float> SmoothingChange;
|
||||
static public event Action<bool> FaceOverrideChange;
|
||||
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
|
@ -45,13 +46,19 @@ namespace ml_dht
|
|||
ms_category.CreateEntry(ModSetting.Enabled.ToString(), Enabled),
|
||||
ms_category.CreateEntry(ModSetting.HeadTracking.ToString(), HeadTracking),
|
||||
ms_category.CreateEntry(ModSetting.EyeTracking.ToString(), EyeTracking),
|
||||
ms_category.CreateEntry(ModSetting.FaceTracking.ToString(), FaceTracking),
|
||||
ms_category.CreateEntry(ModSetting.Blinking.ToString(), Blinking),
|
||||
ms_category.CreateEntry(ModSetting.Mirrored.ToString(), Mirrored),
|
||||
ms_category.CreateEntry(ModSetting.Smoothing.ToString(), (int)(Smoothing * 50f)),
|
||||
ms_category.CreateEntry(ModSetting.FaceOverride.ToString(), FaceOverride)
|
||||
};
|
||||
|
||||
Load();
|
||||
Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue;
|
||||
HeadTracking = (bool)ms_entries[(int)ModSetting.HeadTracking].BoxedValue;
|
||||
EyeTracking = (bool)ms_entries[(int)ModSetting.EyeTracking].BoxedValue;
|
||||
FaceTracking = (bool)ms_entries[(int)ModSetting.FaceTracking].BoxedValue;
|
||||
Blinking = (bool)ms_entries[(int)ModSetting.Blinking].BoxedValue;
|
||||
Mirrored = (bool)ms_entries[(int)ModSetting.Mirrored].BoxedValue;
|
||||
Smoothing = ((int)ms_entries[(int)ModSetting.Smoothing].BoxedValue) * 0.01f;
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(WaitMainMenuUi());
|
||||
}
|
||||
|
@ -67,28 +74,18 @@ namespace ml_dht
|
|||
|
||||
ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_DHT_Call_InpSlider", new Action<string, string>(OnSliderUpdate));
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_DHT_Call_InpToggle", new Action<string, string>(OnToggleUpdate));
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action<string, string>(OnToggleUpdate));
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("OnSliderUpdate_" + ms_category.Identifier, new Action<string, string>(OnSliderUpdate));
|
||||
};
|
||||
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
|
||||
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("updateModSettingDHT", l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
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;
|
||||
HeadTracking = (bool)ms_entries[(int)ModSetting.HeadTracking].BoxedValue;
|
||||
EyeTracking = (bool)ms_entries[(int)ModSetting.EyeTracking].BoxedValue;
|
||||
Blinking = (bool)ms_entries[(int)ModSetting.Blinking].BoxedValue;
|
||||
Mirrored = (bool)ms_entries[(int)ModSetting.Mirrored].BoxedValue;
|
||||
Smoothing = ((int)ms_entries[(int)ModSetting.Smoothing].BoxedValue) * 0.01f;
|
||||
FaceOverride = (bool)ms_entries[(int)ModSetting.FaceOverride].BoxedValue;
|
||||
}
|
||||
|
||||
static void OnSliderUpdate(string p_name, string p_value)
|
||||
{
|
||||
if(Enum.TryParse(p_name, out ModSetting l_setting))
|
||||
|
@ -134,6 +131,13 @@ namespace ml_dht
|
|||
}
|
||||
break;
|
||||
|
||||
case ModSetting.FaceTracking:
|
||||
{
|
||||
FaceTracking = bool.Parse(p_value);
|
||||
FaceTrackingChange?.Invoke(FaceTracking);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.Blinking:
|
||||
{
|
||||
Blinking = bool.Parse(p_value);
|
||||
|
@ -147,13 +151,6 @@ namespace ml_dht
|
|||
MirroredChange?.Invoke(Mirrored);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.FaceOverride:
|
||||
{
|
||||
FaceOverride = bool.Parse(p_value);
|
||||
FaceOverrideChange?.Invoke(FaceOverride);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
|
||||
|
|
70
ml_dht/TrackingModule.cs
Normal file
70
ml_dht/TrackingModule.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -6,13 +6,13 @@ namespace ml_dht
|
|||
{
|
||||
static class Utils
|
||||
{
|
||||
static FieldInfo ms_cohtmlView = typeof(CohtmlControlledViewDisposable).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static void ExecuteScript(this CohtmlControlledViewDisposable p_viewDisposable, string p_script) => ((cohtml.Net.View)ms_cohtmlView.GetValue(p_viewDisposable))?.ExecuteScript(p_script);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>DesktopHeadTracking</Product>
|
||||
<Version>1.1.2</Version>
|
||||
<Version>1.2.0</Version>
|
||||
<Platforms>x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -18,49 +18,62 @@
|
|||
|
||||
<ItemGroup>
|
||||
<None Remove="DesktopHeadTracking.json" />
|
||||
<None Remove="resources\menu.js" />
|
||||
<None Remove="resources\mod_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
<EmbeddedResource Include="resources\mod_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll</HintPath>
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-firstpass">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="cohtml.Net">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Cohtml.Runtime">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll</HintPath>
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AnimationModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
// Add settings
|
||||
var g_modSettingsDHT = [];
|
||||
|
||||
engine.on('updateModSettingDHT', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsDHT.length; i++) {
|
||||
if (g_modSettingsDHT[i].name == _name) {
|
||||
g_modSettingsDHT[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
function inp_slider_mod_dht(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.minValue = parseFloat(_obj.getAttribute('data-min'));
|
||||
this.maxValue = parseFloat(_obj.getAttribute('data-max'));
|
||||
this.percent = 0;
|
||||
this.value = parseFloat(_obj.getAttribute('data-current'));
|
||||
this.dragActive = false;
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
this.stepSize = _obj.getAttribute('data-stepSize') || 0;
|
||||
this.format = _obj.getAttribute('data-format') || '{value}';
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this.stepSize != 0)
|
||||
this.value = Math.round(this.value / this.stepSize) * this.stepSize;
|
||||
else
|
||||
this.value = Math.round(this.value);
|
||||
|
||||
this.valueLabelBackground = document.createElement('div');
|
||||
this.valueLabelBackground.className = 'valueLabel background';
|
||||
this.valueLabelBackground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.obj.appendChild(this.valueLabelBackground);
|
||||
|
||||
this.valueBar = document.createElement('div');
|
||||
this.valueBar.className = 'valueBar';
|
||||
this.valueBar.setAttribute('style', 'width: ' + (((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.obj.appendChild(this.valueBar);
|
||||
|
||||
this.valueLabelForeground = document.createElement('div');
|
||||
this.valueLabelForeground.className = 'valueLabel foreground';
|
||||
this.valueLabelForeground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.valueBar.appendChild(this.valueLabelForeground);
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.dragActive = true;
|
||||
self.mouseMove(_e, false);
|
||||
}
|
||||
|
||||
this.mouseMove = function (_e, _write) {
|
||||
if (self.dragActive) {
|
||||
var rect = _obj.getBoundingClientRect();
|
||||
var start = rect.left;
|
||||
var end = rect.right;
|
||||
self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1);
|
||||
var value = self.percent;
|
||||
value *= (self.maxValue - self.minValue);
|
||||
value += self.minValue;
|
||||
if (self.stepSize != 0) {
|
||||
value = Math.round(value / self.stepSize);
|
||||
self.value = value * self.stepSize;
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
}
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
|
||||
engine.call(self.callbackName, self.name, "" + self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
}
|
||||
|
||||
this.mouseUp = function (_e) {
|
||||
self.mouseMove(_e, true);
|
||||
self.dragActive = false;
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
document.addEventListener('mousemove', this.mouseMove);
|
||||
document.addEventListener('mouseup', this.mouseUp);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
if (self.stepSize != 0)
|
||||
self.value = Math.round(value * self.stepSize) / self.stepSize;
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
|
||||
this.displayImperial = function () {
|
||||
var displays = document.querySelectorAll('.imperialDisplay');
|
||||
for (var i = 0; i < displays.length; i++) {
|
||||
var binding = displays[i].getAttribute('data-binding');
|
||||
if (binding == self.name) {
|
||||
var realFeet = ((self.value * 0.393700) / 12);
|
||||
var feet = Math.floor(realFeet);
|
||||
var inches = Math.floor((realFeet - feet) * 12);
|
||||
displays[i].innerHTML = feet + "'" + inches + '''';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
function inp_toggle_mod_dht(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.value = self.value == "True" ? "False" : "True";
|
||||
self.updateState();
|
||||
}
|
||||
|
||||
this.updateState = function () {
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
}
|
||||
|
||||
this.updateValue(this.value);
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Desktop Head Tracking</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Enabled: </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">Use head tracking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="HeadTracking" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Use eyes tracking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="EyeTracking" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Use blinking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Blinking" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Mirrored movement: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Mirrored" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Movement smoothing: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Smoothing" class ="inp_slider no-scroll" data-min="0" data-max="99" data-current="50"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Override face tracking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="FaceOverride" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('settings-implementation').appendChild(l_block);
|
||||
|
||||
// Update sliders in new menu block
|
||||
let l_sliders = l_block.querySelectorAll('.inp_slider');
|
||||
for (var i = 0; i < l_sliders.length; i++) {
|
||||
g_modSettingsDHT[g_modSettingsDHT.length] = new inp_slider_mod_dht(l_sliders[i], 'MelonMod_DHT_Call_InpSlider');
|
||||
}
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsDHT[g_modSettingsDHT.length] = new inp_toggle_mod_dht(l_toggles[i], 'MelonMod_DHT_Call_InpToggle');
|
||||
}
|
||||
}
|
70
ml_dht/resources/mod_menu.js
Normal file
70
ml_dht/resources/mod_menu.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Add own menu
|
||||
{
|
||||
let l_block = document.createElement('div');
|
||||
l_block.innerHTML = `
|
||||
<div class ="settings-subcategory">
|
||||
<div class ="subcategory-name">Desktop Head Tracking</div>
|
||||
<div class ="subcategory-description"></div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Enabled: </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">Use head tracking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="HeadTracking" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Use eyes tracking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="EyeTracking" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Use face tracking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="FaceTracking" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Use blinking: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Blinking" class ="inp_toggle no-scroll" data-current="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Mirrored movement: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Mirrored" class ="inp_toggle no-scroll" data-current="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class ="row-wrapper">
|
||||
<div class ="option-caption">Movement smoothing: </div>
|
||||
<div class ="option-input">
|
||||
<div id="Smoothing" class ="inp_slider no-scroll" data-min="0" data-max="99" data-current="50"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
`;
|
||||
document.getElementById('settings-implementation').appendChild(l_block);
|
||||
|
||||
// Toggles
|
||||
for (let l_toggle of l_block.querySelectorAll('.inp_toggle'))
|
||||
modsExtension.addSetting('DHT', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_DHT'));
|
||||
|
||||
// Sliders
|
||||
for (let l_slider of l_block.querySelectorAll('.inp_slider'))
|
||||
modsExtension.addSetting('DHT', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_DHT'));
|
||||
}
|
|
@ -13,8 +13,8 @@ namespace ml_lme
|
|||
"leapmotion_hands.asset"
|
||||
};
|
||||
|
||||
static Dictionary<string, AssetBundle> ms_loadedAssets = new Dictionary<string, AssetBundle>();
|
||||
static Dictionary<string, GameObject> ms_loadedObjects = new Dictionary<string, GameObject>();
|
||||
static readonly Dictionary<string, AssetBundle> ms_loadedAssets = new Dictionary<string, AssetBundle>();
|
||||
static readonly Dictionary<string, GameObject> ms_loadedObjects = new Dictionary<string, GameObject>();
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace ml_lme
|
|||
|
||||
foreach(string l_library in ms_libraries)
|
||||
{
|
||||
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + "." + l_library);
|
||||
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + l_library);
|
||||
|
||||
if(!File.Exists(l_library))
|
||||
{
|
||||
|
|
|
@ -24,20 +24,11 @@ namespace ml_lme
|
|||
bool m_gripLeft = false;
|
||||
bool m_gripRight = false;
|
||||
|
||||
~LeapInput()
|
||||
{
|
||||
Settings.EnabledChange -= this.OnEnableChange;
|
||||
Settings.InputChange -= this.OnInputChange;
|
||||
|
||||
MetaPort.Instance.settings.settingBoolChanged.RemoveListener(this.OnGameSettingBoolChange);
|
||||
}
|
||||
|
||||
public override void ModuleAdded()
|
||||
{
|
||||
base.ModuleAdded();
|
||||
|
||||
InputEnabled = Settings.Enabled;
|
||||
HapticFeedback = false;
|
||||
base.InputEnabled = Settings.Enabled;
|
||||
base.HapticFeedback = false;
|
||||
|
||||
m_inVR = Utils.IsInVR();
|
||||
|
||||
|
@ -47,13 +38,6 @@ namespace ml_lme
|
|||
m_handRayLeft.isInteractionRay = true;
|
||||
m_handRayLeft.triggerGazeEvents = false;
|
||||
m_handRayLeft.holderRoot = m_handRayLeft.gameObject;
|
||||
|
||||
m_handRayRight = LeapTracking.Instance.GetRightHand().gameObject.AddComponent<ControllerRay>();
|
||||
m_handRayRight.hand = false;
|
||||
m_handRayRight.generalMask = -1485;
|
||||
m_handRayRight.isInteractionRay = true;
|
||||
m_handRayRight.triggerGazeEvents = false;
|
||||
m_handRayRight.holderRoot = m_handRayRight.gameObject;
|
||||
m_handRayLeft.attachmentDistance = 0f;
|
||||
|
||||
m_lineLeft = m_handRayLeft.gameObject.AddComponent<LineRenderer>();
|
||||
|
@ -67,6 +51,13 @@ namespace ml_lme
|
|||
m_lineLeft.enabled = false;
|
||||
m_lineLeft.receiveShadows = false;
|
||||
m_handRayLeft.lineRenderer = m_lineLeft;
|
||||
|
||||
m_handRayRight = LeapTracking.Instance.GetRightHand().gameObject.AddComponent<ControllerRay>();
|
||||
m_handRayRight.hand = false;
|
||||
m_handRayRight.generalMask = -1485;
|
||||
m_handRayRight.isInteractionRay = true;
|
||||
m_handRayRight.triggerGazeEvents = false;
|
||||
m_handRayRight.holderRoot = m_handRayRight.gameObject;
|
||||
m_handRayRight.attachmentDistance = 0f;
|
||||
|
||||
m_lineRight = m_handRayRight.gameObject.AddComponent<LineRenderer>();
|
||||
|
@ -82,12 +73,14 @@ namespace ml_lme
|
|||
m_handRayRight.lineRenderer = m_lineRight;
|
||||
|
||||
Settings.EnabledChange += this.OnEnableChange;
|
||||
Settings.InputChange += this.OnInputChange;
|
||||
Settings.InteractionChange += this.OnInteractionChange;
|
||||
Settings.GesturesChange += this.OnGesturesChange;
|
||||
Settings.FingersOnlyChange += this.OnFingersOnlyChange;
|
||||
|
||||
OnEnableChange(Settings.Enabled);
|
||||
OnInputChange(Settings.Input);
|
||||
OnInteractionChange(Settings.Interaction);
|
||||
OnGesturesChange(Settings.Gestures);
|
||||
OnFingersOnlyChange(Settings.FingersOnly);
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(WaitForSettings());
|
||||
MelonLoader.MelonCoroutines.Start(WaitForMaterial());
|
||||
|
@ -108,69 +101,173 @@ namespace ml_lme
|
|||
{
|
||||
while(PlayerSetup.Instance == null)
|
||||
yield return null;
|
||||
while(PlayerSetup.Instance.leftRay == null)
|
||||
while(PlayerSetup.Instance.vrRayLeft == null)
|
||||
yield return null;
|
||||
while(PlayerSetup.Instance.leftRay.lineRenderer == null)
|
||||
while(PlayerSetup.Instance.vrRayLeft.lineRenderer == null)
|
||||
yield return null;
|
||||
|
||||
m_lineLeft.material = PlayerSetup.Instance.leftRay.lineRenderer.material;
|
||||
m_lineLeft.gameObject.layer = PlayerSetup.Instance.leftRay.gameObject.layer;
|
||||
m_lineRight.material = PlayerSetup.Instance.leftRay.lineRenderer.material;
|
||||
m_lineRight.gameObject.layer = PlayerSetup.Instance.leftRay.gameObject.layer;
|
||||
m_lineLeft.material = PlayerSetup.Instance.vrRayLeft.lineRenderer.material;
|
||||
m_lineLeft.gameObject.layer = PlayerSetup.Instance.vrRayLeft.gameObject.layer;
|
||||
m_lineRight.material = PlayerSetup.Instance.vrRayLeft.lineRenderer.material;
|
||||
m_lineRight.gameObject.layer = PlayerSetup.Instance.vrRayLeft.gameObject.layer;
|
||||
}
|
||||
|
||||
public override void ModuleDestroyed()
|
||||
{
|
||||
base.ModuleDestroyed();
|
||||
|
||||
if(m_handRayLeft != null)
|
||||
Object.Destroy(m_handRayLeft);
|
||||
m_handRayLeft = null;
|
||||
|
||||
if(m_handRayRight != null)
|
||||
Object.Destroy(m_handRayRight);
|
||||
m_handRayRight = null;
|
||||
|
||||
if(m_lineLeft != null)
|
||||
Object.Destroy(m_lineLeft);
|
||||
m_lineLeft = null;
|
||||
|
||||
if(m_lineRight != null)
|
||||
Object.Destroy(m_lineRight);
|
||||
m_lineRight = null;
|
||||
|
||||
Settings.EnabledChange -= this.OnEnableChange;
|
||||
Settings.InteractionChange -= this.OnInteractionChange;
|
||||
Settings.GesturesChange -= this.OnGesturesChange;
|
||||
Settings.FingersOnlyChange -= this.OnFingersOnlyChange;
|
||||
|
||||
MetaPort.Instance.settings.settingBoolChanged.RemoveListener(this.OnGameSettingBoolChange);
|
||||
}
|
||||
|
||||
public override void UpdateInput()
|
||||
{
|
||||
if(InputEnabled)
|
||||
if(base.InputEnabled)
|
||||
{
|
||||
GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
if(l_data.m_leftHand.m_present)
|
||||
{
|
||||
SetFingersInput(l_data.m_leftHand, true);
|
||||
m_handVisibleLeft = true;
|
||||
|
||||
SetFingersInput(l_data.m_leftHand, true);
|
||||
|
||||
if(Settings.Gestures)
|
||||
{
|
||||
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))
|
||||
{
|
||||
base._inputManager.gestureLeftRaw = (base._inputManager.fingerCurlLeftThumb >= 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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
base._inputManager.gestureLeftRaw = (base._inputManager.fingerCurlLeftThumb >= 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))
|
||||
{
|
||||
base._inputManager.gestureLeftRaw = -1f;
|
||||
}
|
||||
|
||||
base._inputManager.gestureLeft = base._inputManager.gestureLeftRaw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_handVisibleLeft)
|
||||
{
|
||||
ResetFingers(true);
|
||||
m_handVisibleLeft = false;
|
||||
if(Settings.Gestures)
|
||||
ResetGestures(true);
|
||||
}
|
||||
|
||||
m_handVisibleLeft = false;
|
||||
}
|
||||
|
||||
if(l_data.m_rightHand.m_present)
|
||||
{
|
||||
SetFingersInput(l_data.m_rightHand, false);
|
||||
m_handVisibleRight = true;
|
||||
|
||||
SetFingersInput(l_data.m_rightHand, false);
|
||||
|
||||
if(Settings.Gestures)
|
||||
{
|
||||
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))
|
||||
{
|
||||
base._inputManager.gestureRightRaw = (base._inputManager.fingerCurlRightThumb >= 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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
base._inputManager.gestureRightRaw = (base._inputManager.fingerCurlRightThumb >= 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))
|
||||
{
|
||||
base._inputManager.gestureRightRaw = -1f;
|
||||
}
|
||||
|
||||
base._inputManager.gestureRight = base._inputManager.gestureRightRaw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_handVisibleRight)
|
||||
{
|
||||
ResetFingers(false);
|
||||
m_handVisibleRight = false;
|
||||
if(Settings.Gestures)
|
||||
ResetGestures(false);
|
||||
}
|
||||
|
||||
m_handVisibleRight = false;
|
||||
}
|
||||
|
||||
if(!ModSupporter.SkipFingersOverride())
|
||||
{
|
||||
if(m_inVR)
|
||||
{
|
||||
_inputManager.individualFingerTracking = !CVRInputManager._moduleXR.GestureToggleValue;
|
||||
_inputManager.individualFingerTracking |= (l_data.m_leftHand.m_present || l_data.m_rightHand.m_present);
|
||||
}
|
||||
else
|
||||
_inputManager.individualFingerTracking = (l_data.m_leftHand.m_present || l_data.m_rightHand.m_present);
|
||||
IKSystem.Instance.FingerSystem.controlActive = _inputManager.individualFingerTracking;
|
||||
}
|
||||
|
||||
m_handRayLeft.enabled = (l_data.m_leftHand.m_present && (!m_inVR || !Utils.IsLeftHandTracked() || !Settings.FingersOnly));
|
||||
m_handRayRight.enabled = (l_data.m_rightHand.m_present && (!m_inVR || !Utils.IsRightHandTracked() || !Settings.FingersOnly));
|
||||
if(!ModSupporter.SkipFingersOverride() && (!m_inVR || !Utils.AreKnucklesInUse()))
|
||||
SetGameFingersTracking(m_handVisibleRight || m_handVisibleLeft);
|
||||
|
||||
base.UpdateInput();
|
||||
}
|
||||
|
@ -178,180 +275,105 @@ namespace ml_lme
|
|||
|
||||
public override void Update_Interaction()
|
||||
{
|
||||
GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
if(Settings.Input)
|
||||
if(Settings.Interaction)
|
||||
{
|
||||
if(l_data.m_leftHand.m_present && (!m_inVR || !Utils.IsLeftHandTracked() || !Settings.FingersOnly))
|
||||
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
if(m_handVisibleLeft && (!m_inVR || !Utils.IsLeftHandTracked()) && !Settings.FingersOnly)
|
||||
{
|
||||
float l_strength = l_data.m_leftHand.m_grabStrength;
|
||||
|
||||
float l_interactValue = 0f;
|
||||
float l_interactValue;
|
||||
if(m_gripToGrab)
|
||||
l_interactValue = Mathf.Clamp01(Mathf.InverseLerp(Mathf.Min(Settings.GripThreadhold, Settings.InteractThreadhold), Mathf.Max(Settings.GripThreadhold, Settings.InteractThreadhold), l_strength));
|
||||
else
|
||||
l_interactValue = Mathf.Clamp01(Mathf.InverseLerp(0f, Settings.InteractThreadhold, l_strength));
|
||||
_inputManager.interactLeftValue = Mathf.Max(l_interactValue, _inputManager.interactLeftValue);
|
||||
base._inputManager.interactLeftValue = Mathf.Max(l_interactValue, base._inputManager.interactLeftValue);
|
||||
|
||||
if(m_interactLeft != (l_strength > Settings.InteractThreadhold))
|
||||
{
|
||||
m_interactLeft = (l_strength > Settings.InteractThreadhold);
|
||||
_inputManager.interactLeftDown |= m_interactLeft;
|
||||
_inputManager.interactLeftUp |= !m_interactLeft;
|
||||
base._inputManager.interactLeftDown |= m_interactLeft;
|
||||
base._inputManager.interactLeftUp |= !m_interactLeft;
|
||||
}
|
||||
|
||||
float l_gripValue = Mathf.Clamp01(Mathf.InverseLerp(0f, Settings.GripThreadhold, l_strength));
|
||||
_inputManager.gripLeftValue = Mathf.Max(l_gripValue, _inputManager.gripLeftValue);
|
||||
base._inputManager.gripLeftValue = Mathf.Max(l_gripValue, base._inputManager.gripLeftValue);
|
||||
if(m_gripLeft != (l_strength > Settings.GripThreadhold))
|
||||
{
|
||||
m_gripLeft = (l_strength > Settings.GripThreadhold);
|
||||
_inputManager.gripLeftDown |= m_gripLeft;
|
||||
_inputManager.gripLeftUp |= !m_gripLeft;
|
||||
base._inputManager.gripLeftDown |= m_gripLeft;
|
||||
base._inputManager.gripLeftUp |= !m_gripLeft;
|
||||
}
|
||||
}
|
||||
|
||||
if(l_data.m_rightHand.m_present && (!m_inVR || !Utils.IsRightHandTracked() || !Settings.FingersOnly))
|
||||
if(m_handVisibleRight && (!m_inVR || !Utils.IsRightHandTracked()) && !Settings.FingersOnly)
|
||||
{
|
||||
float l_strength = l_data.m_rightHand.m_grabStrength;
|
||||
|
||||
float l_interactValue = 0f;
|
||||
float l_interactValue;
|
||||
if(m_gripToGrab)
|
||||
l_interactValue = Mathf.Clamp01(Mathf.InverseLerp(Mathf.Min(Settings.GripThreadhold, Settings.InteractThreadhold), Mathf.Max(Settings.GripThreadhold, Settings.InteractThreadhold), l_strength));
|
||||
else
|
||||
l_interactValue = Mathf.Clamp01(Mathf.InverseLerp(0f, Settings.InteractThreadhold, l_strength));
|
||||
_inputManager.interactRightValue = Mathf.Max(l_interactValue, _inputManager.interactRightValue);
|
||||
base._inputManager.interactRightValue = Mathf.Max(l_interactValue, base._inputManager.interactRightValue);
|
||||
|
||||
if(m_interactRight != (l_strength > Settings.InteractThreadhold))
|
||||
{
|
||||
m_interactRight = (l_strength > Settings.InteractThreadhold);
|
||||
_inputManager.interactRightDown |= m_interactRight;
|
||||
_inputManager.interactRightUp |= !m_interactRight;
|
||||
base._inputManager.interactRightDown |= m_interactRight;
|
||||
base._inputManager.interactRightUp |= !m_interactRight;
|
||||
}
|
||||
|
||||
float l_gripValue = Mathf.Clamp01(Mathf.InverseLerp(0f, Settings.GripThreadhold, l_strength));
|
||||
_inputManager.gripRightValue = Mathf.Max(l_gripValue, _inputManager.gripRightValue);
|
||||
base._inputManager.gripRightValue = Mathf.Max(l_gripValue, base._inputManager.gripRightValue);
|
||||
if(m_gripRight != (l_strength > Settings.GripThreadhold))
|
||||
{
|
||||
m_gripRight = (l_strength > Settings.GripThreadhold);
|
||||
_inputManager.gripRightDown |= m_gripRight;
|
||||
_inputManager.gripRightUp |= !m_gripRight;
|
||||
base._inputManager.gripRightDown |= m_gripRight;
|
||||
base._inputManager.gripRightUp |= !m_gripRight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(Settings.Gestures)
|
||||
{
|
||||
// Left hand gestures
|
||||
if(l_data.m_leftHand.m_present)
|
||||
{
|
||||
_inputManager.gestureLeftRaw = 0f;
|
||||
|
||||
// Finger Point & Finger Gun
|
||||
if(_inputManager.fingerCurlLeftIndex < 0.2f && _inputManager.fingerCurlLeftMiddle > 0.75f &&
|
||||
_inputManager.fingerCurlLeftRing > 0.75f && _inputManager.fingerCurlLeftPinky > 0.75f)
|
||||
{
|
||||
_inputManager.gestureLeftRaw = _inputManager.fingerCurlLeftThumb >= 0.5f ? 4f : 3f;
|
||||
}
|
||||
|
||||
// Peace Sign
|
||||
if(_inputManager.fingerCurlLeftIndex < 0.2f && _inputManager.fingerCurlLeftMiddle < 0.2f &&
|
||||
_inputManager.fingerCurlLeftRing > 0.75f && _inputManager.fingerCurlLeftPinky > 0.75f)
|
||||
{
|
||||
_inputManager.gestureLeftRaw = 5f;
|
||||
}
|
||||
|
||||
// Rock and Roll
|
||||
if(_inputManager.fingerCurlLeftIndex < 0.2f && _inputManager.fingerCurlLeftMiddle > 0.75f &&
|
||||
_inputManager.fingerCurlLeftRing > 0.75f && _inputManager.fingerCurlLeftPinky < 0.5f)
|
||||
{
|
||||
_inputManager.gestureLeftRaw = 6f;
|
||||
}
|
||||
|
||||
// Fist & Thumbs Up
|
||||
if(_inputManager.fingerCurlLeftIndex > 0.5f && _inputManager.fingerCurlLeftMiddle > 0.5f &&
|
||||
_inputManager.fingerCurlLeftRing > 0.5f && _inputManager.fingerCurlLeftPinky > 0.5f)
|
||||
{
|
||||
_inputManager.gestureLeftRaw = _inputManager.fingerCurlLeftThumb >= 0.5f
|
||||
? (l_data.m_rightHand.m_grabStrength - 0.5f) * 2f
|
||||
: 2f;
|
||||
}
|
||||
|
||||
// Open Hand
|
||||
if(_inputManager.fingerCurlLeftIndex < 0.2f && _inputManager.fingerCurlLeftMiddle < 0.2f &&
|
||||
_inputManager.fingerCurlLeftRing < 0.2f && _inputManager.fingerCurlLeftPinky < 0.2f)
|
||||
{
|
||||
_inputManager.gestureLeftRaw = -1f;
|
||||
}
|
||||
|
||||
_inputManager.gestureLeft = _inputManager.gestureLeftRaw;
|
||||
}
|
||||
|
||||
// Right hand gestures
|
||||
if(l_data.m_rightHand.m_present)
|
||||
{
|
||||
_inputManager.gestureRightRaw = 0f;
|
||||
|
||||
// Finger Point & Finger Gun
|
||||
if(_inputManager.fingerCurlRightIndex < 0.2f && _inputManager.fingerCurlRightMiddle > 0.75f &&
|
||||
_inputManager.fingerCurlRightRing > 0.75f && _inputManager.fingerCurlRightPinky > 0.75f)
|
||||
{
|
||||
_inputManager.gestureRightRaw = _inputManager.fingerCurlRightThumb >= 0.5f ? 4f : 3f;
|
||||
}
|
||||
|
||||
// Peace Sign
|
||||
if(_inputManager.fingerCurlRightIndex < 0.2f && _inputManager.fingerCurlRightMiddle < 0.2f &&
|
||||
_inputManager.fingerCurlRightRing > 0.75f && _inputManager.fingerCurlRightPinky > 0.75f)
|
||||
{
|
||||
_inputManager.gestureRightRaw = 5f;
|
||||
}
|
||||
|
||||
// Rock and Roll
|
||||
if(_inputManager.fingerCurlRightIndex < 0.2f && _inputManager.fingerCurlRightMiddle > 0.75f &&
|
||||
_inputManager.fingerCurlRightRing > 0.75f && _inputManager.fingerCurlRightPinky < 0.5f)
|
||||
{
|
||||
_inputManager.gestureRightRaw = 6f;
|
||||
}
|
||||
|
||||
// Fist & Thumbs Up
|
||||
if(_inputManager.fingerCurlRightIndex > 0.5f && _inputManager.fingerCurlRightMiddle > 0.5f &&
|
||||
_inputManager.fingerCurlRightRing > 0.5f && _inputManager.fingerCurlRightPinky > 0.5f)
|
||||
{
|
||||
_inputManager.gestureRightRaw = _inputManager.fingerCurlRightThumb >= 0.5f
|
||||
? (l_data.m_rightHand.m_grabStrength - 0.5f) * 2f
|
||||
: 2f;
|
||||
}
|
||||
|
||||
// Open Hand
|
||||
if(_inputManager.fingerCurlRightIndex < 0.2f && _inputManager.fingerCurlRightMiddle < 0.2f &&
|
||||
_inputManager.fingerCurlRightRing < 0.2f && _inputManager.fingerCurlRightPinky < 0.2f)
|
||||
{
|
||||
_inputManager.gestureRightRaw = -1f;
|
||||
}
|
||||
|
||||
_inputManager.gestureRight = _inputManager.gestureRightRaw;
|
||||
}
|
||||
ToggleHandRay(m_handVisibleLeft && (!m_inVR || !Utils.IsLeftHandTracked()) && !Settings.FingersOnly, true);
|
||||
ToggleHandRay(m_handVisibleRight && (!m_inVR || !Utils.IsRightHandTracked()) && !Settings.FingersOnly, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Settings changes
|
||||
void OnEnableChange(bool p_state)
|
||||
{
|
||||
InputEnabled = p_state;
|
||||
base.InputEnabled = p_state;
|
||||
|
||||
OnInputChange(p_state && Settings.Input);
|
||||
UpdateFingerTracking();
|
||||
m_handVisibleLeft &= p_state;
|
||||
m_handVisibleRight &= p_state;
|
||||
}
|
||||
|
||||
void OnInputChange(bool p_state)
|
||||
{
|
||||
((MonoBehaviour)m_handRayLeft).enabled = (p_state && Settings.Enabled);
|
||||
((MonoBehaviour)m_handRayRight).enabled = (p_state && Settings.Enabled);
|
||||
m_lineLeft.enabled = (p_state && Settings.Enabled);
|
||||
m_lineRight.enabled = (p_state && Settings.Enabled);
|
||||
|
||||
if(!p_state)
|
||||
{
|
||||
ResetFingers(true);
|
||||
ResetFingers(false);
|
||||
|
||||
if(Settings.Gestures)
|
||||
{
|
||||
ResetGestures(true);
|
||||
ResetGestures(false);
|
||||
}
|
||||
|
||||
// Reset to default, FreedomFingers can go brrr, player should press funny controller button two times
|
||||
SetGameFingersTracking(m_inVR && Utils.AreKnucklesInUse() && !CVRInputManager._moduleXR.GestureToggleValue);
|
||||
}
|
||||
|
||||
OnInteractionChange(Settings.Interaction);
|
||||
}
|
||||
|
||||
void OnInteractionChange(bool p_state)
|
||||
{
|
||||
bool l_state = (p_state && Settings.Enabled && !Settings.FingersOnly);
|
||||
|
||||
ToggleHandRay(l_state, true);
|
||||
ToggleHandRay(l_state, false);
|
||||
|
||||
if(!l_state)
|
||||
{
|
||||
m_handRayLeft.DropObject(true);
|
||||
m_handRayLeft.ClearGrabbedObject();
|
||||
|
@ -368,17 +390,21 @@ namespace ml_lme
|
|||
|
||||
void OnGesturesChange(bool p_state)
|
||||
{
|
||||
_inputManager.gestureLeft = 0f;
|
||||
_inputManager.gestureLeftRaw = 0f;
|
||||
_inputManager.gestureRight = 0f;
|
||||
_inputManager.gestureRightRaw = 0f;
|
||||
base._inputManager.gestureLeft = 0f;
|
||||
base._inputManager.gestureLeftRaw = 0f;
|
||||
base._inputManager.gestureRight = 0f;
|
||||
base._inputManager.gestureRightRaw = 0f;
|
||||
}
|
||||
|
||||
void OnFingersOnlyChange(bool p_state)
|
||||
{
|
||||
OnInteractionChange(Settings.Interaction);
|
||||
}
|
||||
|
||||
// Game events
|
||||
internal void OnAvatarSetup()
|
||||
{
|
||||
m_inVR = Utils.IsInVR();
|
||||
UpdateFingerTracking();
|
||||
}
|
||||
|
||||
internal void OnRayScale(float p_scale)
|
||||
|
@ -388,45 +414,35 @@ namespace ml_lme
|
|||
}
|
||||
|
||||
// Arbitrary
|
||||
void UpdateFingerTracking()
|
||||
{
|
||||
_inputManager.individualFingerTracking = (Settings.Enabled || (m_inVR && Utils.AreKnucklesInUse() && !CVRInputManager._moduleXR.GestureToggleValue));
|
||||
IKSystem.Instance.FingerSystem.controlActive = _inputManager.individualFingerTracking;
|
||||
|
||||
if(!Settings.Enabled)
|
||||
{
|
||||
ResetFingers(true);
|
||||
ResetFingers(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SetFingersInput(GestureMatcher.HandData p_hand, bool p_left)
|
||||
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)
|
||||
{
|
||||
_inputManager.fingerCurlLeftThumb = p_hand.m_bends[0];
|
||||
_inputManager.fingerCurlLeftIndex = p_hand.m_bends[1];
|
||||
_inputManager.fingerCurlLeftMiddle = p_hand.m_bends[2];
|
||||
_inputManager.fingerCurlLeftRing = p_hand.m_bends[3];
|
||||
_inputManager.fingerCurlLeftPinky = p_hand.m_bends[4];
|
||||
_inputManager.fingerSpreadLeftThumb = p_hand.m_spreads[0];
|
||||
_inputManager.fingerSpreadLeftIndex = p_hand.m_spreads[1];
|
||||
_inputManager.fingerSpreadLeftMiddle = p_hand.m_spreads[2];
|
||||
_inputManager.fingerSpreadLeftRing = p_hand.m_spreads[3];
|
||||
_inputManager.fingerSpreadLeftPinky = p_hand.m_spreads[4];
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputManager.fingerCurlRightThumb = p_hand.m_bends[0];
|
||||
_inputManager.fingerCurlRightIndex = p_hand.m_bends[1];
|
||||
_inputManager.fingerCurlRightMiddle = p_hand.m_bends[2];
|
||||
_inputManager.fingerCurlRightRing = p_hand.m_bends[3];
|
||||
_inputManager.fingerCurlRightPinky = p_hand.m_bends[4];
|
||||
_inputManager.fingerSpreadRightThumb = p_hand.m_spreads[0];
|
||||
_inputManager.fingerSpreadRightIndex = p_hand.m_spreads[1];
|
||||
_inputManager.fingerSpreadRightMiddle = p_hand.m_spreads[2];
|
||||
_inputManager.fingerSpreadRightRing = p_hand.m_spreads[3];
|
||||
_inputManager.fingerSpreadRightPinky = p_hand.m_spreads[4];
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,29 +450,29 @@ namespace ml_lme
|
|||
{
|
||||
if(p_left)
|
||||
{
|
||||
_inputManager.fingerCurlLeftThumb = 0f;
|
||||
_inputManager.fingerCurlLeftIndex = 0f;
|
||||
_inputManager.fingerCurlLeftMiddle = 0f;
|
||||
_inputManager.fingerCurlLeftRing = 0f;
|
||||
_inputManager.fingerCurlLeftPinky = 0f;
|
||||
_inputManager.fingerSpreadLeftThumb = 0f;
|
||||
_inputManager.fingerSpreadLeftIndex = 0f;
|
||||
_inputManager.fingerSpreadLeftMiddle = 0f;
|
||||
_inputManager.fingerSpreadLeftRing = 0f;
|
||||
_inputManager.fingerSpreadLeftPinky = 0f;
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputManager.fingerCurlRightThumb = 0f;
|
||||
_inputManager.fingerCurlRightIndex = 0f;
|
||||
_inputManager.fingerCurlRightMiddle = 0f;
|
||||
_inputManager.fingerCurlRightRing = 0f;
|
||||
_inputManager.fingerCurlRightPinky = 0f;
|
||||
_inputManager.fingerSpreadRightThumb = 0f;
|
||||
_inputManager.fingerSpreadRightIndex = 0f;
|
||||
_inputManager.fingerSpreadRightMiddle = 0f;
|
||||
_inputManager.fingerSpreadRightRing = 0f;
|
||||
_inputManager.fingerSpreadRightPinky = 0f;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,13 +480,31 @@ namespace ml_lme
|
|||
{
|
||||
if(p_left)
|
||||
{
|
||||
_inputManager.gestureLeft = 0f;
|
||||
_inputManager.gestureLeftRaw = 0f;
|
||||
base._inputManager.gestureLeft = 0f;
|
||||
base._inputManager.gestureLeftRaw = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputManager.gestureRight = 0f;
|
||||
_inputManager.gestureRightRaw = 0f;
|
||||
base._inputManager.gestureRight = 0f;
|
||||
base._inputManager.gestureRightRaw = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
void ToggleHandRay(bool p_state, bool p_left)
|
||||
{
|
||||
if(p_left)
|
||||
{
|
||||
m_handRayLeft.enabled = p_state;
|
||||
((MonoBehaviour)m_handRayLeft).enabled = p_state;
|
||||
m_lineLeft.enabled = p_state;
|
||||
m_lineLeft.forceRenderingOff = !p_state;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_handRayRight.enabled = p_state;
|
||||
((MonoBehaviour)m_handRayRight).enabled = p_state;
|
||||
m_lineRight.enabled = p_state;
|
||||
m_lineRight.forceRenderingOff = !p_state;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,5 +514,11 @@ namespace ml_lme
|
|||
if(p_name == "ControlUseGripToGrab")
|
||||
m_gripToGrab = p_state;
|
||||
}
|
||||
|
||||
void SetGameFingersTracking(bool p_state)
|
||||
{
|
||||
base._inputManager.individualFingerTracking = p_state;
|
||||
IKSystem.Instance.FingerSystem.controlActive = base._inputManager.individualFingerTracking;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Systems.InputManagement;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
@ -12,7 +11,7 @@ namespace ml_lme
|
|||
public static LeapManager Instance { get; private set; } = null;
|
||||
|
||||
Leap.Controller m_leapController = null;
|
||||
GestureMatcher.LeapData m_leapData = null;
|
||||
LeapParser.LeapData m_leapData = null;
|
||||
|
||||
LeapTracking m_leapTracking = null;
|
||||
LeapTracked m_leapTracked = null;
|
||||
|
@ -24,7 +23,7 @@ namespace ml_lme
|
|||
Instance = this;
|
||||
|
||||
m_leapController = new Leap.Controller();
|
||||
m_leapData = new GestureMatcher.LeapData();
|
||||
m_leapData = new LeapParser.LeapData();
|
||||
|
||||
DontDestroyOnLoad(this);
|
||||
|
||||
|
@ -60,6 +59,23 @@ namespace ml_lme
|
|||
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;
|
||||
}
|
||||
|
@ -90,12 +106,12 @@ namespace ml_lme
|
|||
if(m_leapController.IsConnected)
|
||||
{
|
||||
Leap.Frame l_frame = m_leapController.Frame();
|
||||
GestureMatcher.GetFrameData(l_frame, m_leapData);
|
||||
LeapParser.ParseFrame(l_frame, m_leapData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GestureMatcher.LeapData GetLatestData() => m_leapData;
|
||||
public LeapParser.LeapData GetLatestData() => m_leapData;
|
||||
|
||||
// Device events
|
||||
void OnLeapDeviceInitialized(object p_sender, Leap.DeviceEventArgs p_args)
|
||||
|
@ -169,12 +185,6 @@ namespace ml_lme
|
|||
m_leapTracked.OnAvatarSetup();
|
||||
}
|
||||
|
||||
internal void OnCalibrate()
|
||||
{
|
||||
if(m_leapTracked != null)
|
||||
m_leapTracked.OnCalibrate();
|
||||
}
|
||||
|
||||
internal void OnRayScale(float p_scale)
|
||||
{
|
||||
m_leapInput?.OnRayScale(p_scale);
|
||||
|
|
|
@ -2,15 +2,24 @@
|
|||
|
||||
namespace ml_lme
|
||||
{
|
||||
static class GestureMatcher
|
||||
static class LeapParser
|
||||
{
|
||||
readonly static Vector2[] ms_fingerLimits =
|
||||
readonly static Vector2[] ms_bendLimits =
|
||||
{
|
||||
new Vector2(-50f, 0f),
|
||||
new Vector2(-20f, 30f),
|
||||
new Vector2(-15f, 15f),
|
||||
new Vector2(-10f, 20f),
|
||||
new Vector2(-10f, 25f)
|
||||
new Vector2(0f, 90f),
|
||||
new Vector2(0f, 180f),
|
||||
new Vector2(0f, 180f),
|
||||
new Vector2(0f, 180f),
|
||||
new Vector2(0f, 180f)
|
||||
};
|
||||
|
||||
readonly static Vector2[] ms_spreadLimits =
|
||||
{
|
||||
new Vector2(-25f, 25f), // Unity's default limits
|
||||
new Vector2(-20f, 20f),
|
||||
new Vector2(-7.5f, 7.5f),
|
||||
new Vector2(-7.5f, 7.5f),
|
||||
new Vector2(-20f, 20f)
|
||||
};
|
||||
|
||||
public class HandData
|
||||
|
@ -68,7 +77,7 @@ namespace ml_lme
|
|||
}
|
||||
}
|
||||
|
||||
public static void GetFrameData(Leap.Frame p_frame, LeapData p_data)
|
||||
public static void ParseFrame(Leap.Frame p_frame, LeapData p_data)
|
||||
{
|
||||
p_data.Reset();
|
||||
|
||||
|
@ -93,30 +102,31 @@ namespace ml_lme
|
|||
// Bends
|
||||
foreach(Leap.Finger l_finger in p_hand.Fingers)
|
||||
{
|
||||
Quaternion l_prevSegment = Quaternion.identity;
|
||||
Quaternion l_parentRot = Quaternion.identity;
|
||||
|
||||
float l_angle = 0f;
|
||||
foreach(Leap.Bone l_bone in l_finger.bones)
|
||||
{
|
||||
p_data.m_fingerPosition[(int)l_finger.Type * 4 + (int)l_bone.Type] = l_bone.PrevJoint;
|
||||
p_data.m_fingerRotation[(int)l_finger.Type * 4 + (int)l_bone.Type] = l_bone.Rotation;
|
||||
int l_index = (int)l_finger.Type * 4 + (int)l_bone.Type;
|
||||
p_data.m_fingerPosition[l_index] = l_bone.PrevJoint;
|
||||
p_data.m_fingerRotation[l_index] = l_bone.Rotation;
|
||||
|
||||
if(l_bone.Type == Leap.Bone.BoneType.TYPE_METACARPAL)
|
||||
{
|
||||
l_prevSegment = l_bone.Rotation;
|
||||
l_parentRot = l_bone.Rotation;
|
||||
continue;
|
||||
}
|
||||
|
||||
Quaternion l_diff = Quaternion.Inverse(l_prevSegment) * l_bone.Rotation;
|
||||
l_prevSegment = l_bone.Rotation;
|
||||
|
||||
float l_angleDiff = l_diff.eulerAngles.x;
|
||||
Quaternion l_localRot = Quaternion.Inverse(l_parentRot) * l_bone.Rotation;
|
||||
float l_angleDiff = l_localRot.eulerAngles.x;
|
||||
if(l_angleDiff > 180f)
|
||||
l_angleDiff -= 360f;
|
||||
l_angle += l_angleDiff;
|
||||
|
||||
l_parentRot = l_bone.Rotation;
|
||||
}
|
||||
|
||||
p_data.m_bends[(int)l_finger.Type] = Utils.InverseLerpUnclamped(0f, (l_finger.Type == Leap.Finger.FingerType.TYPE_THUMB) ? 90f : 180f, l_angle);
|
||||
p_data.m_bends[(int)l_finger.Type] = Utils.InverseLerpUnclamped(ms_bendLimits[(int)l_finger.Type].x, ms_bendLimits[(int)l_finger.Type].y, l_angle);
|
||||
}
|
||||
|
||||
// Spreads
|
||||
|
@ -126,24 +136,17 @@ namespace ml_lme
|
|||
Leap.Bone l_child = l_finger.Bone(Leap.Bone.BoneType.TYPE_PROXIMAL);
|
||||
Quaternion l_diff = Quaternion.Inverse(l_parent.Rotation) * l_child.Rotation;
|
||||
|
||||
// Spread - local Y rotation, but thumb is obnoxious
|
||||
float l_angle = 360f - l_diff.eulerAngles.y;
|
||||
// Spread - local Y rotation
|
||||
float l_angle = l_diff.eulerAngles.y;
|
||||
if(l_angle > 180f)
|
||||
l_angle -= 360f;
|
||||
|
||||
// Pain
|
||||
if(p_hand.IsRight)
|
||||
l_angle *= -1f;
|
||||
|
||||
if(l_finger.Type != Leap.Finger.FingerType.TYPE_THUMB)
|
||||
{
|
||||
if(l_angle < 0f)
|
||||
p_data.m_spreads[(int)l_finger.Type] = 0.5f * Utils.InverseLerpUnclamped(ms_fingerLimits[(int)l_finger.Type].x, 0f, l_angle);
|
||||
else
|
||||
p_data.m_spreads[(int)l_finger.Type] = 0.5f + 0.5f * Utils.InverseLerpUnclamped(0f, ms_fingerLimits[(int)l_finger.Type].y, l_angle);
|
||||
}
|
||||
else
|
||||
p_data.m_spreads[(int)l_finger.Type] = Utils.InverseLerpUnclamped(ms_fingerLimits[(int)l_finger.Type].x, ms_fingerLimits[(int)l_finger.Type].y, l_angle);
|
||||
p_data.m_spreads[(int)l_finger.Type] = Utils.InverseLerpUnclamped(ms_spreadLimits[(int)l_finger.Type].x, ms_spreadLimits[(int)l_finger.Type].y, l_angle) * 2f - 1f;
|
||||
if((l_finger.Type != Leap.Finger.FingerType.TYPE_THUMB) && (p_data.m_bends[(int)l_finger.Type] >= 0.8f))
|
||||
p_data.m_spreads[(int)l_finger.Type] = Mathf.Lerp(p_data.m_spreads[(int)l_finger.Type], 0f, (p_data.m_bends[(int)l_finger.Type] - 0.8f) * 5f);
|
||||
}
|
||||
|
||||
p_data.m_grabStrength = Mathf.Clamp01((p_data.m_bends[1] + p_data.m_bends[2] + p_data.m_bends[3] + p_data.m_bends[4]) * 0.25f);
|
|
@ -9,33 +9,37 @@ namespace ml_lme
|
|||
[DisallowMultipleComponent]
|
||||
class LeapTracked : MonoBehaviour
|
||||
{
|
||||
static readonly float[] ms_tposeMuscles = typeof(ABI_RC.Systems.IK.SubSystems.BodySystem).GetField("TPoseMuscles", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as float[];
|
||||
static readonly Quaternion ms_offsetLeft = Quaternion.Euler(0f, 0f, 270f);
|
||||
static readonly Quaternion ms_offsetRight = Quaternion.Euler(0f, 0f, 90f);
|
||||
static readonly Quaternion ms_offsetLeftDesktop = Quaternion.Euler(0f, 90f, 0f);
|
||||
static readonly Quaternion ms_offsetRightDesktop = Quaternion.Euler(0f, 270f, 0f);
|
||||
struct IKInfo
|
||||
{
|
||||
public Vector4 m_armsWeights;
|
||||
public Vector2 m_elbowsWeights;
|
||||
public Transform m_leftHandTarget;
|
||||
public Transform m_rightHandTarget;
|
||||
public Transform m_leftElbowTarget;
|
||||
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[];
|
||||
static readonly Quaternion ms_offsetLeft = Quaternion.Euler(0f, 90f, 0f);
|
||||
static readonly Quaternion ms_offsetRight = Quaternion.Euler(0f, 270f, 0f);
|
||||
|
||||
VRIK m_vrIK = null;
|
||||
Vector4 m_armsWeights = Vector2.zero;
|
||||
bool m_inVR = false;
|
||||
VRIK m_vrIK = null;
|
||||
Transform m_hips = null;
|
||||
Transform m_origLeftHand = null;
|
||||
Transform m_origRightHand = null;
|
||||
Transform m_origLeftElbow = null;
|
||||
Transform m_origRightElbow = null;
|
||||
|
||||
bool m_enabled = true;
|
||||
bool m_fingersOnly = false;
|
||||
bool m_trackElbows = true;
|
||||
|
||||
IKInfo m_vrIKInfo;
|
||||
ArmIK m_leftArmIK = null;
|
||||
ArmIK m_rightArmIK = null;
|
||||
HumanPoseHandler m_poseHandler = null;
|
||||
HumanPose m_pose;
|
||||
Transform m_leftHandTarget = null;
|
||||
Transform m_rightHandTarget = null;
|
||||
bool m_leftTargetActive = false;
|
||||
bool m_rightTargetActive = false;
|
||||
bool m_leftTargetActive = false; // VRIK only
|
||||
bool m_rightTargetActive = false; // VRIK only
|
||||
|
||||
// Unity events
|
||||
void Start()
|
||||
|
@ -63,6 +67,27 @@ 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;
|
||||
|
||||
if(m_leftHandTarget != null)
|
||||
Destroy(m_leftHandTarget);
|
||||
m_leftHandTarget = null;
|
||||
|
||||
if(m_rightHandTarget != null)
|
||||
Destroy(m_rightHandTarget);
|
||||
m_rightHandTarget = null;
|
||||
|
||||
m_poseHandler?.Dispose();
|
||||
m_poseHandler = null;
|
||||
|
||||
m_vrIK = null;
|
||||
|
||||
Settings.EnabledChange -= this.OnEnabledChange;
|
||||
Settings.FingersOnlyChange -= this.OnFingersOnlyChange;
|
||||
Settings.TrackElbowsChange -= this.OnTrackElbowsChange;
|
||||
|
@ -72,7 +97,7 @@ namespace ml_lme
|
|||
{
|
||||
if(m_enabled)
|
||||
{
|
||||
GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
if((m_leftArmIK != null) && (m_rightArmIK != null))
|
||||
{
|
||||
|
@ -89,35 +114,8 @@ namespace ml_lme
|
|||
|
||||
if((m_vrIK != null) && !m_fingersOnly)
|
||||
{
|
||||
if(l_data.m_leftHand.m_present && !m_leftTargetActive)
|
||||
{
|
||||
m_vrIK.solver.leftArm.target = m_leftHandTarget;
|
||||
m_vrIK.solver.leftArm.bendGoal = LeapTracking.Instance.GetLeftElbow();
|
||||
m_vrIK.solver.leftArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
m_leftTargetActive = true;
|
||||
}
|
||||
if(!l_data.m_leftHand.m_present && m_leftTargetActive)
|
||||
{
|
||||
m_vrIK.solver.leftArm.target = m_origLeftHand;
|
||||
m_vrIK.solver.leftArm.bendGoal = m_origLeftElbow;
|
||||
m_vrIK.solver.leftArm.bendGoalWeight = ((m_origLeftElbow != null) ? 1f : 0f);
|
||||
m_leftTargetActive = false;
|
||||
}
|
||||
|
||||
if(l_data.m_rightHand.m_present && !m_rightTargetActive)
|
||||
{
|
||||
m_vrIK.solver.rightArm.target = m_rightHandTarget;
|
||||
m_vrIK.solver.rightArm.bendGoal = LeapTracking.Instance.GetRightElbow();
|
||||
m_vrIK.solver.rightArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
m_rightTargetActive = true;
|
||||
}
|
||||
if(!l_data.m_rightHand.m_present && m_rightTargetActive)
|
||||
{
|
||||
m_vrIK.solver.rightArm.target = m_origRightHand;
|
||||
m_vrIK.solver.rightArm.bendGoal = m_origRightElbow;
|
||||
m_vrIK.solver.rightArm.bendGoalWeight = ((m_origRightElbow != null) ? 1f : 0f);
|
||||
m_rightTargetActive = false;
|
||||
}
|
||||
m_leftTargetActive = l_data.m_leftHand.m_present;
|
||||
m_rightTargetActive = l_data.m_rightHand.m_present;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +124,7 @@ namespace ml_lme
|
|||
{
|
||||
if(m_enabled && !m_inVR && (m_poseHandler != null))
|
||||
{
|
||||
GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
Vector3 l_hipsLocalPos = m_hips.localPosition;
|
||||
Quaternion l_hipsLocalRot = m_hips.localRotation;
|
||||
|
@ -140,76 +138,11 @@ namespace ml_lme
|
|||
}
|
||||
}
|
||||
|
||||
// Tracking update
|
||||
void UpdateFingers(GestureMatcher.LeapData p_data)
|
||||
{
|
||||
if(p_data.m_leftHand.m_present)
|
||||
{
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb1Stretched, Mathf.LerpUnclamped(0.85f, -0.85f, p_data.m_leftHand.m_bends[0]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb2Stretched, Mathf.LerpUnclamped(0.85f, -0.85f, p_data.m_leftHand.m_bends[0]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumb3Stretched, Mathf.LerpUnclamped(0.85f, -0.85f, p_data.m_leftHand.m_bends[0]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftThumbSpread, Mathf.LerpUnclamped(-1.5f, 1.0f, p_data.m_leftHand.m_spreads[0])); // Ok
|
||||
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex1Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[1]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex2Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[1]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndex3Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[1]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftIndexSpread, Mathf.LerpUnclamped(1f, -1f, p_data.m_leftHand.m_spreads[1])); // Ok
|
||||
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle1Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[2]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle2Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[2]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddle3Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[2]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftMiddleSpread, Mathf.LerpUnclamped(2f, -2f, p_data.m_leftHand.m_spreads[2]));
|
||||
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing1Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[3]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing2Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[3]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRing3Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[3]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftRingSpread, Mathf.LerpUnclamped(-2f, 2f, p_data.m_leftHand.m_spreads[3]));
|
||||
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle1Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[4]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle2Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[4]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittle3Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_leftHand.m_bends[4]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.LeftLittleSpread, Mathf.LerpUnclamped(-0.5f, 1f, p_data.m_leftHand.m_spreads[4]));
|
||||
}
|
||||
|
||||
if(p_data.m_rightHand.m_present)
|
||||
{
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb1Stretched, Mathf.LerpUnclamped(0.85f, -0.85f, p_data.m_rightHand.m_bends[0]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb2Stretched, Mathf.LerpUnclamped(0.85f, -0.85f, p_data.m_rightHand.m_bends[0]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumb3Stretched, Mathf.LerpUnclamped(0.85f, -0.85f, p_data.m_rightHand.m_bends[0]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightThumbSpread, Mathf.LerpUnclamped(-1.5f, 1.0f, p_data.m_rightHand.m_spreads[0])); // Ok
|
||||
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex1Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[1]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex2Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[1]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndex3Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[1]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightIndexSpread, Mathf.LerpUnclamped(1f, -1f, p_data.m_rightHand.m_spreads[1])); // Ok
|
||||
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle1Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[2]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle2Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[2]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddle3Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[2]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightMiddleSpread, Mathf.LerpUnclamped(2f, -2f, p_data.m_rightHand.m_spreads[2]));
|
||||
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing1Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[3]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing2Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[3]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRing3Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[3]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightRingSpread, Mathf.LerpUnclamped(-2f, 2f, p_data.m_rightHand.m_spreads[3]));
|
||||
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle1Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[4]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle2Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[4]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittle3Stretched, Mathf.LerpUnclamped(0.7f, -1f, p_data.m_rightHand.m_bends[4]));
|
||||
UpdatePoseMuscle(ref m_pose, (int)MuscleIndex.RightLittleSpread, Mathf.LerpUnclamped(-0.5f, 1f, p_data.m_rightHand.m_spreads[4]));
|
||||
}
|
||||
}
|
||||
|
||||
// Game events
|
||||
internal void OnAvatarClear()
|
||||
{
|
||||
m_vrIK = null;
|
||||
m_origLeftHand = null;
|
||||
m_origRightHand = null;
|
||||
m_origLeftElbow = null;
|
||||
m_origRightElbow = null;
|
||||
m_hips = null;
|
||||
m_armsWeights = Vector2.zero;
|
||||
m_leftArmIK = null;
|
||||
m_rightArmIK = null;
|
||||
m_leftTargetActive = false;
|
||||
|
@ -256,11 +189,11 @@ namespace ml_lme
|
|||
|
||||
Transform l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
|
||||
if(l_hand != null)
|
||||
m_leftHandTarget.localRotation = (m_inVR ? ms_offsetLeft : ms_offsetLeftDesktop) * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
|
||||
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 = (m_inVR ? ms_offsetRight : ms_offsetRightDesktop) * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
|
||||
m_rightHandTarget.localRotation = ms_offsetRight * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
|
||||
|
||||
if(m_vrIK == null)
|
||||
{
|
||||
|
@ -304,10 +237,6 @@ namespace ml_lme
|
|||
}
|
||||
else
|
||||
{
|
||||
m_origLeftHand = m_vrIK.solver.leftArm.target;
|
||||
m_origRightHand = m_vrIK.solver.rightArm.target;
|
||||
m_origLeftElbow = m_vrIK.solver.leftArm.bendGoal;
|
||||
m_origRightElbow = m_vrIK.solver.rightArm.bendGoal;
|
||||
m_vrIK.solver.OnPreUpdate += this.OnIKPreUpdate;
|
||||
m_vrIK.solver.OnPostUpdate += this.OnIKPostUpdate;
|
||||
}
|
||||
|
@ -317,44 +246,56 @@ namespace ml_lme
|
|||
}
|
||||
}
|
||||
|
||||
internal void OnCalibrate()
|
||||
{
|
||||
if(m_vrIK != null)
|
||||
{
|
||||
m_origLeftHand = m_vrIK.solver.leftArm.target;
|
||||
m_origRightHand = m_vrIK.solver.rightArm.target;
|
||||
m_origLeftElbow = m_vrIK.solver.leftArm.bendGoal;
|
||||
m_origRightElbow = m_vrIK.solver.rightArm.bendGoal;
|
||||
}
|
||||
}
|
||||
|
||||
// IK updates
|
||||
// VRIK updates
|
||||
void OnIKPreUpdate()
|
||||
{
|
||||
m_armsWeights.Set(
|
||||
m_vrIK.solver.leftArm.positionWeight,
|
||||
m_vrIK.solver.leftArm.rotationWeight,
|
||||
m_vrIK.solver.rightArm.positionWeight,
|
||||
m_vrIK.solver.rightArm.rotationWeight
|
||||
);
|
||||
|
||||
if(m_leftTargetActive && (Mathf.Approximately(m_armsWeights.x, 0f) || Mathf.Approximately(m_armsWeights.y, 0f)))
|
||||
if(m_leftTargetActive)
|
||||
{
|
||||
m_vrIKInfo.m_leftHandTarget = m_vrIK.solver.leftArm.target;
|
||||
m_vrIKInfo.m_armsWeights.x = m_vrIK.solver.leftArm.positionWeight;
|
||||
m_vrIKInfo.m_armsWeights.y = m_vrIK.solver.leftArm.rotationWeight;
|
||||
m_vrIKInfo.m_leftElbowTarget = m_vrIK.solver.leftArm.bendGoal;
|
||||
m_vrIKInfo.m_elbowsWeights.x = m_vrIK.solver.leftArm.bendGoalWeight;
|
||||
|
||||
m_vrIK.solver.leftArm.target = m_leftHandTarget;
|
||||
m_vrIK.solver.leftArm.positionWeight = 1f;
|
||||
m_vrIK.solver.leftArm.rotationWeight = 1f;
|
||||
m_vrIK.solver.leftArm.bendGoal = LeapTracking.Instance.GetLeftElbow();
|
||||
m_vrIK.solver.leftArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
}
|
||||
if(m_rightTargetActive && (Mathf.Approximately(m_armsWeights.z, 0f) || Mathf.Approximately(m_armsWeights.w, 0f)))
|
||||
if(m_rightTargetActive)
|
||||
{
|
||||
m_vrIKInfo.m_rightHandTarget = m_vrIK.solver.rightArm.target;
|
||||
m_vrIKInfo.m_armsWeights.z = m_vrIK.solver.rightArm.positionWeight;
|
||||
m_vrIKInfo.m_armsWeights.w = m_vrIK.solver.rightArm.rotationWeight;
|
||||
m_vrIKInfo.m_rightElbowTarget = m_vrIK.solver.rightArm.bendGoal;
|
||||
m_vrIKInfo.m_elbowsWeights.y = m_vrIK.solver.rightArm.bendGoalWeight;
|
||||
|
||||
m_vrIK.solver.rightArm.target = m_rightHandTarget;
|
||||
m_vrIK.solver.rightArm.positionWeight = 1f;
|
||||
m_vrIK.solver.rightArm.rotationWeight = 1f;
|
||||
m_vrIK.solver.rightArm.bendGoal = LeapTracking.Instance.GetRightElbow();
|
||||
m_vrIK.solver.rightArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
}
|
||||
}
|
||||
void OnIKPostUpdate()
|
||||
{
|
||||
m_vrIK.solver.leftArm.positionWeight = m_armsWeights.x;
|
||||
m_vrIK.solver.leftArm.rotationWeight = m_armsWeights.y;
|
||||
m_vrIK.solver.rightArm.positionWeight = m_armsWeights.z;
|
||||
m_vrIK.solver.rightArm.rotationWeight = m_armsWeights.w;
|
||||
if(m_leftTargetActive)
|
||||
{
|
||||
m_vrIK.solver.leftArm.target = m_vrIKInfo.m_leftHandTarget;
|
||||
m_vrIK.solver.leftArm.positionWeight = m_vrIKInfo.m_armsWeights.x;
|
||||
m_vrIK.solver.leftArm.rotationWeight = m_vrIKInfo.m_armsWeights.y;
|
||||
m_vrIK.solver.leftArm.bendGoal = m_vrIKInfo.m_leftElbowTarget;
|
||||
m_vrIK.solver.leftArm.bendGoalWeight = m_vrIKInfo.m_elbowsWeights.x;
|
||||
}
|
||||
if(m_rightTargetActive)
|
||||
{
|
||||
m_vrIK.solver.rightArm.target = m_vrIKInfo.m_rightHandTarget;
|
||||
m_vrIK.solver.rightArm.positionWeight = m_vrIKInfo.m_armsWeights.z;
|
||||
m_vrIK.solver.rightArm.rotationWeight = m_vrIKInfo.m_armsWeights.w;
|
||||
m_vrIK.solver.rightArm.bendGoal = m_vrIKInfo.m_rightElbowTarget;
|
||||
m_vrIK.solver.rightArm.bendGoalWeight = m_vrIKInfo.m_elbowsWeights.y;
|
||||
}
|
||||
}
|
||||
|
||||
// Settings
|
||||
|
@ -363,8 +304,7 @@ namespace ml_lme
|
|||
m_enabled = p_state;
|
||||
|
||||
RefreshArmIK();
|
||||
if(!m_enabled || m_fingersOnly)
|
||||
RestoreVRIK();
|
||||
ResetTargetsStates();
|
||||
}
|
||||
|
||||
void OnFingersOnlyChange(bool p_state)
|
||||
|
@ -372,8 +312,7 @@ namespace ml_lme
|
|||
m_fingersOnly = p_state;
|
||||
|
||||
RefreshArmIK();
|
||||
if(!m_enabled || m_fingersOnly)
|
||||
RestoreVRIK();
|
||||
ResetTargetsStates();
|
||||
}
|
||||
|
||||
void OnTrackElbowsChange(bool p_state)
|
||||
|
@ -386,29 +325,14 @@ namespace ml_lme
|
|||
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
}
|
||||
|
||||
RestoreVRIK();
|
||||
ResetTargetsStates();
|
||||
}
|
||||
|
||||
// Arbitrary
|
||||
void RestoreVRIK()
|
||||
void ResetTargetsStates()
|
||||
{
|
||||
if(m_vrIK != null)
|
||||
{
|
||||
if(m_leftTargetActive)
|
||||
{
|
||||
m_vrIK.solver.leftArm.target = m_origLeftHand;
|
||||
m_vrIK.solver.leftArm.bendGoal = m_origLeftElbow;
|
||||
m_vrIK.solver.leftArm.bendGoalWeight = ((m_origLeftElbow != null) ? 1f : 0f);
|
||||
m_leftTargetActive = false;
|
||||
}
|
||||
if(m_rightTargetActive)
|
||||
{
|
||||
m_vrIK.solver.rightArm.target = m_origRightHand;
|
||||
m_vrIK.solver.rightArm.bendGoal = m_origRightElbow;
|
||||
m_vrIK.solver.rightArm.bendGoalWeight = ((m_origRightElbow != null) ? 1f : 0f);
|
||||
m_rightTargetActive = false;
|
||||
}
|
||||
}
|
||||
m_leftTargetActive = false;
|
||||
m_rightTargetActive = false;
|
||||
}
|
||||
|
||||
void RefreshArmIK()
|
||||
|
@ -420,6 +344,65 @@ namespace ml_lme
|
|||
}
|
||||
}
|
||||
|
||||
void UpdateFingers(LeapParser.LeapData p_data)
|
||||
{
|
||||
if(p_data.m_leftHand.m_present)
|
||||
{
|
||||
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]);
|
||||
|
||||
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(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)
|
||||
|
|
|
@ -102,8 +102,36 @@ namespace ml_lme
|
|||
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;
|
||||
|
@ -114,7 +142,7 @@ namespace ml_lme
|
|||
{
|
||||
if(Settings.Enabled)
|
||||
{
|
||||
GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
if(l_data.m_leftHand.m_present)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.IK.SubSystems;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
@ -33,11 +32,6 @@ namespace ml_lme
|
|||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(BodySystem).GetMethod(nameof(BodySystem.Calibrate)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnCalibrate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetControllerRayScale)),
|
||||
null,
|
||||
|
@ -57,6 +51,10 @@ namespace ml_lme
|
|||
{
|
||||
if(ms_instance == this)
|
||||
ms_instance = null;
|
||||
|
||||
if(m_leapManager != null)
|
||||
Object.Destroy(m_leapManager);
|
||||
m_leapManager = null;
|
||||
}
|
||||
|
||||
IEnumerator WaitForRootLogic()
|
||||
|
@ -96,20 +94,6 @@ namespace ml_lme
|
|||
}
|
||||
}
|
||||
|
||||
static void OnCalibrate_Postfix() => ms_instance?.OnCalibrate();
|
||||
void OnCalibrate()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_leapManager != null)
|
||||
m_leapManager.OnCalibrate();
|
||||
}
|
||||
catch(System.Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnRayScale_Postfix(float __0) => ms_instance?.OnRayScale(__0);
|
||||
void OnRayScale(float p_scale)
|
||||
{
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ml_lme
|
||||
{
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System.Reflection;
|
||||
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.5", "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)]
|
||||
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||
[assembly: MelonLoader.MelonAdditionalCredits("NotAKidOnSteam")]
|
||||
|
|
|
@ -4,9 +4,9 @@ using System.Reflection;
|
|||
|
||||
namespace ml_lme
|
||||
{
|
||||
static class Scripts
|
||||
static class ResourcesHandler
|
||||
{
|
||||
public static string GetEmbeddedScript(string p_name)
|
||||
public static string GetEmbeddedResource(string p_name)
|
||||
{
|
||||
string l_result = "";
|
||||
Assembly l_assembly = Assembly.GetExecutingAssembly();
|
|
@ -1,5 +1,4 @@
|
|||
using ABI_RC.Core.InteractionSystem;
|
||||
using cohtml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
@ -32,7 +31,7 @@ namespace ml_lme
|
|||
HeadY,
|
||||
HeadZ,
|
||||
TrackElbows,
|
||||
Input,
|
||||
Interaction,
|
||||
Gestures,
|
||||
InteractThreadhold,
|
||||
GripThreadhold,
|
||||
|
@ -48,7 +47,7 @@ namespace ml_lme
|
|||
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 Input { 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;
|
||||
|
@ -66,7 +65,7 @@ namespace ml_lme
|
|||
static public event Action<bool> HeadAttachChange;
|
||||
static public event Action<Vector3> HeadOffsetChange;
|
||||
static public event Action<bool> TrackElbowsChange;
|
||||
static public event Action<bool> InputChange;
|
||||
static public event Action<bool> InteractionChange;
|
||||
static public event Action<bool> GesturesChange;
|
||||
static public event Action<float> InteractThreadholdChange;
|
||||
static public event Action<float> GripThreadholdChange;
|
||||
|
@ -93,7 +92,7 @@ namespace ml_lme
|
|||
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.Input.ToString(), Input),
|
||||
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)),
|
||||
|
@ -116,15 +115,16 @@ namespace ml_lme
|
|||
|
||||
ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_LME_Call_InpToggle", new Action<string, string>(OnToggleUpdate));
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_LME_Call_InpSlider", new Action<string, string>(OnSliderUpdate));
|
||||
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_LME_Call_InpDropdown", new Action<string, string>(OnDropdownUpdate));
|
||||
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(Scripts.GetEmbeddedScript("menu.js"));
|
||||
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("updateModSettingLME", l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ namespace ml_lme
|
|||
(int)ms_entries[(int)ModSetting.HeadZ].BoxedValue
|
||||
) * 0.01f;
|
||||
TrackElbows = (bool)ms_entries[(int)ModSetting.TrackElbows].BoxedValue;
|
||||
Input = (bool)ms_entries[(int)ModSetting.Input].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;
|
||||
|
@ -199,10 +199,10 @@ namespace ml_lme
|
|||
}
|
||||
break;
|
||||
|
||||
case ModSetting.Input:
|
||||
case ModSetting.Interaction:
|
||||
{
|
||||
Input = bool.Parse(p_value);
|
||||
InputChange?.Invoke(Input);
|
||||
Interaction = bool.Parse(p_value);
|
||||
InteractionChange?.Invoke(Interaction);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Core.UI;
|
||||
using ABI_RC.Systems.InputManagement;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
|
@ -10,10 +8,12 @@ namespace ml_lme
|
|||
{
|
||||
static class Utils
|
||||
{
|
||||
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded);
|
||||
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);
|
||||
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);
|
||||
|
||||
public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false)
|
||||
{
|
||||
|
@ -31,6 +31,8 @@ namespace ml_lme
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
T temp = lhs;
|
||||
|
|
|
@ -4,17 +4,14 @@ namespace ml_lme
|
|||
{
|
||||
class VisualHand
|
||||
{
|
||||
Transform m_root = null;
|
||||
Transform m_wrist = null;
|
||||
Transform[] m_fingers = null;
|
||||
readonly Transform m_wrist = null;
|
||||
readonly Transform[] m_fingers = null;
|
||||
|
||||
public VisualHand(Transform p_root, bool p_left)
|
||||
{
|
||||
m_root = p_root;
|
||||
|
||||
if(m_root != null)
|
||||
if(p_root != null)
|
||||
{
|
||||
m_wrist = m_root.Find(p_left ? "LeftHand/Wrist" : "RightHand/Wrist");
|
||||
m_wrist = p_root.Find(p_left ? "LeftHand/Wrist" : "RightHand/Wrist");
|
||||
if(m_wrist != null)
|
||||
{
|
||||
m_fingers = new Transform[20];
|
||||
|
@ -47,7 +44,7 @@ namespace ml_lme
|
|||
}
|
||||
}
|
||||
|
||||
public void Update(GestureMatcher.HandData p_data)
|
||||
public void Update(LeapParser.HandData p_data)
|
||||
{
|
||||
if(m_wrist != null)
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PackageId>LeapMotionExtension</PackageId>
|
||||
<Version>1.4.0</Version>
|
||||
<Version>1.4.5</Version>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>LeapMotionExtension</Product>
|
||||
|
@ -25,15 +25,15 @@
|
|||
<None Remove="LeapMotionExtension.json" />
|
||||
<None Remove="resources\leapmotion_controller.asset" />
|
||||
<None Remove="resources\leapmotion_hands.asset" />
|
||||
<None Remove="resources\menu.js" />
|
||||
<None Remove="resources\mod_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\leapmotion_controller.asset" />
|
||||
<EmbeddedResource Include="resources\leapmotion_hands.asset" />
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
<EmbeddedResource Include="resources\mod_menu.js" />
|
||||
<EmbeddedResource Include="vendor\LeapSDK\lib\x64\LeapC.dll">
|
||||
<Link>LeapC.dll</Link>
|
||||
<Link>resources/LeapC.dll</Link>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -41,50 +41,62 @@
|
|||
<Reference Include="0Harmony">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-firstpass">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="cohtml.Net">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Cohtml.Runtime">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader">
|
||||
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="ml_pmc">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_pmc.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AnimationModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AssetBundleModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AssetBundleModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.XRModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.XRModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -92,6 +104,10 @@
|
|||
<Folder Include="vendor\LeapCSharp\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"" />
|
||||
</Target>
|
||||
|
|
Binary file not shown.
|
@ -1,442 +0,0 @@
|
|||
// Add settings
|
||||
var g_modSettingsLME = [];
|
||||
|
||||
engine.on('updateModSettingLME', function (_name, _value) {
|
||||
for (var i = 0; i < g_modSettingsLME.length; i++) {
|
||||
if (g_modSettingsLME[i].name == _name) {
|
||||
g_modSettingsLME[i].updateValue(_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Modified from original `inp` types, because I have no js knowledge to hook stuff
|
||||
function inp_toggle_mod_lme(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.value = self.value == "True" ? "False" : "True";
|
||||
self.updateState();
|
||||
}
|
||||
|
||||
this.updateState = function () {
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
|
||||
self.obj.classList.remove("checked");
|
||||
if (self.value == "True") {
|
||||
self.obj.classList.add("checked");
|
||||
}
|
||||
}
|
||||
|
||||
this.updateValue(this.value);
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
function inp_slider_mod_lme(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.minValue = parseFloat(_obj.getAttribute('data-min'));
|
||||
this.maxValue = parseFloat(_obj.getAttribute('data-max'));
|
||||
this.percent = 0;
|
||||
this.value = parseFloat(_obj.getAttribute('data-current'));
|
||||
this.dragActive = false;
|
||||
this.name = _obj.id;
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
this.stepSize = _obj.getAttribute('data-stepSize') || 0;
|
||||
this.format = _obj.getAttribute('data-format') || '{value}';
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this.stepSize != 0)
|
||||
this.value = Math.round(this.value / this.stepSize) * this.stepSize;
|
||||
else
|
||||
this.value = Math.round(this.value);
|
||||
|
||||
this.valueLabelBackground = document.createElement('div');
|
||||
this.valueLabelBackground.className = 'valueLabel background';
|
||||
this.valueLabelBackground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.obj.appendChild(this.valueLabelBackground);
|
||||
|
||||
this.valueBar = document.createElement('div');
|
||||
this.valueBar.className = 'valueBar';
|
||||
this.valueBar.setAttribute('style', 'width: ' + (((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.obj.appendChild(this.valueBar);
|
||||
|
||||
this.valueLabelForeground = document.createElement('div');
|
||||
this.valueLabelForeground.className = 'valueLabel foreground';
|
||||
this.valueLabelForeground.innerHTML = this.format.replace('{value}', this.value);
|
||||
this.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
|
||||
this.valueBar.appendChild(this.valueLabelForeground);
|
||||
|
||||
this.mouseDown = function (_e) {
|
||||
self.dragActive = true;
|
||||
self.mouseMove(_e, false);
|
||||
}
|
||||
|
||||
this.mouseMove = function (_e, _write) {
|
||||
if (self.dragActive) {
|
||||
var rect = _obj.getBoundingClientRect();
|
||||
var start = rect.left;
|
||||
var end = rect.right;
|
||||
self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1);
|
||||
var value = self.percent;
|
||||
value *= (self.maxValue - self.minValue);
|
||||
value += self.minValue;
|
||||
if (self.stepSize != 0) {
|
||||
value = Math.round(value / self.stepSize);
|
||||
self.value = value * self.stepSize;
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
}
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
|
||||
engine.call(self.callbackName, self.name, "" + self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
}
|
||||
|
||||
this.mouseUp = function (_e) {
|
||||
self.mouseMove(_e, true);
|
||||
self.dragActive = false;
|
||||
}
|
||||
|
||||
_obj.addEventListener('mousedown', this.mouseDown);
|
||||
document.addEventListener('mousemove', this.mouseMove);
|
||||
document.addEventListener('mouseup', this.mouseUp);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
if (self.stepSize != 0)
|
||||
self.value = Math.round(value * self.stepSize) / self.stepSize;
|
||||
else
|
||||
self.value = Math.round(value);
|
||||
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
|
||||
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
|
||||
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
|
||||
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
|
||||
self.displayImperial();
|
||||
}
|
||||
|
||||
this.displayImperial = function () {
|
||||
var displays = document.querySelectorAll('.imperialDisplay');
|
||||
for (var i = 0; i < displays.length; i++) {
|
||||
var binding = displays[i].getAttribute('data-binding');
|
||||
if (binding == self.name) {
|
||||
var realFeet = ((self.value * 0.393700) / 12);
|
||||
var feet = Math.floor(realFeet);
|
||||
var inches = Math.floor((realFeet - feet) * 12);
|
||||
displays[i].innerHTML = feet + "'" + inches + '''';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue
|
||||
}
|
||||
}
|
||||
|
||||
function inp_dropdown_mod_lme(_obj, _callbackName) {
|
||||
this.obj = _obj;
|
||||
this.callbackName = _callbackName;
|
||||
this.value = _obj.getAttribute('data-current');
|
||||
this.options = _obj.getAttribute('data-options').split(',');
|
||||
this.name = _obj.id;
|
||||
this.opened = false;
|
||||
this.keyValue = [];
|
||||
this.type = _obj.getAttribute('data-type');
|
||||
|
||||
this.optionElements = [];
|
||||
|
||||
var self = this;
|
||||
|
||||
this.SelectValue = function (_e) {
|
||||
self.value = _e.target.getAttribute('data-key');
|
||||
self.valueElement.innerHTML = _e.target.getAttribute('data-value');
|
||||
self.globalClose();
|
||||
|
||||
engine.call(self.callbackName, self.name, self.value);
|
||||
}
|
||||
|
||||
this.openClick = function (_e) {
|
||||
if (self.obj.classList.contains('open')) {
|
||||
self.obj.classList.remove('open');
|
||||
self.list.setAttribute('style', 'display: none;');
|
||||
} else {
|
||||
self.obj.classList.add('open');
|
||||
self.list.setAttribute('style', 'display: block;');
|
||||
self.opened = true;
|
||||
window.setTimeout(function () { self.opened = false; }, 10);
|
||||
}
|
||||
}
|
||||
|
||||
this.globalClose = function (_e) {
|
||||
if (self.opened) return;
|
||||
self.obj.classList.remove('open');
|
||||
self.list.setAttribute('style', 'display: none;');
|
||||
}
|
||||
|
||||
this.list = document.createElement('div');
|
||||
this.list.className = 'valueList';
|
||||
|
||||
this.updateOptions = function () {
|
||||
self.list.innerHTML = "";
|
||||
for (var i = 0; i < self.options.length; i++) {
|
||||
self.optionElements[i] = document.createElement('div');
|
||||
self.optionElements[i].className = 'listValue';
|
||||
var valuePair = Array.isArray(self.options[i]) ? self.options[i] : self.options[i].split(':');
|
||||
var key = "";
|
||||
var value = "";
|
||||
if (valuePair.length == 1) {
|
||||
key = valuePair[0];
|
||||
value = valuePair[0];
|
||||
} else {
|
||||
key = valuePair[0];
|
||||
value = valuePair[1];
|
||||
}
|
||||
self.keyValue[key] = value;
|
||||
self.optionElements[i].innerHTML = value;
|
||||
self.optionElements[i].setAttribute('data-value', value);
|
||||
self.optionElements[i].setAttribute('data-key', key);
|
||||
self.list.appendChild(self.optionElements[i]);
|
||||
self.optionElements[i].addEventListener('mousedown', self.SelectValue);
|
||||
}
|
||||
|
||||
self.valueElement.innerHTML = self.keyValue[self.value];
|
||||
}
|
||||
|
||||
this.valueElement = document.createElement('div');
|
||||
this.valueElement.className = 'dropdown-value';
|
||||
|
||||
this.updateOptions();
|
||||
|
||||
this.obj.appendChild(this.valueElement);
|
||||
this.obj.appendChild(this.list);
|
||||
this.valueElement.addEventListener('mousedown', this.openClick);
|
||||
document.addEventListener('mousedown', this.globalClose);
|
||||
|
||||
this.getValue = function () {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
this.updateValue = function (value) {
|
||||
self.value = value;
|
||||
self.valueElement.innerHTML = self.keyValue[value];
|
||||
}
|
||||
|
||||
this.setOptions = function (options) {
|
||||
self.options = options;
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
value: this.getValue,
|
||||
updateValue: this.updateValue,
|
||||
updateOptions: this.updateOptions,
|
||||
setOptions: this.setOptions
|
||||
}
|
||||
}
|
||||
|
||||
// Add own menu
|
||||
{
|
||||
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="Input" 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);
|
||||
|
||||
// Update toggles in new menu block
|
||||
let l_toggles = l_block.querySelectorAll('.inp_toggle');
|
||||
for (var i = 0; i < l_toggles.length; i++) {
|
||||
g_modSettingsLME[g_modSettingsLME.length] = new inp_toggle_mod_lme(l_toggles[i], 'MelonMod_LME_Call_InpToggle');
|
||||
}
|
||||
|
||||
// Update sliders in new menu block
|
||||
let l_sliders = l_block.querySelectorAll('.inp_slider');
|
||||
for (var i = 0; i < l_sliders.length; i++) {
|
||||
g_modSettingsLME[g_modSettingsLME.length] = new inp_slider_mod_lme(l_sliders[i], 'MelonMod_LME_Call_InpSlider');
|
||||
}
|
||||
|
||||
//Update dropdowns in new menu block
|
||||
let l_dropdowns = l_block.querySelectorAll('.inp_dropdown');
|
||||
for (var i = 0; i < l_dropdowns.length; i++) {
|
||||
g_modSettingsLME[g_modSettingsLME.length] = new inp_dropdown_mod_lme(l_dropdowns[i], 'MelonMod_LME_Call_InpDropdown');
|
||||
}
|
||||
}
|
162
ml_lme/resources/mod_menu.js
Normal file
162
ml_lme/resources/mod_menu.js
Normal file
|
@ -0,0 +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'));
|
||||
}
|
7
ml_lme/vendor/LeapCSharp/Config.cs
vendored
7
ml_lme/vendor/LeapCSharp/Config.cs
vendored
|
@ -18,6 +18,7 @@ namespace Leap
|
|||
///
|
||||
/// @since 1.0
|
||||
/// </summary>
|
||||
[Obsolete("Config.cs is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public class Config
|
||||
{
|
||||
private Connection _connection;
|
||||
|
@ -29,12 +30,15 @@ namespace Leap
|
|||
/// Note that the Controller.Config provides a properly initialized Config object already.
|
||||
/// @since 3.0
|
||||
/// </summary>
|
||||
[Obsolete("Config.cs is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public Config(Connection.Key connectionKey)
|
||||
{
|
||||
_connection = Connection.GetConnection(connectionKey);
|
||||
_connection.LeapConfigChange += handleConfigChange;
|
||||
_connection.LeapConfigResponse += handleConfigResponse;
|
||||
}
|
||||
|
||||
[Obsolete("Config.cs is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public Config(int connectionId) : this(new Connection.Key(connectionId)) { }
|
||||
|
||||
private void handleConfigChange(object sender, ConfigChangeEventArgs eventArgs)
|
||||
|
@ -87,6 +91,7 @@ namespace Leap
|
|||
///
|
||||
/// @since 3.0
|
||||
/// </summary>
|
||||
[Obsolete("Config.cs is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public bool Get<T>(string key, Action<T> onResult)
|
||||
{
|
||||
uint requestId = _connection.GetConfigValue(key);
|
||||
|
@ -107,6 +112,7 @@ namespace Leap
|
|||
///
|
||||
/// @since 3.0
|
||||
/// </summary>
|
||||
[Obsolete("Config.cs is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public bool Set<T>(string key, T value, Action<bool> onResult) where T : IConvertible
|
||||
{
|
||||
uint requestId = _connection.SetConfigValue<T>(key, value);
|
||||
|
@ -123,6 +129,7 @@ namespace Leap
|
|||
/// Enumerates the possible data types for configuration values.
|
||||
/// @since 1.0
|
||||
/// </summary>
|
||||
[Obsolete("Config.cs is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public enum ValueType
|
||||
{
|
||||
TYPE_UNKNOWN = 0,
|
||||
|
|
52
ml_lme/vendor/LeapCSharp/Connection.cs
vendored
52
ml_lme/vendor/LeapCSharp/Connection.cs
vendored
|
@ -359,9 +359,6 @@ namespace LeapInternal
|
|||
StructMarshal<LEAP_CONFIG_CHANGE_EVENT>.PtrToStruct(_msg.eventStructPtr, out config_change_evt);
|
||||
handleConfigChange(ref config_change_evt);
|
||||
break;
|
||||
case eLeapEventType.eLeapEventType_ConfigResponse:
|
||||
handleConfigResponse(ref _msg);
|
||||
break;
|
||||
case eLeapEventType.eLeapEventType_DroppedFrame:
|
||||
LEAP_DROPPED_FRAME_EVENT dropped_frame_evt;
|
||||
StructMarshal<LEAP_DROPPED_FRAME_EVENT>.PtrToStruct(_msg.eventStructPtr, out dropped_frame_evt);
|
||||
|
@ -749,55 +746,6 @@ namespace LeapInternal
|
|||
}
|
||||
}
|
||||
|
||||
private void handleConfigResponse(ref LEAP_CONNECTION_MESSAGE configMsg)
|
||||
{
|
||||
LEAP_CONFIG_RESPONSE_EVENT config_response_evt;
|
||||
StructMarshal<LEAP_CONFIG_RESPONSE_EVENT>.PtrToStruct(configMsg.eventStructPtr, out config_response_evt);
|
||||
string config_key = "";
|
||||
_configRequests.TryGetValue(config_response_evt.requestId, out config_key);
|
||||
if (config_key != null)
|
||||
_configRequests.Remove(config_response_evt.requestId);
|
||||
|
||||
Config.ValueType dataType;
|
||||
object value;
|
||||
uint requestId = config_response_evt.requestId;
|
||||
if (config_response_evt.value.type != eLeapValueType.eLeapValueType_String)
|
||||
{
|
||||
switch (config_response_evt.value.type)
|
||||
{
|
||||
case eLeapValueType.eLeapValueType_Boolean:
|
||||
dataType = Config.ValueType.TYPE_BOOLEAN;
|
||||
value = config_response_evt.value.boolValue;
|
||||
break;
|
||||
case eLeapValueType.eLeapValueType_Int32:
|
||||
dataType = Config.ValueType.TYPE_INT32;
|
||||
value = config_response_evt.value.intValue;
|
||||
break;
|
||||
case eLeapValueType.eLeapValueType_Float:
|
||||
dataType = Config.ValueType.TYPE_FLOAT;
|
||||
value = config_response_evt.value.floatValue;
|
||||
break;
|
||||
default:
|
||||
dataType = Config.ValueType.TYPE_UNKNOWN;
|
||||
value = new object();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LEAP_CONFIG_RESPONSE_EVENT_WITH_REF_TYPE config_ref_value;
|
||||
StructMarshal<LEAP_CONFIG_RESPONSE_EVENT_WITH_REF_TYPE>.PtrToStruct(configMsg.eventStructPtr, out config_ref_value);
|
||||
dataType = Config.ValueType.TYPE_STRING;
|
||||
value = config_ref_value.value.stringValue;
|
||||
}
|
||||
SetConfigResponseEventArgs args = new SetConfigResponseEventArgs(config_key, dataType, value, requestId);
|
||||
|
||||
if (LeapConfigResponse != null)
|
||||
{
|
||||
LeapConfigResponse.DispatchOnContext(this, EventContext, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void reportLogMessage(ref LEAP_LOG_EVENT logMsg)
|
||||
{
|
||||
if (LeapLogEvent != null)
|
||||
|
|
6
ml_lme/vendor/LeapCSharp/Controller.cs
vendored
6
ml_lme/vendor/LeapCSharp/Controller.cs
vendored
|
@ -43,7 +43,6 @@ namespace Leap
|
|||
bool _disposed = false;
|
||||
bool _supportsMultipleDevices = true;
|
||||
string _serverNamespace = "Leap Service";
|
||||
Config _config;
|
||||
|
||||
/// <summary>
|
||||
/// The SynchronizationContext used for dispatching events.
|
||||
|
@ -871,13 +870,12 @@ namespace Leap
|
|||
///
|
||||
/// @since 1.0
|
||||
/// </summary>
|
||||
[Obsolete("Config.cs is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public Config Config
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_config == null)
|
||||
_config = new Config(this._connection.ConnectionKey);
|
||||
return _config;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
84
ml_lme/vendor/LeapCSharp/Device.cs
vendored
84
ml_lme/vendor/LeapCSharp/Device.cs
vendored
|
@ -121,14 +121,6 @@ namespace Leap
|
|||
IsStreaming = true;
|
||||
else
|
||||
IsStreaming = false;
|
||||
if ((status & eLeapDeviceStatus.eLeapDeviceStatus_Smudged) == eLeapDeviceStatus.eLeapDeviceStatus_Smudged)
|
||||
IsSmudged = true;
|
||||
else
|
||||
IsSmudged = false;
|
||||
if ((status & eLeapDeviceStatus.eLeapDeviceStatus_Robust) == eLeapDeviceStatus.eLeapDeviceStatus_Robust)
|
||||
IsLightingBad = true;
|
||||
else
|
||||
IsLightingBad = false;
|
||||
if ((status & eLeapDeviceStatus.eLeapDeviceStatus_LowResource) == eLeapDeviceStatus.eLeapDeviceStatus_LowResource)
|
||||
IsLowResource = true;
|
||||
else
|
||||
|
@ -263,33 +255,61 @@ namespace Leap
|
|||
return devicePose;
|
||||
}
|
||||
|
||||
//float[] data = new float[16];
|
||||
//eLeapRS result = LeapC.GetDeviceTransform(Handle, data);
|
||||
bool deviceTransformAvailable = LeapC.GetDeviceTransformAvailable(Handle);
|
||||
|
||||
//if (result != eLeapRS.eLeapRS_Success || data == null)
|
||||
//{
|
||||
// devicePose = Pose.identity;
|
||||
// return devicePose;
|
||||
//}
|
||||
if (!deviceTransformAvailable)
|
||||
{
|
||||
devicePose = Pose.identity;
|
||||
poseSet = true;
|
||||
return Pose.identity;
|
||||
}
|
||||
|
||||
//// Get transform matrix and convert to unity space by inverting Z.
|
||||
//Matrix4x4 transformMatrix = new Matrix4x4(
|
||||
// new Vector4(data[0], data[1], data[2], data[3]),
|
||||
// new Vector4(data[4], data[5], data[6], data[7]),
|
||||
// new Vector4(data[8], data[9], data[10], data[11]),
|
||||
// new Vector4(data[12], data[13], data[14], data[15]));
|
||||
//Matrix4x4 toUnity = Matrix4x4.Scale(new Vector3(1, 1, -1));
|
||||
//transformMatrix = toUnity * transformMatrix;
|
||||
float[] data = new float[16];
|
||||
eLeapRS result = LeapC.GetDeviceTransform(Handle, data);
|
||||
|
||||
//// Identity matrix here means we have no device transform, also check validity.
|
||||
//if (transformMatrix.isIdentity || !transformMatrix.ValidTRS())
|
||||
//{
|
||||
// devicePose = Pose.identity;
|
||||
// return devicePose;
|
||||
//}
|
||||
if (result != eLeapRS.eLeapRS_Success || data == null)
|
||||
{
|
||||
devicePose = Pose.identity;
|
||||
poseSet = true;
|
||||
return Pose.identity;
|
||||
}
|
||||
|
||||
//// Return the valid pose
|
||||
//devicePose = new Pose(transformMatrix.GetColumn(3), transformMatrix.rotation);
|
||||
// Using the LEAP->OPENXR device transform matrix
|
||||
// Unitys matrices are generated as 4 columns:
|
||||
Matrix4x4 deviceTransform = new Matrix4x4(
|
||||
new Vector4(data[0], data[1], data[2], data[3]),
|
||||
new Vector4(data[4], data[5], data[6], data[7]),
|
||||
new Vector4(data[8], data[9], data[10], data[11]),
|
||||
new Vector4(data[12], data[13], data[14], data[15]));
|
||||
|
||||
|
||||
// An example of the expected matrix if it were 8cm forward from the head origin
|
||||
// Unitys matrices are generated as 4 columns:
|
||||
//Matrix4x4 deviceTransform = new Matrix4x4(
|
||||
// new Vector4(-0.001f, 0, 0, 0),
|
||||
// new Vector4(0, 0, -0.001f, 0),
|
||||
// new Vector4(0, -0.001f, 0, 0),
|
||||
// new Vector4(0, 0, -0.08f, 1));
|
||||
|
||||
if (deviceTransform == Matrix4x4.identity)
|
||||
{
|
||||
devicePose = Pose.identity;
|
||||
poseSet = true;
|
||||
return Pose.identity;
|
||||
}
|
||||
|
||||
Matrix4x4 openXRToUnity = new Matrix4x4(
|
||||
new Vector4(1f, 0, 0, 0),
|
||||
new Vector4(0, 1f, 0, 0),
|
||||
new Vector4(0, 0, -1f, 0),
|
||||
new Vector4(0, 0, 0, 1));
|
||||
|
||||
deviceTransform = openXRToUnity * deviceTransform;
|
||||
|
||||
Vector3 outputPos = deviceTransform.GetPosition();
|
||||
//Quaternion outputRot = deviceTransform.rotation; // Note: the matrices we receive are not rotatrion matrices. This produces unexpected results
|
||||
|
||||
devicePose = new Pose(outputPos, Quaternion.identity);
|
||||
|
||||
poseSet = true;
|
||||
return devicePose;
|
||||
|
@ -321,6 +341,7 @@ namespace Leap
|
|||
/// over the Leap Motion cameras.
|
||||
/// @since 3.0
|
||||
/// </summary>
|
||||
[Obsolete("IsSmudged is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public bool IsSmudged { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -335,6 +356,7 @@ namespace Leap
|
|||
/// isLightingBad() is true.
|
||||
/// @since 3.0
|
||||
/// </summary>
|
||||
[Obsolete("IsLightingBad is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public bool IsLightingBad { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
1
ml_lme/vendor/LeapCSharp/Events.cs
vendored
1
ml_lme/vendor/LeapCSharp/Events.cs
vendored
|
@ -179,6 +179,7 @@ namespace Leap
|
|||
/// Provides the configuration key, whether the change was successful, and the id of the original change request.
|
||||
/// @since 3.0
|
||||
/// </summary>
|
||||
[Obsolete("Config.cs is not used in Ultraleap's Tracking Service 5.X+. This will be removed in the next Major release")]
|
||||
public class SetConfigResponseEventArgs : LeapEventArgs
|
||||
{
|
||||
public SetConfigResponseEventArgs(string config_key, Config.ValueType dataType, object value, uint requestId) : base(LeapEvent.EVENT_CONFIG_RESPONSE)
|
||||
|
|
2
ml_lme/vendor/LeapCSharp/IController.cs
vendored
2
ml_lme/vendor/LeapCSharp/IController.cs
vendored
|
@ -24,7 +24,7 @@ namespace Leap
|
|||
long Now();
|
||||
|
||||
bool IsConnected { get; }
|
||||
Config Config { get; }
|
||||
|
||||
DeviceList Devices { get; }
|
||||
|
||||
event EventHandler<ConnectionEventArgs> Connect;
|
||||
|
|
3
ml_lme/vendor/LeapCSharp/LeapC.cs
vendored
3
ml_lme/vendor/LeapCSharp/LeapC.cs
vendored
|
@ -1052,6 +1052,9 @@ namespace LeapInternal
|
|||
[DllImport("LeapC", EntryPoint = "LeapGetDeviceInfo", CharSet = CharSet.Ansi)]
|
||||
public static extern eLeapRS GetDeviceInfo(IntPtr hDevice, ref LEAP_DEVICE_INFO info);
|
||||
|
||||
[DllImport("LeapC", EntryPoint = "LeapDeviceTransformAvailable")]
|
||||
public static extern bool GetDeviceTransformAvailable(IntPtr hDevice);
|
||||
|
||||
[DllImport("LeapC", EntryPoint = "LeapGetDeviceTransform")]
|
||||
public static extern eLeapRS GetDeviceTransform(IntPtr hDevice, [MarshalAs(UnmanagedType.LPArray, SizeConst = 16)] float[] transform);
|
||||
|
||||
|
|
22
ml_lme/vendor/LeapCSharp/TransformExtensions.cs
vendored
22
ml_lme/vendor/LeapCSharp/TransformExtensions.cs
vendored
|
@ -41,6 +41,17 @@ namespace Leap
|
|||
return new Frame().CopyFrom(frame).Transform(transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new frame that is a copy of a frame, with an additional rigid
|
||||
* transformation applied to it.
|
||||
*
|
||||
* @param transform The transformation to be applied to the copied frame.
|
||||
*/
|
||||
public static Frame TransformedCopy(this Frame frame, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
return new Frame().CopyFrom(frame).Transform(new LeapTransform(position, rotation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Does an in-place rigid transformation of a Hand.
|
||||
*
|
||||
|
@ -79,6 +90,17 @@ namespace Leap
|
|||
return new Hand().CopyFrom(hand).Transform(transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new hand that is a copy of a hand, with an additional rigid
|
||||
* transformation applied to it.
|
||||
*
|
||||
* @param transform The transformation to be applied to the copied hand.
|
||||
*/
|
||||
public static Hand TransformedCopy(this Hand hand, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
return new Hand().CopyFrom(hand).Transform(new LeapTransform(position, rotation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Does an in-place rigid transformation of a Finger.
|
||||
*
|
||||
|
|
28
ml_lme/vendor/LeapSDK/README.md
vendored
28
ml_lme/vendor/LeapSDK/README.md
vendored
|
@ -88,7 +88,6 @@ SRC_DIR=/usr/share/doc/ultraleap-hand-tracking-service/samples
|
|||
BUILD_TYPE='Release'
|
||||
REPOS_BUILD_ROOT=~/ultraleap-tracking-samples/build
|
||||
REPOS_INSTALL_ROOT=/usr/bin/ultraleap-tracking-samples
|
||||
|
||||
cmake -S ${SRC_DIR} -B ${REPOS_BUILD_ROOT}/${BUILD_TYPE}/LeapSDK/leapc_example `
|
||||
-DCMAKE_INSTALL_PREFIX="${REPOS_INSTALL_ROOT}/leapc_example" `
|
||||
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}"
|
||||
|
@ -96,9 +95,32 @@ cmake -S ${SRC_DIR} -B ${REPOS_BUILD_ROOT}/${BUILD_TYPE}/LeapSDK/leapc_example `
|
|||
cmake --build ${REPOS_BUILD_ROOT}/${BUILD_TYPE}/LeapSDK/leapc_example -j --config ${BUILD_TYPE}
|
||||
```
|
||||
|
||||
### ARM64 Linux
|
||||
|
||||
1. Open the top level directory of the untarred release and select a build directory (eg. ~/ultraleap-tracking-samples/build) to use
|
||||
|
||||
2. Set the CMAKE_PREFIX_PATH directory to the absolute location of LeapSDK
|
||||
|
||||
3. Configure & Generate CMake with the generator of your choice
|
||||
|
||||
4. Open and build the CMake generated project files. For more help, see the CMake documentation.
|
||||
* An example script would be :
|
||||
```bash
|
||||
SRC_DIR='LeapSDK/samples'
|
||||
BUILD_TYPE='Release'
|
||||
REPOS_BUILD_ROOT=~/ultraleap-tracking-samples/build
|
||||
REPOS_INSTALL_ROOT=~/ultraleap-tracking-samples/bin
|
||||
PREFIX_PATH=$(pwd)/LeapSDK
|
||||
|
||||
cmake -S ${SRC_DIR} -B ${REPOS_BUILD_ROOT}/${BUILD_TYPE}/LeapSDK/leapc_example `
|
||||
-DCMAKE_INSTALL_PREFIX="${REPOS_INSTALL_ROOT}/leapc_example"`
|
||||
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" -DCMAKE_PREFIX_PATH="${PREFIX_PATH}"
|
||||
cmake --build ${REPOS_BUILD_ROOT}/${BUILD_TYPE}/LeapSDK/leapc_example -j --config ${BUILD_TYPE}
|
||||
```
|
||||
|
||||
### MacOS
|
||||
|
||||
1. Open CMake using /Library/Application Support/Ultraleap/LeapSDK/samples as the source directory
|
||||
1. Open CMake using /Applications/Ultraleap\ Hand\ Tracking.app/Contents/LeapSDK/samples as the source directory
|
||||
|
||||
2. Select a build directory (eg. ~/ultraleap-tracking-samples/build) to use
|
||||
|
||||
|
@ -107,7 +129,7 @@ cmake --build ${REPOS_BUILD_ROOT}/${BUILD_TYPE}/LeapSDK/leapc_example -j --confi
|
|||
4. Open and build the CMake generated project files. For more help, see the CMake documentation.
|
||||
* An example script would be :
|
||||
```bash
|
||||
SRC_DIR='/Library/Application Support/Ultraleap/LeapSDK/samples'
|
||||
SRC_DIR='/Applications/Ultraleap\ Hand\ Tracking.app/Contents/LeapSDK/samples'
|
||||
BUILD_TYPE='Release'
|
||||
REPOS_BUILD_ROOT=~/ultraleap-tracking-samples/build
|
||||
REPOS_INSTALL_ROOT=~/ultraleap-tracking-samples/bin
|
||||
|
|
2147
ml_lme/vendor/LeapSDK/ThirdPartyNotices.md
vendored
2147
ml_lme/vendor/LeapSDK/ThirdPartyNotices.md
vendored
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue