OVROverlay.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962
  1. /************************************************************************************
  2. Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
  3. Licensed under the Oculus Utilities SDK License Version 1.31 (the "License"); you may not use
  4. the Utilities SDK except in compliance with the License, which is provided at the time of installation
  5. or download, or which otherwise accompanies this software in either electronic or hard copy form.
  6. You may obtain a copy of the License at
  7. https://developer.oculus.com/licenses/utilities-1.31
  8. Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
  9. under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
  10. ANY KIND, either express or implied. See the License for the specific language governing
  11. permissions and limitations under the License.
  12. ************************************************************************************/
  13. using UnityEngine;
  14. using System;
  15. using System.Collections;
  16. using System.Runtime.InteropServices;
  17. #if UNITY_2017_2_OR_NEWER
  18. using Settings = UnityEngine.XR.XRSettings;
  19. #else
  20. using Settings = UnityEngine.VR.VRSettings;
  21. #endif
  22. /// <summary>
  23. /// Add OVROverlay script to an object with an optional mesh primitive
  24. /// rendered as a TimeWarp overlay instead by drawing it into the eye buffer.
  25. /// This will take full advantage of the display resolution and avoid double
  26. /// resampling of the texture.
  27. ///
  28. /// We support 3 types of Overlay shapes right now
  29. /// 1. Quad : This is most common overlay type , you render a quad in Timewarp space.
  30. /// 2. Cylinder: [Mobile Only][Experimental], Display overlay as partial surface of a cylinder
  31. /// * The cylinder's center will be your game object's center
  32. /// * We encoded the cylinder's parameters in transform.scale,
  33. /// **[scale.z] is the radius of the cylinder
  34. /// **[scale.y] is the height of the cylinder
  35. /// **[scale.x] is the length of the arc of cylinder
  36. /// * Limitations
  37. /// **Only the half of the cylinder can be displayed, which means the arc angle has to be smaller than 180 degree, [scale.x] / [scale.z] <= PI
  38. /// **Your camera has to be inside of the inscribed sphere of the cylinder, the overlay will be faded out automatically when the camera is close to the inscribed sphere's surface.
  39. /// **Translation only works correctly with vrDriver 1.04 or above
  40. /// 3. Cubemap: Display overlay as a cube map
  41. /// 4. OffcenterCubemap: [Mobile Only] Display overlay as a cube map with a texture coordinate offset
  42. /// * The actually sampling will looks like [color = texture(cubeLayerSampler, normalize(direction) + offset)] instead of [color = texture( cubeLayerSampler, direction )]
  43. /// * The extra center offset can be feed from transform.position
  44. /// * Note: if transform.position's magnitude is greater than 1, which will cause some cube map pixel always invisible
  45. /// Which is usually not what people wanted, we don't kill the ability for developer to do so here, but will warn out.
  46. /// 5. Equirect: Display overlay as a 360-degree equirectangular skybox.
  47. /// </summary>
  48. public class OVROverlay : MonoBehaviour
  49. {
  50. #region Interface
  51. /// <summary>
  52. /// Determines the on-screen appearance of a layer.
  53. /// </summary>
  54. public enum OverlayShape
  55. {
  56. Quad = OVRPlugin.OverlayShape.Quad,
  57. Cylinder = OVRPlugin.OverlayShape.Cylinder,
  58. Cubemap = OVRPlugin.OverlayShape.Cubemap,
  59. OffcenterCubemap = OVRPlugin.OverlayShape.OffcenterCubemap,
  60. Equirect = OVRPlugin.OverlayShape.Equirect,
  61. }
  62. /// <summary>
  63. /// Whether the layer appears behind or infront of other content in the scene.
  64. /// </summary>
  65. public enum OverlayType
  66. {
  67. None,
  68. Underlay,
  69. Overlay,
  70. };
  71. /// <summary>
  72. /// Specify overlay's type
  73. /// </summary>
  74. [Tooltip("Specify overlay's type")]
  75. public OverlayType currentOverlayType = OverlayType.Overlay;
  76. /// <summary>
  77. /// If true, the texture's content is copied to the compositor each frame.
  78. /// </summary>
  79. [Tooltip("If true, the texture's content is copied to the compositor each frame.")]
  80. public bool isDynamic = false;
  81. /// <summary>
  82. /// If true, the layer would be used to present protected content (e.g. HDCP). The flag is effective only on PC.
  83. /// </summary>
  84. [Tooltip("If true, the layer would be used to present protected content (e.g. HDCP). The flag is effective only on PC.")]
  85. public bool isProtectedContent = false;
  86. //Source and dest rects
  87. public Rect srcRectLeft = new Rect();
  88. public Rect srcRectRight = new Rect();
  89. public Rect destRectLeft = new Rect();
  90. public Rect destRectRight = new Rect();
  91. private OVRPlugin.TextureRectMatrixf textureRectMatrix = OVRPlugin.TextureRectMatrixf.zero;
  92. public bool overrideTextureRectMatrix = false;
  93. public bool overridePerLayerColorScaleAndOffset = false;
  94. public Vector4 colorScale = Vector4.one;
  95. public Vector4 colorOffset = Vector4.zero;
  96. //Warning: Developers should only use this supersample setting if they absolutely have the budget and need for it. It is extremely expensive, and will not be relevant for most developers.
  97. public bool useExpensiveSuperSample = false;
  98. //Property that can hide overlays when required. Should be false when present, true when hidden.
  99. public bool hidden = false;
  100. /// <summary>
  101. /// If true, the layer will be created as an external surface. externalSurfaceObject contains the Surface object. It's effective only on Android.
  102. /// </summary>
  103. [Tooltip("If true, the layer will be created as an external surface. externalSurfaceObject contains the Surface object. It's effective only on Android.")]
  104. public bool isExternalSurface = false;
  105. /// <summary>
  106. /// The width which will be used to create the external surface. It's effective only on Android.
  107. /// </summary>
  108. [Tooltip("The width which will be used to create the external surface. It's effective only on Android.")]
  109. public int externalSurfaceWidth = 0;
  110. /// <summary>
  111. /// The height which will be used to create the external surface. It's effective only on Android.
  112. /// </summary>
  113. [Tooltip("The height which will be used to create the external surface. It's effective only on Android.")]
  114. public int externalSurfaceHeight = 0;
  115. /// <summary>
  116. /// The compositionDepth defines the order of the OVROverlays in composition. The overlay/underlay with smaller compositionDepth would be composited in the front of the overlay/underlay with larger compositionDepth.
  117. /// </summary>
  118. [Tooltip("The compositionDepth defines the order of the OVROverlays in composition. The overlay/underlay with smaller compositionDepth would be composited in the front of the overlay/underlay with larger compositionDepth.")]
  119. public int compositionDepth = 0;
  120. /// <summary>
  121. /// The noDepthBufferTesting will stop layer's depth buffer compositing even if the engine has "Depth buffer sharing" enabled on Rift.
  122. /// </summary>
  123. [Tooltip("The noDepthBufferTesting will stop layer's depth buffer compositing even if the engine has \"Shared Depth Buffer\" enabled")]
  124. public bool noDepthBufferTesting = false;
  125. /// <summary>
  126. /// Specify overlay's shape
  127. /// </summary>
  128. [Tooltip("Specify overlay's shape")]
  129. public OverlayShape currentOverlayShape = OverlayShape.Quad;
  130. private OverlayShape prevOverlayShape = OverlayShape.Quad;
  131. /// <summary>
  132. /// The left- and right-eye Textures to show in the layer.
  133. /// \note If you need to change the texture on a per-frame basis, please use OverrideOverlayTextureInfo(..) to avoid caching issues.
  134. /// </summary>
  135. [Tooltip("The left- and right-eye Textures to show in the layer.")]
  136. public Texture[] textures = new Texture[] { null, null };
  137. protected IntPtr[] texturePtrs = new IntPtr[] { IntPtr.Zero, IntPtr.Zero };
  138. /// <summary>
  139. /// The Surface object (Android only).
  140. /// </summary>
  141. public System.IntPtr externalSurfaceObject;
  142. public delegate void ExternalSurfaceObjectCreated();
  143. /// <summary>
  144. /// Will be triggered after externalSurfaceTextueObject get created.
  145. /// </summary>
  146. public ExternalSurfaceObjectCreated externalSurfaceObjectCreated;
  147. /// <summary>
  148. /// Use this function to set texture and texNativePtr when app is running
  149. /// GetNativeTexturePtr is a slow behavior, the value should be pre-cached
  150. /// </summary>
  151. #if UNITY_2017_2_OR_NEWER
  152. public void OverrideOverlayTextureInfo(Texture srcTexture, IntPtr nativePtr, UnityEngine.XR.XRNode node)
  153. #else
  154. public void OverrideOverlayTextureInfo(Texture srcTexture, IntPtr nativePtr, UnityEngine.VR.VRNode node)
  155. #endif
  156. {
  157. #if UNITY_2017_2_OR_NEWER
  158. int index = (node == UnityEngine.XR.XRNode.RightEye) ? 1 : 0;
  159. #else
  160. int index = (node == UnityEngine.VR.VRNode.RightEye) ? 1 : 0;
  161. #endif
  162. if (textures.Length <= index)
  163. return;
  164. textures[index] = srcTexture;
  165. texturePtrs[index] = nativePtr;
  166. isOverridePending = true;
  167. }
  168. protected bool isOverridePending;
  169. internal const int maxInstances = 15;
  170. public static OVROverlay[] instances = new OVROverlay[maxInstances];
  171. #endregion
  172. private static Material tex2DMaterial;
  173. private static Material cubeMaterial;
  174. private OVRPlugin.LayerLayout layout {
  175. get {
  176. #if UNITY_ANDROID && !UNITY_EDITOR
  177. if (textures.Length == 2 && textures[1] != null)
  178. return OVRPlugin.LayerLayout.Stereo;
  179. #endif
  180. return OVRPlugin.LayerLayout.Mono;
  181. }
  182. }
  183. private struct LayerTexture {
  184. public Texture appTexture;
  185. public IntPtr appTexturePtr;
  186. public Texture[] swapChain;
  187. public IntPtr[] swapChainPtr;
  188. };
  189. private LayerTexture[] layerTextures;
  190. private OVRPlugin.LayerDesc layerDesc;
  191. private int stageCount = -1;
  192. private int layerIndex = -1; // Controls the composition order based on wake-up time.
  193. private int layerId = 0; // The layer's internal handle in the compositor.
  194. private GCHandle layerIdHandle;
  195. private IntPtr layerIdPtr = IntPtr.Zero;
  196. private int frameIndex = 0;
  197. private int prevFrameIndex = -1;
  198. private Renderer rend;
  199. private int texturesPerStage { get { return (layout == OVRPlugin.LayerLayout.Stereo) ? 2 : 1; } }
  200. private bool CreateLayer(int mipLevels, int sampleCount, OVRPlugin.EyeTextureFormat etFormat, int flags, OVRPlugin.Sizei size, OVRPlugin.OverlayShape shape)
  201. {
  202. if (!layerIdHandle.IsAllocated || layerIdPtr == IntPtr.Zero)
  203. {
  204. layerIdHandle = GCHandle.Alloc(layerId, GCHandleType.Pinned);
  205. layerIdPtr = layerIdHandle.AddrOfPinnedObject();
  206. }
  207. if (layerIndex == -1)
  208. {
  209. for (int i = 0; i < maxInstances; ++i)
  210. {
  211. if (instances[i] == null || instances[i] == this)
  212. {
  213. layerIndex = i;
  214. instances[i] = this;
  215. break;
  216. }
  217. }
  218. }
  219. bool needsSetup = (
  220. isOverridePending ||
  221. layerDesc.MipLevels != mipLevels ||
  222. layerDesc.SampleCount != sampleCount ||
  223. layerDesc.Format != etFormat ||
  224. layerDesc.Layout != layout ||
  225. layerDesc.LayerFlags != flags ||
  226. !layerDesc.TextureSize.Equals(size) ||
  227. layerDesc.Shape != shape);
  228. if (!needsSetup)
  229. return false;
  230. OVRPlugin.LayerDesc desc = OVRPlugin.CalculateLayerDesc(shape, layout, size, mipLevels, sampleCount, etFormat, flags);
  231. OVRPlugin.EnqueueSetupLayer(desc, compositionDepth, layerIdPtr);
  232. layerId = (int)layerIdHandle.Target;
  233. if (layerId > 0)
  234. {
  235. layerDesc = desc;
  236. if (isExternalSurface)
  237. {
  238. stageCount = 1;
  239. }
  240. else
  241. {
  242. stageCount = OVRPlugin.GetLayerTextureStageCount(layerId);
  243. }
  244. }
  245. isOverridePending = false;
  246. return true;
  247. }
  248. private bool CreateLayerTextures(bool useMipmaps, OVRPlugin.Sizei size, bool isHdr)
  249. {
  250. if (isExternalSurface)
  251. {
  252. if (externalSurfaceObject == System.IntPtr.Zero)
  253. {
  254. externalSurfaceObject = OVRPlugin.GetLayerAndroidSurfaceObject(layerId);
  255. if (externalSurfaceObject != System.IntPtr.Zero)
  256. {
  257. Debug.LogFormat("GetLayerAndroidSurfaceObject returns {0}", externalSurfaceObject);
  258. if (externalSurfaceObjectCreated != null)
  259. {
  260. externalSurfaceObjectCreated();
  261. }
  262. }
  263. }
  264. return false;
  265. }
  266. bool needsCopy = false;
  267. if (stageCount <= 0)
  268. return false;
  269. // For newer SDKs, blit directly to the surface that will be used in compositing.
  270. if (layerTextures == null)
  271. layerTextures = new LayerTexture[texturesPerStage];
  272. for (int eyeId = 0; eyeId < texturesPerStage; ++eyeId)
  273. {
  274. if (layerTextures[eyeId].swapChain == null)
  275. layerTextures[eyeId].swapChain = new Texture[stageCount];
  276. if (layerTextures[eyeId].swapChainPtr == null)
  277. layerTextures[eyeId].swapChainPtr = new IntPtr[stageCount];
  278. for (int stage = 0; stage < stageCount; ++stage)
  279. {
  280. Texture sc = layerTextures[eyeId].swapChain[stage];
  281. IntPtr scPtr = layerTextures[eyeId].swapChainPtr[stage];
  282. if (sc != null && scPtr != IntPtr.Zero && size.w == sc.width && size.h == sc.height)
  283. continue;
  284. if (scPtr == IntPtr.Zero)
  285. scPtr = OVRPlugin.GetLayerTexture(layerId, stage, (OVRPlugin.Eye)eyeId);
  286. if (scPtr == IntPtr.Zero)
  287. continue;
  288. var txFormat = (isHdr) ? TextureFormat.RGBAHalf : TextureFormat.RGBA32;
  289. if (currentOverlayShape != OverlayShape.Cubemap && currentOverlayShape != OverlayShape.OffcenterCubemap)
  290. sc = Texture2D.CreateExternalTexture(size.w, size.h, txFormat, useMipmaps, true, scPtr);
  291. #if UNITY_2017_1_OR_NEWER
  292. else
  293. sc = Cubemap.CreateExternalTexture(size.w, txFormat, useMipmaps, scPtr);
  294. #endif
  295. layerTextures[eyeId].swapChain[stage] = sc;
  296. layerTextures[eyeId].swapChainPtr[stage] = scPtr;
  297. needsCopy = true;
  298. }
  299. }
  300. return needsCopy;
  301. }
  302. private void DestroyLayerTextures()
  303. {
  304. if (isExternalSurface)
  305. {
  306. return;
  307. }
  308. for (int eyeId = 0; layerTextures != null && eyeId < texturesPerStage; ++eyeId)
  309. {
  310. if (layerTextures[eyeId].swapChain != null)
  311. {
  312. for (int stage = 0; stage < stageCount; ++stage)
  313. DestroyImmediate(layerTextures[eyeId].swapChain[stage]);
  314. }
  315. }
  316. layerTextures = null;
  317. }
  318. private void DestroyLayer()
  319. {
  320. if (layerIndex != -1)
  321. {
  322. // Turn off the overlay if it was on.
  323. OVRPlugin.EnqueueSubmitLayer(true, false, false, IntPtr.Zero, IntPtr.Zero, -1, 0, OVRPose.identity.ToPosef_Legacy(), Vector3.one.ToVector3f(), layerIndex, (OVRPlugin.OverlayShape)prevOverlayShape);
  324. instances[layerIndex] = null;
  325. layerIndex = -1;
  326. }
  327. if (layerIdPtr != IntPtr.Zero)
  328. {
  329. OVRPlugin.EnqueueDestroyLayer(layerIdPtr);
  330. layerIdPtr = IntPtr.Zero;
  331. layerIdHandle.Free();
  332. layerId = 0;
  333. }
  334. layerDesc = new OVRPlugin.LayerDesc();
  335. frameIndex = 0;
  336. prevFrameIndex = -1;
  337. }
  338. /// <summary>
  339. /// Sets the source and dest rects for both eyes. Source explains what portion of the source texture is used, and
  340. /// dest is what portion of the destination texture is rendered into.
  341. /// </summary>
  342. public void SetSrcDestRects(Rect srcLeft, Rect srcRight, Rect destLeft, Rect destRight)
  343. {
  344. srcRectLeft = srcLeft;
  345. srcRectRight = srcRight;
  346. destRectLeft = destLeft;
  347. destRectRight = destRight;
  348. }
  349. public void UpdateTextureRectMatrix()
  350. {
  351. Rect srcRectLeftConverted = new Rect(srcRectLeft.x, 1 - srcRectLeft.y - srcRectLeft.height, srcRectLeft.width, srcRectLeft.height);
  352. Rect srcRectRightConverted = new Rect(srcRectRight.x, 1 - srcRectRight.y - srcRectRight.height, srcRectRight.width, srcRectRight.height);
  353. textureRectMatrix.leftRect = srcRectLeftConverted;
  354. textureRectMatrix.rightRect = srcRectRightConverted;
  355. float leftWidthFactor = srcRectLeftConverted.width / destRectLeft.width;
  356. float leftHeightFactor = srcRectLeftConverted.height / destRectLeft.height;
  357. textureRectMatrix.leftScaleBias = new Vector4(leftWidthFactor, leftHeightFactor, srcRectLeftConverted.x - destRectLeft.x * leftWidthFactor, srcRectLeftConverted.y - destRectLeft.y * leftHeightFactor);
  358. float rightWidthFactor = srcRectRightConverted.width / destRectRight.width;
  359. float rightHeightFactor = srcRectRightConverted.height / destRectRight.height;
  360. textureRectMatrix.rightScaleBias = new Vector4(rightWidthFactor, rightHeightFactor, srcRectRightConverted.x - destRectRight.x * rightWidthFactor, srcRectRightConverted.y - destRectRight.y * rightHeightFactor);
  361. }
  362. public void SetPerLayerColorScaleAndOffset(Vector4 scale, Vector4 offset)
  363. {
  364. colorScale = scale;
  365. colorOffset = offset;
  366. }
  367. private bool LatchLayerTextures()
  368. {
  369. if (isExternalSurface)
  370. {
  371. return true;
  372. }
  373. for (int i = 0; i < texturesPerStage; ++i)
  374. {
  375. if (textures[i] != layerTextures[i].appTexture || layerTextures[i].appTexturePtr == IntPtr.Zero)
  376. {
  377. if (textures[i] != null)
  378. {
  379. #if UNITY_EDITOR
  380. var assetPath = UnityEditor.AssetDatabase.GetAssetPath(textures[i]);
  381. var importer = (UnityEditor.TextureImporter)UnityEditor.TextureImporter.GetAtPath(assetPath);
  382. if (importer && importer.textureType != UnityEditor.TextureImporterType.Default)
  383. {
  384. Debug.LogError("Need Default Texture Type for overlay");
  385. return false;
  386. }
  387. #endif
  388. var rt = textures[i] as RenderTexture;
  389. if (rt && !rt.IsCreated())
  390. rt.Create();
  391. layerTextures[i].appTexturePtr = (texturePtrs[i] != IntPtr.Zero) ? texturePtrs[i] : textures[i].GetNativeTexturePtr();
  392. if (layerTextures[i].appTexturePtr != IntPtr.Zero)
  393. layerTextures[i].appTexture = textures[i];
  394. }
  395. }
  396. if (currentOverlayShape == OverlayShape.Cubemap)
  397. {
  398. if (textures[i] as Cubemap == null)
  399. {
  400. Debug.LogError("Need Cubemap texture for cube map overlay");
  401. return false;
  402. }
  403. }
  404. }
  405. #if !UNITY_ANDROID || UNITY_EDITOR
  406. if (currentOverlayShape == OverlayShape.OffcenterCubemap)
  407. {
  408. Debug.LogWarning("Overlay shape " + currentOverlayShape + " is not supported on current platform");
  409. return false;
  410. }
  411. #endif
  412. if (layerTextures[0].appTexture == null || layerTextures[0].appTexturePtr == IntPtr.Zero)
  413. return false;
  414. return true;
  415. }
  416. private OVRPlugin.LayerDesc GetCurrentLayerDesc()
  417. {
  418. OVRPlugin.Sizei textureSize = new OVRPlugin.Sizei() { w = 0, h = 0 };
  419. if (isExternalSurface)
  420. {
  421. textureSize.w = externalSurfaceWidth;
  422. textureSize.h = externalSurfaceHeight;
  423. }
  424. else
  425. {
  426. if (textures[0] == null)
  427. {
  428. Debug.LogWarning("textures[0] hasn't been set");
  429. }
  430. textureSize.w = textures[0] ? textures[0].width : 0;
  431. textureSize.h = textures[0] ? textures[0].height : 0;
  432. }
  433. OVRPlugin.LayerDesc newDesc = new OVRPlugin.LayerDesc() {
  434. Format = OVRPlugin.EyeTextureFormat.R8G8B8A8_sRGB,
  435. LayerFlags = isExternalSurface ? 0 : (int)OVRPlugin.LayerFlags.TextureOriginAtBottomLeft,
  436. Layout = layout,
  437. MipLevels = 1,
  438. SampleCount = 1,
  439. Shape = (OVRPlugin.OverlayShape)currentOverlayShape,
  440. TextureSize = textureSize
  441. };
  442. var tex2D = textures[0] as Texture2D;
  443. if (tex2D != null)
  444. {
  445. if (tex2D.format == TextureFormat.RGBAHalf || tex2D.format == TextureFormat.RGBAFloat)
  446. newDesc.Format = OVRPlugin.EyeTextureFormat.R16G16B16A16_FP;
  447. newDesc.MipLevels = tex2D.mipmapCount;
  448. }
  449. var texCube = textures[0] as Cubemap;
  450. if (texCube != null)
  451. {
  452. if (texCube.format == TextureFormat.RGBAHalf || texCube.format == TextureFormat.RGBAFloat)
  453. newDesc.Format = OVRPlugin.EyeTextureFormat.R16G16B16A16_FP;
  454. newDesc.MipLevels = texCube.mipmapCount;
  455. }
  456. var rt = textures[0] as RenderTexture;
  457. if (rt != null)
  458. {
  459. newDesc.SampleCount = rt.antiAliasing;
  460. if (rt.format == RenderTextureFormat.ARGBHalf || rt.format == RenderTextureFormat.ARGBFloat || rt.format == RenderTextureFormat.RGB111110Float)
  461. newDesc.Format = OVRPlugin.EyeTextureFormat.R16G16B16A16_FP;
  462. }
  463. if (isProtectedContent)
  464. {
  465. newDesc.LayerFlags |= (int)OVRPlugin.LayerFlags.ProtectedContent;
  466. }
  467. if (isExternalSurface)
  468. {
  469. newDesc.LayerFlags |= (int)OVRPlugin.LayerFlags.AndroidSurfaceSwapChain;
  470. }
  471. return newDesc;
  472. }
  473. private bool PopulateLayer(int mipLevels, bool isHdr, OVRPlugin.Sizei size, int sampleCount, int stage)
  474. {
  475. if (isExternalSurface)
  476. {
  477. return true;
  478. }
  479. bool ret = false;
  480. RenderTextureFormat rtFormat = (isHdr) ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
  481. for (int eyeId = 0; eyeId < texturesPerStage; ++eyeId)
  482. {
  483. Texture et = layerTextures[eyeId].swapChain[stage];
  484. if (et == null)
  485. continue;
  486. for (int mip = 0; mip < mipLevels; ++mip)
  487. {
  488. int width = size.w >> mip;
  489. if (width < 1) width = 1;
  490. int height = size.h >> mip;
  491. if (height < 1) height = 1;
  492. #if UNITY_2017_1_1 || UNITY_2017_2_OR_NEWER
  493. RenderTextureDescriptor descriptor = new RenderTextureDescriptor(width, height, rtFormat, 0);
  494. descriptor.msaaSamples = sampleCount;
  495. descriptor.useMipMap = true;
  496. descriptor.autoGenerateMips = false;
  497. descriptor.sRGB = false;
  498. var tempRTDst = RenderTexture.GetTemporary(descriptor);
  499. #else
  500. var tempRTDst = RenderTexture.GetTemporary(width, height, 0, rtFormat, RenderTextureReadWrite.Linear, sampleCount);
  501. #endif
  502. if (!tempRTDst.IsCreated())
  503. tempRTDst.Create();
  504. tempRTDst.DiscardContents();
  505. bool dataIsLinear = isHdr || (QualitySettings.activeColorSpace == ColorSpace.Linear);
  506. #if !UNITY_2017_1_OR_NEWER
  507. var rt = textures[eyeId] as RenderTexture;
  508. dataIsLinear |= rt != null && rt.sRGB; //HACK: Unity 5.6 and earlier convert to linear on read from sRGB RenderTexture.
  509. #endif
  510. #if UNITY_ANDROID && !UNITY_EDITOR
  511. dataIsLinear = true; //HACK: Graphics.CopyTexture causes linear->srgb conversion on target write with D3D but not GLES.
  512. #endif
  513. if (currentOverlayShape != OverlayShape.Cubemap && currentOverlayShape != OverlayShape.OffcenterCubemap)
  514. {
  515. tex2DMaterial.SetInt("_linearToSrgb", (!isHdr && dataIsLinear) ? 1 : 0);
  516. //Resolve, decompress, swizzle, etc not handled by simple CopyTexture.
  517. #if !UNITY_ANDROID || UNITY_EDITOR
  518. // The PC compositor uses premultiplied alpha, so multiply it here.
  519. tex2DMaterial.SetInt("_premultiply", 1);
  520. #endif
  521. Graphics.Blit(textures[eyeId], tempRTDst, tex2DMaterial);
  522. Graphics.CopyTexture(tempRTDst, 0, 0, et, 0, mip);
  523. }
  524. #if UNITY_2017_1_OR_NEWER
  525. else // Cubemap
  526. {
  527. for (int face = 0; face < 6; ++face)
  528. {
  529. cubeMaterial.SetInt("_linearToSrgb", (!isHdr && dataIsLinear) ? 1 : 0);
  530. #if !UNITY_ANDROID || UNITY_EDITOR
  531. // The PC compositor uses premultiplied alpha, so multiply it here.
  532. cubeMaterial.SetInt("_premultiply", 1);
  533. #endif
  534. cubeMaterial.SetInt("_face", face);
  535. //Resolve, decompress, swizzle, etc not handled by simple CopyTexture.
  536. Graphics.Blit(textures[eyeId], tempRTDst, cubeMaterial);
  537. Graphics.CopyTexture(tempRTDst, 0, 0, et, face, mip);
  538. }
  539. }
  540. #endif
  541. RenderTexture.ReleaseTemporary(tempRTDst);
  542. ret = true;
  543. }
  544. }
  545. return ret;
  546. }
  547. private bool SubmitLayer(bool overlay, bool headLocked, bool noDepthBufferTesting, OVRPose pose, Vector3 scale, int frameIndex)
  548. {
  549. int rightEyeIndex = (texturesPerStage >= 2) ? 1 : 0;
  550. if (overrideTextureRectMatrix)
  551. {
  552. UpdateTextureRectMatrix();
  553. }
  554. bool isOverlayVisible = OVRPlugin.EnqueueSubmitLayer(overlay, headLocked, noDepthBufferTesting,
  555. isExternalSurface ? System.IntPtr.Zero : layerTextures[0].appTexturePtr,
  556. isExternalSurface ? System.IntPtr.Zero : layerTextures[rightEyeIndex].appTexturePtr,
  557. layerId, frameIndex, pose.flipZ().ToPosef_Legacy(), scale.ToVector3f(), layerIndex, (OVRPlugin.OverlayShape)currentOverlayShape,
  558. overrideTextureRectMatrix, textureRectMatrix, overridePerLayerColorScaleAndOffset, colorScale, colorOffset, useExpensiveSuperSample,
  559. hidden);
  560. prevOverlayShape = currentOverlayShape;
  561. return isOverlayVisible;
  562. }
  563. #region Unity Messages
  564. void Awake()
  565. {
  566. Debug.Log("Overlay Awake");
  567. if (tex2DMaterial == null)
  568. tex2DMaterial = new Material(Shader.Find("Oculus/Texture2D Blit"));
  569. if (cubeMaterial == null)
  570. cubeMaterial = new Material(Shader.Find("Oculus/Cubemap Blit"));
  571. rend = GetComponent<Renderer>();
  572. if (textures.Length == 0)
  573. textures = new Texture[] { null };
  574. // Backward compatibility
  575. if (rend != null && textures[0] == null)
  576. textures[0] = rend.material.mainTexture;
  577. }
  578. static public string OpenVROverlayKey { get { return "unity:" + Application.companyName + "." + Application.productName; } }
  579. private ulong OpenVROverlayHandle = OVR.OpenVR.OpenVR.k_ulOverlayHandleInvalid;
  580. void OnEnable()
  581. {
  582. if (OVRManager.OVRManagerinitialized)
  583. InitOVROverlay();
  584. }
  585. void InitOVROverlay()
  586. {
  587. if (!OVRManager.isHmdPresent)
  588. {
  589. enabled = false;
  590. return;
  591. }
  592. constructedOverlayXRDevice = OVRManager.XRDevice.Unknown;
  593. if (OVRManager.loadedXRDevice == OVRManager.XRDevice.OpenVR)
  594. {
  595. OVR.OpenVR.CVROverlay overlay = OVR.OpenVR.OpenVR.Overlay;
  596. if (overlay != null)
  597. {
  598. OVR.OpenVR.EVROverlayError error = overlay.CreateOverlay(OpenVROverlayKey + transform.name, gameObject.name, ref OpenVROverlayHandle);
  599. if (error != OVR.OpenVR.EVROverlayError.None)
  600. {
  601. enabled = false;
  602. return;
  603. }
  604. }
  605. else
  606. {
  607. enabled = false;
  608. return;
  609. }
  610. }
  611. constructedOverlayXRDevice = OVRManager.loadedXRDevice;
  612. xrDeviceConstructed = true;
  613. }
  614. void OnDisable()
  615. {
  616. if ((gameObject.hideFlags & HideFlags.DontSaveInBuild) != 0)
  617. return;
  618. if (!OVRManager.OVRManagerinitialized)
  619. return;
  620. if (OVRManager.loadedXRDevice != constructedOverlayXRDevice)
  621. return;
  622. if (OVRManager.loadedXRDevice == OVRManager.XRDevice.Oculus)
  623. {
  624. DestroyLayerTextures();
  625. DestroyLayer();
  626. }
  627. else if (OVRManager.loadedXRDevice == OVRManager.XRDevice.OpenVR)
  628. {
  629. if (OpenVROverlayHandle != OVR.OpenVR.OpenVR.k_ulOverlayHandleInvalid)
  630. {
  631. OVR.OpenVR.CVROverlay overlay = OVR.OpenVR.OpenVR.Overlay;
  632. if (overlay != null)
  633. {
  634. overlay.DestroyOverlay(OpenVROverlayHandle);
  635. }
  636. OpenVROverlayHandle = OVR.OpenVR.OpenVR.k_ulOverlayHandleInvalid;
  637. }
  638. }
  639. constructedOverlayXRDevice = OVRManager.XRDevice.Unknown;
  640. xrDeviceConstructed = false;
  641. }
  642. void OnDestroy()
  643. {
  644. DestroyLayerTextures();
  645. DestroyLayer();
  646. }
  647. bool ComputeSubmit(ref OVRPose pose, ref Vector3 scale, ref bool overlay, ref bool headLocked)
  648. {
  649. Camera headCamera = Camera.main;
  650. overlay = (currentOverlayType == OverlayType.Overlay);
  651. headLocked = false;
  652. for (var t = transform; t != null && !headLocked; t = t.parent)
  653. headLocked |= (t == headCamera.transform);
  654. pose = (headLocked) ? transform.ToHeadSpacePose(headCamera) : transform.ToTrackingSpacePose(headCamera);
  655. scale = transform.lossyScale;
  656. for (int i = 0; i < 3; ++i)
  657. scale[i] /= headCamera.transform.lossyScale[i];
  658. if (currentOverlayShape == OverlayShape.Cubemap)
  659. {
  660. #if UNITY_ANDROID && !UNITY_EDITOR
  661. //HACK: VRAPI cubemaps assume are yawed 180 degrees relative to LibOVR.
  662. pose.orientation = pose.orientation * Quaternion.AngleAxis(180, Vector3.up);
  663. #endif
  664. pose.position = headCamera.transform.position;
  665. }
  666. // Pack the offsetCenter directly into pose.position for offcenterCubemap
  667. if (currentOverlayShape == OverlayShape.OffcenterCubemap)
  668. {
  669. pose.position = transform.position;
  670. if (pose.position.magnitude > 1.0f)
  671. {
  672. Debug.LogWarning("Your cube map center offset's magnitude is greater than 1, which will cause some cube map pixel always invisible .");
  673. return false;
  674. }
  675. }
  676. // Cylinder overlay sanity checking
  677. if (currentOverlayShape == OverlayShape.Cylinder)
  678. {
  679. float arcAngle = scale.x / scale.z / (float)Math.PI * 180.0f;
  680. if (arcAngle > 180.0f)
  681. {
  682. Debug.LogWarning("Cylinder overlay's arc angle has to be below 180 degree, current arc angle is " + arcAngle + " degree." );
  683. return false;
  684. }
  685. }
  686. return true;
  687. }
  688. void OpenVROverlayUpdate(Vector3 scale, OVRPose pose)
  689. {
  690. OVR.OpenVR.CVROverlay overlayRef = OVR.OpenVR.OpenVR.Overlay;
  691. if (overlayRef == null)
  692. return;
  693. Texture overlayTex = textures[0];
  694. if (overlayTex != null)
  695. {
  696. OVR.OpenVR.EVROverlayError error = overlayRef.ShowOverlay(OpenVROverlayHandle);
  697. if (error == OVR.OpenVR.EVROverlayError.InvalidHandle || error == OVR.OpenVR.EVROverlayError.UnknownOverlay)
  698. {
  699. if (overlayRef.FindOverlay(OpenVROverlayKey + transform.name, ref OpenVROverlayHandle) != OVR.OpenVR.EVROverlayError.None)
  700. return;
  701. }
  702. OVR.OpenVR.Texture_t tex = new OVR.OpenVR.Texture_t();
  703. tex.handle = overlayTex.GetNativeTexturePtr();
  704. tex.eType = SystemInfo.graphicsDeviceVersion.StartsWith("OpenGL") ? OVR.OpenVR.ETextureType.OpenGL : OVR.OpenVR.ETextureType.DirectX;
  705. tex.eColorSpace = OVR.OpenVR.EColorSpace.Auto;
  706. overlayRef.SetOverlayTexture(OpenVROverlayHandle, ref tex);
  707. OVR.OpenVR.VRTextureBounds_t textureBounds = new OVR.OpenVR.VRTextureBounds_t();
  708. textureBounds.uMin = (0 + OpenVRUVOffsetAndScale.x) * OpenVRUVOffsetAndScale.z;
  709. textureBounds.vMin = (1 + OpenVRUVOffsetAndScale.y) * OpenVRUVOffsetAndScale.w;
  710. textureBounds.uMax = (1 + OpenVRUVOffsetAndScale.x) * OpenVRUVOffsetAndScale.z;
  711. textureBounds.vMax = (0 + OpenVRUVOffsetAndScale.y) * OpenVRUVOffsetAndScale.w;
  712. overlayRef.SetOverlayTextureBounds(OpenVROverlayHandle, ref textureBounds);
  713. OVR.OpenVR.HmdVector2_t vecMouseScale = new OVR.OpenVR.HmdVector2_t();
  714. vecMouseScale.v0 = OpenVRMouseScale.x;
  715. vecMouseScale.v1 = OpenVRMouseScale.y;
  716. overlayRef.SetOverlayMouseScale(OpenVROverlayHandle, ref vecMouseScale);
  717. overlayRef.SetOverlayWidthInMeters(OpenVROverlayHandle, scale.x);
  718. Matrix4x4 mat44 = Matrix4x4.TRS(pose.position, pose.orientation, Vector3.one);
  719. OVR.OpenVR.HmdMatrix34_t pose34 = mat44.ConvertToHMDMatrix34();
  720. overlayRef.SetOverlayTransformAbsolute(OpenVROverlayHandle, OVR.OpenVR.ETrackingUniverseOrigin.TrackingUniverseStanding, ref pose34);
  721. }
  722. }
  723. private Vector4 OpenVRUVOffsetAndScale = new Vector4(0, 0, 1.0f, 1.0f);
  724. private Vector2 OpenVRMouseScale = new Vector2(1, 1);
  725. private OVRManager.XRDevice constructedOverlayXRDevice;
  726. private bool xrDeviceConstructed = false;
  727. void LateUpdate()
  728. {
  729. if (!OVRManager.OVRManagerinitialized)
  730. return;
  731. if (!xrDeviceConstructed)
  732. {
  733. InitOVROverlay();
  734. }
  735. if (OVRManager.loadedXRDevice != constructedOverlayXRDevice)
  736. {
  737. Debug.LogError("Warning-XR Device was switched during runtime with overlays still enabled. When doing so, all overlays constructed with the previous XR device must first be disabled.");
  738. return;
  739. }
  740. // The overlay must be specified every eye frame, because it is positioned relative to the
  741. // current head location. If frames are dropped, it will be time warped appropriately,
  742. // just like the eye buffers.
  743. if (currentOverlayType == OverlayType.None || ((textures.Length < texturesPerStage || textures[0] == null) && !isExternalSurface))
  744. return;
  745. OVRPose pose = OVRPose.identity;
  746. Vector3 scale = Vector3.one;
  747. bool overlay = false;
  748. bool headLocked = false;
  749. if (!ComputeSubmit(ref pose, ref scale, ref overlay, ref headLocked))
  750. return;
  751. if (OVRManager.loadedXRDevice == OVRManager.XRDevice.OpenVR)
  752. {
  753. if (currentOverlayShape == OverlayShape.Quad)
  754. OpenVROverlayUpdate(scale, pose);
  755. //No more Overlay processing is required if we're on OpenVR
  756. return;
  757. }
  758. OVRPlugin.LayerDesc newDesc = GetCurrentLayerDesc();
  759. bool isHdr = (newDesc.Format == OVRPlugin.EyeTextureFormat.R16G16B16A16_FP);
  760. // If the layer and textures are created but sizes differ, force re-creating them
  761. if (!layerDesc.TextureSize.Equals(newDesc.TextureSize) && layerId > 0)
  762. {
  763. DestroyLayerTextures();
  764. DestroyLayer();
  765. }
  766. bool createdLayer = CreateLayer(newDesc.MipLevels, newDesc.SampleCount, newDesc.Format, newDesc.LayerFlags, newDesc.TextureSize, newDesc.Shape);
  767. if (layerIndex == -1 || layerId <= 0)
  768. return;
  769. bool useMipmaps = (newDesc.MipLevels > 1);
  770. createdLayer |= CreateLayerTextures(useMipmaps, newDesc.TextureSize, isHdr);
  771. if (!isExternalSurface && (layerTextures[0].appTexture as RenderTexture != null))
  772. isDynamic = true;
  773. if (!LatchLayerTextures())
  774. return;
  775. // Don't populate the same frame image twice.
  776. if (frameIndex > prevFrameIndex)
  777. {
  778. int stage = frameIndex % stageCount;
  779. if (!PopulateLayer (newDesc.MipLevels, isHdr, newDesc.TextureSize, newDesc.SampleCount, stage))
  780. return;
  781. }
  782. bool isOverlayVisible = SubmitLayer(overlay, headLocked, noDepthBufferTesting, pose, scale, frameIndex);
  783. prevFrameIndex = frameIndex;
  784. if (isDynamic)
  785. ++frameIndex;
  786. // Backward compatibility: show regular renderer if overlay isn't visible.
  787. if (rend)
  788. rend.enabled = !isOverlayVisible;
  789. }
  790. #endregion
  791. }