1
0
mirror of synced 2024-12-18 17:25:54 +01:00
WACVR/Assets/Plugin/LIV/Scripts/LIV.cs

522 lines
18 KiB
C#
Raw Normal View History

using UnityEngine;
using UnityEngine.Serialization;
using System.Collections;
using System;
#if UNITY_2017_2_OR_NEWER
using UnityEngine.XR;
#endif
namespace LIV.SDK.Unity
{
[System.Flags]
public enum INVALIDATION_FLAGS : uint
{
NONE = 0,
HMD_CAMERA = 1,
STAGE = 2,
MR_CAMERA_PREFAB = 4,
EXCLUDE_BEHAVIOURS = 8
}
/// <summary>
/// The LIV SDK provides a spectator view of your application.
/// </summary>
/// <remarks>
/// <para>It contextualizes what the user feels & experiences by capturing their body directly inside your world!</para>
/// <para>Thanks to our software, creators can film inside your app and have full control over the camera.</para>
/// <para>With the power of out-of-engine compositing, a creator can express themselves freely without limits;</para>
/// <para>as a real person or an avatar!</para>
/// </remarks>
/// <example>
/// <code>
/// public class StartFromScriptExample : MonoBehaviour
/// {
/// [SerializeField] Camera _hmdCamera;
/// [SerializeField] Transform _stage;
/// [SerializeField] Transform _stageTransform;
/// [SerializeField] Camera _mrCameraPrefab;
/// LIV.SDK.Unity.LIV _liv;
/// private void OnEnable()
/// {
/// _liv = gameObject.AddComponent<LIV.SDK.Unity.LIV>();
/// _liv.HMDCamera = _hmdCamera;
/// _liv.stage = _stage;
/// _liv.stageTransform = _stageTransform;
/// _liv.MRCameraPrefab = _mrCameraPrefab;
/// }
/// private void OnDisable()
/// {
/// if (_liv == null) return;
/// Destroy(_liv);
/// _liv = null;
/// }
/// }
/// </code>
/// </example>
[HelpURL("https://liv.tv/sdk-unity-docs")]
[AddComponentMenu("LIV/LIV")]
public class LIV : MonoBehaviour
{
/// <summary>
/// triggered when the LIV SDK is activated by the LIV App and enabled by the game.
/// </summary>
public System.Action onActivate = null;
/// <summary>
/// triggered before the Mixed Reality camera is about to render.
/// </summary>
public System.Action<SDKRender> onPreRender = null;
/// <summary>
/// triggered before the LIV SDK starts rendering background image.
/// </summary>
public System.Action<SDKRender> onPreRenderBackground = null;
/// <summary>
/// triggered after the LIV SDK starts rendering background image.
/// </summary>
public System.Action<SDKRender> onPostRenderBackground = null;
/// <summary>
/// triggered before the LIV SDK starts rendering the foreground image.
/// </summary>
public System.Action<SDKRender> onPreRenderForeground = null;
/// <summary>
/// triggered after the LIV SDK starts rendering the foreground image.
/// </summary>
public System.Action<SDKRender> onPostRenderForeground = null;
/// <summary>
/// triggered after the Mixed Reality camera has finished rendering.
/// </summary>
public System.Action<SDKRender> onPostRender = null;
/// <summary>
/// triggered when the LIV SDK is deactivated by the LIV App or disabled by the game.
/// </summary>
public System.Action onDeactivate = null;
[Tooltip("This is the topmost transform of your VR rig.")]
[FormerlySerializedAs("TrackedSpaceOrigin")]
[SerializeField] Transform _stage = null;
/// <summary>
/// This is the topmost transform of your VR rig.
/// </summary>
/// <remarks>
/// <para>When implementing VR locomotion(teleporting, joystick, etc),</para>
/// <para>this is the GameObject that you should move around your scene.</para>
/// <para>It represents the centre of the users playspace.</para>
/// </remarks>
public Transform stage {
get {
return _stage == null ? transform.parent : _stage;
}
set {
if (value == null)
{
Debug.LogWarning("LIV: Stage cannot be null!");
}
if (_stage != value)
{
_stageCandidate = value;
_invalidate = (INVALIDATION_FLAGS)SDKUtils.SetFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.STAGE, true);
}
}
}
[Obsolete("Use stage instead")]
public Transform trackedSpaceOrigin {
get {
return stage;
}
set {
stage = value;
}
}
public Matrix4x4 stageLocalToWorldMatrix {
get {
return (stage != null) ? stage.localToWorldMatrix : Matrix4x4.identity;
}
}
public Matrix4x4 stageWorldToLocalMatrix {
get {
return (stage != null) ? stage.worldToLocalMatrix : Matrix4x4.identity;
}
}
[Tooltip("This transform is an additional wrapper to the users playspace.")]
[FormerlySerializedAs("StageTransform")]
[SerializeField] Transform _stageTransform = null;
/// <summary>
/// This transform is an additional wrapper to the users playspace.
/// </summary>
/// <remarks>
/// <para>It allows for user-controlled transformations for special camera effects & transitions.</para>
/// <para>If a creator is using a static camera, this transformation can give the illusion of camera movement.</para>
/// </remarks>
public Transform stageTransform {
get {
return _stageTransform;
}
set {
_stageTransform = value;
}
}
[Tooltip("This is the camera responsible for rendering the users HMD.")]
[FormerlySerializedAs("HMDCamera")]
[SerializeField] Camera _HMDCamera = null;
/// <summary>
/// This is the camera responsible for rendering the users HMD.
/// </summary>
/// <remarks>
/// <para>The LIV SDK, by default clones this object to match your applications rendering setup.</para>
/// <para>You can use your own camera prefab should you want to!</para>
/// </remarks>
public Camera HMDCamera {
get {
return _HMDCamera;
}
set {
if (value == null)
{
Debug.LogWarning("LIV: HMD Camera cannot be null!");
}
if (_HMDCamera != value)
{
_HMDCameraCandidate = value;
_invalidate = (INVALIDATION_FLAGS)SDKUtils.SetFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.HMD_CAMERA, true);
}
}
}
[Tooltip("Camera prefab for customized rendering.")]
[FormerlySerializedAs("MRCameraPrefab")]
[SerializeField] Camera _MRCameraPrefab = null;
/// <summary>
/// Camera prefab for customized rendering.
/// </summary>
/// <remarks>
/// <para>By default, LIV uses the HMD camera as a reference for the Mixed Reality camera.</para>
/// <para>It is cloned and set up as a Mixed Reality camera.This approach works for most apps.</para>
/// <para>However, some games can experience issues because of custom MonoBehaviours attached to this camera.</para>
/// <para>You can use a custom camera prefab for those cases.</para>
/// </remarks>
public Camera MRCameraPrefab {
get {
return _MRCameraPrefab;
}
set {
if (_MRCameraPrefab != value)
{
_MRCameraPrefabCandidate = value;
_invalidate = (INVALIDATION_FLAGS)SDKUtils.SetFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.MR_CAMERA_PREFAB, true);
}
}
}
[Tooltip("This option disables all standard Unity assets for the Mixed Reality rendering.")]
[FormerlySerializedAs("DisableStandardAssets")]
[SerializeField] bool _disableStandardAssets = false;
/// <summary>
/// This option disables all standard Unity assets for the Mixed Reality rendering.
/// </summary>
/// <remarks>
/// <para>Unitys standard assets can interfere with the alpha channel that LIV needs to composite MR correctly.</para>
/// </remarks>
public bool disableStandardAssets {
get {
return _disableStandardAssets;
}
set {
_disableStandardAssets = value;
}
}
[Tooltip("The layer mask defines exactly which object layers should be rendered in MR.")]
[FormerlySerializedAs("SpectatorLayerMask")]
[SerializeField] LayerMask _spectatorLayerMask = ~0;
/// <summary>
/// The layer mask defines exactly which object layers should be rendered in MR.
/// </summary>
/// <remarks>
/// <para>You should use this to hide any in-game avatar that youre using.</para>
/// <para>LIV is meant to include the users body for you!</para>
/// <para>Certain HMD-based effects should be disabled here too.</para>
/// <para>Also, this can be used to render special effects or additional UI only to the MR camera.</para>
/// <para>Useful for showing the players health, or current score!</para>
/// </remarks>
public LayerMask spectatorLayerMask {
get {
return _spectatorLayerMask;
}
set {
_spectatorLayerMask = value;
}
}
[Tooltip("This is for removing unwanted scripts from the cloned MR camera.")]
[FormerlySerializedAs("ExcludeBehaviours")]
[SerializeField]
string[] _excludeBehaviours = new string[] {
"AudioListener",
"Collider",
"SteamVR_Camera",
"SteamVR_Fade",
"SteamVR_ExternalCamera"
};
/// <summary>
/// This is for removing unwanted scripts from the cloned MR camera.
/// </summary>
/// <remarks>
/// <para>By default, we remove the AudioListener, Colliders and SteamVR scripts, as these are not necessary for rendering MR!</para>
/// <para>The excluded string must match the name of the MonoBehaviour.</para>
/// </remarks>
public string[] excludeBehaviours {
get {
return _excludeBehaviours;
}
set {
if (_excludeBehaviours != value)
{
_excludeBehavioursCandidate = value;
_invalidate = (INVALIDATION_FLAGS)SDKUtils.SetFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.EXCLUDE_BEHAVIOURS, true);
}
}
}
/// <summary>
/// Recovers corrupted alpha channel when using post-effects.
/// </summary>
///
[Tooltip("Recovers corrupted alpha channel when using post-effects.")]
[FormerlySerializedAs("FixPostEffectsAlpha")]
[SerializeField]
private bool _fixPostEffectsAlpha = false;
public bool fixPostEffectsAlpha {
get {
return _fixPostEffectsAlpha;
}
set {
_fixPostEffectsAlpha = value;
}
}
/// <summary>
/// Is the curret LIV SDK setup valid.
/// </summary>
public bool isValid {
get {
if (_invalidate != INVALIDATION_FLAGS.NONE) return false;
if (_HMDCamera == null)
{
Debug.LogError("LIV: HMD Camera is a required parameter!");
return false;
}
if (_stage == null)
{
Debug.LogWarning("LIV: Tracked space origin should be assigned!");
}
if (_spectatorLayerMask == 0)
{
Debug.LogWarning("LIV: The spectator layer mask is set to not show anything. Is this correct?");
}
return true;
}
}
bool _isActive = false;
/// <summary>
/// Is the LIV SDK currently active.
/// </summary>
public bool isActive {
get {
return _isActive;
}
}
private bool _isReady {
get {
return isValid && _enabled && SDKBridge.IsActive;
}
}
private SDKRender _render = null;
/// <summary>
/// Script responsible for the MR rendering.
/// </summary>
public SDKRender render { get { return _render; } }
private bool _wasReady = false;
private INVALIDATION_FLAGS _invalidate = INVALIDATION_FLAGS.NONE;
private Transform _stageCandidate = null;
private Camera _HMDCameraCandidate = null;
private Camera _MRCameraPrefabCandidate = null;
private string[] _excludeBehavioursCandidate = null;
private bool _enabled = false;
private Coroutine _waitForEndOfFrameCoroutine;
void OnEnable()
{
_enabled = true;
UpdateSDKReady();
}
void Update()
{
UpdateSDKReady();
Invalidate();
}
void OnDisable()
{
_enabled = false;
UpdateSDKReady();
}
IEnumerator WaitForUnityEndOfFrame()
{
while (Application.isPlaying && enabled)
{
yield return new WaitForEndOfFrame();
if (isActive)
{
_render.Render();
}
}
}
void UpdateSDKReady()
{
bool ready = _isReady;
if (ready != _wasReady)
{
OnSDKReadyChanged(ready);
_wasReady = ready;
}
}
void OnSDKReadyChanged(bool value)
{
if (value)
{
OnSDKActivate();
}
else
{
OnSDKDeactivate();
}
}
void OnSDKActivate()
{
Debug.Log("LIV: Compositor connected, setting up Mixed Reality!");
SubmitSDKOutput();
CreateAssets();
StartRenderCoroutine();
_isActive = true;
if (onActivate != null) onActivate.Invoke();
}
void OnSDKDeactivate()
{
Debug.Log("LIV: Compositor disconnected, cleaning up Mixed Reality.");
if (onDeactivate != null) onDeactivate.Invoke();
StopRenderCoroutine();
DestroyAssets();
_isActive = false;
}
void CreateAssets()
{
DestroyAssets();
_render = new SDKRender(this);
}
void DestroyAssets()
{
if (_render != null)
{
_render.Dispose();
_render = null;
}
}
void StartRenderCoroutine()
{
StopRenderCoroutine();
_waitForEndOfFrameCoroutine = StartCoroutine(WaitForUnityEndOfFrame());
}
void StopRenderCoroutine()
{
if (_waitForEndOfFrameCoroutine != null)
{
StopCoroutine(_waitForEndOfFrameCoroutine);
_waitForEndOfFrameCoroutine = null;
}
}
void SubmitSDKOutput()
{
SDKApplicationOutput output = SDKApplicationOutput.empty;
output.supportedFeatures = FEATURES.BACKGROUND_RENDER |
FEATURES.FOREGROUND_RENDER |
FEATURES.OVERRIDE_POST_PROCESSING |
FEATURES.FIX_FOREGROUND_ALPHA;
output.sdkID = SDKConstants.SDK_ID;
output.sdkVersion = SDKConstants.SDK_VERSION;
output.engineName = SDKConstants.ENGINE_NAME;
output.engineVersion = Application.unityVersion;
output.applicationName = Application.productName;
output.applicationVersion = Application.version;
output.graphicsAPI = SystemInfo.graphicsDeviceType.ToString();
#if UNITY_2017_2_OR_NEWER
output.xrDeviceName = XRSettings.loadedDeviceName;
#endif
SDKBridge.SubmitApplicationOutput(output);
}
void Invalidate()
{
if (SDKUtils.ContainsFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.STAGE))
{
_stage = _stageCandidate;
_stageCandidate = null;
_invalidate = (INVALIDATION_FLAGS)SDKUtils.SetFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.STAGE, false);
}
if (SDKUtils.ContainsFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.HMD_CAMERA))
{
_HMDCamera = _HMDCameraCandidate;
_HMDCameraCandidate = null;
_invalidate = (INVALIDATION_FLAGS)SDKUtils.SetFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.HMD_CAMERA, false);
}
if (SDKUtils.ContainsFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.MR_CAMERA_PREFAB))
{
_MRCameraPrefab = _MRCameraPrefabCandidate;
_MRCameraPrefabCandidate = null;
_invalidate = (INVALIDATION_FLAGS)SDKUtils.SetFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.MR_CAMERA_PREFAB, false);
}
if (SDKUtils.ContainsFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.EXCLUDE_BEHAVIOURS))
{
_excludeBehaviours = _excludeBehavioursCandidate;
_excludeBehavioursCandidate = null;
_invalidate = (INVALIDATION_FLAGS)SDKUtils.SetFlag((uint)_invalidate, (uint)INVALIDATION_FLAGS.EXCLUDE_BEHAVIOURS, false);
}
}
}
}