Initialer Upload neues Unity-Projekt
This commit is contained in:
201
Assets/Oculus/Interaction/Samples/Scripts/AudioPhysics.cs
Normal file
201
Assets/Oculus/Interaction/Samples/Scripts/AudioPhysics.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c94ad25aabf9b34eb79cd433132dee6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
259
Assets/Oculus/Interaction/Samples/Scripts/AudioTrigger.cs
Normal file
259
Assets/Oculus/Interaction/Samples/Scripts/AudioTrigger.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 925ef87c5bafc37469a2f7ec825dee4b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Oculus/Interaction/Samples/Scripts/Body.meta
Normal file
8
Assets/Oculus/Interaction/Samples/Scripts/Body.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22ba64cf9e2c3da45a1a9c295d54f305
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7de5613aa5f224248b2cef5814c0a1e4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
115
Assets/Oculus/Interaction/Samples/Scripts/Body/LockedBodyPose.cs
Normal file
115
Assets/Oculus/Interaction/Samples/Scripts/Body/LockedBodyPose.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91ff0dbc3add03444934d0bd81a06242
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a2c3d97f3738fb45a0b0679a6b88ee4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
138
Assets/Oculus/Interaction/Samples/Scripts/CarouselView.cs
Normal file
138
Assets/Oculus/Interaction/Samples/Scripts/CarouselView.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80e08504c1d9ec34fbdd09d754ea8b5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
65
Assets/Oculus/Interaction/Samples/Scripts/ColorChanger.cs
Normal file
65
Assets/Oculus/Interaction/Samples/Scripts/ColorChanger.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4a2bba66be8bd5409a1b82f1ce54853
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
91
Assets/Oculus/Interaction/Samples/Scripts/CountdownTimer.cs
Normal file
91
Assets/Oculus/Interaction/Samples/Scripts/CountdownTimer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80adaa84dcdec2b41981fa223dab63f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e81f6da227209c49bdc78260f44e85b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a540e547f62942468b7ff96e6e12bc1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de5436acf1069264a8a66caf1eed0513
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a423463f353e4fc4285974c23bd5258e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6586592911e33447986fc3d68dc1c0a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ae31ae32e3e80a48806ef598d17341c
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 826882dcb55017a46ab71dddc25ab18e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acb15a35c5f64f043adf3c8fd8d3fe99
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e94a51fc47eedd84db08e009bc539371
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e12936d5396e634480523f2eb9bcbea
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f38a2c2a6f2edf246a68b41b406dd964
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d03fabf79ac272479773d0172fb4740
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Oculus/Interaction/Samples/Scripts/HandGrabUse/Effects/WaterSpray/Drops_Tiled_Normal-Map.png
(Stored with Git LFS)
Normal file
BIN
Assets/Oculus/Interaction/Samples/Scripts/HandGrabUse/Effects/WaterSpray/Drops_Tiled_Normal-Map.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14c4f10921fa2ed46b4a9be3c07abeee
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures:
|
||||
- _Stamp: {instanceID: 0}
|
||||
- _MainTex: {instanceID: 0}
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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:
|
||||
@ -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: []
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44092c54cacca1046ac25e52c878a0c7
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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() { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1302feb954c92f43bb3d72f377721ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb0e4cf711387a24ba61894d49bb7cdd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94217a4c67be3f143b2ac00d190c8bde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c201135db8d52149949bab567cd1fd6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b637179e0cc734442bf0e50111bcc634
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9dd6a62d4597094408a690179bd0f609
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c686f13208596eb4b8272d78e582aec9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e71d82996a6bf84e94d1cd4dc728f5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de6bd207a644f864b8cc682622c8af52
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c1459b1323037f40adf896ae60f0a57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84f47d10845aedc409ea9f6cec618b5a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
46
Assets/Oculus/Interaction/Samples/Scripts/LookAtTarget.cs
Normal file
46
Assets/Oculus/Interaction/Samples/Scripts/LookAtTarget.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e416343365a0d340be803f69515deb0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Oculus/Interaction/Samples/Scripts/PalmMenu.meta
Normal file
8
Assets/Oculus/Interaction/Samples/Scripts/PalmMenu.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cf7743ec8f39e8438036f29b52fbaba
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f87c900294bbee44b7f74920f84bb29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f9e662d52fa48c44a6c9630c20080c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8ecef668d3a67a469961fc24349d6df
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65307bdd58ef73943b02570244339263
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
96
Assets/Oculus/Interaction/Samples/Scripts/PoseUseSample.cs
Normal file
96
Assets/Oculus/Interaction/Samples/Scripts/PoseUseSample.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14e32a77be61d8b4e906c12d189faef7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
76
Assets/Oculus/Interaction/Samples/Scripts/RespawnOnDrop.cs
Normal file
76
Assets/Oculus/Interaction/Samples/Scripts/RespawnOnDrop.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eeb09e4f18fd0954796cd0b766d28fa4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
190
Assets/Oculus/Interaction/Samples/Scripts/RotationAudioEvents.cs
Normal file
190
Assets/Oculus/Interaction/Samples/Scripts/RotationAudioEvents.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 328b20022c3885e43a8861c57af4caf5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc3d610027e65de40bcf0a2bc84ff198
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
182
Assets/Oculus/Interaction/Samples/Scripts/ScaleAudioEvents.cs
Normal file
182
Assets/Oculus/Interaction/Samples/Scripts/ScaleAudioEvents.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ced30fb0d3378c442934280394576509
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
Assets/Oculus/Interaction/Samples/Scripts/ScaleModifier.cs
Normal file
40
Assets/Oculus/Interaction/Samples/Scripts/ScaleModifier.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 586427432b00198488881015ab3989b6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
73
Assets/Oculus/Interaction/Samples/Scripts/SceneLoader.cs
Normal file
73
Assets/Oculus/Interaction/Samples/Scripts/SceneLoader.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c4c1b38c49ffa449b585d54fcdc4a8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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 {}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2b5b0c4a3ad10941918d5e111f95b70
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Oculus/Interaction/Samples/Scripts/Snap.meta
Normal file
8
Assets/Oculus/Interaction/Samples/Scripts/Snap.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f4127fd63ab7aa4b8ef54daaf07ab59
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37f55338c76a0114c825124a14f571f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 295aa681c5c912f429c7deaba8668f9b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
Assets/Oculus/Interaction/Samples/Scripts/StayInView.cs
Normal file
45
Assets/Oculus/Interaction/Samples/Scripts/StayInView.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Oculus/Interaction/Samples/Scripts/StayInView.cs.meta
Normal file
11
Assets/Oculus/Interaction/Samples/Scripts/StayInView.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56687da4eed4f0541b3e4a77d8008acd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
Reference in New Issue
Block a user