Initialer Upload neues Unity-Projekt
This commit is contained in:
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public class ActiveStateUnityEventWrapper : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Events will fire based on the state of this IActiveState.")]
|
||||
[SerializeField, Interface(typeof(IActiveState))]
|
||||
private UnityEngine.Object _activeState;
|
||||
private IActiveState ActiveState;
|
||||
|
||||
[Tooltip("This event will be fired when the provided IActiveState becomes active.")]
|
||||
[SerializeField]
|
||||
private UnityEvent _whenActivated;
|
||||
|
||||
[Tooltip("This event will be fired when the provided IActiveState becomes inactive.")]
|
||||
[SerializeField]
|
||||
private UnityEvent _whenDeactivated;
|
||||
|
||||
public UnityEvent WhenActivated => _whenActivated;
|
||||
public UnityEvent WhenDeactivated => _whenDeactivated;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("If true, the corresponding event will be fired at the beginning of Update")]
|
||||
private bool _emitOnFirstUpdate = true;
|
||||
|
||||
private bool _emittedOnFirstUpdate = false;
|
||||
|
||||
private bool _savedState;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
ActiveState = _activeState as IActiveState;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.AssertField(ActiveState, nameof(ActiveState));
|
||||
_savedState = false;
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (_emitOnFirstUpdate && !_emittedOnFirstUpdate)
|
||||
{
|
||||
InvokeEvent();
|
||||
_emittedOnFirstUpdate = true;
|
||||
}
|
||||
|
||||
if (_savedState != ActiveState.Active)
|
||||
{
|
||||
_savedState = ActiveState.Active;
|
||||
InvokeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
private void InvokeEvent()
|
||||
{
|
||||
if (_savedState)
|
||||
{
|
||||
_whenActivated.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
_whenDeactivated.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllActiveStateUnityEventWrapper(IActiveState activeState)
|
||||
{
|
||||
InjectActiveState(activeState);
|
||||
}
|
||||
|
||||
public void InjectActiveState(IActiveState activeState)
|
||||
{
|
||||
_activeState = activeState as UnityEngine.Object;
|
||||
ActiveState = activeState;
|
||||
}
|
||||
|
||||
public void InjectOptionalEmitOnFirstUpdate(bool emitOnFirstUpdate)
|
||||
{
|
||||
_emitOnFirstUpdate = emitOnFirstUpdate;
|
||||
}
|
||||
|
||||
public void InjectOptionalWhenActivated(UnityEvent whenActivated)
|
||||
{
|
||||
_whenActivated = whenActivated;
|
||||
}
|
||||
|
||||
public void InjectOptionalWhenDeactivated(UnityEvent whenDeactivated)
|
||||
{
|
||||
_whenDeactivated = whenDeactivated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d2705674196dfc4da9c25fbacda72d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62d408212f3d68b45b2bb2deb2d33239
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Input;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Unity.Input
|
||||
{
|
||||
public class InputAxis : MonoBehaviour, IAxis1D
|
||||
{
|
||||
[SerializeField]
|
||||
private string _axisName;
|
||||
|
||||
public float Value()
|
||||
{
|
||||
return UnityEngine.Input.GetAxis(_axisName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62ac6de3f9c5a0a4c98563d8b26fc962
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Input;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Unity.Input
|
||||
{
|
||||
public class InputButton : MonoBehaviour, IButton
|
||||
{
|
||||
[SerializeField]
|
||||
private string _buttonName;
|
||||
|
||||
public bool Value()
|
||||
{
|
||||
return UnityEngine.Input.GetButton(_buttonName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17cbfd92dddfbac42becf0ad22b3d80d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Input;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Unity.Input
|
||||
{
|
||||
public class InputKey : MonoBehaviour, IButton
|
||||
{
|
||||
[SerializeField]
|
||||
private KeyCode _key;
|
||||
|
||||
public bool Value()
|
||||
{
|
||||
return UnityEngine.Input.GetKey(_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c623d571545a954eb981f14cf5e3eef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using Oculus.Interaction.Input;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.Unity.Input
|
||||
{
|
||||
public class InputMouseButton : MonoBehaviour, IButton
|
||||
{
|
||||
[SerializeField]
|
||||
private int _button;
|
||||
|
||||
public bool Value()
|
||||
{
|
||||
return UnityEngine.Input.GetMouseButton(_button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d388cb9865e6fb34c83ff0b41a218bde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// This component makes it possible to connect Interactables in the
|
||||
/// inspector to Unity Events that are broadcast on state changes.
|
||||
/// </summary>
|
||||
public class InteractableUnityEventWrapper : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Interface(typeof(IInteractableView))]
|
||||
private UnityEngine.Object _interactableView;
|
||||
private IInteractableView InteractableView;
|
||||
|
||||
[SerializeField]
|
||||
private UnityEvent _whenHover;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenUnhover;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenSelect;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenUnselect;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenInteractorViewAdded;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenInteractorViewRemoved;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenSelectingInteractorViewAdded;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenSelectingInteractorViewRemoved;
|
||||
|
||||
#region Properties
|
||||
|
||||
public UnityEvent WhenHover => _whenHover;
|
||||
public UnityEvent WhenUnhover => _whenUnhover;
|
||||
public UnityEvent WhenSelect => _whenSelect;
|
||||
public UnityEvent WhenUnselect => _whenUnselect;
|
||||
public UnityEvent WhenInteractorViewAdded => _whenInteractorViewAdded;
|
||||
public UnityEvent WhenInteractorViewRemoved => _whenInteractorViewRemoved;
|
||||
public UnityEvent WhenSelectingInteractorViewAdded => _whenSelectingInteractorViewAdded;
|
||||
public UnityEvent WhenSelectingInteractorViewRemoved => _whenSelectingInteractorViewRemoved;
|
||||
|
||||
#endregion
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
InteractableView = _interactableView as IInteractableView;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(InteractableView, nameof(InteractableView));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
InteractableView.WhenStateChanged += HandleStateChanged;
|
||||
InteractableView.WhenInteractorViewAdded += HandleInteractorViewAdded;
|
||||
InteractableView.WhenInteractorViewRemoved += HandleInteractorViewRemoved;
|
||||
InteractableView.WhenSelectingInteractorViewAdded += HandleSelectingInteractorViewAdded;
|
||||
InteractableView.WhenSelectingInteractorViewRemoved += HandleSelectingInteractorViewRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
InteractableView.WhenStateChanged -= HandleStateChanged;
|
||||
InteractableView.WhenInteractorViewAdded -= HandleInteractorViewAdded;
|
||||
InteractableView.WhenInteractorViewRemoved -= HandleInteractorViewRemoved;
|
||||
InteractableView.WhenSelectingInteractorViewAdded -= HandleSelectingInteractorViewAdded;
|
||||
InteractableView.WhenSelectingInteractorViewRemoved -= HandleSelectingInteractorViewRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStateChanged(InteractableStateChangeArgs args)
|
||||
{
|
||||
switch (args.NewState)
|
||||
{
|
||||
case InteractableState.Normal:
|
||||
if (args.PreviousState == InteractableState.Hover)
|
||||
{
|
||||
_whenUnhover.Invoke();
|
||||
}
|
||||
|
||||
break;
|
||||
case InteractableState.Hover:
|
||||
if (args.PreviousState == InteractableState.Normal)
|
||||
{
|
||||
_whenHover.Invoke();
|
||||
}
|
||||
else if (args.PreviousState == InteractableState.Select)
|
||||
{
|
||||
_whenUnselect.Invoke();
|
||||
}
|
||||
|
||||
break;
|
||||
case InteractableState.Select:
|
||||
if (args.PreviousState == InteractableState.Hover)
|
||||
{
|
||||
_whenSelect.Invoke();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleInteractorViewAdded(IInteractorView interactorView)
|
||||
{
|
||||
WhenInteractorViewAdded.Invoke();
|
||||
}
|
||||
|
||||
private void HandleInteractorViewRemoved(IInteractorView interactorView)
|
||||
{
|
||||
WhenInteractorViewRemoved.Invoke();
|
||||
}
|
||||
|
||||
private void HandleSelectingInteractorViewAdded(IInteractorView interactorView)
|
||||
{
|
||||
WhenSelectingInteractorViewAdded.Invoke();
|
||||
}
|
||||
|
||||
private void HandleSelectingInteractorViewRemoved(IInteractorView interactorView)
|
||||
{
|
||||
WhenSelectingInteractorViewRemoved.Invoke();
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllInteractableUnityEventWrapper(IInteractableView interactableView)
|
||||
{
|
||||
InjectInteractableView(interactableView);
|
||||
}
|
||||
|
||||
public void InjectInteractableView(IInteractableView interactableView)
|
||||
{
|
||||
_interactableView = interactableView as UnityEngine.Object;
|
||||
InteractableView = interactableView;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1464721f2283eb14e94a33e812b47be4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// This component makes it possible to connect Interactors in the
|
||||
/// inspector to Unity Events that broadcast on state changes
|
||||
/// </summary>
|
||||
public class InteractorUnityEventWrapper : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Interface(typeof(IInteractorView))]
|
||||
private UnityEngine.Object _interactorView;
|
||||
private IInteractorView InteractorView;
|
||||
|
||||
[SerializeField]
|
||||
private UnityEvent _whenEnabled;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenDisabled;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenHover;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenUnhover;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenSelect;
|
||||
[SerializeField]
|
||||
private UnityEvent _whenUnselect;
|
||||
|
||||
public UnityEvent WhenDisabled => _whenDisabled;
|
||||
public UnityEvent WhenEnabled => _whenEnabled;
|
||||
public UnityEvent WhenHover => _whenHover;
|
||||
public UnityEvent WhenUnhover => _whenUnhover;
|
||||
public UnityEvent WhenSelect => _whenSelect;
|
||||
public UnityEvent WhenUnselect => _whenUnselect;
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
InteractorView = _interactorView as IInteractorView;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(InteractorView, nameof(InteractorView));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
InteractorView.WhenStateChanged += HandleStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
InteractorView.WhenStateChanged -= HandleStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStateChanged(InteractorStateChangeArgs args)
|
||||
{
|
||||
switch (args.NewState)
|
||||
{
|
||||
case InteractorState.Disabled:
|
||||
_whenDisabled.Invoke();
|
||||
break;
|
||||
case InteractorState.Normal:
|
||||
if (args.PreviousState == InteractorState.Hover)
|
||||
{
|
||||
_whenUnhover.Invoke();
|
||||
}
|
||||
else if (args.PreviousState == InteractorState.Disabled)
|
||||
{
|
||||
_whenEnabled.Invoke();
|
||||
}
|
||||
break;
|
||||
case InteractorState.Hover:
|
||||
if (args.PreviousState == InteractorState.Normal)
|
||||
{
|
||||
_whenHover.Invoke();
|
||||
}
|
||||
else if (args.PreviousState == InteractorState.Select)
|
||||
{
|
||||
_whenUnselect.Invoke();
|
||||
}
|
||||
|
||||
break;
|
||||
case InteractorState.Select:
|
||||
if (args.PreviousState == InteractorState.Hover)
|
||||
{
|
||||
_whenSelect.Invoke();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllInteractorUnityEventWrapper(IInteractorView interactorView)
|
||||
{
|
||||
InjectInteractorView(interactorView);
|
||||
}
|
||||
|
||||
public void InjectInteractorView(IInteractorView interactorView)
|
||||
{
|
||||
_interactorView = interactorView as UnityEngine.Object;
|
||||
InteractorView = interactorView;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0cf00a93483fd1b4ba357bf6f764d6e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public class PhysicsGrabbable : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private Grabbable _grabbable;
|
||||
|
||||
[SerializeField]
|
||||
private Rigidbody _rigidbody;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("If enabled, the object's mass will scale appropriately as the scale of the object changes.")]
|
||||
private bool _scaleMassWithSize = true;
|
||||
|
||||
private bool _savedIsKinematicState = false;
|
||||
private bool _isBeingTransformed = false;
|
||||
private Vector3 _initialScale;
|
||||
private bool _hasPendingForce;
|
||||
private Vector3 _linearVelocity;
|
||||
private Vector3 _angularVelocity;
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
public event Action<Vector3, Vector3> WhenVelocitiesApplied = delegate { };
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
_grabbable = this.GetComponent<Grabbable>();
|
||||
_rigidbody = this.GetComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(_grabbable, nameof(_grabbable));
|
||||
this.AssertField(_rigidbody, nameof(_rigidbody));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
_grabbable.WhenPointerEventRaised += HandlePointerEventRaised;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
_grabbable.WhenPointerEventRaised -= HandlePointerEventRaised;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePointerEventRaised(PointerEvent evt)
|
||||
{
|
||||
switch (evt.Type)
|
||||
{
|
||||
case PointerEventType.Select:
|
||||
if (_grabbable.SelectingPointsCount == 1 && !_isBeingTransformed)
|
||||
{
|
||||
DisablePhysics();
|
||||
}
|
||||
|
||||
break;
|
||||
case PointerEventType.Unselect:
|
||||
if (_grabbable.SelectingPointsCount == 0)
|
||||
{
|
||||
ReenablePhysics();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisablePhysics()
|
||||
{
|
||||
_isBeingTransformed = true;
|
||||
CachePhysicsState();
|
||||
_rigidbody.isKinematic = true;
|
||||
}
|
||||
|
||||
private void ReenablePhysics()
|
||||
{
|
||||
_isBeingTransformed = false;
|
||||
// update the mass based on the scale change
|
||||
if (_scaleMassWithSize)
|
||||
{
|
||||
float initialScaledVolume = _initialScale.x * _initialScale.y * _initialScale.z;
|
||||
|
||||
Vector3 currentScale = _rigidbody.transform.localScale;
|
||||
float currentScaledVolume = currentScale.x * currentScale.y * currentScale.z;
|
||||
|
||||
float changeInMassFactor = currentScaledVolume / initialScaledVolume;
|
||||
_rigidbody.mass *= changeInMassFactor;
|
||||
}
|
||||
|
||||
// revert the original kinematic state
|
||||
_rigidbody.isKinematic = _savedIsKinematicState;
|
||||
}
|
||||
|
||||
public void ApplyVelocities(Vector3 linearVelocity, Vector3 angularVelocity)
|
||||
{
|
||||
_hasPendingForce = true;
|
||||
_linearVelocity = linearVelocity;
|
||||
_angularVelocity = angularVelocity;
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (_hasPendingForce)
|
||||
{
|
||||
_hasPendingForce = false;
|
||||
_rigidbody.AddForce(_linearVelocity, ForceMode.VelocityChange);
|
||||
_rigidbody.AddTorque(_angularVelocity, ForceMode.VelocityChange);
|
||||
WhenVelocitiesApplied(_linearVelocity, _angularVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
private void CachePhysicsState()
|
||||
{
|
||||
_savedIsKinematicState = _rigidbody.isKinematic;
|
||||
_initialScale = _rigidbody.transform.localScale;
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllPhysicsGrabbable(Grabbable grabbable, Rigidbody rigidbody)
|
||||
{
|
||||
InjectGrabbable(grabbable);
|
||||
InjectRigidbody(rigidbody);
|
||||
}
|
||||
|
||||
public void InjectGrabbable(Grabbable grabbable)
|
||||
{
|
||||
_grabbable = grabbable;
|
||||
}
|
||||
|
||||
public void InjectRigidbody(Rigidbody rigidbody)
|
||||
{
|
||||
_rigidbody = rigidbody;
|
||||
}
|
||||
|
||||
public void InjectOptionalScaleMassWithSize(bool scaleMassWithSize)
|
||||
{
|
||||
_scaleMassWithSize = scaleMassWithSize;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bedaac29a943f844a7074b53c8bdf9f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// PointableCanvas allows any IPointable to forward its
|
||||
/// events onto an associated Canvas via the IPointableCanvas interface
|
||||
/// Requires a PointableCanvasModule present in the scene.
|
||||
/// </summary>
|
||||
public class PointableCanvas : PointableElement, IPointableCanvas
|
||||
{
|
||||
[Tooltip("PointerEvents will be forwarded to this Unity Canvas.")]
|
||||
[SerializeField]
|
||||
private Canvas _canvas;
|
||||
public Canvas Canvas => _canvas;
|
||||
|
||||
private bool _registered = false;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
this.AssertField(Canvas, nameof(Canvas));
|
||||
this.AssertIsTrue(Canvas.TryGetComponent(out GraphicRaycaster raycaster),
|
||||
$"{nameof(PointableCanvas)} requires that the {nameof(Canvas)} object has an attached GraphicRaycaster.");
|
||||
}
|
||||
|
||||
private void Register()
|
||||
{
|
||||
PointableCanvasModule.RegisterPointableCanvas(this);
|
||||
_registered = true;
|
||||
}
|
||||
|
||||
private void Unregister()
|
||||
{
|
||||
if (!_registered) return;
|
||||
PointableCanvasModule.UnregisterPointableCanvas(this);
|
||||
_registered = false;
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
if (_started)
|
||||
{
|
||||
Register();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Unregister();
|
||||
}
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllPointableCanvas(Canvas canvas)
|
||||
{
|
||||
InjectCanvas(canvas);
|
||||
}
|
||||
|
||||
public void InjectCanvas(Canvas canvas)
|
||||
{
|
||||
_canvas = canvas;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ffe41fe81087fa41a2062cc69b99615
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using Oculus.Interaction.UnityCanvas;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public class PointableCanvasMesh : PointableElement
|
||||
{
|
||||
[Tooltip("This CanvasMesh determines the Pose of PointerEvents.")]
|
||||
[SerializeField]
|
||||
[FormerlySerializedAs("_canvasRenderTextureMesh")]
|
||||
private CanvasMesh _canvasMesh;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
this.AssertField(_canvasMesh, nameof(_canvasMesh));
|
||||
}
|
||||
|
||||
public override void ProcessPointerEvent(PointerEvent evt)
|
||||
{
|
||||
Vector3 transformPosition =
|
||||
_canvasMesh.ImposterToCanvasTransformPoint(evt.Pose.position);
|
||||
Pose transformedPose = new Pose(transformPosition, evt.Pose.rotation);
|
||||
base.ProcessPointerEvent(new PointerEvent(evt.Identifier, evt.Type, transformedPose, evt.Data));
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllCanvasMeshPointable(CanvasMesh canvasMesh)
|
||||
{
|
||||
InjectCanvasMesh(canvasMesh);
|
||||
}
|
||||
|
||||
public void InjectCanvasMesh(CanvasMesh canvasMesh)
|
||||
{
|
||||
_canvasMesh = canvasMesh;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b78f8e52fe10fd9499c5e5c8b3f953fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,622 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using System;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public class PointableCanvasEventArgs
|
||||
{
|
||||
public readonly Canvas Canvas;
|
||||
public readonly GameObject Hovered;
|
||||
public readonly bool Dragging;
|
||||
|
||||
public PointableCanvasEventArgs(Canvas canvas, GameObject hovered, bool dragging)
|
||||
{
|
||||
Canvas = canvas;
|
||||
Hovered = hovered;
|
||||
Dragging = dragging;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IPointerInteractableModule manages all InteractableCanvas events in
|
||||
/// the scene and translates them into pointer events for Unity Canvas UIs.
|
||||
/// </summary>
|
||||
public class PointableCanvasModule : PointerInputModule
|
||||
{
|
||||
public static event Action<PointableCanvasEventArgs> WhenSelected;
|
||||
|
||||
public static event Action<PointableCanvasEventArgs> WhenUnselected;
|
||||
|
||||
public static event Action<PointableCanvasEventArgs> WhenSelectableHovered;
|
||||
|
||||
public static event Action<PointableCanvasEventArgs> WhenSelectableUnhovered;
|
||||
|
||||
[Tooltip("If true, the initial press position will be used as the drag start " +
|
||||
"position, rather than the position when drag threshold is exceeded. This is used " +
|
||||
"to prevent the pointer position shifting relative to the surface while dragging.")]
|
||||
[SerializeField]
|
||||
private bool _useInitialPressPositionForDrag = true;
|
||||
|
||||
private Camera _pointerEventCamera;
|
||||
private static PointableCanvasModule _instance = null;
|
||||
private static PointableCanvasModule Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterPointableCanvas(IPointableCanvas pointerCanvas)
|
||||
{
|
||||
Assert.IsNotNull(Instance, $"A <b>{nameof(PointableCanvasModule)}</b> is required in the scene.");
|
||||
Instance.AddPointerCanvas(pointerCanvas);
|
||||
}
|
||||
|
||||
public static void UnregisterPointableCanvas(IPointableCanvas pointerCanvas)
|
||||
{
|
||||
Instance?.RemovePointerCanvas(pointerCanvas);
|
||||
}
|
||||
|
||||
private Dictionary<int, Pointer> _pointerMap = new Dictionary<int, Pointer>();
|
||||
private List<RaycastResult> _raycastResultCache = new List<RaycastResult>();
|
||||
private List<Pointer> _pointersForDeletion = new List<Pointer>();
|
||||
private Dictionary<IPointableCanvas, Action<PointerEvent>> _pointerCanvasActionMap =
|
||||
new Dictionary<IPointableCanvas, Action<PointerEvent>>();
|
||||
|
||||
private Pointer[] _pointersToProcessScratch = Array.Empty<Pointer>();
|
||||
|
||||
private void AddPointerCanvas(IPointableCanvas pointerCanvas)
|
||||
{
|
||||
Action<PointerEvent> pointerCanvasAction = (args) => HandlePointerEvent(pointerCanvas.Canvas, args);
|
||||
_pointerCanvasActionMap.Add(pointerCanvas, pointerCanvasAction);
|
||||
pointerCanvas.WhenPointerEventRaised += pointerCanvasAction;
|
||||
}
|
||||
|
||||
private void RemovePointerCanvas(IPointableCanvas pointerCanvas)
|
||||
{
|
||||
Action<PointerEvent> pointerCanvasAction = _pointerCanvasActionMap[pointerCanvas];
|
||||
_pointerCanvasActionMap.Remove(pointerCanvas);
|
||||
pointerCanvas.WhenPointerEventRaised -= pointerCanvasAction;
|
||||
|
||||
List<int> pointerIDs = new List<int>(_pointerMap.Keys);
|
||||
foreach (int pointerID in pointerIDs)
|
||||
{
|
||||
Pointer pointer = _pointerMap[pointerID];
|
||||
if (pointer.Canvas != pointerCanvas.Canvas)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ClearPointerSelection(pointer.PointerEventData);
|
||||
pointer.MarkForDeletion();
|
||||
_pointersForDeletion.Add(pointer);
|
||||
_pointerMap.Remove(pointerID);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePointerEvent(Canvas canvas, PointerEvent evt)
|
||||
{
|
||||
Pointer pointer;
|
||||
|
||||
switch (evt.Type)
|
||||
{
|
||||
case PointerEventType.Hover:
|
||||
pointer = new Pointer(canvas);
|
||||
pointer.PointerEventData = new PointerEventData(eventSystem);
|
||||
pointer.SetPosition(evt.Pose.position);
|
||||
_pointerMap.Add(evt.Identifier, pointer);
|
||||
break;
|
||||
case PointerEventType.Unhover:
|
||||
pointer = _pointerMap[evt.Identifier];
|
||||
_pointerMap.Remove(evt.Identifier);
|
||||
pointer.MarkForDeletion();
|
||||
_pointersForDeletion.Add(pointer);
|
||||
break;
|
||||
case PointerEventType.Select:
|
||||
pointer = _pointerMap[evt.Identifier];
|
||||
pointer.SetPosition(evt.Pose.position);
|
||||
pointer.Press();
|
||||
break;
|
||||
case PointerEventType.Unselect:
|
||||
pointer = _pointerMap[evt.Identifier];
|
||||
pointer.SetPosition(evt.Pose.position);
|
||||
pointer.Release();
|
||||
break;
|
||||
case PointerEventType.Move:
|
||||
pointer = _pointerMap[evt.Identifier];
|
||||
pointer.SetPosition(evt.Pose.position);
|
||||
break;
|
||||
case PointerEventType.Cancel:
|
||||
pointer = _pointerMap[evt.Identifier];
|
||||
_pointerMap.Remove(evt.Identifier);
|
||||
ClearPointerSelection(pointer.PointerEventData);
|
||||
pointer.MarkForDeletion();
|
||||
_pointersForDeletion.Add(pointer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pointer class that is used for state associated with IPointables that are currently
|
||||
/// tracked by any IPointableCanvases in the scene.
|
||||
/// </summary>
|
||||
private class Pointer
|
||||
{
|
||||
public PointerEventData PointerEventData { get; set; }
|
||||
|
||||
public bool MarkedForDeletion { get; private set; }
|
||||
|
||||
private Canvas _canvas;
|
||||
public Canvas Canvas => _canvas;
|
||||
|
||||
private Vector3 _position;
|
||||
public Vector3 Position => _position;
|
||||
|
||||
private GameObject _hoveredSelectable;
|
||||
public GameObject HoveredSelectable => _hoveredSelectable;
|
||||
|
||||
|
||||
private bool _pressing = false;
|
||||
private bool _pressed;
|
||||
private bool _released;
|
||||
|
||||
public Pointer(Canvas canvas)
|
||||
{
|
||||
_canvas = canvas;
|
||||
_pressed = _released = false;
|
||||
}
|
||||
|
||||
public void Press()
|
||||
{
|
||||
if (_pressing) return;
|
||||
_pressing = true;
|
||||
_pressed = true;
|
||||
}
|
||||
public void Release()
|
||||
{
|
||||
if (!_pressing) return;
|
||||
_pressing = false;
|
||||
_released = true;
|
||||
}
|
||||
|
||||
public void ReadAndResetPressedReleased(out bool pressed, out bool released)
|
||||
{
|
||||
pressed = _pressed;
|
||||
released = _released;
|
||||
_pressed = _released = false;
|
||||
}
|
||||
|
||||
public void MarkForDeletion()
|
||||
{
|
||||
MarkedForDeletion = true;
|
||||
Release();
|
||||
}
|
||||
|
||||
public void SetPosition(Vector3 position)
|
||||
{
|
||||
_position = position;
|
||||
}
|
||||
|
||||
public void SetHoveredSelectable(GameObject hoveredSelectable)
|
||||
{
|
||||
_hoveredSelectable = hoveredSelectable;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
Assert.IsNull(_instance, "There must be at most one PointableCanvasModule in the scene");
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
// Must unset _instance prior to calling the base.OnDestroy, otherwise error is thrown:
|
||||
// Can't add component to object that is being destroyed.
|
||||
// UnityEngine.EventSystems.BaseInputModule:get_input ()
|
||||
_instance = null;
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
this.BeginStart(ref _started, () => base.Start());
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
if (_started)
|
||||
{
|
||||
_pointerEventCamera = gameObject.AddComponent<Camera>();
|
||||
_pointerEventCamera.nearClipPlane = 0.1f;
|
||||
|
||||
// We do not need this camera to be enabled to serve this module's purposes:
|
||||
// as a dependency for Canvases and for its WorldToScreenPoint functionality
|
||||
_pointerEventCamera.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Destroy(_pointerEventCamera);
|
||||
_pointerEventCamera = null;
|
||||
}
|
||||
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
// Based On FindFirstRaycast
|
||||
protected static RaycastResult FindFirstRaycastWithinCanvas(List<RaycastResult> candidates, Canvas canvas)
|
||||
{
|
||||
GameObject candidateGameObject;
|
||||
Canvas candidateCanvas;
|
||||
for (var i = 0; i < candidates.Count; ++i)
|
||||
{
|
||||
candidateGameObject = candidates[i].gameObject;
|
||||
if (candidateGameObject == null) continue;
|
||||
|
||||
candidateCanvas = candidateGameObject.GetComponentInParent<Canvas>();
|
||||
if (candidateCanvas == null) continue;
|
||||
if (candidateCanvas.rootCanvas != canvas) continue;
|
||||
|
||||
return candidates[i];
|
||||
}
|
||||
return new RaycastResult();
|
||||
}
|
||||
|
||||
private void UpdateRaycasts(Pointer pointer, out bool pressed, out bool released)
|
||||
{
|
||||
PointerEventData pointerEventData = pointer.PointerEventData;
|
||||
Vector2 prevPosition = pointerEventData.position;
|
||||
pointerEventData.Reset();
|
||||
|
||||
pointer.ReadAndResetPressedReleased(out pressed, out released);
|
||||
|
||||
if (pointer.MarkedForDeletion)
|
||||
{
|
||||
pointerEventData.pointerCurrentRaycast = new RaycastResult();
|
||||
return;
|
||||
}
|
||||
|
||||
Canvas canvas = pointer.Canvas;
|
||||
canvas.worldCamera = _pointerEventCamera;
|
||||
|
||||
Vector3 position = Vector3.zero;
|
||||
var plane = new Plane(-1f * canvas.transform.forward, canvas.transform.position);
|
||||
var ray = new Ray(pointer.Position - canvas.transform.forward, canvas.transform.forward);
|
||||
|
||||
float enter;
|
||||
if (plane.Raycast(ray, out enter))
|
||||
{
|
||||
position = ray.GetPoint(enter);
|
||||
}
|
||||
|
||||
// We need to position our camera at an offset from the Pointer position or else
|
||||
// a graphic raycast may ignore a world canvas that's outside of our regular camera view(s)
|
||||
_pointerEventCamera.transform.position = pointer.Position - canvas.transform.forward;
|
||||
_pointerEventCamera.transform.LookAt(pointer.Position, canvas.transform.up);
|
||||
|
||||
Vector2 pointerPosition = _pointerEventCamera.WorldToScreenPoint(position);
|
||||
pointerEventData.position = pointerPosition;
|
||||
|
||||
// RaycastAll raycasts against with every GraphicRaycaster in the scene,
|
||||
// including nested ones like in the case of a dropdown
|
||||
eventSystem.RaycastAll(pointerEventData, _raycastResultCache);
|
||||
|
||||
RaycastResult firstResult = FindFirstRaycastWithinCanvas(_raycastResultCache, canvas);
|
||||
pointer.PointerEventData.pointerCurrentRaycast = firstResult;
|
||||
|
||||
_raycastResultCache.Clear();
|
||||
|
||||
// We use a static translation offset from the canvas for 2D position delta tracking
|
||||
_pointerEventCamera.transform.position = canvas.transform.position - canvas.transform.forward;
|
||||
_pointerEventCamera.transform.LookAt(canvas.transform.position, canvas.transform.up);
|
||||
|
||||
pointerPosition = _pointerEventCamera.WorldToScreenPoint(position);
|
||||
pointerEventData.position = pointerPosition;
|
||||
|
||||
if (pressed)
|
||||
{
|
||||
pointerEventData.delta = Vector2.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
pointerEventData.delta = pointerEventData.position - prevPosition;
|
||||
}
|
||||
|
||||
pointerEventData.button = PointerEventData.InputButton.Left;
|
||||
}
|
||||
|
||||
public override void Process()
|
||||
{
|
||||
ProcessPointers(_pointersForDeletion, true);
|
||||
ProcessPointers(_pointerMap.Values, false);
|
||||
}
|
||||
|
||||
private void ProcessPointers(ICollection<Pointer> pointers, bool clearAndReleasePointers)
|
||||
{
|
||||
// Before processing pointers, take a copy of the array since _pointersForDeletion or
|
||||
// _pointerMap may be modified if a pointer event handler adds or removes a
|
||||
// PointableCanvas.
|
||||
|
||||
int pointersToProcessCount = pointers.Count;
|
||||
if (pointersToProcessCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pointersToProcessCount > _pointersToProcessScratch.Length)
|
||||
{
|
||||
_pointersToProcessScratch = new Pointer[pointersToProcessCount];
|
||||
}
|
||||
|
||||
pointers.CopyTo(_pointersToProcessScratch, 0);
|
||||
if (clearAndReleasePointers)
|
||||
{
|
||||
pointers.Clear();
|
||||
}
|
||||
|
||||
foreach (Pointer pointer in _pointersToProcessScratch)
|
||||
{
|
||||
ProcessPointer(pointer, clearAndReleasePointers);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessPointer(Pointer pointer, bool forceRelease = false)
|
||||
{
|
||||
bool pressed = false;
|
||||
bool released = false;
|
||||
bool wasDragging = pointer.PointerEventData.dragging;
|
||||
|
||||
UpdateRaycasts(pointer, out pressed, out released);
|
||||
|
||||
PointerEventData pointerEventData = pointer.PointerEventData;
|
||||
UpdatePointerEventData(pointerEventData, pressed, released);
|
||||
|
||||
released |= forceRelease;
|
||||
|
||||
if (!released)
|
||||
{
|
||||
ProcessMove(pointerEventData);
|
||||
ProcessDrag(pointerEventData);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandlePointerExitAndEnter(pointerEventData, null);
|
||||
RemovePointerData(pointerEventData);
|
||||
}
|
||||
|
||||
HandleSelectableHover(pointer, wasDragging);
|
||||
HandleSelectablePress(pointer, pressed, released, wasDragging);
|
||||
}
|
||||
|
||||
private void HandleSelectableHover(Pointer pointer, bool wasDragging)
|
||||
{
|
||||
bool dragging = pointer.PointerEventData.dragging || wasDragging;
|
||||
|
||||
GameObject currentOverGo = pointer.PointerEventData.pointerCurrentRaycast.gameObject;
|
||||
GameObject prevHoveredSelectable = pointer.HoveredSelectable;
|
||||
GameObject newHoveredSelectable = ExecuteEvents.GetEventHandler<ISelectHandler>(currentOverGo);
|
||||
pointer.SetHoveredSelectable(newHoveredSelectable);
|
||||
|
||||
if (newHoveredSelectable != null && newHoveredSelectable != prevHoveredSelectable)
|
||||
{
|
||||
WhenSelectableHovered?.Invoke(new PointableCanvasEventArgs(pointer.Canvas, pointer.HoveredSelectable, dragging));
|
||||
}
|
||||
else if (prevHoveredSelectable != null && newHoveredSelectable == null)
|
||||
{
|
||||
WhenSelectableUnhovered?.Invoke(new PointableCanvasEventArgs(pointer.Canvas, pointer.HoveredSelectable, dragging));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSelectablePress(Pointer pointer, bool pressed, bool released, bool wasDragging)
|
||||
{
|
||||
bool dragging = pointer.PointerEventData.dragging || wasDragging;
|
||||
|
||||
if (pressed)
|
||||
{
|
||||
WhenSelected?.Invoke(new PointableCanvasEventArgs(pointer.Canvas, pointer.HoveredSelectable, dragging));
|
||||
}
|
||||
else if (released && !pointer.MarkedForDeletion)
|
||||
{
|
||||
// Unity handles UI selection on release, so we verify the hovered element has been selected
|
||||
bool hasSelectedHoveredObject = pointer.HoveredSelectable != null &&
|
||||
pointer.HoveredSelectable == pointer.PointerEventData.selectedObject;
|
||||
GameObject selectedObject = hasSelectedHoveredObject ? pointer.HoveredSelectable : null;
|
||||
WhenUnselected?.Invoke(new PointableCanvasEventArgs(pointer.Canvas, selectedObject, dragging));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is based on ProcessTouchPoint in StandaloneInputModule,
|
||||
/// but is instead used for Pointer events
|
||||
/// </summary>
|
||||
protected void UpdatePointerEventData(PointerEventData pointerEvent, bool pressed, bool released)
|
||||
{
|
||||
var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject;
|
||||
|
||||
// PointerDown notification
|
||||
if (pressed)
|
||||
{
|
||||
pointerEvent.eligibleForClick = true;
|
||||
pointerEvent.delta = Vector2.zero;
|
||||
pointerEvent.dragging = false;
|
||||
pointerEvent.useDragThreshold = true;
|
||||
pointerEvent.pressPosition = pointerEvent.position;
|
||||
pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast;
|
||||
|
||||
DeselectIfSelectionChanged(currentOverGo, pointerEvent);
|
||||
|
||||
if (pointerEvent.pointerEnter != currentOverGo)
|
||||
{
|
||||
// send a pointer enter to the touched element if it isn't the one to select...
|
||||
HandlePointerExitAndEnter(pointerEvent, currentOverGo);
|
||||
pointerEvent.pointerEnter = currentOverGo;
|
||||
}
|
||||
|
||||
// search for the control that will receive the press
|
||||
// if we can't find a press handler set the press
|
||||
// handler to be what would receive a click.
|
||||
var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler);
|
||||
|
||||
// didnt find a press handler... search for a click handler
|
||||
if (newPressed == null)
|
||||
newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
|
||||
|
||||
float time = Time.unscaledTime;
|
||||
|
||||
if (newPressed == pointerEvent.lastPress)
|
||||
{
|
||||
var diffTime = time - pointerEvent.clickTime;
|
||||
if (diffTime < 0.3f)
|
||||
++pointerEvent.clickCount;
|
||||
else
|
||||
pointerEvent.clickCount = 1;
|
||||
|
||||
pointerEvent.clickTime = time;
|
||||
}
|
||||
else
|
||||
{
|
||||
pointerEvent.clickCount = 1;
|
||||
}
|
||||
|
||||
pointerEvent.pointerPress = newPressed;
|
||||
pointerEvent.rawPointerPress = currentOverGo;
|
||||
|
||||
pointerEvent.clickTime = time;
|
||||
|
||||
// Save the drag handler as well
|
||||
pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
|
||||
|
||||
if (pointerEvent.pointerDrag != null)
|
||||
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag);
|
||||
|
||||
}
|
||||
|
||||
// PointerUp notification
|
||||
if (released)
|
||||
{
|
||||
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
|
||||
|
||||
// see if we mouse up on the same element that we clicked on...
|
||||
var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
|
||||
|
||||
// PointerClick and Drop events
|
||||
if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick)
|
||||
{
|
||||
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler);
|
||||
}
|
||||
|
||||
if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
|
||||
{
|
||||
ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler);
|
||||
}
|
||||
|
||||
pointerEvent.eligibleForClick = false;
|
||||
pointerEvent.pointerPress = null;
|
||||
pointerEvent.rawPointerPress = null;
|
||||
|
||||
if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
|
||||
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler);
|
||||
|
||||
pointerEvent.dragging = false;
|
||||
pointerEvent.pointerDrag = null;
|
||||
|
||||
// send exit events as we need to simulate this on touch up on touch device
|
||||
ExecuteEvents.ExecuteHierarchy(pointerEvent.pointerEnter, pointerEvent, ExecuteEvents.pointerExitHandler);
|
||||
pointerEvent.pointerEnter = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override of PointerInputModule's ProcessDrag to allow using the initial press position for drag begin.
|
||||
/// Set _useInitialPressPositionForDrag to false if you prefer the default behaviour of PointerInputModule.
|
||||
/// </summary>
|
||||
protected override void ProcessDrag(PointerEventData pointerEvent)
|
||||
{
|
||||
if (!pointerEvent.IsPointerMoving() ||
|
||||
pointerEvent.pointerDrag == null)
|
||||
return;
|
||||
|
||||
if (!pointerEvent.dragging
|
||||
&& ShouldStartDrag(pointerEvent.pressPosition, pointerEvent.position,
|
||||
eventSystem.pixelDragThreshold, pointerEvent.useDragThreshold))
|
||||
{
|
||||
if (_useInitialPressPositionForDrag)
|
||||
{
|
||||
pointerEvent.position = pointerEvent.pressPosition;
|
||||
}
|
||||
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent,
|
||||
ExecuteEvents.beginDragHandler);
|
||||
pointerEvent.dragging = true;
|
||||
}
|
||||
|
||||
// Drag notification
|
||||
if (pointerEvent.dragging)
|
||||
{
|
||||
// Before doing drag we should cancel any pointer down state
|
||||
// And clear selection!
|
||||
if (pointerEvent.pointerPress != pointerEvent.pointerDrag)
|
||||
{
|
||||
ClearPointerSelection(pointerEvent);
|
||||
}
|
||||
|
||||
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent,
|
||||
ExecuteEvents.dragHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearPointerSelection(PointerEventData pointerEvent)
|
||||
{
|
||||
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent,
|
||||
ExecuteEvents.pointerUpHandler);
|
||||
|
||||
pointerEvent.eligibleForClick = false;
|
||||
pointerEvent.pointerPress = null;
|
||||
pointerEvent.rawPointerPress = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used in PointerInputModule's ProcessDrag implementation. Brought into this subclass with a protected
|
||||
/// signature (as opposed to the parent's private signature) to be used in this subclass's overridden ProcessDrag.
|
||||
/// </summary>
|
||||
protected static bool ShouldStartDrag(Vector2 pressPos, Vector2 currentPos, float threshold, bool useDragThreshold)
|
||||
{
|
||||
if (!useDragThreshold)
|
||||
return true;
|
||||
|
||||
return (pressPos - currentPos).sqrMagnitude >= threshold * threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2518c50cb3fc6a6458d4b743c2f69c7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// This component makes it possible to connect PointableCanvases in the
|
||||
/// inspector to Unity Events that are broadcast from PointableCanvasModule.
|
||||
/// Useful for hooking into uGUI navigation.
|
||||
/// </summary>
|
||||
public class PointableCanvasUnityEventWrapper : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Interface(typeof(IPointableCanvas))]
|
||||
private UnityEngine.Object _pointableCanvas;
|
||||
private IPointableCanvas PointableCanvas;
|
||||
|
||||
[SerializeField, Tooltip("Selection and hover events will not be fired while dragging.")]
|
||||
private bool _suppressWhileDragging = true;
|
||||
|
||||
[SerializeField, Tooltip("Raised when beginning hover of a uGUI selectable")]
|
||||
private UnityEvent _whenBeginHighlight;
|
||||
|
||||
[SerializeField, Tooltip("Raised when ending hover of a uGUI selectable")]
|
||||
private UnityEvent _whenEndHighlight;
|
||||
|
||||
[SerializeField, Tooltip("Raised when selecting a hovered uGUI selectable")]
|
||||
private UnityEvent _whenSelectedHovered;
|
||||
|
||||
[SerializeField, Tooltip("Raised when selecting with no uGUI selectable hovered")]
|
||||
private UnityEvent _whenSelectedEmpty;
|
||||
|
||||
[SerializeField, Tooltip("Raised when deselecting a hovered uGUI selectable")]
|
||||
private UnityEvent _whenUnselectedHovered;
|
||||
|
||||
[SerializeField, Tooltip("Raised when deselecting with no uGUI selectable hovered")]
|
||||
private UnityEvent _whenUnselectedEmpty;
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
private bool ShouldFireEvent(PointableCanvasEventArgs args)
|
||||
{
|
||||
if (args.Canvas != PointableCanvas.Canvas)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_suppressWhileDragging && args.Dragging)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void PointableCanvasModule_WhenSelectableHoverEnter(PointableCanvasEventArgs args)
|
||||
{
|
||||
if (ShouldFireEvent(args))
|
||||
{
|
||||
_whenBeginHighlight.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void PointableCanvasModule_WhenSelectableHoverExit(PointableCanvasEventArgs args)
|
||||
{
|
||||
if (ShouldFireEvent(args))
|
||||
{
|
||||
_whenEndHighlight.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void PointableCanvasModule_WhenSelectableSelected(PointableCanvasEventArgs args)
|
||||
{
|
||||
if (ShouldFireEvent(args))
|
||||
{
|
||||
if (args.Hovered == null)
|
||||
_whenSelectedEmpty.Invoke();
|
||||
else
|
||||
_whenSelectedHovered.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void PointableCanvasModule_WhenSelectableUnselected(PointableCanvasEventArgs args)
|
||||
{
|
||||
if (ShouldFireEvent(args))
|
||||
{
|
||||
if (args.Hovered == null)
|
||||
_whenUnselectedEmpty.Invoke();
|
||||
else
|
||||
_whenUnselectedHovered.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
PointableCanvas = _pointableCanvas as IPointableCanvas;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(PointableCanvas, nameof(PointableCanvas));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
PointableCanvasModule.WhenSelectableHovered += PointableCanvasModule_WhenSelectableHoverEnter;
|
||||
PointableCanvasModule.WhenSelectableUnhovered += PointableCanvasModule_WhenSelectableHoverExit;
|
||||
PointableCanvasModule.WhenSelected += PointableCanvasModule_WhenSelectableSelected;
|
||||
PointableCanvasModule.WhenUnselected += PointableCanvasModule_WhenSelectableUnselected;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
PointableCanvasModule.WhenSelectableHovered -= PointableCanvasModule_WhenSelectableHoverEnter;
|
||||
PointableCanvasModule.WhenSelectableUnhovered -= PointableCanvasModule_WhenSelectableHoverExit;
|
||||
PointableCanvasModule.WhenSelected -= PointableCanvasModule_WhenSelectableSelected;
|
||||
PointableCanvasModule.WhenUnselected -= PointableCanvasModule_WhenSelectableUnselected;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5626a0b1dc955a43be59ce7ea116678
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// This componenet makes it possible to connect IPointables in the
|
||||
/// inspector to Unity Events that are broadcast on IPointable events.
|
||||
/// </summary>
|
||||
public class PointableUnityEventWrapper : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Interface(typeof(IPointable))]
|
||||
private UnityEngine.Object _pointable;
|
||||
private IPointable Pointable;
|
||||
|
||||
private HashSet<int> _pointers;
|
||||
|
||||
[SerializeField]
|
||||
private UnityEvent<PointerEvent> _whenRelease;
|
||||
[SerializeField]
|
||||
private UnityEvent<PointerEvent> _whenHover;
|
||||
[SerializeField]
|
||||
private UnityEvent<PointerEvent> _whenUnhover;
|
||||
[SerializeField]
|
||||
private UnityEvent<PointerEvent> _whenSelect;
|
||||
[SerializeField]
|
||||
private UnityEvent<PointerEvent> _whenUnselect;
|
||||
[SerializeField]
|
||||
private UnityEvent<PointerEvent> _whenMove;
|
||||
[SerializeField]
|
||||
private UnityEvent<PointerEvent> _whenCancel;
|
||||
|
||||
public UnityEvent<PointerEvent> WhenRelease => _whenRelease;
|
||||
public UnityEvent<PointerEvent> WhenHover => _whenHover;
|
||||
public UnityEvent<PointerEvent> WhenUnhover => _whenUnhover;
|
||||
public UnityEvent<PointerEvent> WhenSelect => _whenSelect;
|
||||
public UnityEvent<PointerEvent> WhenUnselect => _whenUnselect;
|
||||
public UnityEvent<PointerEvent> WhenMove => _whenMove;
|
||||
public UnityEvent<PointerEvent> WhenCancel => _whenCancel;
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Pointable = _pointable as IPointable;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(Pointable, nameof(Pointable));
|
||||
_pointers = new HashSet<int>();
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Pointable.WhenPointerEventRaised += HandlePointerEventRaised;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Pointable.WhenPointerEventRaised -= HandlePointerEventRaised;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePointerEventRaised(PointerEvent evt)
|
||||
{
|
||||
switch (evt.Type)
|
||||
{
|
||||
case PointerEventType.Hover:
|
||||
_whenHover.Invoke(evt);
|
||||
_pointers.Add(evt.Identifier);
|
||||
break;
|
||||
case PointerEventType.Unhover:
|
||||
_whenUnhover.Invoke(evt);
|
||||
_pointers.Remove(evt.Identifier);
|
||||
break;
|
||||
case PointerEventType.Select:
|
||||
_whenSelect.Invoke(evt);
|
||||
break;
|
||||
case PointerEventType.Unselect:
|
||||
if (_pointers.Contains(evt.Identifier))
|
||||
{
|
||||
_whenRelease.Invoke(evt);
|
||||
}
|
||||
_whenUnselect.Invoke(evt);
|
||||
break;
|
||||
case PointerEventType.Move:
|
||||
_whenMove.Invoke(evt);
|
||||
break;
|
||||
case PointerEventType.Cancel:
|
||||
_whenCancel.Invoke(evt);
|
||||
_pointers.Remove(evt.Identifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllPointableUnityEventWrapper(IPointable pointable)
|
||||
{
|
||||
InjectPointable(pointable);
|
||||
}
|
||||
|
||||
public void InjectPointable(IPointable pointable)
|
||||
{
|
||||
_pointable = pointable as UnityEngine.Object;
|
||||
Pointable = pointable;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2f8f6e9e6f3e114b9bf9a57c2160615
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public class SelectorUnityEventWrapper : MonoBehaviour
|
||||
{
|
||||
[SerializeField, Interface(typeof(ISelector))]
|
||||
private UnityEngine.Object _selector;
|
||||
private ISelector Selector;
|
||||
|
||||
[SerializeField]
|
||||
private UnityEvent _whenSelected;
|
||||
|
||||
[SerializeField]
|
||||
private UnityEvent _whenUnselected;
|
||||
|
||||
public UnityEvent WhenSelected => _whenSelected;
|
||||
public UnityEvent WhenUnselected => _whenUnselected;
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Selector = _selector as ISelector;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(Selector, nameof(Selector));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Selector.WhenSelected += HandleSelected;
|
||||
Selector.WhenUnselected += HandleUnselected;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Selector.WhenSelected -= HandleSelected;
|
||||
Selector.WhenUnselected -= HandleUnselected;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSelected()
|
||||
{
|
||||
_whenSelected.Invoke();
|
||||
}
|
||||
|
||||
private void HandleUnselected()
|
||||
{
|
||||
_whenUnselected.Invoke();
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllSelectorUnityEventWrapper(ISelector selector)
|
||||
{
|
||||
InjectSelector(selector);
|
||||
}
|
||||
|
||||
public void InjectSelector(ISelector selector)
|
||||
{
|
||||
_selector = selector as UnityEngine.Object;
|
||||
Selector = selector;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de76f7169412b8f4896235a1585d8939
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
/// <summary>
|
||||
/// Override Toggle to clear state on drag while still bubbling events up through
|
||||
/// the hierarchy. Particularly useful for buttons inside of scroll views.
|
||||
/// </summary>
|
||||
public class ToggleDeselect : Toggle
|
||||
{
|
||||
[SerializeField]
|
||||
private bool _clearStateOnDrag = false;
|
||||
|
||||
public bool ClearStateOnDrag
|
||||
{
|
||||
get
|
||||
{
|
||||
return _clearStateOnDrag;
|
||||
}
|
||||
|
||||
set
|
||||
|
||||
{
|
||||
_clearStateOnDrag = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBeginDrag(PointerEventData pointerEventData)
|
||||
{
|
||||
if (!_clearStateOnDrag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
InstantClearState();
|
||||
DoStateTransition(SelectionState.Normal, true);
|
||||
ExecuteEvents.ExecuteHierarchy(
|
||||
transform.parent.gameObject,
|
||||
pointerEventData,
|
||||
ExecuteEvents.beginDragHandler
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af559e57b81a946479f0ad978a1a3763
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86ce14e80f612a041be6385c43fed586
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Oculus.Interaction.Surfaces;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.UnityCanvas
|
||||
{
|
||||
public class CanvasCylinder : CanvasMesh, ICurvedPlane, ICylinderClipper
|
||||
{
|
||||
[Serializable]
|
||||
public struct MeshGenerationSettings
|
||||
{
|
||||
[Delayed]
|
||||
public float VerticesPerDegree;
|
||||
|
||||
[Delayed]
|
||||
public int MaxHorizontalResolution;
|
||||
|
||||
[Delayed]
|
||||
public int MaxVerticalResolution;
|
||||
}
|
||||
|
||||
public const int MIN_RESOLUTION = 2;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The cylinder used to dictate the position and radius of the mesh.")]
|
||||
private Cylinder _cylinder;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Determines how the mesh is projected on the cylinder wall. " +
|
||||
"Vertical results in a left-to-right curvature, Horizontal results in a top-to-bottom curvature.")]
|
||||
private CylinderOrientation _orientation = CylinderOrientation.Vertical;
|
||||
|
||||
[SerializeField]
|
||||
private MeshGenerationSettings _meshGeneration = new MeshGenerationSettings()
|
||||
{
|
||||
VerticesPerDegree = 1.4f,
|
||||
MaxHorizontalResolution = 128,
|
||||
MaxVerticalResolution = 32
|
||||
};
|
||||
|
||||
public float Radius => _cylinder.Radius;
|
||||
public Cylinder Cylinder => _cylinder;
|
||||
public float ArcDegrees { get; private set; }
|
||||
public float Rotation { get; private set; }
|
||||
public float Bottom { get; private set; }
|
||||
public float Top { get; private set; }
|
||||
|
||||
private float CylinderRelativeScale => _cylinder.transform.lossyScale.x / transform.lossyScale.x;
|
||||
|
||||
public bool GetCylinderSegment(out CylinderSegment segment)
|
||||
{
|
||||
segment = new CylinderSegment(Rotation, ArcDegrees, Bottom, Top);
|
||||
return _started && isActiveAndEnabled;
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
this.BeginStart(ref _started, () => base.Start());
|
||||
this.AssertField(_cylinder, nameof(_cylinder));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
_meshGeneration.MaxHorizontalResolution = Mathf.Max(MIN_RESOLUTION,
|
||||
_meshGeneration.MaxHorizontalResolution);
|
||||
_meshGeneration.MaxVerticalResolution = Mathf.Max(MIN_RESOLUTION,
|
||||
_meshGeneration.MaxVerticalResolution);
|
||||
_meshGeneration.VerticesPerDegree = Mathf.Max(0, _meshGeneration.VerticesPerDegree);
|
||||
|
||||
if (Application.isPlaying && _started)
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
UpdateImposter();
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
protected override void UpdateImposter()
|
||||
{
|
||||
base.UpdateImposter();
|
||||
UpdateMeshPosition();
|
||||
UpdateCurvedPlane();
|
||||
}
|
||||
|
||||
protected override Vector3 MeshInverseTransform(Vector3 localPosition)
|
||||
{
|
||||
float angle = Mathf.Atan2(localPosition.x, localPosition.z + Radius);
|
||||
float x = angle * Radius;
|
||||
float y = localPosition.y;
|
||||
return new Vector3(x, y);
|
||||
}
|
||||
|
||||
protected override void GenerateMesh(out List<Vector3> verts,
|
||||
out List<int> tris,
|
||||
out List<Vector2> uvs)
|
||||
{
|
||||
verts = new List<Vector3>();
|
||||
tris = new List<int>();
|
||||
uvs = new List<Vector2>();
|
||||
|
||||
Vector2 worldSize = GetWorldSize();
|
||||
float scaledRadius = Radius * CylinderRelativeScale;
|
||||
|
||||
float xPos = worldSize.x * 0.5f;
|
||||
float xNeg = -xPos;
|
||||
float yPos = worldSize.y * 0.5f;
|
||||
float yNeg = -yPos;
|
||||
|
||||
Vector2Int GetClampedResolution(float arcMax, float axisMax)
|
||||
{
|
||||
int horizontalResolution = Mathf.Max(2,
|
||||
Mathf.RoundToInt(_meshGeneration.VerticesPerDegree *
|
||||
Mathf.Rad2Deg * arcMax / scaledRadius));
|
||||
int verticalResolution =
|
||||
Mathf.Max(2, Mathf.RoundToInt(horizontalResolution * axisMax / arcMax));
|
||||
|
||||
horizontalResolution = Mathf.Clamp(horizontalResolution, 2,
|
||||
_meshGeneration.MaxHorizontalResolution);
|
||||
verticalResolution = Mathf.Clamp(verticalResolution, 2,
|
||||
_meshGeneration.MaxVerticalResolution);
|
||||
|
||||
return new Vector2Int(horizontalResolution, verticalResolution);
|
||||
}
|
||||
|
||||
Vector3 GetCurvedPoint(float u, float v)
|
||||
{
|
||||
float x = Mathf.Lerp(xNeg, xPos, u);
|
||||
float y = Mathf.Lerp(yNeg, yPos, v);
|
||||
|
||||
float angle;
|
||||
Vector3 point;
|
||||
|
||||
switch (_orientation)
|
||||
{
|
||||
default:
|
||||
case CylinderOrientation.Vertical:
|
||||
angle = x / scaledRadius;
|
||||
point.x = Mathf.Sin(angle) * scaledRadius;
|
||||
point.y = y;
|
||||
point.z = Mathf.Cos(angle) * scaledRadius - scaledRadius;
|
||||
break;
|
||||
case CylinderOrientation.Horizontal:
|
||||
angle = y / scaledRadius;
|
||||
point.x = x;
|
||||
point.y = Mathf.Sin(angle) * scaledRadius;
|
||||
point.z = Mathf.Cos(angle) * scaledRadius - scaledRadius;
|
||||
break;
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
Vector2Int resolution;
|
||||
switch (_orientation)
|
||||
{
|
||||
default:
|
||||
case CylinderOrientation.Vertical:
|
||||
resolution = GetClampedResolution(xPos, yPos);
|
||||
break;
|
||||
case CylinderOrientation.Horizontal:
|
||||
resolution = GetClampedResolution(yPos, xPos);
|
||||
break;
|
||||
}
|
||||
|
||||
for (int y = 0; y < resolution.y; y++)
|
||||
{
|
||||
for (int x = 0; x < resolution.x; x++)
|
||||
{
|
||||
float u = x / (resolution.x - 1.0f);
|
||||
float v = y / (resolution.y - 1.0f);
|
||||
|
||||
verts.Add(GetCurvedPoint(u, v));
|
||||
uvs.Add(new Vector2(u, v));
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < resolution.y - 1; y++)
|
||||
{
|
||||
for (int x = 0; x < resolution.x - 1; x++)
|
||||
{
|
||||
int v00 = x + y * resolution.x;
|
||||
int v10 = v00 + 1;
|
||||
int v01 = v00 + resolution.x;
|
||||
int v11 = v00 + 1 + resolution.x;
|
||||
|
||||
tris.Add(v00);
|
||||
tris.Add(v11);
|
||||
tris.Add(v10);
|
||||
|
||||
tris.Add(v00);
|
||||
tris.Add(v01);
|
||||
tris.Add(v11);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMeshPosition()
|
||||
{
|
||||
Vector3 posInCylinder = _cylinder.transform.InverseTransformPoint(transform.position);
|
||||
|
||||
Vector3 localYOffset = new Vector3(0, posInCylinder.y, 0);
|
||||
Vector3 localCancelY = posInCylinder - localYOffset;
|
||||
|
||||
// If canvas position is on cylinder center axis, project forward.
|
||||
// Otherwise, project canvas onto cylinder wall from center axis.
|
||||
Vector3 projection = Mathf.Approximately(localCancelY.sqrMagnitude, 0f) ?
|
||||
Vector3.forward : localCancelY.normalized;
|
||||
|
||||
Vector3 localUp;
|
||||
switch (_orientation)
|
||||
{
|
||||
default:
|
||||
case CylinderOrientation.Vertical:
|
||||
localUp = Vector3.up;
|
||||
break;
|
||||
case CylinderOrientation.Horizontal:
|
||||
localUp = Vector3.right;
|
||||
break;
|
||||
}
|
||||
|
||||
transform.position = _cylinder.transform.TransformPoint((projection * _cylinder.Radius) + localYOffset);
|
||||
transform.rotation = _cylinder.transform.rotation * Quaternion.LookRotation(projection, localUp);
|
||||
|
||||
if (_meshCollider != null &&
|
||||
_meshCollider.transform != transform &&
|
||||
!transform.IsChildOf(_meshCollider.transform))
|
||||
{
|
||||
_meshCollider.transform.position = transform.position;
|
||||
_meshCollider.transform.rotation = transform.rotation;
|
||||
_meshCollider.transform.localScale *= transform.lossyScale.x / _meshCollider.transform.lossyScale.x;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 GetWorldSize()
|
||||
{
|
||||
Vector2Int resolution = _canvasRenderTexture.GetBaseResolutionToUse();
|
||||
float width = _canvasRenderTexture.PixelsToUnits(Mathf.RoundToInt(resolution.x));
|
||||
float height = _canvasRenderTexture.PixelsToUnits(Mathf.RoundToInt(resolution.y));
|
||||
return new Vector2(width, height) / transform.lossyScale;
|
||||
}
|
||||
|
||||
private void UpdateCurvedPlane()
|
||||
{
|
||||
// Get world size in cylinder space
|
||||
Vector2 cylinderSize = GetWorldSize() / CylinderRelativeScale;
|
||||
|
||||
float arcSize, axisSize;
|
||||
switch (_orientation)
|
||||
{
|
||||
default:
|
||||
case CylinderOrientation.Vertical:
|
||||
arcSize = cylinderSize.x;
|
||||
axisSize = cylinderSize.y;
|
||||
break;
|
||||
case CylinderOrientation.Horizontal:
|
||||
arcSize = cylinderSize.y;
|
||||
axisSize = cylinderSize.x;
|
||||
break;
|
||||
}
|
||||
|
||||
Vector3 posInCylinder = Cylinder.transform.InverseTransformPoint(transform.position);
|
||||
Rotation = Mathf.Atan2(posInCylinder.x, posInCylinder.z) * Mathf.Rad2Deg;
|
||||
ArcDegrees = (arcSize * 0.5f / Radius) * 2f * Mathf.Rad2Deg;
|
||||
Top = posInCylinder.y + (axisSize * 0.5f);
|
||||
Bottom = posInCylinder.y - (axisSize * 0.5f);
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllCanvasCylinder(CanvasRenderTexture canvasRenderTexture,
|
||||
MeshFilter meshFilter,
|
||||
Cylinder cylinder,
|
||||
CylinderOrientation orientation)
|
||||
{
|
||||
InjectAllCanvasMesh(canvasRenderTexture, meshFilter);
|
||||
InjectCylinder(cylinder);
|
||||
InjectOrientation(orientation);
|
||||
}
|
||||
|
||||
public void InjectCylinder(Cylinder cylinder)
|
||||
{
|
||||
_cylinder = cylinder;
|
||||
}
|
||||
|
||||
public void InjectOrientation(CylinderOrientation orientation)
|
||||
{
|
||||
_orientation = orientation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85ef10d17088b3c4cad9a5d13887b157
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace Oculus.Interaction.UnityCanvas
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public abstract class CanvasMesh : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Mesh construction will be driven by this texture.")]
|
||||
[SerializeField]
|
||||
protected CanvasRenderTexture _canvasRenderTexture;
|
||||
|
||||
[Tooltip("The mesh filter that will be driven.")]
|
||||
[SerializeField]
|
||||
protected MeshFilter _meshFilter;
|
||||
|
||||
[Tooltip("Optional mesh collider that will be driven.")]
|
||||
[SerializeField, Optional]
|
||||
protected MeshCollider _meshCollider = null;
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
protected abstract Vector3 MeshInverseTransform(Vector3 localPosition);
|
||||
|
||||
protected abstract void GenerateMesh(out List<Vector3> verts, out List<int> tris, out List<Vector2> uvs);
|
||||
|
||||
/// <summary>
|
||||
/// Transform a position in world space relative to the imposter to an associated position relative
|
||||
/// to the original canvas in world space.
|
||||
/// </summary>
|
||||
public Vector3 ImposterToCanvasTransformPoint(Vector3 worldPosition)
|
||||
{
|
||||
Vector3 localToImposter =
|
||||
_meshFilter.transform.InverseTransformPoint(worldPosition);
|
||||
Vector3 canvasLocalPosition = MeshInverseTransform(localToImposter) /
|
||||
_canvasRenderTexture.transform.localScale.x;
|
||||
Vector3 transformedWorldPosition = _canvasRenderTexture.transform.TransformPoint(canvasLocalPosition);
|
||||
return transformedWorldPosition;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(_meshFilter, nameof(_meshFilter));
|
||||
this.AssertField(_canvasRenderTexture, nameof(_canvasRenderTexture));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
UpdateImposter();
|
||||
|
||||
_canvasRenderTexture.OnUpdateRenderTexture += HandleUpdateRenderTexture;
|
||||
if (_canvasRenderTexture.Texture != null)
|
||||
{
|
||||
HandleUpdateRenderTexture(_canvasRenderTexture.Texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
_canvasRenderTexture.OnUpdateRenderTexture -= HandleUpdateRenderTexture;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void HandleUpdateRenderTexture(Texture texture)
|
||||
{
|
||||
UpdateImposter();
|
||||
}
|
||||
|
||||
protected virtual void UpdateImposter()
|
||||
{
|
||||
Profiler.BeginSample("InterfaceRenderer.UpdateImposter");
|
||||
try
|
||||
{
|
||||
GenerateMesh(out List<Vector3> verts, out List<int> tris, out List<Vector2> uvs);
|
||||
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.SetVertices(verts);
|
||||
mesh.SetUVs(0, uvs);
|
||||
mesh.SetTriangles(tris, 0);
|
||||
|
||||
mesh.RecalculateBounds();
|
||||
mesh.RecalculateNormals();
|
||||
|
||||
_meshFilter.mesh = mesh;
|
||||
if (_meshCollider != null)
|
||||
{
|
||||
_meshCollider.sharedMesh = _meshFilter.sharedMesh;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllCanvasMesh(CanvasRenderTexture canvasRenderTexture, MeshFilter meshFilter)
|
||||
{
|
||||
InjectCanvasRenderTexture(canvasRenderTexture);
|
||||
InjectMeshFilter(meshFilter);
|
||||
}
|
||||
|
||||
public void InjectCanvasRenderTexture(CanvasRenderTexture canvasRenderTexture)
|
||||
{
|
||||
_canvasRenderTexture = canvasRenderTexture;
|
||||
}
|
||||
|
||||
public void InjectMeshFilter(MeshFilter meshFilter)
|
||||
{
|
||||
_meshFilter = meshFilter;
|
||||
}
|
||||
|
||||
public void InjectOptionalMeshCollider(MeshCollider meshCollider)
|
||||
{
|
||||
_meshCollider = meshCollider;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e17f00312f4fea429367cb5b305689d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace Oculus.Interaction.UnityCanvas
|
||||
{
|
||||
public class CanvasMeshRenderer : MonoBehaviour
|
||||
{
|
||||
private static readonly int MainTexShaderID = Shader.PropertyToID("_MainTex");
|
||||
|
||||
[Tooltip("The canvas texture that will be rendered.")]
|
||||
[SerializeField]
|
||||
protected CanvasRenderTexture _canvasRenderTexture;
|
||||
|
||||
[Tooltip("The mesh renderer that will be driven.")]
|
||||
[SerializeField]
|
||||
protected MeshRenderer _meshRenderer;
|
||||
|
||||
[Tooltip("Determines the shader used for rendering. " +
|
||||
"See the documentation for details on these rendering modes.")]
|
||||
[SerializeField]
|
||||
protected int _renderingMode = (int)RenderingMode.AlphaCutout;
|
||||
|
||||
[Tooltip("Requires MSAA. Provides limited transparency useful for " +
|
||||
"anti-aliasing soft edges of UI elements.")]
|
||||
[SerializeField]
|
||||
private bool _useAlphaToMask = true;
|
||||
|
||||
[Tooltip("Select the alpha cutoff used for the cutout rendering.")]
|
||||
[Range(0, 1)]
|
||||
[SerializeField]
|
||||
private float _alphaCutoutThreshold = 0.5f;
|
||||
|
||||
private RenderingMode RenderingMode => (RenderingMode)_renderingMode;
|
||||
|
||||
protected virtual string GetShaderName()
|
||||
{
|
||||
switch (RenderingMode)
|
||||
{
|
||||
case RenderingMode.AlphaBlended:
|
||||
return "Hidden/Imposter_AlphaBlended";
|
||||
case RenderingMode.AlphaCutout:
|
||||
if (_useAlphaToMask)
|
||||
{
|
||||
return "Hidden/Imposter_AlphaToMask";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Hidden/Imposter_AlphaCutout";
|
||||
}
|
||||
default:
|
||||
case RenderingMode.Opaque:
|
||||
return "Hidden/Imposter_Opaque";
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SetAdditionalProperties(MaterialPropertyBlock block)
|
||||
{
|
||||
block.SetFloat("_Cutoff", GetAlphaCutoutThreshold());
|
||||
}
|
||||
|
||||
protected virtual float GetAlphaCutoutThreshold()
|
||||
{
|
||||
if (RenderingMode == RenderingMode.AlphaCutout &&
|
||||
!_useAlphaToMask)
|
||||
{
|
||||
return _alphaCutoutThreshold;
|
||||
}
|
||||
return 1f;
|
||||
}
|
||||
|
||||
protected Material _material;
|
||||
protected bool _started;
|
||||
|
||||
protected virtual void HandleUpdateRenderTexture(Texture texture)
|
||||
{
|
||||
_meshRenderer.material = _material;
|
||||
var block = new MaterialPropertyBlock();
|
||||
_meshRenderer.GetPropertyBlock(block);
|
||||
block.SetTexture(MainTexShaderID, texture);
|
||||
SetAdditionalProperties(block);
|
||||
_meshRenderer.SetPropertyBlock(block);
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(_meshRenderer, nameof(_meshRenderer));
|
||||
this.AssertField(_canvasRenderTexture, nameof(_canvasRenderTexture));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
Profiler.BeginSample("InterfaceRenderer.UpdateMaterial");
|
||||
try
|
||||
{
|
||||
_material = new Material(Shader.Find(GetShaderName()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
_canvasRenderTexture.OnUpdateRenderTexture += HandleUpdateRenderTexture;
|
||||
if (_canvasRenderTexture.Texture != null)
|
||||
{
|
||||
HandleUpdateRenderTexture(_canvasRenderTexture.Texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
if (_material != null)
|
||||
{
|
||||
Destroy(_material);
|
||||
_material = null;
|
||||
}
|
||||
_canvasRenderTexture.OnUpdateRenderTexture -= HandleUpdateRenderTexture;
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class Properties
|
||||
{
|
||||
public static readonly string RenderingMode = nameof(_renderingMode);
|
||||
public static readonly string UseAlphaToMask = nameof(_useAlphaToMask);
|
||||
public static readonly string AlphaCutoutThreshold = nameof(_alphaCutoutThreshold);
|
||||
}
|
||||
|
||||
#region Inject
|
||||
public void InjectAllCanvasMeshRenderer(CanvasRenderTexture canvasRenderTexture,
|
||||
MeshRenderer meshRenderer)
|
||||
{
|
||||
InjectCanvasRenderTexture(canvasRenderTexture);
|
||||
InjectMeshRenderer(meshRenderer);
|
||||
}
|
||||
|
||||
public void InjectCanvasRenderTexture(CanvasRenderTexture canvasRenderTexture)
|
||||
{
|
||||
_canvasRenderTexture = canvasRenderTexture;
|
||||
}
|
||||
|
||||
public void InjectMeshRenderer(MeshRenderer meshRenderer)
|
||||
{
|
||||
_meshRenderer = meshRenderer;
|
||||
}
|
||||
|
||||
public void InjectOptionalRenderingMode(RenderingMode renderingMode)
|
||||
{
|
||||
_renderingMode = (int)renderingMode;
|
||||
}
|
||||
|
||||
public void InjectOptionalAlphaCutoutThreshold(float alphaCutoutThreshold)
|
||||
{
|
||||
_alphaCutoutThreshold = alphaCutoutThreshold;
|
||||
}
|
||||
|
||||
public void InjectOptionalUseAlphaToMask(bool useAlphaToMask)
|
||||
{
|
||||
_useAlphaToMask = useAlphaToMask;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49d3bbd18ed10f44ba1e0b24dbfee754
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.UnityCanvas
|
||||
{
|
||||
public class CanvasRect : CanvasMesh
|
||||
{
|
||||
protected override Vector3 MeshInverseTransform(Vector3 localPosition)
|
||||
{
|
||||
return localPosition;
|
||||
}
|
||||
|
||||
protected override void GenerateMesh(out List<Vector3> verts,
|
||||
out List<int> tris,
|
||||
out List<Vector2> uvs)
|
||||
{
|
||||
verts = new List<Vector3>();
|
||||
tris = new List<int>();
|
||||
uvs = new List<Vector2>();
|
||||
|
||||
var resolution = _canvasRenderTexture.GetBaseResolutionToUse();
|
||||
Vector2 worldSize = new Vector2(
|
||||
_canvasRenderTexture.PixelsToUnits(Mathf.RoundToInt(resolution.x)),
|
||||
_canvasRenderTexture.PixelsToUnits(Mathf.RoundToInt(resolution.y))
|
||||
) / transform.lossyScale;
|
||||
|
||||
float xPos = worldSize.x * 0.5f;
|
||||
float xNeg = -xPos;
|
||||
|
||||
float yPos = worldSize.y * 0.5f;
|
||||
float yNeg = -yPos;
|
||||
|
||||
verts.Add(new Vector3(xNeg, yNeg, 0));
|
||||
verts.Add(new Vector3(xNeg, yPos, 0));
|
||||
verts.Add(new Vector3(xPos, yPos, 0));
|
||||
verts.Add(new Vector3(xPos, yNeg, 0));
|
||||
|
||||
tris.Add(0);
|
||||
tris.Add(1);
|
||||
tris.Add(2);
|
||||
|
||||
tris.Add(0);
|
||||
tris.Add(2);
|
||||
tris.Add(3);
|
||||
|
||||
uvs.Add(new Vector2(0, 0));
|
||||
uvs.Add(new Vector2(0, 1));
|
||||
uvs.Add(new Vector2(1, 1));
|
||||
uvs.Add(new Vector2(1, 0));
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllCanvasRect(CanvasRenderTexture canvasRenderTexture, MeshFilter meshFilter)
|
||||
{
|
||||
InjectAllCanvasMesh(canvasRenderTexture, meshFilter);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2025423b67857c349bf8cf435762b5cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Oculus.Interaction.UnityCanvas
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public class CanvasRenderTexture : MonoBehaviour
|
||||
{
|
||||
private class TransformChangeListener : MonoBehaviour
|
||||
{
|
||||
public event Action WhenRectTransformDimensionsChanged = delegate { };
|
||||
|
||||
private void OnRectTransformDimensionsChange()
|
||||
{
|
||||
WhenRectTransformDimensionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public enum DriveMode
|
||||
{
|
||||
Auto,
|
||||
Manual
|
||||
}
|
||||
|
||||
public const int DEFAULT_UI_LAYERMASK = 1 << 5; //Hardcoded as the UI layer in Unity.
|
||||
|
||||
private static readonly Vector2Int DEFAULT_TEXTURE_RES = new Vector2Int(128, 128);
|
||||
|
||||
[Tooltip("The Unity canvas that will be rendered.")]
|
||||
[SerializeField]
|
||||
private Canvas _canvas;
|
||||
|
||||
[Tooltip("If you need extra resolution, you can use this as a whole-integer multiplier " +
|
||||
"of the final resolution used to render the texture.")]
|
||||
[Range(1, 3)]
|
||||
[Delayed]
|
||||
[SerializeField]
|
||||
private int _renderScale = 1;
|
||||
|
||||
[Tooltip("If set to auto, texture dimensions will take the size of the attached " +
|
||||
"RectTransform into consideration, in addition to the configured pixel-per-unit ratio.")]
|
||||
[SerializeField]
|
||||
private DriveMode _dimensionsDriveMode = DriveMode.Auto;
|
||||
|
||||
[Tooltip("The exact pixel resolution of the texture used for interface rendering.")]
|
||||
[Delayed]
|
||||
[SerializeField]
|
||||
private Vector2Int _resolution = DEFAULT_TEXTURE_RES;
|
||||
|
||||
[Tooltip("Whether or not mip-maps should be auto-generated for the texture. " +
|
||||
"Can help aliasing if the texture can be " +
|
||||
"viewed from many difference distances.")]
|
||||
[SerializeField]
|
||||
private bool _generateMipMaps = false;
|
||||
|
||||
[Tooltip("Pixels per unit ratio used to drive the texture dimensions.")]
|
||||
[SerializeField]
|
||||
private int _pixelsPerUnit = 100;
|
||||
|
||||
[Header("Rendering Settings")]
|
||||
[Tooltip("The layers to render when the rendering texture is created. " +
|
||||
"All child renderers should be part of this mask.")]
|
||||
[SerializeField]
|
||||
private LayerMask _renderingLayers = DEFAULT_UI_LAYERMASK;
|
||||
|
||||
public LayerMask RenderingLayers => _renderingLayers;
|
||||
|
||||
public Action<Texture> OnUpdateRenderTexture = delegate { };
|
||||
|
||||
public int RenderScale
|
||||
{
|
||||
get
|
||||
{
|
||||
return _renderScale;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_renderScale < 1 || _renderScale > 3)
|
||||
{
|
||||
throw new ArgumentException($"Render scale must be between 1 and 3, but was {value}");
|
||||
}
|
||||
|
||||
if (_renderScale == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_renderScale = value;
|
||||
|
||||
if (isActiveAndEnabled && Application.isPlaying)
|
||||
{
|
||||
UpdateCamera();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Camera OverlayCamera => _camera;
|
||||
|
||||
public Texture Texture => _tex;
|
||||
|
||||
private TransformChangeListener _listener;
|
||||
private RenderTexture _tex;
|
||||
private Camera _camera;
|
||||
|
||||
protected bool _started = false;
|
||||
|
||||
public Vector2Int CalcAutoResolution()
|
||||
{
|
||||
if (_canvas == null)
|
||||
{
|
||||
return DEFAULT_TEXTURE_RES;
|
||||
}
|
||||
|
||||
var rectTransform = _canvas.GetComponent<RectTransform>();
|
||||
if (rectTransform == null)
|
||||
{
|
||||
return DEFAULT_TEXTURE_RES;
|
||||
}
|
||||
|
||||
Vector2 size = rectTransform.sizeDelta;
|
||||
size.x *= rectTransform.lossyScale.x;
|
||||
size.y *= rectTransform.lossyScale.y;
|
||||
|
||||
int x = Mathf.RoundToInt(UnitsToPixels(size.x));
|
||||
int y = Mathf.RoundToInt(UnitsToPixels(size.y));
|
||||
return new Vector2Int(Mathf.Max(x, 1), Mathf.Max(y, 1));
|
||||
}
|
||||
|
||||
public Vector2Int GetBaseResolutionToUse()
|
||||
{
|
||||
if (_dimensionsDriveMode == DriveMode.Auto)
|
||||
{
|
||||
return CalcAutoResolution();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _resolution;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2Int GetScaledResolutionToUse()
|
||||
{
|
||||
Vector2 resolution = GetBaseResolutionToUse();
|
||||
return Vector2Int.RoundToInt(resolution * _renderScale);
|
||||
}
|
||||
|
||||
public float PixelsToUnits(float pixels)
|
||||
{
|
||||
return (1f / _pixelsPerUnit) * pixels;
|
||||
}
|
||||
|
||||
public float UnitsToPixels(float units)
|
||||
{
|
||||
return _pixelsPerUnit * units;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected void OnValidate()
|
||||
{
|
||||
if (Application.isPlaying && _started)
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
UpdateCamera();
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
protected void Start()
|
||||
{
|
||||
this.BeginStart(ref _started);
|
||||
this.AssertField(_canvas, nameof(_canvas));
|
||||
this.EndStart(ref _started);
|
||||
}
|
||||
|
||||
protected void OnEnable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
if (_listener == null)
|
||||
{
|
||||
_listener = _canvas.gameObject.AddComponent<TransformChangeListener>();
|
||||
}
|
||||
_listener.WhenRectTransformDimensionsChanged += WhenCanvasRectTransformDimensionsChanged;
|
||||
UpdateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenCanvasRectTransformDimensionsChanged()
|
||||
{
|
||||
UpdateCamera();
|
||||
}
|
||||
|
||||
protected void OnDisable()
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
if (_camera?.gameObject != null)
|
||||
{
|
||||
Destroy(_camera.gameObject);
|
||||
}
|
||||
if (_tex != null)
|
||||
{
|
||||
DestroyImmediate(_tex);
|
||||
}
|
||||
if (_listener != null)
|
||||
{
|
||||
_listener.WhenRectTransformDimensionsChanged -= WhenCanvasRectTransformDimensionsChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateCamera()
|
||||
{
|
||||
if (!Application.isPlaying || !_started)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Profiler.BeginSample("InterfaceRenderer.UpdateCamera");
|
||||
try
|
||||
{
|
||||
if (_camera == null)
|
||||
{
|
||||
GameObject cameraObj = CreateChildObject("__Camera");
|
||||
_camera = cameraObj.AddComponent<Camera>();
|
||||
|
||||
_camera.orthographic = true;
|
||||
_camera.nearClipPlane = -0.1f;
|
||||
_camera.farClipPlane = 0.1f;
|
||||
_camera.backgroundColor = new Color(0, 0, 0, 0);
|
||||
_camera.clearFlags = CameraClearFlags.SolidColor;
|
||||
}
|
||||
|
||||
UpdateRenderTexture();
|
||||
UpdateOrthoSize();
|
||||
UpdateCameraCullingMask();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateRenderTexture()
|
||||
{
|
||||
Profiler.BeginSample("InterfaceRenderer.UpdateRenderTexture");
|
||||
try
|
||||
{
|
||||
Vector2Int resolutionToUse = GetScaledResolutionToUse();
|
||||
|
||||
if (_tex == null ||
|
||||
_tex.width != resolutionToUse.x ||
|
||||
_tex.height != resolutionToUse.y ||
|
||||
_tex.autoGenerateMips != _generateMipMaps)
|
||||
{
|
||||
if (_tex != null)
|
||||
{
|
||||
_camera.targetTexture = null;
|
||||
DestroyImmediate(_tex);
|
||||
}
|
||||
|
||||
_tex = new RenderTexture(resolutionToUse.x, resolutionToUse.y, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
|
||||
_tex.filterMode = FilterMode.Bilinear;
|
||||
_tex.autoGenerateMips = _generateMipMaps;
|
||||
_camera.targetTexture = _tex;
|
||||
|
||||
OnUpdateRenderTexture(_tex);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOrthoSize()
|
||||
{
|
||||
if (_camera != null)
|
||||
{
|
||||
_camera.orthographicSize = PixelsToUnits(GetBaseResolutionToUse().y) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCameraCullingMask()
|
||||
{
|
||||
if (_camera != null)
|
||||
{
|
||||
_camera.cullingMask = _renderingLayers.value;
|
||||
}
|
||||
}
|
||||
|
||||
protected GameObject CreateChildObject(string name)
|
||||
{
|
||||
GameObject obj = new GameObject(name);
|
||||
|
||||
obj.transform.SetParent(_canvas.transform);
|
||||
obj.transform.localPosition = Vector3.zero;
|
||||
obj.transform.localRotation = Quaternion.identity;
|
||||
obj.transform.localScale = Vector3.one;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static class Properties
|
||||
{
|
||||
public static readonly string DimensionDriveMode = nameof(_dimensionsDriveMode);
|
||||
public static readonly string Resolution = nameof(_resolution);
|
||||
public static readonly string RenderScale = nameof(_renderScale);
|
||||
public static readonly string PixelsPerUnit = nameof(_pixelsPerUnit);
|
||||
public static readonly string RenderLayers = nameof(_renderingLayers);
|
||||
public static readonly string GenerateMipMaps = nameof(_generateMipMaps);
|
||||
public static readonly string Canvas = nameof(_canvas);
|
||||
}
|
||||
|
||||
#region Inject
|
||||
|
||||
public void InjectAllCanvasRenderTexture(Canvas canvas,
|
||||
int pixelsPerUnit,
|
||||
int renderScale,
|
||||
LayerMask renderingLayers,
|
||||
bool generateMipMaps)
|
||||
{
|
||||
InjectCanvas(canvas);
|
||||
InjectPixelsPerUnit(pixelsPerUnit);
|
||||
InjectRenderScale(renderScale);
|
||||
InjectRenderingLayers(renderingLayers);
|
||||
InjectGenerateMipMaps(generateMipMaps);
|
||||
}
|
||||
|
||||
public void InjectCanvas(Canvas canvas)
|
||||
{
|
||||
_canvas = canvas;
|
||||
}
|
||||
|
||||
public void InjectPixelsPerUnit(int pixelsPerUnit)
|
||||
{
|
||||
_pixelsPerUnit = pixelsPerUnit;
|
||||
}
|
||||
|
||||
public void InjectRenderScale(int renderScale)
|
||||
{
|
||||
_renderScale = renderScale;
|
||||
}
|
||||
|
||||
public void InjectRenderingLayers(LayerMask renderingLayers)
|
||||
{
|
||||
_renderingLayers = renderingLayers;
|
||||
}
|
||||
|
||||
public void InjectGenerateMipMaps(bool generateMipMaps)
|
||||
{
|
||||
_generateMipMaps = generateMipMaps;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7ecff74e52843a41ab3a441ac81379e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- _defaultUIMaterial: {fileID: 2100000, guid: bc9f80a2ae0e0a24d870176e48ab1b93,
|
||||
type: 2}
|
||||
- _imposterMaterial: {fileID: 2100000, guid: 5c093e4058df12042a75bcb967ca1554, type: 2}
|
||||
- _depthQuadMaterial: {fileID: 2100000, guid: c7cda63c3ebb50847950fc3a925d784f,
|
||||
type: 2}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.UnityCanvas
|
||||
{
|
||||
public enum RenderingMode
|
||||
{
|
||||
[InspectorName("Alpha-Blended")]
|
||||
AlphaBlended = 0,
|
||||
[InspectorName("Alpha-Cutout")]
|
||||
AlphaCutout,
|
||||
[InspectorName("Opaque")]
|
||||
Opaque,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e5de7ab187e8dc47a0f654ad1e1107a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Oculus.Interaction.UnityCanvas
|
||||
{
|
||||
/// <summary>
|
||||
/// Dropdowns menus in Unity are automatically set to sorting order 30000, which
|
||||
/// does not play nicely with world space UIs.
|
||||
/// Meant to be used in conjunction with an EventTrigger on a given Dropdown, this component
|
||||
/// can be used to set a different sorting order on this and any child canvas.
|
||||
/// </summary>
|
||||
public class UpdateCanvasSortingOrder : MonoBehaviour
|
||||
{
|
||||
public void SetCanvasSortingOrder(int sortingOrder)
|
||||
{
|
||||
Canvas[] canvases = transform.parent.gameObject.GetComponentsInChildren<Canvas>();
|
||||
if (canvases == null) return;
|
||||
foreach (Canvas canvas in canvases)
|
||||
{
|
||||
canvas.sortingOrder = sortingOrder;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f40996cdf2361b8478af26d6e2630d42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
43
Assets/Oculus/Interaction/Runtime/Scripts/Unity/UnityInfo.cs
Normal file
43
Assets/Oculus/Interaction/Runtime/Scripts/Unity/UnityInfo.cs
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace Oculus.Interaction
|
||||
{
|
||||
public static class UnityInfo
|
||||
{
|
||||
public static bool IsEditor()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool Version_2020_3_Or_Newer()
|
||||
{
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59709ed6d58f88541a420b6ef3165513
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user