mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 06:19:22 +00:00
182 lines
No EOL
6.7 KiB
C#
182 lines
No EOL
6.7 KiB
C#
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 const float MaxMagnitude = 750000000000f;
|
|
|
|
private float _updateInterval = 0.05f;
|
|
private float _lastUpdate;
|
|
|
|
private string _userId;
|
|
private PuppetMaster puppetMaster { get; set; }
|
|
private RelativeSyncMarker _relativeSyncMarker;
|
|
|
|
private RelativeSyncData _relativeSyncData;
|
|
private RelativeSyncData _lastSyncData;
|
|
|
|
#region Unity Events
|
|
|
|
private void Start()
|
|
{
|
|
puppetMaster = GetComponent<PuppetMaster>();
|
|
|
|
_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;
|
|
Transform hipTrans = (animator.avatar != null && animator.isHuman)
|
|
? animator.GetBoneTransform(HumanBodyBones.Hips) : null;
|
|
|
|
Vector3 relativeHipPos = default;
|
|
Quaternion relativeHipRot = default;
|
|
if (hipTrans != null)
|
|
{
|
|
Vector3 worldRootPos = avatarTransform.position;
|
|
Quaternion worldRootRot = avatarTransform.rotation;
|
|
|
|
Vector3 hipPos = hipTrans.position;
|
|
Quaternion hipRot = hipTrans.rotation;
|
|
|
|
relativeHipPos = Quaternion.Inverse(worldRootRot) * (hipPos - worldRootPos);
|
|
relativeHipRot = Quaternion.Inverse(worldRootRot) * hipRot;
|
|
}
|
|
|
|
// TODO: handle the case where hip is not synced but is found on remote client
|
|
|
|
float lerp = Mathf.Min((Time.time - _lastUpdate) / _updateInterval, 1f);
|
|
|
|
ApplyRelativeRotation(avatarTransform, hipTrans, lerp);
|
|
ApplyRelativePosition(hipTrans, lerp);
|
|
|
|
// idk if needed (both player root & avatar root are set to same world position) -_-_-_-
|
|
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;
|
|
}
|
|
}
|
|
|
|
private void ApplyRelativeRotation(Transform avatarTransform, Transform hipTransform, float lerp)
|
|
{
|
|
if (!_relativeSyncMarker.ApplyRelativeRotation ||
|
|
!(_relativeSyncData.LocalRootRotation.sqrMagnitude < MaxMagnitude))
|
|
return; // not applying relative rotation or data is invalid
|
|
|
|
Quaternion markerRotation = _relativeSyncMarker.transform.rotation;
|
|
Quaternion lastWorldRotation = markerRotation * Quaternion.Euler(_lastSyncData.LocalRootRotation);
|
|
Quaternion worldRotation = markerRotation * Quaternion.Euler(_relativeSyncData.LocalRootRotation);
|
|
|
|
if (_relativeSyncMarker.OnlyApplyRelativeHeading)
|
|
{
|
|
Vector3 currentWorldUp = avatarTransform.up;
|
|
|
|
Vector3 currentForward = lastWorldRotation * Vector3.forward;
|
|
Vector3 targetForward = worldRotation * Vector3.forward;
|
|
|
|
currentForward = Vector3.ProjectOnPlane(currentForward, currentWorldUp).normalized;
|
|
targetForward = Vector3.ProjectOnPlane(targetForward, currentWorldUp).normalized;
|
|
|
|
lastWorldRotation = Quaternion.LookRotation(currentForward, currentWorldUp);
|
|
worldRotation = Quaternion.LookRotation(targetForward, currentWorldUp);
|
|
}
|
|
|
|
transform.rotation = Quaternion.Slerp(lastWorldRotation, worldRotation, lerp);
|
|
}
|
|
|
|
private void ApplyRelativePosition(Transform hipTransform, float lerp)
|
|
{
|
|
if (!_relativeSyncMarker.ApplyRelativePosition ||
|
|
!(_relativeSyncData.LocalRootPosition.sqrMagnitude < MaxMagnitude))
|
|
return; // not applying relative position or data is invalid
|
|
|
|
Transform targetTransform = _relativeSyncMarker.transform;
|
|
|
|
Vector3 lastWorldPosition = targetTransform.TransformPoint(_lastSyncData.LocalRootPosition);
|
|
Vector3 worldPosition = targetTransform.TransformPoint(_relativeSyncData.LocalRootPosition);
|
|
transform.position = Vector3.Lerp(lastWorldPosition, worldPosition, lerp);
|
|
|
|
// if (hipTransform == null)
|
|
// return;
|
|
//
|
|
// Vector3 lastWorldHipPosition = targetTransform.TransformPoint(_lastSyncData.LocalHipPosition);
|
|
// Vector3 worldHipPosition = targetTransform.TransformPoint(_relativeSyncData.LocalHipPosition);
|
|
// hipTransform.position = Vector3.Lerp(lastWorldHipPosition, worldHipPosition, lerp);
|
|
}
|
|
|
|
#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)
|
|
return;
|
|
|
|
Animator avatarAnimator = puppetMaster._animator;
|
|
if (avatarAnimator == null)
|
|
return; // i dont care to bother
|
|
|
|
RelativeSyncManager.GetRelativeAvatarPositionsFromMarker(
|
|
avatarAnimator, _relativeSyncMarker.transform,
|
|
out Vector3 relativePosition, out Vector3 relativeRotation);
|
|
|
|
// set last sync data to current position and rotation so we don't lerp from the last marker
|
|
_lastSyncData.LocalRootPosition = relativePosition;
|
|
_lastSyncData.LocalRootRotation = relativeRotation;
|
|
_lastUpdate = Time.time; // reset update time
|
|
}
|
|
|
|
public void SetRelativePositions(Vector3 position, Vector3 rotation)
|
|
{
|
|
// calculate update interval
|
|
float prevUpdate = _lastUpdate;
|
|
_lastUpdate = Time.time;
|
|
_updateInterval = _lastUpdate - prevUpdate;
|
|
|
|
// cycle last sync data
|
|
_lastSyncData = _relativeSyncData;
|
|
|
|
// set new sync data
|
|
_relativeSyncData.LocalRootPosition = position;
|
|
_relativeSyncData.LocalRootRotation = rotation;
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
private struct RelativeSyncData
|
|
{
|
|
public Vector3 LocalRootPosition;
|
|
public Vector3 LocalRootRotation;
|
|
}
|
|
} |