ViveNavMesh.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. using UnityEngine;
  2. using UnityEngine.AI;
  3. using UnityEngine.Serialization;
  4. using UnityEngine.Rendering;
  5. using System.Collections.Generic;
  6. /// \brief A version of Unity's baked navmesh that is converted to a (serializable) component. This allows the navmesh
  7. /// used for Vive navigation to be separated form the AI Navmesh. ViveNavMesh also handles the rendering of the
  8. /// NavMesh grid in-game.
  9. [AddComponentMenu("Vive Teleporter/Vive Nav Mesh")]
  10. [ExecuteInEditMode]
  11. public class ViveNavMesh : MonoBehaviour
  12. {
  13. /// Material used for the floor mesh when the user is selecting a point to teleport to
  14. public Material GroundMaterial
  15. {
  16. get { return _GroundMaterial; }
  17. set
  18. {
  19. Material old = _GroundMaterial;
  20. _GroundMaterial = value;
  21. if(_GroundMaterial != null)
  22. _GroundMaterial.SetFloat(AlphaShaderID, GroundAlpha);
  23. if (old != _GroundMaterial)
  24. Cleanup();
  25. }
  26. }
  27. private Material _GroundMaterial;
  28. [FormerlySerializedAs("_GroundMaterial")]
  29. [SerializeField]
  30. private Material _GroundMaterialSource;
  31. /// \brief The alpha (transparency) value of the rendered ground mesh)
  32. /// \sa GroundMaterial
  33. [Range(0,1)]
  34. public float GroundAlpha = 1.0f;
  35. private float LastGroundAlpha = 1.0f;
  36. private int AlphaShaderID = -1;
  37. public int LayerMask
  38. {
  39. get { return _LayerMask; }
  40. set { _LayerMask = value; }
  41. }
  42. [SerializeField]
  43. private int _LayerMask = 0;
  44. public bool IgnoreLayerMask
  45. {
  46. get { return _IgnoreLayerMask; }
  47. set { _IgnoreLayerMask = value; }
  48. }
  49. [SerializeField]
  50. private bool _IgnoreLayerMask = true;
  51. public int QueryTriggerInteraction
  52. {
  53. get { return _QueryTriggerInteraction; }
  54. set { _QueryTriggerInteraction = value; }
  55. }
  56. [SerializeField]
  57. private int _QueryTriggerInteraction = 0;
  58. /// A Mesh that represents the "Selectable" area of the world. This is converted from Unity's NavMesh in ViveNavMeshEditor
  59. public Mesh SelectableMesh
  60. {
  61. get { return _SelectableMesh; }
  62. set { _SelectableMesh = value; Cleanup(); } // Cleanup because we need to change the mesh inside command buffers
  63. }
  64. [SerializeField] [HideInInspector]
  65. private Mesh _SelectableMesh;
  66. /// \brief The border points of SelectableMesh. This is automatically generated in ViveNavMeshEditor.
  67. ///
  68. /// This is an array of Vector3 arrays, where each Vector3 array is the points in a polyline. These polylines combined
  69. /// describe the borders of SelectableMesh. We have to use BorderPointSets instead of a jagged Vector3[][] array because
  70. /// Unity can't serialize jagged arrays for some reason.
  71. public BorderPointSet[] SelectableMeshBorder
  72. {
  73. get { return _SelectableMeshBorder; }
  74. set { _SelectableMeshBorder = value; }
  75. }
  76. [SerializeField] [HideInInspector]
  77. private BorderPointSet[] _SelectableMeshBorder;
  78. [SerializeField] [HideInInspector]
  79. private int _NavAreaMask = ~0; // Initialize to all
  80. public bool IgnoreSlopedSurfaces { get { return _IgnoreSlopedSurfaces; } }
  81. [SerializeField]
  82. private bool _IgnoreSlopedSurfaces = true;
  83. public float SampleRadius
  84. {
  85. get { return _SampleRadius; }
  86. }
  87. [SerializeField]
  88. private float _SampleRadius = 0.25f;
  89. public NavmeshDewarpingMethod DewarpingMethod { get { return _DewarpingMethod; } }
  90. [SerializeField]
  91. private NavmeshDewarpingMethod _DewarpingMethod = NavmeshDewarpingMethod.None;
  92. private Dictionary<Camera, CommandBuffer> cameras = new Dictionary<Camera, CommandBuffer>();
  93. void Start () {
  94. if (SelectableMesh == null)
  95. SelectableMesh = new Mesh();
  96. if (_SelectableMeshBorder == null)
  97. _SelectableMeshBorder = new BorderPointSet[0];
  98. AlphaShaderID = Shader.PropertyToID("_Alpha");
  99. if(_GroundMaterialSource != null)
  100. GroundMaterial = new Material(_GroundMaterialSource);
  101. if (GroundAlpha != LastGroundAlpha && GroundMaterial != null)
  102. {
  103. GroundMaterial.SetFloat(AlphaShaderID, GroundAlpha);
  104. LastGroundAlpha = GroundAlpha;
  105. }
  106. #if UNITY_EDITOR
  107. UnityEditor.SceneView.RepaintAll();
  108. #endif
  109. }
  110. private void Cleanup()
  111. {
  112. foreach (var cam in cameras)
  113. {
  114. if (cam.Key)
  115. {
  116. cam.Key.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, cam.Value);
  117. }
  118. }
  119. cameras.Clear();
  120. }
  121. public void OnEnable()
  122. {
  123. Cleanup();
  124. }
  125. public void OnDisable()
  126. {
  127. Cleanup();
  128. }
  129. /// \brief Casts a ray against the Navmesh and attempts to calculate the ray's worldspace intersection with it.
  130. ///
  131. /// This uses Physics raycasts to perform the raycast calculation, so the teleport surface must have a collider
  132. /// on it.
  133. ///
  134. /// \param p1 First (origin) point of ray
  135. /// \param p2 Last (end) point of ray
  136. /// \param pointOnNavmesh If the raycast hit something on the navmesh.
  137. /// \param hitPoint If hit, the point of the hit. Otherwise zero.
  138. /// \param normal If hit, the normal of the hit surface. Otherwise (0, 1, 0)
  139. ///
  140. /// \return If the raycast hit something.
  141. public bool Linecast(Vector3 p1, Vector3 p2, out bool pointOnNavmesh, out Vector3 hitPoint, out Vector3 normal)
  142. {
  143. RaycastHit hit;
  144. Vector3 dir = p2 - p1;
  145. float dist = dir.magnitude;
  146. dir /= dist;
  147. if(Physics.Raycast(p1, dir, out hit, dist, _IgnoreLayerMask ? ~_LayerMask : _LayerMask, (QueryTriggerInteraction) _QueryTriggerInteraction))
  148. {
  149. normal = hit.normal;
  150. if (Vector3.Dot(Vector3.up, hit.normal) < 0.99f && _IgnoreSlopedSurfaces)
  151. {
  152. pointOnNavmesh = false;
  153. hitPoint = hit.point;
  154. return true;
  155. }
  156. hitPoint = hit.point;
  157. NavMeshHit navHit;
  158. pointOnNavmesh = NavMesh.SamplePosition(hitPoint, out navHit, _SampleRadius, _NavAreaMask);
  159. return true;
  160. }
  161. pointOnNavmesh = false;
  162. hitPoint = Vector3.zero;
  163. normal = Vector3.up;
  164. return false;
  165. }
  166. }
  167. [System.Serializable]
  168. public class BorderPointSet
  169. {
  170. public Vector3[] Points;
  171. public BorderPointSet(Vector3[] Points)
  172. {
  173. this.Points = Points;
  174. }
  175. }
  176. [System.Serializable]
  177. public enum NavmeshDewarpingMethod
  178. {
  179. None, RoundToVoxelSize, RaycastDownward
  180. }