XRoom_Unity/Assets/Photon/FusionXRHost/Scripts/Desktop/MouseTeleport.cs
2025-05-31 10:20:20 +03:30

241 lines
9.5 KiB
C#

using UnityEngine.InputSystem;
using UnityEngine;
using Fusion.XR.Host.Rig;
using Fusion.XR.Host.Grabbing;
using Fusion.XR.Host.Locomotion;
namespace Fusion.XR.Host.Desktop
{
public interface IMouseTeleportHover
{
void OnHoverHit(RaycastHit hit);
void OnNoHover();
}
/**
* Allow to rotate the rig head with the mouse
* Allow to left click to teleport to a valid target
* Allow to have partial interaction capability
*/
public class MouseTeleport : MonoBehaviour
{
public HardwareRig rig;
public Camera mouseCamera;
public Vector3 defaultLeftHandPosition;
public Vector3 defaultRightHandPosition;
public Quaternion defaultLeftHandRotation;
public Quaternion defaultRightHandRotation;
public bool forceFirstPersonView = false;
public DesktopController hardwareRigControl;
public HardwareHand grabberHand;
public HardwareHand beamerHand;
public RayBeamer rayBeamer;
GameObject grabbed = null;
public IMouseTeleportHover hoverListener;
public const float HAND_RANGE = 0.7f;
float grabHandDistance = 0;
Transform Head => rig == null ? null : rig.headset.transform;
private void Awake()
{
hardwareRigControl = GetComponentInParent<DesktopController>();
if (mouseCamera == null) mouseCamera = GetComponentInChildren<Camera>();
if (rig == null) rig = GetComponentInParent<HardwareRig>();
defaultLeftHandPosition = Head.InverseTransformPoint(rig.leftHand.transform.position);
defaultRightHandPosition = Head.InverseTransformPoint(rig.rightHand.transform.position);
defaultLeftHandRotation = Quaternion.Inverse(Head.rotation) * rig.leftHand.transform.rotation;
defaultRightHandRotation = Quaternion.Inverse(Head.rotation) * rig.rightHand.transform.rotation;
}
void Start()
{
if (forceFirstPersonView)
{
mouseCamera.transform.position = Head.position;
mouseCamera.transform.rotation = Head.rotation;
}
// grab and teleport are done with left hand
grabberHand = rig.leftHand;
beamerHand = rig.leftHand;
rayBeamer = rig.leftHand.GetComponentInChildren<RayBeamer>();
}
bool CheckGrab(Ray mouseRay)
{
bool didTouch = false;
// No teleport/click when right click is pressed. Only rotation
if (!Mouse.current.rightButton.isPressed && Mouse.current.leftButton.isPressed)
{
if (Physics.Raycast(mouseRay, out RaycastHit hit, 40f))
{
// check if there is already a grabbed object
if (grabbed == null)
{
/*
// check if the hit object can be grab
var grabbable = hit.collider.GetComponentInParent<Grabbable>();
if (grabbable)
{
// the ray hit a grabbable object
didTouch = true;
grabbed = grabbable;
// do not display the ray
rayBeamer.CancelHit();
// We move the local hand to the hit position, and active isGrabbing
grabberHand.transform.position = hit.point;
grabberHand.isGrabbing = true;
grabHandDistance = (hit.point - mouseRay.origin).magnitude;
// TO DO Update the position of the grabbed object
}
*/
// check if the hit object can be grab
GameObject grabbableObject = null;
var grabbable = hit.collider.GetComponentInParent<Grabbable>();
if (grabbable)
{
grabbableObject = grabbable.gameObject;
}
/* else
{
var networkGrabbable = hit.collider.GetComponentInParent<NetworkHandColliderGrabbable>();
if (networkGrabbable)
{
grabbableObject = networkGrabbable.gameObject;
}
}*/
if (grabbableObject != null)
{
// the ray hit a grabbable object
didTouch = true;
grabbed = grabbableObject;
// do not display the ray
rayBeamer.CancelHit();
// We move the local hand to the hit position, and active isGrabbing
grabberHand.transform.position = hit.point;
grabberHand.isGrabbing = true;
grabHandDistance = (hit.point - mouseRay.origin).magnitude;
// TO DO Update the position of the grabbed object
}
}
}
}
return didTouch;
}
void CheckUngrab()
{
// Check if we should ungrab
if (!Mouse.current.leftButton.wasReleasedThisFrame && !Mouse.current.leftButton.isPressed)
{
if (grabbed != null)
{
grabbed = null;
grabberHand.isGrabbing = false;
}
}
}
Vector3 SearchTarget(Ray mouseRay)
{
var target = mouseRay.origin + mouseRay.direction * 20;
if (rayBeamer.BeamCast(out RaycastHit hit, mouseRay.origin, mouseRay.direction))
{
target = hit.point;
if (hoverListener != null) hoverListener.OnHoverHit(hit);
}
else
{
if (hoverListener != null) hoverListener.OnNoHover();
}
return target;
}
void Update()
{
rayBeamer.isRayEnabled = false;
var mouseRay = mouseCamera.ScreenPointToRay(Mouse.current.position.ReadValue());
// Storing the distance before checking ungrab, as we want to reset the hand position at ungrab during the next Update
// so that the grabbing system has the time to drop it where it is
if (grabbed == null) grabHandDistance = 0;
// Check if the mouse hit a grabbable object
bool didTouch = CheckGrab(mouseRay);
bool beamerRotationHandled = false;
bool grabberHandPositionHandled = didTouch;
CheckUngrab();
Vector3 target = Vector3.zero;
bool targetSearched = false;
if (hoverListener != null)
{
// If a hover listener expect to know if a hover occured, we have to check whatever buttons are clicked
target = SearchTarget(mouseRay);
targetSearched = true;
}
if (!didTouch && grabbed == null && Mouse.current.rightButton.isPressed == false)
{
if (Mouse.current.leftButton.isPressed || Mouse.current.leftButton.wasReleasedThisFrame)
{
rayBeamer.isRayEnabled = true;
if (targetSearched == false)
{
target = SearchTarget(mouseRay);
}
beamerRotationHandled = true;
var currentBeamLocalRotation = Quaternion.Inverse(beamerHand.transform.rotation) * rayBeamer.origin.rotation;
var beamRotation = Quaternion.LookRotation(target - rayBeamer.origin.position);
// Explanation:
// We have: beamerHand.transform.rotation * currentBeamLocalRotation = rayBeamer.origin.rotation
// We want: rayBeamer.origin.rotation = beamRotation;
// So beamerHand.transform.rotation * currentBeamLocalRotation = beamRotation
// So beamerHand.transform.rotation * currentBeamLocalRotation * Quaternion.Inverse(currentBeamLocalRotation) = beamRotation * Quaternion.Inverse(currentBeamLocalRotation)
// So beamerHand.transform.rotation * Quaternion.Identity = beamRotation * Quaternion.Inverse(currentBeamLocalRotation). Simplified to:
beamerHand.transform.rotation = beamRotation * Quaternion.Inverse(currentBeamLocalRotation);
}
}
rig.rightHand.transform.position = Head.TransformPoint(defaultRightHandPosition);
rig.rightHand.transform.rotation = Head.rotation * defaultLeftHandRotation;
if (grabHandDistance != 0)
{
grabberHandPositionHandled = true;
rig.leftHand.transform.position = mouseRay.origin + mouseRay.direction * grabHandDistance;
}
if (!grabberHandPositionHandled)
{
rig.leftHand.transform.position = Head.TransformPoint(defaultLeftHandPosition) + mouseRay.direction * HAND_RANGE;
}
if (!beamerRotationHandled)
{
rig.leftHand.transform.LookAt(mouseRay.origin + mouseRay.direction * 2f);
rig.leftHand.transform.Rotate(-40, 0, 0);
}
}
}
}