NavmeshAdd.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. using UnityEngine;
  2. namespace Pathfinding {
  3. /// <summary>
  4. /// Adds new geometry to a recast graph.
  5. ///
  6. /// This component will add new geometry to a recast graph similar
  7. /// to how a NavmeshCut component removes it.
  8. ///
  9. /// There are quite a few limitations to this component though.
  10. /// This navmesh geometry will not be connected to the rest of the navmesh
  11. /// in the same tile unless very exactly positioned so that the
  12. /// triangles line up exactly.
  13. /// It will be connected to neighbouring tiles if positioned so that
  14. /// it lines up with the tile border.
  15. ///
  16. /// This component has a few very specific use-cases.
  17. /// For example if you have a tiled recast graph
  18. /// this component could be used to add bridges
  19. /// in that world.
  20. /// You would create a NavmeshCut object cutting out a hole for the bridge.
  21. /// then add a NavmeshAdd object which fills that space.
  22. /// Make sure NavmeshCut.CutsAddedGeom is disabled on the NavmeshCut, otherwise it will
  23. /// cut away the NavmeshAdd object.
  24. /// Then you can add links between the added geometry and the rest of the world, preferably using NodeLink3.
  25. /// </summary>
  26. [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_navmesh_add.php")]
  27. public class NavmeshAdd : NavmeshClipper {
  28. public enum MeshType {
  29. Rectangle,
  30. CustomMesh
  31. }
  32. public MeshType type;
  33. /// <summary>
  34. /// Custom mesh to use.
  35. /// The contour(s) of the mesh will be extracted.
  36. /// If you get the "max perturbations" error when cutting with this, check the normals on the mesh.
  37. /// They should all point in the same direction. Try flipping them if that does not help.
  38. /// </summary>
  39. public Mesh mesh;
  40. /// <summary>Cached vertices</summary>
  41. Vector3[] verts;
  42. /// <summary>Cached triangles</summary>
  43. int[] tris;
  44. /// <summary>Size of the rectangle</summary>
  45. public Vector2 rectangleSize = new Vector2(1, 1);
  46. public float meshScale = 1;
  47. public Vector3 center;
  48. /// <summary>
  49. /// Includes rotation and scale in calculations.
  50. /// This is slower since a lot more matrix multiplications are needed but gives more flexibility.
  51. /// </summary>
  52. [UnityEngine.Serialization.FormerlySerializedAsAttribute("useRotation")]
  53. public bool useRotationAndScale;
  54. /// <summary>
  55. /// Distance between positions to require an update of the navmesh.
  56. /// A smaller distance gives better accuracy, but requires more updates when moving the object over time,
  57. /// so it is often slower.
  58. /// </summary>
  59. [Tooltip("Distance between positions to require an update of the navmesh\nA smaller distance gives better accuracy, but requires more updates when moving the object over time, so it is often slower.")]
  60. public float updateDistance = 0.4f;
  61. /// <summary>
  62. /// How many degrees rotation that is required for an update to the navmesh.
  63. /// Should be between 0 and 180.
  64. /// </summary>
  65. [Tooltip("How many degrees rotation that is required for an update to the navmesh. Should be between 0 and 180.")]
  66. public float updateRotationDistance = 10;
  67. /// <summary>cached transform component</summary>
  68. protected Transform tr;
  69. Vector3 lastPosition;
  70. Quaternion lastRotation;
  71. /// <summary>
  72. /// Returns true if this object has moved so much that it requires an update.
  73. /// When an update to the navmesh has been done, call NotifyUpdated to be able to get
  74. /// relavant output from this method again.
  75. /// </summary>
  76. public override bool RequiresUpdate () {
  77. return (tr.position-lastPosition).sqrMagnitude > updateDistance*updateDistance || (useRotationAndScale && (Quaternion.Angle(lastRotation, tr.rotation) > updateRotationDistance));
  78. }
  79. /// <summary>
  80. /// Forces this navmesh add to update the navmesh.
  81. ///
  82. /// This update is not instant, it is done the next time it is checked if it needs updating.
  83. /// See: <see cref="Pathfinding.NavmeshUpdates.updateInterval"/>
  84. /// See: <see cref="Pathfinding.NavmeshUpdates.ForceUpdate()"/>
  85. /// </summary>
  86. public override void ForceUpdate () {
  87. lastPosition = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
  88. }
  89. protected override void Awake () {
  90. base.Awake();
  91. tr = transform;
  92. }
  93. /// <summary>Internal method to notify the NavmeshAdd that it has just been used to update the navmesh</summary>
  94. internal override void NotifyUpdated () {
  95. lastPosition = tr.position;
  96. if (useRotationAndScale) {
  97. lastRotation = tr.rotation;
  98. }
  99. }
  100. public Vector3 Center {
  101. get {
  102. return tr.position + (useRotationAndScale ? tr.TransformPoint(center) : center);
  103. }
  104. }
  105. [ContextMenu("Rebuild Mesh")]
  106. public void RebuildMesh () {
  107. if (type == MeshType.CustomMesh) {
  108. if (mesh == null) {
  109. verts = null;
  110. tris = null;
  111. } else {
  112. verts = mesh.vertices;
  113. tris = mesh.triangles;
  114. }
  115. } else { // Rectangle
  116. if (verts == null || verts.Length != 4 || tris == null || tris.Length != 6) {
  117. verts = new Vector3[4];
  118. tris = new int[6];
  119. }
  120. tris[0] = 0;
  121. tris[1] = 1;
  122. tris[2] = 2;
  123. tris[3] = 0;
  124. tris[4] = 2;
  125. tris[5] = 3;
  126. verts[0] = new Vector3(-rectangleSize.x*0.5f, 0, -rectangleSize.y*0.5f);
  127. verts[1] = new Vector3(rectangleSize.x*0.5f, 0, -rectangleSize.y*0.5f);
  128. verts[2] = new Vector3(rectangleSize.x*0.5f, 0, rectangleSize.y*0.5f);
  129. verts[3] = new Vector3(-rectangleSize.x*0.5f, 0, rectangleSize.y*0.5f);
  130. }
  131. }
  132. /// <summary>
  133. /// Bounds in XZ space after transforming using the *inverse* transform of the inverseTransform parameter.
  134. /// The transformation will typically transform the vertices to graph space and this is used to
  135. /// figure out which tiles the add intersects.
  136. /// </summary>
  137. public override Rect GetBounds (Pathfinding.Util.GraphTransform inverseTransform) {
  138. if (this.verts == null) RebuildMesh();
  139. var verts = Pathfinding.Util.ArrayPool<Int3>.Claim(this.verts != null? this.verts.Length : 0);
  140. int[] tris;
  141. GetMesh(ref verts, out tris, inverseTransform);
  142. Rect r = new Rect();
  143. for (int i = 0; i < tris.Length; i++) {
  144. var p = (Vector3)verts[tris[i]];
  145. if (i == 0) {
  146. r = new Rect(p.x, p.z, 0, 0);
  147. } else {
  148. r.xMax = System.Math.Max(r.xMax, p.x);
  149. r.yMax = System.Math.Max(r.yMax, p.z);
  150. r.xMin = System.Math.Min(r.xMin, p.x);
  151. r.yMin = System.Math.Min(r.yMin, p.z);
  152. }
  153. }
  154. Pathfinding.Util.ArrayPool<Int3>.Release(ref verts);
  155. return r;
  156. }
  157. /// <summary>Copy the mesh to the vertex and triangle buffers after the vertices have been transformed using the inverse of the inverseTransform parameter.</summary>
  158. /// <param name="vbuffer">Assumed to be either null or an array which has a length of zero or a power of two. If this mesh has more
  159. /// vertices than can fit in the buffer then the buffer will be pooled using Pathfinding.Util.ArrayPool.Release and
  160. /// a new sufficiently large buffer will be taken from the pool.</param>
  161. /// <param name="tbuffer">This will be set to the internal triangle buffer. You must not modify this array.</param>
  162. /// <param name="inverseTransform">All vertices will be transformed using the #Pathfinding.GraphTransform.InverseTransform method.
  163. /// This is typically used to transform from world space to graph space.</param>
  164. public void GetMesh (ref Int3[] vbuffer, out int[] tbuffer, Pathfinding.Util.GraphTransform inverseTransform = null) {
  165. if (verts == null) RebuildMesh();
  166. if (verts == null) {
  167. tbuffer = Util.ArrayPool<int>.Claim(0);
  168. return;
  169. }
  170. if (vbuffer == null || vbuffer.Length < verts.Length) {
  171. if (vbuffer != null) Util.ArrayPool<Int3>.Release(ref vbuffer);
  172. vbuffer = Util.ArrayPool<Int3>.Claim(verts.Length);
  173. }
  174. tbuffer = tris;
  175. if (useRotationAndScale) {
  176. Matrix4x4 m = Matrix4x4.TRS(tr.position + center, tr.rotation, tr.localScale * meshScale);
  177. for (int i = 0; i < verts.Length; i++) {
  178. var v = m.MultiplyPoint3x4(verts[i]);
  179. if (inverseTransform != null) v = inverseTransform.InverseTransform(v);
  180. vbuffer[i] = (Int3)v;
  181. }
  182. } else {
  183. Vector3 voffset = tr.position + center;
  184. for (int i = 0; i < verts.Length; i++) {
  185. var v = voffset + verts[i]*meshScale;
  186. if (inverseTransform != null) v = inverseTransform.InverseTransform(v);
  187. vbuffer[i] = (Int3)v;
  188. }
  189. }
  190. }
  191. public static readonly Color GizmoColor = new Color(154.0f/255, 35.0f/255, 239.0f/255);
  192. #if UNITY_EDITOR
  193. public static Int3[] gizmoBuffer;
  194. public void OnDrawGizmos () {
  195. if (tr == null) tr = transform;
  196. int[] tbuffer;
  197. GetMesh(ref gizmoBuffer, out tbuffer);
  198. Gizmos.color = GizmoColor;
  199. for (int i = 0; i < tbuffer.Length; i += 3) {
  200. var v1 = (Vector3)gizmoBuffer[tbuffer[i+0]];
  201. var v2 = (Vector3)gizmoBuffer[tbuffer[i+1]];
  202. var v3 = (Vector3)gizmoBuffer[tbuffer[i+2]];
  203. Gizmos.DrawLine(v1, v2);
  204. Gizmos.DrawLine(v2, v3);
  205. Gizmos.DrawLine(v3, v1);
  206. }
  207. }
  208. #endif
  209. }
  210. }