Phillipes_Fablab/Assets/Oculus/Interaction/Runtime/Scripts/Input/Hands/HandPhysicsCapsules.cs
2023-01-09 11:46:20 +01:00

341 lines
11 KiB
C#

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Input
{
public class HandPhysicsCapsules : MonoBehaviour
{
[SerializeField, Interface(typeof(IHandVisual))]
private MonoBehaviour _handVisual;
private IHandVisual HandVisual;
[Space]
[SerializeField]
[Tooltip("Capsules will be generated as triggers")]
private bool _asTriggers = false;
[SerializeField]
[Tooltip("Capsules will be generated in this Layer")]
private int _useLayer = 0;
[SerializeField]
[Tooltip("Capsules reaching this joint will not be generated")]
private HandFingerJointFlags _mask = HandFingerJointFlags.All;
private Action _whenCapsulesGenerated = delegate { };
public event Action WhenCapsulesGenerated
{
add
{
_whenCapsulesGenerated += value;
if (_capsulesGenerated)
{
value.Invoke();
}
}
remove
{
_whenCapsulesGenerated -= value;
}
}
private Transform _rootTransform;
public Transform RootTransform => _rootTransform;
private List<BoneCapsule> _capsules;
public IList<BoneCapsule> Capsules { get; private set; }
private JointsRadiusFeature _jointsradius;
private Rigidbody[] _rigidbodies;
private bool _capsulesAreActive;
private bool _capsulesGenerated;
protected bool _started;
#region Editor events
protected virtual void Reset()
{
_useLayer = this.gameObject.layer;
this.TryGetComponent(out HandVisual);
}
#endregion
protected virtual void Awake()
{
HandVisual = _handVisual as IHandVisual;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
Assert.IsNotNull(HandVisual, $"{nameof(HandPhysicsCapsules)} needs a {nameof(HandVisual)}");
HandVisual.Hand.TryGetAspect(out _jointsradius);
Assert.IsNotNull(_jointsradius, $"{nameof(HandPhysicsCapsules)} Hand needs a {nameof(_jointsradius)}");
GenerateCapsules();
this.EndStart(ref _started);
}
private void GenerateCapsules()
{
_rigidbodies = new Rigidbody[(int)HandJointId.HandMaxSkinnable];
Transform _holder = new GameObject("Capsules").transform;
_holder.SetParent(transform, false);
_holder.localPosition = Vector3.zero;
_holder.localRotation = Quaternion.identity;
_holder.gameObject.layer = _useLayer;
int capsulesCount = Constants.NUM_HAND_JOINTS;
_capsules = new List<BoneCapsule>(capsulesCount);
Capsules = _capsules.AsReadOnly();
for (int i = (int)HandJointId.HandThumb0; i < (int)HandJointId.HandEnd; ++i)
{
HandJointId currentJoint = (HandJointId)i;
HandJointId parentJoint = HandJointUtils.JointParentList[i];
if (parentJoint == HandJointId.Invalid
|| ((1 << (int)currentJoint) & (int)_mask) == 0)
{
continue;
}
Vector3 boneEnd = HandVisual.GetJointPose(currentJoint, Space.World).position;
if (!TryGetJointRigidbody(parentJoint, out Rigidbody body))
{
Pose parentPose = HandVisual.GetJointPose(parentJoint, Space.World);
body = CreateJointRigidbody(parentJoint, _holder, parentPose);
}
string boneName = $"{parentJoint}-{currentJoint} CapsuleCollider";
float boneRadius = _jointsradius.GetJointRadius(parentJoint);
float offset = currentJoint >= HandJointId.HandMaxSkinnable ? -boneRadius
: parentJoint == HandJointId.HandStart ? boneRadius
: 0f;
CapsuleCollider collider = CreateCollider(boneName,
body.transform, boneEnd, boneRadius, offset);
BoneCapsule capsule = new BoneCapsule(parentJoint, currentJoint, body, collider);
_capsules.Add(capsule);
}
IgnoreSelfCollisions();
_capsulesAreActive = false;
_capsulesGenerated = true;
_whenCapsulesGenerated.Invoke();
}
private void IgnoreSelfCollisions()
{
for (int i = 0; i < _capsules.Count; i++)
{
for (int j = i + 1; j < _capsules.Count; j++)
{
Physics.IgnoreCollision(_capsules[i].CapsuleCollider, _capsules[j].CapsuleCollider);
}
}
}
private bool TryGetJointRigidbody(HandJointId joint, out Rigidbody body)
{
body = _rigidbodies[(int)joint];
return body != null;
}
private Rigidbody CreateJointRigidbody(HandJointId joint,
Transform holder, Pose pose)
{
string name = $"{joint} Rigidbody";
Rigidbody rigidbody = new GameObject(name)
.AddComponent<Rigidbody>();
rigidbody.mass = 1.0f;
rigidbody.isKinematic = true;
rigidbody.useGravity = false;
rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
rigidbody.transform.SetParent(holder, false);
rigidbody.transform.SetPose(pose);
rigidbody.gameObject.SetActive(false);
rigidbody.gameObject.layer = _useLayer;
_rigidbodies[(int)joint] = rigidbody;
return rigidbody;
}
private CapsuleCollider CreateCollider(string name,
Transform from, Vector3 to, float radius, float offset)
{
CapsuleCollider collider = new GameObject(name)
.AddComponent<CapsuleCollider>();
collider.isTrigger = _asTriggers;
Vector3 boneDirection = to - from.position;
Quaternion boneRotation = Quaternion.LookRotation(boneDirection);
float boneLength = boneDirection.magnitude;
boneLength -= Mathf.Abs(offset);
collider.radius = radius;
collider.height = boneLength + radius * 2.0f;
collider.direction = 2;
collider.center = Vector3.forward * (boneLength * 0.5f + Mathf.Max(0f, offset));
Transform capsuleTransform = collider.transform;
capsuleTransform.SetParent(from, false);
capsuleTransform.SetPositionAndRotation(from.position, boneRotation);
collider.gameObject.layer = _useLayer;
return collider;
}
protected virtual void OnEnable()
{
if (_started)
{
HandVisual.WhenHandVisualUpdated += HandleHandVisualUpdated;
}
}
protected virtual void OnDisable()
{
if (_started)
{
HandVisual.WhenHandVisualUpdated -= HandleHandVisualUpdated;
EnableRigidbodies(false);
}
}
protected virtual void LateUpdate()
{
if (_capsulesAreActive && !HandVisual.IsVisible)
{
EnableRigidbodies(false);
}
}
private void EnableRigidbodies(bool enable)
{
if (_rigidbodies != null
|| enable == _capsulesAreActive)
{
return;
}
foreach (Rigidbody body in _rigidbodies)
{
body.gameObject.SetActive(enable);
}
_capsulesAreActive = enable;
}
private void HandleHandVisualUpdated()
{
_capsulesAreActive = HandVisual.IsVisible;
for (int i = 0; i < (int)HandJointId.HandMaxSkinnable; ++i)
{
Rigidbody jointbody = _rigidbodies[i];
if (jointbody == null)
{
continue;
}
GameObject jointGO = jointbody.gameObject;
if (_capsulesAreActive)
{
Pose bonePose = HandVisual.GetJointPose((HandJointId)i, Space.World);
bool justActivated = false;
if (!jointGO.activeSelf)
{
jointGO.SetActive(true);
justActivated = true;
}
if (_asTriggers)
{
jointbody.transform.SetPositionAndRotation(bonePose.position, bonePose.rotation);
}
else if (justActivated)
{
jointbody.position = bonePose.position;
jointbody.rotation = bonePose.rotation;
}
else
{
jointbody.MovePosition(bonePose.position);
jointbody.MoveRotation(bonePose.rotation);
}
}
else if (jointGO.activeSelf)
{
jointGO.SetActive(false);
}
}
}
#region Inject
public void InjectAllOVRHandPhysicsCapsules(IHandVisual handVisual,
bool asTriggers, int useLayer)
{
InjectHandVisual(handVisual);
InjectAsTriggers(asTriggers);
InjectUseLayer(useLayer);
}
public void InjectHandVisual(IHandVisual handVisual)
{
_handVisual = handVisual as MonoBehaviour;
HandVisual = handVisual;
}
public void InjectAsTriggers(bool asTriggers)
{
_asTriggers = asTriggers;
}
public void InjectUseLayer(int useLayer)
{
_useLayer = useLayer;
}
#endregion
}
public class BoneCapsule
{
public HandJointId StartJoint { get; private set; }
public HandJointId EndJoint { get; private set; }
public Rigidbody CapsuleRigidbody { get; private set; }
public CapsuleCollider CapsuleCollider { get; private set; }
public BoneCapsule(HandJointId fromJoint, HandJointId toJoint, Rigidbody body, CapsuleCollider collider)
{
StartJoint = fromJoint;
EndJoint = toJoint;
CapsuleRigidbody = body;
CapsuleCollider = collider;
}
}
}