XRMeshChain.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. namespace Unity.XRTools.Rendering
  4. {
  5. /// <summary>
  6. /// The mesh chain handles all the translation between Unity's mesh class,
  7. /// what the line renderers want to do, and what the billboard-pipe based shaders expect
  8. /// If you need more custom/optimized access to this kind of mesh information, feel
  9. /// free to hook into this structure directly.
  10. /// </summary>
  11. public class XRMeshChain
  12. {
  13. /// <summary>
  14. /// What part of the mesh to refresh
  15. /// </summary>
  16. [System.Flags]
  17. public enum MeshRefreshFlag
  18. {
  19. /// <summary>
  20. /// Don't refresh any of the mesh
  21. /// </summary>
  22. None = 0,
  23. /// <summary>
  24. /// Refresh positions
  25. /// </summary>
  26. Positions = 1,
  27. /// <summary>
  28. /// Refresh colors
  29. /// </summary>
  30. Colors = 2,
  31. /// <summary>
  32. /// Refresh sizes
  33. /// </summary>
  34. Sizes = 4,
  35. /// <summary>
  36. /// Refresh all mesh components
  37. /// </summary>
  38. All = 7
  39. }
  40. Vector3[] m_Verts;
  41. Color32[] m_Colors;
  42. List<Vector4> m_ShapeData; // xy: UV coordinates for GPU expansion zw: Size of this vertex, size of the neighbor
  43. List<Vector3> m_NeighborPoints; // Location of the next point this pipe connects to, or itself if it is a billboard
  44. // Update flags to prevent unnecessary mesh data generation
  45. MeshRefreshFlag m_DataThatNeedsUpdate = MeshRefreshFlag.All;
  46. // Cached runtime data
  47. Mesh m_Mesh;
  48. Transform m_OwnerTransform;
  49. bool m_WorldSpaceData;
  50. /// <summary>
  51. /// Whether the control points of this mesh chain have been referenced in world or local space
  52. /// This makes sure the bounding box of the mesh is updated appropriately for culling
  53. /// </summary>
  54. public bool worldSpaceData
  55. {
  56. get { return m_WorldSpaceData; }
  57. set { m_WorldSpaceData = value; }
  58. }
  59. /// <summary>
  60. /// How many primitives/quads this mesh chain supports and has reserved memory for
  61. /// </summary>
  62. public int reservedElements { get; private set; }
  63. /// <summary>
  64. /// If using world space data, this option will force the center of the bounding box to be the root
  65. /// </summary>
  66. public bool centerAtRoot { get; set; }
  67. /// <summary>
  68. /// Initialize a new XRMeshChain
  69. /// </summary>
  70. public XRMeshChain()
  71. {
  72. reservedElements = 0;
  73. }
  74. /// <summary>
  75. /// Creates or re-creates the mesh with all the data needed for billboard-pipe based line rendering
  76. /// </summary>
  77. /// <param name="owner">The gameobject that will own the created mesh</param>
  78. /// <param name="dynamic">Whether this mesh is going to updated frequently or not</param>
  79. /// <param name="totalElements">How many total billboards and pipes are needed for this renderer</param>
  80. public void GenerateMesh(GameObject owner, bool dynamic, int totalElements, bool setMesh = true)
  81. {
  82. // Precache neccessary data
  83. // The mesh, vertex and triangle counts
  84. if (m_Mesh == null)
  85. {
  86. m_Mesh = new Mesh();
  87. }
  88. if (dynamic == true)
  89. {
  90. m_Mesh.MarkDynamic();
  91. }
  92. if (setMesh)
  93. owner.GetComponent<MeshFilter>().mesh = m_Mesh;
  94. m_OwnerTransform = owner.transform;
  95. reservedElements = totalElements;
  96. var vertCount = 4 * reservedElements;
  97. var triCount = 6 * reservedElements;
  98. m_Verts = new Vector3[vertCount];
  99. m_Colors = new Color32[vertCount];
  100. m_ShapeData = new List<Vector4>(vertCount);
  101. m_NeighborPoints = new List<Vector3>(vertCount);
  102. var triangles = new int[triCount];
  103. var defaultWhite = new Color32(255, 255, 255, 255);
  104. var uvSet1 = new Vector4(0, 0, 1, 1);
  105. var uvSet2 = new Vector4(1, 0, 1, 1);
  106. var uvSet3 = new Vector4(1, 1, 1, 1);
  107. var uvSet4 = new Vector4(0, 1, 1, 1);
  108. // Set up the basic data for all of our geometry
  109. var pointCounter = 0;
  110. while (pointCounter < reservedElements)
  111. {
  112. // Get where in the various indices we need to write
  113. var vertOffset = pointCounter * 4;
  114. var triOffset = pointCounter * 6;
  115. // Store default color
  116. m_Colors[vertOffset] = defaultWhite;
  117. m_Colors[vertOffset + 1] = defaultWhite;
  118. m_Colors[vertOffset + 2] = defaultWhite;
  119. m_Colors[vertOffset + 3] = defaultWhite;
  120. // Write traditional billboard coordinates
  121. // We use the UV coordinates to determine direction each
  122. // individual vertex will expand in, in screen space
  123. // Last two coordinates are size expansion
  124. m_ShapeData.Add(uvSet1);
  125. m_ShapeData.Add(uvSet2);
  126. m_ShapeData.Add(uvSet3);
  127. m_ShapeData.Add(uvSet4);
  128. // Zero out neighbor points
  129. m_NeighborPoints.Add(Vector3.zero);
  130. m_NeighborPoints.Add(Vector3.zero);
  131. m_NeighborPoints.Add(Vector3.zero);
  132. m_NeighborPoints.Add(Vector3.zero);
  133. // And a proper index buffer for this element
  134. triangles[triOffset] = vertOffset;
  135. triangles[triOffset + 1] = vertOffset + 1;
  136. triangles[triOffset + 2] = vertOffset + 2;
  137. triangles[triOffset + 3] = vertOffset;
  138. triangles[triOffset + 4] = vertOffset + 2;
  139. triangles[triOffset + 5] = vertOffset + 3;
  140. pointCounter++;
  141. }
  142. // Now set any values we can
  143. m_Mesh.triangles = null;
  144. m_Mesh.vertices = m_Verts;
  145. m_Mesh.SetUVs(0, m_ShapeData);
  146. m_Mesh.SetUVs(1, m_NeighborPoints);
  147. m_Mesh.triangles = triangles;
  148. }
  149. /// <summary>
  150. /// Updates any mesh vertex data that is marked as dirty
  151. /// </summary>
  152. public void RefreshMesh()
  153. {
  154. if ((m_DataThatNeedsUpdate & MeshRefreshFlag.Positions) != 0)
  155. {
  156. m_Mesh.vertices = m_Verts;
  157. m_Mesh.SetUVs(1, m_NeighborPoints);
  158. }
  159. if ((m_DataThatNeedsUpdate & MeshRefreshFlag.Colors) != 0)
  160. {
  161. m_Mesh.colors32 = m_Colors;
  162. }
  163. if ((m_DataThatNeedsUpdate & MeshRefreshFlag.Sizes) != 0)
  164. {
  165. m_Mesh.SetUVs(0, m_ShapeData);
  166. }
  167. m_DataThatNeedsUpdate = MeshRefreshFlag.None;
  168. m_Mesh.RecalculateBounds();
  169. if (m_WorldSpaceData == true)
  170. {
  171. var newBounds = m_Mesh.bounds;
  172. newBounds.center = centerAtRoot ? Vector3.zero : m_OwnerTransform.InverseTransformPoint(newBounds.center);
  173. m_Mesh.bounds = newBounds;
  174. }
  175. }
  176. /// <summary>
  177. /// Used by external classes to alert the mesh chain that they have modified its data
  178. /// </summary>
  179. /// <param name="dataThatNeedsUpdate">Which type of data (position, color, size) has been changed</param>
  180. public void SetMeshDataDirty(MeshRefreshFlag dataThatNeedsUpdate)
  181. {
  182. m_DataThatNeedsUpdate |= dataThatNeedsUpdate;
  183. }
  184. /// <summary>
  185. /// Sets the position of a specific point in the chain
  186. /// </summary>
  187. /// <param name="elementIndex">Which control point to update</param>
  188. /// <param name="position">The updated position of the control point</param>
  189. public void SetElementPosition(int elementIndex, ref Vector3 position)
  190. {
  191. var offset = elementIndex * 4;
  192. m_Verts[offset] = position;
  193. m_Verts[offset + 1] = position;
  194. m_Verts[offset + 2] = position;
  195. m_Verts[offset + 3] = position;
  196. m_NeighborPoints[offset] = position;
  197. m_NeighborPoints[offset + 1] = position;
  198. m_NeighborPoints[offset + 2] = position;
  199. m_NeighborPoints[offset + 3] = position;
  200. }
  201. /// <summary>
  202. /// Sets the endpoints of a pipe in the chain - The pipe equivalent of SetElementPosition
  203. /// </summary>
  204. /// <param name="elementIndex">Which control pipe to update</param>
  205. /// <param name="startPoint">The position of the previous control point being connected to</param>
  206. /// <param name="endPoint">The position of the next control point being connected to</param>
  207. public void SetElementPipe(int elementIndex, ref Vector3 startPoint, ref Vector3 endPoint)
  208. {
  209. var offset = elementIndex * 4;
  210. m_Verts[offset] = startPoint;
  211. m_Verts[offset + 1] = startPoint;
  212. m_Verts[offset + 2] = endPoint;
  213. m_Verts[offset + 3] = endPoint;
  214. m_NeighborPoints[offset] = endPoint;
  215. m_NeighborPoints[offset + 1] = endPoint;
  216. m_NeighborPoints[offset + 2] = startPoint;
  217. m_NeighborPoints[offset + 3] = startPoint;
  218. }
  219. /// <summary>
  220. /// Sets the size of the billboard or pipe being rendered
  221. /// </summary>
  222. /// <param name="elementIndex">The index of the control point to update</param>
  223. /// <param name="sizeModification">What the radius or width of the element should be</param>
  224. public void SetElementSize(int elementIndex, float sizeModification)
  225. {
  226. var offset = elementIndex * 4;
  227. m_ShapeData[offset] = new Vector4(0, 0, sizeModification, sizeModification);
  228. m_ShapeData[offset + 1] = new Vector4(1, 0, sizeModification, sizeModification);
  229. m_ShapeData[offset + 2] = new Vector4(1, 1, sizeModification, sizeModification);
  230. m_ShapeData[offset + 3] = new Vector4(0, 1, sizeModification, sizeModification);
  231. }
  232. /// <summary>
  233. /// Sets the size of the pipe being rendered
  234. /// </summary>
  235. /// <param name="elementIndex">The index of the pipe control point to update</param>
  236. /// <param name="startSize">The start size of the pipe</param>
  237. /// <param name="endSize">The end size of the pipe</param>
  238. public void SetElementSize(int elementIndex, float startSize, float endSize)
  239. {
  240. var offset = elementIndex * 4;
  241. m_ShapeData[offset] = new Vector4(0, 0, startSize, endSize);
  242. m_ShapeData[offset + 1] = new Vector4(1, 0, startSize, endSize);
  243. m_ShapeData[offset + 2] = new Vector4(1, 1, endSize, startSize);
  244. m_ShapeData[offset + 3] = new Vector4(0, 1, endSize, startSize);
  245. }
  246. /// <summary>
  247. /// Sets the color of a billboard or pipe in the chain
  248. /// </summary>
  249. /// <param name="elementIndex">The index of the element we are coloring</param>
  250. /// <param name="color">What the color of this element should be</param>
  251. public void SetElementColor(int elementIndex, ref Color color)
  252. {
  253. var offset = elementIndex * 4;
  254. m_Colors[offset] = color;
  255. m_Colors[offset + 1] = m_Colors[offset];
  256. m_Colors[offset + 2] = m_Colors[offset];
  257. m_Colors[offset + 3] = m_Colors[offset];
  258. }
  259. /// <summary>
  260. /// Sets the color of a billboard or pipe in the chain
  261. /// </summary>
  262. /// <param name="elementIndex">The index of the element we are coloring</param>
  263. /// <param name="color">What the color of this element should be</param>
  264. public void SetElementColor32(int elementIndex, ref Color32 color)
  265. {
  266. var offset = elementIndex * 4;
  267. m_Colors[offset] = color;
  268. m_Colors[offset + 1] = color;
  269. m_Colors[offset + 2] = color;
  270. m_Colors[offset + 3] = color;
  271. }
  272. /// <summary>
  273. /// Sets the colors of a pipe in the chain
  274. /// </summary>
  275. /// <param name="elementIndex">The index of the pipe we are coloring</param>
  276. /// <param name="startColor">The color of the startpoint of the pipe</param>
  277. /// <param name="endColor">The color of the endpoint of the pipe</param>
  278. public void SetElementColor(int elementIndex, ref Color startColor, ref Color endColor)
  279. {
  280. var offset = elementIndex * 4;
  281. m_Colors[offset] = startColor;
  282. m_Colors[offset + 1] = m_Colors[offset];
  283. m_Colors[offset + 2] = endColor;
  284. m_Colors[offset + 3] = m_Colors[offset + 2];
  285. }
  286. /// <summary>
  287. /// Sets the colors of a pipe in the chain
  288. /// </summary>
  289. /// <param name="elementIndex">The index of the pipe we are coloring</param>
  290. /// <param name="startColor">The color of the startpoint of the pipe</param>
  291. /// <param name="endColor">The color of the endpoint of the pipe</param>
  292. public void SetElementColor32(int elementIndex, ref Color32 startColor, ref Color32 endColor)
  293. {
  294. var offset = elementIndex * 4;
  295. m_Colors[offset] = startColor;
  296. m_Colors[offset + 1] = m_Colors[offset];
  297. m_Colors[offset + 2] = endColor;
  298. m_Colors[offset + 3] = m_Colors[offset + 2];
  299. }
  300. }
  301. }