Initialer Upload neues Unity-Projekt

This commit is contained in:
Daniel Ocks
2025-07-21 09:11:14 +02:00
commit eeca72985b
14558 changed files with 1508140 additions and 0 deletions

View File

@ -0,0 +1,201 @@
/*
* 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 UnityEngine.Assertions;
using UnityEngine;
using Oculus.Interaction;
namespace Oculus.Interaction
{
public class AudioPhysics : MonoBehaviour
{
[Tooltip("Add a reference to the rigidbody on this gameobject.")]
[SerializeField]
private Rigidbody _rigidbody;
[Tooltip("Reference an audio trigger instance for soft and hard collisions.")]
[SerializeField]
private ImpactAudio _impactAudioEvents;
[Tooltip("Collisions below this value will play a soft audio event, and collisions above will play a hard audio event.")]
[Range(0.0f, 8.0f)]
[SerializeField]
private float _velocitySplit = 1.0f;
[Tooltip("Collisions below this value will be ignored and will not play audio.")]
[Range(0.0f, 2.0f)]
[SerializeField]
private float _minimumVelocity = 0;
[Tooltip("The shortest amount of time in seconds between collisions. Used to cull multiple fast collision events.")]
[Range(0.0f, 2.0f)]
[SerializeField]
private float _timeBetweenCollisions = 0.2f;
[Tooltip("By default (false), when two physics objects collide with physics audio components, we only play the one with the higher velocity." +
"Setting this to true will allow both impacts to play.")]
[SerializeField]
private bool _allowMultipleCollisions = false;
private float _timeAtLastCollision = 0f;
protected bool _started = false;
private CollisionEvents _collisionEvents;
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertField(_impactAudioEvents.SoftCollisionSound, nameof(_impactAudioEvents.SoftCollisionSound));
this.AssertField(_impactAudioEvents.HardCollisionSound, nameof(_impactAudioEvents.HardCollisionSound));
this.AssertField(_rigidbody, nameof(_rigidbody));
_collisionEvents = _rigidbody.gameObject.AddComponent<CollisionEvents>();
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
_collisionEvents.WhenCollisionEnter += HandleCollisionEnter;
}
}
protected virtual void OnDisable()
{
if (_started)
{
_collisionEvents.WhenCollisionEnter -= HandleCollisionEnter;
}
}
protected virtual void OnDestroy()
{
if (_collisionEvents != null)
{
Destroy(_collisionEvents);
}
}
private void HandleCollisionEnter(Collision collision)
{
TryPlayCollisionAudio(collision, _rigidbody);
}
private void TryPlayCollisionAudio(Collision collision, Rigidbody rigidbody)
{
// capture the velocity of the impact. TODO lets see if there is another way to get this data that might be better
float collisionMagnitude = collision.relativeVelocity.sqrMagnitude;
// make sure the gameobject we collided with is valid
if (collision.collider.gameObject == null)
{
return;
}
// cull all the collisions we want to ignore
// ignore any physics impacts that happen too close together
float deltaTime = Time.time - _timeAtLastCollision;
if (_timeBetweenCollisions > deltaTime)
{
return;
}
// only play a single sound when two physics objects collide
if (_allowMultipleCollisions == false)
{
// check the object we collided with if it has an audio physics component
if (collision.collider.gameObject.TryGetComponent(out AudioPhysics otherAudioPhysicsObj))
{
if (GetObjectVelocity(otherAudioPhysicsObj) > GetObjectVelocity(this))
{
return;
}
}
}
// update time variable for impacts too close together
_timeAtLastCollision = Time.time;
// play the audio
PlayCollisionAudio(_impactAudioEvents, collisionMagnitude);
}
private void PlayCollisionAudio(ImpactAudio impactAudio, float magnitude)
{
// early out if there is no physics audio available
if (impactAudio.HardCollisionSound == null || impactAudio.SoftCollisionSound == null)
{
return;
}
// cull audio by minimum velocity value
if (magnitude > _minimumVelocity)
{
// play the hard or soft sound determined by the slider
if (magnitude > _velocitySplit && impactAudio.HardCollisionSound != null)
{
PlaySoundOnAudioTrigger(impactAudio.HardCollisionSound);
}
else
{
PlaySoundOnAudioTrigger(impactAudio.SoftCollisionSound);
}
}
}
private static float GetObjectVelocity(AudioPhysics target)
{
return target._rigidbody.velocity.sqrMagnitude;
}
private void PlaySoundOnAudioTrigger(AudioTrigger audioTrigger)
{
if (audioTrigger != null)
{
audioTrigger.PlayAudio();
}
}
public class CollisionEvents : MonoBehaviour
{
public event Action<Collision> WhenCollisionEnter = delegate { };
private void OnCollisionEnter(Collision collision)
{
WhenCollisionEnter.Invoke(collision);
}
}
}
[Serializable]
public struct ImpactAudio
{
[Tooltip("Hard collision sound will play when impact velocity is above the velocity split value.")]
[SerializeField]
private AudioTrigger _hardCollisionSound;
[Tooltip("Soft collision sound will play when impact velocity is below the velocity split value.")]
[SerializeField]
private AudioTrigger _softCollisionSound;
public AudioTrigger HardCollisionSound => _hardCollisionSound;
public AudioTrigger SoftCollisionSound => _softCollisionSound;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c94ad25aabf9b34eb79cd433132dee6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,259 @@
/*
* 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 UnityEngine;
using Random = UnityEngine.Random;
namespace Oculus.Interaction
{
public class AudioTrigger : MonoBehaviour
{
[SerializeField]
private AudioSource _audioSource;
[Tooltip("Audio clip arrays with a value greater than 1 will have randomized playback.")]
[SerializeField]
private AudioClip[] _audioClips;
[Tooltip("Volume set here will override the volume set on the attached sound source component.")]
[Range(0f, 1f)]
[SerializeField]
private float _volume = 0.7f;
public float Volume
{
get
{
return _volume;
}
set
{
_volume = value;
}
}
[Tooltip("Check the 'Use Random Range' bool to and adjust the min and max slider values for randomized volume level playback.")]
[SerializeField]
private MinMaxPair _volumeRandomization;
public MinMaxPair VolumeRandomization
{
get
{
return _volumeRandomization;
}
set
{
_volumeRandomization = value;
}
}
[Tooltip("Pitch set here will override the volume set on the attached sound source component.")]
[SerializeField]
[Range(-3f, 3f)]
[Space(10)]
private float _pitch = 1f;
public float Pitch
{
get
{
return _pitch;
}
set
{
_pitch = value;
}
}
[Tooltip("Check the 'Use Random Range' bool to and adjust the min and max slider values for randomized volume level playback.")]
[SerializeField]
private MinMaxPair _pitchRandomization;
public MinMaxPair PitchRandomization
{
get
{
return _pitchRandomization;
}
set
{
_pitchRandomization = value;
}
}
[Tooltip("True by default. Set to false for sounds to bypass the spatializer plugin. Will override settings on attached audio source.")]
[SerializeField]
[Space(10)]
private bool _spatialize = true;
public bool Spatialize
{
get
{
return _spatialize;
}
set
{
_spatialize = value;
}
}
[Tooltip("False by default. Set to true to enable looping on this sound. Will override settings on attached audio source.")]
[SerializeField]
private bool _loop = false;
public bool Loop
{
get
{
return _loop;
}
set
{
_loop = value;
}
}
[Tooltip("100% by default. Sets likelyhood sample will actually play when called")]
[SerializeField]
private float _chanceToPlay = 100;
public float ChanceToPlay
{
get
{
return _chanceToPlay;
}
set
{
_chanceToPlay = value;
}
}
[Tooltip("If enabled, audio will play automatically when this gameobject is enabled")]
[SerializeField, Optional]
private bool _playOnStart = false;
private int _previousAudioClipIndex = -1;
protected virtual void Start()
{
if (_audioSource == null)
{
_audioSource = gameObject.GetComponent<AudioSource>();
}
this.AssertField(_audioSource, nameof(_audioSource));
this.AssertCollectionField(_audioClips, nameof(_audioClips));
// Play audio on start if enabled
if (_playOnStart)
{
PlayAudio();
}
}
public void PlayAudio()
{
// Check if random chance is set
float pick = Random.Range(0.0f, 100.0f);
if (_chanceToPlay < 100 && pick > _chanceToPlay)
{
return;
}
// Check if volume randomization is set
if (_volumeRandomization.UseRandomRange == true)
{
_audioSource.volume = Random.Range(_volumeRandomization.Min, _volumeRandomization.Max);
}
else
{
_audioSource.volume = _volume;
}
// Check if pitch randomization is set
if (_pitchRandomization.UseRandomRange == true)
{
_audioSource.pitch = Random.Range(_pitchRandomization.Min, _pitchRandomization.Max);
}
else
{
_audioSource.pitch = _pitch;
}
_audioSource.spatialize = _spatialize;
_audioSource.loop = _loop;
_audioSource.clip = RandomClipWithoutRepeat();
_audioSource.Play();
}
/// <summary>
/// Choose a random clip without repeating the last clip
/// </summary>
private AudioClip RandomClipWithoutRepeat()
{
if (_audioClips.Length == 1)
{
return _audioClips[0];
}
int randomOffset = Random.Range(1, _audioClips.Length);
int index = (_previousAudioClipIndex + randomOffset) % _audioClips.Length;
_previousAudioClipIndex = index;
return _audioClips[index];
}
#region Inject
public void InjectAllAudioTrigger(AudioSource audioSource, AudioClip[] audioClips)
{
InjectAudioSource(audioSource);
InjectAudioClips(audioClips);
}
public void InjectAudioSource(AudioSource audioSource)
{
_audioSource = audioSource;
}
public void InjectAudioClips(AudioClip[] audioClips)
{
_audioClips = audioClips;
}
public void InjectOptionalPlayOnStart(bool playOnStart)
{
_playOnStart = playOnStart;
}
#endregion
}
[System.Serializable]
public struct MinMaxPair
{
[SerializeField]
private bool _useRandomRange;
[SerializeField]
private float _min;
[SerializeField]
private float _max;
public bool UseRandomRange => _useRandomRange;
public float Min => _min;
public float Max => _max;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 925ef87c5bafc37469a2f7ec825dee4b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 22ba64cf9e2c3da45a1a9c295d54f305
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,135 @@
/*
* 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 UnityEngine;
using Oculus.Interaction.Body.Input;
using Oculus.Interaction.Body.PoseDetection;
namespace Oculus.Interaction.Body.Samples
{
public class BodyPoseSwitcher : MonoBehaviour, IBodyPose
{
public enum PoseSource
{
PoseA,
PoseB,
}
public event Action WhenBodyPoseUpdated = delegate { };
[SerializeField, Interface(typeof(IBodyPose))]
private UnityEngine.Object _poseA;
private IBodyPose PoseA;
[SerializeField, Interface(typeof(IBodyPose))]
private UnityEngine.Object _poseB;
private IBodyPose PoseB;
[SerializeField]
private PoseSource _source = PoseSource.PoseA;
public ISkeletonMapping SkeletonMapping => GetPose().SkeletonMapping;
public bool GetJointPoseFromRoot(BodyJointId bodyJointId, out Pose pose) =>
GetPose().GetJointPoseFromRoot(bodyJointId, out pose);
public bool GetJointPoseLocal(BodyJointId bodyJointId, out Pose pose) =>
GetPose().GetJointPoseLocal(bodyJointId, out pose);
protected bool _started = false;
public PoseSource Source
{
get { return _source; }
set
{
bool changed = value != _source;
_source = value;
if (changed)
{
WhenBodyPoseUpdated.Invoke();
}
}
}
public void UsePoseA()
{
Source = PoseSource.PoseA;
}
public void UsePoseB()
{
Source = PoseSource.PoseB;
}
protected virtual void Awake()
{
PoseA = _poseA as IBodyPose;
PoseB = _poseB as IBodyPose;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertField(PoseA, nameof(PoseA));
this.AssertField(PoseB, nameof(PoseB));
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
PoseA.WhenBodyPoseUpdated += () => OnPoseUpdated(PoseSource.PoseA);
PoseB.WhenBodyPoseUpdated += () => OnPoseUpdated(PoseSource.PoseB);
}
}
protected virtual void OnDisable()
{
if (_started)
{
PoseA.WhenBodyPoseUpdated -= () => OnPoseUpdated(PoseSource.PoseA);
PoseB.WhenBodyPoseUpdated -= () => OnPoseUpdated(PoseSource.PoseB);
}
}
private void OnPoseUpdated(PoseSource source)
{
if (source == Source)
{
WhenBodyPoseUpdated.Invoke();
}
}
private IBodyPose GetPose()
{
switch (Source)
{
default:
case PoseSource.PoseA:
return PoseA;
case PoseSource.PoseB:
return PoseB;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7de5613aa5f224248b2cef5814c0a1e4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,115 @@
/*
* 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 UnityEngine;
using Oculus.Interaction.Body.Input;
using Oculus.Interaction.Body.PoseDetection;
using System.Collections.Generic;
namespace Oculus.Interaction.Body.Samples
{
public class LockedBodyPose : MonoBehaviour, IBodyPose
{
private static readonly Pose HIP_OFFSET = new Pose()
{
position = new Vector3(0f, 0.923987f, 0f),
rotation = Quaternion.Euler(0, 270, 270),
};
public event Action WhenBodyPoseUpdated = delegate { };
[Tooltip("The body pose to be locked")]
[SerializeField, Interface(typeof(IBodyPose))]
private UnityEngine.Object _pose;
private IBodyPose Pose;
[Tooltip("The body pose will be locked relative to this " +
"joint at the specified offset.")]
[SerializeField]
private BodyJointId _referenceJoint = BodyJointId.Body_Hips;
[Tooltip("The reference joint will be placed at " +
"this offset from the root.")]
[SerializeField]
private Pose _referenceOffset = HIP_OFFSET;
protected bool _started = false;
private Dictionary<BodyJointId, Pose> _lockedPoses;
public ISkeletonMapping SkeletonMapping => Pose.SkeletonMapping;
public bool GetJointPoseLocal(BodyJointId bodyJointId, out Pose pose) =>
Pose.GetJointPoseLocal(bodyJointId, out pose);
public bool GetJointPoseFromRoot(BodyJointId bodyJointId, out Pose pose) =>
_lockedPoses.TryGetValue(bodyJointId, out pose);
private void UpdateLockedBodyPose()
{
_lockedPoses.Clear();
for (int i = 0; i < Constants.NUM_BODY_JOINTS; ++i)
{
BodyJointId jointId = (BodyJointId)i;
if (Pose.GetJointPoseFromRoot(_referenceJoint, out Pose referencePose) &&
Pose.GetJointPoseFromRoot(jointId, out Pose jointPose))
{
ref Pose offset = ref referencePose;
PoseUtils.Invert(ref offset);
PoseUtils.Multiply(offset, jointPose, ref jointPose);
PoseUtils.Multiply(_referenceOffset, jointPose, ref jointPose);
_lockedPoses[jointId] = jointPose;
}
}
WhenBodyPoseUpdated.Invoke();
}
protected virtual void Awake()
{
_lockedPoses = new Dictionary<BodyJointId, Pose>();
Pose = _pose as IBodyPose;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertField(Pose, nameof(Pose));
UpdateLockedBodyPose();
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
Pose.WhenBodyPoseUpdated += UpdateLockedBodyPose;
}
}
protected virtual void OnDisable()
{
if (_started)
{
Pose.WhenBodyPoseUpdated -= UpdateLockedBodyPose;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91ff0dbc3add03444934d0bd81a06242
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,91 @@
/*
* 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 UnityEngine;
using UnityEngine.Events;
using TMPro;
namespace Oculus.Interaction.Body.Samples
{
public class PoseCaptureCountdown : MonoBehaviour
{
[SerializeField]
private UnityEvent _timerStart = new UnityEvent();
[SerializeField]
private UnityEvent _timerSecondTick = new UnityEvent();
[SerializeField]
private UnityEvent _timeUp = new UnityEvent();
[SerializeField]
private TextMeshProUGUI _countdownText;
[SerializeField]
private string _poseText = "Capture Pose";
[SerializeField]
private float duration = 10f;
[SerializeField, Optional]
private Renderer _renderer;
[SerializeField, Optional]
private Color _resetColor;
private float _timer = 0f;
public void Restart()
{
_timer = duration;
_timerStart.Invoke();
if (_renderer != null)
{
_renderer.material.color = _resetColor;
}
}
private void Update()
{
bool wasCounting = _timer > 0f;
if (wasCounting)
{
int prevSecond = (int)_timer;
_timer -= Time.unscaledDeltaTime;
if ((int)_timer < prevSecond)
{
_timerSecondTick.Invoke();
}
}
bool isCounting = _timer > 0f;
if (wasCounting && !isCounting)
{
_timer = 0f;
_timeUp.Invoke();
_countdownText.text = _poseText;
}
else if (isCounting)
{
_countdownText.text = _timer.ToString("#0.0");
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a2c3d97f3738fb45a0b0679a6b88ee4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,138 @@
/*
* 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 UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;
namespace Oculus.Interaction.Samples
{
public class CarouselView : MonoBehaviour
{
[SerializeField]
private RectTransform _viewport;
[SerializeField]
private RectTransform _content;
[SerializeField]
private AnimationCurve _easeCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
[SerializeField, Optional]
private GameObject _emptyCarouselVisuals;
public int CurrentChildIndex => _currentChildIndex;
public RectTransform ContentArea => _content;
private int _currentChildIndex = 0;
private float _scrollVal = 0;
protected virtual void Start()
{
this.AssertField(_viewport, nameof(_viewport));
this.AssertField(_content, nameof(_content));
}
public void ScrollRight()
{
if (_content.childCount <= 1)
{
return;
}
else if (_currentChildIndex > 0)
{
RectTransform currentChild = GetCurrentChild();
_content.GetChild(0).SetAsLastSibling();
LayoutRebuilder.ForceRebuildLayoutImmediate(_content);
ScrollToChild(currentChild, 1);
}
else
{
_currentChildIndex++;
}
_scrollVal = Time.time;
}
public void ScrollLeft()
{
if (_content.childCount <= 1)
{
return;
}
else if (_currentChildIndex < _content.childCount - 1)
{
RectTransform currentChild = GetCurrentChild();
_content.GetChild(_content.childCount - 1).SetAsFirstSibling();
LayoutRebuilder.ForceRebuildLayoutImmediate(_content);
ScrollToChild(currentChild, 1);
}
else
{
_currentChildIndex--;
}
_scrollVal = Time.time;
}
private RectTransform GetCurrentChild()
{
return _content.GetChild(_currentChildIndex) as RectTransform;
}
private void ScrollToChild(RectTransform child, float amount01)
{
if (child == null)
{
return;
}
amount01 = Mathf.Clamp01(amount01);
Vector3 viewportCenter = _viewport.TransformPoint(_viewport.rect.center);
Vector3 imageCenter = child.TransformPoint(child.rect.center);
Vector3 offset = imageCenter - viewportCenter;
if (offset.sqrMagnitude > float.Epsilon)
{
Vector3 targetPosition = _content.position - offset;
float lerp = Mathf.Clamp01(_easeCurve.Evaluate(amount01));
_content.position = Vector3.Lerp(_content.position, targetPosition, lerp);
}
}
protected virtual void Update()
{
_currentChildIndex = Mathf.Clamp(
_currentChildIndex, 0, _content.childCount - 1);
bool hasImages = _content.childCount > 0;
if (hasImages)
{
RectTransform currentImage = GetCurrentChild();
ScrollToChild(currentImage, Time.time - _scrollVal);
}
if (_emptyCarouselVisuals != null)
{
_emptyCarouselVisuals.SetActive(!hasImages);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80e08504c1d9ec34fbdd09d754ea8b5d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,65 @@
/*
* 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 UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Samples
{
public class ColorChanger : MonoBehaviour
{
[SerializeField]
private Renderer _target;
private Material _targetMaterial;
private Color _savedColor;
private float _lastHue = 0;
public void NextColor()
{
_lastHue = (_lastHue + 0.3f) % 1f;
Color newColor = Color.HSVToRGB(_lastHue, 0.8f, 0.8f);
_targetMaterial.color = newColor;
}
public void Save()
{
_savedColor = _targetMaterial.color;
}
public void Revert()
{
_targetMaterial.color = _savedColor;
}
protected virtual void Start()
{
this.AssertField(_target, nameof(_target));
_targetMaterial = _target.material;
this.AssertField(_targetMaterial, nameof(_targetMaterial));
_savedColor = _targetMaterial.color;
}
private void OnDestroy()
{
Destroy(_targetMaterial);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4a2bba66be8bd5409a1b82f1ce54853
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,91 @@
/*
* 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 UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Events;
namespace Oculus.Interaction.Samples
{
/// <summary>
/// Performs a callback after a countdown timer has elapsed.
/// The countdown can be enabled or disabled by an external party.
/// </summary>
public class CountdownTimer : MonoBehaviour
{
[SerializeField, Min(0)]
private float _countdownTime = 1.0f;
[SerializeField]
private bool _countdownOn = false;
[SerializeField]
private UnityEvent _callback;
[SerializeField]
private UnityEvent<float> _progressCallback;
private float _countdownTimer;
public bool CountdownOn
{
get => _countdownOn;
set
{
if (value)
{
if (!_countdownOn)
{
_countdownTimer = _countdownTime;
}
}
_countdownOn = value;
}
}
private void Awake()
{
Assert.IsTrue(_countdownTime >= 0, "Countdown Time must be positive.");
}
private void Update()
{
if (!_countdownOn || _countdownTimer < 0)
{
_progressCallback.Invoke(0);
return;
}
_countdownTimer -= Time.deltaTime;
if (_countdownTimer < 0f)
{
_countdownTimer = -1f;
_callback.Invoke();
_progressCallback.Invoke(1);
return;
}
_progressCallback.Invoke(1 - _countdownTimer / _countdownTime);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80adaa84dcdec2b41981fa223dab63f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,49 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples
{
public class EnableTargetOnStart : MonoBehaviour
{
public MonoBehaviour[] _components;
public GameObject[] _gameObjects;
void Start()
{
if (_components != null)
{
foreach (MonoBehaviour target in _components)
{
target.enabled = true;
}
}
if (_gameObjects != null)
{
foreach (GameObject target in _gameObjects)
{
target.SetActive(true);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2e81f6da227209c49bdc78260f44e85b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
/*
* 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 TMPro;
using UnityEngine;
namespace Oculus.Interaction.Samples
{
public class FadeTextAfterActive : MonoBehaviour
{
[SerializeField] float _fadeOutTime;
[SerializeField] TextMeshPro _text;
float _timeLeft;
protected virtual void OnEnable()
{
_timeLeft = _fadeOutTime;
_text.fontMaterial.color = new Color(_text.color.r, _text.color.g, _text.color.b, 255);
}
protected virtual void Update()
{
if (_timeLeft <= 0)
{
return;
}
float percentDone = 1 - _timeLeft / _fadeOutTime;
float alpha = Mathf.SmoothStep(1, 0, percentDone);
_text.color = new Color(_text.color.r, _text.color.g, _text.color.b, alpha);
_timeLeft -= Time.deltaTime;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a540e547f62942468b7ff96e6e12bc1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: de5436acf1069264a8a66caf1eed0513
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a423463f353e4fc4285974c23bd5258e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,51 @@
/*
* 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.HandGrab.Editor;
using UnityEditor;
namespace Oculus.Interaction.HandGrab.Recorder.Editor
{
[CustomEditor(typeof(HandGrabPoseLiveRecorder))]
public class HandGrabPoseLiveRecorderEditor : UnityEditor.Editor
{
private SerializedProperty _ghostProviderProperty;
private void Awake()
{
_ghostProviderProperty = serializedObject.FindProperty("_ghostProvider");
AssignMissingGhostProvider();
}
private void AssignMissingGhostProvider()
{
if (_ghostProviderProperty.objectReferenceValue != null)
{
return;
}
if (HandGhostProviderUtils.TryGetDefaultProvider(out Visuals.HandGhostProvider provider))
{
_ghostProviderProperty.objectReferenceValue = provider;
serializedObject.ApplyModifiedProperties();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6586592911e33447986fc3d68dc1c0a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
{
"name": "Oculus.Interaction.Samples.Editor",
"rootNamespace": "",
"references": [
"GUID:48af58ae5328ff048acacd924604a804",
"GUID:2a230cb87a1d3ba4a98bdc0ddae76e6c",
"GUID:aad0989a2d3885f4e8846b6f0609300c"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0ae31ae32e3e80a48806ef598d17341c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,381 @@
/*
* 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.HandGrab.Visuals;
using Oculus.Interaction.Input;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace Oculus.Interaction.HandGrab.Recorder
{
public class HandGrabPoseLiveRecorder : MonoBehaviour, IActiveState
{
[SerializeField]
private HandGrabInteractor _leftHand;
[SerializeField]
private HandGrabInteractor _rightHand;
[SerializeField]
[Tooltip("Prototypes of the static hands (ghosts) that visualize holding poses")]
private HandGhostProvider _ghostProvider;
[SerializeField, Optional]
private TimerUIControl _timerControl;
[SerializeField, Optional]
private TMPro.TextMeshPro _delayLabel;
private RigidbodyDetector _leftDetector;
private RigidbodyDetector _rightDetector;
private WaitForSeconds _waitOneSeconds = new WaitForSeconds(1f);
private Coroutine _delayedSnapRoutine;
public UnityEvent WhenTimeStep;
public UnityEvent WhenSnapshot;
public UnityEvent WhenError;
[Space]
public UnityEvent<bool> WhenCanUndo;
public UnityEvent<bool> WhenCanRedo;
public UnityEvent WhenGrabAllowed;
public UnityEvent WhenGrabDisallowed;
private struct RecorderStep
{
public HandPose RawHandPose { get; private set; }
public Pose GrabPoint { get; private set; }
public Rigidbody Item { get; private set; }
public float HandScale { get; private set; }
public HandGrabInteractable interactable;
public RecorderStep(HandPose rawPose, Pose grabPoint, float scale, Rigidbody item)
{
this.RawHandPose = new HandPose(rawPose);
this.GrabPoint = grabPoint;
this.HandScale = scale;
this.Item = item;
interactable = null;
}
public void ClearInteractable()
{
if (interactable != null)
{
Destroy(interactable.gameObject);
}
}
}
private List<RecorderStep> _recorderSteps = new List<RecorderStep>();
private int _currentStepIndex;
private int CurrentStepIndex
{
get
{
return _currentStepIndex;
}
set
{
_currentStepIndex = value;
WhenCanUndo?.Invoke(_currentStepIndex >= 0);
WhenCanRedo?.Invoke(_currentStepIndex + 1 < _recorderSteps.Count);
}
}
public bool Active => _grabbingEnabled;
private bool _grabbingEnabled = true;
private void Awake()
{
_leftHand.InjectOptionalActiveState(this);
_rightHand.InjectOptionalActiveState(this);
}
private void Start()
{
ClearSnapshot();
_leftDetector = _leftHand.Rigidbody.gameObject.AddComponent<RigidbodyDetector>();
_leftDetector.IgnoreBody(_rightHand.Rigidbody);
_rightDetector = _rightHand.Rigidbody.gameObject.AddComponent<RigidbodyDetector>();
_rightDetector.IgnoreBody(_leftHand.Rigidbody);
CurrentStepIndex = -1;
EnableGrabbing(true);
}
public void Record()
{
ClearSnapshot();
if (_timerControl != null)
{
_delayedSnapRoutine = StartCoroutine(DelayedSnapshot(_timerControl.DelaySeconds));
}
else
{
TakeSnapshot();
}
}
private void ClearSnapshot()
{
if (_delayedSnapRoutine != null)
{
StopCoroutine(_delayedSnapRoutine);
_delayedSnapRoutine = null;
}
_delayLabel.text = string.Empty;
}
private IEnumerator DelayedSnapshot(int seconds)
{
for (int i = seconds; i > 0; i--)
{
_delayLabel.text = i.ToString();
WhenTimeStep?.Invoke();
yield return _waitOneSeconds;
}
if (TakeSnapshot())
{
_delayLabel.text = "<size=10>Snap!";
WhenSnapshot?.Invoke();
}
else
{
_delayLabel.text = "<size=10>Error";
WhenError?.Invoke();
}
yield return _waitOneSeconds;
_delayLabel.text = string.Empty;
}
private bool TakeSnapshot()
{
Rigidbody leftItem = FindNearestItem(_leftHand.Rigidbody, _leftDetector, out float leftDistance);
Rigidbody rightItem = FindNearestItem(_rightHand.Rigidbody, _rightDetector, out float rightDistance);
if (leftDistance < rightDistance
&& leftItem != null)
{
return Record(_leftHand.Hand, leftItem);
}
else if (rightItem != null)
{
return Record(_rightHand.Hand, rightItem);
}
Debug.LogError("No rigidbody detected near any hand");
return false;
}
private Rigidbody FindNearestItem(Rigidbody handBody, RigidbodyDetector detector, out float bestDistance)
{
Vector3 referencePoint = handBody.worldCenterOfMass;
float minDistance = float.PositiveInfinity;
Rigidbody bestItem = null;
foreach (Rigidbody item in detector.IntersectingBodies)
{
Vector3 point = item.worldCenterOfMass;
float distance = Vector3.Distance(point, referencePoint);
if (distance < minDistance)
{
minDistance = distance;
bestItem = item;
}
}
bestDistance = minDistance;
return bestItem;
}
public void Undo()
{
if (CurrentStepIndex < 0)
{
return;
}
_recorderSteps[CurrentStepIndex].ClearInteractable();
CurrentStepIndex--;
}
public void Redo()
{
if (CurrentStepIndex + 1 >= _recorderSteps.Count)
{
return;
}
CurrentStepIndex++;
RecorderStep recorderStep = _recorderSteps[CurrentStepIndex];
AddHandGrabPose(recorderStep, out recorderStep.interactable, out HandGrabPose point);
AttachGhost(point, recorderStep.HandScale);
_recorderSteps[CurrentStepIndex] = recorderStep;
}
public void EnableGrabbing(bool enable)
{
_grabbingEnabled = enable;
if (enable)
{
WhenGrabAllowed?.Invoke();
}
else
{
WhenGrabDisallowed?.Invoke();
}
}
private bool Record(IHand hand, Rigidbody item)
{
HandPose trackedHandPose = TrackedPose(hand);
if (trackedHandPose == null)
{
Debug.LogError("Tracked Pose could not be retrieved", this);
return false;
}
if (!hand.GetRootPose(out Pose handRoot))
{
Debug.LogError("Hand Root pose could not be retrieved", this);
return false;
}
Pose gripPoint = PoseUtils.DeltaScaled(item.transform, handRoot);
RecorderStep recorderStep = new RecorderStep(trackedHandPose, gripPoint, hand.Scale, item);
AddHandGrabPose(recorderStep, out recorderStep.interactable, out HandGrabPose point);
AttachGhost(point, recorderStep.HandScale);
int nextStep = CurrentStepIndex + 1;
if (nextStep < _recorderSteps.Count)
{
_recorderSteps.RemoveRange(nextStep, _recorderSteps.Count - nextStep);
}
_recorderSteps.Add(recorderStep);
CurrentStepIndex = _recorderSteps.Count - 1;
return true;
}
private HandPose TrackedPose(IHand hand)
{
if (!hand.GetJointPosesLocal(out ReadOnlyHandJointPoses localJoints))
{
return null;
}
HandPose result = new HandPose(hand.Handedness);
for (int i = 0; i < FingersMetadata.HAND_JOINT_IDS.Length; ++i)
{
HandJointId jointID = FingersMetadata.HAND_JOINT_IDS[i];
result.JointRotations[i] = localJoints[jointID].rotation;
}
return result;
}
private void AddHandGrabPose(RecorderStep recorderStep,
out HandGrabInteractable interactable, out HandGrabPose handGrabPose)
{
interactable = HandGrabUtils.CreateHandGrabInteractable(recorderStep.Item.transform);
if (recorderStep.Item.TryGetComponent(out Grabbable grabbable))
{
interactable.InjectOptionalPointableElement(grabbable);
}
var pointData = new HandGrabUtils.HandGrabPoseData()
{
handPose = recorderStep.RawHandPose,
scale = (recorderStep.HandScale / interactable.RelativeTo.lossyScale.x),
gripPose = recorderStep.GrabPoint,
};
handGrabPose = HandGrabUtils.LoadHandGrabPose(interactable, pointData);
}
private void AttachGhost(HandGrabPose point, float referenceScale)
{
if (_ghostProvider == null)
{
return;
}
HandGhost ghostPrefab = _ghostProvider.GetHand(point.HandPose.Handedness);
HandGhost ghost = GameObject.Instantiate(ghostPrefab, point.transform);
ghost.transform.localScale = Vector3.one * (referenceScale / point.transform.lossyScale.x);
ghost.SetPose(point);
}
}
public class RigidbodyDetector : MonoBehaviour
{
private HashSet<Rigidbody> _ignoredBodies = new HashSet<Rigidbody>();
public List<Rigidbody> IntersectingBodies { get; private set; } = new List<Rigidbody>();
public void IgnoreBody(Rigidbody body)
{
if (!_ignoredBodies.Contains(body))
{
_ignoredBodies.Add(body);
}
if (IntersectingBodies.Contains(body))
{
IntersectingBodies.Remove(body);
}
}
public void UnIgnoreBody(Rigidbody body)
{
if (_ignoredBodies.Contains(body))
{
_ignoredBodies.Remove(body);
}
}
private void OnTriggerEnter(Collider collider)
{
Rigidbody rigidbody = collider.attachedRigidbody;
if (rigidbody == null || _ignoredBodies.Contains(rigidbody))
{
return;
}
if (!IntersectingBodies.Contains(rigidbody))
{
IntersectingBodies.Add(rigidbody);
}
}
private void OnTriggerExit(Collider collider)
{
Rigidbody rigidbody = collider.attachedRigidbody;
if (rigidbody == null)
{
return;
}
if (IntersectingBodies.Contains(rigidbody))
{
IntersectingBodies.Remove(rigidbody);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 826882dcb55017a46ab71dddc25ab18e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,90 @@
/*
* 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 UnityEngine;
using UnityEngine.UI;
namespace Oculus.Interaction.HandGrab.Recorder
{
public class TimerUIControl : MonoBehaviour
{
[SerializeField]
private TMPro.TextMeshProUGUI _timerLabel;
[SerializeField]
private int _delaySeconds = 3;
[SerializeField]
private int _maxSeconds = 10;
[SerializeField]
private Button _moreButton;
[SerializeField]
private Button _lessButton;
public int DelaySeconds
{
get
{
return _delaySeconds;
}
set
{
_delaySeconds = Mathf.Clamp(value, 0, _maxSeconds);
UpdateDisplay(value);
}
}
private void OnEnable()
{
_moreButton.onClick.AddListener(IncreaseTime);
_lessButton.onClick.AddListener(DecreaseTime);
}
private void OnDisable()
{
_moreButton.onClick.RemoveListener(IncreaseTime);
_lessButton.onClick.RemoveListener(DecreaseTime);
}
private void Start()
{
UpdateDisplay(DelaySeconds);
}
private void IncreaseTime()
{
DelaySeconds++;
}
private void DecreaseTime()
{
DelaySeconds--;
}
private void UpdateDisplay(int seconds)
{
_timerLabel.text = $"{seconds}\nseconds";
_lessButton.interactable = seconds > 0;
_moreButton.interactable = seconds < _maxSeconds;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: acb15a35c5f64f043adf3c8fd8d3fe99
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e94a51fc47eedd84db08e009bc539371
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e12936d5396e634480523f2eb9bcbea
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,105 @@
/*
* 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;
using UnityEngine;
namespace Oculus.Interaction.Demo
{
[RequireComponent(typeof(MeshFilter))]
public class MeshBlit : MonoBehaviour
{
private static int MAIN_TEX = Shader.PropertyToID("_MainTex");
public Material material;
public RenderTexture renderTexture;
[SerializeField]
private float _blitsPerSecond = -1;
public float BlitsPerSecond
{
get
{
return _blitsPerSecond;
}
set
{
SetBlitsPerSecond(value);
}
}
private Mesh _mesh;
private WaitForSeconds _waitForSeconds;
private Mesh Mesh => _mesh ? _mesh : _mesh = GetComponent<MeshFilter>().sharedMesh;
private void OnEnable()
{
SetBlitsPerSecond(_blitsPerSecond);
StartCoroutine(BlitRoutine());
IEnumerator BlitRoutine()
{
while (true)
{
yield return _waitForSeconds;
Blit();
}
}
}
public void Blit()
{
if (renderTexture == null)
{
throw new NullReferenceException("MeshBlit.Blit must have a RenderTexture assigned");
}
if (material == null)
{
throw new NullReferenceException("MeshBlit.Blit must have a Material assigned");
}
if (Mesh == null)
{
throw new NullReferenceException("MeshBlit.Blit's MeshFilter has no mesh");
}
RenderTexture temp = RenderTexture.GetTemporary(renderTexture.descriptor);
Graphics.Blit(renderTexture, temp);
material.SetTexture(MAIN_TEX, temp);
var previous = RenderTexture.active;
RenderTexture.active = renderTexture;
material.SetPass(0);
Graphics.DrawMeshNow(Mesh, transform.localToWorldMatrix);
RenderTexture.active = previous;
material.SetTexture(MAIN_TEX, null);
RenderTexture.ReleaseTemporary(temp);
}
private void SetBlitsPerSecond(float value)
{
_blitsPerSecond = value;
_waitForSeconds = value > 0 ? new WaitForSeconds(1 / _blitsPerSecond) : null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f38a2c2a6f2edf246a68b41b406dd964
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6d03fabf79ac272479773d0172fb4740
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,97 @@
fileFormatVersion: 2
guid: 06aafc7fcb186454893618a0b137089b
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 0
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 1
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
// @lint-ignore SOCIALVRTEXTUREIMPORTS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,88 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities 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.
************************************************************************************/
Shader "MeshBlit/MeshBlitStamp"
{
Properties
{
[NoScaleOffset] _Stamp("Stamp", 2D) = "black" {}
_StampMultipler("Stamp Multipler", Float) = 1
[Enum(UV0,0,UV1,1)] _UV("UV Set", Float) = 1
[HideInInspector]_MainTex("Texture", 2D) = "black" {}
[HideInInspector]_Subtract("Subtract", float) = 0
}
SubShader
{
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 normal : NORMAL;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 stampUV : TEXCOORD1;
float3 normalWS : TEXCOORD4;
float4 vertex : SV_POSITION;
};
float4x4 _StampMatrix;
sampler2D _MainTex;
sampler2D _Stamp;
half _StampMultipler;
float _Subtract;
half _UV;
v2f vert(appdata v)
{
v2f o;
float2 uv = (_UV == 0) ? v.uv0 : v.uv1;
o.uv = uv;
#if SHADER_API_D3D11
uv.y = 1 - uv.y;
#endif
o.vertex = float4(uv * 2 - 1, 0.5, 1);
float4 positionWS = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1));
o.stampUV = mul(_StampMatrix, positionWS);
o.normalWS = normalize(UnityObjectToWorldNormal(v.normal)); //TODO dont include the stamp on backfaces
return o;
}
half4 frag(v2f i) : SV_Target
{
float4 col = tex2D(_MainTex, i.uv);
col = max(0, col - _Subtract);
float2 stampUV = saturate((i.stampUV.xy / i.stampUV.w) * 0.5 + 0.5);
half4 stamp = tex2D(_Stamp, stampUV);
col += stamp * _StampMultipler;
col = saturate(col);
return col;
}
ENDCG
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 14c4f10921fa2ed46b4a9be3c07abeee
ShaderImporter:
externalObjects: {}
defaultTextures:
- _Stamp: {instanceID: 0}
- _MainTex: {instanceID: 0}
nonModifiableTextures: []
preprocessorOverride: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,358 @@
/*
* 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.HandGrab;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace Oculus.Interaction.Demo
{
public class WaterSpray : MonoBehaviour, IHandGrabUseDelegate
{
public enum NozzleMode
{
Spray,
Stream
}
[Header("Input")]
[SerializeField]
private Transform _trigger;
[SerializeField]
private Transform _nozzle;
[SerializeField]
private AnimationCurve _triggerRotationCurve;
[SerializeField]
private SnapAxis _axis = SnapAxis.X;
[SerializeField]
[Range(0f, 1f)]
private float _releaseThresold = 0.3f;
[SerializeField]
[Range(0f, 1f)]
private float _fireThresold = 0.9f;
[SerializeField]
private float _triggerSpeed = 3f;
[SerializeField]
private AnimationCurve _strengthCurve = AnimationCurve.EaseInOut(0f,0f,1f,1f);
[Header("Output")]
[SerializeField, Tooltip("Masks the Raycast used to find objects to make wet")]
private LayerMask _raycastLayerMask = ~0;
[SerializeField, Tooltip("The spread angle when spraying, larger values will make a larger area wet")]
private float _spraySpreadAngle = 40;
[SerializeField, Tooltip("The spread angle when using stream, larger values will make a larger area wet")]
private float _streamSpreadAngle = 4;
[SerializeField]
private float _sprayStrength = 1.5f;
[SerializeField]
private int _sprayHits = 6;
[SerializeField]
private float _sprayRandomness = 6f;
[SerializeField, Tooltip("The max distance of the spray, controls the raycast and shader")]
private float _maxDistance = 2;
[SerializeField]
private float _dryingSpeed = 0.1f;
[SerializeField, Tooltip("Material for applying a stamp, should using the MeshBlitStamp shader or similar")]
private Material _sprayStampMaterial = null;
[SerializeField, Tooltip("When not null, will be set as the '_WetBumpMap' property on wet renderers")]
private Texture _waterBumpOverride = null;
[SerializeField]
private UnityEvent WhenSpray;
[SerializeField]
private UnityEvent WhenStream;
private static readonly int WET_MAP_PROPERTY = Shader.PropertyToID("_WetMap");
private static readonly int STAMP_MULTIPLIER_PROPERTY = Shader.PropertyToID("_StampMultipler");
private static readonly int SUBTRACT_PROPERTY = Shader.PropertyToID("_Subtract");
private static readonly int WET_BUMPMAP_PROPERTY = Shader.PropertyToID("_WetBumpMap");
private static readonly int STAMP_MATRIX_PROPERTY = Shader.PropertyToID("_StampMatrix");
private static readonly WaitForSeconds WAIT_TIME = new WaitForSeconds(0.1f);
private bool _wasFired = false;
private float _dampedUseStrength = 0;
private float _lastUseTime;
#region input
private void SprayWater()
{
NozzleMode mode = GetNozzleMode();
if (mode == NozzleMode.Spray)
{
Spray();
WhenSpray?.Invoke();
}
else if (mode == NozzleMode.Stream)
{
Stream();
WhenStream?.Invoke();
}
}
private void UpdateTriggerRotation(float progress)
{
float value = _triggerRotationCurve.Evaluate(progress);
Vector3 angles = _trigger.localEulerAngles;
if ((_axis & SnapAxis.X) != 0)
{
angles.x = value;
}
if ((_axis & SnapAxis.Y) != 0)
{
angles.y = value;
}
if ((_axis & SnapAxis.Z) != 0)
{
angles.z = value;
}
_trigger.localEulerAngles = angles;
}
private NozzleMode GetNozzleMode()
{
int rotations = ((int)_nozzle.localEulerAngles.z + 45) / 90;
if (rotations % 2 == 0)
{
return NozzleMode.Spray;
}
return NozzleMode.Stream;
}
#endregion
#region output
private void Spray()
{
StartCoroutine(StampRoutine(_sprayHits, _sprayRandomness, _spraySpreadAngle, _sprayStrength));
}
private void Stream()
{
StartCoroutine(StampRoutine(_sprayHits, 0, _streamSpreadAngle, _sprayStrength));
}
private IEnumerator StampRoutine(int stampCount, float randomness, float spread, float strength)
{
StartStamping();
Pose originalPose = _nozzle.GetPose();
for (int i = 0; i < stampCount; i++)
{
yield return WAIT_TIME;
Pose randomPose = originalPose;
randomPose.rotation =
randomPose.rotation *
Quaternion.Euler(
Random.Range(-randomness, randomness),
Random.Range(-randomness, randomness),
0f);
Stamp(randomPose, _maxDistance, spread, strength);
}
StartDrying();
}
private void StartStamping()
{
_sprayStampMaterial.SetFloat(SUBTRACT_PROPERTY, 0);
}
private void StartDrying()
{
_sprayStampMaterial.SetMatrix(STAMP_MATRIX_PROPERTY, Matrix4x4.zero);
_sprayStampMaterial.SetFloat(SUBTRACT_PROPERTY, _dryingSpeed);
}
private void Stamp(Pose pose, float maxDistance, float angle, float strength)
{
_sprayStampMaterial.SetMatrix(STAMP_MATRIX_PROPERTY, CreateStampMatrix(pose, angle));
_sprayStampMaterial.SetFloat(STAMP_MULTIPLIER_PROPERTY, strength);
float radius = Mathf.Tan(Mathf.Deg2Rad * angle / 2) * maxDistance;
Vector3 startPoint = pose.position + pose.forward * radius;
Vector3 endPoint = pose.position + pose.forward * maxDistance;
int hitCount = Physics.OverlapCapsuleNonAlloc(startPoint, endPoint, radius, NonAlloc._overlapResults, _raycastLayerMask.value, QueryTriggerInteraction.Ignore);
HashSet<Transform> roots = NonAlloc.GetRootsFromOverlapResults(hitCount);
foreach (Transform rootObject in roots)
{
RenderSplash(rootObject);
}
roots.Clear();
}
/// <summary>
/// Finds Meshes that are part of the rootObject and blits the material on them
/// </summary>
private void RenderSplash(Transform rootObject)
{
List<MeshFilter> meshFilters = NonAlloc.GetMeshFiltersInChildren(rootObject);
for (int i = 0; i < meshFilters.Count; i++)
{
int id = meshFilters[i].GetInstanceID();
if (!NonAlloc._blits.ContainsKey(id)) { NonAlloc._blits[id] = CreateMeshBlit(meshFilters[i]); }
NonAlloc._blits[id].Blit();
}
}
/// <summary>
/// Sets up a new mesh blit on the mesh filter for the water spray
/// </summary>
private MeshBlit CreateMeshBlit(MeshFilter meshFilter)
{
MeshBlit newBlit = meshFilter.gameObject.AddComponent<MeshBlit>();
newBlit.material = _sprayStampMaterial;
newBlit.renderTexture = new RenderTexture(512, 512, 0, RenderTextureFormat.RHalf);
newBlit.BlitsPerSecond = 30;
if (meshFilter.TryGetComponent(out Renderer renderer))
{
renderer.GetPropertyBlock(NonAlloc.PropertyBlock);
NonAlloc.PropertyBlock.SetTexture(WET_MAP_PROPERTY, newBlit.renderTexture);
if (_waterBumpOverride)
{
NonAlloc.PropertyBlock.SetTexture(WET_BUMPMAP_PROPERTY, _waterBumpOverride);
}
renderer.SetPropertyBlock(NonAlloc.PropertyBlock);
}
return newBlit;
}
private Matrix4x4 CreateStampMatrix(Pose pose, float angle)
{
Matrix4x4 viewMatrix = Matrix4x4.TRS(pose.position, pose.rotation, Vector3.one).inverse;
viewMatrix.m20 *= -1f;
viewMatrix.m21 *= -1f;
viewMatrix.m22 *= -1f;
viewMatrix.m23 *= -1f;
return GL.GetGPUProjectionMatrix(Matrix4x4.Perspective(angle, 1, 0, _maxDistance), true) * viewMatrix;
}
/// <summary>
/// Cleans destroyed MeshBlits form the dictionary
/// </summary>
private void OnDestroy()
{
NonAlloc.CleanUpDestroyedBlits();
}
public void BeginUse()
{
_dampedUseStrength = 0f;
_lastUseTime = Time.realtimeSinceStartup;
}
public void EndUse()
{
}
public float ComputeUseStrength(float strength)
{
float delta = Time.realtimeSinceStartup - _lastUseTime;
_lastUseTime = Time.realtimeSinceStartup;
if (strength > _dampedUseStrength)
{
_dampedUseStrength = Mathf.Lerp(_dampedUseStrength, strength, _triggerSpeed * delta);
}
else
{
_dampedUseStrength = strength;
}
float progress = _strengthCurve.Evaluate(_dampedUseStrength);
UpdateTriggerProgress(progress);
return progress;
}
private void UpdateTriggerProgress(float progress)
{
UpdateTriggerRotation(progress);
if (progress >= _fireThresold && !_wasFired)
{
_wasFired = true;
SprayWater();
}
else if (progress <= _releaseThresold)
{
_wasFired = false;
}
}
#endregion
/// <summary>
/// Allocation helpers
/// </summary>
static class NonAlloc
{
public static readonly Collider[] _overlapResults = new Collider[12];
public static readonly Dictionary<int, MeshBlit> _blits = new Dictionary<int, MeshBlit>();
public static MaterialPropertyBlock PropertyBlock => _block != null ? _block : _block = new MaterialPropertyBlock();
private static readonly List<MeshFilter> _meshFilters = new List<MeshFilter>();
private static readonly HashSet<Transform> _roots = new HashSet<Transform>();
private static MaterialPropertyBlock _block;
public static List<MeshFilter> GetMeshFiltersInChildren(Transform root)
{
root.GetComponentsInChildren(_meshFilters);
return _meshFilters;
}
public static HashSet<Transform> GetRootsFromOverlapResults(int hitCount)
{
_roots.Clear();
for (int i = 0; i < hitCount; i++)
{
Transform root = GetRoot(_overlapResults[i]);
_roots.Add(root);
}
return _roots;
}
/// <summary>
/// Returns the most likely 'root object' for the hit e.g. the rigidbody
/// </summary>
static Transform GetRoot(Collider hit)
{
return hit.attachedRigidbody ? hit.attachedRigidbody.transform :
hit.transform.parent ? hit.transform.parent :
hit.transform;
}
public static void CleanUpDestroyedBlits()
{
if (!_blits.ContainsValue(null)) { return; }
foreach (int key in new List<int>(_blits.Keys))
{
if (_blits[key] == null) _blits.Remove(key);
}
}
}
}
}

View File

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 00b3471f79f999345b21b7979abca356
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- sprayStampMaterial: {fileID: 2100000, guid: 44092c54cacca1046ac25e52c878a0c7, type: 2}
- waterBumpOverride: {fileID: 2800000, guid: 06aafc7fcb186454893618a0b137089b, type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,37 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: WaterSpray
m_Shader: {fileID: 4800000, guid: 14c4f10921fa2ed46b4a9be3c07abeee, type: 3}
m_ShaderKeywords:
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _Stamp:
m_Texture: {fileID: 2800000, guid: c6766195a787bd94cbe3f230af954ee4, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _StampMultipler: 5
- _StampStrength: 3
- _Subtract: 0.002
- _UV: 1
m_Colors:
- _Subtract: {r: 0.039215688, g: 0.039215688, b: 0.039215688, a: 1}
m_BuildTextureStacks: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 44092c54cacca1046ac25e52c878a0c7
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,93 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Demo
{
public class WaterSprayNozzleTransformer : MonoBehaviour, ITransformer
{
[SerializeField]
private float _factor = 3f;
[SerializeField]
private float _snapAngle = 90;
[SerializeField]
[Range(0f, 1f)]
private float _snappiness = 0.8f;
[SerializeField]
private int _maxSteps = 1;
private float _relativeAngle = 0f;
private int _stepsCount = 0;
private IGrabbable _grabbable;
private Pose _previousGrabPose;
public void Initialize(IGrabbable grabbable)
{
_grabbable = grabbable;
}
public void BeginTransform()
{
_previousGrabPose = _grabbable.GrabPoints[0];
_relativeAngle = 0.0f;
_stepsCount = 0;
}
public void UpdateTransform()
{
Pose grabPoint = _grabbable.GrabPoints[0];
Transform targetTransform = _grabbable.Transform;
Vector3 localAxis = Vector3.forward;
Vector3 worldAxis = targetTransform.TransformDirection(localAxis);
Vector3 initialVector = Vector3.ProjectOnPlane(_previousGrabPose.right, worldAxis).normalized;
Vector3 targetVector = Vector3.ProjectOnPlane(grabPoint.right, worldAxis).normalized;
float angleDelta = Vector3.SignedAngle(initialVector, targetVector, worldAxis) * _factor;
_relativeAngle += angleDelta;
if (Mathf.Abs(_relativeAngle) > _snapAngle * (1 - _snappiness)
&& Mathf.Abs(_stepsCount + Mathf.Sign(_relativeAngle)) <= _maxSteps)
{
float currentAngle = targetTransform.localEulerAngles.z;
int turns = Mathf.FloorToInt((currentAngle + _snappiness * 0.5f) / _snapAngle);
float sign = Mathf.Sign(_relativeAngle);
float angle = sign > 0 ? _snapAngle * (turns + 1) : _snapAngle * turns;
Vector3 rot = targetTransform.localEulerAngles;
rot.z = angle;
targetTransform.localEulerAngles = rot;
_relativeAngle = 0;
_stepsCount += (int)sign;
}
else
{
targetTransform.Rotate(worldAxis, angleDelta, Space.World);
}
_previousGrabPose = grabPoint;
}
public void EndTransform() { }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1302feb954c92f43bb3d72f377721ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eb0e4cf711387a24ba61894d49bb7cdd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Demo
{
/// <summary>
/// Supplies the 'main' directionl light properties to the BasicPBR shader
/// </summary>
public class BasicPBRGlobals : MonoBehaviour
{
[SerializeField]
private Light _mainlight;
private void Update()
{
UpateShaderGlobals();
}
private void UpateShaderGlobals()
{
Light light = _mainlight;
bool hasLight = light && light.isActiveAndEnabled;
Shader.SetGlobalVector("_BasicPBRLightDir", hasLight ? light.transform.forward : Vector3.down);
Shader.SetGlobalColor("_BasicPBRLightColor", hasLight ? light.color : Color.black);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 94217a4c67be3f143b2ac00d190c8bde
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8c201135db8d52149949bab567cd1fd6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,255 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities 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.
************************************************************************************/
// PBR Shader adapted from Standard (Metallic) that works in built-in and URP pipelines
// Supports 1 directional light without specular
Shader "Unlit/BasicPBR"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Texture", 2D) = "white" {}
_Metallic("Metallic", Range(0,1)) = 0
_Gloss("Gloss", Range(0,1)) = 0
[NoScaleOffset] _BumpMap("Normal Map", 2D) = "bump" {}
[NoScaleOffset] _WetMap("Wet Map", 2D) = "black" {}
[Enum(UV0,0,UV1,1)] _WetMapUV("Wet Map UV Set", Float) = 1
// droplets for non-porous horizontal surfaces
[HideInInspector] _WetBumpMap("Wet Bump Map", 2D) = "bump" {}
[Toggle(VERTEX_COLOR_LIGHTMAP)] _VertexColorLightmap("Vertex Color Lightmap", Float) = 0
_VertexColorLightmapScale("Vertex Color Lightmap Scale", Float) = 1
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
fixed4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
float3 normalWS : TEXCOORD2;
float3 tangentWS: TEXCOORD3;
float3 bitangentWS: TEXCOORD4;
float3 positionWS : TEXCOORD5;
fixed4 color : COLOR;
UNITY_FOG_COORDS(6)
float4 vertex : SV_POSITION;
UNITY_VERTEX_OUTPUT_STEREO
};
// standard properties
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
fixed _Metallic;
fixed _Gloss;
sampler2D _WetMap;
half _WetMapUV;
sampler2D _WetBumpMap;
float _VertexColorLightmapScale;
// globals
float3 _BasicPBRLightDir;
fixed3 _BasicPBRLightColor;
v2f vert(appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_OUTPUT(v2f, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv0 = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv1 = v.texcoord1;
o.positionWS = mul(unity_ObjectToWorld, v.vertex);
o.normalWS = normalize(UnityObjectToWorldNormal(v.normal));
o.tangentWS = normalize(mul(unity_ObjectToWorld, v.tangent).xyz);
o.bitangentWS = normalize(cross(o.normalWS, o.tangentWS.xyz));
o.color = v.color;
UNITY_TRANSFER_FOG(o, o.vertex);
return o;
}
// UnityStandardUtils.cginc#L46
half3 DiffuseAndSpecularFromMetallic(half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
{
specColor = lerp(unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;
oneMinusReflectivity = oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
return albedo * oneMinusReflectivity;
}
// UnityImageBasedLighting.cginc#L522
half3 Unity_GlossyEnvironment(half3 reflectDir, half perceptualRoughness)
{
perceptualRoughness = perceptualRoughness * (1.7 - 0.7 * perceptualRoughness);
half mip = perceptualRoughness * 6;
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflectDir, mip);
return DecodeHDR(rgbm, unity_SpecCube1_HDR);
}
inline half3 Pow4(half3 x)
{
return x * x * x * x;
}
// UnityStandardBRDF.cginc#L92
inline half3 FresnelLerp(half3 F0, half3 F90, half cosA)
{
half t = Pow4(1 - cosA);
return lerp(F0, F90, t);
}
// UnityStandardBRDF.cginc#L299
half SurfaceReduction(float perceptualRoughness)
{
float roughness = perceptualRoughness * perceptualRoughness;
#ifdef UNITY_COLORSPACE_GAMMA
return 1.0 - 0.28 * roughness * perceptualRoughness;
#else
return 1.0 / (roughness * roughness + 1.0);
#endif
}
// UnityStandardBRDF.cginc#L338
float3 Specular(float roughness, float3 normal, float3 viewDir)
{
half3 halfDir = normalize(-_BasicPBRLightDir + viewDir);
float nh = saturate(dot(normal, halfDir));
float lh = saturate(dot(_BasicPBRLightDir, halfDir));
float a = roughness * roughness;
float a2 = a * a;
float d = nh * nh * (a2 - 1.f) + 1.00001f;
#ifdef UNITY_COLORSPACE_GAMMA
float specularTerm = a / (max(0.32f, lh) * (1.5f + a) * d);
#else
float specularTerm = a2 / (max(0.1f, lh * lh) * (a + 0.5f) * (d * d) * 4);
#endif
#if defined (SHADER_API_MOBILE)
specularTerm = specularTerm - 1e-4f;
#endif
return specularTerm * 0.3f;
}
// https://seblagarde.wordpress.com/2013/04/14/water-drop-3b-physically-based-wet-surfaces/
void AddWater(float2 uv, float metalness, inout half3 diffuse, inout float smoothness, inout fixed4 bumpMap, float2 wsPos, float3 normalWS)
{
fixed wetMap = tex2D(_WetMap, uv).r;
float porosity = saturate((1 - smoothness) - 0.2);//saturate(((1-Gloss) - 0.5)) / 0.4 );
float factor = lerp(1, 0.2, (1 - metalness) * porosity);
float collectWater = max(0, normalWS.y);
diffuse *= lerp(1.0, factor, wetMap);
smoothness = lerp(smoothness, 0.95, saturate(wetMap * wetMap));// lerp(1, factor, 0.5 * wetMap));
bumpMap = lerp(bumpMap, tex2D(_WetBumpMap, wsPos * 20), wetMap * collectWater);
}
fixed4 frag(v2f i) : SV_Target
{
// BRDF texture inputs
fixed4 mainTex = tex2D(_MainTex, i.uv0);
float4 bumpMap = tex2D(_BumpMap, i.uv0);
// BEDF values
fixed3 albedo = mainTex.rgb * _Color.rgb;
float metalness = _Metallic;
float smoothness = _Gloss;
float oneMinusReflectivity;
float3 specColor;
float3 diffColor = DiffuseAndSpecularFromMetallic(albedo.rgb, metalness, /*out*/ specColor, /*out*/ oneMinusReflectivity);
AddWater((_WetMapUV == 0) ? i.uv0 : i.uv1, metalness, /*inout*/ diffColor, /*inout*/ smoothness, /*inout*/ bumpMap, i.positionWS.xz, i.normalWS);
float3x3 tangentMatrix = transpose(float3x3(i.tangentWS, i.bitangentWS, i.normalWS));
float3 normal = normalize(mul(tangentMatrix, UnpackNormal(bumpMap)));
float3 ambient =
#if VERTEX_COLOR_LIGHTMAP
i.color.rgb * _VertexColorLightmapScale;
#else
ShadeSH9(float4(normal, 1));
#endif
half nl = saturate(dot(normal, -_BasicPBRLightDir));
half3 diffuse = diffColor * (ambient + _BasicPBRLightColor * nl);
float perceptualRoughness = 1 - smoothness;
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.positionWS);
half nv = abs(dot(normal, viewDir));
float3 reflectDir = -reflect(viewDir, normal);
float3 specularGI = Unity_GlossyEnvironment(reflectDir, perceptualRoughness);
half grazingTerm = saturate(smoothness + (1 - oneMinusReflectivity));
half3 specular = SurfaceReduction(perceptualRoughness) * specularGI * FresnelLerp(specColor, grazingTerm, nv); +
+ Specular(perceptualRoughness, normal, viewDir) * specColor * nl * _BasicPBRLightColor;
// non BRDF texture inputs
half3 color = (diffuse + specular);
UNITY_APPLY_FOG(i.fogCoord, color);
return fixed4(color, 1);
}
ENDCG
SubShader
{
Tags{ "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
Tags{ "LightMode" = "UniversalForward" }
CGPROGRAM
#pragma shader_feature VERTEX_COLOR_LIGHTMAP
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
ENDCG
}
}
SubShader
{
Tags{ "RenderType" = "Opaque" }
Pass
{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma shader_feature VERTEX_COLOR_LIGHTMAP
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
ENDCG
}
}
}

View File

@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: f98fc22324f0f5248bc2a4b595aa681d
ShaderImporter:
externalObjects: {}
defaultTextures:
- _MainTex: {instanceID: 0}
- _MetallicGlossMap: {instanceID: 0}
- _BumpMap: {instanceID: 0}
- _OcclusionMap: {instanceID: 0}
- _EmissionMap: {instanceID: 0}
- _WetMap: {instanceID: 0}
- _WetBumpMap: {fileID: 2800000, guid: 06aafc7fcb186454893618a0b137089b, type: 3}
nonModifiableTextures: []
preprocessorOverride: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,91 @@
/*
* 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.HandGrab;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Samples
{
public class HideHandVisualOnGrab : MonoBehaviour
{
[SerializeField]
private HandGrabInteractor _handGrabInteractor;
[SerializeField, Interface(typeof(IHandVisual))]
private UnityEngine.Object _handVisual;
private IHandVisual HandVisual;
protected virtual void Awake()
{
HandVisual = _handVisual as IHandVisual;
}
protected virtual void Start()
{
this.AssertField(HandVisual, nameof(HandVisual));
}
protected virtual void Update()
{
GameObject shouldHideHandComponent = null;
if (_handGrabInteractor.State == InteractorState.Select)
{
shouldHideHandComponent = _handGrabInteractor.SelectedInteractable?.gameObject;
}
if (shouldHideHandComponent)
{
if (shouldHideHandComponent.TryGetComponent(out ShouldHideHandOnGrab component))
{
HandVisual.ForceOffVisibility = true;
}
}
else
{
HandVisual.ForceOffVisibility = false;
}
}
#region Inject
public void InjectAll(HandGrabInteractor handGrabInteractor,
IHandVisual handVisual)
{
InjectHandGrabInteractor(handGrabInteractor);
InjectHandVisual(handVisual);
}
private void InjectHandGrabInteractor(HandGrabInteractor handGrabInteractor)
{
_handGrabInteractor = handGrabInteractor;
}
private void InjectHandVisual(IHandVisual handVisual)
{
_handVisual = handVisual as UnityEngine.Object;
HandVisual = handVisual;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b637179e0cc734442bf0e50111bcc634
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9dd6a62d4597094408a690179bd0f609
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,129 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Locomotion
{
[RequireComponent(typeof(AudioSource))]
public class AdjustableAudio : MonoBehaviour
{
[SerializeField]
private AudioSource _audioSource = null;
[SerializeField]
private AudioClip _audioClip = null;
public AudioClip AudioClip
{
get
{
return _audioClip;
}
set
{
_audioClip = value;
}
}
[SerializeField, Range(0f,1f)]
private float _volumeFactor = 1f;
public float VolumeFactor
{
get
{
return _volumeFactor;
}
set
{
_volumeFactor = value;
}
}
[SerializeField]
private AnimationCurve _volumeCurve = AnimationCurve.Linear(0f, 0f, 1f, 1f);
public AnimationCurve VolumeCurve
{
get
{
return _volumeCurve;
}
set
{
_volumeCurve = value;
}
}
[SerializeField]
private AnimationCurve _pitchCurve = AnimationCurve.Linear(0f, 0.5f, 1f, 1.5f);
public AnimationCurve PitchCurve
{
get
{
return _pitchCurve;
}
set
{
_pitchCurve = value;
}
}
protected bool _started;
#region Editor events
protected virtual void Reset()
{
_audioSource = gameObject.GetComponent<AudioSource>();
_audioClip = _audioSource.clip;
}
#endregion
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertField(_audioSource, nameof(_audioSource));
this.EndStart(ref _started);
}
public void PlayAudio(float volumeT, float pitchT, float pan = 0f)
{
if (!_audioSource.isActiveAndEnabled)
{
return;
}
_audioSource.volume = _volumeCurve.Evaluate(volumeT) * VolumeFactor;
_audioSource.pitch = _pitchCurve.Evaluate(pitchT);
_audioSource.panStereo = pan;
_audioSource.PlayOneShot(_audioClip);
}
#region Inject
public void InjectAllAdjustableAudio(AudioSource audioSource)
{
InjectAudioSource(audioSource);
}
public void InjectAudioSource(AudioSource audioSource)
{
_audioSource = audioSource;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c686f13208596eb4b8272d78e582aec9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,53 @@
/*
* 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 UnityEngine;
using UnityEngine.Events;
namespace Oculus.Interaction.Samples
{
public class LocomotionTutorialAnimationUnityEventWrapper : MonoBehaviour
{
public UnityEvent WhenEnableTeleportRay;
public UnityEvent WhenDisableTeleportRay;
public UnityEvent WhenEnableTurningRing;
public UnityEvent WhenDisableTurningRing;
public void EnableTeleportRay()
{
WhenEnableTeleportRay.Invoke();
}
public void DisableTeleportRay()
{
WhenDisableTeleportRay.Invoke();
}
public void EnableTurningRing()
{
WhenEnableTurningRing.Invoke();
}
public void DisableTurningRing()
{
WhenDisableTurningRing.Invoke();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e71d82996a6bf84e94d1cd4dc728f5d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,184 @@
/*
* 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.Locomotion;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace Oculus.Interaction.Samples
{
public class LocomotionTutorialProgressTracker : MonoBehaviour
{
[SerializeField]
private Image[] _dots;
[SerializeField]
private Sprite _pendingSprite;
[SerializeField]
private Sprite _currentSprite;
[SerializeField]
private Sprite _completedSprite;
[SerializeField]
private List<LocomotionEvent.TranslationType> _consumeTranslationEvents = new List<LocomotionEvent.TranslationType>();
[SerializeField]
private List<LocomotionEvent.RotationType> _consumeRotationEvents = new List<LocomotionEvent.RotationType>();
[SerializeField, Interface(typeof(ILocomotionEventHandler))]
private UnityEngine.Object _locomotionHandler;
private ILocomotionEventHandler LocomotionHandler;
public UnityEvent WhenCompleted;
protected bool _started;
private int _currentProgress = 0;
private int _totalProgress = 0;
protected virtual void Awake()
{
LocomotionHandler = _locomotionHandler as ILocomotionEventHandler;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertCollectionField(_dots, nameof(_dots));
this.AssertCollectionItems(_consumeTranslationEvents, nameof(_consumeTranslationEvents));
this.AssertCollectionItems(_consumeRotationEvents, nameof(_consumeRotationEvents));
this.AssertField(_pendingSprite, nameof(_pendingSprite));
this.AssertField(_currentSprite, nameof(_currentSprite));
this.AssertField(_completedSprite, nameof(_completedSprite));
this.AssertField(LocomotionHandler, nameof(LocomotionHandler));
_totalProgress = _dots.Length;
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
LocomotionHandler.WhenLocomotionEventHandled += LocomotionEventHandled;
ResetProgress();
}
}
protected virtual void OnDisable()
{
if (_started)
{
LocomotionHandler.WhenLocomotionEventHandled -= LocomotionEventHandled;
}
}
private void LocomotionEventHandled(LocomotionEvent arg1, Pose arg2)
{
if (_consumeRotationEvents.Contains(arg1.Rotation)
|| _consumeTranslationEvents.Contains(arg1.Translation))
{
Progress();
}
}
private void Progress()
{
_currentProgress++;
if (_currentProgress <= _totalProgress)
{
_dots[_currentProgress - 1].sprite = _completedSprite;
}
if (_currentProgress < _totalProgress)
{
_dots[_currentProgress].sprite = _currentSprite;
}
if (_currentProgress >= _totalProgress)
{
WhenCompleted.Invoke();
}
}
private void ResetProgress()
{
_currentProgress = 0;
for (int i = 0; i < _dots.Length; i++)
{
_dots[i].sprite = i == 0 ? _currentSprite : _pendingSprite;
}
}
#region Inject
public void InjectAllLocomotionTutorialProgressTracker(Image[] dots,
Sprite pendingSprite, Sprite currentSprite, Sprite completedSprite,
List<LocomotionEvent.TranslationType> consumeTranslationEvents,
List<LocomotionEvent.RotationType> consumeRotationEvents,
ILocomotionEventHandler locomotionHandler)
{
InjectDots(dots);
InjectPendingSprite(pendingSprite);
InjectCurrentSprite(currentSprite);
InjectCompletedSprite(completedSprite);
InjectConsumeTranslationEvents(consumeTranslationEvents);
InjectConsumeRotationEvents(consumeRotationEvents);
InjectLocomotionHandler(locomotionHandler);
}
public void InjectDots(Image[] dots)
{
_dots = dots;
}
public void InjectPendingSprite(Sprite pendingSprite)
{
_pendingSprite = pendingSprite;
}
public void InjectCurrentSprite(Sprite currentSprite)
{
_currentSprite = currentSprite;
}
public void InjectCompletedSprite(Sprite completedSprite)
{
_completedSprite = completedSprite;
}
public void InjectConsumeTranslationEvents(List<LocomotionEvent.TranslationType> consumeTranslationEvents)
{
_consumeTranslationEvents = consumeTranslationEvents;
}
public void InjectConsumeRotationEvents(List<LocomotionEvent.RotationType> consumeRotationEvents)
{
_consumeRotationEvents = consumeRotationEvents;
}
public void InjectLocomotionHandler(ILocomotionEventHandler locomotionHandler)
{
_locomotionHandler = locomotionHandler as UnityEngine.Object;
LocomotionHandler = locomotionHandler;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: de6bd207a644f864b8cc682622c8af52
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,226 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples
{
public class LocomotionTutorialTurnVisual : MonoBehaviour
{
[SerializeField, Range(-1f, 1f)]
private float _value;
[SerializeField, Range(0f, 1f)]
private float _progress;
[Header("Visual renderers")]
[SerializeField]
private Renderer _leftArrow;
[SerializeField]
private Renderer _rightArrow;
[SerializeField]
private TubeRenderer _leftTrail;
[SerializeField]
private TubeRenderer _rightTrail;
[SerializeField]
private MaterialPropertyBlockEditor _leftMaterialBlock;
[SerializeField]
private MaterialPropertyBlockEditor _rightMaterialBlock;
[Header("Visual parameters")]
[SerializeField]
private float _verticalOffset = 0.02f;
public float VerticalOffset
{
get => _verticalOffset;
set => _verticalOffset = value;
}
[SerializeField]
private float _radius = 0.07f;
[SerializeField]
private float _margin = 2f;
[SerializeField]
private float _trailLength = 15f;
[SerializeField]
private float _maxAngle = 45f;
[SerializeField]
private float _railGap = 0.005f;
[SerializeField]
private float _squeezeLength = 5f;
[SerializeField]
private Color _disabledColor = new Color(1f, 1f, 1f, 0.2f);
public Color DisabledColor
{
get => _disabledColor;
set => _disabledColor = value;
}
[SerializeField]
private Color _enabledColor = new Color(1f, 1f, 1f, 0.6f);
public Color EnabledColor
{
get => _enabledColor;
set => _enabledColor = value;
}
[SerializeField]
private Color _highligtedColor = new Color(1f, 1f, 1f, 1f);
public Color HighligtedColor
{
get => _highligtedColor;
set => _highligtedColor = value;
}
private const float _degreesPerSegment = 1f;
private static readonly Quaternion _rotationCorrectionLeft = Quaternion.Euler(0f, -90f, 0f);
private static readonly int _colorShaderPropertyID = Shader.PropertyToID("_Color");
protected bool _started;
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertField(_leftTrail, nameof(_leftTrail));
this.AssertField(_rightTrail, nameof(_rightTrail));
this.AssertField(_leftArrow, nameof(_leftArrow));
this.AssertField(_rightArrow, nameof(_rightArrow));
this.AssertField(_leftMaterialBlock, nameof(_leftMaterialBlock));
this.AssertField(_rightMaterialBlock, nameof(_rightMaterialBlock));
InitializeVisuals();
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
_leftTrail.enabled = true;
_rightTrail.enabled = true;
_leftArrow.enabled = true;
_rightArrow.enabled = true;
}
protected virtual void OnDisable()
{
_leftTrail.enabled = false;
_rightTrail.enabled = false;
_leftArrow.enabled = false;
_rightArrow.enabled = false;
}
protected virtual void Update()
{
UpdateArrows();
UpdateColors();
}
private void InitializeVisuals()
{
TubePoint[] trailPoints = InitializeSegment(new Vector2(_margin, _maxAngle + _squeezeLength));
_leftTrail.RenderTube(trailPoints, Space.Self);
_rightTrail.RenderTube(trailPoints, Space.Self);
}
private void UpdateArrows()
{
float value = _value;
float angle = Mathf.Lerp(0f, _maxAngle, Mathf.Abs(value));
bool isLeft = value < 0;
bool isRight = value > 0;
bool follow = false;
float squeeze = Mathf.Lerp(0f, _squeezeLength, _progress);
angle = Mathf.Max(angle, _trailLength);
UpdateArrowPosition(isRight ? angle + squeeze : _trailLength, _rightArrow.transform);
RotateTrail(follow && isRight ? angle - _trailLength : 0f, _rightTrail);
UpdateTrail(isRight ? (follow ? _trailLength : angle) + squeeze : _trailLength, _rightTrail);
UpdateArrowPosition(isLeft ? -angle - squeeze : -_trailLength, _leftArrow.transform);
RotateTrail(follow && isLeft ? -angle + _trailLength : 0f, _leftTrail);
UpdateTrail(isLeft ? (follow ? _trailLength : angle) + squeeze : _trailLength, _leftTrail);
}
private void UpdateArrowPosition(float angle, Transform arrow)
{
Quaternion rotation = Quaternion.AngleAxis(angle, Vector3.up);
arrow.localPosition = rotation * Vector3.forward * _radius;
arrow.localRotation = rotation * _rotationCorrectionLeft;
}
private void RotateTrail(float angle, TubeRenderer trail)
{
trail.transform.localRotation = Quaternion.AngleAxis(angle, Vector3.up);
}
private void UpdateTrail(float angle, TubeRenderer trail)
{
float max = _maxAngle + _squeezeLength;
float segmentLenght = trail.TotalLength;
float start = -100;
float end = (max - angle - _margin) / max;
trail.StartFadeThresold = segmentLenght * start;
trail.EndFadeThresold = segmentLenght * end;
trail.InvertThreshold = false;
trail.RedrawFadeThresholds();
}
private void UpdateColors()
{
bool isSelection = Mathf.Abs(_progress) >= 1f;
bool isLeft = _value < 0;
bool isRight = _value > 0;
Color activeColor = isSelection? _highligtedColor : _enabledColor;
_leftMaterialBlock.MaterialPropertyBlock.SetColor(_colorShaderPropertyID, isLeft ? activeColor : _disabledColor);
_rightMaterialBlock.MaterialPropertyBlock.SetColor(_colorShaderPropertyID, isRight ? activeColor : _disabledColor);
_leftMaterialBlock.UpdateMaterialPropertyBlock();
_rightMaterialBlock.UpdateMaterialPropertyBlock();
}
private TubePoint[] InitializeSegment(Vector2 minMax)
{
float lowLimit = minMax.x;
float upLimit = minMax.y;
int segments = Mathf.RoundToInt(Mathf.Repeat(upLimit - lowLimit, 360f) / _degreesPerSegment);
TubePoint[] tubePoints = new TubePoint[segments];
float segmentLenght = 1f / segments;
for (int i = 0; i < segments; i++)
{
Quaternion rotation = Quaternion.AngleAxis(-i * _degreesPerSegment - lowLimit, Vector3.up);
tubePoints[i] = new TubePoint()
{
position = rotation * Vector3.forward * _radius,
rotation = rotation * _rotationCorrectionLeft,
relativeLength = i * segmentLenght
};
}
return tubePoints;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c1459b1323037f40adf896ae60f0a57
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,120 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Locomotion
{
public class LocomotorSound : MonoBehaviour
{
[SerializeField]
private PlayerLocomotor _locomotor;
[SerializeField]
private AdjustableAudio _translationSound;
[SerializeField]
private AdjustableAudio _translationDeniedSound;
[SerializeField]
private AdjustableAudio _snapTurnSound;
[SerializeField]
private AnimationCurve _translationCurve = AnimationCurve.EaseInOut(0f, 0f, 2f, 1f);
[SerializeField]
private AnimationCurve _rotationCurve = AnimationCurve.EaseInOut(0f, 0f, 180f, 1f);
[SerializeField]
private float _pitchVariance = 0.05f;
protected bool _started;
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertField(_locomotor, nameof(_locomotor));
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
_locomotor.WhenLocomotionEventHandled += HandleLocomotionEvent;
}
}
protected virtual void OnDisable()
{
if (_started)
{
_locomotor.WhenLocomotionEventHandled -= HandleLocomotionEvent;
}
}
private void HandleLocomotionEvent(LocomotionEvent locomotionEvent, Pose delta)
{
if (locomotionEvent.Translation > LocomotionEvent.TranslationType.Velocity)
{
PlayTranslationSound(delta.position.magnitude);
}
if (locomotionEvent.Rotation > LocomotionEvent.RotationType.Velocity)
{
PlayRotationSound(delta.rotation.y * delta.rotation.w);
}
if (locomotionEvent.Translation == LocomotionEvent.TranslationType.None
&& locomotionEvent.Rotation == LocomotionEvent.RotationType.None)
{
PlayDenialSound(delta.position.magnitude);
}
}
private void PlayTranslationSound(float translationDistance)
{
float t = _translationCurve.Evaluate(translationDistance);
float pitch = t + Random.Range(-_pitchVariance, _pitchVariance);
_translationSound.PlayAudio(t, pitch);
}
private void PlayDenialSound(float translationDistance)
{
float t = _translationCurve.Evaluate(translationDistance);
float pitch = t + Random.Range(-_pitchVariance, _pitchVariance);
_translationDeniedSound.PlayAudio(t, pitch);
}
private void PlayRotationSound(float rotationLength)
{
float t = _rotationCurve.Evaluate(Mathf.Abs(rotationLength));
float pitch = t + Random.Range(-_pitchVariance, _pitchVariance);
_snapTurnSound.PlayAudio(t, pitch, rotationLength);
}
#region Inject
public void InjectAllLocomotorSound(PlayerLocomotor locomotor)
{
InjectPlayerLocomotor(locomotor);
}
public void InjectPlayerLocomotor(PlayerLocomotor locomotor)
{
_locomotor = locomotor;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84f47d10845aedc409ea9f6cec618b5a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
/*
* 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 UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Samples
{
public class LookAtTarget : MonoBehaviour
{
[SerializeField]
private Transform _toRotate;
[SerializeField]
private Transform _target;
protected virtual void Start()
{
this.AssertField(_toRotate, nameof(_toRotate));
this.AssertField(_target, nameof(_target));
}
protected virtual void Update()
{
Vector3 dirToTarget = (_target.position - _toRotate.position).normalized;
_toRotate.LookAt(_toRotate.position - dirToTarget, Vector3.up);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e416343365a0d340be803f69515deb0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6cf7743ec8f39e8438036f29b52fbaba
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,75 @@
/*
* 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.PalmMenu
{
/// <summary>
/// Filters to one set of GameObjects or the other, depending on which hand is the user's dominant hand.
/// </summary>
public class DominantHandGameObjectFilter : MonoBehaviour, IGameObjectFilter
{
[SerializeField, Interface(typeof(IHand))]
private Object _leftHand;
[SerializeField]
private GameObject[] _leftHandedGameObjects;
[SerializeField]
private GameObject[] _rightHandedGameObjects;
private IHand LeftHand { get; set; }
private readonly HashSet<GameObject> _leftHandedGameObjectSet =
new HashSet<GameObject>();
private readonly HashSet<GameObject> _rightHandedGameObjectSet =
new HashSet<GameObject>();
protected virtual void Start()
{
foreach (var go in _leftHandedGameObjects)
{
_leftHandedGameObjectSet.Add(go);
}
foreach (var go in _rightHandedGameObjects)
{
_rightHandedGameObjectSet.Add(go);
}
LeftHand = _leftHand as IHand;
}
public bool Filter(GameObject go)
{
if (LeftHand.IsDominantHand)
{
return _leftHandedGameObjectSet.Contains(go);
}
else
{
return _rightHandedGameObjectSet.Contains(go);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f87c900294bbee44b7f74920f84bb29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,78 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples.PalmMenu
{
/// <summary>
/// Matches the position and rotation of the user's dominant hand in world space. Normally this
/// sort of behavior is done using the transform hierarchy, but in this case doing it via a script
/// is cleaner as it (1) allows us to avoid nesting elements under the hand prefab itself and (2)
/// allows the behavior to easily swap between hands depending on which hand is dominant. This
/// also provides a convenient location for the rotation math that keeps the menu aligned "y-up"
/// while still facing the "aim point" and located at the "anchor point." The default anchor- and
/// aim-point values roughly center the menu just above the palms, facing away from the hands.
/// </summary>
public class MatchNonDominantPalmWorldSpaceTransform : MonoBehaviour
{
[SerializeField, Interface(typeof(IHand))]
private Object _leftHand;
[SerializeField, Interface(typeof(IHand))]
private Object _rightHand;
[SerializeField]
private Vector3 _leftAnchorPoint = new Vector3(-0.0608603321f, 0.00953984447f, 0.000258127693f);
[SerializeField]
private Vector3 _leftAimPoint = new Vector3(-0.0749258399f, 0.0893092677f, 0.000258127693f);
[SerializeField]
private Vector3 _rightAnchorPoint = new Vector3(0.0652603358f, -0.011439844f, -0.00455812784f);
[SerializeField]
private Vector3 _rightAimPoint = new Vector3(0.0793258473f, -0.0912092775f, -0.00455812784f);
private IHand LeftHand { get; set; }
private IHand RightHand { get; set; }
protected virtual void Awake()
{
LeftHand = _leftHand as IHand;
RightHand = _rightHand as IHand;
}
private void Update()
{
var anchor = LeftHand.IsDominantHand ? _rightAnchorPoint : _leftAnchorPoint;
var aim = LeftHand.IsDominantHand ? _rightAimPoint : _leftAimPoint;
var hand = LeftHand.IsDominantHand ? RightHand : LeftHand;
Pose wristPose;
if (hand.GetJointPose(HandJointId.HandWristRoot, out wristPose))
{
var anchorPose = new Pose(anchor, Quaternion.identity).GetTransformedBy(wristPose);
var aimPose = new Pose(aim, Quaternion.identity).GetTransformedBy(wristPose);
this.transform.SetPositionAndRotation(anchorPose.position, Quaternion.LookRotation((aimPose.position - anchorPose.position).normalized));
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f9e662d52fa48c44a6c9630c20080c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,156 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples.PalmMenu
{
/// <summary>
/// Example of a bespoke behavior created to control a particular palm menu. This menu primarily controls the swiping behavior,
/// showing and hiding various options and controlling the pagination dots depending on the state of the menu. Note that, for
/// buttons with several possible icons, the states of those buttons are controlled by the PalmMenuExampleButtonHandlers script,
/// which manages the state of the various handlers.
/// </summary>
public class PalmMenuExample : MonoBehaviour
{
[SerializeField]
private PokeInteractable _menuInteractable;
[SerializeField]
private GameObject _menuParent;
[SerializeField]
private RectTransform _menuPanel;
[SerializeField]
private RectTransform[] _buttons;
[SerializeField]
private RectTransform[] _paginationDots;
[SerializeField]
private RectTransform _selectionIndicatorDot;
[SerializeField]
private AnimationCurve _paginationButtonScaleCurve;
[SerializeField]
private float _defaultButtonDistance = 50f;
[SerializeField]
private AudioSource _paginationSwipeAudio;
[SerializeField]
private AudioSource _showMenuAudio;
[SerializeField]
private AudioSource _hideMenuAudio;
private int _currentSelectedButtonIdx;
private void Start()
{
_currentSelectedButtonIdx = CalculateNearestButtonIdx();
_selectionIndicatorDot.position = _paginationDots[_currentSelectedButtonIdx].position;
_menuParent.SetActive(false);
}
private void Update()
{
var nearestButtonIdx = CalculateNearestButtonIdx();
if (nearestButtonIdx != _currentSelectedButtonIdx)
{
_currentSelectedButtonIdx = nearestButtonIdx;
_paginationSwipeAudio.Play();
_selectionIndicatorDot.position = _paginationDots[_currentSelectedButtonIdx].position;
}
if (_menuInteractable.State != InteractableState.Select)
{
LerpToButton();
}
}
private int CalculateNearestButtonIdx()
{
var nearestButtonIdx = 0;
var nearestDistance = float.PositiveInfinity;
for (int idx = 0; idx < _buttons.Length; ++idx)
{
var deltaX = _buttons[idx].localPosition.x + _menuPanel.anchoredPosition.x;
var adjacentIdx = deltaX < 0f ? idx + 1 : idx - 1;
var distanceX = Mathf.Abs(deltaX);
if (distanceX < nearestDistance)
{
nearestButtonIdx = idx;
nearestDistance = distanceX;
}
var normalizingDistance = _defaultButtonDistance;
if (adjacentIdx >= 0 && adjacentIdx < _buttons.Length)
{
normalizingDistance = Mathf.Abs(_buttons[adjacentIdx].localPosition.x - _buttons[idx].localPosition.x);
}
var scale = _paginationButtonScaleCurve.Evaluate(distanceX / normalizingDistance);
_buttons[idx].localScale = scale * Vector3.one;
}
return nearestButtonIdx;
}
private void LerpToButton()
{
var nearestX = _buttons[0].localPosition.x;
var nearestDistance = Mathf.Abs(nearestX + _menuPanel.anchoredPosition.x);
for (int idx = 1; idx < _buttons.Length; ++idx)
{
var x = _buttons[idx].localPosition.x;
var distance = Mathf.Abs(x + _menuPanel.anchoredPosition.x);
if (distance < nearestDistance)
{
nearestX = x;
nearestDistance = distance;
}
}
const float t = 0.2f;
_menuPanel.anchoredPosition = Vector2.Lerp(_menuPanel.anchoredPosition, new Vector2(-nearestX, 0f), t);
}
/// <summary>
/// Show/hide the menu.
/// </summary>
public void ToggleMenu()
{
if (_menuParent.activeSelf)
{
_hideMenuAudio.Play();
_menuParent.SetActive(false);
}
else
{
_showMenuAudio.Play();
_menuParent.SetActive(true);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c8ecef668d3a67a469961fc24349d6df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,203 @@
/*
* 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 TMPro;
using UnityEngine;
namespace Oculus.Interaction.Samples.PalmMenu
{
/// <summary>
/// Example of a bespoke behavior created to react to a particular palm menu. This controls the state
/// of the object that responds to the menu, but also parts of the menu itself, specifically those
/// which depend on the state of the controlled object (swappable icons, various text boxes, etc.).
/// </summary>
public class PalmMenuExampleButtonHandlers : MonoBehaviour
{
[SerializeField]
private GameObject _controlledObject;
[SerializeField]
private Color[] _colors;
[SerializeField]
private GameObject _rotationEnabledIcon;
[SerializeField]
private GameObject _rotationDisabledIcon;
[SerializeField]
private float _rotationLerpSpeed = 1f;
[SerializeField]
private TMP_Text _rotationDirectionText;
[SerializeField]
private string[] _rotationDirectionNames;
[SerializeField]
private GameObject[] _rotationDirectionIcons;
[SerializeField]
private Quaternion[] _rotationDirections;
[SerializeField]
private TMP_Text _elevationText;
[SerializeField]
private float _elevationChangeIncrement;
[SerializeField]
private float _elevationChangeLerpSpeed = 1f;
[SerializeField]
private TMP_Text _shapeNameText;
[SerializeField]
private string[] _shapeNames;
[SerializeField]
private Mesh[] _shapes;
private int _currentColorIdx;
private bool _rotationEnabled;
private int _currentRotationDirectionIdx;
private Vector3 _targetPosition;
private int _currentShapeIdx;
private void Start()
{
_currentColorIdx = _colors.Length;
CycleColor();
_rotationEnabled = false;
ToggleRotationEnabled();
_currentRotationDirectionIdx = _rotationDirections.Length;
CycleRotationDirection();
_targetPosition = _controlledObject.transform.position;
IncrementElevation(true);
IncrementElevation(false);
_currentShapeIdx = _shapes.Length;
CycleShape(true);
}
private void Update()
{
if (_rotationEnabled)
{
var rotation = Quaternion.Slerp(Quaternion.identity, _rotationDirections[_currentRotationDirectionIdx], _rotationLerpSpeed * Time.deltaTime);
_controlledObject.transform.rotation = rotation * _controlledObject.transform.rotation;
}
_controlledObject.transform.position = Vector3.Lerp(_controlledObject.transform.position, _targetPosition, _elevationChangeLerpSpeed * Time.deltaTime);
}
/// <summary>
/// Change the color of the controlled object to the next in the list of allowed colors, looping if the end of the list is reached.
/// </summary>
public void CycleColor()
{
_currentColorIdx += 1;
if (_currentColorIdx >= _colors.Length)
{
_currentColorIdx = 0;
}
_controlledObject.GetComponent<Renderer>().material.SetColor("_Color", _colors[_currentColorIdx]);
}
/// <summary>
/// Toggle whether or not rotation is enabled, and set the icon of the controlling button to display what will happen next time the button is pressed.
/// </summary>
public void ToggleRotationEnabled()
{
_rotationEnabled = !_rotationEnabled;
_rotationEnabledIcon.SetActive(!_rotationEnabled);
_rotationDisabledIcon.SetActive(_rotationEnabled);
}
/// <summary>
/// Change the rotation direction of the controlled object to the next in the list of allowed directions, looping if the end of the list is reached.
/// Set the icon of the controlling button to display what will happen next time the button is pressed.
/// </summary>
public void CycleRotationDirection()
{
Debug.Assert(_rotationDirectionNames.Length == _rotationDirections.Length);
Debug.Assert(_rotationDirectionNames.Length == _rotationDirectionIcons.Length);
_currentRotationDirectionIdx += 1;
if (_currentRotationDirectionIdx >= _rotationDirections.Length)
{
_currentRotationDirectionIdx = 0;
}
int nextRotationDirectionIdx = _currentRotationDirectionIdx + 1;
if (nextRotationDirectionIdx >= _rotationDirections.Length)
{
nextRotationDirectionIdx = 0;
}
_rotationDirectionText.text = _rotationDirectionNames[nextRotationDirectionIdx];
for (int idx = 0; idx < _rotationDirections.Length; ++idx)
{
_rotationDirectionIcons[idx].SetActive(idx == nextRotationDirectionIdx);
}
}
/// <summary>
/// Change the target elevation of the controlled object in the requested direction, within the limits [0.2, 2].
/// Set the text to display the new target elevation.
/// </summary>
public void IncrementElevation(bool up)
{
float increment = _elevationChangeIncrement;
if (!up)
{
increment *= -1f;
}
_targetPosition = new Vector3(_targetPosition.x, Mathf.Clamp(_targetPosition.y + increment, 0.2f, 2f), _targetPosition.z);
_elevationText.text = "Elevation: " + _targetPosition.y.ToString("0.0");
}
/// <summary>
/// Change the shape of the controlled object to the next or previous in the list of allowed shapes, depending on the requested direction, looping beyond the bounds of the list.
/// Set the text to display the name of the current shape.
/// </summary>
public void CycleShape(bool cycleForward)
{
Debug.Assert(_shapeNames.Length == _shapes.Length);
_currentShapeIdx += cycleForward ? 1 : -1;
if (_currentShapeIdx >= _shapes.Length)
{
_currentShapeIdx = 0;
}
else if (_currentShapeIdx < 0)
{
_currentShapeIdx = _shapes.Length - 1;
}
_shapeNameText.text = _shapeNames[_currentShapeIdx];
_controlledObject.GetComponent<MeshFilter>().mesh = _shapes[_currentShapeIdx];
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 65307bdd58ef73943b02570244339263
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,96 @@
/*
* 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 TMPro;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Samples
{
public class PoseUseSample : MonoBehaviour
{
[SerializeField, Interface(typeof(IHmd))]
private UnityEngine.Object _hmd;
private IHmd Hmd { get; set; }
[SerializeField]
private ActiveStateSelector[] _poses;
[SerializeField]
private Material[] _onSelectIcons;
[SerializeField]
private GameObject _poseActiveVisualPrefab;
private GameObject[] _poseActiveVisuals;
protected virtual void Awake()
{
Hmd = _hmd as IHmd;
}
protected virtual void Start()
{
this.AssertField(Hmd, nameof(Hmd));
this.AssertField(_poseActiveVisualPrefab, nameof(_poseActiveVisualPrefab));
_poseActiveVisuals = new GameObject[_poses.Length];
for (int i = 0; i < _poses.Length; i++)
{
_poseActiveVisuals[i] = Instantiate(_poseActiveVisualPrefab);
_poseActiveVisuals[i].GetComponentInChildren<TextMeshPro>().text = _poses[i].name;
_poseActiveVisuals[i].GetComponentInChildren<ParticleSystemRenderer>().material = _onSelectIcons[i];
_poseActiveVisuals[i].SetActive(false);
int poseNumber = i;
_poses[i].WhenSelected += () => ShowVisuals(poseNumber);
_poses[i].WhenUnselected += () => HideVisuals(poseNumber);
}
}
private void ShowVisuals(int poseNumber)
{
if (!Hmd.TryGetRootPose(out Pose hmdPose))
{
return;
}
Vector3 spawnSpot = hmdPose.position + hmdPose.forward;
_poseActiveVisuals[poseNumber].transform.position = spawnSpot;
_poseActiveVisuals[poseNumber].transform.LookAt(2 * _poseActiveVisuals[poseNumber].transform.position - hmdPose.position);
var hands = _poses[poseNumber].GetComponents<HandRef>();
Vector3 visualsPos = Vector3.zero;
foreach (var hand in hands)
{
hand.GetRootPose(out Pose wristPose);
Vector3 forward = hand.Handedness == Handedness.Left ? wristPose.right : -wristPose.right;
visualsPos += wristPose.position + forward * .15f + Vector3.up * .02f;
}
_poseActiveVisuals[poseNumber].transform.position = visualsPos / hands.Length;
_poseActiveVisuals[poseNumber].gameObject.SetActive(true);
}
private void HideVisuals(int poseNumber)
{
_poseActiveVisuals[poseNumber].gameObject.SetActive(false);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 14e32a77be61d8b4e906c12d189faef7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,76 @@
/*
* 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 UnityEngine;
using UnityEngine.Events;
namespace Oculus.Interaction.Samples
{
public class RespawnOnDrop : MonoBehaviour
{
[SerializeField]
private float _yThresholdForRespawn;
[SerializeField]
private UnityEvent _whenRespawned = new UnityEvent();
public UnityEvent WhenRespawned => _whenRespawned;
// cached starting transform
private Vector3 _initialPosition;
private Quaternion _initialRotation;
private Vector3 _initialScale;
private TwoGrabFreeTransformer[] _freeTransformers;
private Rigidbody _rigidBody;
protected virtual void OnEnable()
{
_initialPosition = transform.position;
_initialRotation = transform.rotation;
_initialScale = transform.localScale;
_freeTransformers = GetComponents<TwoGrabFreeTransformer>();
_rigidBody = GetComponent<Rigidbody>();
}
protected virtual void Update()
{
if (transform.position.y < _yThresholdForRespawn)
{
transform.position = _initialPosition;
transform.rotation = _initialRotation;
transform.localScale = _initialScale;
if (_rigidBody)
{
_rigidBody.velocity = Vector3.zero;
_rigidBody.angularVelocity = Vector3.zero;
}
foreach (var freeTransformer in _freeTransformers)
{
freeTransformer.MarkAsBaseScale();
}
_whenRespawned.Invoke();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eeb09e4f18fd0954796cd0b766d28fa4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,190 @@
/*
* 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 UnityEngine;
using UnityEngine.Events;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Samples
{
/// <summary>
/// Raises events when an object is rotated relative to a provided transform. Rotated
/// events will be raised when the rotation exceeds a provided angle threshold, in degrees.
/// Events are raised only once per directional sweep, so if an event was fired while angle
/// was increasing, the next must fire while angle decreases.
/// </summary>
public class RotationAudioEvents : MonoBehaviour
{
private enum Direction
{
None,
Opening,
Closing,
}
[SerializeField, Interface(typeof(IInteractableView))]
private UnityEngine.Object _interactableView;
[Tooltip("Transform to track rotation of. If not provided, transform of this component is used.")]
[SerializeField, Optional]
private Transform _trackedTransform;
[SerializeField]
private Transform _relativeTo;
[Tooltip("The angle delta at which the threshold crossed event will be fired.")]
[SerializeField]
private float _thresholdDeg = 20f;
[Tooltip("Maximum rotation arc within which the crossed event will be triggered.")]
[SerializeField, Range(1f, 150f)]
private float _maxRangeDeg = 150f;
[SerializeField]
private UnityEvent _whenRotationStarted = new UnityEvent();
[SerializeField]
private UnityEvent _whenRotationEnded = new UnityEvent();
[SerializeField]
private UnityEvent _whenRotatedOpen = new UnityEvent();
[SerializeField]
private UnityEvent _whenRotatedClosed = new UnityEvent();
public UnityEvent WhenRotationStarted => _whenRotationStarted;
public UnityEvent WhenRotationEnded => _whenRotationEnded;
public UnityEvent WhenRotatedOpen => _whenRotatedOpen;
public UnityEvent WhenRotatedClosed => _whenRotatedClosed;
private IInteractableView InteractableView;
private Transform TrackedTransform
{
get => _trackedTransform == null ? transform : _trackedTransform;
}
private float _baseDelta;
private bool _isRotating;
private Direction _lastCrossedDirection;
protected bool _started;
private void RotationStarted()
{
_baseDelta = GetTotalDelta();
_lastCrossedDirection = Direction.None;
_whenRotationStarted.Invoke();
}
private void RotationEnded()
{
_whenRotationEnded.Invoke();
}
private Quaternion GetCurrentRotation()
{
return Quaternion.Inverse(_relativeTo.rotation) * TrackedTransform.rotation;
}
private float GetTotalDelta()
{
return Quaternion.Angle(_relativeTo.rotation, GetCurrentRotation());
}
private void UpdateRotation()
{
float totalDelta = GetTotalDelta();
if (totalDelta > _maxRangeDeg)
{
return;
}
if (Mathf.Abs(totalDelta - _baseDelta) > _thresholdDeg)
{
var _direction = totalDelta - _baseDelta > 0 ?
Direction.Opening :
Direction.Closing;
if (_direction != _lastCrossedDirection)
{
_lastCrossedDirection = _direction;
if (_direction == Direction.Opening)
{
_whenRotatedOpen.Invoke();
}
else
{
_whenRotatedClosed.Invoke();
}
}
}
if (_lastCrossedDirection == Direction.Opening)
{
_baseDelta = Mathf.Max(_baseDelta, totalDelta);
}
else if (_lastCrossedDirection == Direction.Closing)
{
_baseDelta = Mathf.Min(_baseDelta, totalDelta);
}
}
protected virtual void Awake()
{
InteractableView = _interactableView as IInteractableView;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertField(InteractableView, nameof(InteractableView));
this.AssertField(TrackedTransform, nameof(TrackedTransform));
this.AssertField(_relativeTo, nameof(_relativeTo));
this.EndStart(ref _started);
}
protected virtual void Update()
{
bool wasRotating = _isRotating;
_isRotating = InteractableView.State == InteractableState.Select;
if (!_isRotating)
{
if (wasRotating)
{
RotationEnded();
}
}
else
{
if (!wasRotating)
{
RotationStarted();
}
UpdateRotation();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 328b20022c3885e43a8861c57af4caf5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples
{
public class SamplesInfoPanel : MonoBehaviour
{
public void HandleUrlButton(string url)
{
Application.OpenURL(url);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc3d610027e65de40bcf0a2bc84ff198
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,182 @@
/*
* 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 UnityEngine;
using UnityEngine.Events;
using UnityEngine.Assertions;
namespace Oculus.Interaction.Samples
{
/// <summary>
/// Raises events when an object is scaled up or down. Events are raised in steps,
/// meaning scale changes are only responded to when the scale magnitude delta since
/// last step exceeds a provided amount.
/// </summary>
public class ScaleAudioEvents : MonoBehaviour
{
private enum Direction
{
None,
ScaleUp,
ScaleDown,
}
[SerializeField, Interface(typeof(IInteractableView))]
private UnityEngine.Object _interactableView;
[Tooltip("Transform to track scale of. If not provided, transform of this component is used.")]
[SerializeField, Optional]
private Transform _trackedTransform;
[Tooltip("The increase in scale magnitude that will fire the step event")]
[SerializeField]
private float _stepSize = 0.4f;
[Tooltip("Events will not be fired more frequently than this many times per second")]
[SerializeField]
private int _maxEventFreq = 20;
[SerializeField]
private UnityEvent _whenScalingStarted = new UnityEvent();
[SerializeField]
private UnityEvent _whenScalingEnded = new UnityEvent();
[SerializeField]
private UnityEvent _whenScaledUp = new UnityEvent();
[SerializeField]
private UnityEvent _whenScaledDown = new UnityEvent();
public UnityEvent WhenScalingStarted => _whenScalingStarted;
public UnityEvent WhenScalingEnded => _whenScalingEnded;
public UnityEvent WhenScaledUp => _whenScaledUp;
public UnityEvent WhenScaledDown => _whenScaledDown;
private IInteractableView InteractableView;
private Transform TrackedTransform
{
get => _trackedTransform == null ? transform : _trackedTransform;
}
private bool _isScaling;
private Vector3 _lastStep;
private float _lastEventTime;
private Direction _direction = Direction.None;
protected bool _started;
private void ScalingStarted()
{
_lastStep = TrackedTransform.localScale;
_whenScalingStarted.Invoke();
}
private void ScalingEnded()
{
_whenScalingEnded.Invoke();
}
private float GetTotalDelta(out Direction direction)
{
float prevMagnitude = _lastStep.magnitude;
float newMagnitude = TrackedTransform.localScale.magnitude;
if (newMagnitude == prevMagnitude)
{
direction = Direction.None;
}
else
{
direction = newMagnitude > prevMagnitude ? Direction.ScaleUp : Direction.ScaleDown;
}
return direction == Direction.ScaleUp ?
newMagnitude - prevMagnitude :
prevMagnitude - newMagnitude;
}
private void UpdateScaling()
{
if (_stepSize <= 0 || _maxEventFreq <= 0)
{
return;
}
float effectiveStepSize = _stepSize;
float totalDelta = GetTotalDelta(out _direction);
if (totalDelta > effectiveStepSize)
{
_lastStep = TrackedTransform.localScale;
float timeSince = Time.time - _lastEventTime;
if (timeSince >= 1f / _maxEventFreq)
{
_lastEventTime = Time.time;
if (_direction == Direction.ScaleUp)
{
_whenScaledUp.Invoke();
}
else
{
_whenScaledDown.Invoke();
}
}
}
}
protected virtual void Awake()
{
InteractableView = _interactableView as IInteractableView;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
this.AssertField(InteractableView, nameof(InteractableView));
this.AssertField(TrackedTransform, nameof(TrackedTransform));
this.EndStart(ref _started);
}
protected virtual void Update()
{
bool wasScaling = _isScaling;
_isScaling = InteractableView.State == InteractableState.Select;
if (!_isScaling)
{
if (wasScaling)
{
ScalingEnded();
}
}
else
{
if (!wasScaling)
{
ScalingStarted();
}
UpdateScaling();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ced30fb0d3378c442934280394576509
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples
{
public class ScaleModifier : MonoBehaviour
{
public void SetScaleX(float x)
{
transform.localScale = new Vector3(x, transform.localScale.y, transform.localScale.z);
}
public void SetScaleY(float y)
{
transform.localScale = new Vector3(transform.localScale.x, y, transform.localScale.z);
}
public void SetScaleZ(float z)
{
transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, z);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 586427432b00198488881015ab3989b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,73 @@
/*
* 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;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Oculus.Interaction.Samples
{
public class SceneLoader : MonoBehaviour
{
private bool _loading = false;
public Action<string> WhenLoadingScene = delegate { };
public Action<string> WhenSceneLoaded = delegate { };
private int _waitingCount = 0;
public void Load(string sceneName)
{
if (_loading) return;
_loading = true;
// make sure we wait for all parties concerned to let us know they're ready to load
_waitingCount = WhenLoadingScene.GetInvocationList().Length-1; // remove the count for the blank delegate
if (_waitingCount == 0)
{
// if nobody else cares just set the preload to go directly to the loading of the scene
HandleReadyToLoad(sceneName);
}
else
{
WhenLoadingScene.Invoke(sceneName);
}
}
// this should be called after handling any pre-load tasks (e.g. fade to white) by anyone who regsistered with WhenLoadingScene in order for the loading to proceed
public void HandleReadyToLoad(string sceneName)
{
_waitingCount--;
if (_waitingCount <= 0)
{
StartCoroutine(LoadSceneAsync(sceneName));
}
}
private IEnumerator LoadSceneAsync(string sceneName)
{
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
while (!asyncLoad.isDone)
{
yield return null;
}
WhenSceneLoaded.Invoke(sceneName);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c4c1b38c49ffa449b585d54fcdc4a8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,26 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples
{
public class ShouldHideHandOnGrab : MonoBehaviour {}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2b5b0c4a3ad10941918d5e111f95b70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6f4127fd63ab7aa4b8ef54daaf07ab59
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,54 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples
{
public class ConstantRotation : MonoBehaviour
{
[SerializeField]
private float _rotationSpeed;
[SerializeField]
private Vector3 _localAxis = Vector3.up;
#region Properties
public float RotationSpeed
{
get => _rotationSpeed;
set => _rotationSpeed = value;
}
public Vector3 LocalAxis
{
get => _localAxis;
set => _localAxis = value;
}
#endregion
protected virtual void Update()
{
transform.Rotate(_localAxis, _rotationSpeed * Time.deltaTime, Space.Self);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37f55338c76a0114c825124a14f571f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,62 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples
{
public class ListSnapPoseDelegateRoundedBoxVisual : MonoBehaviour
{
[SerializeField]
private ListSnapPoseDelegate _listSnapPoseDelegate;
[SerializeField]
private RoundedBoxProperties _properties;
[SerializeField]
private SnapInteractable _snapInteractable;
[SerializeField]
private float _minSize = 0f;
[SerializeField]
private ProgressCurve _curve;
private float _targetWidth = 0;
private float _startWidth = 0;
protected virtual void LateUpdate()
{
float targetWidth = Mathf.Max(_listSnapPoseDelegate.Size, _minSize);
if (targetWidth != _targetWidth)
{
_targetWidth = targetWidth;
_curve.Start();
_startWidth = _properties.Width;
}
_properties.Width = Mathf.Lerp(_startWidth, _targetWidth, _curve.Progress());
_properties.BorderColor =
_snapInteractable.Interactors.Count != _snapInteractable.SelectingInteractors.Count
? new Color(1, 1, 1, 1)
: new Color(1, 1, 1, 0.5f);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 295aa681c5c912f429c7deaba8668f9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,45 @@
/*
* 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 UnityEngine;
namespace Oculus.Interaction.Samples
{
public class StayInView : MonoBehaviour
{
[SerializeField]
private Transform _eyeCenter;
[SerializeField]
private float _extraDistanceForward = 0;
[SerializeField]
private bool _zeroOutEyeHeight = true;
void Update()
{
transform.rotation = Quaternion.identity;
transform.position = _eyeCenter.position;
transform.Rotate(0, _eyeCenter.rotation.eulerAngles.y, 0, Space.Self);
transform.position = _eyeCenter.position + transform.forward.normalized * _extraDistanceForward;
if (_zeroOutEyeHeight)
transform.position = new Vector3(transform.position.x, 0, transform.position.z);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 56687da4eed4f0541b3e4a77d8008acd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b4017bc80c7c2c846bd23987b8aa006e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More