NxrStereoController.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright 2016 Nibiru. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #if NIBIRU_VR
  15. #define NXR_HACK
  16. #endif
  17. using UnityEngine;
  18. using System.Collections;
  19. using System.Linq;
  20. using UnityEngine.Rendering;
  21. namespace Nxr.Internal
  22. {
  23. [RequireComponent(typeof(Camera))]
  24. [AddComponentMenu("NXR/Controller/NxrStereoController")]
  25. public class NxrStereoController : MonoBehaviour
  26. {
  27. // Flags whether we rendered in stereo for this frame.
  28. private bool renderedStereo = false;
  29. // Cache for speed, except in editor (don't want to get out of sync with the scene).
  30. private NxrEye[] eyes;
  31. private NxrHead mHead;
  32. /// Returns an array of stereo cameras that are controlled by this instance of
  33. /// the script.
  34. /// @note This array is cached for speedier access. Call
  35. /// InvalidateEyes if it is ever necessary to reset the cache.
  36. public NxrEye[] Eyes
  37. {
  38. get
  39. {
  40. if (eyes == null)
  41. {
  42. eyes = GetComponentsInChildren<NxrEye>(true)
  43. .Where(eye => eye.Controller == this)
  44. .ToArray();
  45. }
  46. if (eyes == null)
  47. {
  48. NxrEye[] NxrEyess = FindObjectsOfType<NxrEye>();
  49. if (NxrEyess.Length > 0)
  50. {
  51. eyes = NxrEyess;
  52. }
  53. }
  54. return eyes;
  55. }
  56. }
  57. /// Returns the nearest NxrHead that affects our eyes.
  58. /// @note Cached for speed. Call InvalidateEyes to clear the cache.
  59. public NxrHead Head
  60. {
  61. get
  62. {
  63. #if UNITY_EDITOR
  64. NxrHead mHead = null; // Local variable rather than member, so as not to cache.
  65. #endif
  66. if (mHead == null)
  67. {
  68. mHead = FindObjectOfType<NxrHead>();
  69. }
  70. return mHead;
  71. }
  72. }
  73. public Camera cam { get; private set; }
  74. void Awake()
  75. {
  76. NxrViewer.Create();
  77. cam = GetComponent<Camera>();
  78. AddStereoRig();
  79. NxrOverrideSettings.OnProfileChangedEvent += OnProfileChanged;
  80. }
  81. void OnProfileChanged()
  82. {
  83. Debug.Log("OnProfileChanged");
  84. NxrEye[] eyes = NxrViewer.Instance.eyes;
  85. foreach (NxrEye eye in eyes)
  86. {
  87. if (eye != null)
  88. {
  89. eye.UpdateCameraProjection();
  90. }
  91. }
  92. }
  93. /// Helper routine for creation of a stereo rig. Used by the
  94. /// custom editor for this class, or to build the rig at runtime.
  95. public void AddStereoRig()
  96. {
  97. Debug.Log("AddStereoRig.CreateEye");
  98. CreateEye(NxrViewer.Eye.Left);
  99. CreateEye(NxrViewer.Eye.Right);
  100. if (Head == null)
  101. {
  102. gameObject.AddComponent<NxrHead>();
  103. // Don't track position for dynamically added Head components, or else
  104. // you may unexpectedly find your camera pinned to the origin.
  105. }
  106. Head.SetTrackPosition(NxrViewer.Instance.TrackerPosition);
  107. }
  108. // Helper routine for creation of a stereo eye.
  109. private void CreateEye(NxrViewer.Eye eye)
  110. {
  111. string nm = name + (eye == NxrViewer.Eye.Left ? " Left" : " Right");
  112. NxrEye[] eyes = GetComponentsInChildren<NxrEye>();
  113. NxrEye mNxrEye = null;
  114. if (eyes != null && eyes.Length > 0)
  115. {
  116. foreach(NxrEye mEye in eyes)
  117. {
  118. if(mEye.eye == eye)
  119. {
  120. mNxrEye = mEye;
  121. break;
  122. }
  123. }
  124. }
  125. // 创建新的
  126. if (mNxrEye == null)
  127. {
  128. GameObject go = new GameObject(nm);
  129. go.transform.SetParent(transform, false);
  130. go.AddComponent<Camera>().enabled = false;
  131. mNxrEye = go.AddComponent<NxrEye>();
  132. }
  133. if(NxrOverrideSettings.OnEyeCameraInitEvent != null) NxrOverrideSettings.OnEyeCameraInitEvent(eye, mNxrEye.gameObject);
  134. mNxrEye.Controller = this;
  135. mNxrEye.eye = eye;
  136. mNxrEye.CopyCameraAndMakeSideBySide(this);
  137. mNxrEye.OnPostRenderListener += OnPostRenderListener;
  138. mNxrEye.OnPreRenderListener += OnPreRenderListener;
  139. NxrViewer.Instance.eyes[eye == NxrViewer.Eye.Left ? 0 : 1] = mNxrEye;
  140. Debug.Log("CreateEye:" + nm + (eyes == null));
  141. }
  142. void OnPreRenderListener(int cacheTextureId, NxrViewer.Eye eyeType)
  143. {
  144. if (NxrGlobal.isVR9Platform) return;
  145. if (NxrViewer.USE_DTR && NxrGlobal.supportDtr)
  146. {
  147. // 左右眼绘制开始
  148. NibiruRenderEventType eventType = eyeType == NxrViewer.Eye.Left ? NibiruRenderEventType.LeftEyeBeginFrame : NibiruRenderEventType.RightEyeBeginFrame;
  149. NxrPluginEvent.IssueWithData(eventType, cacheTextureId);
  150. if (NxrGlobal.DEBUG_LOG_ENABLED) Debug.Log("OnPreRender.eye[" + eyeType + "]");
  151. }
  152. }
  153. void OnPostRenderListener(int cacheTextureId, NxrViewer.Eye eyeType)
  154. {
  155. if (NxrGlobal.isVR9Platform)
  156. {
  157. if (eyeType == NxrViewer.Eye.Right && Application.isMobilePlatform)
  158. {
  159. if (NxrGlobal.DEBUG_LOG_ENABLED) Debug.Log("OnPostRenderListener.PrepareFrame.Right");
  160. NxrPluginEvent.Issue(NibiruRenderEventType.PrepareFrame);
  161. }
  162. return;
  163. }
  164. if (NxrViewer.USE_DTR && NxrGlobal.supportDtr)
  165. {
  166. // 左右眼绘制结束
  167. NibiruRenderEventType eventType = eyeType == NxrViewer.Eye.Left ? NibiruRenderEventType.LeftEyeEndFrame : NibiruRenderEventType.RightEyeEndFrame;
  168. // 左右眼绘制结束事件
  169. // int eyeTextureId = (int)cam.targetTexture.GetNativeTexturePtr();
  170. NxrPluginEvent.IssueWithData(eventType, cacheTextureId);
  171. if(NxrGlobal.DEBUG_LOG_ENABLED) Debug.Log("OnPostRender.eye[" + eyeType + "]");
  172. }
  173. if (NxrViewer.USE_DTR && eyeType == NxrViewer.Eye.Right)
  174. {
  175. // NxrViewer.Instance.EnterXRMode();
  176. }
  177. }
  178. void OnEnable()
  179. {
  180. #if UNITY_2019_1_OR_NEWER
  181. if (UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset != null)
  182. {
  183. RenderPipelineManager.beginCameraRendering += CameraPreRender;
  184. }
  185. #endif
  186. StartCoroutine("EndOfFrame");
  187. }
  188. void OnDisable()
  189. {
  190. #if UNITY_2019_1_OR_NEWER
  191. if (UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset != null)
  192. {
  193. RenderPipelineManager.beginCameraRendering -= CameraPreRender;
  194. }
  195. #endif
  196. StopCoroutine("EndOfFrame");
  197. }
  198. #if UNITY_2019_1_OR_NEWER
  199. public void CameraPreRender(ScriptableRenderContext context, Camera mcam)
  200. {
  201. if (mcam.gameObject == cam.gameObject)
  202. {
  203. OnPreCull();
  204. }
  205. }
  206. #endif
  207. void OnPreCull()
  208. {
  209. if (NxrViewer.Instance.SplitScreenModeEnabled)
  210. {
  211. // Activate the eyes under our control.
  212. NxrEye[] eyes = Eyes;
  213. for (int i = 0, n = eyes.Length; i < n; i++)
  214. {
  215. if(!eyes[i].cam.enabled) eyes[i].cam.enabled = true;
  216. }
  217. // Turn off the mono camera so it doesn't waste time rendering. Remember to reenable.
  218. // @note The mono camera is left on from beginning of frame till now in order that other game
  219. // logic (e.g. referring to Camera.main) continues to work as expected. center camera is only used for raycasting
  220. #if NXR_HACK
  221. #warning Due to a Unity bug, a worldspace canvas in a camera that renders to a RenderTexture allocates infinite memory. Remove the hack ASAP as the fix gets released.
  222. BlackOutMonoCamera();
  223. #else
  224. if (!NxrViewer.Instance.IsWinPlatform) cam.enabled = false;
  225. #endif
  226. renderedStereo = true;
  227. }
  228. }
  229. public void EndOfFrameCore()
  230. {
  231. // If *we* turned off the mono cam, turn it back on for next frame.
  232. if (renderedStereo)
  233. {
  234. #if NXR_HACK
  235. RestoreMonoCamera();
  236. #else
  237. cam.enabled = true;
  238. #endif
  239. renderedStereo = false;
  240. }
  241. }
  242. IEnumerator EndOfFrame()
  243. {
  244. while (true)
  245. {
  246. yield return new WaitForEndOfFrame();
  247. EndOfFrameCore();
  248. }
  249. }
  250. #if NXR_HACK
  251. private CameraClearFlags m_MonoCameraClearFlags;
  252. private Color m_MonoCameraBackgroundColor;
  253. private int m_MonoCameraCullingMask;
  254. private void BlackOutMonoCamera()
  255. {
  256. if (NxrViewer.Instance.IsWinPlatform) return;
  257. m_MonoCameraClearFlags = cam.clearFlags;
  258. m_MonoCameraBackgroundColor = cam.backgroundColor;
  259. m_MonoCameraCullingMask = cam.cullingMask;
  260. cam.clearFlags = CameraClearFlags.SolidColor;
  261. cam.backgroundColor = Color.black;
  262. cam.cullingMask = 0;
  263. }
  264. private void RestoreMonoCamera()
  265. {
  266. if (NxrViewer.Instance.IsWinPlatform) return;
  267. cam.clearFlags = m_MonoCameraClearFlags;
  268. cam.backgroundColor = m_MonoCameraBackgroundColor;
  269. cam.cullingMask = m_MonoCameraCullingMask;
  270. }
  271. #endif
  272. }
  273. }