public void SetState (bool open) {
            this.open = open;

            if (updateGraphsWithGUO) {
                // Update the graph below the door
                // Set the tag of the nodes below the door
                // To something indicating that the door is open or closed
                GraphUpdateObject guo = new GraphUpdateObject(bounds);
                int tag = open ? opentag : closedtag;

                // There are only 32 tags
                if (tag > 31) { Debug.LogError ("tag > 31"); return; }

                guo.modifyTag = true;
                guo.setTag = tag;
                guo.updatePhysics = false;

                AstarPath.active.UpdateGraphs (guo);
            }

            // Play door animations
            if (open) {
                GetComponent<Animation>().Play ("Open");
            } else {
                GetComponent<Animation>().Play ("Close");
            }
        }
		/** Updates graphs and checks if all nodes are still reachable from each other.
		 * Graphs are updated, then a check is made to see if the nodes are still reachable from each other.
		 * If they are not, the graphs are reverted to before the update and \a false is returned.\n
		 * This is slower than a normal graph update.
		 * All queued graph updates and thread safe callbacks will be flushed during this function.
		 *
		 * \note This might return true for small areas even if there is no possible path if AstarPath.minAreaSize is greater than zero (0).
		 * So when using this, it is recommended to set AstarPath.minAreaSize to 0 (A* Inspector -> Settings -> Pathfinding)
		 *
		 * \param guo The GraphUpdateObject to update the graphs with
		 * \param node1 Node which should have a valid path to \a node2. All nodes should be walkable or \a false will be returned.
		 * \param node2 Node which should have a valid path to \a node1. All nodes should be walkable or \a false will be returned.
		 * \param alwaysRevert If true, reverts the graphs to the old state even if no blocking ocurred
		 *
		 * \returns True if the given nodes are still reachable from each other after the \a guo has been applied. False otherwise.
		 *
\code
var guo = new GraphUpdateObject (tower.GetComponent<Collider>.bounds);
var spawnPointNode = AstarPath.active.GetNearest (spawnPoint.position).node;
var goalNode = AstarPath.active.GetNearest (goalNode.position).node;
if (GraphUpdateUtilities.UpdateGraphsNoBlock (guo, spawnPointNode, goalNode, false)) {
	// Valid tower position
	// Since the last parameter (which is called "alwaysRevert") in the method call was false
	// The graph is now updated and the game can just continue
} else {
	// Invalid tower position. It blocks the path between the spawn point and the goal
	// The effect on the graph has been reverted
	Destroy (tower);
}
\endcode
		 */
		public static bool UpdateGraphsNoBlock (GraphUpdateObject guo, GraphNode node1, GraphNode node2, bool alwaysRevert = false) {
			List<GraphNode> buffer = ListPool<GraphNode>.Claim ();
			buffer.Add (node1);
			buffer.Add (node2);

			bool worked = UpdateGraphsNoBlock (guo, buffer, alwaysRevert);
			ListPool<GraphNode>.Release (buffer);
			return worked;
		}
Example #3
0
        public void RemoveObject () {
            Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
            RaycastHit hit;
            if ( Physics.Raycast (ray, out hit, Mathf.Infinity)) {
                if (hit.collider.isTrigger || hit.transform.gameObject.name == "Ground") return;
			
                Bounds b = hit.collider.bounds;
                Destroy (hit.collider);
                Destroy (hit.collider.gameObject);
			
                //Pathfinding.Console.Write ("// Placing Object\n");
                if (issueGUOs) {
                    GraphUpdateObject guo = new GraphUpdateObject(b);
                    AstarPath.active.UpdateGraphs (guo,0.0f);
                    if (direct) {
                        //Pathfinding.Console.Write ("// Flushing\n");
                        AstarPath.active.FlushGraphUpdates();
                    }
                }
            }
        }
Example #4
0
        public void PlaceObject () {
		
            Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
            RaycastHit hit;
            if ( Physics.Raycast (ray, out hit, Mathf.Infinity)) {
                Vector3 p = hit.point;
			
                GameObject obj = (GameObject)GameObject.Instantiate (go,p,Quaternion.identity);
			
                if (issueGUOs) {
                    Bounds b = obj.GetComponent<Collider>().bounds;
                    //Pathfinding.Console.Write ("// Placing Object\n");
                    GraphUpdateObject guo = new GraphUpdateObject(b);
                    AstarPath.active.UpdateGraphs (guo);
                    if (direct) {
                        //Pathfinding.Console.Write ("// Flushing\n");
                        AstarPath.active.FlushGraphUpdates();
                    }
                }
            }
        }
		/** Updates graphs and checks if all nodes are still reachable from each other.
		 * Graphs are updated, then a check is made to see if the nodes are still reachable from each other.
		 * If they are not, the graphs are reverted to before the update and \a false is returned.
		 * This is slower than a normal graph update.
		 * All queued graph updates and thread safe callbacks will be flushed during this function.
		 *
		 * \note This might return true for small areas even if there is no possible path if AstarPath.minAreaSize is greater than zero (0).
		 * So when using this, it is recommended to set AstarPath.minAreaSize to 0. (A* Inspector -> Settings -> Pathfinding)
		 *
		 * \param guo The GraphUpdateObject to update the graphs with
		 * \param nodes Nodes which should have valid paths between them. All nodes should be walkable or \a false will be returned.
		 * \param alwaysRevert If true, reverts the graphs to the old state even if no blocking ocurred
		 *
		 * \returns True if the given nodes are still reachable from each other after the \a guo has been applied. False otherwise.
		 */
		public static bool UpdateGraphsNoBlock (GraphUpdateObject guo, List<GraphNode> nodes, bool alwaysRevert = false) {

			//Make sure all nodes are walkable
			for (int i=0;i<nodes.Count;i++) if (!nodes[i].Walkable) return false;

			//Track changed nodes to enable reversion of the guo
			guo.trackChangedNodes = true;
			bool worked = true;

			AstarPath.RegisterSafeUpdate (delegate () {

				AstarPath.active.UpdateGraphs (guo);

				//Call thread safe callbacks, includes graph updates
				AstarPath.active.FlushGraphUpdates();

				//Check if all nodes are in the same area and that they are walkable, i.e that there are paths between all of them
				worked = worked && PathUtilities.IsPathPossible (nodes);

				//If it did not work, revert the GUO
				if (!worked || alwaysRevert) {
					guo.RevertFromBackup ();

					//The revert operation does not revert ALL nodes' area values, so we must flood fill again
					AstarPath.active.FloodFill ();
				}
			});

			//Force the thread safe callback to be called
			AstarPath.active.FlushThreadSafeCallbacks();

			//Disable tracking nodes, not strictly necessary, but will slightly reduce the cance that some user causes errors
			guo.trackChangedNodes = false;

			return worked;
		}
Example #6
0
		/** Internal function to update an area of the graph */
		public void UpdateArea (GraphUpdateObject o) {

			if (nodes == null || nodes.Length != width*depth) {
				Debug.LogWarning ("The Grid Graph is not scanned, cannot update area ");
				//Not scanned
				return;
			}

			//Copy the bounds
			Bounds b = o.bounds;

			// Take the bounds and transform it using the matrix
			// Then convert that to a rectangle which contains
			// all nodes that might be inside the bounds
			Vector3 min, max;
			GetBoundsMinMax (b,inverseMatrix,out min, out max);

			int minX = Mathf.RoundToInt (min.x-0.5F);
			int maxX = Mathf.RoundToInt (max.x-0.5F);

			int minZ = Mathf.RoundToInt (min.z-0.5F);
			int maxZ = Mathf.RoundToInt (max.z-0.5F);

			//We now have coordinates in local space (i.e 1 unit = 1 node)
			var originalRect = new IntRect(minX,minZ,maxX,maxZ);
			var affectRect = originalRect;

			// Rect which covers the whole grid
			var gridRect = new IntRect(0,0,width-1,depth-1);

			var physicsRect = originalRect;

			int erosion = o.updateErosion ? erodeIterations : 0;

#if ASTARDEBUG
			Matrix4x4 debugMatrix = matrix;
			debugMatrix *= Matrix4x4.TRS (new Vector3(0.5f,0,0.5f),Quaternion.identity,Vector3.one);

			originalRect.DebugDraw (debugMatrix,Color.red);
#endif

			bool willChangeWalkability = o.updatePhysics || o.modifyWalkability;

			//Calculate the largest bounding box which might be affected

			if (o.updatePhysics && !o.modifyWalkability) {
				// Enqueue the collision.diameter margin for physics calls
				if (collision.collisionCheck) {
					Vector3 margin = new Vector3 (collision.diameter,0,collision.diameter)*0.5F;

					min -= margin*1.02F;//0.02 safety margin, physics is rarely very accurate
					max += margin*1.02F;

					physicsRect = new IntRect(
					                            Mathf.RoundToInt (min.x-0.5F),
					                            Mathf.RoundToInt (min.z-0.5F),
					                            Mathf.RoundToInt (max.x-0.5F),
					                            Mathf.RoundToInt (max.z-0.5F)
					                            );

					affectRect = IntRect.Union (physicsRect, affectRect);
				}
			}

			if (willChangeWalkability || erosion > 0) {
				// Enqueue affect radius for erosion. +1 for updating connectivity info at the border
				affectRect = affectRect.Expand (erosion + 1);
			}

			// Clamp the rect to the grid bounds
			IntRect clampedRect = IntRect.Intersection (affectRect,gridRect);

			// Mark nodes that might be changed
			for (int x = clampedRect.xmin; x <= clampedRect.xmax;x++) {
				for (int z = clampedRect.ymin;z <= clampedRect.ymax;z++) {
					o.WillUpdateNode (nodes[z*width+x]);
				}
			}

			// Update Physics
			if (o.updatePhysics && !o.modifyWalkability) {

				collision.Initialize (matrix,nodeSize);

				clampedRect = IntRect.Intersection (physicsRect,gridRect);

				for (int x = clampedRect.xmin; x <= clampedRect.xmax;x++) {
					for (int z = clampedRect.ymin;z <= clampedRect.ymax;z++) {

						int index = z*width+x;

						GridNode node = nodes[index];

						UpdateNodePositionCollision (node,x,z, o.resetPenaltyOnPhysics);
					}
				}
			}

			//Apply GUO

			clampedRect = IntRect.Intersection (originalRect, gridRect);
			for (int x = clampedRect.xmin; x <= clampedRect.xmax;x++) {
				for (int z = clampedRect.ymin;z <= clampedRect.ymax;z++) {
					int index = z*width+x;

					GridNode node = nodes[index];

					if (willChangeWalkability) {
						node.Walkable = node.WalkableErosion;
						if (o.bounds.Contains ((Vector3)node.position)) o.Apply (node);
						node.WalkableErosion = node.Walkable;
					} else {
						if (o.bounds.Contains ((Vector3)node.position)) o.Apply (node);
					}
				}
			}

#if ASTARDEBUG
			physicsRect.DebugDraw (debugMatrix,Color.blue);
			affectRect.DebugDraw (debugMatrix,Color.black);
#endif

			// Recalculate connections
			if (willChangeWalkability && erosion == 0) {

				clampedRect = IntRect.Intersection (affectRect, gridRect);
				for (int x = clampedRect.xmin; x <= clampedRect.xmax;x++) {
					for (int z = clampedRect.ymin;z <= clampedRect.ymax;z++) {
						int index = z*width+x;

						GridNode node = nodes[index];

						CalculateConnections (nodes,x,z,node);
					}
				}
			} else if (willChangeWalkability && erosion > 0) {


				clampedRect = IntRect.Union (originalRect, physicsRect);

				IntRect erosionRect1 = clampedRect.Expand (erosion);
				IntRect erosionRect2 = erosionRect1.Expand (erosion);

				erosionRect1 = IntRect.Intersection (erosionRect1,gridRect);
				erosionRect2 = IntRect.Intersection (erosionRect2,gridRect);

#if ASTARDEBUG
				erosionRect1.DebugDraw (debugMatrix,Color.magenta);
				erosionRect2.DebugDraw (debugMatrix,Color.cyan);
#endif


				// * all nodes inside clampedRect might have had their walkability changed
				// * all nodes inside erosionRect1 might get affected by erosion from clampedRect and erosionRect2
				// * all nodes inside erosionRect2 (but outside erosionRect1) will be reset to previous walkability
				//     after calculation since their erosion might not be correctly calculated (nodes outside erosionRect2 might have an effect)

				for (int x = erosionRect2.xmin; x <= erosionRect2.xmax;x++) {
					for (int z = erosionRect2.ymin;z <= erosionRect2.ymax;z++) {

						int index = z*width+x;

						GridNode node = nodes[index];

						bool tmp = node.Walkable;
						node.Walkable = node.WalkableErosion;

						if (!erosionRect1.Contains (x,z)) {
							//Save the border's walkabilty data (will be reset later)
							node.TmpWalkable = tmp;
						}
					}
				}

				for (int x = erosionRect2.xmin; x <= erosionRect2.xmax;x++) {
					for (int z = erosionRect2.ymin;z <= erosionRect2.ymax;z++) {
						int index = z*width+x;

						GridNode node = nodes[index];

						CalculateConnections (nodes,x,z,node);
					}
				}

				// Erode the walkable area
				ErodeWalkableArea (erosionRect2.xmin,erosionRect2.ymin,erosionRect2.xmax+1,erosionRect2.ymax+1);

				for (int x = erosionRect2.xmin; x <= erosionRect2.xmax;x++) {
					for (int z = erosionRect2.ymin;z <= erosionRect2.ymax;z++) {
						if (erosionRect1.Contains (x,z)) continue;

						int index = z*width+x;

						GridNode node = nodes[index];

						//Restore temporarily stored data
						node.Walkable = node.TmpWalkable;
					}
				}

				// Recalculate connections of all affected nodes
				for (int x = erosionRect2.xmin; x <= erosionRect2.xmax;x++) {
					for (int z = erosionRect2.ymin;z <= erosionRect2.ymax;z++) {
						int index = z*width+x;

						GridNode node = nodes[index];
						CalculateConnections (nodes,x,z,node);
					}
				}
			}
		}
Example #7
0
		public void UpdateAreaInit (GraphUpdateObject o) {}
Example #8
0
		public GraphUpdateThreading CanUpdateAsync (GraphUpdateObject o) {
			return GraphUpdateThreading.UnityThread;
		}
		public new void UpdateArea (GraphUpdateObject o) {

			if (nodes == null || nodes.Length != width*depth*layerCount) {
				Debug.LogWarning ("The Grid Graph is not scanned, cannot update area ");
				//Not scanned
				return;
			}

			//Copy the bounds
			Bounds b = o.bounds;

			//Matrix inverse
			//node.position = matrix.MultiplyPoint3x4 (new Vector3 (x+0.5F,0,z+0.5F));

			Vector3 min, max;
			GetBoundsMinMax (b,inverseMatrix,out min, out max);

			int minX = Mathf.RoundToInt (min.x-0.5F);
			int maxX = Mathf.RoundToInt (max.x-0.5F);

			int minZ = Mathf.RoundToInt (min.z-0.5F);
			int maxZ = Mathf.RoundToInt (max.z-0.5F);
			//We now have coordinates in local space (i.e 1 unit = 1 node)

			var originalRect = new IntRect(minX,minZ,maxX,maxZ);
			var affectRect = originalRect;

			var gridRect = new IntRect(0,0,width-1,depth-1);

			var physicsRect = originalRect;

#if ASTARDEBUG
			Matrix4x4 debugMatrix = matrix;
			debugMatrix *= Matrix4x4.TRS (new Vector3(0.5f,0,0.5f),Quaternion.identity,Vector3.one);

			originalRect.DebugDraw (debugMatrix,Color.red);
#endif

			bool willChangeWalkability = o.updatePhysics || o.modifyWalkability;

			bool willChangeNodeInstances = (o is LayerGridGraphUpdate && ((LayerGridGraphUpdate)o).recalculateNodes);
			bool preserveExistingNodes = (o is LayerGridGraphUpdate ? ((LayerGridGraphUpdate)o).preserveExistingNodes : true);

			int erosion = o.updateErosion ? erodeIterations : 0;

			if (o.trackChangedNodes	&& willChangeNodeInstances) {
				Debug.LogError ("Cannot track changed nodes when creating or deleting nodes.\nWill not update LayerGridGraph");
				return;
			}

			//Calculate the largest bounding box which might be affected

			if (o.updatePhysics && !o.modifyWalkability) {
				//Enqueue the collision.diameter margin for physics calls
				if (collision.collisionCheck) {
					Vector3 margin = new Vector3 (collision.diameter,0,collision.diameter)*0.5F;

					min -= margin*1.02F;//0.02 safety margin, physics is rarely very accurate
					max += margin*1.02F;

					physicsRect = new IntRect(
					                            Mathf.RoundToInt (min.x-0.5F),
					                            Mathf.RoundToInt (min.z-0.5F),
					                            Mathf.RoundToInt (max.x-0.5F),
					                            Mathf.RoundToInt (max.z-0.5F)
					                            );

					affectRect = IntRect.Union (physicsRect, affectRect);
				}
			}

			if (willChangeWalkability || erosion > 0) {
				//Enqueue affect radius for erosion. +1 for updating connectivity info at the border
				affectRect = affectRect.Expand (erosion + 1);
			}

			IntRect clampedRect = IntRect.Intersection (affectRect,gridRect);

			//Mark nodes that might be changed
			if (!willChangeNodeInstances) {
				for (int x = clampedRect.xmin; x <= clampedRect.xmax;x++) {
					for (int z = clampedRect.ymin;z <= clampedRect.ymax;z++) {
						for (int y=0;y<layerCount;y++) {
							o.WillUpdateNode (nodes[y*width*depth + z*width+x]);
						}
					}
				}
			}

			//Update Physics
			if (o.updatePhysics && !o.modifyWalkability) {

				collision.Initialize (matrix,nodeSize);

				clampedRect = IntRect.Intersection (physicsRect,gridRect);

				bool addedNodes = false;

				for (int x = clampedRect.xmin; x <= clampedRect.xmax;x++) {
					for (int z = clampedRect.ymin;z <= clampedRect.ymax;z++) {
						/** \todo FIX */
						addedNodes |= RecalculateCell (x,z,preserveExistingNodes);
					}
				}

				for (int x = clampedRect.xmin; x <= clampedRect.xmax;x++) {
					for (int z = clampedRect.ymin;z <= clampedRect.ymax;z++) {
						for (int y=0;y<layerCount;y++) {
							int index = y*width*depth + z*width+x;

							var node = nodes[index];

							if (node == null) continue;

							CalculateConnections (nodes,node,x,z,y);
						}
					}
				}
			}

			//Apply GUO

			clampedRect = IntRect.Intersection (originalRect, gridRect);
			for (int x = clampedRect.xmin; x <= clampedRect.xmax;x++) {
				for (int z = clampedRect.ymin;z <= clampedRect.ymax;z++) {
					for (int y=0;y<layerCount;y++) {
						int index = y*width*depth + z*width+x;

						var node = nodes[index];

						if (node == null) continue;

						if (willChangeWalkability) {
							node.Walkable = node.WalkableErosion;
							if (o.bounds.Contains ((Vector3)node.position)) o.Apply (node);
							node.WalkableErosion = node.Walkable;
						} else {
							if (o.bounds.Contains ((Vector3)node.position)) o.Apply (node);
						}
					}
				}
			}

#if ASTARDEBUG
			physicsRect.DebugDraw (debugMatrix,Color.blue);
			affectRect.DebugDraw (debugMatrix,Color.black);
#endif

			//Recalculate connections
			if (willChangeWalkability && erosion == 0) {

				clampedRect = IntRect.Intersection (affectRect, gridRect);
				for (int x = clampedRect.xmin; x <= clampedRect.xmax;x++) {
					for (int z = clampedRect.ymin;z <= clampedRect.ymax;z++) {
						for (int y=0;y<layerCount;y++) {
							int index = y*width*depth + z*width+x;

							var node = nodes[index];

							if (node == null) continue;

							CalculateConnections (nodes,node,x,z,y);
						}
					}
				}
			} else if (willChangeWalkability && erosion > 0) {


				clampedRect = IntRect.Union (originalRect, physicsRect);

				IntRect erosionRect1 = clampedRect.Expand (erosion);
				IntRect erosionRect2 = erosionRect1.Expand (erosion);

				erosionRect1 = IntRect.Intersection (erosionRect1,gridRect);
				erosionRect2 = IntRect.Intersection (erosionRect2,gridRect);

#if ASTARDEBUG
				erosionRect1.DebugDraw (debugMatrix,Color.magenta);
				erosionRect2.DebugDraw (debugMatrix,Color.cyan);
#endif

				/*
				all nodes inside clampedRect might have had their walkability changed
				all nodes inside erosionRect1 might get affected by erosion from clampedRect and erosionRect2
				all nodes inside erosionRect2 (but outside erosionRect1) will be reset to previous walkability
				after calculation since their erosion might not be correctly calculated (nodes outside erosionRect2 would maybe have effect)
				*/

				for (int x = erosionRect2.xmin; x <= erosionRect2.xmax;x++) {
					for (int z = erosionRect2.ymin;z <= erosionRect2.ymax;z++) {
						for (int y=0;y<layerCount;y++) {
							int index = y*width*depth + z*width+x;

							var node = nodes[index];

							if (node == null) continue;

							bool tmp = node.Walkable;
							node.Walkable = node.WalkableErosion;

							if (!erosionRect1.Contains (x,z)) {
								//Save the border's walkabilty data in bit 16 (will be reset later)
								node.TmpWalkable = tmp;
							}
						}
					}
				}

				for (int x = erosionRect2.xmin; x <= erosionRect2.xmax;x++) {
					for (int z = erosionRect2.ymin;z <= erosionRect2.ymax;z++) {
						for (int y=0;y<layerCount;y++) {
							int index = y*width*depth + z*width+x;

							var node = nodes[index];

							if (node == null) continue;

#if ASTARDEBUG
							if (!node.Walkable)
								Debug.DrawRay ((Vector3)node.position, Vector3.up*2,Color.red);
#endif
							CalculateConnections (nodes,node,x,z,y);
						}
					}
				}

				// Erode the walkable area
				ErodeWalkableArea (erosionRect2.xmin,erosionRect2.ymin,erosionRect2.xmax+1,erosionRect2.ymax+1);

				for (int x = erosionRect2.xmin; x <= erosionRect2.xmax;x++) {
					for (int z = erosionRect2.ymin;z <= erosionRect2.ymax;z++) {
						if (erosionRect1.Contains (x,z)) continue;

						for (int y=0;y<layerCount;y++) {
							int index = y*width*depth + z*width+x;

							var node = nodes[index];

							if (node == null) continue;

							// Restore temporarily stored data
							node.Walkable = node.TmpWalkable;
						}
					}
				}

				// Recalculate connections of all affected nodes
				for (int x = erosionRect2.xmin; x <= erosionRect2.xmax;x++) {
					for (int z = erosionRect2.ymin;z <= erosionRect2.ymax;z++) {
						for (int y=0;y<layerCount;y++) {
							int index = y*width*depth + z*width+x;

							var node = nodes[index];

							if (node == null) continue;

							CalculateConnections (nodes,node,x,z,y);
						}
					}
				}
			}
		}
		public static void UpdateArea (GraphUpdateObject o, INavmesh graph) {

			//System.DateTime startTime = System.DateTime.UtcNow;

			Bounds bounds = o.bounds;

			Rect r = Rect.MinMaxRect (bounds.min.x,bounds.min.z,bounds.max.x,bounds.max.z);

			var r2 = new IntRect(
				Mathf.FloorToInt(bounds.min.x*Int3.Precision),
				Mathf.FloorToInt(bounds.min.z*Int3.Precision),
				Mathf.FloorToInt(bounds.max.x*Int3.Precision),
				Mathf.FloorToInt(bounds.max.z*Int3.Precision)
			);

			var a = new Int3(r2.xmin,0,r2.ymin);
			var b = new Int3(r2.xmin,0,r2.ymax);
			var c = new Int3(r2.xmax,0,r2.ymin);
			var d = new Int3(r2.xmax,0,r2.ymax);

			graph.GetNodes (_node => {
				var node = _node as TriangleMeshNode;

				bool inside = false;

				int allLeft = 0;
				int allRight = 0;
				int allTop = 0;
				int allBottom = 0;

				for (int v=0;v<3;v++) {

					Int3 p = node.GetVertex(v);
					var vert = (Vector3)p;

					if (r2.Contains (p.x,p.z)) {
						inside = true;
						break;
					}

					if (vert.x < r.xMin) allLeft++;
					if (vert.x > r.xMax) allRight++;
					if (vert.z < r.yMin) allTop++;
					if (vert.z > r.yMax) allBottom++;
				}
				if (!inside) {
					if (allLeft == 3 || allRight == 3 || allTop == 3 || allBottom == 3) {
						return true;
					}
				}

				for (int v=0;v<3;v++) {
					int v2 = v > 1 ? 0 : v+1;

					Int3 vert1 = node.GetVertex(v);
					Int3 vert2 = node.GetVertex(v2);

					if (Polygon.Intersects (a,b,vert1,vert2)) { inside = true; break; }
					if (Polygon.Intersects (a,c,vert1,vert2)) { inside = true; break; }
					if (Polygon.Intersects (c,d,vert1,vert2)) { inside = true; break; }
					if (Polygon.Intersects (d,b,vert1,vert2)) { inside = true; break; }
				}

				if (node.ContainsPoint (a) || node.ContainsPoint (b) || node.ContainsPoint (c) || node.ContainsPoint (d)) {
					inside = true;
				}

				if (!inside) {
					return true;
				}

				o.WillUpdateNode(node);
				o.Apply (node);
				return true;
			});
		}
		/** Updates graphs with a created GUO.
		 * Creates a Pathfinding.GraphUpdateObject with a Pathfinding.GraphUpdateShape
		 * representing the polygon of this object and update all graphs using AstarPath.UpdateGraphs.
		 * This will not update graphs directly. See AstarPath.UpdateGraph for more info.
		 */
		public void Apply () {

			if (AstarPath.active == null) {
				Debug.LogError ("There is no AstarPath object in the scene");
				return;
			}

			GraphUpdateObject guo;

			if (points == null || points.Length == 0) {

				var coll = GetComponent<Collider>();
				var rend = GetComponent<Renderer>();

				Bounds b;
				if (coll != null) b = coll.bounds;
				else if (rend != null) b = rend.bounds;
				else {
					Debug.LogWarning ("Cannot apply GraphUpdateScene, no points defined and no renderer or collider attached");
					return;
				}

				if (b.size.y < minBoundsHeight) b.size = new Vector3(b.size.x,minBoundsHeight,b.size.z);

				guo = new GraphUpdateObject (b);

			} else {
				var shape = new GraphUpdateShape ();
				shape.convex = convex;
				Vector3[] worldPoints = points;
				if (!useWorldSpace) {
					worldPoints = new Vector3[points.Length];
					Matrix4x4 matrix = transform.localToWorldMatrix;
					for (int i=0;i<worldPoints.Length;i++) worldPoints[i] = matrix.MultiplyPoint3x4 (points[i]);
				}

				shape.points = worldPoints;

				Bounds b = shape.GetBounds ();
				if (b.size.y < minBoundsHeight) b.size = new Vector3(b.size.x,minBoundsHeight,b.size.z);
				guo = new GraphUpdateObject (b);
				guo.shape = shape;
			}

			firstApplied = true;

			guo.modifyWalkability = modifyWalkability;
			guo.setWalkability = setWalkability;
			guo.addPenalty = penaltyDelta;
			guo.updatePhysics = updatePhysics;
			guo.updateErosion = updateErosion;
			guo.resetPenaltyOnPhysics = resetPenaltyOnPhysics;

			guo.modifyTag = modifyTag;
			guo.setTag = setTag;

			AstarPath.active.UpdateGraphs (guo);
		}
		public void UpdateArea (GraphUpdateObject guo) {

			Bounds b = guo.bounds;
			b.center -= forcedBounds.min;

			//Figure out which tiles are affected
			var r = new IntRect (Mathf.FloorToInt (b.min.x / (tileSizeX*cellSize)), Mathf.FloorToInt (b.min.z / (tileSizeZ*cellSize)), Mathf.FloorToInt (b.max.x / (tileSizeX*cellSize)), Mathf.FloorToInt (b.max.z / (tileSizeZ*cellSize)));
			//Clamp to bounds
			r = IntRect.Intersection (r, new IntRect (0,0,tileXCount-1,tileZCount-1));

			if (!guo.updatePhysics) {


				for ( int z=r.ymin;z<=r.ymax;z++) {
					for ( int x=r.xmin;x<=r.xmax;x++) {
						NavmeshTile tile = tiles[z*tileXCount + x];
						tile.flag = true;
					}
				}

				for ( int z=r.ymin;z<=r.ymax;z++) {
					for ( int x=r.xmin;x<=r.xmax;x++) {
						NavmeshTile tile = tiles[z*tileXCount + x];
						if ( tile.flag ) {
							tile.flag = false;

							NavMeshGraph.UpdateArea (guo, tile);
						}
					}
				}

				return;
			}

			if (!dynamic) {
				throw new System.Exception ("Recast graph must be marked as dynamic to enable graph updates with updatePhysics = true");
			}

			Voxelize vox = globalVox;

			if (vox == null) {
				throw new System.InvalidOperationException ("No Voxelizer object. UpdateAreaInit should have been called before this function.");
			}



			AstarProfiler.StartProfile ("Init");

			/** \bug No bounds checking */

			//r.DebugDraw (Matrix4x4.TRS (forcedBounds.min, Quaternion.identity, new Vector3 (tileSize*cellSize, 1, tileSize*cellSize)), Color.red);

			//Debug.Break ();



			AstarProfiler.StartProfile ("RemoveConnections");



			for (int x=r.xmin;x<=r.xmax;x++) {
				for (int z=r.ymin;z<=r.ymax;z++) {
					RemoveConnectionsFromTile (tiles[x + z*tileXCount]);
				}
			}



			AstarProfiler.EndProfile ("RemoveConnections");

			AstarProfiler.StartProfile ("Build Tiles");

			for (int x=r.xmin;x<=r.xmax;x++) {
				for (int z=r.ymin;z<=r.ymax;z++) {
					BuildTileMesh (vox, x,z);
				}
			}



			AstarProfiler.EndProfile ("Build Tiles");


			AstarProfiler.StartProfile ("ConnectTiles");
			uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex (this);

			for (int x=r.xmin;x<=r.xmax;x++) {
				for (int z=r.ymin;z<=r.ymax;z++) {
					NavmeshTile tile = tiles[x + z*tileXCount];
					GraphNode[] nodes = tile.nodes;

					for (int i=0;i<nodes.Length;i++) nodes[i].GraphIndex = graphIndex;
				}
			}



			//Connect the newly create tiles with the old tiles and with each other
			r = r.Expand (1);
			//Clamp to bounds
			r = IntRect.Intersection (r, new IntRect (0,0,tileXCount-1,tileZCount-1));

			for (int x=r.xmin;x<=r.xmax;x++) {
				for (int z=r.ymin;z<=r.ymax;z++) {
					if (x < tileXCount-1 && r.Contains (x+1, z)) {
						ConnectTiles (tiles[x + z*tileXCount], tiles[x+1 + z*tileXCount]);
					}
					if (z < tileZCount-1 && r.Contains (x, z+1)) {
						ConnectTiles (tiles[x + z*tileXCount], tiles[x + (z+1)*tileXCount]);
					}
				}
			}

			AstarProfiler.EndProfile ("ConnectTiles");
			AstarProfiler.PrintResults ();
		}
		public void UpdateAreaInit (GraphUpdateObject o) {

			if (!o.updatePhysics) {
				return;
			}

			if (!dynamic) {
				throw new System.Exception ("Recast graph must be marked as dynamic to enable graph updates");
			}

			AstarProfiler.Reset ();
			AstarProfiler.StartProfile ("UpdateAreaInit");
			AstarProfiler.StartProfile ("CollectMeshes");

			RelevantGraphSurface.UpdateAllPositions ();

			//Calculate world bounds of all affected tiles
			IntRect touchingTiles = GetTouchingTiles ( o.bounds );
			Bounds tileBounds = GetTileBounds (touchingTiles);

			int voxelCharacterRadius = Mathf.CeilToInt (characterRadius/cellSize);
			int borderSize = voxelCharacterRadius + 3;

			//Expand borderSize voxels on each side
			tileBounds.Expand (new Vector3 (borderSize,0,borderSize)*cellSize*2);

			List<ExtraMesh> extraMeshes;

			CollectMeshes (out extraMeshes, tileBounds);

			Voxelize vox = globalVox;

			if (vox == null) {

				//Create the voxelizer and set all settings
				vox = new Voxelize (cellHeight, cellSize, walkableClimb, walkableHeight, maxSlope);

				vox.maxEdgeLength = maxEdgeLength;

				if (dynamic) globalVox = vox;

			}

			vox.inputExtraMeshes = extraMeshes;

			AstarProfiler.EndProfile ("CollectMeshes");
			AstarProfiler.EndProfile ("UpdateAreaInit");
		}
		public GraphUpdateThreading CanUpdateAsync (GraphUpdateObject o) {
			return o.updatePhysics ? GraphUpdateThreading.SeparateAndUnityInit : GraphUpdateThreading.SeparateThread;
		}
        /** Revert graphs when destroyed.
	 * When the DynamicObstacle is destroyed, a last graph update should be done to revert nodes to their original state */
        public void OnDestroy () {
            if (AstarPath.active != null) {
                GraphUpdateObject guo = new GraphUpdateObject (prevBounds);
                AstarPath.active.UpdateGraphs (guo);
            }
        }
        /** Update all dirty tiles now */
        public void UpdateDirtyTiles () {

            if (graph == null) {
                new System.InvalidOperationException ("No graph is set on this object");
            }

            if (graph.tileXCount * graph.tileZCount != dirtyTiles.Length) {
                Debug.LogError ("Graph has changed dimensions. Clearing queued graph updates and resetting.");
                SetGraph (graph);
                return;
            }

            for (int z = 0; z < graph.tileZCount; z++) {
                for (int x = 0; x < graph.tileXCount; x++) {
                    if ( dirtyTiles[z*graph.tileXCount + x] ) {
                        dirtyTiles[z*graph.tileXCount + x] = false;

                        var bounds = graph.GetTileBounds (x, z);

                        // Shrink it a bit to make sure other tiles
                        // are not included because of rounding errors
                        bounds.extents *= 0.5f;

                        var guo = new GraphUpdateObject (bounds);
                        guo.nnConstraint.graphMask = 1 << (int)graph.graphIndex;

                        AstarPath.active.UpdateGraphs (guo);
                    }
                }
            }

            anyDirtyTiles = false;
        }
		public void UpdateArea (GraphUpdateObject o) {
			UpdateArea (o, this);
		}