Add ultimate xr

This commit is contained in:
2024-08-06 21:58:35 +02:00
parent 864033bf10
commit 7165bacd9d
3952 changed files with 2162037 additions and 35 deletions

View File

@@ -0,0 +1,45 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ArmoredDoor.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using UltimateXR.Avatar;
using UltimateXR.CameraUtils;
using UnityEngine;
namespace UltimateXR.Examples.FullScene.Doors
{
/// <summary>
/// Component to model an automatic door that can not be opened from the outside unless
/// <see cref="AutomaticDoor.OpenDoor" /> is called explicitly.
/// </summary>
public class ArmoredDoor : AutomaticDoor
{
#region Protected Overrides AutomaticDoor
/// <summary>
/// Gets whether automatic opening is allowed. Only from the inside and the if avatar is not peeking through geometry.
/// </summary>
protected override bool IsOpeningAllowed => IsAvatarInside(UxrAvatar.LocalAvatar) && !UxrCameraWallFade.IsAvatarPeekingThroughGeometry(UxrAvatar.LocalAvatar);
#endregion
#region Private Methods
/// <summary>
/// Checks whether the avatar is on the back side of the door.
/// </summary>
/// <param name="avatar">Avatar to check</param>
/// <returns>
/// Whether the avatar is on the back side of the door. This means it is behind the plane pointed by the floor
/// center and looking towards the negative Z.
/// </returns>
private bool IsAvatarInside(UxrAvatar avatar)
{
Plane doorPlane = new Plane(FloorCenter.forward, FloorCenter.position);
return !doorPlane.GetSide(avatar.CameraPosition);
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,179 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AutomaticDoor.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Linq;
using UltimateXR.Animation.Interpolation;
using UltimateXR.Audio;
using UltimateXR.Avatar;
using UltimateXR.Core.Components;
using UnityEngine;
namespace UltimateXR.Examples.FullScene.Doors
{
/// <summary>
/// Component to model de behavior of a door that opens automatically when the user gets near and closes
/// when the user moves away from it.
/// </summary>
public class AutomaticDoor : UxrComponent
{
#region Inspector Properties/Serialized Fields
[SerializeField] private Transform _floorCenter;
[SerializeField] private Transform _leftDoor;
[SerializeField] private Transform _rightDoor;
[SerializeField] private Vector3 _leftOpenLocalOffset;
[SerializeField] private Vector3 _rightOpenLocalOffset;
[SerializeField] private float _openDelaySeconds;
[SerializeField] private float _openDurationSeconds = 0.8f;
[SerializeField] private float _openDistance = 1.5f;
[SerializeField] private float _closeDistance = 2.0f;
[SerializeField] private UxrEasing _openEasing = UxrEasing.EaseOutCubic;
[SerializeField] private UxrEasing _closeEasing = UxrEasing.EaseInCubic;
[SerializeField] private UxrAudioSample _audioOpen;
[SerializeField] private UxrAudioSample _audioClose;
#endregion
#region Public Types & Data
/// <summary>
/// Gets a value from 0.0 (completely closed) to 1.0 (completely open) telling how open the door currently is.
/// </summary>
public float OpenValue { get; private set; }
/// <summary>
/// Gets if the door is open or opening.
/// </summary>
public bool IsOpen { get; private set; }
#endregion
#region Public Methods
/// <summary>
/// Opens the door. This can be used in child implementations where opening can be disallowed under certain
/// conditions. See <see cref="ArmoredDoor" /> for an example.
/// </summary>
/// <param name="playSound">Whether to play the open sound</param>
public void OpenDoor(bool playSound)
{
BeginSync();
IsOpen = true;
if (playSound)
{
_audioOpen.Play(FloorCenter.position);
}
EndSyncMethod(new object[] { playSound });
}
/// <summary>
/// Closes the door.
/// </summary>
/// <param name="playSound">Whether to play the close sound</param>
public void CloseDoor(bool playSound)
{
BeginSync();
// Over closing distance and door completely open: close door
IsOpen = false;
_openDelayTimer = 0.0f;
if (playSound)
{
_audioClose.Play(FloorCenter.position);
}
EndSyncMethod(new object[] { playSound });
}
#endregion
#region Unity
/// <summary>
/// Stores initial state.
/// </summary>
protected override void Awake()
{
base.Awake();
_leftStartLocalPosition = _leftDoor.localPosition;
_rightStartLocalPosition = _rightDoor.localPosition;
}
/// <summary>
/// Updates the door.
/// </summary>
private void Update()
{
if (UxrAvatar.LocalAvatar == null)
{
return;
}
// Check distance to door
UxrAvatar closestAvatar = UxrAvatar.EnabledComponents.OrderBy(a => Vector3.Distance(a.CameraFloorPosition, FloorCenter.position)).FirstOrDefault();
if (closestAvatar == UxrAvatar.LocalAvatar)
{
// The closest avatar will determine the door state.
float closestAvatarDistance = Vector3.Distance(closestAvatar.CameraFloorPosition, FloorCenter.position);
if (closestAvatarDistance < _openDistance && Mathf.Approximately(OpenValue, 0.0f))
{
_openDelayTimer += Time.deltaTime;
if (_openDelayTimer > _openDelaySeconds && IsOpeningAllowed)
{
// Within opening distance, door completely closed and opening allowed: open door
OpenDoor(true);
}
}
else if (closestAvatarDistance > _closeDistance && Mathf.Approximately(OpenValue, 1.0f))
{
CloseDoor(true);
}
}
// Update timer and perform interpolation
OpenValue = Mathf.Clamp01(OpenValue + Time.deltaTime * (1.0f / _openDurationSeconds) * (IsOpen ? 1.0f : -1.0f));
float t = UxrInterpolator.GetInterpolationFactor(OpenValue, IsOpen ? _openEasing : _closeEasing);
_leftDoor.transform.localPosition = Vector3.Lerp(_leftStartLocalPosition, _leftStartLocalPosition + _leftOpenLocalOffset, t);
_rightDoor.transform.localPosition = Vector3.Lerp(_rightStartLocalPosition, _rightStartLocalPosition + _rightOpenLocalOffset, t);
}
#endregion
#region Protected Types & Data
/// <summary>
/// Gets if opening is allowed. Can be used by child implementations to disallow opening under certain conditions. See
/// <see cref="ArmoredDoor" /> for an example.
/// </summary>
protected virtual bool IsOpeningAllowed => true;
/// <summary>
/// Gets the door center at floor level.
/// </summary>
protected Transform FloorCenter => _floorCenter != null ? _floorCenter : transform;
#endregion
#region Private Types & Data
private float _openDelayTimer;
private Vector3 _leftStartLocalPosition;
private Vector3 _rightStartLocalPosition;
#endregion
}
}

View File

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

View File

@@ -0,0 +1,347 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="HandScanner.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UltimateXR.Animation.Interpolation;
using UltimateXR.Animation.Materials;
using UltimateXR.Audio;
using UltimateXR.Avatar;
using UltimateXR.Core;
using UltimateXR.Core.Components;
using UltimateXR.Extensions.Unity.Math;
using UltimateXR.Extensions.Unity.Render;
using UnityEngine;
namespace UltimateXR.Examples.FullScene.Doors
{
/// <summary>
/// Component that handles the hand scanning required to open an <see cref="ArmoredDoor" />.
/// </summary>
public class HandScanner : UxrComponent
{
#region Inspector Properties/Serialized Fields
[SerializeField] private ArmoredDoor _armoredDoor;
[SerializeField] private Renderer _validLight;
[SerializeField] private Renderer _invalidLight;
[SerializeField] private Renderer _scannerBeam;
[SerializeField] private Vector3 _scannerBeamTopLocalPos;
[SerializeField] private Vector3 _scannerBeamBottomLocalPos;
[SerializeField] private int _beamCount = 5;
[SerializeField] [Range(0.0f, 1.0f)] private float _beamTrailDelay = 0.1f;
[SerializeField] private UxrEasing _beamEeasing = UxrEasing.EaseInOutQuint;
[SerializeField] private Vector3 _beamMaxScale = Vector3.one;
[SerializeField] private Renderer _handRendererLeft;
[SerializeField] private Renderer _handRendererRight;
[SerializeField] private UxrHandSide _defaultHandSide = UxrHandSide.Right;
[SerializeField] private BoxCollider _handBoxValidPos;
[SerializeField] private float _scanSeconds = 1.5f;
[SerializeField] private float _resultSeconds = 2.0f;
[SerializeField] private UxrAudioSample _audioScan;
[SerializeField] private UxrAudioSample _audioError;
[SerializeField] private UxrAudioSample _audioOk;
#endregion
#region Public Types & Data
/// <summary>
/// Event called right after a hand was scanned. Parameters are the avatar that was scanned and if the scan granted
/// access.
/// </summary>
public event Action<UxrAvatar, UxrHandSide, bool> HandScanned;
#endregion
#region Unity
/// <summary>
/// Initializes the component.
/// </summary>
protected override void Awake()
{
base.Awake();
_colorValid = _validLight.sharedMaterial.GetColor(UxrConstants.Shaders.StandardColorVarName);
_colorInvalid = _invalidLight.sharedMaterial.GetColor(UxrConstants.Shaders.StandardColorVarName);
_handSide = _defaultHandSide;
_beamScale = _scannerBeam.transform.localScale;
_beams.Add(_scannerBeam);
for (int i = 0; i < _beamCount - 1; ++i)
{
_beams.Add(Instantiate(_scannerBeam.gameObject, _scannerBeam.transform.position, _scannerBeam.transform.rotation, _scannerBeam.transform.parent).GetComponent<Renderer>());
}
}
/// <summary>
/// Sets the default scanning state.
/// </summary>
protected override void OnEnable()
{
base.OnEnable();
_scanReady = true;
_validLight.enabled = false;
_invalidLight.enabled = false;
_handRendererLeft.enabled = _defaultHandSide == UxrHandSide.Left;
_handRendererRight.enabled = _defaultHandSide == UxrHandSide.Right;
_handRendererLeft.material.color = ColorExt.ColorAlpha(_handRendererLeft.material.color, _defaultHandSide == UxrHandSide.Left ? 1.0f : 0.0f);
_handRendererRight.material.color = ColorExt.ColorAlpha(_handRendererRight.material.color, _defaultHandSide == UxrHandSide.Right ? 1.0f : 0.0f);
EnableBeams(false);
}
/// <summary>
/// Disables the component.
/// </summary>
protected override void OnDisable()
{
base.OnEnable();
_scanReady = true;
_validLight.enabled = false;
_invalidLight.enabled = false;
EnableBeams(false);
}
/// <summary>
/// Updates the component. Performs the scanning process.
/// </summary>
private void Update()
{
if (UxrAvatar.LocalAvatar == null || !_scanReady)
{
return;
}
// Update scanning & beam
if (_scanTimer < 0.0f)
{
if (_armoredDoor != null && _armoredDoor.IsOpen)
{
// If we are controlling an armored door and it is already open, ignore the hands.
}
else
{
// Waiting for hand to be scanned. Look for hand:
if (UxrAvatar.LocalAvatar.GetHandBone(UxrHandSide.Left).position.IsInsideBox(_handBoxValidPos))
{
StartScan(UxrAvatar.LocalAvatar, UxrHandSide.Left);
}
else if (UxrAvatar.LocalAvatar.GetHandBone(UxrHandSide.Right).position.IsInsideBox(_handBoxValidPos))
{
StartScan(UxrAvatar.LocalAvatar, UxrHandSide.Right);
}
}
}
else
{
// Hand is scanning
_scanTimer += Time.deltaTime;
EnableBeams(true);
for (int i = 0; i < _beams.Count; ++i)
{
float beamStartTime = i / (_beams.Count == 1 ? 1.0f : _beams.Count - 1.0f) * _beamTrailDelay * _scanSeconds;
float beamDuration = _scanSeconds - _beamTrailDelay;
float t = Mathf.Clamp01((_scanTimer - beamStartTime) / beamDuration);
float tScale = 1.0f - Mathf.Abs(t - 0.5f) * 2.0f;
_beams[i].transform.localPosition = Vector3.Lerp(_scannerBeamTopLocalPos, _scannerBeamBottomLocalPos, UxrInterpolator.GetInterpolationFactor(t, _beamEeasing));
_beams[i].transform.localScale = Vector3.Lerp(_beamScale, _beamMaxScale, Mathf.Pow(tScale, 8.0f));
}
// Check for conditions
if (_avatarScanning == UxrAvatar.LocalAvatar)
{
if (UxrAvatar.LocalAvatar.GetHandBone(_handSide).position.IsInsideBox(_handBoxValidPos))
{
if (!UxrAvatar.LocalAvatar.GetHandBone(UxrUtils.GetOppositeSide(_handSide)).position.IsInsideBox(_handBoxValidPos))
{
// Keep scanning
if (_scanTimer > _scanSeconds)
{
ProcessScanResult(UxrAvatar.LocalAvatar, _handSide, true);
if (_armoredDoor != null)
{
_armoredDoor.OpenDoor(false);
}
}
}
else
{
// Opposite hand got in. Aborting.
ProcessScanResult(UxrAvatar.LocalAvatar, _handSide, false);
}
}
else
{
// Scanning hand got out. Aborting.
ProcessScanResult(UxrAvatar.LocalAvatar, _handSide, false);
}
}
}
// Update scan side
if (_handSide == UxrHandSide.Left)
{
if (_handRendererRight.enabled)
{
float rightAlpha = _handRendererRight.material.color.a - Time.deltaTime * HandAlphaSwitchSpeed;
_handRendererRight.material.color = ColorExt.ColorAlpha(_handRendererRight.material.color, Mathf.Clamp01(rightAlpha));
if (rightAlpha < 0.0f)
{
_handRendererRight.enabled = false;
}
}
if (!_handRendererLeft.enabled)
{
_handRendererLeft.enabled = true;
}
_handRendererLeft.material.color = ColorExt.ColorAlpha(_handRendererLeft.material.color, Mathf.Clamp01(_handRendererLeft.material.color.a + Time.deltaTime * HandAlphaSwitchSpeed));
}
else
{
if (_handRendererLeft.enabled)
{
float leftAlpha = _handRendererLeft.material.color.a - Time.deltaTime * HandAlphaSwitchSpeed;
_handRendererLeft.material.color = ColorExt.ColorAlpha(_handRendererLeft.material.color, Mathf.Clamp01(leftAlpha));
if (leftAlpha < 0.0f)
{
_handRendererLeft.enabled = false;
}
}
if (!_handRendererRight.enabled)
{
_handRendererRight.enabled = true;
}
_handRendererRight.material.color = ColorExt.ColorAlpha(_handRendererRight.material.color, Mathf.Clamp01(_handRendererRight.material.color.a + Time.deltaTime * HandAlphaSwitchSpeed));
}
}
#endregion
#region Private Methods
/// <summary>
/// Starts scanning a hand.
/// </summary>
/// <param name="avatarScanning">The avatar that started scanning</param>
/// <param name="handSide">The side of the hand being scanned</param>
private void StartScan(UxrAvatar avatarScanning, UxrHandSide handSide)
{
BeginSync();
_scanTimer = 0.0f;
_handSide = handSide;
_avatarScanning = avatarScanning;
_audioScan.Play(transform.position);
EndSyncMethod(new object[] { avatarScanning, handSide });
}
/// <summary>
/// Enables/disables the scanning beams.
/// </summary>
/// <param name="enable">Whether the beams should be enabled</param>
private void EnableBeams(bool enable)
{
_beams.ForEach(r => r.enabled = enable);
}
/// <summary>
/// Processes a scan result.
/// </summary>
/// <param name="avatar">Avatar that was scanned</param>
/// <param name="handSide">Which hand was scanned</param>
/// <param name="isValid">Whether the access was granted</param>
private void ProcessScanResult(UxrAvatar avatar, UxrHandSide handSide, bool isValid)
{
BeginSync();
EnableBeams(false);
_scanReady = false;
_scanTimer = -1.0f;
if (isValid)
{
_audioOk.Play(transform.position);
_validLight.enabled = true;
UxrAnimatedMaterial.AnimateBlinkColor(_validLight.gameObject,
UxrConstants.Shaders.StandardColorVarName,
_colorValid.WithAlpha(0.0f),
_colorValid,
UxrAnimatedMaterial.DefaultBlinkFrequency,
_resultSeconds,
UxrMaterialMode.InstanceOnly,
() =>
{
_scanReady = true;
_validLight.enabled = false;
});
}
else
{
_audioError.Play(transform.position);
_invalidLight.enabled = true;
UxrAnimatedMaterial.AnimateBlinkColor(_invalidLight.gameObject,
UxrConstants.Shaders.StandardColorVarName,
_colorInvalid.WithAlpha(0.0f),
_colorInvalid,
UxrAnimatedMaterial.DefaultBlinkFrequency,
_resultSeconds,
UxrMaterialMode.InstanceOnly,
() =>
{
_scanReady = true;
_invalidLight.enabled = false;
});
}
HandScanned?.Invoke(avatar, handSide, isValid);
EndSyncMethod(new object[] { avatar, handSide, isValid });
}
#endregion
#region Private Types & Data
/// <summary>
/// Controls how fast the hand image will switch from one side to the other when a different hand than the currently
/// shown is placed on the scanner.
/// </summary>
private const float HandAlphaSwitchSpeed = 4.0f;
private readonly List<Renderer> _beams = new List<Renderer>();
private bool _scanReady = true;
private float _scanTimer = -1.0f;
private Vector3 _beamScale;
private UxrAvatar _avatarScanning;
private UxrHandSide _handSide;
private Color _colorValid;
private Color _colorInvalid;
#endregion
}
}

View File

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