mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-04 07:19:22 +00:00
[RelativeSync] Initial Release
This commit is contained in:
parent
4f177633a4
commit
ca7df56e76
11 changed files with 647 additions and 0 deletions
156
RelativeSync/RelativeSync/Components/RelativeSyncController.cs
Normal file
156
RelativeSync/RelativeSync/Components/RelativeSyncController.cs
Normal file
|
@ -0,0 +1,156 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.RelativeSync.Components;
|
||||
|
||||
[DefaultExecutionOrder(int.MaxValue)] // make sure this runs after NetIKController
|
||||
public class RelativeSyncController : MonoBehaviour
|
||||
{
|
||||
private static float MaxMagnitude = 750000000000f;
|
||||
|
||||
private string _userId;
|
||||
private PuppetMaster puppetMaster { get; set; }
|
||||
private NetIKController netIkController { get; set; }
|
||||
|
||||
// private bool _syncMarkerChangedSinceLastSync;
|
||||
private RelativeSyncMarker _relativeSyncMarker;
|
||||
|
||||
private RelativeSyncData _relativeSyncData;
|
||||
private RelativeSyncData _lastSyncData;
|
||||
|
||||
#region Unity Events
|
||||
|
||||
private void Start()
|
||||
{
|
||||
puppetMaster = GetComponent<PuppetMaster>();
|
||||
netIkController = GetComponent<NetIKController>();
|
||||
|
||||
_userId = puppetMaster._playerDescriptor.ownerId;
|
||||
RelativeSyncManager.RelativeSyncControllers.Add(_userId, this);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
RelativeSyncManager.RelativeSyncControllers.Remove(_userId);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (puppetMaster._isHidden)
|
||||
return;
|
||||
|
||||
if (_relativeSyncMarker == null)
|
||||
return;
|
||||
|
||||
Animator animator = puppetMaster._animator;
|
||||
if (animator == null)
|
||||
return;
|
||||
|
||||
Transform avatarTransform = animator.transform;
|
||||
|
||||
Vector3 worldRootPos = avatarTransform.position;
|
||||
Quaternion worldRootRot = avatarTransform.rotation;
|
||||
|
||||
Vector3 relativeHipPos = default;
|
||||
Quaternion relativeHipRot = default;
|
||||
Transform hipTrans = animator.GetBoneTransform(HumanBodyBones.Hips);
|
||||
if (hipTrans != null)
|
||||
{
|
||||
Vector3 hipPos = hipTrans.position;
|
||||
Quaternion hipRot = hipTrans.rotation;
|
||||
|
||||
relativeHipPos = Quaternion.Inverse(worldRootRot) * (hipPos - worldRootPos);
|
||||
relativeHipRot = Quaternion.Inverse(worldRootRot) * hipRot;
|
||||
}
|
||||
|
||||
// todo: this is fucked and idk why, is technically slightly differing sync rates,
|
||||
// but even reimplementing dynamic tps here didnt fix the jitter
|
||||
float lerp = netIkController.GetLerpSpeed();
|
||||
|
||||
Vector3 targetLocalPosition = _relativeSyncData.LocalRootPosition;
|
||||
Quaternion targetLocalRotation = Quaternion.Euler(_relativeSyncData.LocalRootRotation);
|
||||
Transform targetTransform = _relativeSyncMarker.transform;
|
||||
|
||||
if (_relativeSyncMarker.ApplyRelativeRotation && _relativeSyncData.LocalRootRotation.sqrMagnitude < MaxMagnitude)
|
||||
{
|
||||
Quaternion rotation = targetTransform.rotation;
|
||||
Quaternion worldRotation = rotation * targetLocalRotation;
|
||||
Quaternion lastRotation = rotation * Quaternion.Euler(_lastSyncData.LocalRootRotation);
|
||||
|
||||
if (_relativeSyncMarker.OnlyApplyRelativeHeading)
|
||||
{
|
||||
Vector3 currentForward = lastRotation * Vector3.forward;
|
||||
Vector3 targetForward = worldRotation * Vector3.forward;
|
||||
Vector3 currentWorldUp = avatarTransform.up; // up direction of player before we touch it
|
||||
|
||||
// project forward vectors to the ground plane
|
||||
currentForward = Vector3.ProjectOnPlane(currentForward, currentWorldUp).normalized;
|
||||
targetForward = Vector3.ProjectOnPlane(targetForward, currentWorldUp).normalized;
|
||||
|
||||
lastRotation = Quaternion.LookRotation(currentForward, currentWorldUp);
|
||||
worldRotation = Quaternion.LookRotation(targetForward, currentWorldUp);
|
||||
}
|
||||
|
||||
transform.rotation = Quaternion.Slerp(lastRotation, worldRotation, lerp);
|
||||
}
|
||||
|
||||
if (_relativeSyncMarker.ApplyRelativePosition && _relativeSyncData.LocalRootPosition.sqrMagnitude < MaxMagnitude)
|
||||
{
|
||||
Vector3 worldPosition = targetTransform.TransformPoint(targetLocalPosition);
|
||||
transform.position = Vector3.Lerp(targetTransform.TransformPoint(_lastSyncData.LocalRootPosition), worldPosition, lerp);
|
||||
}
|
||||
|
||||
// negate avatar transform movement
|
||||
avatarTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
|
||||
// fix hip syncing because it is not relative to root, it is synced in world space -_-
|
||||
if (hipTrans != null)
|
||||
{
|
||||
hipTrans.position = transform.position + transform.rotation * relativeHipPos;
|
||||
hipTrans.rotation = transform.rotation * relativeHipRot;
|
||||
}
|
||||
|
||||
_lastSyncData.LocalRootPosition = targetLocalPosition;
|
||||
_lastSyncData.LocalRootRotation = targetLocalRotation.eulerAngles;
|
||||
}
|
||||
|
||||
#endregion Unity Events
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void SetRelativeSyncMarker(RelativeSyncMarker target)
|
||||
{
|
||||
if (_relativeSyncMarker == target)
|
||||
return;
|
||||
|
||||
_relativeSyncMarker = target;
|
||||
|
||||
// calculate relative position and rotation so lerp can smooth it out (hack)
|
||||
if (_relativeSyncMarker != null)
|
||||
{
|
||||
Transform avatarTransform = puppetMaster._animator.transform;
|
||||
Transform markerTransform = _relativeSyncMarker.transform;
|
||||
Vector3 localPosition = markerTransform.InverseTransformPoint(avatarTransform.position);
|
||||
Quaternion localRotation = Quaternion.Inverse(markerTransform.rotation) * avatarTransform.rotation;
|
||||
|
||||
// set last sync data to current position and rotation so we don't lerp from the last marker
|
||||
_lastSyncData.LocalRootPosition = localPosition;
|
||||
_lastSyncData.LocalRootRotation = localRotation.eulerAngles;
|
||||
//Debug.Log($"SetRelativeSyncMarker: {_relativeSyncMarker.name}");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRelativePositions(Vector3 position, Vector3 rotation)
|
||||
{
|
||||
_relativeSyncData.LocalRootPosition = position;
|
||||
_relativeSyncData.LocalRootRotation = rotation;
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
public struct RelativeSyncData
|
||||
{
|
||||
public Vector3 LocalRootPosition;
|
||||
public Vector3 LocalRootRotation;
|
||||
}
|
||||
}
|
50
RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs
Normal file
50
RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using ABI.CCK.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.RelativeSync.Components;
|
||||
|
||||
public class RelativeSyncMarker : MonoBehaviour
|
||||
{
|
||||
public int pathHash { get; private set; }
|
||||
|
||||
public bool ApplyRelativePosition = true;
|
||||
public bool ApplyRelativeRotation = true;
|
||||
public bool OnlyApplyRelativeHeading;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
string path = GetGameObjectPath(transform);
|
||||
pathHash = path.GetHashCode();
|
||||
RelativeSyncManager.RelativeSyncTransforms.Add(pathHash, this);
|
||||
|
||||
ConfigureForPotentialMovementParent();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
RelativeSyncManager.RelativeSyncTransforms.Remove(pathHash);
|
||||
}
|
||||
|
||||
private void ConfigureForPotentialMovementParent()
|
||||
{
|
||||
if (!gameObject.TryGetComponent(out CVRMovementParent movementParent))
|
||||
return;
|
||||
|
||||
// TODO: a refactor may be needed to handle the orientation mode being animated
|
||||
|
||||
// respect orientation mode & gravity zone
|
||||
ApplyRelativeRotation = movementParent.orientationMode == CVRMovementParent.OrientationMode.RotateWithParent;
|
||||
OnlyApplyRelativeHeading = movementParent.GetComponent<GravityZone>() == null;
|
||||
}
|
||||
|
||||
private static string GetGameObjectPath(Transform transform)
|
||||
{
|
||||
string path = transform.name;
|
||||
while (transform.parent != null)
|
||||
{
|
||||
transform = transform.parent;
|
||||
path = transform.name + "/" + path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
82
RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs
Normal file
82
RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.Movement;
|
||||
using NAK.RelativeSync.Networking;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.RelativeSync.Components;
|
||||
|
||||
[DefaultExecutionOrder(int.MaxValue)]
|
||||
public class RelativeSyncMonitor : MonoBehaviour
|
||||
{
|
||||
private BetterBetterCharacterController _characterController { get; set; }
|
||||
|
||||
private RelativeSyncMarker _relativeSyncMarker;
|
||||
private RelativeSyncMarker _lastRelativeSyncMarker;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_characterController = GetComponent<BetterBetterCharacterController>();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (_characterController == null)
|
||||
return;
|
||||
|
||||
CheckForRelativeSyncMarker();
|
||||
|
||||
if (_relativeSyncMarker == null)
|
||||
{
|
||||
if (_lastRelativeSyncMarker == null)
|
||||
return;
|
||||
|
||||
// send empty position and rotation to stop syncing
|
||||
SendEmptyPositionAndRotation();
|
||||
_lastRelativeSyncMarker = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_lastRelativeSyncMarker = _relativeSyncMarker;
|
||||
|
||||
SendCurrentPositionAndRotation();
|
||||
}
|
||||
|
||||
private void CheckForRelativeSyncMarker()
|
||||
{
|
||||
if (_characterController._isSitting && _characterController._lastCvrSeat)
|
||||
{
|
||||
RelativeSyncMarker newMarker = _characterController._lastCvrSeat.GetComponent<RelativeSyncMarker>();
|
||||
_relativeSyncMarker = newMarker;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_characterController._previousMovementParent != null)
|
||||
{
|
||||
RelativeSyncMarker newMarker = _characterController._previousMovementParent.GetComponent<RelativeSyncMarker>();
|
||||
_relativeSyncMarker = newMarker;
|
||||
return;
|
||||
}
|
||||
|
||||
// none found
|
||||
_relativeSyncMarker = null;
|
||||
}
|
||||
|
||||
private void SendCurrentPositionAndRotation()
|
||||
{
|
||||
// because our syncing is retarded, we need to sync relative from the avatar root...
|
||||
Transform avatarRoot = PlayerSetup.Instance._avatar.transform;
|
||||
Vector3 avatarRootPosition = avatarRoot.position; // PlayerSetup.Instance.GetPlayerPosition()
|
||||
Quaternion avatarRootRotation = avatarRoot.rotation; // PlayerSetup.Instance.GetPlayerRotation()
|
||||
|
||||
Transform markerTransform = _relativeSyncMarker.transform;
|
||||
Vector3 localPosition = markerTransform.InverseTransformPoint(avatarRootPosition);
|
||||
Quaternion localRotation = Quaternion.Inverse(markerTransform.rotation) * avatarRootRotation;
|
||||
|
||||
ModNetwork.SendNetworkPosition(_relativeSyncMarker.pathHash, localPosition, localRotation.eulerAngles);
|
||||
}
|
||||
|
||||
private void SendEmptyPositionAndRotation()
|
||||
{
|
||||
ModNetwork.SendNetworkPosition(RelativeSyncManager.NoTarget, Vector3.zero, Vector3.zero);
|
||||
}
|
||||
}
|
34
RelativeSync/RelativeSync/RelativeSyncManager.cs
Normal file
34
RelativeSync/RelativeSync/RelativeSyncManager.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using ABI_RC.Core.Base;
|
||||
using ABI_RC.Core.Player;
|
||||
using NAK.RelativeSync.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.RelativeSync;
|
||||
|
||||
public static class RelativeSyncManager
|
||||
{
|
||||
public const int NoTarget = -1;
|
||||
|
||||
public static readonly Dictionary<int, RelativeSyncMarker> RelativeSyncTransforms = new();
|
||||
public static readonly Dictionary<string, RelativeSyncController> RelativeSyncControllers = new();
|
||||
|
||||
public static void ApplyRelativeSync(string userId, int target, Vector3 position, Vector3 rotation)
|
||||
{
|
||||
if (!RelativeSyncControllers.TryGetValue(userId, out RelativeSyncController controller))
|
||||
if (CVRPlayerManager.Instance.GetPlayerPuppetMaster(userId, out PuppetMaster pm))
|
||||
controller = pm.AddComponentIfMissing<RelativeSyncController>();
|
||||
|
||||
if (controller == null)
|
||||
{
|
||||
RelativeSyncMod.Logger.Error($"Failed to apply relative sync for user {userId}");
|
||||
return;
|
||||
}
|
||||
|
||||
// find target transform
|
||||
RelativeSyncMarker syncMarker = null;
|
||||
if (target != NoTarget) RelativeSyncTransforms.TryGetValue(target, out syncMarker);
|
||||
|
||||
controller.SetRelativePositions(position, rotation);
|
||||
controller.SetRelativeSyncMarker(syncMarker);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue