Move third party assets to ThirdParty folder
This commit is contained in:
8
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors.meta
vendored
Normal file
8
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2818067eea97d0428518f54c85e8854
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/ArmoredDoor.cs
vendored
Normal file
45
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/ArmoredDoor.cs
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
11
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/ArmoredDoor.cs.meta
vendored
Normal file
11
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/ArmoredDoor.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f0997d4343ed2b4cb9b90f035e6ab72
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
179
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/AutomaticDoor.cs
vendored
Normal file
179
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/AutomaticDoor.cs
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
11
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/AutomaticDoor.cs.meta
vendored
Normal file
11
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/AutomaticDoor.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dc12cc8ceda632428882a248e42df44
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
347
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/HandScanner.cs
vendored
Normal file
347
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/HandScanner.cs
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
11
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/HandScanner.cs.meta
vendored
Normal file
11
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Doors/HandScanner.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48bb21b6c6a28c64bb55c8b2c38283b0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
186
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/GlobalLogic.cs
vendored
Normal file
186
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/GlobalLogic.cs
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="GlobalLogic.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.CameraUtils;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Devices.Keyboard;
|
||||
using UltimateXR.Examples.FullScene.Doors;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UltimateXR.Locomotion;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene
|
||||
{
|
||||
public class GlobalLogic : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[Header("Spawn positions")] [SerializeField] private Transform _spawnMain;
|
||||
[SerializeField] private Transform _spawnLab;
|
||||
[SerializeField] private Transform _spawnControllers;
|
||||
[SerializeField] private Transform _spawnShootingRange;
|
||||
|
||||
[Header("Location volumes")] [SerializeField] private BoxCollider _boxSpawnRoomMirror;
|
||||
[SerializeField] private BoxCollider _boxSpawnRoomDoor;
|
||||
[SerializeField] private BoxCollider _boxCentralRoom;
|
||||
[SerializeField] private BoxCollider _boxLabRoom;
|
||||
[SerializeField] private BoxCollider _boxControllerRoom;
|
||||
[SerializeField] private BoxCollider _boxShootingRange;
|
||||
|
||||
[Header("Relevant elements")] [SerializeField] private GameObject _rootUnrestrictedArea;
|
||||
[SerializeField] private GameObject _rootRestrictedArea;
|
||||
[SerializeField] private UxrComponent _mirrorComponent;
|
||||
[SerializeField] private GameObject _controllerRoomElements;
|
||||
[SerializeField] private GameObject _rootLabElements;
|
||||
[SerializeField] private ArmoredDoor _armoredDoor;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to avatar events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
UxrAvatar.LocalAvatarStarted += UxrAvatar_LocalAvatarStarted;
|
||||
UxrAvatar.GlobalAvatarMoved += UxrAvatar_GlobalAvatarMoved;
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from avatar events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
UxrAvatar.LocalAvatarStarted -= UxrAvatar_LocalAvatarStarted;
|
||||
UxrAvatar.GlobalAvatarMoved -= UxrAvatar_GlobalAvatarMoved;
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles some keyboard shortcuts to reset, quit or quick spawn to different places.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (UxrKeyboardInput.GetPressDown(UxrKey.Enter))
|
||||
{
|
||||
SceneManager.LoadScene(0);
|
||||
}
|
||||
else if (UxrKeyboardInput.GetPressDown(UxrKey.Q))
|
||||
{
|
||||
Application.Quit();
|
||||
}
|
||||
|
||||
if (UxrKeyboardInput.GetPressDown(UxrKey.Digit1))
|
||||
{
|
||||
UxrManager.Instance.TeleportLocalAvatar(_spawnMain.position, _spawnMain.rotation, UxrTranslationType.Fade);
|
||||
}
|
||||
else if (UxrKeyboardInput.GetPressDown(UxrKey.Digit2))
|
||||
{
|
||||
UxrManager.Instance.TeleportLocalAvatar(_spawnLab.position, _spawnLab.rotation, UxrTranslationType.Fade);
|
||||
}
|
||||
else if (UxrKeyboardInput.GetPressDown(UxrKey.Digit3))
|
||||
{
|
||||
UxrManager.Instance.TeleportLocalAvatar(_spawnControllers.position, _spawnControllers.rotation, UxrTranslationType.Fade);
|
||||
}
|
||||
else if (UxrKeyboardInput.GetPressDown(UxrKey.Digit4))
|
||||
{
|
||||
UxrManager.Instance.TeleportLocalAvatar(_spawnShootingRange.position, _spawnShootingRange.rotation, UxrTranslationType.Fade);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when the local avatar called its Start(). Moves the avatar to the spawn point and initializes the visible
|
||||
/// elements.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void UxrAvatar_LocalAvatarStarted(object sender, UxrAvatarStartedEventArgs e)
|
||||
{
|
||||
UxrManager.Instance.MoveAvatarTo(UxrAvatar.LocalAvatar, _spawnMain);
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the avatar moved/teleported. We use it to enable/disable objects based on potential visibility.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void UxrAvatar_GlobalAvatarMoved(object sender, UxrAvatarMoveEventArgs e)
|
||||
{
|
||||
if (ReferenceEquals(sender, UxrAvatar.LocalAvatar))
|
||||
{
|
||||
UpdateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called each frame after all avatars have been updated.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
if (UxrAvatar.LocalAvatar == null || UxrCameraWallFade.IsAvatarPeekingThroughGeometry(UxrAvatar.LocalAvatar))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_rootRestrictedArea.CheckSetActive(UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxShootingRange) || _armoredDoor.OpenValue > 0.0f);
|
||||
_rootUnrestrictedArea.CheckSetActive(!UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxShootingRange) || _armoredDoor.OpenValue > 0.0f);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the visible elements based on the current avatar position.
|
||||
/// </summary>
|
||||
private void UpdateVisibility()
|
||||
{
|
||||
if (UxrAvatar.LocalAvatar == null || UxrCameraWallFade.IsAvatarPeekingThroughGeometry(UxrAvatar.LocalAvatar))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_mirrorComponent.CheckSetEnabled(UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxSpawnRoomMirror));
|
||||
_rootRestrictedArea.CheckSetActive(UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxShootingRange) || _armoredDoor.OpenValue > 0.0f);
|
||||
_rootUnrestrictedArea.CheckSetActive(!UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxShootingRange) || _armoredDoor.OpenValue > 0.0f);
|
||||
|
||||
if (UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxSpawnRoomMirror))
|
||||
{
|
||||
_controllerRoomElements.CheckSetActive(false);
|
||||
_rootLabElements.CheckSetActive(false);
|
||||
}
|
||||
else if (UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxSpawnRoomDoor))
|
||||
{
|
||||
_controllerRoomElements.CheckSetActive(false);
|
||||
_rootLabElements.CheckSetActive(true);
|
||||
}
|
||||
else if (UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxCentralRoom) || UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxLabRoom) || UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxControllerRoom))
|
||||
{
|
||||
_controllerRoomElements.CheckSetActive(true);
|
||||
_rootLabElements.CheckSetActive(true);
|
||||
}
|
||||
else if (UxrAvatar.LocalAvatar.CameraPosition.IsInsideBox(_boxShootingRange))
|
||||
{
|
||||
_controllerRoomElements.CheckSetActive(false);
|
||||
_rootLabElements.CheckSetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/GlobalLogic.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/GlobalLogic.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad8f739b413ab794aa3cc730f7546754
|
||||
timeCreated: 1539279150
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab.meta
vendored
Normal file
10
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab.meta
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb1671cf87acc9d4294eb168f5985392
|
||||
folderAsset: yes
|
||||
timeCreated: 1530530963
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
111
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Battery.cs
vendored
Normal file
111
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Battery.cs
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="Battery.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Manipulation;
|
||||
using UltimateXR.Manipulation.Helpers;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene.Lab
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that adds the smooth slide-in/slide-out effects to the grabbable battery.
|
||||
/// </summary>
|
||||
public class Battery : UxrAutoSlideInObject
|
||||
{
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the avatars updated event so that the manipulation logic is done after UltimateXR's manipulation
|
||||
/// logic has been updated.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the avatars updated event.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called after UltimateXR has done all the frame updating. Does the manipulation logic.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
if (GrabbableObject.CurrentAnchor != null)
|
||||
{
|
||||
// Constrain transform when the battery is inside a door
|
||||
|
||||
GeneratorDoor generatorDoor = GrabbableObject.CurrentAnchor.GetComponentInParent<GeneratorDoor>();
|
||||
|
||||
if (generatorDoor != null && !generatorDoor.IsLockOpen)
|
||||
{
|
||||
// Lock is closed? Battery can't move
|
||||
GrabbableObject.IsLockedInPlace = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Battery can move
|
||||
GrabbableObject.IsLockedInPlace = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnPlacedAfterSlidingIn()
|
||||
{
|
||||
base.OnPlacedAfterSlidingIn();
|
||||
|
||||
if (GrabbableObject.CurrentAnchor != null)
|
||||
{
|
||||
GeneratorDoor generatorDoor = GrabbableObject.CurrentAnchor.GetComponentInParent<GeneratorDoor>();
|
||||
|
||||
if (generatorDoor != null)
|
||||
{
|
||||
// Turn lights on when the battery finished sliding in
|
||||
generatorDoor.IsBatteryInContact = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after the battery was grabbed.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectGrabbed(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectGrabbed(e);
|
||||
|
||||
if (!GrabbableObject.IsLockedInPlace && e.GrabbableAnchor != null)
|
||||
{
|
||||
GeneratorDoor generatorDoor = e.GrabbableAnchor.GetComponentInParent<GeneratorDoor>();
|
||||
|
||||
if (generatorDoor != null)
|
||||
{
|
||||
// Turn lights off when the battery is moved away
|
||||
generatorDoor.IsBatteryInContact = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Battery.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Battery.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9d66e1c4a827a345a4c670b5cbf2d14
|
||||
timeCreated: 1530605952
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
30
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/BatteryAnchor.cs
vendored
Normal file
30
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/BatteryAnchor.cs
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="BatteryAnchor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene.Lab
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that helps enumerate the potential battery anchors in a scene using LabBatteryAnchor.EnabledComponents.
|
||||
/// Battery placing has some additional special mechanics for the sliding-in/sliding-out behavior. This component
|
||||
/// helps identifying where those places are.
|
||||
/// </summary>
|
||||
public class BatteryAnchor : UxrComponent<BatteryAnchor>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrGrabbableObjectAnchor _batteryAnchor;
|
||||
|
||||
/// <summary>
|
||||
/// Actual anchor component where a battery can be placed.
|
||||
/// </summary>
|
||||
public UxrGrabbableObjectAnchor Anchor => _batteryAnchor;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/BatteryAnchor.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/BatteryAnchor.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d25f62cc89144e0811a69ceae042e06
|
||||
timeCreated: 1655561113
|
||||
201
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/GeneratorDoor.cs
vendored
Normal file
201
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/GeneratorDoor.cs
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="GeneratorDoor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Manipulation;
|
||||
using UltimateXR.Manipulation.Helpers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene.Lab
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that handles the generator door, which has battery locks to lock the battery in place.
|
||||
/// </summary>
|
||||
public class GeneratorDoor : UxrComponent<GeneratorDoor>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrAutoSlideInAnchor _batteryAnchor;
|
||||
[SerializeField] private UxrGrabbableObject _grabbableLock;
|
||||
[SerializeField] private Transform[] _locks;
|
||||
[SerializeField] private float _lockHandleAngleClosed;
|
||||
[SerializeField] private float _lockHandleAngleOpen;
|
||||
[SerializeField] private bool _startLockOpen = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the battery lock is open.
|
||||
/// </summary>
|
||||
public bool IsLockOpen
|
||||
{
|
||||
get => LockHandleOpenValue > 0.5f;
|
||||
private set
|
||||
{
|
||||
// Set rotation using the correct property to avoid interference between grabbable object constraint calculation and manually setting its transform.
|
||||
_grabbableLock.SingleRotationAxisDegrees = value ? _lockHandleAngleOpen : _lockHandleAngleClosed;
|
||||
|
||||
for (int i = 0; i < _locks.Length; ++i)
|
||||
{
|
||||
_locks[i].transform.localRotation = _lockInitialRotation[i] * Quaternion.AngleAxis((value ? 1.0f : 0.0f) * (_lockHandleAngleOpen - _lockHandleAngleClosed), Vector3.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether there is a battery placed inside the generator and it is currently in contact with the bottom. This
|
||||
/// allows to switch things on when the battery is actually in contact instead of being placed, because there is a
|
||||
/// slide-in animation after the battery has been placed.
|
||||
/// </summary>
|
||||
public bool IsBatteryInContact { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_lockInitialRotation = new Quaternion[_locks.Length];
|
||||
|
||||
for (int i = 0; i < _locks.Length; ++i)
|
||||
{
|
||||
_lockInitialRotation[i] = _locks[i].localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the events that help model the behavior.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_batteryAnchor.Anchor.Placed += Battery_Placed;
|
||||
_batteryAnchor.Anchor.Removed += Battery_Removed;
|
||||
_grabbableLock.ConstraintsApplied += Lock_ConstraintsApplied;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
_batteryAnchor.Anchor.Placed -= Battery_Placed;
|
||||
_batteryAnchor.Anchor.Removed -= Battery_Removed;
|
||||
_grabbableLock.ConstraintsApplied -= Lock_ConstraintsApplied;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the lock open state.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
IsBatteryInContact = _batteryAnchor.Anchor.CurrentPlacedObject != null;
|
||||
IsLockOpen = _startLockOpen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the ability of the door to let a battery be placed inside.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
// The battery door anchor is disabled if the lock is closed and there is no battery inside
|
||||
|
||||
if (_batteryAnchor.Anchor.CurrentPlacedObject == null && !IsLockOpen)
|
||||
{
|
||||
_batteryAnchor.enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_batteryAnchor.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called after the lock has finished being manipulated so that additional constraints can be applied to it.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Lock_ConstraintsApplied(object sender, UxrApplyConstraintsEventArgs e)
|
||||
{
|
||||
float lockHandleOpenValue = LockHandleOpenValue;
|
||||
float locksOpenValue = 1.0f - (1.0f - lockHandleOpenValue) * (1.0f - lockHandleOpenValue);
|
||||
|
||||
// Update small locks based on the main lock open value
|
||||
|
||||
for (int i = 0; i < _locks.Length; ++i)
|
||||
{
|
||||
_locks[i].transform.localRotation = _lockInitialRotation[i] * Quaternion.AngleAxis(locksOpenValue * (_lockHandleAngleOpen - _lockHandleAngleClosed), Vector3.right);
|
||||
}
|
||||
|
||||
// Main lock can be manipulated only while the battery is completely inside or there is no battery
|
||||
|
||||
if (_batteryAnchor.Anchor.CurrentPlacedObject != null && _batteryAnchor.Anchor.CurrentPlacedObject.transform.localPosition.z > 0.01f)
|
||||
{
|
||||
_grabbableLock.IsLockedInPlace = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_grabbableLock.IsLockedInPlace = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after a battery was placed inside.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Battery_Placed(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
// In order to make the lights turn on only when the battery reached the bottom, we control this from the Battery component.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after the battery was removed.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Battery_Removed(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
IsBatteryInContact = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value between 0.0 and 1.0 telling how open the lock is.
|
||||
/// </summary>
|
||||
private float LockHandleOpenValue
|
||||
{
|
||||
get
|
||||
{
|
||||
float lockHandleOpenValue = Mathf.Clamp01((_grabbableLock.transform.localRotation.eulerAngles.z - _lockHandleAngleClosed) / (_lockHandleAngleOpen - _lockHandleAngleClosed));
|
||||
return lockHandleOpenValue;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isBatteryInContact;
|
||||
private Quaternion[] _lockInitialRotation;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/GeneratorDoor.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/GeneratorDoor.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 241d54f6114ee524895079db62d245cf
|
||||
timeCreated: 1530536555
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
51
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Lamp.cs
vendored
Normal file
51
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Lamp.cs
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="Lamp.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene.Lab
|
||||
{
|
||||
/// <summary>
|
||||
/// Binds the light intensity to the state of the currently placed light bulbs.
|
||||
/// </summary>
|
||||
public class Lamp : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrGrabbableObjectAnchor[] _sockets;
|
||||
[SerializeField] private Light _light;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Updates the light intensity based on the currently placed light bulbs.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
float lightBulbIntensity = 0.0f;
|
||||
|
||||
foreach (UxrGrabbableObjectAnchor socket in _sockets)
|
||||
{
|
||||
if (socket.CurrentPlacedObject != null)
|
||||
{
|
||||
LightBulb lightBulb = socket.CurrentPlacedObject.GetComponentInChildren<LightBulb>();
|
||||
|
||||
if (lightBulb != null)
|
||||
{
|
||||
lightBulbIntensity += lightBulb.Intensity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_light.intensity = lightBulbIntensity;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Lamp.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Lamp.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1672ad7753c9a75438933016a4b0e54d
|
||||
timeCreated: 1530530970
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
82
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Laser.LaserBurn.cs
vendored
Normal file
82
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Laser.LaserBurn.cs
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="Laser.LaserBurn.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene.Lab
|
||||
{
|
||||
public partial class Laser
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores all information for a burn result of pointing the enabled laser to an object.
|
||||
/// </summary>
|
||||
private class LaserBurn
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform component of the GameObject that is used to represent the burn.
|
||||
/// </summary>
|
||||
public Transform Transform => GameObject.transform;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last normal of the laser impact that caused the laser burn.
|
||||
/// </summary>
|
||||
public Vector3 LastWorldNormal => Transform.TransformVector(LastNormal);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last world-space position in the burn path.
|
||||
/// </summary>
|
||||
public Vector3 LastWorldPathPosition => Transform.TransformPoint(PathPositions[PathPositions.Count - 1]);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dynamically created object to represent the burn.
|
||||
/// </summary>
|
||||
public GameObject GameObject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dynamically created object that represents the incandescent part in the burn.
|
||||
/// </summary>
|
||||
public GameObject GameObjectIncandescent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collider that was hit.
|
||||
/// </summary>
|
||||
public Collider Collider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the burn path line renderer.
|
||||
/// </summary>
|
||||
public LineRenderer LineRenderer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the incandescent path line renderer.
|
||||
/// </summary>
|
||||
public LineRenderer IncandescentLineRenderer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the positions in the burn path.
|
||||
/// </summary>
|
||||
public List<Vector3> PathPositions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation times of each path position so that we can fade them out based on age.
|
||||
/// </summary>
|
||||
public List<float> PathCreationTimes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Last hit normal in local coordinates of the burn object.
|
||||
/// </summary>
|
||||
public Vector3 LastNormal { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Laser.LaserBurn.cs.meta
vendored
Normal file
3
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Laser.LaserBurn.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8164104655d2449c88e83840fb643de0
|
||||
timeCreated: 1655550081
|
||||
586
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Laser.cs
vendored
Normal file
586
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Laser.cs
vendored
Normal file
@@ -0,0 +1,586 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="Laser.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Devices;
|
||||
using UltimateXR.Extensions.Unity.Math;
|
||||
using UltimateXR.Extensions.Unity.Render;
|
||||
using UltimateXR.Haptics.Helpers;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene.Lab
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that handles the laser in the lab room.
|
||||
/// </summary>
|
||||
public partial class Laser : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Transform _laserSource;
|
||||
[SerializeField] private LayerMask _collisionMask = -1;
|
||||
[SerializeField] private float _laserRayWidth = 0.003f;
|
||||
[SerializeField] private float _laserRayLength = 5.0f;
|
||||
[SerializeField] private Color _laserColor = Color.red;
|
||||
[SerializeField] private float _laserBurnDelaySeconds = 0.2f; // Time the laser needs to be travelling at a speed less than LaserBurnParticlesMaxSpeed to create burn FX
|
||||
[SerializeField] private float _laserBurnSpeedThreshold = 0.4f; // Maximum speed the laser can travel before stopping particles emission
|
||||
[SerializeField] private Color _laserBurnColor = new Color(0.0f, 0.0f, 0.0f, 0.6f);
|
||||
[SerializeField] private float _laserBurnVertexDistance = 0.03f;
|
||||
[SerializeField] private float _laserBurnHeightOffset = 0.001f;
|
||||
[SerializeField] private float _laserBurnDurationFadeStart = 3.0f;
|
||||
[SerializeField] private float _laserBurnDurationFadeEnd = 6.0f;
|
||||
[SerializeField] private Color _laserBurnIncandescentColor = new Color(0.7f, 0.7f, 0.1f, 1.0f);
|
||||
[SerializeField] private float _laserBurnIncandescentDurationFadeEnd = 1.0f;
|
||||
[SerializeField] private ParticleSystem _laserBurnParticles;
|
||||
[SerializeField] private float _laserBurnParticlesHeightOffset;
|
||||
[SerializeField] private bool _laserBurnReflectParticles;
|
||||
[SerializeField] private GameObject _enableWhenLaserActive;
|
||||
[SerializeField] private UxrGrabbableObject _triggerGrabbable;
|
||||
[SerializeField] private Transform _trigger;
|
||||
[SerializeField] private Vector3 _triggerOffset;
|
||||
[SerializeField] private UxrFixedHapticFeedback _laserHaptics;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Sets up internal data.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_triggerInitialOffset = _trigger.localPosition;
|
||||
|
||||
// Line renderer setup
|
||||
|
||||
_laserLineRenderer = gameObject.AddComponent<LineRenderer>();
|
||||
_laserLineRenderer.useWorldSpace = false;
|
||||
|
||||
SetLaserLineRendererMesh(_laserRayLength);
|
||||
|
||||
_laserLineRenderer.material = new Material(ShaderExt.UnlitTransparentColor);
|
||||
_laserLineRenderer.material.renderQueue = (int)RenderQueue.Overlay + 1;
|
||||
_laserLineRenderer.material.color = _laserColor;
|
||||
_laserLineRenderer.loop = true;
|
||||
_laserLineRenderer.enabled = false;
|
||||
|
||||
_laserBurns = new List<LaserBurn>();
|
||||
|
||||
ParticleSystem.EmissionModule emission = _laserBurnParticles.emission;
|
||||
emission.enabled = false;
|
||||
|
||||
_laserHaptics.enabled = false;
|
||||
|
||||
_createBurnTimer = _laserBurnDelaySeconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to avatar updated event.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from avatar updated event.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// We update the laser after all VR avatars have been updated to make sure it's processed after all manipulation.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
// Check if there is a hand grabbing the laser
|
||||
|
||||
if (UxrGrabManager.Instance.GetGrabbingHand(_triggerGrabbable, 0, out UxrGrabber grabber))
|
||||
{
|
||||
// Check if it's the local avatar, because the local avatar will drive the state changes (IsLaserEnabled is network synchronized).
|
||||
|
||||
if (grabber.Avatar.AvatarMode == UxrAvatarMode.Local)
|
||||
{
|
||||
// It is! see which hand to check for a trigger squeeze
|
||||
|
||||
float trigger = UxrAvatar.LocalAvatarInput.GetInput1D(grabber.Side, UxrInput1D.Trigger);
|
||||
|
||||
_trigger.localPosition = _triggerInitialOffset + _triggerOffset * trigger;
|
||||
|
||||
_triggerGrabbable.GetGrabPoint(0).GetGripPoseInfo(grabber.Avatar).PoseBlendValue = trigger;
|
||||
|
||||
if (UxrAvatar.LocalAvatarInput.GetButtonsPress(grabber.Side, UxrInputButtons.Trigger))
|
||||
{
|
||||
// Trigger is squeezed
|
||||
|
||||
if (IsLaserEnabled == false)
|
||||
{
|
||||
IsLaserEnabled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IsLaserEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there are no grabs, we don't need to sync using IsLaserEnabled because it can be solved locally.
|
||||
_laserLineRenderer.enabled = false;
|
||||
}
|
||||
|
||||
// Check laser raycast
|
||||
|
||||
if (IsLaserEnabled)
|
||||
{
|
||||
float currentRayLength = _laserRayLength;
|
||||
|
||||
if (Physics.Raycast(_laserSource.position, _laserSource.forward, out RaycastHit hitInfo, currentRayLength, _collisionMask, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
if (CurrentLaserBurn == null)
|
||||
{
|
||||
// This is a new burn -> initialize
|
||||
_laserBurns[_laserBurns.Count - 1] = CreateNewLaserBurn(hitInfo.collider);
|
||||
_createBurnTimer = _laserBurnDelaySeconds;
|
||||
}
|
||||
else if (CurrentLaserBurn.Collider != hitInfo.collider)
|
||||
{
|
||||
// If we hit another object we create a new laser burn entry
|
||||
_laserBurns.Add(CreateNewLaserBurn(hitInfo.collider));
|
||||
_createBurnTimer = _laserBurnDelaySeconds;
|
||||
}
|
||||
|
||||
// Check if laser travel speed is below threshold to create a burn. If so decrement timer which will start a burn if it reaches 0.
|
||||
|
||||
if (Vector3.Distance(_lastLaserHitPosition, hitInfo.point) / Time.deltaTime < _laserBurnSpeedThreshold)
|
||||
{
|
||||
_createBurnTimer -= Time.deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not enough speed -> new burn
|
||||
_laserBurns.Add(CreateNewLaserBurn(hitInfo.collider));
|
||||
_createBurnTimer = _laserBurnDelaySeconds;
|
||||
}
|
||||
|
||||
_lastLaserHitPosition = hitInfo.point;
|
||||
|
||||
// Needs to start burn FX?
|
||||
|
||||
ParticleSystem.EmissionModule emission = _laserBurnParticles.emission;
|
||||
emission.enabled = _createBurnTimer < 0.0f;
|
||||
_laserBurnParticles.transform.position = hitInfo.point + hitInfo.normal * _laserBurnParticlesHeightOffset;
|
||||
_laserBurnParticles.transform.rotation = _laserBurnReflectParticles ? Quaternion.LookRotation(Vector3.Reflect(_laserSource.forward, hitInfo.normal)) : Quaternion.Slerp(Quaternion.LookRotation(hitInfo.normal, Vector3.right), Quaternion.LookRotation(Vector3.up, Vector3.right), 0.9f);
|
||||
|
||||
if (_createBurnTimer < 0.0f)
|
||||
{
|
||||
// Burn trail
|
||||
|
||||
float segmentDistance = CurrentLaserBurn.PathPositions.Count == 0 ? 0.0f : Vector3.Distance(hitInfo.point, CurrentLaserBurn.LastWorldPathPosition);
|
||||
|
||||
if (CurrentLaserBurn.PathPositions.Count == 0 || segmentDistance > _laserBurnVertexDistance)
|
||||
{
|
||||
// Here we should create a new segment since the burn has travelled enough distance to create a new one, but first we are going to check if we somehow
|
||||
// went over a hole, bump or depression in the geometry from the last point to this one. We do not want a burn strip to appear over gaps on the geometry so in that case
|
||||
// what we will do is just create a new laser burn and skip this segment
|
||||
|
||||
if (CurrentLaserBurn.PathPositions.Count > 0 && segmentDistance > BurnGapCheckMinDistance)
|
||||
{
|
||||
bool startNewBurn = false;
|
||||
|
||||
Vector3 vectorAB = hitInfo.point - CurrentLaserBurn.LastWorldPathPosition;
|
||||
|
||||
for (int checkStep = 0; checkStep < BurnGapCheckSteps; ++checkStep)
|
||||
{
|
||||
// Perform a series of steps casting rays from the laser source to intermediate points between the last burn segment and the current one looking for changes in depth
|
||||
|
||||
float t = (checkStep + 1.0f) / (BurnGapCheckSteps + 1.0f);
|
||||
Vector3 pointCheck = hitInfo.point + vectorAB * t;
|
||||
Vector3 perpendicular = Vector3.Cross(Vector3.Cross(vectorAB.normalized, ((CurrentLaserBurn.LastWorldNormal + hitInfo.normal) * 0.5f).normalized), vectorAB.normalized);
|
||||
Vector3 raySource = CurrentLaserBurn.LastWorldPathPosition + vectorAB * 0.5f + perpendicular * BurnGapCheckRaySourceDistance;
|
||||
|
||||
if (Physics.Raycast(raySource, (pointCheck - raySource).normalized, out RaycastHit hitInfoBurnGapCheck, _laserRayLength, _collisionMask, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
float distanceToLine = hitInfoBurnGapCheck.point.DistanceToLine(CurrentLaserBurn.LastWorldPathPosition, hitInfo.point);
|
||||
|
||||
if (distanceToLine > BurnGapCheckLineDistanceThreshold)
|
||||
{
|
||||
// Depth change too big -> create new laser burn
|
||||
startNewBurn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Raycast did not find intersection -> create new laser burn
|
||||
startNewBurn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (startNewBurn)
|
||||
{
|
||||
_laserBurns.Add(CreateNewLaserBurn(hitInfo.collider));
|
||||
}
|
||||
}
|
||||
|
||||
// Add last point and offset a little bit from the geometry to draw correctly
|
||||
|
||||
CurrentLaserBurn.PathPositions.Add(CurrentLaserBurn.Transform.InverseTransformPoint(hitInfo.point + hitInfo.normal * _laserBurnHeightOffset));
|
||||
CurrentLaserBurn.PathCreationTimes.Add(Time.time);
|
||||
CurrentLaserBurn.LastNormal = CurrentLaserBurn.Transform.InverseTransformVector(hitInfo.normal);
|
||||
UpdateLaserBurnLineRenderer(CurrentLaserBurn);
|
||||
UpdateLaserBurns(0, _laserBurns.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateLaserBurns(0, _laserBurns.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateLaserBurns(0, _laserBurns.Count);
|
||||
}
|
||||
|
||||
currentRayLength = hitInfo.distance;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParticleSystem.EmissionModule emission = _laserBurnParticles.emission;
|
||||
emission.enabled = false;
|
||||
_createBurnTimer = _laserBurnDelaySeconds;
|
||||
|
||||
UpdateLaserBurns(0, _laserBurns.Count);
|
||||
}
|
||||
|
||||
SetLaserLineRendererMesh(currentRayLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParticleSystem.EmissionModule emission = _laserBurnParticles.emission;
|
||||
emission.enabled = false;
|
||||
_createBurnTimer = _laserBurnDelaySeconds;
|
||||
|
||||
UpdateLaserBurns(0, _laserBurns.Count);
|
||||
}
|
||||
|
||||
if (_laserHaptics && grabber != null && grabber.Avatar.AvatarMode == UxrAvatarMode.Local)
|
||||
{
|
||||
_laserHaptics.enabled = IsLaserEnabled;
|
||||
_laserHaptics.HandSide = grabber.Side;
|
||||
}
|
||||
else
|
||||
{
|
||||
_laserHaptics.enabled = false;
|
||||
}
|
||||
|
||||
if (_enableWhenLaserActive && !_enableWhenLaserActive.activeSelf && IsLaserEnabled)
|
||||
{
|
||||
_enableWhenLaserActive.SetActive(true);
|
||||
}
|
||||
else if (_enableWhenLaserActive && _enableWhenLaserActive.activeSelf && !IsLaserEnabled)
|
||||
{
|
||||
_enableWhenLaserActive.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the laser line renderer
|
||||
/// </summary>
|
||||
/// <param name="rayLength">Current laser ray length</param>
|
||||
private void SetLaserLineRendererMesh(float rayLength)
|
||||
{
|
||||
_laserLineRenderer.startWidth = _laserRayWidth;
|
||||
_laserLineRenderer.endWidth = _laserRayWidth;
|
||||
|
||||
Vector3[] positions =
|
||||
{
|
||||
new Vector3(0.0f, 0.0f, 0.0f),
|
||||
new Vector3(0.0f, 0.0f, rayLength > LaserGradientLength ? LaserGradientLength : rayLength * 0.33f),
|
||||
new Vector3(0.0f, 0.0f, rayLength < LaserGradientLength * 2.0f ? rayLength * 0.66f : rayLength - LaserGradientLength),
|
||||
new Vector3(0.0f, 0.0f, rayLength)
|
||||
};
|
||||
|
||||
for (int i = 0; i < positions.Length; ++i)
|
||||
{
|
||||
positions[i] = _laserLineRenderer.transform.InverseTransformPoint(_laserSource.TransformPoint(positions[i]));
|
||||
}
|
||||
|
||||
_laserLineRenderer.positionCount = 4;
|
||||
_laserLineRenderer.SetPositions(positions);
|
||||
|
||||
Gradient colorGradient = new Gradient();
|
||||
|
||||
colorGradient.colorKeys = new[]
|
||||
{
|
||||
new GradientColorKey(Color.white, 0.0f),
|
||||
new GradientColorKey(Color.white, rayLength > LaserGradientLength ? LaserGradientLength / rayLength : 0.33f),
|
||||
new GradientColorKey(Color.white, rayLength < LaserGradientLength * 2.0f ? 0.66f : 1.0f - LaserGradientLength / rayLength),
|
||||
new GradientColorKey(Color.white, 1.0f)
|
||||
};
|
||||
|
||||
colorGradient.alphaKeys = new[]
|
||||
{
|
||||
new GradientAlphaKey(0.0f, 0.0f),
|
||||
new GradientAlphaKey(_laserColor.a, rayLength > LaserGradientLength ? LaserGradientLength / rayLength : 0.3f),
|
||||
new GradientAlphaKey(_laserColor.a, rayLength < LaserGradientLength * 2.0f ? 0.66f : 1.0f - LaserGradientLength / rayLength),
|
||||
new GradientAlphaKey(0.0f, 1.0f)
|
||||
};
|
||||
|
||||
_laserLineRenderer.colorGradient = colorGradient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new laser burn entry
|
||||
/// </summary>
|
||||
/// <param name="collider">Collider the laser burn is attached to</param>
|
||||
/// <returns>New laser burn object</returns>
|
||||
private LaserBurn CreateNewLaserBurn(Collider collider)
|
||||
{
|
||||
LaserBurn newLaserBurn = new LaserBurn();
|
||||
|
||||
newLaserBurn.Collider = collider;
|
||||
|
||||
newLaserBurn.GameObject = new GameObject("LaserBurn");
|
||||
newLaserBurn.GameObject.transform.parent = collider.transform;
|
||||
newLaserBurn.GameObject.transform.localPosition = Vector3.zero;
|
||||
newLaserBurn.GameObject.transform.localRotation = Quaternion.identity;
|
||||
newLaserBurn.LineRenderer = newLaserBurn.GameObject.AddComponent<LineRenderer>();
|
||||
newLaserBurn.LineRenderer.receiveShadows = true;
|
||||
newLaserBurn.LineRenderer.shadowCastingMode = ShadowCastingMode.Off;
|
||||
newLaserBurn.LineRenderer.useWorldSpace = false;
|
||||
newLaserBurn.LineRenderer.material = new Material(ShaderExt.UnlitTransparentColor);
|
||||
newLaserBurn.LineRenderer.material.renderQueue = (int)RenderQueue.Overlay + 1;
|
||||
newLaserBurn.LineRenderer.material.color = _laserBurnColor;
|
||||
newLaserBurn.LineRenderer.loop = false;
|
||||
newLaserBurn.LineRenderer.positionCount = 0;
|
||||
|
||||
newLaserBurn.GameObjectIncandescent = new GameObject("LaserBurnIncandescent");
|
||||
newLaserBurn.GameObjectIncandescent.transform.parent = collider.transform;
|
||||
newLaserBurn.GameObjectIncandescent.transform.localPosition = Vector3.zero;
|
||||
newLaserBurn.GameObjectIncandescent.transform.localRotation = Quaternion.identity;
|
||||
newLaserBurn.IncandescentLineRenderer = newLaserBurn.GameObjectIncandescent.AddComponent<LineRenderer>();
|
||||
newLaserBurn.IncandescentLineRenderer.receiveShadows = false;
|
||||
newLaserBurn.IncandescentLineRenderer.shadowCastingMode = ShadowCastingMode.Off;
|
||||
newLaserBurn.IncandescentLineRenderer.useWorldSpace = false;
|
||||
newLaserBurn.IncandescentLineRenderer.material = new Material(ShaderExt.UnlitAdditiveColor);
|
||||
newLaserBurn.IncandescentLineRenderer.material.renderQueue = (int)RenderQueue.Overlay + 2;
|
||||
newLaserBurn.IncandescentLineRenderer.material.color = _laserBurnIncandescentColor;
|
||||
newLaserBurn.IncandescentLineRenderer.loop = false;
|
||||
newLaserBurn.IncandescentLineRenderer.positionCount = 0;
|
||||
|
||||
newLaserBurn.PathPositions = new List<Vector3>();
|
||||
newLaserBurn.PathCreationTimes = new List<float>();
|
||||
|
||||
_createBurnTimer = _laserBurnDelaySeconds;
|
||||
|
||||
return newLaserBurn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the given range of laser burns. Unused laser burns will be deleted
|
||||
/// </summary>
|
||||
/// <param name="startIndex">The start index</param>
|
||||
/// <param name="count">The number of items to update</param>
|
||||
private void UpdateLaserBurns(int startIndex, int count)
|
||||
{
|
||||
for (int i = startIndex; i < startIndex + count && i < _laserBurns.Count; ++i)
|
||||
{
|
||||
UpdateLaserBurnLineRenderer(_laserBurns[i]);
|
||||
|
||||
if (_laserBurns[i].PathPositions.Count <= 1 && _laserBurns[i] != CurrentLaserBurn)
|
||||
{
|
||||
Destroy(_laserBurns[i].GameObject);
|
||||
Destroy(_laserBurns[i].GameObjectIncandescent);
|
||||
_laserBurns.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the laser burn line renderer
|
||||
/// </summary>
|
||||
/// <param name="laserBurn">LaserBurn object whose LineRenderer to update</param>
|
||||
private void UpdateLaserBurnLineRenderer(LaserBurn laserBurn)
|
||||
{
|
||||
if (laserBurn == null || laserBurn.GameObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
laserBurn.LineRenderer.startWidth = _laserRayWidth * 2.5f;
|
||||
laserBurn.LineRenderer.endWidth = _laserRayWidth * 2.5f;
|
||||
laserBurn.IncandescentLineRenderer.startWidth = _laserRayWidth * 1.5f;
|
||||
laserBurn.IncandescentLineRenderer.endWidth = _laserRayWidth * 1.5f;
|
||||
|
||||
// Remove segment positions that have already faded out. Keep just 1 if all need to be deleted to have LastPathPosition accessible.
|
||||
|
||||
int lastIndexToDelete = -1;
|
||||
|
||||
for (int i = 0; i < laserBurn.PathCreationTimes.Count; ++i)
|
||||
{
|
||||
if (Time.time - laserBurn.PathCreationTimes[i] >= _laserBurnDurationFadeEnd)
|
||||
{
|
||||
lastIndexToDelete = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastIndexToDelete >= 0 && laserBurn.PathPositions.Count > 1)
|
||||
{
|
||||
laserBurn.PathCreationTimes.RemoveRange(0, lastIndexToDelete + 1);
|
||||
laserBurn.PathPositions.RemoveRange(0, lastIndexToDelete + 1);
|
||||
}
|
||||
|
||||
// Inside the burn range, compute the start index for the incandescent part
|
||||
|
||||
int incandescentIndexStart = 0;
|
||||
|
||||
for (int i = 0; i < laserBurn.PathCreationTimes.Count; ++i)
|
||||
{
|
||||
if (Time.time - laserBurn.PathCreationTimes[i] < _laserBurnIncandescentDurationFadeEnd)
|
||||
{
|
||||
incandescentIndexStart = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create color and alpha gradients. Maximum number of entries for Unity's LineRenderer are 8.
|
||||
|
||||
if (laserBurn.PathCreationTimes.Count > 0)
|
||||
{
|
||||
Gradient colorGradient = new Gradient();
|
||||
Gradient colorGradientIncandescent = new Gradient();
|
||||
|
||||
colorGradient.colorKeys = new[] { new GradientColorKey(Color.white, 0.0f), new GradientColorKey(Color.white, 1.0f) };
|
||||
colorGradientIncandescent.colorKeys = new[] { new GradientColorKey(Color.white, 0.0f), new GradientColorKey(Color.white, 1.0f) };
|
||||
|
||||
GradientAlphaKey[] alphaKeys = new GradientAlphaKey[8];
|
||||
GradientAlphaKey[] alphaKeysIncandescent = new GradientAlphaKey[8];
|
||||
|
||||
for (int i = 0; i < alphaKeys.Length; ++i)
|
||||
{
|
||||
float t = i / (alphaKeys.Length - 1.0f);
|
||||
int timeIndex = Mathf.Clamp(Mathf.RoundToInt(t * (laserBurn.PathCreationTimes.Count - 1.0f)), 0, laserBurn.PathCreationTimes.Count - 1);
|
||||
float life = Time.time - laserBurn.PathCreationTimes[timeIndex];
|
||||
alphaKeys[i].alpha = life < _laserBurnDurationFadeStart ? 1.0f : 1.0f - Mathf.Clamp01((life - _laserBurnDurationFadeStart) / (_laserBurnDurationFadeEnd - _laserBurnDurationFadeStart));
|
||||
alphaKeys[i].time = t;
|
||||
}
|
||||
|
||||
for (int i = 0; i < alphaKeysIncandescent.Length; ++i)
|
||||
{
|
||||
float t = i / (alphaKeysIncandescent.Length - 1.0f);
|
||||
int numIncandescentEntries = laserBurn.PathCreationTimes.Count - incandescentIndexStart;
|
||||
int timeIndex = Mathf.Clamp(Mathf.RoundToInt(t * (numIncandescentEntries - 1.0f)), 0, numIncandescentEntries - 1);
|
||||
float life = Time.time - laserBurn.PathCreationTimes[incandescentIndexStart + timeIndex];
|
||||
alphaKeysIncandescent[i].alpha = 1.0f - Mathf.Clamp01(life / _laserBurnIncandescentDurationFadeEnd);
|
||||
alphaKeysIncandescent[i].time = t;
|
||||
}
|
||||
|
||||
colorGradient.alphaKeys = alphaKeys;
|
||||
colorGradientIncandescent.alphaKeys = alphaKeysIncandescent;
|
||||
|
||||
laserBurn.LineRenderer.colorGradient = colorGradient;
|
||||
laserBurn.IncandescentLineRenderer.colorGradient = colorGradientIncandescent;
|
||||
}
|
||||
|
||||
// Update positions
|
||||
|
||||
if (laserBurn.PathPositions.Count > 1)
|
||||
{
|
||||
laserBurn.LineRenderer.positionCount = laserBurn.PathPositions.Count;
|
||||
laserBurn.LineRenderer.SetPositions(laserBurn.PathPositions.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
laserBurn.LineRenderer.positionCount = 0;
|
||||
}
|
||||
|
||||
if (incandescentIndexStart < laserBurn.PathPositions.Count - 1)
|
||||
{
|
||||
int count = laserBurn.PathPositions.Count - incandescentIndexStart;
|
||||
laserBurn.IncandescentLineRenderer.positionCount = count;
|
||||
laserBurn.IncandescentLineRenderer.SetPositions(laserBurn.PathPositions.GetRange(incandescentIndexStart, count).ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
laserBurn.IncandescentLineRenderer.positionCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the laser is enabled.
|
||||
/// </summary>
|
||||
private bool IsLaserEnabled
|
||||
{
|
||||
get => _laserLineRenderer.enabled;
|
||||
set
|
||||
{
|
||||
if (_laserLineRenderer.enabled != value)
|
||||
{
|
||||
BeginSync();
|
||||
|
||||
if (value)
|
||||
{
|
||||
// Start a new empty laser burn (it will be setup later, we use null to force to set up a new one)
|
||||
_laserBurns.Add(null);
|
||||
}
|
||||
|
||||
_laserLineRenderer.enabled = value;
|
||||
EndSyncProperty(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current laser burn being generated.
|
||||
/// </summary>
|
||||
private LaserBurn CurrentLaserBurn => _laserBurns[_laserBurns.Count - 1];
|
||||
|
||||
private const float LaserGradientLength = 0.4f; // Laser is rendered with a gradient of this length to make it a little less dull
|
||||
private const float BurnGapCheckSteps = 4; // Number of steps to check for gaps in the geometry when computing laser burns
|
||||
private const float BurnGapCheckMinDistance = 0.02f; // Only perform gap checks if the separation between two consecutive laser burn segments is greater than this value
|
||||
private const float BurnGapCheckRaySourceDistance = 0.1f; // The raycasts performed on each gap check step will be casted from this distance to the geometry
|
||||
private const float BurnGapCheckLineDistanceThreshold = 0.002f; // Allow this amount of depth variation when checking for gaps or bumps in the geometry
|
||||
|
||||
private Vector3 _triggerInitialOffset;
|
||||
private LineRenderer _laserLineRenderer;
|
||||
private List<LaserBurn> _laserBurns;
|
||||
private Vector3 _lastLaserHitPosition;
|
||||
private float _createBurnTimer;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Laser.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/Laser.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82bab9d2f1f530649af83fd846d679e9
|
||||
timeCreated: 1530622409
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
112
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/LightBulb.cs
vendored
Normal file
112
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/LightBulb.cs
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="LightBulb.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene.Lab
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows to model light bulbs that will affect the light attached to the <see cref="Lamp" /> they are placed on.
|
||||
/// </summary>
|
||||
public class LightBulb : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrGrabbableObject _grabbableObject;
|
||||
[SerializeField] private float _lightIntensity;
|
||||
[SerializeField] private bool _isFaulty;
|
||||
[SerializeField] private Color _emissiveDisabled = Color.black;
|
||||
[SerializeField] private Color _emissiveEnabled = Color.white;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the light intensity contributed by the light bulb, which may flicker if it's faulty or be zero if it's not
|
||||
/// connected to the lamp.
|
||||
/// </summary>
|
||||
public float Intensity
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_grabbableObject.CurrentAnchor == null)
|
||||
{
|
||||
// Not attached to anything
|
||||
return 0.0f;
|
||||
}
|
||||
if (_grabbableObject.CurrentAnchor.GetComponentInParent<Lamp>())
|
||||
{
|
||||
// Attached to a lamp. See if it is faulty or works correctly.
|
||||
|
||||
if (_isFaulty)
|
||||
{
|
||||
float noise = Mathf.PerlinNoise(_randX + Time.time * 20.0f, _randY * 10.0f);
|
||||
|
||||
if (noise > 0.66f)
|
||||
{
|
||||
return _lightIntensity;
|
||||
}
|
||||
if (noise > 0.16f)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return _lightIntensity * 0.5f;
|
||||
}
|
||||
return _lightIntensity;
|
||||
}
|
||||
// Not attached to a lamp
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
Renderer renderer = GetComponent<Renderer>();
|
||||
|
||||
if (renderer)
|
||||
{
|
||||
_material = renderer.material;
|
||||
}
|
||||
|
||||
_randX = Random.value;
|
||||
_randY = Random.value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the emissive based on the light intensity.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (_material && _grabbableObject && _lightIntensity > 0.0f)
|
||||
{
|
||||
Color emissiveColor = Color.Lerp(_emissiveDisabled, _emissiveEnabled, Intensity / _lightIntensity);
|
||||
_material.SetColor(UxrConstants.Shaders.EmissionColorVarName, emissiveColor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private Material _material;
|
||||
private float _randX;
|
||||
private float _randY;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/LightBulb.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/LightBulb.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70e977db6e159b54f80cba9f6dc6f6a3
|
||||
timeCreated: 1530531429
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
122
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/SpringOnRelease.cs
vendored
Normal file
122
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/SpringOnRelease.cs
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="SpringOnRelease.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene.Lab
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a spring behavior to an object whenever it is released.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(UxrGrabbableObject))]
|
||||
public class SpringOnRelease : UxrGrabbableObjectComponent<SpringOnRelease>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private float _springAmplitudeMultiplier;
|
||||
[SerializeField] private float _springMaxAmplitude;
|
||||
[SerializeField] private float _springRotAmplitudeMultiplier;
|
||||
[SerializeField] private float _springMaxRotAmplitude;
|
||||
[SerializeField] private float _springDuration;
|
||||
[SerializeField] private float _springFrequency;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Updates the spring if it is currently active.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (_timer > 0.0f)
|
||||
{
|
||||
_timer -= Time.deltaTime;
|
||||
|
||||
if (_timer < 0.0f)
|
||||
{
|
||||
transform.position = _releasePosition;
|
||||
transform.eulerAngles = _releaseEuler;
|
||||
}
|
||||
else
|
||||
{
|
||||
float t = (_springDuration - _timer) / _springDuration;
|
||||
Vector3 delta = _filteredVelocity * (Mathf.Sin(-(_springDuration - _timer) * Mathf.PI * 2.0f * _springFrequency) * (1.0f - t));
|
||||
transform.position = _releasePosition + delta;
|
||||
|
||||
Vector3 deltaEuler = _filteredAngularVelocity * (Mathf.Sin(-(_springDuration - _timer) * Mathf.PI * 2.0f * _springFrequency) * (1.0f - t));
|
||||
transform.eulerAngles = _releaseEuler + deltaEuler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called right after the object was grabbed.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectGrabbed(UxrManipulationEventArgs e)
|
||||
{
|
||||
_timer = -1.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after the object was released.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectReleased(UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.IsGrabbedStateChanged)
|
||||
{
|
||||
_releasePosition = transform.position;
|
||||
_releaseEuler = transform.eulerAngles;
|
||||
_releaseVelocity = e.ReleaseVelocity;
|
||||
_releaseAngularVelocity = e.ReleaseAngularVelocity;
|
||||
_timer = _springDuration;
|
||||
|
||||
_filteredVelocity = _releaseVelocity * _springAmplitudeMultiplier;
|
||||
|
||||
if (_filteredVelocity.magnitude > _springMaxAmplitude)
|
||||
{
|
||||
_filteredVelocity = _filteredVelocity.normalized * _springMaxAmplitude;
|
||||
}
|
||||
|
||||
_filteredAngularVelocity = _releaseAngularVelocity * _springRotAmplitudeMultiplier;
|
||||
|
||||
if (Mathf.Abs(_filteredAngularVelocity.x) > _springMaxRotAmplitude)
|
||||
{
|
||||
_filteredAngularVelocity.x = _filteredAngularVelocity.x > 0.0f ? _springMaxRotAmplitude : -_springMaxRotAmplitude;
|
||||
}
|
||||
if (Mathf.Abs(_filteredAngularVelocity.y) > _springMaxRotAmplitude)
|
||||
{
|
||||
_filteredAngularVelocity.y = _filteredAngularVelocity.y > 0.0f ? _springMaxRotAmplitude : -_springMaxRotAmplitude;
|
||||
}
|
||||
if (Mathf.Abs(_filteredAngularVelocity.z) > _springMaxRotAmplitude)
|
||||
{
|
||||
_filteredAngularVelocity.z = _filteredAngularVelocity.z > 0.0f ? _springMaxRotAmplitude : -_springMaxRotAmplitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private Vector3 _releasePosition;
|
||||
private Vector3 _releaseEuler;
|
||||
private Vector3 _releaseVelocity;
|
||||
private Vector3 _releaseAngularVelocity;
|
||||
private Vector3 _filteredVelocity;
|
||||
private Vector3 _filteredAngularVelocity;
|
||||
private float _timer;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/SpringOnRelease.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Lab/SpringOnRelease.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e9ebb4e3c01c8c4287ce10c7e1f4edd
|
||||
timeCreated: 1530876385
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Weapons.meta
vendored
Normal file
10
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Weapons.meta
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 175666b96a0a9704493170bcd4896304
|
||||
folderAsset: yes
|
||||
timeCreated: 1530189790
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
71
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Weapons/MagAmmoIndicator.cs
vendored
Normal file
71
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Weapons/MagAmmoIndicator.cs
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="MagAmmoIndicator.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Mechanics.Weapons;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Examples.FullScene.Weapons
|
||||
{
|
||||
[RequireComponent(typeof(UxrFirearmMag))]
|
||||
public class MagAmmoIndicator : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _setValueOnStart = true;
|
||||
[SerializeField] private Renderer _renderer;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
_mag = GetComponent<UxrFirearmMag>();
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
_mag.RoundsChanged += OnRoundsChanged;
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
_mag.RoundsChanged -= OnRoundsChanged;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
if (_setValueOnStart)
|
||||
{
|
||||
OnRoundsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
private void OnRoundsChanged()
|
||||
{
|
||||
_renderer.material.SetFloat(FillVariableName, (float)_mag.Rounds / _mag.Capacity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private const string FillVariableName = "_Fill";
|
||||
|
||||
private UxrFirearmMag _mag;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Weapons/MagAmmoIndicator.cs.meta
vendored
Normal file
13
Assets/ThirdParty/UltimateXR/Samples/FullScene/Scripts/Weapons/MagAmmoIndicator.cs.meta
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80780a72a3aeaf549a04f479bfc41f47
|
||||
timeCreated: 1533626585
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user