Skip to content

Commit

Permalink
feat(ik)!: add ik support (#133)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Mod folder structure has changed. Please remove the old mod entirely before installing this version.
  • Loading branch information
Xenira committed Mar 10, 2024
1 parent 5133d65 commit f028a00
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 20 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ jobs:
- name: Copy Files
run: |
mkdir -p ./artifact/BepInEx/plugins/techtonica_vr/assets && mkdir -p ./artifact/Techtonica_Data
cp techtonica_vr.* ./artifact/BepInEx/plugins
cp techtonica_vr.* ./artifact/BepInEx/plugins/techtonica_vr
cp -r ./libs/* ./artifact/Techtonica_Data/
cp -r ./unity/AssetBundles/StandaloneWindows/* ./artifact/BepInEx/plugins/techtonica_vr/assets
cp {README.adoc,CHANGELOG.md,LICENSE} ./artifact/BepInEx/plugins/techtonica_vr
cp {README.adoc,CHANGELOG.md,LICENSE,manifest.json,icon.png} ./artifact/
- name: Upload Release Asset Artifact
uses: actions/upload-artifact@v4
Expand Down
35 changes: 29 additions & 6 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ permissions:

name: release-please

env:
MOD_NAME: "techtonica_vr"
PI_UTILS_VERSION: "0.2.0"
TTIK_VERSION: "0.1.4"

jobs:
release-please:
runs-on: ubuntu-latest
Expand All @@ -36,21 +41,39 @@ jobs:
file: "Tobey.UnityAudio.zip"
version: "tags/v2.0.2"
token: ${{ secrets.DEPENDENCY_TOKEN }}
- name: Download PiUtils
if: ${{ steps.release.outputs.release_created }}
uses: dsaltares/fetch-gh-release-asset@1.1.1
with:
repo: "xenira/PiUtils"
file: "pi_utils-v${{ env.PI_UTILS_VERSION }}.zip"
version: "tags/v${{ env.PI_UTILS_VERSION }}"
token: ${{ secrets.DEPENDENCY_TOKEN }}
- name: Download TTIK
if: ${{ steps.release.outputs.release_created }}
uses: dsaltares/fetch-gh-release-asset@1.1.1
with:
repo: "xenira/TTIK"
file: "ttik-v${{ env.TTIK_VERSION }}.zip"
version: "tags/v${{ env.TTIK_VERSION }}"
token: ${{ secrets.DEPENDENCY_TOKEN }}
- name: Zip release artifact
if: ${{ steps.release.outputs.release_created }}
run: |
cd artifact && npx downdoc -a env-github -a env-thunderstore README.adoc && zip -r techtonica_vr-${{ steps.release.outputs.tag_name }}-thunderstore.zip ./ && cd ..
mv artifact/techtonica_vr-${{ steps.release.outputs.tag_name }}-thunderstore.zip .
cd artifact && npx downdoc -a env-github -a env-thunderstore README.adoc && zip -r ${{ env.MOD_NAME }}-${{ steps.release.outputs.tag_name }}-thunderstore.zip ./ && cd ..
mv artifact${{ env.MOD_NAME }}-${{ steps.release.outputs.tag_name }}-thunderstore.zip .
rm artifact/README.md
unzip Tobey.UnityAudio.zip -d ./artifact/
cd artifact && zip -r techtonica_vr-${{ steps.release.outputs.tag_name }}.zip ./ && cd ..
mv artifact/techtonica_vr-${{ steps.release.outputs.tag_name }}.zip .
unzip -n pi_utils-v${{ env.PI_UTILS_VERSION }}.zip -d ./artifact/
unzip -n ttik-v${{ env.TTIK_VERSION }}.zip -d ./artifact/
cd artifact && zip -r ${{ env.MOD_NAME }}-${{ steps.release.outputs.tag_name }}.zip ./ && cd ..
mv artifact/${{ env.MOD_NAME }}-${{ steps.release.outputs.tag_name }}.zip .
- name: Upload release artifact
if: ${{ steps.release.outputs.release_created }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload ${{ steps.release.outputs.tag_name }} techtonica_vr-${{ steps.release.outputs.tag_name }}.zip --repo Xenira/TechtonicaVR
gh release upload ${{ steps.release.outputs.tag_name }} ${{ env.MOD_NAME }}-${{ steps.release.outputs.tag_name }}.zip --repo Xenira/TechtonicaVR
- name: Upload to Thunderstore
uses: GreenTF/upload-thunderstore-package@v4.3
if: ${{ steps.release.outputs.release_created }}
Expand All @@ -61,7 +84,7 @@ jobs:
description: VR mod for Techtonica
categories: |
mods
file: techtonica_vr-${{ steps.release.outputs.tag_name }}-thunderstore.zip
file: ${{ env.MOD_NAME }}-${{ steps.release.outputs.tag_name }}-thunderstore.zip
version: "${{ steps.release.outputs.major }}.${{ steps.release.outputs.major }}.${{ steps.release.outputs.patch }}"
repo: thunderstore.io
token: ${{ secrets.THUNDERSTORE_KEY }}
19 changes: 14 additions & 5 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,17 @@ You can install the mod from https://thunderstore.io/c/techtonica/p/3_141/Techto
. Copy the `BepInEx` and `Techtonica_Data` folders to the `Techtonica` folder in `steamapps/common` directory.
. Run the game. The mod should be loaded automatically by BepInEx.

=== Audio Fix
Teleport and Snap turn have audio cues that are not played unless https://github.com/toebeann/Tobey.UnityAudio[Tobey.UnityAudio] is installed. You can install it from https://thunderstore.io/package/toebeann/TobeyUnityAudio/[Thunderstore] or the https://github.com/toebeann/Tobey.UnityAudio/releases[GitHub Releases] page.
=== Dependencies
The github release bundles the dependencies with the mod. The thunderstore release does not include the dependencies. You need to install them separately or use a mod manager that can install dependencies.

==== TTIK (Required)
This mod requires the https://github.com/Xenira/TTIK[TTIK] mod to be installed. This mod adds inverse kinematics (IK) to the game. This is required to render the player's body in VR. It also allows both VR and non-VR players to see the player's body movements. When playing co-op, the host needs to have this mod installed. For other non-VR players the mod is optional.

==== PiUtils (Required)
This mod requires the https://github.com/Xenira/PiUtils[PiUtils] mod to be installed. This mod provides some utility functions that are used by the VR mod.

- The GitHub release of the VR mod includes the audio fix so you don't need to install it separately.
- The thunderstore release does not include the audio fix unless you install it using a mod manager that installs dependencies.
==== Audio Fix (Optional)
Teleport and Snap turn have audio cues that are not played unless https://github.com/toebeann/Tobey.UnityAudio[Tobey.UnityAudio] is installed. You can install it from https://thunderstore.io/package/toebeann/TobeyUnityAudio/[Thunderstore] or the https://github.com/toebeann/Tobey.UnityAudio/releases[GitHub Releases] page.

To manually install the UnityAudio extract the `BepInEx` folder from the downloaded archive into the game's installation directory.

Expand All @@ -96,7 +102,6 @@ To disable the mod change the `Enabled` under `[General]` value in the `de.xenir
=== Uninstalling the Mod
To uninstall the mod remove the mod's files from the `BepInEx/plugins` folder. This should be the following files:

- `techtonica_vr.dll`
- 'techtonica_vr' folder

If you installed the audio fix, remove the `BepInEx/patches/Tobey/UnityAudio` and `BepInEx/plugin/Tobey/UnityAudio` folder from the game's installation directory.
Expand Down Expand Up @@ -185,6 +190,10 @@ Menu Downward Offset:: Offset of the menu in the downward direction. Default: `0
Menu Scroll Speed:: Speed of scrolling through menus by moving the cursor to the edge. Speed increases when nearer to the edge. Default: `0.125`
Menu Scroll Deadzone:: Deadzone for scrolling through menus by moving the cursor to the edge. In percent from the center. Effectively the size of the region not triggering scrolling. Default: `0.35`

[horizontal]
.Graphics
Display Body:: Enables or disables rendering of the player's body. When false only hands are rendered. Default: `true`

[horizontal]
.Debug
Debug Mode:: Mostly used for development. Default: `false`
Expand Down
1 change: 1 addition & 0 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dependencies": [
"BepInEx-BepInExPack-5.4.2100",
"3_141-PiUtils-0.2.0",
"3_141-TTIK-0.1.4",
"Tobey-UnityAudio-2.0.2"
]
}
4 changes: 4 additions & 0 deletions plugin/TechtonicaVr.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<PackageReference Include="BepInEx.PluginInfoProps" Version="2.*" />
<PackageReference Include="techtonica-libs" Version="0.3.0-e" />
<PackageReference Include="PiUtils" Version="0.*" />
<PackageReference Include="TTIK" Version="0.*" />
<!-- <PackageReference Include="UnityEngine.Modules" Version="5.6.0" IncludeAssets="compile" /> -->
</ItemGroup>

Expand All @@ -29,6 +30,9 @@
<Reference Include="PiUtils">
<HintPath>..\..\PiUtils\plugin\bin\Debug\netstandard2.1\pi_utils.dll</HintPath>
</Reference>
<Reference Include="TTIK">
<HintPath>..\..\TTIK\plugin\bin\Debug\netstandard2.1\ttik.dll</HintPath>
</Reference>
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions plugin/src/ModConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class ModConfig

// Graphics
public static ConfigEntry<int> targetFPS;
public static ConfigEntry<bool> displayBody;

// UI
public static ConfigEntry<float> menuSpawnDistance;
Expand Down Expand Up @@ -85,6 +86,7 @@ public static void Init(ConfigFile config)

// Graphics
targetFPS = config.Bind("Graphics", "Target FPS", 144, "Target FPS. No effect right now, but will be used in the future, once I figure out how to do it");
displayBody = config.Bind("Graphics", "Display Body", true, "Display player body. If disabled, only hands are visible. Does not affect other players");

// UI
menuSpawnDistance = config.Bind("UI", "Menu Spawn Distance", 0.8f, "Distance from head to spawn Menus");
Expand Down
4 changes: 4 additions & 0 deletions plugin/src/TechtonicaVR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
using TechtonicaVR.Assets;
using UnityEngine.SceneManagement;
using PiUtils.Util;
using TechtonicaVR.Ik;

namespace TechtonicaVR;

[BepInPlugin("de.xenira.techtonicavr", MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
[BepInProcess("Techtonica.exe")]
[BepInDependency("de.xenira.ttik")]
[BepInDependency("de.xenira.pi_utils")]
[BepInDependency("Tobey.UnityAudio", BepInDependency.DependencyFlags.SoftDependency)]
public class TechtonicaVR : BaseUnityPlugin
Expand Down Expand Up @@ -77,6 +79,8 @@ private void Awake()

StartCoroutine(AssetLoader.Load());

IkSetup.SetupIk();

TechtonicaVR.Logger.LogInfo($"Plugin {MyPluginInfo.PLUGIN_GUID} is loaded!");

// Add listener for scene change
Expand Down
12 changes: 6 additions & 6 deletions plugin/src/camera/VRCameraManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ public class VRCameraManager : MonoBehaviour

public Transform vrRoot;
public Transform camRoot;
public GameObject rightHandObject;
public GameObject rightHandModel;
public GameObject leftHandObject;
public GameObject leftHandModel;
public static GameObject rightHandObject;
public static GameObject rightHandModel;
public static GameObject leftHandObject;
public static GameObject leftHandModel;

public SteamVR_CameraHelper cameraHelperPrefab;

private SteamVR_CameraHelper cameraHelper;

Check warning on line 23 in plugin/src/camera/VRCameraManager.cs

View workflow job for this annotation

GitHub Actions / build

The field 'VRCameraManager.cameraHelper' is never used

Check warning on line 23 in plugin/src/camera/VRCameraManager.cs

View workflow job for this annotation

GitHub Actions / build

The field 'VRCameraManager.cameraHelper' is never used

public static Camera mainCamera;
public static bool mainGameLoaded = false;
public static VRCameraManager instance;

public static VRCameraManager Create()
Expand Down Expand Up @@ -73,8 +74,6 @@ private void SetupCamera()
mainCamera.gameObject.AddComponent<SteamVR_Camera>();
mainCamera.gameObject.AddComponent<SteamVR_TrackedObject>();
var techCam = mainCamera.gameObject.AddComponent<TechMainCamera>();
HmdMatrix44_t leftEyeMatrix = OpenVR.System.GetProjectionMatrix(EVREye.Eye_Left, mainCamera.nearClipPlane, mainCamera.farClipPlane);
HmdMatrix44_t rightEyeMatrix = OpenVR.System.GetProjectionMatrix(EVREye.Eye_Right, mainCamera.nearClipPlane, mainCamera.farClipPlane);

if (PlayerFirstPersonController.instance != null)
{
Expand All @@ -95,6 +94,7 @@ private void SetupCamera()
Vignette.Create();
}
Teleport.Create().transform.parent = mainCamera.transform;
mainGameLoaded = true;
}

FindObjectsOfType<Headlamp>().ForEach(h => h.transform.parent = mainCamera.transform);
Expand Down
95 changes: 95 additions & 0 deletions plugin/src/ik/IkSetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using PiUtils.Objects.Behaviours;
using PiUtils.Util;
using TechtonicaVR.Input;
using TechtonicaVR.VRCamera;
using TTIK.Network;
using UnityEngine;
using Valve.VR;
using Valve.VR.InteractionSystem;

namespace TechtonicaVR.Ik;

public class IkSetup
{
private static PluginLogger Logger = PluginLogger.GetLogger<IkSetup>();

public static void SetupIk()
{
GameObject headTarget = null;
Grabable headGrab = null;
GameObject leftHandTarget = null;
Grabable leftHandGrab = null;
GameObject rightHandTarget = null;
Grabable rightHandGrab = null;
NetworkIkPlayer.OnLocalPlayerInitialized += (instance) =>
{
Logger.LogDebug("Local ik player initialized. Requesting 3pt tracking...");
headTarget = new GameObject("HeadTarget");
headTarget.AddComponent<DontDestroyOnLoad>();
headTarget.transform.localPosition = new Vector3(0.0066f, -0.1317f, -0.1482f);
headTarget.transform.localRotation = Quaternion.Euler(5.8911f, 85.6196f, 240.5798f);
headGrab = headTarget.AddComponent<Grabable>();
leftHandTarget = new GameObject("LeftHandTarget");
leftHandTarget.AddComponent<DontDestroyOnLoad>();
leftHandTarget.transform.localPosition = new Vector3(0.0446f, 0.0913f, -0.1265f);
leftHandTarget.transform.localRotation = Quaternion.Euler(22.2776f, 183.4834f, 127.5222f);
leftHandGrab = leftHandTarget.AddComponent<Grabable>();
rightHandTarget = new GameObject("RightHandTarget");
rightHandTarget.AddComponent<DontDestroyOnLoad>();
rightHandTarget.transform.localPosition = new Vector3(0.0004f, 0.0901f, -0.1355f);
rightHandTarget.transform.localRotation = Quaternion.Euler(328.5594f, 6.5692f, 279.517f);
rightHandGrab = rightHandTarget.AddComponent<Grabable>();
AsyncGameObject.DelayUntil(() => instance.CmdInitIkPlayer(), () => VRCameraManager.mainGameLoaded);
};
NetworkIkPlayer.OnIkInitialized += (instance) =>
{
Logger.LogDebug("Ik player initialized. Starting calibration...");
AsyncGameObject.DelayUntil(() =>
{
headTarget.transform.SetParent(VRCameraManager.mainCamera.transform, false);
leftHandTarget.transform.SetParent(VRCameraManager.leftHandObject.transform, false);
rightHandTarget.transform.SetParent(VRCameraManager.rightHandObject.transform, false);
instance.calibrate(headTarget.transform, leftHandTarget.transform, rightHandTarget.transform);
instance.calibrated(null);
}, () => VRCameraManager.mainGameLoaded);
instance.OnCalibrated += () =>
{
Logger.LogDebug("Ik player calibrated. Starting tracking...");
VRCameraManager.leftHandModel.SetActive(!ModConfig.displayBody.Value);
VRCameraManager.rightHandModel.SetActive(!ModConfig.displayBody.Value);
instance.avatar.SetActive(ModConfig.displayBody.Value);
// SteamVRInputMapper.Grab.ButtonPressed += (object _sender, SteamVR_Input_Sources source) =>
// {
// Logger.LogDebug($"Grabbing target with source {source}");
// // leftHandGrab.TryGrab(VRCameraManager.rightHandObject.transform);
// // headGrab.TryGrab(VRCameraManager.rightHandObject.transform);
// rightHandGrab.TryGrab(VRCameraManager.leftHandObject.transform);
// };
// SteamVRInputMapper.Grab.ButtonReleased += (object _sender, SteamVR_Input_Sources _source) =>
// {
// Logger.LogDebug("Releasing target");
// headGrab.Release();
// leftHandGrab.Release();
// rightHandGrab.Release();
// };
};
};

// #### Not used until we have FBT ####
// SteamVRInputMapper.IKCalibrate.ButtonReleased += (object _sender, SteamVR_Input_Sources _source) =>
// {
// Logger.LogDebug("Calibration button released. Setting calibration values");
// if (NetworkIkPlayer.localInstance.ikPlayer.calibrating == false)
// {
// return;
// }
// NetworkIkPlayer.localInstance.ikPlayer.calibrated(null);
// };
}
}
7 changes: 5 additions & 2 deletions plugin/src/input/SteamvrInputMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,17 @@ public static class SteamVRInputMapper
public static Button UIShortcut2 = new Button(SteamVR_Actions._default.UIShortcut2);

// IK
public static Button IKCalibrate = new Button(SteamVR_Actions.IK.Calibrate);
// Not used until we have FBT
// public static Button IKCalibrate = new Button(SteamVR_Actions.IK.Calibrate);

public static Button Grab = new Button(SteamVR_Actions._default.GrabGrip);

public static void MapActions()
{
Logger.LogInfo("Mapping SteamVR actions...");
UIClick.action.actionSet.Activate();
IKCalibrate.action.actionSet.Activate();
// Not used until we have FBT
// IKCalibrate.action.actionSet.Activate();
SteamVR_Actions._default.Move.AddOnUpdateListener(HandleSteamVRMove, SteamVR_Input_Sources.Any);
SteamVR_Actions._default.MenuJoystickPrimary.AddOnUpdateListener(HandleSteamVRMenuJoystickPrimary, SteamVR_Input_Sources.Any);
SteamVR_Actions._default.MenuJoystickSecondary.AddOnUpdateListener(HandleSteamVRMenuJoystickSecondary, SteamVR_Input_Sources.Any);
Expand Down

0 comments on commit f028a00

Please sign in to comment.