mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-03 14:59:23 +00:00
[RelativeSync] Alt Pickup Test
This commit is contained in:
parent
564e50070a
commit
f2c3fcc50e
4 changed files with 438 additions and 1 deletions
221
RelativeSync/FixedJointPickupable.cs
Normal file
221
RelativeSync/FixedJointPickupable.cs
Normal file
|
@ -0,0 +1,221 @@
|
|||
using ABI_RC.Core.InteractionSystem.Base;
|
||||
using ABI.CCK.Components;
|
||||
using UnityEngine;
|
||||
|
||||
// https://wirewhiz.com/vr-grabbing-tutorial/
|
||||
|
||||
public class CollisionLogger : MonoBehaviour
|
||||
{
|
||||
private int _touchCount;
|
||||
public int touchCount
|
||||
{
|
||||
get => _touchCount;
|
||||
private set
|
||||
{
|
||||
if (!countChangedThisFrame && _touchCount != value) countChangedThisFrame = true;
|
||||
_touchCount = Mathf.Max(0, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool countChangedThisFrame {get; private set;} = true;
|
||||
|
||||
private void OnCollisionEnter(Collision _)
|
||||
=> touchCount++;
|
||||
private void OnCollisionExit(Collision _)
|
||||
=> touchCount--;
|
||||
private void LateUpdate()
|
||||
=> countChangedThisFrame = false;
|
||||
}
|
||||
|
||||
public class FixedJointPickupable : Pickupable
|
||||
{
|
||||
private Rigidbody _rigidbody;
|
||||
private bool _originalGravityState;
|
||||
|
||||
private Vector3 _initialGrabOffset;
|
||||
private Vector3 _unCollidedOffset;
|
||||
private float _unCollideInterpolationTime;
|
||||
|
||||
private readonly JointDrive positionDrive = new() { positionSpring = 2000, positionDamper = 10, maximumForce = 3.402823e+38f };
|
||||
private readonly JointDrive rotationDrive = new() { positionSpring = 2000, positionDamper = 0, maximumForce = 3.402823e+38f };
|
||||
private ConfigurableJoint _configurableJoint;
|
||||
private FixedJoint _fixedJoint;
|
||||
|
||||
private CollisionLogger _collisionLogger;
|
||||
|
||||
private Transform _pickupHandTransform;
|
||||
private Vector3 _previousHandPosition;
|
||||
private Vector3 _previousHandRotation;
|
||||
private float _grabStartTime;
|
||||
|
||||
public override bool CanPickup => IsPickupable && isActiveAndEnabled;
|
||||
|
||||
public override bool DisallowTheft => false;
|
||||
|
||||
public override bool IsAutoHold => false;
|
||||
|
||||
public override float MaxGrabDistance => 100f;
|
||||
|
||||
public override float MaxPushDistance => 100f;
|
||||
|
||||
public override bool IsObjectRotationAllowed => true;
|
||||
|
||||
public override bool IsObjectPushPullAllowed => true;
|
||||
|
||||
public override bool IsObjectInteractionAllowed => true;
|
||||
|
||||
public override Transform RootTransform => base.transform;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_rigidbody = GetComponent<Rigidbody>();
|
||||
if (_rigidbody == null)
|
||||
{
|
||||
_rigidbody = gameObject.AddComponent<Rigidbody>();
|
||||
_rigidbody.useGravity = true;
|
||||
_rigidbody.isKinematic = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGrab(Vector3 grabPoint)
|
||||
{
|
||||
_grabStartTime = Time.time;
|
||||
_pickupHandTransform = ControllerRay.pivotPoint.transform;
|
||||
|
||||
ControllerRay.UpdateGrabDistance(Vector3.Distance(ControllerRay.transform.position, grabPoint));
|
||||
_pickupHandTransform.rotation = transform.rotation;
|
||||
|
||||
// handTransform in local space of the object (initial rotation is already known)
|
||||
_initialGrabOffset = transform.InverseTransformPoint(grabPoint);
|
||||
|
||||
// ensure our object has a CollisionLogger (hack)
|
||||
if (!gameObject.TryGetComponent(out _collisionLogger))
|
||||
_collisionLogger = gameObject.AddComponent<CollisionLogger>();
|
||||
|
||||
// ensure ControllerRay has a rigidbody (hack)
|
||||
if (!_pickupHandTransform.TryGetComponent(out Rigidbody handRigidBody))
|
||||
{
|
||||
handRigidBody = _pickupHandTransform.gameObject.AddComponent<Rigidbody>();
|
||||
handRigidBody.isKinematic = true;
|
||||
handRigidBody.useGravity = false;
|
||||
}
|
||||
|
||||
// ensure Pickupable has a fixed joint (hack)
|
||||
if (!_pickupHandTransform.gameObject.TryGetComponent(out _fixedJoint))
|
||||
{
|
||||
_fixedJoint = _pickupHandTransform.gameObject.AddComponent<FixedJoint>();
|
||||
_fixedJoint.enableCollision = false;
|
||||
_fixedJoint.autoConfigureConnectedAnchor = false;
|
||||
_fixedJoint.connectedAnchor = _initialGrabOffset; // it'll snap to this position after interpolation
|
||||
}
|
||||
|
||||
// ensure Pickupable has a configurable joint (hack)
|
||||
if (!_pickupHandTransform.gameObject.TryGetComponent(out _configurableJoint))
|
||||
{
|
||||
_configurableJoint = _pickupHandTransform.gameObject.AddComponent<ConfigurableJoint>();
|
||||
|
||||
_configurableJoint.enableCollision = false;
|
||||
_configurableJoint.autoConfigureConnectedAnchor = false;
|
||||
|
||||
_configurableJoint.xMotion = ConfigurableJointMotion.Free;
|
||||
_configurableJoint.yMotion = ConfigurableJointMotion.Free;
|
||||
_configurableJoint.zMotion = ConfigurableJointMotion.Free;
|
||||
_configurableJoint.angularXMotion = ConfigurableJointMotion.Free;
|
||||
_configurableJoint.angularYMotion = ConfigurableJointMotion.Free;
|
||||
_configurableJoint.angularZMotion = ConfigurableJointMotion.Free;
|
||||
|
||||
_configurableJoint.xDrive = positionDrive;
|
||||
_configurableJoint.yDrive = positionDrive;
|
||||
_configurableJoint.zDrive = positionDrive;
|
||||
|
||||
_configurableJoint.rotationDriveMode = RotationDriveMode.Slerp;
|
||||
_configurableJoint.slerpDrive = rotationDrive;
|
||||
}
|
||||
|
||||
//_fixedJoint.connectedBody = _rigidbody;
|
||||
_configurableJoint.connectedBody = _rigidbody; // start off floppy
|
||||
|
||||
_originalGravityState = _rigidbody.useGravity;
|
||||
_rigidbody.useGravity = false;
|
||||
|
||||
// Reset velocities to avoid unintended motion
|
||||
_rigidbody.velocity = Vector3.zero;
|
||||
_rigidbody.angularVelocity = Vector3.zero;
|
||||
}
|
||||
|
||||
public override void OnDrop()
|
||||
{
|
||||
if (_collisionLogger != null)
|
||||
{
|
||||
Destroy(_collisionLogger);
|
||||
_collisionLogger = null;
|
||||
}
|
||||
|
||||
if (_fixedJoint != null)
|
||||
{
|
||||
Destroy(_fixedJoint);
|
||||
_fixedJoint = null;
|
||||
}
|
||||
|
||||
if (_configurableJoint != null)
|
||||
{
|
||||
Destroy(_configurableJoint);
|
||||
_configurableJoint = null;
|
||||
}
|
||||
|
||||
_rigidbody.useGravity = _originalGravityState;
|
||||
}
|
||||
|
||||
public override void OnFlingTowardsTarget(Vector3 target)
|
||||
{
|
||||
// Implement fling logic if needed
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsPickedUp)
|
||||
return;
|
||||
|
||||
if (_unCollideInterpolationTime < 1f)
|
||||
{
|
||||
_unCollideInterpolationTime += Time.deltaTime / 0.1f; // 0.01s to interpolate to hand
|
||||
_unCollideInterpolationTime = Mathf.Min(_unCollideInterpolationTime, 1f);
|
||||
|
||||
_fixedJoint.connectedBody = null; // hack so we don't need to lerp connectedAnchor ourselves
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, _pickupHandTransform.rotation, _unCollideInterpolationTime);
|
||||
//transform.position = Vector3.Lerp(transform.position, transform.TransformPoint(_initialGrabOffset), _unCollideInterpolationTime); // no work
|
||||
_fixedJoint.connectedBody = _rigidbody; // keep interpolation start point
|
||||
_fixedJoint.connectedAnchor = Vector3.Lerp(_unCollidedOffset, _initialGrabOffset, _unCollideInterpolationTime);
|
||||
}
|
||||
|
||||
if (_collisionLogger.countChangedThisFrame)
|
||||
{
|
||||
if (_collisionLogger.touchCount == 0)
|
||||
{
|
||||
_configurableJoint.connectedBody = null;
|
||||
_fixedJoint.connectedBody = _rigidbody; // keep interpolation start point
|
||||
_fixedJoint.connectedAnchor = _unCollidedOffset = transform.InverseTransformPoint(_pickupHandTransform.position);
|
||||
_unCollideInterpolationTime = 0f; // interpolate to hand
|
||||
}
|
||||
else if(_collisionLogger.touchCount > 0)
|
||||
{
|
||||
_fixedJoint.connectedBody = null;
|
||||
_configurableJoint.connectedBody = _rigidbody;
|
||||
_unCollideInterpolationTime = 1f; // no interpolation
|
||||
}
|
||||
}
|
||||
|
||||
UpdatePositionAndRotation();
|
||||
|
||||
_previousHandPosition = _pickupHandTransform.position;
|
||||
_previousHandRotation = _pickupHandTransform.rotation.eulerAngles;
|
||||
}
|
||||
|
||||
private void UpdatePositionAndRotation()
|
||||
{
|
||||
if (_rigidbody.isKinematic)
|
||||
{
|
||||
transform.SetPositionAndRotation(_pickupHandTransform.position, _pickupHandTransform.rotation);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,9 @@ public class RelativeSyncMod : MelonMod
|
|||
// Experimental sync hack
|
||||
ApplyPatches(typeof(CVRSpawnablePatches));
|
||||
|
||||
// Experimental pickup in chair hack
|
||||
ApplyPatches(typeof(CVRPickupObjectPatches));
|
||||
|
||||
// Experimental no interpolation on Better Better Character Controller
|
||||
ApplyPatches(typeof(BetterBetterCharacterControllerPatches));
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@ internal static class ModSettings
|
|||
Category.CreateEntry("ExpNoInterpolationOnBBCC", true,
|
||||
"Exp Disable Interpolation on BBCC", description: "Disable interpolation on Better Better Character Controller. May help reduce local jitter on synced movement parents.");
|
||||
|
||||
private static readonly MelonPreferences_Entry<bool> ExpSeatAndPickupsHack =
|
||||
Category.CreateEntry("ExpSeatAndPickupsHack", true,
|
||||
"Exp Seat and Pickups Hack", description: "Forces CVRSeat to update after Character Controller update.");
|
||||
|
||||
#endregion Melon Preferences
|
||||
|
||||
internal static void Initialize()
|
||||
|
@ -44,5 +48,6 @@ internal static class ModSettings
|
|||
ModNetwork.Debug_NetworkOutbound = DebugLogOutbound.Value;
|
||||
Patches.CVRSpawnablePatches.UseHack = ExpSyncedObjectHack.Value;
|
||||
Patches.BetterBetterCharacterControllerPatches.NoInterpolation = ExpNoInterpolationOnBBCC.Value;
|
||||
Patches.BetterBetterCharacterControllerPatches.UseSeatAndPickupsHack = ExpSeatAndPickupsHack.Value;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
using ABI_RC.Core.Base;
|
||||
using ABI_RC.Core.InteractionSystem;
|
||||
using ABI_RC.Core.InteractionSystem.Base;
|
||||
using ABI_RC.Core.IO;
|
||||
using ABI_RC.Core.Networking.Jobs;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Systems.Movement;
|
||||
using ABI.CCK.Components;
|
||||
using HarmonyLib;
|
||||
|
@ -19,7 +22,6 @@ internal static class PlayerSetupPatches
|
|||
{
|
||||
__instance.AddComponentIfMissing<RelativeSyncMonitor>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal static class PuppetMasterPatches
|
||||
|
@ -40,6 +42,13 @@ internal static class CVRSeatPatches
|
|||
{
|
||||
__instance.AddComponentIfMissing<RelativeSyncMarker>();
|
||||
}
|
||||
|
||||
internal static bool canUpdate;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CVRSeat), nameof(CVRSeat.Update))]
|
||||
private static bool Prefix_CVRSpawnable_Update()
|
||||
=> !BetterBetterCharacterControllerPatches.UseSeatAndPickupsHack || canUpdate;
|
||||
}
|
||||
|
||||
internal static class CVRMovementParentPatches
|
||||
|
@ -110,4 +119,203 @@ internal static class BetterBetterCharacterControllerPatches
|
|||
_initialInterpolation = _rigidbody.interpolation;
|
||||
NoInterpolation = _noInterpolation; // get initial value as patch runs later than settings init
|
||||
}
|
||||
|
||||
internal static bool UseSeatAndPickupsHack;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(BetterBetterCharacterController), nameof(BetterBetterCharacterController.OnAfterSimulationUpdate))]
|
||||
private static void Postfix_BetterBetterCharacterController_OnAfterSimulationUpdate(ref CVRSeat ____lastCvrSeat)
|
||||
{
|
||||
if (!UseSeatAndPickupsHack)
|
||||
return;
|
||||
|
||||
// solve chairs
|
||||
if (____lastCvrSeat != null)
|
||||
{
|
||||
CVRSeatPatches.canUpdate = true;
|
||||
____lastCvrSeat.Update();
|
||||
CVRSeatPatches.canUpdate = false;
|
||||
}
|
||||
|
||||
// now solve held pickups (very hacky)
|
||||
CVRPickupObjectPatches.canUpdate = true;
|
||||
|
||||
Pickupable heldPickup;
|
||||
if (!MetaPort.Instance.isUsingVr)
|
||||
{
|
||||
heldPickup = PlayerSetup.Instance.desktopRay.grabbedObject;
|
||||
if (heldPickup != null && heldPickup is CVRPickupObject pickupObject)
|
||||
CVRPickupObjectPatches.FixedFixedUpdate(pickupObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
heldPickup = PlayerSetup.Instance.vrRayLeft.grabbedObject;
|
||||
if (heldPickup != null && heldPickup is CVRPickupObject pickupObjectLeft)
|
||||
CVRPickupObjectPatches.FixedFixedUpdate(pickupObjectLeft);
|
||||
heldPickup = PlayerSetup.Instance.vrRayRight.grabbedObject;
|
||||
if (heldPickup != null && heldPickup is CVRPickupObject pickupObjectRight)
|
||||
CVRPickupObjectPatches.FixedFixedUpdate(pickupObjectRight);
|
||||
}
|
||||
|
||||
CVRPickupObjectPatches.canUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class CVRPickupObjectPatches
|
||||
{
|
||||
internal static bool canUpdate;
|
||||
internal static float themultiplier = 20f;
|
||||
|
||||
// if (!(pickupObject.transform.position.y >= pickupObject._respawnHeight))
|
||||
// pickupObject.ResetLocation(); // reset if below respawn height
|
||||
|
||||
internal static void FixedBasicUpdate(CVRPickupObject pickupObject)
|
||||
{
|
||||
if (pickupObject._currentlyFlung && pickupObject._directFlung)
|
||||
{
|
||||
pickupObject.isTeleGrabbed = true;
|
||||
pickupObject.transform.position =
|
||||
Vector3.SmoothDamp(pickupObject.transform.position, pickupObject._flingTarget, ref pickupObject._flingVelocity, 0.25f);
|
||||
pickupObject._rigidbody.useGravity = false;
|
||||
if (Vector3.Distance(pickupObject.transform.position, pickupObject._flingTarget) < 0.1f)
|
||||
{
|
||||
pickupObject.ResetFlungStatus();
|
||||
SchedulerSystem.RemoveJob(pickupObject.ResetFlungStatus);
|
||||
}
|
||||
}
|
||||
|
||||
if (pickupObject.updateWithPhysics
|
||||
|| pickupObject.ControllerRay == null)
|
||||
return;
|
||||
|
||||
if (pickupObject.gripType == CVRPickupObject.GripType.Free)
|
||||
pickupObject.transform.rotation = pickupObject.ControllerRay.pivotPoint.rotation;
|
||||
|
||||
else if (pickupObject.gripType == CVRPickupObject.GripType.Origin)
|
||||
{
|
||||
if (pickupObject.gripOrigin == null || pickupObject.gripOrigin == pickupObject.transform)
|
||||
{
|
||||
pickupObject.transform.rotation = pickupObject.ControllerRay.pivotPoint.rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
pickupObject.transform.rotation =
|
||||
Quaternion.Inverse(pickupObject.gripOrigin.localRotation * Quaternion.Inverse(pickupObject.ControllerRay.pivotPoint.rotation));
|
||||
Vector3 b = pickupObject.ControllerRay.pivotPoint.position - pickupObject.gripOrigin.position;
|
||||
pickupObject.transform.position += b;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 vector = pickupObject.transform.position;
|
||||
if (pickupObject.gripType == CVRPickupObject.GripType.Free)
|
||||
{
|
||||
vector = pickupObject.ControllerRay.pivotPoint.position + pickupObject.ControllerRay.pivotPoint.TransformDirection(pickupObject._initialPositionOffset) - pickupObject.transform.position;
|
||||
}
|
||||
if (pickupObject.gripType == CVRPickupObject.GripType.Origin)
|
||||
{
|
||||
if (pickupObject.gripOrigin == null)
|
||||
{
|
||||
vector = pickupObject.ControllerRay.pivotPoint.position - pickupObject.transform.position;
|
||||
}
|
||||
else
|
||||
{
|
||||
vector = pickupObject.ControllerRay.pivotPoint.position - pickupObject.gripOrigin.position;
|
||||
}
|
||||
}
|
||||
|
||||
var snappingVelocity = pickupObject.GetSnappingVelocity(
|
||||
out Vector3 snappingPointPosition,
|
||||
out CVRSnappingPoint snappingPoint,
|
||||
out SnappingReference snappingReference);
|
||||
|
||||
if (snappingVelocity.HasValue
|
||||
&& snappingPoint != null
|
||||
&& snappingReference != null &&
|
||||
Vector3.Distance(
|
||||
pickupObject.transform.position + vector + snappingReference.referencePoint.position -
|
||||
pickupObject.transform.position, snappingPointPosition) < snappingPoint.distance * 1.25)
|
||||
vector = snappingVelocity.Value;
|
||||
|
||||
pickupObject.transform.position += vector;
|
||||
pickupObject._resultVelocity = vector / Time.deltaTime;
|
||||
}
|
||||
|
||||
internal static void FixedFixedUpdate(CVRPickupObject pickupObject)
|
||||
{
|
||||
if (!pickupObject.updateWithPhysics || pickupObject.ControllerRay == null)
|
||||
return;
|
||||
|
||||
// Determine the target position and rotation based on the grip type
|
||||
Vector3 targetPosition = Vector3.zero;
|
||||
Quaternion targetRotation = pickupObject.ControllerRay.pivotPoint.rotation;
|
||||
|
||||
if (pickupObject.gripType == CVRPickupObject.GripType.Free)
|
||||
{
|
||||
targetPosition = pickupObject.ControllerRay.pivotPoint.position +
|
||||
pickupObject.ControllerRay.pivotPoint.TransformDirection(pickupObject._initialPositionOffset);
|
||||
}
|
||||
else if (pickupObject.gripType == CVRPickupObject.GripType.Origin)
|
||||
{
|
||||
targetPosition = pickupObject.gripOrigin != null
|
||||
? pickupObject.ControllerRay.pivotPoint.position - pickupObject.gripOrigin.position + pickupObject.transform.position
|
||||
: pickupObject.ControllerRay.pivotPoint.position;
|
||||
}
|
||||
|
||||
// Handle snapping if necessary
|
||||
var snappingVelocity = pickupObject.GetSnappingVelocity(
|
||||
out Vector3 snappingPointPosition,
|
||||
out CVRSnappingPoint snappingPoint,
|
||||
out SnappingReference snappingReference);
|
||||
|
||||
if (snappingVelocity.HasValue &&
|
||||
snappingPoint != null &&
|
||||
snappingReference != null &&
|
||||
Vector3.Distance(targetPosition + snappingReference.referencePoint.position - pickupObject.transform.position, snappingPointPosition) < snappingPoint.distance * 1.25)
|
||||
{
|
||||
targetPosition = snappingVelocity.Value + pickupObject.transform.position;
|
||||
}
|
||||
|
||||
// Smoothly interpolate to the target position and rotation
|
||||
// pickupObject._rigidbody.position = Vector3.Lerp(pickupObject._rigidbody.position, targetPosition, Time.fixedDeltaTime * 10f);
|
||||
// pickupObject._rigidbody.rotation = Quaternion.Slerp(pickupObject._rigidbody.rotation, targetRotation, Time.fixedDeltaTime * 10f);
|
||||
|
||||
// Apply small corrective forces to handle collisions more naturally
|
||||
Vector3 correctiveForce = (targetPosition - pickupObject._rigidbody.position) / Time.fixedDeltaTime;
|
||||
pickupObject._rigidbody.AddForce(correctiveForce, ForceMode.VelocityChange);
|
||||
//pickupObject._rigidbody.AddTorque(Vector3.Cross(pickupObject._rigidbody.angularVelocity, correctiveForce) * themultiplier, ForceMode.VelocityChange);
|
||||
|
||||
// Reset angular velocity to ensure precise control over rotation
|
||||
//pickupObject._rigidbody.angularVelocity = Vector3.zero;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CVRPickupObject), nameof(CVRPickupObject.FixedUpdate))]
|
||||
private static bool Prefix_CVRPickupObject_FixedUpdate(ref CVRPickupObject __instance)
|
||||
{
|
||||
if (!BetterBetterCharacterControllerPatches.UseSeatAndPickupsHack)
|
||||
return true;
|
||||
|
||||
if (!(__instance.transform.position.y >= __instance._respawnHeight))
|
||||
__instance.ResetLocation(); // reset if below respawn height
|
||||
|
||||
return canUpdate;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(CVRPickupObject), nameof(CVRPickupObject.Update))]
|
||||
private static bool Prefix_CVRPickupObject_Update(ref CVRPickupObject __instance)
|
||||
{
|
||||
if (!BetterBetterCharacterControllerPatches.UseSeatAndPickupsHack)
|
||||
return true;
|
||||
|
||||
return canUpdate;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(CVRPickupObject), nameof(CVRPickupObject.Start))]
|
||||
private static void Postfix_CVRPickupObject_Start(ref CVRPickupObject __instance)
|
||||
{
|
||||
__instance.AddComponentIfMissing<FixedJointPickupable>();
|
||||
UnityEngine.Object.Destroy(__instance);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue