NROverlayMeshGenerator.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /****************************************************************************
  2. * Copyright 2019 Nreal Techonology Limited. All rights reserved.
  3. *
  4. * This file is part of NRSDK.
  5. *
  6. * https://www.nreal.ai/
  7. *
  8. *****************************************************************************/
  9. namespace NRKernal.Experimental
  10. {
  11. using System.Collections.Generic;
  12. using UnityEngine;
  13. /// <summary>
  14. /// When attached to a GameObject with an NROverlay component, NROverlayMeshGenerator will use a mesh renderer
  15. /// to preview the appearance of the NROverlay as it would appear as a TimeWarp overlay on a headset.
  16. /// </summary>
  17. [RequireComponent(typeof(MeshFilter))]
  18. [RequireComponent(typeof(MeshRenderer))]
  19. [ExecuteInEditMode]
  20. public class NROverlayMeshGenerator : MonoBehaviour
  21. {
  22. private Mesh m_Mesh;
  23. private List<Vector3> m_Verts = new List<Vector3>();
  24. private List<Vector2> m_UV = new List<Vector2>();
  25. private List<int> m_Tris = new List<int>();
  26. private NROverlay m_Overlay;
  27. private MeshFilter m_MeshFilter;
  28. private MeshCollider m_MeshCollider;
  29. private MeshRenderer m_MeshRenderer;
  30. private Transform m_Transform;
  31. private NROverlay.OverlayShape m_LastShape;
  32. private Vector3 m_LastPosition;
  33. private Quaternion m_LastRotation;
  34. private Vector3 m_LastScale;
  35. private bool m_Awake = false;
  36. private Material previewMat;
  37. private Material editorPreviewMat;
  38. protected void Awake()
  39. {
  40. //gameObject.hideFlags = HideFlags.HideInHierarchy;
  41. m_MeshFilter = GetComponent<MeshFilter>();
  42. m_MeshCollider = GetComponent<MeshCollider>();
  43. m_MeshRenderer = GetComponent<MeshRenderer>();
  44. m_Transform = transform;
  45. m_Awake = true;
  46. }
  47. public void SetOverlay(NROverlay overlay)
  48. {
  49. m_Overlay = overlay;
  50. }
  51. private Rect GetBoundingRect(Rect a, Rect b)
  52. {
  53. float xMin = Mathf.Min(a.x, b.x);
  54. float xMax = Mathf.Max(a.x + a.width, b.x + b.width);
  55. float yMin = Mathf.Min(a.y, b.y);
  56. float yMax = Mathf.Max(a.y + a.height, b.y + b.height);
  57. return new Rect(xMin, yMin, xMax - xMin, yMax - yMin);
  58. }
  59. private Rect GetBoundingRect()
  60. {
  61. Rect a, b;
  62. a = b = new Rect(0, 0, 1, 1);
  63. float xMin = Mathf.Min(a.x, b.x);
  64. float xMax = Mathf.Max(a.x + a.width, b.x + b.width);
  65. float yMin = Mathf.Min(a.y, b.y);
  66. float yMax = Mathf.Max(a.y + a.height, b.y + b.height);
  67. return new Rect(xMin, yMin, xMax - xMin, yMax - yMin);
  68. }
  69. protected void OnEnable()
  70. {
  71. #if UNITY_EDITOR
  72. UnityEditor.EditorApplication.update += Update;
  73. #endif
  74. }
  75. protected void OnDisable()
  76. {
  77. #if UNITY_EDITOR
  78. UnityEditor.EditorApplication.update -= Update;
  79. #endif
  80. }
  81. private void Update()
  82. {
  83. if (!m_Awake)
  84. {
  85. Awake();
  86. }
  87. if (m_Overlay)
  88. {
  89. NROverlay.OverlayShape shape = m_Overlay.overlayShape;
  90. Vector3 position = m_Transform.position;
  91. Quaternion rotation = m_Transform.rotation;
  92. Vector3 scale = m_Transform.lossyScale;
  93. //Rect destRectLeft = _Overlay.overrideTextureRectMatrix ? _Overlay.destRectLeft : new Rect(0, 0, 1, 1);
  94. //Rect destRectRight = _Overlay.overrideTextureRectMatrix ? _Overlay.destRectRight : new Rect(0, 0, 1, 1);
  95. //Rect srcRectLeft = _Overlay.overrideTextureRectMatrix ? _Overlay.srcRectLeft : new Rect(0, 0, 1, 1);
  96. Texture texture = m_Overlay.MainTexture;
  97. // Re-generate the mesh if necessary
  98. if (m_Mesh == null ||
  99. m_LastShape != shape ||
  100. m_LastPosition != position ||
  101. m_LastRotation != rotation ||
  102. m_LastScale != scale
  103. )
  104. {
  105. UpdateMesh(shape, position, rotation, scale, GetBoundingRect());
  106. m_LastShape = shape;
  107. m_LastPosition = position;
  108. m_LastRotation = rotation;
  109. m_LastScale = scale;
  110. }
  111. // Generate the material and update textures if necessary
  112. #if UNITY_EDITOR
  113. if (m_Overlay.previewInEditor)
  114. {
  115. if (editorPreviewMat == null)
  116. {
  117. editorPreviewMat = new Material(Resources.Load<Shader>("Overlay/Materials/OverlayPreview"));
  118. }
  119. m_MeshRenderer.sharedMaterial = editorPreviewMat;
  120. m_MeshRenderer.sharedMaterial.mainTexture = m_Overlay.MainTexture;
  121. }
  122. else
  123. #endif
  124. {
  125. if (previewMat == null)
  126. {
  127. previewMat = new Material(Resources.Load<Shader>("Overlay/Materials/OverlayMask"));
  128. }
  129. m_MeshRenderer.sharedMaterial = previewMat;
  130. }
  131. }
  132. }
  133. private void UpdateMesh(NROverlay.OverlayShape shape, Vector3 position, Quaternion rotation, Vector3 scale, Rect rect)
  134. {
  135. if (m_MeshFilter)
  136. {
  137. if (m_Mesh == null)
  138. {
  139. m_Mesh = new Mesh() { name = "Overlay" };
  140. m_Mesh.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
  141. }
  142. m_Mesh.Clear();
  143. m_Verts.Clear();
  144. m_UV.Clear();
  145. m_Tris.Clear();
  146. GenerateMesh(m_Verts, m_UV, m_Tris, shape, position, rotation, scale, rect);
  147. m_Mesh.SetVertices(m_Verts);
  148. m_Mesh.SetUVs(0, m_UV);
  149. m_Mesh.SetTriangles(m_Tris, 0);
  150. m_Mesh.UploadMeshData(false);
  151. m_MeshFilter.sharedMesh = m_Mesh;
  152. if (m_MeshCollider)
  153. {
  154. m_MeshCollider.sharedMesh = m_Mesh;
  155. }
  156. }
  157. }
  158. public static void GenerateMesh(List<Vector3> verts, List<Vector2> uvs, List<int> tris, NROverlay.OverlayShape shape, Vector3 position, Quaternion rotation, Vector3 scale, Rect rect)
  159. {
  160. switch (shape)
  161. {
  162. //case NROverlay.OverlayShape.Equirect:
  163. // BuildSphere(verts, uvs, tris, position, rotation, scale, rect);
  164. // break;
  165. //case NROverlay.OverlayShape.Cubemap:
  166. //case NROverlay.OverlayShape.OffcenterCubemap:
  167. // BuildCube(verts, uvs, tris, position, rotation, scale);
  168. // break;
  169. case NROverlay.OverlayShape.Quad:
  170. BuildQuad(verts, uvs, tris, rect);
  171. break;
  172. //case NROverlay.OverlayShape.Cylinder:
  173. // BuildHemicylinder(verts, uvs, tris, scale, rect);
  174. // break;
  175. }
  176. }
  177. private static Vector2 GetSphereUV(float theta, float phi, float expand_coef)
  178. {
  179. float thetaU = ((theta / (2 * Mathf.PI) - 0.5f) / expand_coef) + 0.5f;
  180. float phiV = ((phi / Mathf.PI) / expand_coef) + 0.5f;
  181. return new Vector2(thetaU, phiV);
  182. }
  183. private static Vector3 GetSphereVert(float theta, float phi)
  184. {
  185. return new Vector3(-Mathf.Sin(theta) * Mathf.Cos(phi), Mathf.Sin(phi), -Mathf.Cos(theta) * Mathf.Cos(phi));
  186. }
  187. public static void BuildSphere(List<Vector3> verts, List<Vector2> uv, List<int> triangles, Vector3 position, Quaternion rotation, Vector3 scale, Rect rect, float worldScale = 800, int latitudes = 128, int longitudes = 128, float expand_coef = 1.0f)
  188. {
  189. position = Quaternion.Inverse(rotation) * position;
  190. latitudes = Mathf.CeilToInt(latitudes * rect.height);
  191. longitudes = Mathf.CeilToInt(longitudes * rect.width);
  192. float minTheta = Mathf.PI * 2 * (rect.x);
  193. float minPhi = Mathf.PI * (0.5f - rect.y - rect.height);
  194. float thetaScale = Mathf.PI * 2 * rect.width / longitudes;
  195. float phiScale = Mathf.PI * rect.height / latitudes;
  196. for (int j = 0; j < latitudes + 1; j += 1)
  197. {
  198. for (int k = 0; k < longitudes + 1; k++)
  199. {
  200. float theta = minTheta + k * thetaScale;
  201. float phi = minPhi + j * phiScale;
  202. Vector2 suv = GetSphereUV(theta, phi, expand_coef);
  203. uv.Add(new Vector2((suv.x - rect.x) / rect.width, (suv.y - rect.y) / rect.height));
  204. Vector3 vert = GetSphereVert(theta, phi);
  205. vert.x = (worldScale * vert.x - position.x) / scale.x;
  206. vert.y = (worldScale * vert.y - position.y) / scale.y;
  207. vert.z = (worldScale * vert.z - position.z) / scale.z;
  208. verts.Add(vert);
  209. }
  210. }
  211. for (int j = 0; j < latitudes; j++)
  212. {
  213. for (int k = 0; k < longitudes; k++)
  214. {
  215. triangles.Add((j * (longitudes + 1)) + k);
  216. triangles.Add(((j + 1) * (longitudes + 1)) + k);
  217. triangles.Add(((j + 1) * (longitudes + 1)) + k + 1);
  218. triangles.Add(((j + 1) * (longitudes + 1)) + k + 1);
  219. triangles.Add((j * (longitudes + 1)) + k + 1);
  220. triangles.Add((j * (longitudes + 1)) + k);
  221. }
  222. }
  223. }
  224. private enum CubeFace
  225. {
  226. Right,
  227. Left,
  228. Top,
  229. Bottom,
  230. Front,
  231. Back,
  232. COUNT
  233. }
  234. private static readonly Vector3[] BottomLeft = new Vector3[]{
  235. new Vector3(-0.5f, -0.5f, -0.5f),
  236. new Vector3(0.5f, -0.5f, 0.5f),
  237. new Vector3(0.5f, 0.5f, -0.5f),
  238. new Vector3(0.5f, -0.5f, 0.5f),
  239. new Vector3(0.5f, -0.5f, -0.5f),
  240. new Vector3(-0.5f, -0.5f, 0.5f)
  241. };
  242. private static readonly Vector3[] RightVector = new Vector3[]{
  243. Vector3.forward,
  244. Vector3.back,
  245. Vector3.left,
  246. Vector3.left,
  247. Vector3.left,
  248. Vector3.right
  249. };
  250. private static readonly Vector3[] UpVector = new Vector3[]{
  251. Vector3.up,
  252. Vector3.up,
  253. Vector3.forward,
  254. Vector3.back,
  255. Vector3.up,
  256. Vector3.up
  257. };
  258. private static Vector2 GetCubeUV(CubeFace face, Vector2 sideUV, float expand_coef)
  259. {
  260. sideUV = (sideUV - 0.5f * Vector2.one) / expand_coef + 0.5f * Vector2.one;
  261. switch (face)
  262. {
  263. case CubeFace.Bottom:
  264. return new Vector2(sideUV.x / 3, sideUV.y / 2);
  265. case CubeFace.Front:
  266. return new Vector2((1 + sideUV.x) / 3, sideUV.y / 2);
  267. case CubeFace.Back:
  268. return new Vector2((2 + sideUV.x) / 3, sideUV.y / 2);
  269. case CubeFace.Right:
  270. return new Vector2(sideUV.x / 3, (1 + sideUV.y) / 2);
  271. case CubeFace.Left:
  272. return new Vector2((1 + sideUV.x) / 3, (1 + sideUV.y) / 2);
  273. case CubeFace.Top:
  274. return new Vector2((2 + sideUV.x) / 3, (1 + sideUV.y) / 2);
  275. default:
  276. return Vector2.zero;
  277. }
  278. }
  279. private static Vector3 GetCubeVert(CubeFace face, Vector2 sideUV, float expand_coef)
  280. {
  281. return BottomLeft[(int)face] + sideUV.x * RightVector[(int)face] + sideUV.y * UpVector[(int)face];
  282. }
  283. public static void BuildCube(List<Vector3> verts, List<Vector2> uv, List<int> triangles, Vector3 position, Quaternion rotation, Vector3 scale, float worldScale = 800, int subQuads = 1, float expand_coef = 1.01f)
  284. {
  285. position = Quaternion.Inverse(rotation) * position;
  286. int vertsPerSide = (subQuads + 1) * (subQuads + 1);
  287. for (int i = 0; i < (int)CubeFace.COUNT; i++)
  288. {
  289. for (int j = 0; j < subQuads + 1; j++)
  290. {
  291. for (int k = 0; k < subQuads + 1; k++)
  292. {
  293. float u = j / (float)subQuads;
  294. float v = k / (float)subQuads;
  295. uv.Add(GetCubeUV((CubeFace)i, new Vector2(u, v), expand_coef));
  296. Vector3 vert = GetCubeVert((CubeFace)i, new Vector2(u, v), expand_coef);
  297. vert.x = (worldScale * vert.x - position.x) / scale.x;
  298. vert.y = (worldScale * vert.y - position.y) / scale.y;
  299. vert.z = (worldScale * vert.z - position.z) / scale.z;
  300. verts.Add(vert);
  301. }
  302. }
  303. for (int j = 0; j < subQuads; j++)
  304. {
  305. for (int k = 0; k < subQuads; k++)
  306. {
  307. triangles.Add(vertsPerSide * i + ((j + 1) * (subQuads + 1)) + k);
  308. triangles.Add(vertsPerSide * i + (j * (subQuads + 1)) + k);
  309. triangles.Add(vertsPerSide * i + ((j + 1) * (subQuads + 1)) + k + 1);
  310. triangles.Add(vertsPerSide * i + ((j + 1) * (subQuads + 1)) + k + 1);
  311. triangles.Add(vertsPerSide * i + (j * (subQuads + 1)) + k);
  312. triangles.Add(vertsPerSide * i + (j * (subQuads + 1)) + k + 1);
  313. }
  314. }
  315. }
  316. }
  317. public static void BuildQuad(List<Vector3> verts, List<Vector2> uv, List<int> triangles, Rect rect)
  318. {
  319. verts.Add(new Vector3(rect.x - 0.5f, (1 - rect.y - rect.height) - 0.5f, 0));
  320. verts.Add(new Vector3(rect.x - 0.5f, (1 - rect.y) - 0.5f, 0));
  321. verts.Add(new Vector3(rect.x + rect.width - 0.5f, (1 - rect.y) - 0.5f, 0));
  322. verts.Add(new Vector3(rect.x + rect.width - 0.5f, (1 - rect.y - rect.height) - 0.5f, 0));
  323. uv.Add(new Vector2(0, 0));
  324. uv.Add(new Vector2(0, 1));
  325. uv.Add(new Vector2(1, 1));
  326. uv.Add(new Vector2(1, 0));
  327. triangles.Add(0);
  328. triangles.Add(1);
  329. triangles.Add(2);
  330. triangles.Add(2);
  331. triangles.Add(3);
  332. triangles.Add(0);
  333. }
  334. public static void BuildHemicylinder(List<Vector3> verts, List<Vector2> uv, List<int> triangles, Vector3 scale, Rect rect, int longitudes = 128)
  335. {
  336. float height = Mathf.Abs(scale.y) * rect.height;
  337. float radius = scale.z;
  338. float arcLength = scale.x * rect.width;
  339. float arcAngle = arcLength / radius;
  340. float minAngle = scale.x * (-0.5f + rect.x) / radius;
  341. int columns = Mathf.CeilToInt(longitudes * arcAngle / (2 * Mathf.PI));
  342. // we don't want super tall skinny triangles because that can lead to artifacting.
  343. // make triangles no more than 2x taller than wide
  344. float triangleWidth = arcLength / columns;
  345. float ratio = height / triangleWidth;
  346. int rows = Mathf.CeilToInt(ratio / 2);
  347. for (int j = 0; j < rows + 1; j += 1)
  348. {
  349. for (int k = 0; k < columns + 1; k++)
  350. {
  351. uv.Add(new Vector2((k / (float)columns), 1 - (j / (float)rows)));
  352. Vector3 vert = Vector3.zero;
  353. // because the scale is used to control the parameters, we need
  354. // to reverse multiply by scale to appear correctly
  355. vert.x = (Mathf.Sin(minAngle + (k * arcAngle / columns)) * radius) / scale.x;
  356. vert.y = (0.5f - rect.y - rect.height + rect.height * (1 - j / (float)rows));
  357. vert.z = (Mathf.Cos(minAngle + (k * arcAngle / columns)) * radius) / scale.z;
  358. verts.Add(vert);
  359. }
  360. }
  361. for (int j = 0; j < rows; j++)
  362. {
  363. for (int k = 0; k < columns; k++)
  364. {
  365. triangles.Add((j * (columns + 1)) + k);
  366. triangles.Add(((j + 1) * (columns + 1)) + k + 1);
  367. triangles.Add(((j + 1) * (columns + 1)) + k);
  368. triangles.Add(((j + 1) * (columns + 1)) + k + 1);
  369. triangles.Add((j * (columns + 1)) + k);
  370. triangles.Add((j * (columns + 1)) + k + 1);
  371. }
  372. }
  373. }
  374. }
  375. }