/* * 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 Oculus.Interaction.Input; using System.Collections.Generic; using UnityEngine; namespace Oculus.Interaction.Samples { /// /// The MRPassThroughVisulaize is used when in MR passthrough is toggled on the hand will not have /// an overlay of a 3d object hand overlaying the hand. When the hand overlaps a 3d object or a /// canvas it will then apply 3d in game hand overlay over the hand so that details are not lost /// or confused when interacting with 3d objects. /// public class MRPassThroughHandVisualize : MonoBehaviour { [SerializeField] private List _eyeAnchors; [SerializeField] private HandVisual _handVisual; [Header("Raycast Properties")] [SerializeField] private LayerMask _layer; [SerializeField] private float _sphereRadius; [SerializeField] private float _castDistance; [Header("Material Properties")] [SerializeField] private MaterialPropertyBlockEditor[] _handMaterialPropertyBlocks; [SerializeField] private float _opacity; [SerializeField] private float _outlineOpacity; [SerializeField] private float _animationSpeed; private float _currentOpacity; private float _currentOutlineOpacity; private readonly int _opacityId = Shader.PropertyToID("_Opacity"); private readonly int _outlineOpacityId = Shader.PropertyToID("_OutlineOpacity"); private (Vector3, float) _palmTarget; private readonly HandJointId[] _handJointTargets = new HandJointId[] { HandJointId.HandIndex2, HandJointId.HandIndex3, HandJointId.HandThumb2, HandJointId.HandThumb3, HandJointId.HandMiddle2, HandJointId.HandMiddle3, HandJointId.HandRing2, HandJointId.HandRing3, HandJointId.HandPinky2, HandJointId.HandPinky3, }; private Ray[] _eyeRays; private bool _started = false; private void Start() { this.BeginStart(ref _started); this.AssertField(_handVisual, nameof(_handVisual)); this.AssertCollectionField(_handMaterialPropertyBlocks, nameof(_handMaterialPropertyBlocks)); this.EndStart(ref _started); _eyeRays = new Ray[_eyeAnchors.Count]; _currentOpacity = _opacity; _currentOutlineOpacity = _outlineOpacity; var palmJoints = new List(){ _handVisual.GetJointPose(HandJointId.HandWristRoot, Space.World).position, _handVisual.GetJointPose(HandJointId.HandThumb1, Space.World).position, _handVisual.GetJointPose(HandJointId.HandIndex1, Space.World).position, _handVisual.GetJointPose(HandJointId.HandMiddle1, Space.World).position, _handVisual.GetJointPose(HandJointId.HandRing1, Space.World).position, _handVisual.GetJointPose(HandJointId.HandPinky1, Space.World).position, }; var palmCenter = Vector3.zero; foreach (var origin in palmJoints) { palmCenter += origin; } palmCenter *= (1.0f / (float)palmJoints.Count); var WristTransform = _handVisual.GetTransformByHandJointId(HandJointId.HandWristRoot); var palmCenterWrist = WristTransform.InverseTransformPoint(palmCenter); var maxDistance = 0.0f; foreach (var origin in palmJoints) { maxDistance = Mathf.Max(maxDistance, Vector3.Distance(palmCenter, origin)); } _palmTarget = (palmCenterWrist, maxDistance * 0.65f); } private bool SphereCast(Vector3 target, float radius) { for (int i = 0; i < _eyeAnchors.Count; i++) { var AnchorPosition = _eyeAnchors[i].position; var AnchorDirection = (target - AnchorPosition).normalized; _eyeRays[i] = new Ray(AnchorPosition, AnchorDirection); } foreach (var ray in _eyeRays) { if (Physics.SphereCast(ray, radius, _castDistance, _layer)) { return true; } } return false; } private bool SphereCastAllTargets() { var WristTransform = _handVisual.GetTransformByHandJointId(HandJointId.HandWristRoot); var PalmCenter = WristTransform.TransformPoint(_palmTarget.Item1); if (SphereCast(PalmCenter, _palmTarget.Item2)) { return true; } foreach (var joint in _handJointTargets) { var pose = _handVisual.GetJointPose(joint, Space.World); if (SphereCast(pose.position, _sphereRadius)) { return true; } } return false; } private void UpdateMaterialPropertyBlock(bool sphereCastHit) { var targetOpacity = sphereCastHit ? _opacity : 0.0f; var targetOutlineOpacity = sphereCastHit ? _outlineOpacity : 0.0f; var animParam = _animationSpeed * Time.deltaTime; _currentOpacity = Mathf.Lerp(_currentOpacity, targetOpacity, animParam); _currentOutlineOpacity = Mathf.Lerp(_currentOutlineOpacity, targetOutlineOpacity, animParam); foreach (var handMaterialPropertyBlock in _handMaterialPropertyBlocks) { handMaterialPropertyBlock.MaterialPropertyBlock.SetFloat(_opacityId, _currentOpacity); handMaterialPropertyBlock.MaterialPropertyBlock.SetFloat(_outlineOpacityId, _currentOutlineOpacity); } } private void Update() { if (MRPassthrough.PassThrough.IsPassThroughOn) { if (_eyeAnchors == null || _handVisual == null) { return; } UpdateMaterialPropertyBlock(SphereCastAllTargets()); } else { foreach (var handMaterialPropertyBlock in _handMaterialPropertyBlocks) { handMaterialPropertyBlock.MaterialPropertyBlock.SetFloat(_opacityId, _opacity); handMaterialPropertyBlock.MaterialPropertyBlock.SetFloat(_outlineOpacityId, _outlineOpacity); } } } } }