/****************************************************************************
* Copyright 2019 Nreal Techonology Limited. All rights reserved.
*
* This file is part of NRSDK.
*
* https://www.nreal.ai/
*
*****************************************************************************/
namespace NRKernal
{
using AOT;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// A nr virtual displayer.
[HelpURL("https://developer.nreal.ai/develop/unity/customize-phone-controller")]
[ScriptOrder(NativeConstants.NRVIRTUALDISPLAY_ORDER)]
public class NRVirtualDisplayer : SingletonBehaviour, ISystemButtonStateReceiver
{
///
/// Event queue for all listeners interested in onDisplayScreenChanged events.
///
public static event Action OnDisplayScreenChangedEvent;
/// State of the system button.
[NonSerialized] public static SystemInputState SystemButtonState = new SystemInputState();
/// The camera.
[SerializeField] Camera m_UICamera;
/// The virtual controller.
[SerializeField] MultiScreenController m_VirtualController;
private ISystemButtonStateProvider m_ISystemButtonStateProvider;
/// The screen resolution.
private Vector2 m_ScreenResolution;
/// Not supported on runtime.
private const float ScaleFactor = 1f;
/// The virtual display FPS.
public static int VirtualDisplayFPS = 24;
/// The current time.
private float m_CurrentTime;
public enum DisplayMode
{
None,
Unity,
AndroidNative
}
private DisplayMode m_DisplayMode = DisplayMode.AndroidNative;
public static DisplayMode displayMode
{
get
{
if (Instance != null)
{
return Instance.m_DisplayMode;
}
else
{
return DisplayMode.AndroidNative;
}
}
}
#if UNITY_EDITOR
private static RenderTexture m_ControllerScreen;
#endif
private NRDisplaySubsystem m_Subsystem;
public NRDisplaySubsystem Subsystem
{
get
{
if (m_Subsystem == null)
{
string str_match = NRDisplaySubsystemDescriptor.Name;
List descriptors = new List();
NRSubsystemManager.GetSubsystemDescriptors(descriptors);
foreach (var des in descriptors)
{
if (des.id.Equals(str_match))
{
m_Subsystem = des.Create();
}
}
}
return m_Subsystem;
}
}
/// Event queue for all listeners interested in OnMultiDisplayInited events.
public event Action OnMultiDisplayInitialized;
/// True to run in background.
public static bool RunInBackground = true;
/// True if is initialize, false if not.
private bool m_IsInitialized = false;
private void OnApplicationPause(bool pause)
{
if (!m_IsInitialized || isDirty)
{
return;
}
if (RunInBackground)
{
if (pause)
{
this.Pause();
}
else
{
this.Resume();
}
}
else
{
NRDevice.ForceKill();
}
}
/// Starts this object.
void Start()
{
if (isDirty) return;
this.CreateDisplay();
}
private void CreateDisplay()
{
if (m_IsInitialized) return;
NRDebugger.Info("[NRVirtualDisplayer] Create display.");
try
{
NRDevice.Instance.Init();
}
catch (Exception e)
{
NRDebugger.Error("[NRVirtualDisplayer] NRDevice init error:" + e.ToString());
throw;
}
Subsystem.ListenMainScrResolutionChanged(OnDisplayResolutionChanged);
Subsystem.Start();
#if !UNITY_EDITOR && UNITY_ANDROID
if (m_VirtualController == null)
{
var phoneScreenReplayceTool = FindObjectOfType();
if (phoneScreenReplayceTool == null)
{
NRDebugger.Info("[NRVirtualDisplayer] Use default phone sceen provider.");
this.BindVirtualDisplayProvider(new NRDefaultPhoneScreenProvider());
}
else
{
NRDebugger.Info("[NRVirtualDisplayer] Use replayced phone sceen provider.");
this.BindVirtualDisplayProvider(phoneScreenReplayceTool.CreatePhoneScreenProvider());
}
}
else
{
this.BindVirtualDisplayProvider(null);
}
#else
this.BindVirtualDisplayProvider(null);
#endif
NRSessionManager.Instance.VirtualDisplayer = this;
NRDebugger.Info("[NRVirtualDisplayer] Initialize");
m_IsInitialized = true;
OnMultiDisplayInitialized?.Invoke();
}
private void Pause()
{
if (!m_IsInitialized)
{
return;
}
Subsystem.Pause();
}
private void Resume()
{
if (!m_IsInitialized)
{
return;
}
Subsystem.Resume();
}
private void Update()
{
if (!m_IsInitialized) return;
#if UNITY_EDITOR
UpdateEmulator();
if (m_VirtualController)
{
m_VirtualController.gameObject.SetActive(NRInput.EmulateVirtualDisplayInEditor);
}
#endif
if (m_DisplayMode == DisplayMode.Unity)
{
if (Subsystem.running)
{
m_CurrentTime += Time.deltaTime;
}
if (Subsystem.running && m_CurrentTime > (1f / VirtualDisplayFPS))
{
m_CurrentTime = 0;
m_UICamera?.Render();
}
}
}
/// Destories this object.
public void Stop()
{
Subsystem.Stop();
#if UNITY_EDITOR
m_ControllerScreen?.Release();
m_ControllerScreen = null;
#endif
}
///
/// Base OnDestroy method that destroys the Singleton's unique instance. Called by Unity when
/// destroying a MonoBehaviour. Scripts that extend Singleton should be sure to call
/// base.OnDestroy() to ensure the underlying static Instance reference is properly cleaned up.
new void OnDestroy()
{
if (isDirty) return;
base.OnDestroy();
this.Stop();
}
/// If m_VirtualController is null, use android native
/// fragment as the virtual controller provider.
private void BindVirtualDisplayProvider(NRPhoneScreenProviderBase provider)
{
if (m_ISystemButtonStateProvider != null)
{
return;
}
bool targetOSX = false;
#if UNITY_EDITOR
targetOSX = EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.StandaloneOSX;
#endif
if (Application.platform == RuntimePlatform.OSXPlayer || targetOSX)
{
// No virtual controller on Mac
m_DisplayMode = DisplayMode.None;
m_ISystemButtonStateProvider = null;
if (m_VirtualController != null)
{
m_VirtualController.gameObject.SetActive(false);
}
}
else if (provider != null && m_VirtualController == null && Application.platform == RuntimePlatform.Android)
{
m_DisplayMode = DisplayMode.AndroidNative;
m_ISystemButtonStateProvider = provider;
NRDebugger.Info("[NRVirtualDisplayer] Bind android native controller");
}
else
{
m_DisplayMode = DisplayMode.Unity;
transform.position = Vector3.one * 99999f;
m_ISystemButtonStateProvider = m_VirtualController;
NRDebugger.Info("[NRVirtualDisplayer] Bind unity virtual controller");
#if UNITY_EDITOR
var canvas = transform.GetComponentInChildren