mirror of
https://github.com/hanetzer/sdraw_mods_cvr.git
synced 2025-09-04 02:49: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);
|
||||
}
|
|
@ -1,85 +1,80 @@
|
|||
using ABI_RC.Core;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
class AvatarParameter
|
||||
{
|
||||
public enum ParameterType
|
||||
{
|
||||
Upright,
|
||||
GroundedRaw,
|
||||
Moving
|
||||
}
|
||||
|
||||
readonly ParameterType m_type;
|
||||
readonly string m_name;
|
||||
readonly int m_hash = 0;
|
||||
readonly bool m_sync;
|
||||
readonly AnimatorControllerParameterType m_innerType;
|
||||
readonly CVRAnimatorManager m_manager = null;
|
||||
|
||||
public AvatarParameter(ParameterType p_type, CVRAnimatorManager p_manager)
|
||||
{
|
||||
m_type = p_type;
|
||||
m_name = p_type.ToString();
|
||||
m_manager = p_manager;
|
||||
|
||||
Regex l_regex = new Regex("^#?" + m_name + '$');
|
||||
foreach(var l_param in m_manager.animator.parameters)
|
||||
{
|
||||
if(l_regex.IsMatch(l_param.name))
|
||||
{
|
||||
m_hash = l_param.nameHash;
|
||||
m_sync = (l_param.name[0] != '#');
|
||||
m_innerType = l_param.type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(MotionTweaker p_tweaker)
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case ParameterType.Upright:
|
||||
SetFloat(p_tweaker.GetUpright());
|
||||
break;
|
||||
|
||||
case ParameterType.GroundedRaw:
|
||||
SetBoolean(p_tweaker.GetGroundedRaw());
|
||||
break;
|
||||
|
||||
case ParameterType.Moving:
|
||||
SetBoolean(p_tweaker.GetMoving());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValid() => (m_hash != 0);
|
||||
public ParameterType GetParameterType() => m_type;
|
||||
|
||||
void SetFloat(float p_value)
|
||||
{
|
||||
if(m_innerType == AnimatorControllerParameterType.Float)
|
||||
{
|
||||
if(m_sync)
|
||||
m_manager.SetAnimatorParameterFloat(m_name, p_value);
|
||||
else
|
||||
m_manager.animator.SetFloat(m_hash, p_value);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBoolean(bool p_value)
|
||||
{
|
||||
if(m_innerType == AnimatorControllerParameterType.Bool)
|
||||
{
|
||||
if(m_sync)
|
||||
m_manager.SetAnimatorParameterBool(m_name, p_value);
|
||||
else
|
||||
m_manager.animator.SetBool(m_hash, p_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using ABI_RC.Core;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
class AvatarParameter
|
||||
{
|
||||
public enum ParameterType
|
||||
{
|
||||
GroundedRaw,
|
||||
Moving
|
||||
}
|
||||
|
||||
readonly ParameterType m_type;
|
||||
readonly string m_name;
|
||||
readonly int m_hash = 0;
|
||||
readonly bool m_sync;
|
||||
readonly AnimatorControllerParameterType m_innerType;
|
||||
readonly CVRAnimatorManager m_manager = null;
|
||||
|
||||
public AvatarParameter(ParameterType p_type, CVRAnimatorManager p_manager)
|
||||
{
|
||||
m_type = p_type;
|
||||
m_name = p_type.ToString();
|
||||
m_manager = p_manager;
|
||||
|
||||
Regex l_regex = new Regex("^#?" + m_name + '$');
|
||||
foreach(var l_param in m_manager.animator.parameters)
|
||||
{
|
||||
if(l_regex.IsMatch(l_param.name))
|
||||
{
|
||||
m_hash = l_param.nameHash;
|
||||
m_sync = !l_param.name.StartsWith('#');
|
||||
m_innerType = l_param.type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(MotionTweaker p_tweaker)
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case ParameterType.GroundedRaw:
|
||||
SetBoolean(p_tweaker.GetGroundedRaw());
|
||||
break;
|
||||
|
||||
case ParameterType.Moving:
|
||||
SetBoolean(p_tweaker.GetMoving());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValid() => (m_hash != 0);
|
||||
public ParameterType GetParameterType() => m_type;
|
||||
|
||||
void SetFloat(float p_value)
|
||||
{
|
||||
if(m_innerType == AnimatorControllerParameterType.Float)
|
||||
{
|
||||
if(m_sync)
|
||||
m_manager.SetAnimatorParameterFloat(m_name, p_value);
|
||||
else
|
||||
m_manager.animator.SetFloat(m_hash, p_value);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBoolean(bool p_value)
|
||||
{
|
||||
if(m_innerType == AnimatorControllerParameterType.Bool)
|
||||
{
|
||||
if(m_sync)
|
||||
m_manager.SetAnimatorParameterBool(m_name, p_value);
|
||||
else
|
||||
m_manager.animator.SetBool(m_hash, p_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
231
ml_amt/Main.cs
231
ml_amt/Main.cs
|
@ -1,111 +1,120 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.IK.SubSystems;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
public class AvatarMotionTweaker : MelonLoader.MelonMod
|
||||
{
|
||||
static AvatarMotionTweaker ms_instance = null;
|
||||
|
||||
MotionTweaker m_localTweaker = null;
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
if(ms_instance == null)
|
||||
ms_instance = this;
|
||||
|
||||
Settings.Init();
|
||||
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(BodySystem).GetMethod(nameof(BodySystem.Calibrate)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnCalibrate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
|
||||
// Fixes
|
||||
Fixes.AnimatorOverrideControllerFix.Init(HarmonyInstance);
|
||||
Fixes.MovementJumpFix.Init(HarmonyInstance);
|
||||
|
||||
ModSupporter.Init();
|
||||
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
|
||||
}
|
||||
|
||||
IEnumerator WaitForLocalPlayer()
|
||||
{
|
||||
while(PlayerSetup.Instance == null)
|
||||
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()
|
||||
{
|
||||
if(ms_instance == this)
|
||||
ms_instance = null;
|
||||
|
||||
m_localTweaker = null;
|
||||
}
|
||||
|
||||
static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
|
||||
void OnAvatarClear()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_localTweaker != null)
|
||||
m_localTweaker.OnAvatarClear();
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
|
||||
void OnSetupAvatar()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_localTweaker != null)
|
||||
m_localTweaker.OnSetupAvatar();
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCalibrate_Postfix() => ms_instance?.OnCalibrate();
|
||||
void OnCalibrate()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_localTweaker != null)
|
||||
m_localTweaker.OnCalibrate();
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.IK.SubSystems;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
public class AvatarMotionTweaker : MelonLoader.MelonMod
|
||||
{
|
||||
static AvatarMotionTweaker ms_instance = null;
|
||||
|
||||
MotionTweaker m_localTweaker = null;
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
if(ms_instance == null)
|
||||
ms_instance = this;
|
||||
|
||||
Settings.Init();
|
||||
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
|
||||
null,
|
||||
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
|
||||
);
|
||||
HarmonyInstance.Patch(
|
||||
typeof(BodySystem).GetMethod(nameof(BodySystem.Calibrate)),
|
||||
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))
|
||||
);
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
|
||||
}
|
||||
|
||||
IEnumerator WaitForLocalPlayer()
|
||||
{
|
||||
while(PlayerSetup.Instance == null)
|
||||
yield return null;
|
||||
|
||||
m_localTweaker = PlayerSetup.Instance.gameObject.AddComponent<MotionTweaker>();
|
||||
}
|
||||
|
||||
public override void OnDeinitializeMelon()
|
||||
{
|
||||
if(ms_instance == this)
|
||||
ms_instance = null;
|
||||
|
||||
if(m_localTweaker != null)
|
||||
UnityEngine.Object.Destroy(m_localTweaker);
|
||||
m_localTweaker = null;
|
||||
}
|
||||
|
||||
static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
|
||||
void OnAvatarClear()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_localTweaker != null)
|
||||
m_localTweaker.OnAvatarClear();
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
|
||||
void OnSetupAvatar()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_localTweaker != null)
|
||||
m_localTweaker.OnSetupAvatar();
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(l_exception);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCalibrate_Postfix() => ms_instance?.OnCalibrate();
|
||||
void OnCalibrate()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_localTweaker != null)
|
||||
m_localTweaker.OnCalibrate();
|
||||
}
|
||||
catch(Exception l_exception)
|
||||
{
|
||||
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,305 +1,315 @@
|
|||
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;
|
||||
using RootMotion.FinalIK;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
class MotionTweaker : MonoBehaviour
|
||||
{
|
||||
struct IKState
|
||||
{
|
||||
public float m_weight;
|
||||
public float m_locomotionWeight;
|
||||
public bool m_plantFeet;
|
||||
public bool m_bendNormalLeft;
|
||||
public bool m_bendNormalRight;
|
||||
}
|
||||
|
||||
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
|
||||
static readonly int ms_emoteHash = Animator.StringToHash("Emote");
|
||||
|
||||
IKState m_ikState;
|
||||
VRIK m_vrIk = null;
|
||||
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;
|
||||
bool m_grounded = false;
|
||||
bool m_groundedRaw = false;
|
||||
bool m_moving = false;
|
||||
bool m_locomotionOverride = false;
|
||||
|
||||
bool m_ikOverrideFly = true;
|
||||
bool m_ikOverrideJump = true;
|
||||
|
||||
bool m_detectEmotes = true;
|
||||
bool m_emoteActive = false;
|
||||
|
||||
bool m_followHips = true;
|
||||
Vector3 m_hipsToPlayer = Vector3.zero;
|
||||
|
||||
Vector3 m_massCenter = Vector3.zero;
|
||||
|
||||
readonly List<AvatarParameter> m_parameters = null;
|
||||
|
||||
internal MotionTweaker()
|
||||
{
|
||||
m_parameters = new List<AvatarParameter>();
|
||||
}
|
||||
|
||||
// Unity events
|
||||
void Start()
|
||||
{
|
||||
m_inVR = Utils.IsInVR();
|
||||
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if(m_avatarReady)
|
||||
{
|
||||
m_grounded = MovementSystem.Instance.IsGrounded();
|
||||
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);
|
||||
}
|
||||
|
||||
m_emoteActive = false;
|
||||
if(m_detectEmotes && (m_locomotionLayer >= 0))
|
||||
{
|
||||
AnimatorStateInfo l_animState = PlayerSetup.Instance._animator.GetCurrentAnimatorStateInfo(m_locomotionLayer);
|
||||
m_emoteActive = (l_animState.tagHash == ms_emoteHash);
|
||||
}
|
||||
|
||||
if(m_parameters.Count > 0)
|
||||
{
|
||||
foreach(AvatarParameter l_param in m_parameters)
|
||||
l_param.Update(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Game events
|
||||
internal void OnAvatarClear()
|
||||
{
|
||||
m_vrIk = null;
|
||||
m_locomotionLayer = -1;
|
||||
m_grounded = false;
|
||||
m_groundedRaw = false;
|
||||
m_avatarReady = false;
|
||||
m_avatarScale = 1f;
|
||||
m_locomotionOffset = Vector3.zero;
|
||||
m_emoteActive = false;
|
||||
m_moving = false;
|
||||
m_locomotionOverride = false;
|
||||
m_hipsToPlayer = Vector3.zero;
|
||||
m_avatarHips = null;
|
||||
m_massCenter = Vector3.zero;
|
||||
m_parameters.Clear();
|
||||
}
|
||||
|
||||
internal void OnSetupAvatar()
|
||||
{
|
||||
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());
|
||||
|
||||
// Apply VRIK tweaks
|
||||
if(m_vrIk != null)
|
||||
{
|
||||
m_locomotionOffset = m_vrIk.solver.locomotion.offset;
|
||||
m_massCenter = m_locomotionOffset;
|
||||
|
||||
if(m_vrIk.solver.HasToes())
|
||||
{
|
||||
Transform l_foot = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftFoot);
|
||||
if(l_foot == null)
|
||||
l_foot = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightFoot);
|
||||
|
||||
Transform l_toe = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftToes);
|
||||
if(l_toe == null)
|
||||
l_toe = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightToes);
|
||||
|
||||
if((l_foot != null) && (l_toe != null))
|
||||
{
|
||||
Vector3 l_footPos = (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_foot.GetMatrix()) * ms_pointVector;
|
||||
Vector3 l_toePos = (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_toe.GetMatrix()) * ms_pointVector;
|
||||
m_massCenter = new Vector3(0f, 0f, l_toePos.z - l_footPos.z);
|
||||
}
|
||||
}
|
||||
|
||||
m_vrIk.solver.locomotion.offset = (Settings.MassCenter ? m_massCenter : m_locomotionOffset);
|
||||
|
||||
m_vrIk.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
|
||||
m_vrIk.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
|
||||
}
|
||||
|
||||
m_avatarReady = true;
|
||||
}
|
||||
|
||||
internal void OnCalibrate()
|
||||
{
|
||||
if(m_avatarReady && (m_vrIk != null) && (m_vrIk.solver.spine.pelvisTarget != null) && (m_vrIk.solver.leftLeg.target == null) && (m_vrIk.solver.rightLeg.target == null))
|
||||
{
|
||||
// Do not consider 4PT as FBT (!!!)
|
||||
m_vrIk.solver.spine.bodyPosStiffness = 0.55f;
|
||||
m_vrIk.solver.spine.bodyRotStiffness = 0.1f;
|
||||
m_vrIk.solver.spine.neckStiffness = 0.5f;
|
||||
m_vrIk.solver.spine.chestClampWeight = 0.55f;
|
||||
m_vrIk.solver.spine.moveBodyBackWhenCrouching = 0.5f;
|
||||
m_vrIk.solver.spine.maxRootAngle = 25f;
|
||||
m_vrIk.fixTransforms = false;
|
||||
|
||||
BodySystem.isCalibratedAsFullBody = false;
|
||||
BodySystem.TrackingLeftLegEnabled = false;
|
||||
BodySystem.TrackingRightLegEnabled = false;
|
||||
BodySystem.TrackingLocomotionEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// IK events
|
||||
void OnIKPreUpdate()
|
||||
{
|
||||
bool l_locomotionOverride = false;
|
||||
|
||||
m_ikState.m_weight = m_vrIk.solver.IKPositionWeight;
|
||||
m_ikState.m_locomotionWeight = m_vrIk.solver.locomotion.weight;
|
||||
m_ikState.m_plantFeet = m_vrIk.solver.plantFeet;
|
||||
m_ikState.m_bendNormalLeft = m_vrIk.solver.leftLeg.useAnimatedBendNormal;
|
||||
m_ikState.m_bendNormalRight = m_vrIk.solver.rightLeg.useAnimatedBendNormal;
|
||||
|
||||
if(m_detectEmotes && m_emoteActive)
|
||||
m_vrIk.solver.IKPositionWeight = 0f;
|
||||
|
||||
if(!BodySystem.isCalibratedAsFullBody)
|
||||
{
|
||||
if(PlayerSetup.Instance.avatarUpright <= PlayerSetup.Instance.avatarCrouchLimit)
|
||||
{
|
||||
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
|
||||
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
|
||||
l_locomotionOverride = true;
|
||||
}
|
||||
|
||||
if(m_ikOverrideFly && MovementSystem.Instance.flying)
|
||||
{
|
||||
m_vrIk.solver.locomotion.weight = 0f;
|
||||
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
|
||||
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
|
||||
l_locomotionOverride = true;
|
||||
}
|
||||
if(m_ikOverrideJump && !m_grounded && !MovementSystem.Instance.flying)
|
||||
{
|
||||
m_vrIk.solver.locomotion.weight = 0f;
|
||||
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
|
||||
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
|
||||
l_locomotionOverride = true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void OnIKPostUpdate()
|
||||
{
|
||||
m_vrIk.solver.IKPositionWeight = m_ikState.m_weight;
|
||||
m_vrIk.solver.locomotion.weight = m_ikState.m_locomotionWeight;
|
||||
m_vrIk.solver.plantFeet = m_ikState.m_plantFeet;
|
||||
m_vrIk.solver.leftLeg.useAnimatedBendNormal = m_ikState.m_bendNormalLeft;
|
||||
m_vrIk.solver.rightLeg.useAnimatedBendNormal = m_ikState.m_bendNormalRight;
|
||||
}
|
||||
|
||||
// Settings
|
||||
internal void SetCrouchLimit(float p_value)
|
||||
{
|
||||
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Max(Mathf.Clamp01(p_value), PlayerSetup.Instance.avatarProneLimit);
|
||||
}
|
||||
internal void SetProneLimit(float p_value)
|
||||
{
|
||||
PlayerSetup.Instance.avatarProneLimit = Mathf.Min(Mathf.Clamp01(p_value), PlayerSetup.Instance.avatarCrouchLimit);
|
||||
}
|
||||
internal void SetIKOverrideFly(bool p_state)
|
||||
{
|
||||
m_ikOverrideFly = p_state;
|
||||
}
|
||||
internal void SetIKOverrideJump(bool p_state)
|
||||
{
|
||||
m_ikOverrideJump = p_state;
|
||||
}
|
||||
internal void SetDetectEmotes(bool p_state)
|
||||
{
|
||||
m_detectEmotes = p_state;
|
||||
}
|
||||
internal void SetFollowHips(bool p_state)
|
||||
{
|
||||
m_followHips = p_state;
|
||||
}
|
||||
void OnMassCenterChange(bool p_state)
|
||||
{
|
||||
if(m_vrIk != null)
|
||||
m_vrIk.solver.locomotion.offset = (Settings.MassCenter ? (m_massCenter * GetRelativeScale()) : m_locomotionOffset);
|
||||
}
|
||||
|
||||
// Arbitrary
|
||||
float GetRelativeScale()
|
||||
{
|
||||
return ((m_avatarScale > 0f) ? (PlayerSetup.Instance._avatar.transform.localScale.y / m_avatarScale) : 0f);
|
||||
}
|
||||
|
||||
// Parameters access
|
||||
public float GetUpright() => PlayerSetup.Instance.avatarUpright;
|
||||
public bool GetGroundedRaw() => m_groundedRaw;
|
||||
public bool GetMoving() => m_moving;
|
||||
}
|
||||
}
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.IK;
|
||||
using ABI_RC.Systems.IK.SubSystems;
|
||||
using ABI_RC.Systems.MovementSystem;
|
||||
using RootMotion.FinalIK;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
class MotionTweaker : MonoBehaviour
|
||||
{
|
||||
struct IKState
|
||||
{
|
||||
public float m_weight;
|
||||
public float m_locomotionWeight;
|
||||
public bool m_plantFeet;
|
||||
public bool m_bendNormalLeft;
|
||||
public bool m_bendNormalRight;
|
||||
}
|
||||
|
||||
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
|
||||
static readonly int ms_emoteHash = Animator.StringToHash("Emote");
|
||||
|
||||
IKState m_ikState;
|
||||
VRIK m_vrIk = null;
|
||||
int m_locomotionLayer = 0;
|
||||
float m_avatarScale = 1f;
|
||||
Vector3 m_locomotionOffset = Vector3.zero; // Original locomotion offset
|
||||
bool m_inVR = false;
|
||||
|
||||
bool m_avatarReady = false;
|
||||
bool m_grounded = false;
|
||||
bool m_groundedRaw = false;
|
||||
bool m_moving = false;
|
||||
bool m_locomotionOverride = false;
|
||||
|
||||
bool m_ikOverrideFly = true;
|
||||
bool m_ikOverrideJump = true;
|
||||
|
||||
bool m_detectEmotes = true;
|
||||
bool m_emoteActive = false;
|
||||
|
||||
Vector3 m_massCenter = Vector3.zero;
|
||||
|
||||
Transform m_ikLimits = null;
|
||||
|
||||
readonly List<AvatarParameter> m_parameters = null;
|
||||
|
||||
internal MotionTweaker()
|
||||
{
|
||||
m_parameters = new List<AvatarParameter>();
|
||||
}
|
||||
|
||||
// Unity events
|
||||
void Start()
|
||||
{
|
||||
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.MassCenterChange += this.OnMassCenterChange;
|
||||
}
|
||||
|
||||
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.MassCenterChange -= this.OnMassCenterChange;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if(m_avatarReady)
|
||||
{
|
||||
m_grounded = MovementSystem.Instance.IsGrounded();
|
||||
m_groundedRaw = MovementSystem.Instance.IsGroundedRaw();
|
||||
m_moving = !Mathf.Approximately(MovementSystem.Instance.movementVector.magnitude, 0f);
|
||||
|
||||
UpdateIKLimits();
|
||||
|
||||
m_emoteActive = false;
|
||||
if(m_detectEmotes && (m_locomotionLayer >= 0))
|
||||
{
|
||||
AnimatorStateInfo l_animState = PlayerSetup.Instance._animator.GetCurrentAnimatorStateInfo(m_locomotionLayer);
|
||||
m_emoteActive = (l_animState.tagHash == ms_emoteHash);
|
||||
}
|
||||
|
||||
if(m_parameters.Count > 0)
|
||||
{
|
||||
foreach(AvatarParameter l_param in m_parameters)
|
||||
l_param.Update(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Game events
|
||||
internal void OnAvatarClear()
|
||||
{
|
||||
m_vrIk = null;
|
||||
m_locomotionLayer = -1;
|
||||
m_grounded = false;
|
||||
m_groundedRaw = false;
|
||||
m_avatarReady = false;
|
||||
m_avatarScale = 1f;
|
||||
m_locomotionOffset = Vector3.zero;
|
||||
m_emoteActive = false;
|
||||
m_moving = false;
|
||||
m_locomotionOverride = false;
|
||||
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()
|
||||
{
|
||||
m_inVR = Utils.IsInVR();
|
||||
m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
|
||||
m_locomotionLayer = PlayerSetup.Instance._animator.GetLayerIndex("Locomotion/Emotes");
|
||||
m_avatarScale = Mathf.Abs(PlayerSetup.Instance._avatar.transform.localScale.y);
|
||||
|
||||
// Parse animator parameters
|
||||
m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.GroundedRaw, PlayerSetup.Instance.animatorManager));
|
||||
m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.Moving, PlayerSetup.Instance.animatorManager));
|
||||
m_parameters.RemoveAll(p => !p.IsValid());
|
||||
|
||||
// Avatar custom IK limits
|
||||
m_ikLimits = PlayerSetup.Instance._avatar.transform.Find("[IKLimits]");
|
||||
UpdateIKLimits();
|
||||
|
||||
// Apply VRIK tweaks
|
||||
if(m_vrIk != null)
|
||||
{
|
||||
m_locomotionOffset = m_vrIk.solver.locomotion.offset;
|
||||
m_massCenter = m_locomotionOffset;
|
||||
|
||||
if(m_vrIk.solver.HasToes())
|
||||
{
|
||||
Transform l_foot = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftFoot);
|
||||
if(l_foot == null)
|
||||
l_foot = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightFoot);
|
||||
|
||||
Transform l_toe = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftToes);
|
||||
if(l_toe == null)
|
||||
l_toe = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightToes);
|
||||
|
||||
if((l_foot != null) && (l_toe != null))
|
||||
{
|
||||
Vector3 l_footPos = (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_foot.GetMatrix()) * ms_pointVector;
|
||||
Vector3 l_toePos = (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_toe.GetMatrix()) * ms_pointVector;
|
||||
m_massCenter = new Vector3(0f, 0f, l_toePos.z - l_footPos.z);
|
||||
}
|
||||
}
|
||||
|
||||
m_vrIk.solver.locomotion.offset = (Settings.MassCenter ? m_massCenter : m_locomotionOffset);
|
||||
|
||||
m_vrIk.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
|
||||
m_vrIk.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
|
||||
}
|
||||
|
||||
m_avatarReady = true;
|
||||
}
|
||||
|
||||
internal void OnCalibrate()
|
||||
{
|
||||
if(m_avatarReady && (m_vrIk != null) && (m_vrIk.solver.spine.pelvisTarget != null) && (m_vrIk.solver.leftLeg.target == null) && (m_vrIk.solver.rightLeg.target == null))
|
||||
{
|
||||
// Do not consider 4PT as FBT (!!!)
|
||||
m_vrIk.solver.spine.bodyPosStiffness = 0.55f;
|
||||
m_vrIk.solver.spine.bodyRotStiffness = 0.1f;
|
||||
m_vrIk.solver.spine.neckStiffness = 0.5f;
|
||||
m_vrIk.solver.spine.chestClampWeight = 0.55f;
|
||||
m_vrIk.solver.spine.moveBodyBackWhenCrouching = 0.5f;
|
||||
m_vrIk.solver.spine.maxRootAngle = 25f;
|
||||
m_vrIk.fixTransforms = false;
|
||||
|
||||
BodySystem.isCalibratedAsFullBody = false;
|
||||
BodySystem.TrackingLeftLegEnabled = false;
|
||||
BodySystem.TrackingRightLegEnabled = false;
|
||||
BodySystem.TrackingLocomotionEnabled = true;
|
||||
|
||||
IKSystem.Instance.applyOriginalHipRotation = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnPlayspaceScale()
|
||||
{
|
||||
if((m_vrIk != null) && Settings.MassCenter)
|
||||
m_vrIk.solver.locomotion.offset = m_massCenter * GetRelativeScale();
|
||||
}
|
||||
|
||||
// IK events
|
||||
void OnIKPreUpdate()
|
||||
{
|
||||
bool l_locomotionOverride = false;
|
||||
|
||||
m_ikState.m_weight = m_vrIk.solver.IKPositionWeight;
|
||||
m_ikState.m_locomotionWeight = m_vrIk.solver.locomotion.weight;
|
||||
m_ikState.m_plantFeet = m_vrIk.solver.plantFeet;
|
||||
m_ikState.m_bendNormalLeft = m_vrIk.solver.leftLeg.useAnimatedBendNormal;
|
||||
m_ikState.m_bendNormalRight = m_vrIk.solver.rightLeg.useAnimatedBendNormal;
|
||||
|
||||
if(m_detectEmotes && m_emoteActive)
|
||||
m_vrIk.solver.IKPositionWeight = 0f;
|
||||
|
||||
if(!BodySystem.isCalibratedAsFullBody)
|
||||
{
|
||||
if(PlayerSetup.Instance.avatarUpright <= PlayerSetup.Instance.avatarCrouchLimit)
|
||||
{
|
||||
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
|
||||
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
|
||||
l_locomotionOverride = true;
|
||||
}
|
||||
|
||||
if(m_ikOverrideFly && MovementSystem.Instance.flying)
|
||||
{
|
||||
m_vrIk.solver.locomotion.weight = 0f;
|
||||
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
|
||||
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
|
||||
l_locomotionOverride = true;
|
||||
}
|
||||
if(m_ikOverrideJump && !m_grounded && !MovementSystem.Instance.flying)
|
||||
{
|
||||
m_vrIk.solver.locomotion.weight = 0f;
|
||||
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
|
||||
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
|
||||
l_locomotionOverride = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_locomotionOverride && !l_locomotionOverride)
|
||||
m_vrIk.solver.Reset();
|
||||
m_locomotionOverride = l_locomotionOverride;
|
||||
}
|
||||
|
||||
void OnIKPostUpdate()
|
||||
{
|
||||
m_vrIk.solver.IKPositionWeight = m_ikState.m_weight;
|
||||
m_vrIk.solver.locomotion.weight = m_ikState.m_locomotionWeight;
|
||||
m_vrIk.solver.plantFeet = m_ikState.m_plantFeet;
|
||||
m_vrIk.solver.leftLeg.useAnimatedBendNormal = m_ikState.m_bendNormalLeft;
|
||||
m_vrIk.solver.rightLeg.useAnimatedBendNormal = m_ikState.m_bendNormalRight;
|
||||
}
|
||||
|
||||
// Settings
|
||||
internal void SetCrouchLimit(float p_value)
|
||||
{
|
||||
if(m_ikLimits == null)
|
||||
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(p_value);
|
||||
}
|
||||
internal void SetProneLimit(float p_value)
|
||||
{
|
||||
if(m_ikLimits == null)
|
||||
PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(p_value);
|
||||
}
|
||||
internal void SetIKOverrideFly(bool p_state)
|
||||
{
|
||||
m_ikOverrideFly = p_state;
|
||||
}
|
||||
internal void SetIKOverrideJump(bool p_state)
|
||||
{
|
||||
m_ikOverrideJump = p_state;
|
||||
}
|
||||
internal void SetDetectEmotes(bool p_state)
|
||||
{
|
||||
m_detectEmotes = p_state;
|
||||
}
|
||||
void OnMassCenterChange(bool p_state)
|
||||
{
|
||||
if(m_vrIk != null)
|
||||
m_vrIk.solver.locomotion.offset = (Settings.MassCenter ? (m_massCenter * GetRelativeScale()) : m_locomotionOffset);
|
||||
}
|
||||
|
||||
// Arbitrary
|
||||
float GetRelativeScale()
|
||||
{
|
||||
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;
|
||||
public bool GetMoving() => m_moving;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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)]
|
||||
[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.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||
|
|
|
@ -1,43 +1,33 @@
|
|||
# Avatar Motion Tweaker
|
||||
This mod adds features for AAS animator and avatar locomotion behaviour.
|
||||
|
||||

|
||||
|
||||
# Installation
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
* Get [latest release DLL](../../../releases/latest):
|
||||
* Put `ml_amt.dll` in `Mods` folder of game
|
||||
|
||||
# Usage
|
||||
Available mod's settings in `Settings - IK - Avatar Motion Tweaker`:
|
||||
* **Crouch limit:** defines crouch limit; default value - `75`.
|
||||
* **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].
|
||||
* Note: Can be set as local-only (not synced) if starts with `#` character.
|
||||
* Note: Shouldn't be used for transitions between poses in desktop mode. In desktop mode its value is driven by avatar animations. Use `CVR Parameter Stream` for detecting desktop/VR modes and change AAS animator transitions accordingly.
|
||||
* **`GroundedRaw`:** defines instant grounding state of player instead of delayed default parameter `Grounded`; boolean.
|
||||
* Note: Can be set as local-only (not synced) if starts with `#` character.
|
||||
* **`Moving`:** defines movement state of player; boolean.
|
||||
* Note: Can be set as local-only (not synced) if starts with `#` character.
|
||||
|
||||
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!
|
||||
# Avatar Motion Tweaker
|
||||
This mod adds features for AAS animator and avatar locomotion behaviour.
|
||||
|
||||

|
||||
|
||||
# Installation
|
||||
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
|
||||
* Get [latest release DLL](../../../releases/latest):
|
||||
* Put `ml_amt.dll` in `Mods` folder of game
|
||||
|
||||
# Usage
|
||||
Available mod's settings in `Settings - IK - Avatar Motion Tweaker`:
|
||||
* **Crouch limit:** defines crouch limit; default value - `75`.
|
||||
* **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`.
|
||||
* **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).
|
||||
|
||||
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].
|
||||
* Note: Can be set as local-only (not synced) if starts with `#` character.
|
||||
* Note: Shouldn't be used for transitions between poses in desktop mode. In desktop mode its value is driven by avatar animations. Use `CVR Parameter Stream` for detecting desktop/VR modes and change AAS animator transitions accordingly.
|
||||
* **`GroundedRaw`:** defines instant grounding state of player instead of delayed default parameter `Grounded`; boolean.
|
||||
* Note: Can be set as local-only (not synced) if starts with `#` character.
|
||||
* **`Moving`:** defines movement state of player; boolean.
|
||||
* Note: Can be set as local-only (not synced) if starts with `#` character.
|
||||
|
||||
Additional mod's behaviour:
|
||||
* Overrides and fixes IK behaviour in 4PT mode (head, hands and hips).
|
||||
* 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,183 +1,147 @@
|
|||
using ABI_RC.Core.InteractionSystem;
|
||||
using cohtml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
static class Settings
|
||||
{
|
||||
enum ModSetting
|
||||
{
|
||||
CrouchLimit,
|
||||
ProneLimit,
|
||||
IKOverrideFly,
|
||||
IKOverrideJump,
|
||||
DetectEmotes,
|
||||
FollowHips,
|
||||
ScaledJump,
|
||||
MassCenter,
|
||||
OverrideFix
|
||||
};
|
||||
|
||||
public static float CrouchLimit { get; private set; } = 0.75f;
|
||||
public static float ProneLimit { get; private set; } = 0.4f;
|
||||
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;
|
||||
|
||||
static public event Action<float> CrouchLimitChange;
|
||||
static public event Action<float> ProneLimitChange;
|
||||
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()
|
||||
{
|
||||
ms_category = MelonLoader.MelonPreferences.CreateCategory("AMT", null, true);
|
||||
|
||||
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
|
||||
{
|
||||
ms_category.CreateEntry(ModSetting.CrouchLimit.ToString(), (int)(CrouchLimit * 100f)),
|
||||
ms_category.CreateEntry(ModSetting.ProneLimit.ToString(), (int)(ProneLimit * 100f)),
|
||||
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)
|
||||
};
|
||||
|
||||
CrouchLimit = ((int)ms_entries[(int)ModSetting.CrouchLimit].BoxedValue) * 0.01f;
|
||||
ProneLimit = ((int)ms_entries[(int)ModSetting.ProneLimit].BoxedValue) * 0.01f;
|
||||
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());
|
||||
}
|
||||
|
||||
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("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.Listener.FinishLoad += (_) =>
|
||||
{
|
||||
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
|
||||
foreach(var l_entry in ms_entries)
|
||||
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSettingAMT", l_entry.DisplayName, l_entry.GetValueAsString());
|
||||
};
|
||||
}
|
||||
|
||||
static void OnSliderUpdate(string p_name, string p_value)
|
||||
{
|
||||
if(Enum.TryParse(p_name, out ModSetting l_setting))
|
||||
{
|
||||
switch(l_setting)
|
||||
{
|
||||
case ModSetting.CrouchLimit:
|
||||
{
|
||||
CrouchLimit = int.Parse(p_value) * 0.01f;
|
||||
CrouchLimitChange?.Invoke(CrouchLimit);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.ProneLimit:
|
||||
{
|
||||
ProneLimit = int.Parse(p_value) * 0.01f;
|
||||
ProneLimitChange?.Invoke(ProneLimit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnToggleUpdate(string p_name, string p_value)
|
||||
{
|
||||
if(Enum.TryParse(p_name, out ModSetting l_setting))
|
||||
{
|
||||
switch(l_setting)
|
||||
{
|
||||
case ModSetting.IKOverrideFly:
|
||||
{
|
||||
IKOverrideFly = bool.Parse(p_value);
|
||||
IKOverrideFlyChange?.Invoke(IKOverrideFly);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.IKOverrideJump:
|
||||
{
|
||||
IKOverrideJump = bool.Parse(p_value);
|
||||
IKOverrideJumpChange?.Invoke(IKOverrideJump);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.DetectEmotes:
|
||||
{
|
||||
DetectEmotes = bool.Parse(p_value);
|
||||
DetectEmotesChange?.Invoke(DetectEmotes);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using ABI_RC.Core.InteractionSystem;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
static class Settings
|
||||
{
|
||||
enum ModSetting
|
||||
{
|
||||
CrouchLimit,
|
||||
ProneLimit,
|
||||
IKOverrideFly,
|
||||
IKOverrideJump,
|
||||
DetectEmotes,
|
||||
MassCenter
|
||||
};
|
||||
|
||||
public static float CrouchLimit { get; private set; } = 0.75f;
|
||||
public static float ProneLimit { get; private set; } = 0.4f;
|
||||
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 MassCenter { get; private set; } = true;
|
||||
|
||||
static MelonLoader.MelonPreferences_Category ms_category = null;
|
||||
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
|
||||
|
||||
static public event Action<float> CrouchLimitChange;
|
||||
static public event Action<float> ProneLimitChange;
|
||||
static public event Action<bool> IKOverrideFlyChange;
|
||||
static public event Action<bool> IKOverrideJumpChange;
|
||||
static public event Action<bool> DetectEmotesChange;
|
||||
static public event Action<bool> MassCenterChange;
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
ms_category = MelonLoader.MelonPreferences.CreateCategory("AMT", null, true);
|
||||
|
||||
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
|
||||
{
|
||||
ms_category.CreateEntry(ModSetting.CrouchLimit.ToString(), (int)(CrouchLimit * 100f)),
|
||||
ms_category.CreateEntry(ModSetting.ProneLimit.ToString(), (int)(ProneLimit * 100f)),
|
||||
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.MassCenter.ToString(), MassCenter)
|
||||
};
|
||||
|
||||
CrouchLimit = ((int)ms_entries[(int)ModSetting.CrouchLimit].BoxedValue) * 0.01f;
|
||||
ProneLimit = ((int)ms_entries[(int)ModSetting.ProneLimit].BoxedValue) * 0.01f;
|
||||
IKOverrideFly = (bool)ms_entries[(int)ModSetting.IKOverrideFly].BoxedValue;
|
||||
IKOverrideJump = (bool)ms_entries[(int)ModSetting.IKOverrideJump].BoxedValue;
|
||||
DetectEmotes = (bool)ms_entries[(int)ModSetting.DetectEmotes].BoxedValue;
|
||||
MassCenter = (bool)ms_entries[(int)ModSetting.MassCenter].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("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(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 OnSliderUpdate(string p_name, string p_value)
|
||||
{
|
||||
if(Enum.TryParse(p_name, out ModSetting l_setting))
|
||||
{
|
||||
switch(l_setting)
|
||||
{
|
||||
case ModSetting.CrouchLimit:
|
||||
{
|
||||
CrouchLimit = int.Parse(p_value) * 0.01f;
|
||||
CrouchLimitChange?.Invoke(CrouchLimit);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.ProneLimit:
|
||||
{
|
||||
ProneLimit = int.Parse(p_value) * 0.01f;
|
||||
ProneLimitChange?.Invoke(ProneLimit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnToggleUpdate(string p_name, string p_value)
|
||||
{
|
||||
if(Enum.TryParse(p_name, out ModSetting l_setting))
|
||||
{
|
||||
switch(l_setting)
|
||||
{
|
||||
case ModSetting.IKOverrideFly:
|
||||
{
|
||||
IKOverrideFly = bool.Parse(p_value);
|
||||
IKOverrideFlyChange?.Invoke(IKOverrideFly);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.IKOverrideJump:
|
||||
{
|
||||
IKOverrideJump = bool.Parse(p_value);
|
||||
IKOverrideJumpChange?.Invoke(IKOverrideJump);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.DetectEmotes:
|
||||
{
|
||||
DetectEmotes = bool.Parse(p_value);
|
||||
DetectEmotesChange?.Invoke(DetectEmotes);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.MassCenter:
|
||||
{
|
||||
MassCenter = bool.Parse(p_value);
|
||||
MassCenterChange?.Invoke(MassCenter);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
103
ml_amt/Utils.cs
103
ml_amt/Utils.cs
|
@ -1,58 +1,45 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.UI;
|
||||
using ABI_RC.Systems.MovementSystem;
|
||||
using RootMotion.FinalIK;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
static readonly FieldInfo ms_grounded = typeof(MovementSystem).GetField("_isGrounded", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly FieldInfo ms_groundedRaw = typeof(MovementSystem).GetField("_isGroundedRaw", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly FieldInfo ms_hasToes = typeof(IKSolverVR).GetField("hasToes", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static MethodInfo ms_getSineKeyframes = typeof(IKSolverVR).GetMethod("GetSineKeyframes", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
static FieldInfo ms_cohtmlView = typeof(CohtmlControlledViewDisposable).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;
|
||||
if(CVRWorld.Instance != null)
|
||||
{
|
||||
l_result = CVRWorld.Instance.baseMovementSpeed;
|
||||
l_result *= CVRWorld.Instance.sprintMultiplier;
|
||||
l_result *= CVRWorld.Instance.inAirMovementMultiplier;
|
||||
l_result *= CVRWorld.Instance.flyMultiplier;
|
||||
}
|
||||
return l_result;
|
||||
}
|
||||
|
||||
public static void ExecuteScript(this CohtmlControlledViewDisposable p_instance, string p_script) => ((cohtml.Net.View)ms_cohtmlView.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)
|
||||
{
|
||||
return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one);
|
||||
}
|
||||
}
|
||||
}
|
||||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core.UI;
|
||||
using ABI_RC.Systems.MovementSystem;
|
||||
using RootMotion.FinalIK;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_amt
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
static readonly FieldInfo ms_grounded = typeof(MovementSystem).GetField("_isGrounded", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly FieldInfo ms_groundedRaw = typeof(MovementSystem).GetField("_isGroundedRaw", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly FieldInfo ms_hasToes = typeof(IKSolverVR).GetField("hasToes", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
public static bool IsInVR() => ((ABI_RC.Core.Savior.CheckVR.Instance != null) && ABI_RC.Core.Savior.CheckVR.Instance.hasVrDeviceLoaded);
|
||||
|
||||
public static bool IsGroundedRaw(this MovementSystem p_instance) => (bool)ms_groundedRaw.GetValue(MovementSystem.Instance);
|
||||
|
||||
public static bool HasToes(this IKSolverVR p_instance) => (bool)ms_hasToes.GetValue(p_instance);
|
||||
|
||||
public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
|
||||
public static float GetWorldMovementLimit()
|
||||
{
|
||||
float l_result = 1f;
|
||||
if(CVRWorld.Instance != null)
|
||||
{
|
||||
l_result = CVRWorld.Instance.baseMovementSpeed;
|
||||
l_result *= CVRWorld.Instance.sprintMultiplier;
|
||||
l_result *= CVRWorld.Instance.inAirMovementMultiplier;
|
||||
l_result *= CVRWorld.Instance.flyMultiplier;
|
||||
}
|
||||
return l_result;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,89 +1,97 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>AvatarMotionTweaker</Product>
|
||||
<PackageId>AvatarMotionTweaker</PackageId>
|
||||
<Version>1.2.9</Version>
|
||||
<Platforms>x64</Platforms>
|
||||
<AssemblyName>ml_amt</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Test.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="AvatarMotionTweaker.json" />
|
||||
<None Remove="resources\menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
</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="Assembly-CSharp-firstpass">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="cohtml.Net">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Cohtml.Runtime">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.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="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>
|
||||
</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>
|
||||
<Reference Include="UnityEngine.PhysicsModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.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>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>AvatarMotionTweaker</Product>
|
||||
<PackageId>AvatarMotionTweaker</PackageId>
|
||||
<Version>1.3.6</Version>
|
||||
<Platforms>x64</Platforms>
|
||||
<AssemblyName>ml_amt</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Test.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="AvatarMotionTweaker.json" />
|
||||
<None Remove="resources\mod_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
1008
ml_lme/LeapInput.cs
1008
ml_lme/LeapInput.cs
File diff suppressed because it is too large
Load diff
|
@ -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);
|
|
@ -1,429 +1,412 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.IK;
|
||||
using RootMotion.FinalIK;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
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);
|
||||
|
||||
VRIK m_vrIK = null;
|
||||
Vector4 m_armsWeights = Vector2.zero;
|
||||
bool m_inVR = false;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
// Unity events
|
||||
void Start()
|
||||
{
|
||||
m_inVR = Utils.IsInVR();
|
||||
|
||||
m_leftHandTarget = new GameObject("RotationTarget").transform;
|
||||
m_leftHandTarget.parent = LeapTracking.Instance.GetLeftHand();
|
||||
m_leftHandTarget.localPosition = Vector3.zero;
|
||||
m_leftHandTarget.localRotation = Quaternion.identity;
|
||||
|
||||
m_rightHandTarget = new GameObject("RotationTarget").transform;
|
||||
m_rightHandTarget.parent = LeapTracking.Instance.GetRightHand();
|
||||
m_rightHandTarget.localPosition = Vector3.zero;
|
||||
m_rightHandTarget.localRotation = Quaternion.identity;
|
||||
|
||||
Settings.EnabledChange += this.OnEnabledChange;
|
||||
Settings.FingersOnlyChange += this.OnFingersOnlyChange;
|
||||
Settings.TrackElbowsChange += this.OnTrackElbowsChange;
|
||||
|
||||
OnEnabledChange(Settings.Enabled);
|
||||
OnFingersOnlyChange(Settings.FingersOnly);
|
||||
OnTrackElbowsChange(Settings.TrackElbows);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
Settings.EnabledChange -= this.OnEnabledChange;
|
||||
Settings.FingersOnlyChange -= this.OnFingersOnlyChange;
|
||||
Settings.TrackElbowsChange -= this.OnTrackElbowsChange;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if(m_enabled)
|
||||
{
|
||||
GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
if((m_leftArmIK != null) && (m_rightArmIK != null))
|
||||
{
|
||||
m_leftArmIK.solver.IKPositionWeight = Mathf.Lerp(m_leftArmIK.solver.IKPositionWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
m_leftArmIK.solver.IKRotationWeight = Mathf.Lerp(m_leftArmIK.solver.IKRotationWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
if(m_trackElbows)
|
||||
m_leftArmIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_leftArmIK.solver.arm.bendGoalWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
|
||||
m_rightArmIK.solver.IKPositionWeight = Mathf.Lerp(m_rightArmIK.solver.IKPositionWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
m_rightArmIK.solver.IKRotationWeight = Mathf.Lerp(m_rightArmIK.solver.IKRotationWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
if(m_trackElbows)
|
||||
m_rightArmIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_rightArmIK.solver.arm.bendGoalWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if(m_enabled && !m_inVR && (m_poseHandler != null))
|
||||
{
|
||||
GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
Vector3 l_hipsLocalPos = m_hips.localPosition;
|
||||
Quaternion l_hipsLocalRot = m_hips.localRotation;
|
||||
|
||||
m_poseHandler.GetHumanPose(ref m_pose);
|
||||
UpdateFingers(l_data);
|
||||
m_poseHandler.SetHumanPose(ref m_pose);
|
||||
|
||||
m_hips.localPosition = l_hipsLocalPos;
|
||||
m_hips.localRotation = l_hipsLocalRot;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
m_rightTargetActive = false;
|
||||
|
||||
if(!m_inVR)
|
||||
m_poseHandler?.Dispose();
|
||||
m_poseHandler = null;
|
||||
|
||||
m_leftHandTarget.localPosition = Vector3.zero;
|
||||
m_leftHandTarget.localRotation = Quaternion.identity;
|
||||
m_rightHandTarget.localPosition = Vector3.zero;
|
||||
m_rightHandTarget.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
internal void OnAvatarSetup()
|
||||
{
|
||||
m_inVR = Utils.IsInVR();
|
||||
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
|
||||
|
||||
if(PlayerSetup.Instance._animator.isHuman)
|
||||
{
|
||||
Vector3 l_hipsPos = Vector3.zero;
|
||||
m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
|
||||
if(m_hips != null)
|
||||
l_hipsPos = m_hips.localPosition;
|
||||
|
||||
if(!m_inVR)
|
||||
{
|
||||
// Force desktop avatar into T-Pose
|
||||
m_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._avatar.transform);
|
||||
m_poseHandler.GetHumanPose(ref m_pose);
|
||||
|
||||
HumanPose l_tPose = new HumanPose
|
||||
{
|
||||
bodyPosition = m_pose.bodyPosition,
|
||||
bodyRotation = m_pose.bodyRotation,
|
||||
muscles = new float[m_pose.muscles.Length]
|
||||
};
|
||||
for(int i = 0; i < l_tPose.muscles.Length; i++)
|
||||
l_tPose.muscles[i] = ms_tposeMuscles[i];
|
||||
m_poseHandler.SetHumanPose(ref l_tPose);
|
||||
}
|
||||
|
||||
Transform l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
|
||||
if(l_hand != null)
|
||||
m_leftHandTarget.localRotation = (m_inVR ? ms_offsetLeft : ms_offsetLeftDesktop) * (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;
|
||||
|
||||
if(m_vrIK == null)
|
||||
{
|
||||
Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest);
|
||||
if(l_chest == null)
|
||||
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest);
|
||||
if(l_chest == null)
|
||||
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine);
|
||||
|
||||
m_leftArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
|
||||
m_leftArmIK.solver.isLeft = true;
|
||||
m_leftArmIK.solver.SetChain(
|
||||
l_chest,
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftShoulder),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand),
|
||||
PlayerSetup.Instance._animator.transform
|
||||
);
|
||||
m_leftArmIK.solver.arm.target = m_leftHandTarget;
|
||||
m_leftArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetLeftElbow();
|
||||
m_leftArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
m_leftArmIK.enabled = (m_enabled && !m_fingersOnly);
|
||||
|
||||
m_rightArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
|
||||
m_rightArmIK.solver.isLeft = false;
|
||||
m_rightArmIK.solver.SetChain(
|
||||
l_chest,
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand),
|
||||
PlayerSetup.Instance._animator.transform
|
||||
);
|
||||
m_rightArmIK.solver.arm.target = m_rightHandTarget;
|
||||
m_rightArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetRightElbow();
|
||||
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
m_rightArmIK.enabled = (m_enabled && !m_fingersOnly);
|
||||
|
||||
m_poseHandler?.SetHumanPose(ref m_pose);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if(m_hips != null)
|
||||
m_hips.localPosition = l_hipsPos;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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)))
|
||||
{
|
||||
m_vrIK.solver.leftArm.positionWeight = 1f;
|
||||
m_vrIK.solver.leftArm.rotationWeight = 1f;
|
||||
}
|
||||
if(m_rightTargetActive && (Mathf.Approximately(m_armsWeights.z, 0f) || Mathf.Approximately(m_armsWeights.w, 0f)))
|
||||
{
|
||||
m_vrIK.solver.rightArm.positionWeight = 1f;
|
||||
m_vrIK.solver.rightArm.rotationWeight = 1f;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// Settings
|
||||
void OnEnabledChange(bool p_state)
|
||||
{
|
||||
m_enabled = p_state;
|
||||
|
||||
RefreshArmIK();
|
||||
if(!m_enabled || m_fingersOnly)
|
||||
RestoreVRIK();
|
||||
}
|
||||
|
||||
void OnFingersOnlyChange(bool p_state)
|
||||
{
|
||||
m_fingersOnly = p_state;
|
||||
|
||||
RefreshArmIK();
|
||||
if(!m_enabled || m_fingersOnly)
|
||||
RestoreVRIK();
|
||||
}
|
||||
|
||||
void OnTrackElbowsChange(bool p_state)
|
||||
{
|
||||
m_trackElbows = p_state;
|
||||
|
||||
if((m_leftArmIK != null) && (m_rightArmIK != null))
|
||||
{
|
||||
m_leftArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
}
|
||||
|
||||
RestoreVRIK();
|
||||
}
|
||||
|
||||
// Arbitrary
|
||||
void RestoreVRIK()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshArmIK()
|
||||
{
|
||||
if((m_leftArmIK != null) && (m_rightArmIK != null))
|
||||
{
|
||||
m_leftArmIK.enabled = (m_enabled && !m_fingersOnly);
|
||||
m_rightArmIK.enabled = (m_enabled && !m_fingersOnly);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdatePoseMuscle(ref HumanPose p_pose, int p_index, float p_value)
|
||||
{
|
||||
if(p_pose.muscles.Length > p_index)
|
||||
p_pose.muscles[p_index] = p_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.IK;
|
||||
using RootMotion.FinalIK;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_lme
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
class LeapTracked : MonoBehaviour
|
||||
{
|
||||
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);
|
||||
|
||||
bool m_inVR = false;
|
||||
VRIK m_vrIK = null;
|
||||
Transform m_hips = 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; // VRIK only
|
||||
bool m_rightTargetActive = false; // VRIK only
|
||||
|
||||
// Unity events
|
||||
void Start()
|
||||
{
|
||||
m_inVR = Utils.IsInVR();
|
||||
|
||||
m_leftHandTarget = new GameObject("RotationTarget").transform;
|
||||
m_leftHandTarget.parent = LeapTracking.Instance.GetLeftHand();
|
||||
m_leftHandTarget.localPosition = Vector3.zero;
|
||||
m_leftHandTarget.localRotation = Quaternion.identity;
|
||||
|
||||
m_rightHandTarget = new GameObject("RotationTarget").transform;
|
||||
m_rightHandTarget.parent = LeapTracking.Instance.GetRightHand();
|
||||
m_rightHandTarget.localPosition = Vector3.zero;
|
||||
m_rightHandTarget.localRotation = Quaternion.identity;
|
||||
|
||||
Settings.EnabledChange += this.OnEnabledChange;
|
||||
Settings.FingersOnlyChange += this.OnFingersOnlyChange;
|
||||
Settings.TrackElbowsChange += this.OnTrackElbowsChange;
|
||||
|
||||
OnEnabledChange(Settings.Enabled);
|
||||
OnFingersOnlyChange(Settings.FingersOnly);
|
||||
OnTrackElbowsChange(Settings.TrackElbows);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if(m_enabled)
|
||||
{
|
||||
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
if((m_leftArmIK != null) && (m_rightArmIK != null))
|
||||
{
|
||||
m_leftArmIK.solver.IKPositionWeight = Mathf.Lerp(m_leftArmIK.solver.IKPositionWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
m_leftArmIK.solver.IKRotationWeight = Mathf.Lerp(m_leftArmIK.solver.IKRotationWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
if(m_trackElbows)
|
||||
m_leftArmIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_leftArmIK.solver.arm.bendGoalWeight, (l_data.m_leftHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
|
||||
m_rightArmIK.solver.IKPositionWeight = Mathf.Lerp(m_rightArmIK.solver.IKPositionWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
m_rightArmIK.solver.IKRotationWeight = Mathf.Lerp(m_rightArmIK.solver.IKRotationWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
if(m_trackElbows)
|
||||
m_rightArmIK.solver.arm.bendGoalWeight = Mathf.Lerp(m_rightArmIK.solver.arm.bendGoalWeight, (l_data.m_rightHand.m_present && !m_fingersOnly) ? 1f : 0f, 0.25f);
|
||||
}
|
||||
|
||||
if((m_vrIK != null) && !m_fingersOnly)
|
||||
{
|
||||
m_leftTargetActive = l_data.m_leftHand.m_present;
|
||||
m_rightTargetActive = l_data.m_rightHand.m_present;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if(m_enabled && !m_inVR && (m_poseHandler != null))
|
||||
{
|
||||
LeapParser.LeapData l_data = LeapManager.Instance.GetLatestData();
|
||||
|
||||
Vector3 l_hipsLocalPos = m_hips.localPosition;
|
||||
Quaternion l_hipsLocalRot = m_hips.localRotation;
|
||||
|
||||
m_poseHandler.GetHumanPose(ref m_pose);
|
||||
UpdateFingers(l_data);
|
||||
m_poseHandler.SetHumanPose(ref m_pose);
|
||||
|
||||
m_hips.localPosition = l_hipsLocalPos;
|
||||
m_hips.localRotation = l_hipsLocalRot;
|
||||
}
|
||||
}
|
||||
|
||||
// Game events
|
||||
internal void OnAvatarClear()
|
||||
{
|
||||
m_vrIK = null;
|
||||
m_hips = null;
|
||||
m_leftArmIK = null;
|
||||
m_rightArmIK = null;
|
||||
m_leftTargetActive = false;
|
||||
m_rightTargetActive = false;
|
||||
|
||||
if(!m_inVR)
|
||||
m_poseHandler?.Dispose();
|
||||
m_poseHandler = null;
|
||||
|
||||
m_leftHandTarget.localPosition = Vector3.zero;
|
||||
m_leftHandTarget.localRotation = Quaternion.identity;
|
||||
m_rightHandTarget.localPosition = Vector3.zero;
|
||||
m_rightHandTarget.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
internal void OnAvatarSetup()
|
||||
{
|
||||
m_inVR = Utils.IsInVR();
|
||||
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
|
||||
|
||||
if(PlayerSetup.Instance._animator.isHuman)
|
||||
{
|
||||
Vector3 l_hipsPos = Vector3.zero;
|
||||
m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
|
||||
if(m_hips != null)
|
||||
l_hipsPos = m_hips.localPosition;
|
||||
|
||||
if(!m_inVR)
|
||||
{
|
||||
// Force desktop avatar into T-Pose
|
||||
m_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._avatar.transform);
|
||||
m_poseHandler.GetHumanPose(ref m_pose);
|
||||
|
||||
HumanPose l_tPose = new HumanPose
|
||||
{
|
||||
bodyPosition = m_pose.bodyPosition,
|
||||
bodyRotation = m_pose.bodyRotation,
|
||||
muscles = new float[m_pose.muscles.Length]
|
||||
};
|
||||
for(int i = 0; i < l_tPose.muscles.Length; i++)
|
||||
l_tPose.muscles[i] = ms_tposeMuscles[i];
|
||||
m_poseHandler.SetHumanPose(ref l_tPose);
|
||||
}
|
||||
|
||||
Transform l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand);
|
||||
if(l_hand != null)
|
||||
m_leftHandTarget.localRotation = ms_offsetLeft * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
|
||||
|
||||
l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand);
|
||||
if(l_hand != null)
|
||||
m_rightHandTarget.localRotation = ms_offsetRight * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
|
||||
|
||||
if(m_vrIK == null)
|
||||
{
|
||||
Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest);
|
||||
if(l_chest == null)
|
||||
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest);
|
||||
if(l_chest == null)
|
||||
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine);
|
||||
|
||||
m_leftArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
|
||||
m_leftArmIK.solver.isLeft = true;
|
||||
m_leftArmIK.solver.SetChain(
|
||||
l_chest,
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftShoulder),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftUpperArm),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftLowerArm),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftHand),
|
||||
PlayerSetup.Instance._animator.transform
|
||||
);
|
||||
m_leftArmIK.solver.arm.target = m_leftHandTarget;
|
||||
m_leftArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetLeftElbow();
|
||||
m_leftArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
m_leftArmIK.enabled = (m_enabled && !m_fingersOnly);
|
||||
|
||||
m_rightArmIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
|
||||
m_rightArmIK.solver.isLeft = false;
|
||||
m_rightArmIK.solver.SetChain(
|
||||
l_chest,
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
|
||||
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand),
|
||||
PlayerSetup.Instance._animator.transform
|
||||
);
|
||||
m_rightArmIK.solver.arm.target = m_rightHandTarget;
|
||||
m_rightArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetRightElbow();
|
||||
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
m_rightArmIK.enabled = (m_enabled && !m_fingersOnly);
|
||||
|
||||
m_poseHandler?.SetHumanPose(ref m_pose);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vrIK.solver.OnPreUpdate += this.OnIKPreUpdate;
|
||||
m_vrIK.solver.OnPostUpdate += this.OnIKPostUpdate;
|
||||
}
|
||||
|
||||
if(m_hips != null)
|
||||
m_hips.localPosition = l_hipsPos;
|
||||
}
|
||||
}
|
||||
|
||||
// VRIK updates
|
||||
void OnIKPreUpdate()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
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
|
||||
void OnEnabledChange(bool p_state)
|
||||
{
|
||||
m_enabled = p_state;
|
||||
|
||||
RefreshArmIK();
|
||||
ResetTargetsStates();
|
||||
}
|
||||
|
||||
void OnFingersOnlyChange(bool p_state)
|
||||
{
|
||||
m_fingersOnly = p_state;
|
||||
|
||||
RefreshArmIK();
|
||||
ResetTargetsStates();
|
||||
}
|
||||
|
||||
void OnTrackElbowsChange(bool p_state)
|
||||
{
|
||||
m_trackElbows = p_state;
|
||||
|
||||
if((m_leftArmIK != null) && (m_rightArmIK != null))
|
||||
{
|
||||
m_leftArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
|
||||
}
|
||||
|
||||
ResetTargetsStates();
|
||||
}
|
||||
|
||||
// Arbitrary
|
||||
void ResetTargetsStates()
|
||||
{
|
||||
m_leftTargetActive = false;
|
||||
m_rightTargetActive = false;
|
||||
}
|
||||
|
||||
void RefreshArmIK()
|
||||
{
|
||||
if((m_leftArmIK != null) && (m_rightArmIK != null))
|
||||
{
|
||||
m_leftArmIK.enabled = (m_enabled && !m_fingersOnly);
|
||||
m_rightArmIK.enabled = (m_enabled && !m_fingersOnly);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
p_pose.muscles[p_index] = p_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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.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,48 +1,50 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Core.UI;
|
||||
using ABI_RC.Systems.InputManagement;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_lme
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
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 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.lossyScale : Vector3.one);
|
||||
}
|
||||
|
||||
public static void ShowHUDNotification(string p_title, string p_message, string p_small = "", bool p_immediate = false)
|
||||
{
|
||||
if(CohtmlHud.Instance != null)
|
||||
{
|
||||
if(p_immediate)
|
||||
CohtmlHud.Instance.ViewDropTextImmediate(p_title, p_message, p_small);
|
||||
else
|
||||
CohtmlHud.Instance.ViewDropText(p_title, p_message, p_small);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Swap<T>(ref T lhs, ref T rhs)
|
||||
{
|
||||
T temp = lhs;
|
||||
lhs = rhs;
|
||||
rhs = temp;
|
||||
}
|
||||
|
||||
public static float InverseLerpUnclamped(float a, float b, float value)
|
||||
{
|
||||
if(a != b)
|
||||
return (value - a) / (b - a);
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Core.UI;
|
||||
using ABI_RC.Systems.InputManagement;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
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 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.lossyScale : Vector3.one);
|
||||
}
|
||||
|
||||
public static void ShowHUDNotification(string p_title, string p_message, string p_small = "", bool p_immediate = false)
|
||||
{
|
||||
if(CohtmlHud.Instance != null)
|
||||
{
|
||||
if(p_immediate)
|
||||
CohtmlHud.Instance.ViewDropTextImmediate(p_title, p_message, p_small);
|
||||
else
|
||||
CohtmlHud.Instance.ViewDropText(p_title, p_message, p_small);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
lhs = rhs;
|
||||
rhs = temp;
|
||||
}
|
||||
|
||||
public static float InverseLerpUnclamped(float a, float b, float value)
|
||||
{
|
||||
if(a != b)
|
||||
return (value - a) / (b - a);
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -1,99 +1,115 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PackageId>LeapMotionExtension</PackageId>
|
||||
<Version>1.4.0</Version>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>LeapMotionExtension</Product>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="vendor\LeapSDK\**" />
|
||||
<EmbeddedResource Remove="vendor\LeapSDK\**" />
|
||||
<None Remove="vendor\LeapSDK\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="LeapMotionExtension.json" />
|
||||
<None Remove="resources\leapmotion_controller.asset" />
|
||||
<None Remove="resources\leapmotion_hands.asset" />
|
||||
<None Remove="resources\menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\leapmotion_controller.asset" />
|
||||
<EmbeddedResource Include="resources\leapmotion_hands.asset" />
|
||||
<EmbeddedResource Include="resources\menu.js" />
|
||||
<EmbeddedResource Include="vendor\LeapSDK\lib\x64\LeapC.dll">
|
||||
<Link>LeapC.dll</Link>
|
||||
</EmbeddedResource>
|
||||
</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="Assembly-CSharp-firstpass">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="cohtml.Net">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Cohtml.Runtime">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.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="ml_pmc">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_pmc.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.AssetBundleModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AssetBundleModule.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>
|
||||
<Reference Include="UnityEngine.XRModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.XRModule.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="vendor\LeapCSharp\" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PackageId>LeapMotionExtension</PackageId>
|
||||
<Version>1.4.5</Version>
|
||||
<Authors>SDraw</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>LeapMotionExtension</Product>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="vendor\LeapSDK\**" />
|
||||
<EmbeddedResource Remove="vendor\LeapSDK\**" />
|
||||
<None Remove="vendor\LeapSDK\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="LeapMotionExtension.json" />
|
||||
<None Remove="resources\leapmotion_controller.asset" />
|
||||
<None Remove="resources\leapmotion_hands.asset" />
|
||||
<None Remove="resources\mod_menu.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\leapmotion_controller.asset" />
|
||||
<EmbeddedResource Include="resources\leapmotion_hands.asset" />
|
||||
<EmbeddedResource Include="resources\mod_menu.js" />
|
||||
<EmbeddedResource Include="vendor\LeapSDK\lib\x64\LeapC.dll">
|
||||
<Link>resources/LeapC.dll</Link>
|
||||
</EmbeddedResource>
|
||||
</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="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>
|
||||
|
||||
<ItemGroup>
|
||||
<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>
|
||||
|
||||
</Project>
|
||||
|
|
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.
|
||||
*
|
||||
|
|
964
ml_lme/vendor/LeapSDK/LICENSE.md
vendored
964
ml_lme/vendor/LeapSDK/LICENSE.md
vendored
|
@ -1,482 +1,482 @@
|
|||
ULTRALEAP TRACKING SDK AGREEMENT
|
||||
|
||||
Updated: 22 March 2022
|
||||
|
||||
Permitted uses
|
||||
|
||||
This SDK Agreement (“Agreement”) covers use of the Ultraleap hand tracking SDK (the “SDK”) by
|
||||
individuals and businesses for the following purposes:
|
||||
|
||||
1. Your personal, non-commercial use (for the avoidance of doubt, excluding use for the design or
|
||||
manufacture of a commercial or distributable product (e.g in design studios)); or
|
||||
|
||||
2. Commercial use for the development and sale consumer facing games, made available for sale to
|
||||
be purchased by consumers for personal use either at retail or through app stores (excluding,
|
||||
without limitation, location-based entertainment and arcade applications); or
|
||||
|
||||
3. Demonstration of your application to internal and external stakeholders and customers where
|
||||
there is no transaction, no sale of tickets specifically for the application, or any other form of
|
||||
compensation for you or your organisation,
|
||||
but in all cases excluding applications relating to the following: (a) the production of or trade in tobacco,
|
||||
alcoholic beverages, and related products, (b) the production or trade in weapons of any kind or any
|
||||
military applications, (c) casinos, gambling and equivalent enterprises, (d) human cloning, human
|
||||
embryos, or stem cells, or (e) nuclear energy.
|
||||
Any other uses, or applications using third party hardware are “Specialised Applications” and will require
|
||||
a separate license agreement. Please contact Ultraleap info@ultraleap.com for more information.
|
||||
In each case, the SDK may only be used with Ultraleap Hardware and Ultraleap Software.
|
||||
|
||||
1. Parties
|
||||
|
||||
1.1. This Agreement is made between the individual or entity (“you” or the “Developer”) that accepts
|
||||
it and Ultraleap Limited (“Ultraleap”). You accept this Agreement by (a) accepting it on download
|
||||
of the SDK, or (b) if you use or access the SDK or any part of the SDK. Your entry into this
|
||||
Agreement also binds your authorized users, and your company or organisation.
|
||||
|
||||
1.2. If you do not agree to the terms of this Agreement you must not use the SDK.
|
||||
|
||||
1.3. Capitalized terms bear the meanings given in the “Definitions” section of this Agreement.
|
||||
|
||||
1.4. This Agreement incorporates the terms of the Ultraleap Hand Tracking End User License
|
||||
Agreement (“EULA”), which is available at https://developer.leapmotion.com/end-user-license-
|
||||
agreement or from Ultraleap on request. In the event of a conflict between these terms and the
|
||||
EULA, these terms will prevail.
|
||||
|
||||
2. License
|
||||
|
||||
Development License
|
||||
|
||||
2.1. Conditional on your compliance with the terms and conditions of this Agreement, Ultraleap
|
||||
hereby grants you a limited, non-exclusive, personal, revocable, non-sublicensable, and non-
|
||||
transferable license to:
|
||||
|
||||
2.1.1. install and use a reasonable number of copies of the SDK on computers owned or
|
||||
controlled by you for the purpose of developing and testing applications that (a) are not
|
||||
Specialised Applications and (b) are intended for use solely in connection with Ultraleap
|
||||
Hardware and Ultraleap Software (each being an “Ultraleap Enabled Application”); and
|
||||
|
||||
2.1.2. modify and incorporate into your Ultraleap Enabled Application any sample code
|
||||
provided in the SDK.
|
||||
|
||||
Distribution License
|
||||
|
||||
2.2. Conditional on your compliance with the terms and conditions of this Agreement, Ultraleap
|
||||
hereby grants you a limited, non-exclusive, personal, revocable, non-transferrable license of
|
||||
Ultraleap’s intellectual property rights to the extent necessary to:
|
||||
|
||||
2.2.1. copy and distribute (or have copied and distributed) the Ultraleap Redistributables,
|
||||
solely as compiled with, incorporated into, or packaged with, your Ultraleap Enabled
|
||||
Application; and
|
||||
|
||||
2.2.2. to make (but not have made), use, sell, offer for sale, and import your Ultraleap Enabled
|
||||
Application.
|
||||
|
||||
3. Restrictions
|
||||
|
||||
3.1. The license granted to you in section 2.1 and section 2.2 is subject to the following restrictions,
|
||||
as well as others listed in this Agreement:
|
||||
|
||||
3.1.1. Except as expressly permitted in section 2.1, (a) you may not publish, distribute, or copy
|
||||
the SDK, and (b) you may not modify or create derivative works of the SDK;
|
||||
|
||||
3.1.2. Except as expressly permitted in section 2.2, you may not, and may not allow any third
|
||||
party, directly or indirectly, to publish, post, or otherwise make available, the Ultraleap
|
||||
Redistributables;
|
||||
|
||||
3.1.3. You may not, and may not enable others to, distributed the Non-Redistributable
|
||||
Materials;
|
||||
|
||||
3.1.4. You may use the SDK solely in connection with Ultraleap Hardware and/or Ultraleap
|
||||
Software;
|
||||
|
||||
3.1.5. You may not use the SDK to create, or aid in the creation, directly or indirectly, of any
|
||||
software or hardware which provides hand tracking functionality or which is otherwise
|
||||
substantially similar to the features or functionality of Ultraleap products;
|
||||
|
||||
3.1.6. You may not, and may not enable others to, directly or indirectly, reverse engineer,
|
||||
decompile, disassemble, or otherwise attempt to reconstruct, identify, or discover any
|
||||
source code, underlying ideas, techniques, or algorithms in the Ultraleap Software, the
|
||||
Ultraleap Hardware, or any software which forms part of the SDK, nor attempt to
|
||||
circumvent any related security measures (except as and only to the extent any
|
||||
foregoing restriction is prohibited by applicable law notwithstanding the foregoing
|
||||
restriction, or to the extent as may be permitted by licensing terms governing the use of
|
||||
any open source software components or sample code contained within the SDK;
|
||||
|
||||
3.1.7. You may not remove, obscure, or alter any proprietary rights or confidentiality notices
|
||||
within the SDK or any software, documentation, or other materials in it or supplied with
|
||||
it;
|
||||
|
||||
3.1.8. You must not allow the Ultraleap Software or SDK to fall under the terms of any license
|
||||
which would obligate you or Ultraleap to make available or publish any part of the
|
||||
Ultraleap Software or SDK.
|
||||
|
||||
3.1.9. You may not create Ultraleap Enabled Applications or other software that prevent or
|
||||
degrade the interaction of applications developed by others with the Ultraleap Software;
|
||||
|
||||
3.1.10. You may not represent functionality provided by any Ultraleap hardware or software as
|
||||
your technology or the technology of any third party. For example (without limitation)
|
||||
you may not describe any application, technology, or feature developed or distributed
|
||||
by you that incorporates Ultraleap technology as your gesture or touchless control
|
||||
technology without providing attribution to Ultraleap; and
|
||||
|
||||
3.1.11. You may not allow your Ultraleap Enabled Application to be used for a High Risk Use.
|
||||
|
||||
4. Updates
|
||||
|
||||
4.1. The terms of this Agreement will apply to any Updates which Ultraleap (in its sole discretion)
|
||||
makes available to you. You agree that Updates may require you to change or update your
|
||||
Ultraleap Enabled Application, and may affect your ability to use, access, or interact with the
|
||||
Ultraleap Software, the Ultraleap Hardware, and/or the SDK. You are solely responsible for
|
||||
turning off any auto-update functionality of the Ultraleap Software.
|
||||
|
||||
5. Trademarks and Marketing
|
||||
|
||||
5.1. Conditioned upon compliance with the terms and conditions of this Agreement, Ultraleap grants
|
||||
you a limited, non-exclusive, personal, license to reproduce and use Ultraleap trademarks solely
|
||||
to (a) mark the Ultraleap Enabled Application, (b) produce and make available related collateral,
|
||||
and (c) to promote and market your Ultraleap Enabled Application, in each case solely in
|
||||
accordance with the Ultraleap trademark guidelines that Ultraleap may provide to you from time
|
||||
to time.
|
||||
|
||||
5.2. For so long as Ultraleap technology is included with the Ultraleap Enabled Application, you must
|
||||
identify on the packaging of the Ultraleap Enabled Application, the loading screen and start-up
|
||||
messages for the Ultraleap Enabled Application, and list on your website and marketing collateral
|
||||
(in each case, where applicable), as prominently as other listed features and functionality, that
|
||||
Ultraleap technology is included with the Ultraleap Enabled Application, in accordance with the
|
||||
Ultraleap trademark guidelines that Ultraleap may provide to you from time to time. All
|
||||
references to Ultraleap or Ultraleap Technology will be subject to Ultraleap’s prior approval,
|
||||
which will not be unreasonably withheld.
|
||||
|
||||
5.3. Ultraleap may at its option mention you and your products using Ultraleap technology in
|
||||
Ultraleap’s press releases, press briefings, social media accounts, and/or website, and may use
|
||||
your trademarks for such purpose. You grant to Ultraleap and its affiliates a non-exclusive,
|
||||
worldwide and royalty-free limited license to use, reproduce, display, perform, publish and
|
||||
distribute screenshots, elements, assets, photographic, graphic or video reproductions or
|
||||
fragments of your Ultraleap Enabled Application in any medium or media, solely for purposes of
|
||||
promotion of your Ultraleap Enabled Application or of Ultraleap and its technology and business.
|
||||
The rights set out in this section 5.3 will survive termination of this Agreement in respect of
|
||||
materials already in existence as at the date of termination.
|
||||
|
||||
6. EULA and Other Licenses
|
||||
|
||||
6.1. Example code made publicly available by Ultraleap on its developer web site may be provided
|
||||
subject to the Apache 2.0 license, this Agreement, or other licenses, as specified in the notice or
|
||||
readme files distributed with the example or in related documentation. The SDK may otherwise
|
||||
include software or other materials that are provided under a separate license agreement, and
|
||||
that separate license will govern the use of such software or other materials in the event of a
|
||||
conflict with this Agreement. Any such separate license agreement may be indicated in the
|
||||
license, notice, or readme files distributed with the applicable software or other materials or in
|
||||
related documentation.
|
||||
|
||||
6.2. You must either require end users of your Ultraleap Enabled Application to affirmatively agree to
|
||||
the Ultraleap EULA, or require its End Users to affirmatively agree to your own end user license
|
||||
agreement that protects Ultraleap at least as much as the Ultraleap EULA.
|
||||
|
||||
7. High Risk Uses and Waiver
|
||||
|
||||
7.1. Notwithstanding anything in this Agreement, you are not licensed to, and you agree not to, use,
|
||||
copy, sell, offer for sale, or distribute the SDK, Ultraleap Hardware, Ultraleap Software or
|
||||
Ultraleap Redistributables (whether compiled with, incorporated into, or packaged with your
|
||||
Ultraleap Enabled Application or otherwise), for or in connection with uses where failure or fault
|
||||
of the Ultraleap Hardware, Ultraleap Software, Ultraleap Redistributables or your Ultraleap
|
||||
Enabled Application could lead to death or serious bodily injury of any person, or to severe
|
||||
physical or environmental damage (“High Risk Use”). Any such use is strictly prohibited.
|
||||
|
||||
7.2. You acknowledge the SDK may allow you to develop Ultraleap Enabled Applications that enable
|
||||
the control of motorized or mechanical equipment, or other systems, machines or devices. If you
|
||||
elect to use the SDK in such a way, you must take steps to design and test your Ultraleap Enabled
|
||||
Applications to ensure that your Ultraleap Enabled Applications do not present risks of personal
|
||||
injury or death, property damage, or other losses. The Ultraleap Hardware, the Ultraleap
|
||||
Software, the Ultraleap Redistributables and other software in the SDK may not always function
|
||||
as intended. You must design your Ultraleap Enabled Applications so that any failure of Ultraleap
|
||||
Technology and/or such other software as Ultraleap may make available from time to time does
|
||||
not cause personal injury or death, property damage, or other losses. If you choose to use the
|
||||
SDK, (i) you assume all risk that use of the Ultraleap Technology and/or such other software by
|
||||
you or by any others causes any harm or loss, including to the end users of your Ultraleap
|
||||
Enabled Applications or to third parties, (ii) you hereby waive, on behalf of yourself and your
|
||||
Authorized Users, all claims against Ultraleap and its affiliates related to such use, harm or loss
|
||||
(including, but not limited to, any claim that Ultraleap Technology or such other software is
|
||||
defective), and (iii) you agree to hold Ultraleap and its affiliates harmless from such claims.
|
||||
|
||||
8. Confidentiality and Data Protection
|
||||
|
||||
8.1. Beta Software etc. Obligations. You acknowledge and agree that Ultraleap may share alpha or
|
||||
beta software or hardware with you that it identifies as non-public. If so, you agree not to
|
||||
disclose such software or hardware to others without the prior written consent of Ultraleap
|
||||
until the time, if any, it is made public by Ultraleap, and to use such software or hardware only
|
||||
as expressly permitted by Ultraleap. Without limitation to the foregoing, the distribution license
|
||||
set out in section 2.2 shall not apply to any alpha or beta software which may be shared with
|
||||
you.
|
||||
|
||||
8.2. Your Information. Ultraleap may collect personal information provided by you or your
|
||||
Authorized Users to Ultraleap or any group company of Ultraleap in connection with the SDK,
|
||||
and may collect other information from you or your Authorized Users, including technical, non-
|
||||
personally identifiable and/or aggregated information such as usage statistics, hardware
|
||||
configuration, problem / fault data, IP addresses, version number of the SDK, information about
|
||||
which tools and/or services in the SDK are being used and how they are being used, and any
|
||||
other information described in Ultraleap’s privacy policy, currently available at
|
||||
https://www.ultraleap.com/privacy-policy/. Ultraleap may use the information collected to
|
||||
facilitate the provision of Updates and other services to you, to verify compliance with, and
|
||||
enforce, the terms of this Agreement, to improve the SDK and Ultraleap’s other products, and
|
||||
for any other purposes set out in Ultraleap’s privacy policy (these uses, collectively, are
|
||||
“Permitted Uses”). The information collected may be transferred to, stored, and processed in a
|
||||
destination outside the European Economic Area, including (without limitation) by our staff in
|
||||
the USA, China, Japan, and Hong Kong. By submitting information about you and/or your
|
||||
Authorized Users to Ultraleap through your access and use of the SDK, you consent to
|
||||
Ultraleap’s collection and use of the information for the Permitted Uses and represent that you
|
||||
have obtained all consents and permits necessary under applicable law to disclose your
|
||||
Authorized Users’ information to Ultraleap for the Permitted Uses. You further agree that
|
||||
Ultraleap may provide any information collected under this Section 8.2, including your or your
|
||||
Authorized Users’ user name, IP address or other identifying information to law enforcement
|
||||
authorities or as required by applicable law or regulation.
|
||||
|
||||
9. Ownership and Feedback
|
||||
|
||||
9.1. As between you and Ultraleap, Ultraleap owns all right, title, and interest, including all
|
||||
intellectual property rights, in and to the SDK, the Ultraleap Software, Ultraleap Hardware, the
|
||||
Ultraleap Redistributables, and all documentation associated with the foregoing, other than any
|
||||
third party software or materials incorporated into the SDK. You agree not to contest Ultraleap’s
|
||||
ownership of any of the foregoing.
|
||||
|
||||
9.2. Subject to Section 9.1, Ultraleap agrees that it obtains no right, title, or interest from you (or
|
||||
your licensors) under this Agreement in or to your Ultraleap Enabled Applications, including any
|
||||
intellectual property rights which subsist in those Ultraleap Enabled Applications.
|
||||
|
||||
9.3. Feedback. You may (but are not required to) provide feedback, comments, and suggestions
|
||||
(collectively “Feedback”) to Ultraleap. You hereby grant to Ultraleap a non-exclusive, perpetual,
|
||||
irrevocable, paid-up, transferrable, sub-licensable, worldwide license under all intellectual
|
||||
property rights covering such Feedback to use, disclose, and exploit all such Feedback for any
|
||||
purpose.
|
||||
|
||||
10. Your Obligations and Warranties
|
||||
|
||||
In addition to your other obligations under this Agreement, you warrant and agree that:
|
||||
|
||||
10.1. you are at least 18 years of age and have the right and authority to enter into this Agreement on
|
||||
your own behalf and that of your Authorized Users. If you are entering into this Agreement on
|
||||
behalf of your company or organization, you warrant that you have the right and authority to
|
||||
legally bind your company or organization and its Authorized Users;
|
||||
|
||||
10.2. you will use the SDK only in accordance with all accompanying documentation, and in the
|
||||
manner expressly permitted by this Agreement; and
|
||||
|
||||
10.3. your use of the SDK, and the marketing, sales and distribution of your Ultraleap Enabled
|
||||
Application, will be in compliance with all applicable laws and regulations and all UK, U.S. and
|
||||
local or foreign export and re-export restrictions applicable to the technology and
|
||||
documentation provided under this Agreement (including privacy and data security laws and
|
||||
regulations), and you will not develop any Ultraleap Enabled Application which would commit or
|
||||
facilitate the commission of a crime, or other tortious, unlawful, or illegal act.
|
||||
|
||||
11. Agreement and Development Program
|
||||
|
||||
11.1. We reserve the right to change this Agreement, the SDK or the Ultraleap development and
|
||||
licensing program at any time in our discretion. Ultraleap may require that you either accept
|
||||
and agree to the new terms of this Agreement, or, if you do not agree to the new terms, cease
|
||||
or terminate your use of the SDK. Your continued use of the SDK after changes to this
|
||||
Agreement take effect will constitute your acceptance of the changes. If you do not agree to a
|
||||
change, you must stop using the SDK and terminate this Agreement. Any termination of this
|
||||
Agreement by you under this Section 11 (and only this Section 11) will not affect your right,
|
||||
subject to your continued compliance with your obligations under this Agreement, to continue
|
||||
to distribute versions of your Ultraleap Enabled Application created and first distributed before
|
||||
termination, and will not affect the right of your End Users to continue using such versions of
|
||||
your Ultraleap Enabled Application, both of which rights will survive termination.
|
||||
|
||||
12. Term and Termination
|
||||
|
||||
12.1. Term. This Agreement will continue to apply until terminated by either you or Ultraleap as set
|
||||
out below.
|
||||
|
||||
12.2. Termination by You. If you want to terminate this Agreement, you may terminate it by
|
||||
uninstalling and destroying all copies of the SDK that are in the possession, custody or control of
|
||||
you, your Authorized Users and your organization.
|
||||
|
||||
12.3. Termination by Ultraleap. Ultraleap may at any time, terminate this Agreement with you for
|
||||
any reason or for no reason in Ultraleap’s sole discretion, including as a result of non-
|
||||
compliance by you with the restrictions in in this Agreement, or for other reasons.
|
||||
|
||||
12.4. Effect of Termination. Upon termination of this Agreement, all rights granted to you under this
|
||||
Agreement will immediately terminate and you must immediately cease all use and destroy all
|
||||
copies of the SDK in your and your Authorized Users’ possession, custody or control, and, except
|
||||
as specifically set out in Section 11, cease your distribution of Ultraleap Enabled Applications.
|
||||
Sections 3, 8.1, 8.2, 9, 12.4, 14-16, and 17, will survive termination of this Agreement.
|
||||
Termination of this Agreement will not affect the right of your End Users who have downloaded
|
||||
your Ultraleap Enabled Application prior to termination to continue using it.
|
||||
|
||||
13. Indemnification.
|
||||
|
||||
13.1. You agree to indemnify, hold harmless and, at Ultraleap’s option, defend Ultraleap and its
|
||||
affiliates and their respective officers, directors, employees, agents, and representatives
|
||||
harmless from any and all judgments, awards, settlements, liabilities, damages, costs, penalties,
|
||||
fines and other expenses (including court costs and reasonable attorneys’ fees) incurred by
|
||||
them arising out of or relating to any third party claim (a) with respect to your Ultraleap Enabled
|
||||
Application, including products liability, privacy, or intellectual property infringement claims, or
|
||||
(b) based upon your negligence or wilful misconduct or any breach or alleged breach of your
|
||||
representations, warranties, and covenants under this Agreement. In no event may you enter
|
||||
into any settlement or like agreement with a third party that affects Ultraleap’s rights or binds
|
||||
Ultraleap or its affiliates in any way, without the prior written consent of Ultraleap.
|
||||
|
||||
14. Warranty Disclaimer.
|
||||
|
||||
14.1. THE SDK, THE ULTRALEAP SOFTWARE AND THE ULTRALEAP REDISTRIBUTABLES ARE PROVIDED
|
||||
"AS IS" WITHOUT WARRANTY OF ANY KIND. ULTRALEAP, ON BEHALF OF ITSELF AND ITS
|
||||
SUPPLIERS, HEREBY DISCLAIMS ALL REPRESENTATIONS, PROMISES, OR WARRANTIES, WHETHER
|
||||
EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SDK, THE ULTRALEAP
|
||||
SOFTWARE AND THE ULTRALEAP REDISTRIBUTABLES, INCLUDING THEIR CONDITION,
|
||||
AVAILABILITY, OR THE EXISTENCE OF ANY LATENT DEFECTS, AND ULTRALEAP SPECIFICALLY
|
||||
DISCLAIMS ALL IMPLIED WARRANTIES OF MERCHANTABILITY, TITLE, NONINFRINGEMENT,
|
||||
SUITABILITY, AND FITNESS FOR ANY PURPOSE. ULTRALEAP DOES NOT WARRANT THAT THE SDK,
|
||||
THE ULTRALEAP SOFTWARE OR THE ULTALEAP REDISTRIBUTABLES WILL BE ERROR-FREE OR
|
||||
THAT THEY WILL WORK WITHOUT INTERRUPTION.
|
||||
|
||||
15. Limitation of Liability.
|
||||
|
||||
15.1. ULTRALEAP SHALL NOT IN ANY CIRCUMSTANCES WHATEVER BE LIABLE TO YOU, WHETHER IN
|
||||
CONTRACT, TORT (INCLUDING NEGLIGENCE), BREACH OF STATUTORY DUTY, OR OTHERWISE,
|
||||
ARISING UNDER OR IN CONNECTION WITH THE AGREEMENT FOR:
|
||||
|
||||
15.1.1. LOSS OF PROFITS, SALES, BUSINESS, OR REVENUE;
|
||||
|
||||
15.1.2. BUSINESS INTERRUPTION;
|
||||
|
||||
15.1.3. LOSS OF ANTICIPATED SAVINGS;
|
||||
|
||||
15.1.4. LOSS OR CORRUPTION OF DATA OR INFORMATION;
|
||||
|
||||
15.1.5. LOSS OF BUSINESS OPPORTUNITY, GOODWILL OR REPUTATION; OR
|
||||
|
||||
15.1.6. ANY INDIRECT OR CONSEQUENTIAL LOSS OR DAMAGE.
|
||||
|
||||
15.2. OTHER THAN THE LOSSES SET OUT ABOVE (FOR WHICH ULTRALEAP IS NOT LIABLE),
|
||||
ULTRALEAP’S MAXIMUM AGGREGATE LIABILITY UNDER OR IN CONNECTION WITH THE
|
||||
AGREEMENT WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE), BREACH OF STATUTORY
|
||||
DUTY, OR OTHERWISE, SHALL IN ALL CIRCUMSTANCES BE LIMITED TO $1,000 (ONE THOUSAND
|
||||
US DOLLARS). THIS MAXIMUM CAP DOES NOT APPLY TO DEATH OR PERSONAL INJURY
|
||||
RESULTING FROM ULTRALEAP'S NEGLIGENCE; FRAUD OR FRAUDULENT MISREPRESENTATION;
|
||||
OR ANY OTHER LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED BY APPLICABLE LAW.
|
||||
|
||||
15.3. THE AGREEMENT SETS OUT THE FULL EXTENT OF ULTRALEAP’S OBLIGATIONS AND LIABILITIES IN
|
||||
RESPECT OF THE SUPPLY OF THE ULTRALEAP DEVICES, DELIVERABLES AND SOFTWARE. EXCEPT
|
||||
AS EXPRESSLY STATED IN THE AGREEMENT, THERE ARE NO CONDITIONS, WARRANTIES,
|
||||
REPRESENTATIONS OR OTHER TERMS, EXPRESS OR IMPLIED, THAT ARE BINDING ON
|
||||
ULTRALEAP. ANY CONDITION, WARRANTY, REPRESENTATION OR OTHER TERM CONCERNING
|
||||
THE SUPPLY OF THE ULTRALEAP HARDWARE, ULTRALEAP SOFTWARE, THE SDK, THE ULTRALEAP
|
||||
REDISTRIBUTABLES, OR ANY OTHER ULTRALEAP TECHNOLOGY WHICH MIGHT OTHERWISE BE
|
||||
IMPLIED INTO, OR INCORPORATED IN THE AGREEMENT WHETHER BY STATUTE, COMMON LAW
|
||||
OR OTHERWISE, INCLUDING ANY WARRANTY OR CONDITION OF MERCHANTABILITY OR FITNESS
|
||||
FOR A PARTICULAR PURPOSE, IS EXCLUDED TO THE FULLEST EXTENT PERMITTED BY LAW. THESE
|
||||
LIMITATIONS WILL APPLY NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY
|
||||
LIMITED REMEDY. THE PARTIES AGREE THAT THE FOREGOING LIMITATIONS REPRESENT A
|
||||
REASONABLE ALLOCATION OF RISK UNDER THIS AGREEMENT.
|
||||
|
||||
16. Miscellaneous.
|
||||
|
||||
16.1. Assignment. You may not assign this Agreement without the prior written consent of Ultraleap.
|
||||
Any assignment without such consent is void and of no effect. Ultraleap may assign this
|
||||
Agreement without your consent in connection with (a) a merger or consolidation of Ultraleap,
|
||||
(b) a sale or assignment of substantially all its assets, or (c) any other transaction which results
|
||||
in another entity or person owning substantially all of the assets of Ultraleap, or (d) to any of its
|
||||
affiliates. In the event of a permitted assignment, this Agreement will inure to the benefit of and
|
||||
be binding upon the parties and their respective successors and permitted assigns.
|
||||
|
||||
16.2. Waiver; Severability. The failure of the other party to enforce any rights under this Agreement
|
||||
will not be deemed a waiver of any rights. The rights and remedies of the parties in this
|
||||
Agreement are not exclusive and are in addition to any other rights and remedies provided by
|
||||
law. If any provision of this Agreement is held by a court of competent jurisdiction to be
|
||||
contrary to law, the remaining provisions of this Agreement will remain in full force and effect.
|
||||
|
||||
16.3. Reservation. All licenses not expressly granted in this Agreement are reserved and no other
|
||||
licenses, immunity or rights, express or implied, are granted by Ultraleap, by implication,
|
||||
estoppel, or otherwise. The software in the SDK is licensed, not sold.
|
||||
|
||||
16.4. Export Restrictions. The Ultraleap Software is subject to United States and UK export laws and
|
||||
regulations. You must comply with all domestic and international export laws and regulations
|
||||
that apply to the Ultraleap Software. These laws include restrictions on destinations, end users,
|
||||
and end use.
|
||||
|
||||
16.5. Governing Law and Jurisdiction. This Agreement will be exclusively governed by and construed
|
||||
under the laws of the England and Wales, without reference to or application of rules governing
|
||||
choice of laws. All disputes arising out of or related to this Agreement will be subject to the
|
||||
exclusive jurisdiction of courts of England and you hereby consent to such jurisdiction. However,
|
||||
Ultraleap may apply to any court or tribunal worldwide, including but not limited to those
|
||||
having jurisdiction over you or your Authorized Users, to seek injunctive relief.
|
||||
|
||||
16.6. Relationship of the Parties. This Agreement does not create any agency, partnership, or joint
|
||||
venture relationship between Ultraleap and you. This Agreement is for the sole benefit of
|
||||
Ultraleap and you (and indemnified parties), and no other persons will have any right or remedy
|
||||
under this Agreement.
|
||||
|
||||
16.7. Notices. The address for notice to Ultraleap under this Agreement is:
|
||||
Ultraleap Limited
|
||||
The West Wing
|
||||
Glass Wharf
|
||||
Bristol, BS2 0EL
|
||||
United Kingdom
|
||||
Ultraleap may provide you notice under this Agreement by email or other electronic
|
||||
communication or by posting communications to its development community on the Ultraleap
|
||||
developer portal. You consent to receive such notices in any of the foregoing manners and
|
||||
agree that any such notices by Ultraleap will satisfy any legal communication requirements.
|
||||
16.8. Entire Agreement. This Agreement is the entire understanding of the parties with respect to its
|
||||
subject matter and supersedes any previous or contemporaneous communications, whether
|
||||
oral or written with respect to such subject matter.
|
||||
|
||||
17. Definitions
|
||||
|
||||
Whenever capitalized in this Agreement:
|
||||
“Authorized Users” means your employees and contractors, members of your organization or, if you
|
||||
are an educational institution, your faculty, staff and registered students, who (a) have a
|
||||
demonstrable need to know or use the SDK in order to develop and test Ultraleap Enabled
|
||||
Applications on your behalf and (b) each have written and binding agreements with you to protect
|
||||
against the unauthorized use and disclosure of the SDK consistent with the terms and conditions of
|
||||
this Agreement. Authorized Users do not include End Users.
|
||||
“End User” means your end user customer(s) or licensee(s).
|
||||
“Non-Redistributable Materials” means the Ultraleap Software, and any other code, files or
|
||||
materials that are not specifically designated in the SDK as made available for incorporation into
|
||||
Ultraleap Enabled Applications or that are specifically designated in the SDK as not subject to
|
||||
distribution.
|
||||
“SDK” means, collectively, the Ultraleap Redistributables, tools, APIs, sample code, software,
|
||||
documentation, other materials and any updates to the foregoing that may be provided or made
|
||||
available to you by Ultraleap in connection with this Agreement, via the Ultraleap developer portal or
|
||||
otherwise for use in connection with the Ultraleap development program to develop Ultraleap
|
||||
Enabled Applications.
|
||||
“Specialized Application” means an Ultraleap Enabled Application which does not fall within the
|
||||
permitted uses set out in this Agreement.
|
||||
“Ultraleap” “we” or “us” means Ultraleap Limited, a company registered in England with company
|
||||
number 08781720, with a principal place of business at The West Wing, Glass Wharf, Bristol, BS2 0EL,
|
||||
United Kingdom.
|
||||
“Ultraleap Hardware” means the Leap Motion Controller, Stereo IR 170, Stereo IR 170 EK or Ultraleap 3Di each being a device that
|
||||
detects and reads movements within a 3-D interaction space to precisely interact with and control
|
||||
software on a computing device, or an Ultraleap-authorized embedded optical module.
|
||||
“Ultraleap Redistributables” means any .lib code, .dll files, .so files, sample code, or other materials
|
||||
we specifically designate in the SDK as made available for incorporation into or distribution with
|
||||
Ultraleap Enabled Applications.
|
||||
“Ultraleap Software” means the Ultraleap core services application and related applications that
|
||||
interact with Ultraleap Hardware and an operating system to make motion control functionality
|
||||
available to Ultraleap Enabled Applications, and includes any Updates thereto.
|
||||
“Updates” means updates, upgrades, modifications, enhancements, revisions, new releases or new
|
||||
versions to the SDK that Ultraleap may make available to you in connection with this Agreement.
|
||||
Other capitalized terms used in this Agreement have the meaning given them elsewhere in this
|
||||
Agreement.
|
||||
|
||||
18. Supplemental Terms Applicable to the Use of Image API
|
||||
|
||||
18.1. Purpose. You and/or your Ultraleap Enabled Application may access the Image API and use
|
||||
image data available through the Image API only for the purpose of developing and testing
|
||||
Ultraleap Enabled Applications, and only for use with Ultraleap Hardware. You may not use the
|
||||
Image API to develop or aid development of competing motion tracking hardware or software.
|
||||
Any use of the Image API is subject to the terms of the Agreement.
|
||||
|
||||
18.2. Data Protection.
|
||||
|
||||
18.2.1. If you or your Ultraleap Enabled Application collects, uploads, stores, transmits, or
|
||||
shares images, videos, or other personal information available through the Image API,
|
||||
either through or in connection with your Ultraleap Enabled Application, you must
|
||||
expressly provide users with your privacy policy and adhere to it.
|
||||
|
||||
18.2.2. You must obtain specific, opt-in consent from the user for any use that is beyond the
|
||||
limited and express purpose of your Ultraleap Enabled Application.
|
||||
|
||||
18.2.3. You and your Ultraleap Enabled Application must use and store information collected
|
||||
form users securely and only for as long as it is required.
|
||||
|
||||
18.2.4. You agree that you will protect the privacy and legal rights of users, and will comply with
|
||||
all applicable criminal, civil, and statutory privacy and data protection laws and
|
||||
regulations.
|
||||
ULTRALEAP TRACKING SDK AGREEMENT
|
||||
|
||||
Updated: 22 March 2022
|
||||
|
||||
Permitted uses
|
||||
|
||||
This SDK Agreement (“Agreement”) covers use of the Ultraleap hand tracking SDK (the “SDK”) by
|
||||
individuals and businesses for the following purposes:
|
||||
|
||||
1. Your personal, non-commercial use (for the avoidance of doubt, excluding use for the design or
|
||||
manufacture of a commercial or distributable product (e.g in design studios)); or
|
||||
|
||||
2. Commercial use for the development and sale consumer facing games, made available for sale to
|
||||
be purchased by consumers for personal use either at retail or through app stores (excluding,
|
||||
without limitation, location-based entertainment and arcade applications); or
|
||||
|
||||
3. Demonstration of your application to internal and external stakeholders and customers where
|
||||
there is no transaction, no sale of tickets specifically for the application, or any other form of
|
||||
compensation for you or your organisation,
|
||||
but in all cases excluding applications relating to the following: (a) the production of or trade in tobacco,
|
||||
alcoholic beverages, and related products, (b) the production or trade in weapons of any kind or any
|
||||
military applications, (c) casinos, gambling and equivalent enterprises, (d) human cloning, human
|
||||
embryos, or stem cells, or (e) nuclear energy.
|
||||
Any other uses, or applications using third party hardware are “Specialised Applications” and will require
|
||||
a separate license agreement. Please contact Ultraleap info@ultraleap.com for more information.
|
||||
In each case, the SDK may only be used with Ultraleap Hardware and Ultraleap Software.
|
||||
|
||||
1. Parties
|
||||
|
||||
1.1. This Agreement is made between the individual or entity (“you” or the “Developer”) that accepts
|
||||
it and Ultraleap Limited (“Ultraleap”). You accept this Agreement by (a) accepting it on download
|
||||
of the SDK, or (b) if you use or access the SDK or any part of the SDK. Your entry into this
|
||||
Agreement also binds your authorized users, and your company or organisation.
|
||||
|
||||
1.2. If you do not agree to the terms of this Agreement you must not use the SDK.
|
||||
|
||||
1.3. Capitalized terms bear the meanings given in the “Definitions” section of this Agreement.
|
||||
|
||||
1.4. This Agreement incorporates the terms of the Ultraleap Hand Tracking End User License
|
||||
Agreement (“EULA”), which is available at https://developer.leapmotion.com/end-user-license-
|
||||
agreement or from Ultraleap on request. In the event of a conflict between these terms and the
|
||||
EULA, these terms will prevail.
|
||||
|
||||
2. License
|
||||
|
||||
Development License
|
||||
|
||||
2.1. Conditional on your compliance with the terms and conditions of this Agreement, Ultraleap
|
||||
hereby grants you a limited, non-exclusive, personal, revocable, non-sublicensable, and non-
|
||||
transferable license to:
|
||||
|
||||
2.1.1. install and use a reasonable number of copies of the SDK on computers owned or
|
||||
controlled by you for the purpose of developing and testing applications that (a) are not
|
||||
Specialised Applications and (b) are intended for use solely in connection with Ultraleap
|
||||
Hardware and Ultraleap Software (each being an “Ultraleap Enabled Application”); and
|
||||
|
||||
2.1.2. modify and incorporate into your Ultraleap Enabled Application any sample code
|
||||
provided in the SDK.
|
||||
|
||||
Distribution License
|
||||
|
||||
2.2. Conditional on your compliance with the terms and conditions of this Agreement, Ultraleap
|
||||
hereby grants you a limited, non-exclusive, personal, revocable, non-transferrable license of
|
||||
Ultraleap’s intellectual property rights to the extent necessary to:
|
||||
|
||||
2.2.1. copy and distribute (or have copied and distributed) the Ultraleap Redistributables,
|
||||
solely as compiled with, incorporated into, or packaged with, your Ultraleap Enabled
|
||||
Application; and
|
||||
|
||||
2.2.2. to make (but not have made), use, sell, offer for sale, and import your Ultraleap Enabled
|
||||
Application.
|
||||
|
||||
3. Restrictions
|
||||
|
||||
3.1. The license granted to you in section 2.1 and section 2.2 is subject to the following restrictions,
|
||||
as well as others listed in this Agreement:
|
||||
|
||||
3.1.1. Except as expressly permitted in section 2.1, (a) you may not publish, distribute, or copy
|
||||
the SDK, and (b) you may not modify or create derivative works of the SDK;
|
||||
|
||||
3.1.2. Except as expressly permitted in section 2.2, you may not, and may not allow any third
|
||||
party, directly or indirectly, to publish, post, or otherwise make available, the Ultraleap
|
||||
Redistributables;
|
||||
|
||||
3.1.3. You may not, and may not enable others to, distributed the Non-Redistributable
|
||||
Materials;
|
||||
|
||||
3.1.4. You may use the SDK solely in connection with Ultraleap Hardware and/or Ultraleap
|
||||
Software;
|
||||
|
||||
3.1.5. You may not use the SDK to create, or aid in the creation, directly or indirectly, of any
|
||||
software or hardware which provides hand tracking functionality or which is otherwise
|
||||
substantially similar to the features or functionality of Ultraleap products;
|
||||
|
||||
3.1.6. You may not, and may not enable others to, directly or indirectly, reverse engineer,
|
||||
decompile, disassemble, or otherwise attempt to reconstruct, identify, or discover any
|
||||
source code, underlying ideas, techniques, or algorithms in the Ultraleap Software, the
|
||||
Ultraleap Hardware, or any software which forms part of the SDK, nor attempt to
|
||||
circumvent any related security measures (except as and only to the extent any
|
||||
foregoing restriction is prohibited by applicable law notwithstanding the foregoing
|
||||
restriction, or to the extent as may be permitted by licensing terms governing the use of
|
||||
any open source software components or sample code contained within the SDK;
|
||||
|
||||
3.1.7. You may not remove, obscure, or alter any proprietary rights or confidentiality notices
|
||||
within the SDK or any software, documentation, or other materials in it or supplied with
|
||||
it;
|
||||
|
||||
3.1.8. You must not allow the Ultraleap Software or SDK to fall under the terms of any license
|
||||
which would obligate you or Ultraleap to make available or publish any part of the
|
||||
Ultraleap Software or SDK.
|
||||
|
||||
3.1.9. You may not create Ultraleap Enabled Applications or other software that prevent or
|
||||
degrade the interaction of applications developed by others with the Ultraleap Software;
|
||||
|
||||
3.1.10. You may not represent functionality provided by any Ultraleap hardware or software as
|
||||
your technology or the technology of any third party. For example (without limitation)
|
||||
you may not describe any application, technology, or feature developed or distributed
|
||||
by you that incorporates Ultraleap technology as your gesture or touchless control
|
||||
technology without providing attribution to Ultraleap; and
|
||||
|
||||
3.1.11. You may not allow your Ultraleap Enabled Application to be used for a High Risk Use.
|
||||
|
||||
4. Updates
|
||||
|
||||
4.1. The terms of this Agreement will apply to any Updates which Ultraleap (in its sole discretion)
|
||||
makes available to you. You agree that Updates may require you to change or update your
|
||||
Ultraleap Enabled Application, and may affect your ability to use, access, or interact with the
|
||||
Ultraleap Software, the Ultraleap Hardware, and/or the SDK. You are solely responsible for
|
||||
turning off any auto-update functionality of the Ultraleap Software.
|
||||
|
||||
5. Trademarks and Marketing
|
||||
|
||||
5.1. Conditioned upon compliance with the terms and conditions of this Agreement, Ultraleap grants
|
||||
you a limited, non-exclusive, personal, license to reproduce and use Ultraleap trademarks solely
|
||||
to (a) mark the Ultraleap Enabled Application, (b) produce and make available related collateral,
|
||||
and (c) to promote and market your Ultraleap Enabled Application, in each case solely in
|
||||
accordance with the Ultraleap trademark guidelines that Ultraleap may provide to you from time
|
||||
to time.
|
||||
|
||||
5.2. For so long as Ultraleap technology is included with the Ultraleap Enabled Application, you must
|
||||
identify on the packaging of the Ultraleap Enabled Application, the loading screen and start-up
|
||||
messages for the Ultraleap Enabled Application, and list on your website and marketing collateral
|
||||
(in each case, where applicable), as prominently as other listed features and functionality, that
|
||||
Ultraleap technology is included with the Ultraleap Enabled Application, in accordance with the
|
||||
Ultraleap trademark guidelines that Ultraleap may provide to you from time to time. All
|
||||
references to Ultraleap or Ultraleap Technology will be subject to Ultraleap’s prior approval,
|
||||
which will not be unreasonably withheld.
|
||||
|
||||
5.3. Ultraleap may at its option mention you and your products using Ultraleap technology in
|
||||
Ultraleap’s press releases, press briefings, social media accounts, and/or website, and may use
|
||||
your trademarks for such purpose. You grant to Ultraleap and its affiliates a non-exclusive,
|
||||
worldwide and royalty-free limited license to use, reproduce, display, perform, publish and
|
||||
distribute screenshots, elements, assets, photographic, graphic or video reproductions or
|
||||
fragments of your Ultraleap Enabled Application in any medium or media, solely for purposes of
|
||||
promotion of your Ultraleap Enabled Application or of Ultraleap and its technology and business.
|
||||
The rights set out in this section 5.3 will survive termination of this Agreement in respect of
|
||||
materials already in existence as at the date of termination.
|
||||
|
||||
6. EULA and Other Licenses
|
||||
|
||||
6.1. Example code made publicly available by Ultraleap on its developer web site may be provided
|
||||
subject to the Apache 2.0 license, this Agreement, or other licenses, as specified in the notice or
|
||||
readme files distributed with the example or in related documentation. The SDK may otherwise
|
||||
include software or other materials that are provided under a separate license agreement, and
|
||||
that separate license will govern the use of such software or other materials in the event of a
|
||||
conflict with this Agreement. Any such separate license agreement may be indicated in the
|
||||
license, notice, or readme files distributed with the applicable software or other materials or in
|
||||
related documentation.
|
||||
|
||||
6.2. You must either require end users of your Ultraleap Enabled Application to affirmatively agree to
|
||||
the Ultraleap EULA, or require its End Users to affirmatively agree to your own end user license
|
||||
agreement that protects Ultraleap at least as much as the Ultraleap EULA.
|
||||
|
||||
7. High Risk Uses and Waiver
|
||||
|
||||
7.1. Notwithstanding anything in this Agreement, you are not licensed to, and you agree not to, use,
|
||||
copy, sell, offer for sale, or distribute the SDK, Ultraleap Hardware, Ultraleap Software or
|
||||
Ultraleap Redistributables (whether compiled with, incorporated into, or packaged with your
|
||||
Ultraleap Enabled Application or otherwise), for or in connection with uses where failure or fault
|
||||
of the Ultraleap Hardware, Ultraleap Software, Ultraleap Redistributables or your Ultraleap
|
||||
Enabled Application could lead to death or serious bodily injury of any person, or to severe
|
||||
physical or environmental damage (“High Risk Use”). Any such use is strictly prohibited.
|
||||
|
||||
7.2. You acknowledge the SDK may allow you to develop Ultraleap Enabled Applications that enable
|
||||
the control of motorized or mechanical equipment, or other systems, machines or devices. If you
|
||||
elect to use the SDK in such a way, you must take steps to design and test your Ultraleap Enabled
|
||||
Applications to ensure that your Ultraleap Enabled Applications do not present risks of personal
|
||||
injury or death, property damage, or other losses. The Ultraleap Hardware, the Ultraleap
|
||||
Software, the Ultraleap Redistributables and other software in the SDK may not always function
|
||||
as intended. You must design your Ultraleap Enabled Applications so that any failure of Ultraleap
|
||||
Technology and/or such other software as Ultraleap may make available from time to time does
|
||||
not cause personal injury or death, property damage, or other losses. If you choose to use the
|
||||
SDK, (i) you assume all risk that use of the Ultraleap Technology and/or such other software by
|
||||
you or by any others causes any harm or loss, including to the end users of your Ultraleap
|
||||
Enabled Applications or to third parties, (ii) you hereby waive, on behalf of yourself and your
|
||||
Authorized Users, all claims against Ultraleap and its affiliates related to such use, harm or loss
|
||||
(including, but not limited to, any claim that Ultraleap Technology or such other software is
|
||||
defective), and (iii) you agree to hold Ultraleap and its affiliates harmless from such claims.
|
||||
|
||||
8. Confidentiality and Data Protection
|
||||
|
||||
8.1. Beta Software etc. Obligations. You acknowledge and agree that Ultraleap may share alpha or
|
||||
beta software or hardware with you that it identifies as non-public. If so, you agree not to
|
||||
disclose such software or hardware to others without the prior written consent of Ultraleap
|
||||
until the time, if any, it is made public by Ultraleap, and to use such software or hardware only
|
||||
as expressly permitted by Ultraleap. Without limitation to the foregoing, the distribution license
|
||||
set out in section 2.2 shall not apply to any alpha or beta software which may be shared with
|
||||
you.
|
||||
|
||||
8.2. Your Information. Ultraleap may collect personal information provided by you or your
|
||||
Authorized Users to Ultraleap or any group company of Ultraleap in connection with the SDK,
|
||||
and may collect other information from you or your Authorized Users, including technical, non-
|
||||
personally identifiable and/or aggregated information such as usage statistics, hardware
|
||||
configuration, problem / fault data, IP addresses, version number of the SDK, information about
|
||||
which tools and/or services in the SDK are being used and how they are being used, and any
|
||||
other information described in Ultraleap’s privacy policy, currently available at
|
||||
https://www.ultraleap.com/privacy-policy/. Ultraleap may use the information collected to
|
||||
facilitate the provision of Updates and other services to you, to verify compliance with, and
|
||||
enforce, the terms of this Agreement, to improve the SDK and Ultraleap’s other products, and
|
||||
for any other purposes set out in Ultraleap’s privacy policy (these uses, collectively, are
|
||||
“Permitted Uses”). The information collected may be transferred to, stored, and processed in a
|
||||
destination outside the European Economic Area, including (without limitation) by our staff in
|
||||
the USA, China, Japan, and Hong Kong. By submitting information about you and/or your
|
||||
Authorized Users to Ultraleap through your access and use of the SDK, you consent to
|
||||
Ultraleap’s collection and use of the information for the Permitted Uses and represent that you
|
||||
have obtained all consents and permits necessary under applicable law to disclose your
|
||||
Authorized Users’ information to Ultraleap for the Permitted Uses. You further agree that
|
||||
Ultraleap may provide any information collected under this Section 8.2, including your or your
|
||||
Authorized Users’ user name, IP address or other identifying information to law enforcement
|
||||
authorities or as required by applicable law or regulation.
|
||||
|
||||
9. Ownership and Feedback
|
||||
|
||||
9.1. As between you and Ultraleap, Ultraleap owns all right, title, and interest, including all
|
||||
intellectual property rights, in and to the SDK, the Ultraleap Software, Ultraleap Hardware, the
|
||||
Ultraleap Redistributables, and all documentation associated with the foregoing, other than any
|
||||
third party software or materials incorporated into the SDK. You agree not to contest Ultraleap’s
|
||||
ownership of any of the foregoing.
|
||||
|
||||
9.2. Subject to Section 9.1, Ultraleap agrees that it obtains no right, title, or interest from you (or
|
||||
your licensors) under this Agreement in or to your Ultraleap Enabled Applications, including any
|
||||
intellectual property rights which subsist in those Ultraleap Enabled Applications.
|
||||
|
||||
9.3. Feedback. You may (but are not required to) provide feedback, comments, and suggestions
|
||||
(collectively “Feedback”) to Ultraleap. You hereby grant to Ultraleap a non-exclusive, perpetual,
|
||||
irrevocable, paid-up, transferrable, sub-licensable, worldwide license under all intellectual
|
||||
property rights covering such Feedback to use, disclose, and exploit all such Feedback for any
|
||||
purpose.
|
||||
|
||||
10. Your Obligations and Warranties
|
||||
|
||||
In addition to your other obligations under this Agreement, you warrant and agree that:
|
||||
|
||||
10.1. you are at least 18 years of age and have the right and authority to enter into this Agreement on
|
||||
your own behalf and that of your Authorized Users. If you are entering into this Agreement on
|
||||
behalf of your company or organization, you warrant that you have the right and authority to
|
||||
legally bind your company or organization and its Authorized Users;
|
||||
|
||||
10.2. you will use the SDK only in accordance with all accompanying documentation, and in the
|
||||
manner expressly permitted by this Agreement; and
|
||||
|
||||
10.3. your use of the SDK, and the marketing, sales and distribution of your Ultraleap Enabled
|
||||
Application, will be in compliance with all applicable laws and regulations and all UK, U.S. and
|
||||
local or foreign export and re-export restrictions applicable to the technology and
|
||||
documentation provided under this Agreement (including privacy and data security laws and
|
||||
regulations), and you will not develop any Ultraleap Enabled Application which would commit or
|
||||
facilitate the commission of a crime, or other tortious, unlawful, or illegal act.
|
||||
|
||||
11. Agreement and Development Program
|
||||
|
||||
11.1. We reserve the right to change this Agreement, the SDK or the Ultraleap development and
|
||||
licensing program at any time in our discretion. Ultraleap may require that you either accept
|
||||
and agree to the new terms of this Agreement, or, if you do not agree to the new terms, cease
|
||||
or terminate your use of the SDK. Your continued use of the SDK after changes to this
|
||||
Agreement take effect will constitute your acceptance of the changes. If you do not agree to a
|
||||
change, you must stop using the SDK and terminate this Agreement. Any termination of this
|
||||
Agreement by you under this Section 11 (and only this Section 11) will not affect your right,
|
||||
subject to your continued compliance with your obligations under this Agreement, to continue
|
||||
to distribute versions of your Ultraleap Enabled Application created and first distributed before
|
||||
termination, and will not affect the right of your End Users to continue using such versions of
|
||||
your Ultraleap Enabled Application, both of which rights will survive termination.
|
||||
|
||||
12. Term and Termination
|
||||
|
||||
12.1. Term. This Agreement will continue to apply until terminated by either you or Ultraleap as set
|
||||
out below.
|
||||
|
||||
12.2. Termination by You. If you want to terminate this Agreement, you may terminate it by
|
||||
uninstalling and destroying all copies of the SDK that are in the possession, custody or control of
|
||||
you, your Authorized Users and your organization.
|
||||
|
||||
12.3. Termination by Ultraleap. Ultraleap may at any time, terminate this Agreement with you for
|
||||
any reason or for no reason in Ultraleap’s sole discretion, including as a result of non-
|
||||
compliance by you with the restrictions in in this Agreement, or for other reasons.
|
||||
|
||||
12.4. Effect of Termination. Upon termination of this Agreement, all rights granted to you under this
|
||||
Agreement will immediately terminate and you must immediately cease all use and destroy all
|
||||
copies of the SDK in your and your Authorized Users’ possession, custody or control, and, except
|
||||
as specifically set out in Section 11, cease your distribution of Ultraleap Enabled Applications.
|
||||
Sections 3, 8.1, 8.2, 9, 12.4, 14-16, and 17, will survive termination of this Agreement.
|
||||
Termination of this Agreement will not affect the right of your End Users who have downloaded
|
||||
your Ultraleap Enabled Application prior to termination to continue using it.
|
||||
|
||||
13. Indemnification.
|
||||
|
||||
13.1. You agree to indemnify, hold harmless and, at Ultraleap’s option, defend Ultraleap and its
|
||||
affiliates and their respective officers, directors, employees, agents, and representatives
|
||||
harmless from any and all judgments, awards, settlements, liabilities, damages, costs, penalties,
|
||||
fines and other expenses (including court costs and reasonable attorneys’ fees) incurred by
|
||||
them arising out of or relating to any third party claim (a) with respect to your Ultraleap Enabled
|
||||
Application, including products liability, privacy, or intellectual property infringement claims, or
|
||||
(b) based upon your negligence or wilful misconduct or any breach or alleged breach of your
|
||||
representations, warranties, and covenants under this Agreement. In no event may you enter
|
||||
into any settlement or like agreement with a third party that affects Ultraleap’s rights or binds
|
||||
Ultraleap or its affiliates in any way, without the prior written consent of Ultraleap.
|
||||
|
||||
14. Warranty Disclaimer.
|
||||
|
||||
14.1. THE SDK, THE ULTRALEAP SOFTWARE AND THE ULTRALEAP REDISTRIBUTABLES ARE PROVIDED
|
||||
"AS IS" WITHOUT WARRANTY OF ANY KIND. ULTRALEAP, ON BEHALF OF ITSELF AND ITS
|
||||
SUPPLIERS, HEREBY DISCLAIMS ALL REPRESENTATIONS, PROMISES, OR WARRANTIES, WHETHER
|
||||
EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SDK, THE ULTRALEAP
|
||||
SOFTWARE AND THE ULTRALEAP REDISTRIBUTABLES, INCLUDING THEIR CONDITION,
|
||||
AVAILABILITY, OR THE EXISTENCE OF ANY LATENT DEFECTS, AND ULTRALEAP SPECIFICALLY
|
||||
DISCLAIMS ALL IMPLIED WARRANTIES OF MERCHANTABILITY, TITLE, NONINFRINGEMENT,
|
||||
SUITABILITY, AND FITNESS FOR ANY PURPOSE. ULTRALEAP DOES NOT WARRANT THAT THE SDK,
|
||||
THE ULTRALEAP SOFTWARE OR THE ULTALEAP REDISTRIBUTABLES WILL BE ERROR-FREE OR
|
||||
THAT THEY WILL WORK WITHOUT INTERRUPTION.
|
||||
|
||||
15. Limitation of Liability.
|
||||
|
||||
15.1. ULTRALEAP SHALL NOT IN ANY CIRCUMSTANCES WHATEVER BE LIABLE TO YOU, WHETHER IN
|
||||
CONTRACT, TORT (INCLUDING NEGLIGENCE), BREACH OF STATUTORY DUTY, OR OTHERWISE,
|
||||
ARISING UNDER OR IN CONNECTION WITH THE AGREEMENT FOR:
|
||||
|
||||
15.1.1. LOSS OF PROFITS, SALES, BUSINESS, OR REVENUE;
|
||||
|
||||
15.1.2. BUSINESS INTERRUPTION;
|
||||
|
||||
15.1.3. LOSS OF ANTICIPATED SAVINGS;
|
||||
|
||||
15.1.4. LOSS OR CORRUPTION OF DATA OR INFORMATION;
|
||||
|
||||
15.1.5. LOSS OF BUSINESS OPPORTUNITY, GOODWILL OR REPUTATION; OR
|
||||
|
||||
15.1.6. ANY INDIRECT OR CONSEQUENTIAL LOSS OR DAMAGE.
|
||||
|
||||
15.2. OTHER THAN THE LOSSES SET OUT ABOVE (FOR WHICH ULTRALEAP IS NOT LIABLE),
|
||||
ULTRALEAP’S MAXIMUM AGGREGATE LIABILITY UNDER OR IN CONNECTION WITH THE
|
||||
AGREEMENT WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE), BREACH OF STATUTORY
|
||||
DUTY, OR OTHERWISE, SHALL IN ALL CIRCUMSTANCES BE LIMITED TO $1,000 (ONE THOUSAND
|
||||
US DOLLARS). THIS MAXIMUM CAP DOES NOT APPLY TO DEATH OR PERSONAL INJURY
|
||||
RESULTING FROM ULTRALEAP'S NEGLIGENCE; FRAUD OR FRAUDULENT MISREPRESENTATION;
|
||||
OR ANY OTHER LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED BY APPLICABLE LAW.
|
||||
|
||||
15.3. THE AGREEMENT SETS OUT THE FULL EXTENT OF ULTRALEAP’S OBLIGATIONS AND LIABILITIES IN
|
||||
RESPECT OF THE SUPPLY OF THE ULTRALEAP DEVICES, DELIVERABLES AND SOFTWARE. EXCEPT
|
||||
AS EXPRESSLY STATED IN THE AGREEMENT, THERE ARE NO CONDITIONS, WARRANTIES,
|
||||
REPRESENTATIONS OR OTHER TERMS, EXPRESS OR IMPLIED, THAT ARE BINDING ON
|
||||
ULTRALEAP. ANY CONDITION, WARRANTY, REPRESENTATION OR OTHER TERM CONCERNING
|
||||
THE SUPPLY OF THE ULTRALEAP HARDWARE, ULTRALEAP SOFTWARE, THE SDK, THE ULTRALEAP
|
||||
REDISTRIBUTABLES, OR ANY OTHER ULTRALEAP TECHNOLOGY WHICH MIGHT OTHERWISE BE
|
||||
IMPLIED INTO, OR INCORPORATED IN THE AGREEMENT WHETHER BY STATUTE, COMMON LAW
|
||||
OR OTHERWISE, INCLUDING ANY WARRANTY OR CONDITION OF MERCHANTABILITY OR FITNESS
|
||||
FOR A PARTICULAR PURPOSE, IS EXCLUDED TO THE FULLEST EXTENT PERMITTED BY LAW. THESE
|
||||
LIMITATIONS WILL APPLY NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY
|
||||
LIMITED REMEDY. THE PARTIES AGREE THAT THE FOREGOING LIMITATIONS REPRESENT A
|
||||
REASONABLE ALLOCATION OF RISK UNDER THIS AGREEMENT.
|
||||
|
||||
16. Miscellaneous.
|
||||
|
||||
16.1. Assignment. You may not assign this Agreement without the prior written consent of Ultraleap.
|
||||
Any assignment without such consent is void and of no effect. Ultraleap may assign this
|
||||
Agreement without your consent in connection with (a) a merger or consolidation of Ultraleap,
|
||||
(b) a sale or assignment of substantially all its assets, or (c) any other transaction which results
|
||||
in another entity or person owning substantially all of the assets of Ultraleap, or (d) to any of its
|
||||
affiliates. In the event of a permitted assignment, this Agreement will inure to the benefit of and
|
||||
be binding upon the parties and their respective successors and permitted assigns.
|
||||
|
||||
16.2. Waiver; Severability. The failure of the other party to enforce any rights under this Agreement
|
||||
will not be deemed a waiver of any rights. The rights and remedies of the parties in this
|
||||
Agreement are not exclusive and are in addition to any other rights and remedies provided by
|
||||
law. If any provision of this Agreement is held by a court of competent jurisdiction to be
|
||||
contrary to law, the remaining provisions of this Agreement will remain in full force and effect.
|
||||
|
||||
16.3. Reservation. All licenses not expressly granted in this Agreement are reserved and no other
|
||||
licenses, immunity or rights, express or implied, are granted by Ultraleap, by implication,
|
||||
estoppel, or otherwise. The software in the SDK is licensed, not sold.
|
||||
|
||||
16.4. Export Restrictions. The Ultraleap Software is subject to United States and UK export laws and
|
||||
regulations. You must comply with all domestic and international export laws and regulations
|
||||
that apply to the Ultraleap Software. These laws include restrictions on destinations, end users,
|
||||
and end use.
|
||||
|
||||
16.5. Governing Law and Jurisdiction. This Agreement will be exclusively governed by and construed
|
||||
under the laws of the England and Wales, without reference to or application of rules governing
|
||||
choice of laws. All disputes arising out of or related to this Agreement will be subject to the
|
||||
exclusive jurisdiction of courts of England and you hereby consent to such jurisdiction. However,
|
||||
Ultraleap may apply to any court or tribunal worldwide, including but not limited to those
|
||||
having jurisdiction over you or your Authorized Users, to seek injunctive relief.
|
||||
|
||||
16.6. Relationship of the Parties. This Agreement does not create any agency, partnership, or joint
|
||||
venture relationship between Ultraleap and you. This Agreement is for the sole benefit of
|
||||
Ultraleap and you (and indemnified parties), and no other persons will have any right or remedy
|
||||
under this Agreement.
|
||||
|
||||
16.7. Notices. The address for notice to Ultraleap under this Agreement is:
|
||||
Ultraleap Limited
|
||||
The West Wing
|
||||
Glass Wharf
|
||||
Bristol, BS2 0EL
|
||||
United Kingdom
|
||||
Ultraleap may provide you notice under this Agreement by email or other electronic
|
||||
communication or by posting communications to its development community on the Ultraleap
|
||||
developer portal. You consent to receive such notices in any of the foregoing manners and
|
||||
agree that any such notices by Ultraleap will satisfy any legal communication requirements.
|
||||
16.8. Entire Agreement. This Agreement is the entire understanding of the parties with respect to its
|
||||
subject matter and supersedes any previous or contemporaneous communications, whether
|
||||
oral or written with respect to such subject matter.
|
||||
|
||||
17. Definitions
|
||||
|
||||
Whenever capitalized in this Agreement:
|
||||
“Authorized Users” means your employees and contractors, members of your organization or, if you
|
||||
are an educational institution, your faculty, staff and registered students, who (a) have a
|
||||
demonstrable need to know or use the SDK in order to develop and test Ultraleap Enabled
|
||||
Applications on your behalf and (b) each have written and binding agreements with you to protect
|
||||
against the unauthorized use and disclosure of the SDK consistent with the terms and conditions of
|
||||
this Agreement. Authorized Users do not include End Users.
|
||||
“End User” means your end user customer(s) or licensee(s).
|
||||
“Non-Redistributable Materials” means the Ultraleap Software, and any other code, files or
|
||||
materials that are not specifically designated in the SDK as made available for incorporation into
|
||||
Ultraleap Enabled Applications or that are specifically designated in the SDK as not subject to
|
||||
distribution.
|
||||
“SDK” means, collectively, the Ultraleap Redistributables, tools, APIs, sample code, software,
|
||||
documentation, other materials and any updates to the foregoing that may be provided or made
|
||||
available to you by Ultraleap in connection with this Agreement, via the Ultraleap developer portal or
|
||||
otherwise for use in connection with the Ultraleap development program to develop Ultraleap
|
||||
Enabled Applications.
|
||||
“Specialized Application” means an Ultraleap Enabled Application which does not fall within the
|
||||
permitted uses set out in this Agreement.
|
||||
“Ultraleap” “we” or “us” means Ultraleap Limited, a company registered in England with company
|
||||
number 08781720, with a principal place of business at The West Wing, Glass Wharf, Bristol, BS2 0EL,
|
||||
United Kingdom.
|
||||
“Ultraleap Hardware” means the Leap Motion Controller, Stereo IR 170, Stereo IR 170 EK or Ultraleap 3Di each being a device that
|
||||
detects and reads movements within a 3-D interaction space to precisely interact with and control
|
||||
software on a computing device, or an Ultraleap-authorized embedded optical module.
|
||||
“Ultraleap Redistributables” means any .lib code, .dll files, .so files, sample code, or other materials
|
||||
we specifically designate in the SDK as made available for incorporation into or distribution with
|
||||
Ultraleap Enabled Applications.
|
||||
“Ultraleap Software” means the Ultraleap core services application and related applications that
|
||||
interact with Ultraleap Hardware and an operating system to make motion control functionality
|
||||
available to Ultraleap Enabled Applications, and includes any Updates thereto.
|
||||
“Updates” means updates, upgrades, modifications, enhancements, revisions, new releases or new
|
||||
versions to the SDK that Ultraleap may make available to you in connection with this Agreement.
|
||||
Other capitalized terms used in this Agreement have the meaning given them elsewhere in this
|
||||
Agreement.
|
||||
|
||||
18. Supplemental Terms Applicable to the Use of Image API
|
||||
|
||||
18.1. Purpose. You and/or your Ultraleap Enabled Application may access the Image API and use
|
||||
image data available through the Image API only for the purpose of developing and testing
|
||||
Ultraleap Enabled Applications, and only for use with Ultraleap Hardware. You may not use the
|
||||
Image API to develop or aid development of competing motion tracking hardware or software.
|
||||
Any use of the Image API is subject to the terms of the Agreement.
|
||||
|
||||
18.2. Data Protection.
|
||||
|
||||
18.2.1. If you or your Ultraleap Enabled Application collects, uploads, stores, transmits, or
|
||||
shares images, videos, or other personal information available through the Image API,
|
||||
either through or in connection with your Ultraleap Enabled Application, you must
|
||||
expressly provide users with your privacy policy and adhere to it.
|
||||
|
||||
18.2.2. You must obtain specific, opt-in consent from the user for any use that is beyond the
|
||||
limited and express purpose of your Ultraleap Enabled Application.
|
||||
|
||||
18.2.3. You and your Ultraleap Enabled Application must use and store information collected
|
||||
form users securely and only for as long as it is required.
|
||||
|
||||
18.2.4. You agree that you will protect the privacy and legal rights of users, and will comply with
|
||||
all applicable criminal, civil, and statutory privacy and data protection laws and
|
||||
regulations.
|
||||
|
|
296
ml_lme/vendor/LeapSDK/README.md
vendored
296
ml_lme/vendor/LeapSDK/README.md
vendored
|
@ -1,137 +1,159 @@
|
|||
# Ultraleap SDK
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## Package contents:
|
||||
|
||||
LeapSDK
|
||||
- include
|
||||
* API headers.
|
||||
- lib
|
||||
* dynamic API library and CMake scripts.
|
||||
- samples
|
||||
* Various samples demonstrating several different usages.
|
||||
- LICENSE.md
|
||||
* Ultraleap Tracking SDK license.
|
||||
- README.md
|
||||
* Ultraleap Tracking SDK readme.
|
||||
- ThirdPartyNotices.md
|
||||
* Ultraleap Tracking SDK third party licenses.
|
||||
|
||||
## Requirements:
|
||||
|
||||
1. Running requires
|
||||
* Ultraleap Tracking Software https://developer.leapmotion.com/get-started/
|
||||
|
||||
2. Building Samples requires
|
||||
* CMake 3.16.3+ (https://cmake.org/)
|
||||
* Microsoft Visual Studio 15+ (Windows)
|
||||
* GCC (Linux - tested on v9.4.0)
|
||||
|
||||
## Installation:
|
||||
|
||||
The LeapSDK is installed with Ultraleap Tracking.
|
||||
|
||||
## Usage:
|
||||
|
||||
1. For CMake projects
|
||||
* Ensure LeapSDK is in a directory considered as a prefix by find_package.
|
||||
(https://cmake.org/cmake/help/v3.16/command/find_package.html)
|
||||
* Or : directly set LeapSDK_DIR to <install_dir>/LeapSDK/lib/cmake/LeapSDK
|
||||
* Or : Pass the LeapSDK's path to find_package with the PATHS option.
|
||||
* call find_package(LeapSDK 5 [PATHS ...]).
|
||||
* call target_link_libraries(<your project> PUBLIC|PRIVATE LeapSDK::LeapC).
|
||||
* Ensure LeapC.dll/LeapC.so is in your dynamic library search path.
|
||||
* A popular option is to add a post-build step that copies it to your project's output directory.
|
||||
|
||||
2. For non-CMake projects
|
||||
* Use a C/C++ compiler such as MSVC, Clang or GCC.
|
||||
* Add LeapSDK/include to the compiler include search paths.
|
||||
* Either add a linker reference to LeapC.lib or dynamically load LeapC.dll/LeapC.so.
|
||||
|
||||
## Building Samples:
|
||||
|
||||
### Windows
|
||||
|
||||
1. Open CMake using LeapSDK/samples as the source directory
|
||||
|
||||
2. Select a build directory (often LeapSDK/samples/build) to use
|
||||
|
||||
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 :
|
||||
```powershell
|
||||
$env:BUILD_TYPE = 'Release'
|
||||
$env:REPOS_BUILD_ROOT = 'C:/build'
|
||||
$env:REPOS_INSTALL_ROOT = 'C:/Program Files'
|
||||
|
||||
cmake -S "C:/Program Files/Ultraleap/LeapSDK/samples" -B $env:REPOS_BUILD_ROOT/$env:BUILD_TYPE/LeapSDK/leapc_example `
|
||||
-DCMAKE_INSTALL_PREFIX="$env:REPOS_INSTALL_ROOT/leapc_example" `
|
||||
-DCMAKE_BUILD_TYPE="$env:BUILD_TYPE"
|
||||
|
||||
cmake --build $env:REPOS_BUILD_ROOT/$env:BUILD_TYPE/LeapSDK/leapc_example -j --config $env:BUILD_TYPE
|
||||
```
|
||||
|
||||
### x64 Linux
|
||||
|
||||
1. Open CMake using /usr/share/doc/ultraleap-hand-tracking-service/samples as the source directory
|
||||
|
||||
2. Select a build directory (eg. ~/ultraleap-tracking-samples/build) to use
|
||||
|
||||
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=/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}"
|
||||
|
||||
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
|
||||
|
||||
2. Select a build directory (eg. ~/ultraleap-tracking-samples/build) to use
|
||||
|
||||
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='/Library/Application Support/Ultraleap/LeapSDK/samples'
|
||||
BUILD_TYPE='Release'
|
||||
REPOS_BUILD_ROOT=~/ultraleap-tracking-samples/build
|
||||
REPOS_INSTALL_ROOT=~/ultraleap-tracking-samples/bin
|
||||
|
||||
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}"
|
||||
|
||||
cmake --build ${REPOS_BUILD_ROOT}/${BUILD_TYPE}/LeapSDK/leapc_example -j --config ${BUILD_TYPE}
|
||||
```
|
||||
|
||||
## Resources:
|
||||
|
||||
1. Ultraleap For Developers Site (https://developer.leapmotion.com)
|
||||
provides examples, community forums, Ultraleap news, and documentation
|
||||
to help you to learn how to develop applications using the Ultraleap Tracking
|
||||
SDK.
|
||||
|
||||
2. C# and Unity bindings (https://github.com/leapmotion/UnityModules)
|
||||
|
||||
3. C++ bindings matching the old API (https://github.com/leapmotion/LeapCxx)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012-2020 Ultraleap Ltd. All rights reserved.
|
||||
|
||||
Use subject to the terms of the Ultraleap Tracking SDK Agreement `LICENSE.md` next to this `README.md` file.
|
||||
# Ultraleap SDK
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## Package contents:
|
||||
|
||||
LeapSDK
|
||||
- include
|
||||
* API headers.
|
||||
- lib
|
||||
* dynamic API library and CMake scripts.
|
||||
- samples
|
||||
* Various samples demonstrating several different usages.
|
||||
- LICENSE.md
|
||||
* Ultraleap Tracking SDK license.
|
||||
- README.md
|
||||
* Ultraleap Tracking SDK readme.
|
||||
- ThirdPartyNotices.md
|
||||
* Ultraleap Tracking SDK third party licenses.
|
||||
|
||||
## Requirements:
|
||||
|
||||
1. Running requires
|
||||
* Ultraleap Tracking Software https://developer.leapmotion.com/get-started/
|
||||
|
||||
2. Building Samples requires
|
||||
* CMake 3.16.3+ (https://cmake.org/)
|
||||
* Microsoft Visual Studio 15+ (Windows)
|
||||
* GCC (Linux - tested on v9.4.0)
|
||||
|
||||
## Installation:
|
||||
|
||||
The LeapSDK is installed with Ultraleap Tracking.
|
||||
|
||||
## Usage:
|
||||
|
||||
1. For CMake projects
|
||||
* Ensure LeapSDK is in a directory considered as a prefix by find_package.
|
||||
(https://cmake.org/cmake/help/v3.16/command/find_package.html)
|
||||
* Or : directly set LeapSDK_DIR to <install_dir>/LeapSDK/lib/cmake/LeapSDK
|
||||
* Or : Pass the LeapSDK's path to find_package with the PATHS option.
|
||||
* call find_package(LeapSDK 5 [PATHS ...]).
|
||||
* call target_link_libraries(<your project> PUBLIC|PRIVATE LeapSDK::LeapC).
|
||||
* Ensure LeapC.dll/LeapC.so is in your dynamic library search path.
|
||||
* A popular option is to add a post-build step that copies it to your project's output directory.
|
||||
|
||||
2. For non-CMake projects
|
||||
* Use a C/C++ compiler such as MSVC, Clang or GCC.
|
||||
* Add LeapSDK/include to the compiler include search paths.
|
||||
* Either add a linker reference to LeapC.lib or dynamically load LeapC.dll/LeapC.so.
|
||||
|
||||
## Building Samples:
|
||||
|
||||
### Windows
|
||||
|
||||
1. Open CMake using LeapSDK/samples as the source directory
|
||||
|
||||
2. Select a build directory (often LeapSDK/samples/build) to use
|
||||
|
||||
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 :
|
||||
```powershell
|
||||
$env:BUILD_TYPE = 'Release'
|
||||
$env:REPOS_BUILD_ROOT = 'C:/build'
|
||||
$env:REPOS_INSTALL_ROOT = 'C:/Program Files'
|
||||
|
||||
cmake -S "C:/Program Files/Ultraleap/LeapSDK/samples" -B $env:REPOS_BUILD_ROOT/$env:BUILD_TYPE/LeapSDK/leapc_example `
|
||||
-DCMAKE_INSTALL_PREFIX="$env:REPOS_INSTALL_ROOT/leapc_example" `
|
||||
-DCMAKE_BUILD_TYPE="$env:BUILD_TYPE"
|
||||
|
||||
cmake --build $env:REPOS_BUILD_ROOT/$env:BUILD_TYPE/LeapSDK/leapc_example -j --config $env:BUILD_TYPE
|
||||
```
|
||||
|
||||
### x64 Linux
|
||||
|
||||
1. Open CMake using /usr/share/doc/ultraleap-hand-tracking-service/samples as the source directory
|
||||
|
||||
2. Select a build directory (eg. ~/ultraleap-tracking-samples/build) to use
|
||||
|
||||
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=/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}"
|
||||
|
||||
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 /Applications/Ultraleap\ Hand\ Tracking.app/Contents/LeapSDK/samples as the source directory
|
||||
|
||||
2. Select a build directory (eg. ~/ultraleap-tracking-samples/build) to use
|
||||
|
||||
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='/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
|
||||
|
||||
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}"
|
||||
|
||||
cmake --build ${REPOS_BUILD_ROOT}/${BUILD_TYPE}/LeapSDK/leapc_example -j --config ${BUILD_TYPE}
|
||||
```
|
||||
|
||||
## Resources:
|
||||
|
||||
1. Ultraleap For Developers Site (https://developer.leapmotion.com)
|
||||
provides examples, community forums, Ultraleap news, and documentation
|
||||
to help you to learn how to develop applications using the Ultraleap Tracking
|
||||
SDK.
|
||||
|
||||
2. C# and Unity bindings (https://github.com/leapmotion/UnityModules)
|
||||
|
||||
3. C++ bindings matching the old API (https://github.com/leapmotion/LeapCxx)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Copyright © 2012-2020 Ultraleap Ltd. All rights reserved.
|
||||
|
||||
Use subject to the terms of the Ultraleap Tracking SDK Agreement `LICENSE.md` next to this `README.md` file.
|
||||
|
|
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