コード例 #1
0
		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");
		}
コード例 #2
0
		protected void BuildTileMesh (Voxelize vox, int x, int z) {

			AstarProfiler.StartProfile ("Build Tile");

			AstarProfiler.StartProfile ("Init");

			//World size of tile
			float tcsx = tileSizeX*cellSize;
			float tcsz = tileSizeZ*cellSize;

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

			Vector3 forcedBoundsMin = forcedBounds.min;
			Vector3 forcedBoundsMax = forcedBounds.max;

			var bounds = new Bounds ();
			bounds.SetMinMax(new Vector3 (x*tcsx, 0, z*tcsz) + forcedBoundsMin,
						new Vector3 ((x+1)*tcsx + forcedBoundsMin.x, forcedBoundsMax.y, (z+1)*tcsz + forcedBoundsMin.z)
				);
			vox.borderSize = voxelCharacterRadius + 3;

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

			vox.forcedBounds = bounds;
			vox.width = tileSizeX + vox.borderSize*2;
			vox.depth = tileSizeZ + vox.borderSize*2;

			if (!useTiles && relevantGraphSurfaceMode == RelevantGraphSurfaceMode.OnlyForCompletelyInsideTile) {
				// This best reflects what the user would actually want
				vox.relevantGraphSurfaceMode = RelevantGraphSurfaceMode.RequireForAll;
			} else {
				vox.relevantGraphSurfaceMode = relevantGraphSurfaceMode;
			}

			vox.minRegionSize = Mathf.RoundToInt(minRegionSize / (cellSize*cellSize));

 #if ASTARDEBUG
			Debug.Log ("Building Tile " + x+","+z);
			System.Console.WriteLine ("Recast Graph -- Voxelizing");
#endif
			AstarProfiler.EndProfile ("Init");


			//Init voxelizer
			vox.Init ();

			vox.CollectMeshes ();

			vox.VoxelizeInput ();

			AstarProfiler.StartProfile ("Filter Ledges");

#if ASTAR_RECAST_VOXEL_DEBUG
			if (importMode) {
				if (File.Exists(Application.dataPath+"/tile."+x+"."+z)) {
					FileStream fs = File.OpenRead (Application.dataPath+"/tile."+x+"."+z);
					byte[] bytes = new byte[fs.Length];
					fs.Read (bytes,0,(int)fs.Length);
					VoxelArea tmpVox = new VoxelArea(vox.width,vox.depth);
					Pathfinding.Voxels.VoxelSerializeUtility.DeserializeVoxelAreaData (bytes,tmpVox);
					Pathfinding.Voxels.VoxelSerializeUtility.MergeVoxelAreaData(tmpVox,vox.voxelArea,vox.voxelWalkableClimb);
				}
			}
			if (exportMode) {
				FileStream fs = File.Create(Application.dataPath+"/tile."+x+"."+z);
				byte[] bytes = Pathfinding.Voxels.VoxelSerializeUtility.SerializeVoxelAreaData(vox.voxelArea);
				fs.Write(bytes,0,bytes.Length);
				fs.Close();
			}
#endif

			vox.FilterLedges (vox.voxelWalkableHeight, vox.voxelWalkableClimb, vox.cellSize, vox.cellHeight, vox.forcedBounds.min);

			AstarProfiler.EndProfile ("Filter Ledges");

			AstarProfiler.StartProfile ("Filter Low Height Spans");
			vox.FilterLowHeightSpans (vox.voxelWalkableHeight, vox.cellSize, vox.cellHeight, vox.forcedBounds.min);
			AstarProfiler.EndProfile ("Filter Low Height Spans");

			vox.BuildCompactField ();

			vox.BuildVoxelConnections ();

#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Eroding");
#endif

			vox.ErodeWalkableArea (voxelCharacterRadius);

#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Building Distance Field");
#endif

			vox.BuildDistanceField ();

#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Building Regions");
#endif

			vox.BuildRegions ();

#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Building Contours");
#endif

			var cset = new VoxelContourSet ();

			vox.BuildContours (contourMaxError,1,cset,Voxelize.RC_CONTOUR_TESS_WALL_EDGES);

#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Building Poly Mesh");
#endif

			VoxelMesh mesh;

			vox.BuildPolyMesh (cset,3,out mesh);

#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Building Nodes");
#endif

			//Vector3[] vertices = new Vector3[mesh.verts.Length];

			AstarProfiler.StartProfile ("Build Nodes");

			// Debug code
			//matrix = Matrix4x4.TRS (vox.voxelOffset,Quaternion.identity,Int3.Precision*vox.cellScale);

			//Position the vertices correctly in the world
			for (int i=0;i<mesh.verts.Length;i++) {
				//Note the multiplication is Scalar multiplication of vectors
				mesh.verts[i] = ((mesh.verts[i]*Int3.Precision) * vox.cellScale) + (Int3)vox.voxelOffset;

				// Debug code
				//Debug.DrawRay (matrix.MultiplyPoint3x4(vertices[i]),Vector3.up,Color.red);
			}


#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Generating Nodes");
#endif

			NavmeshTile tile = CreateTile (vox, mesh, x,z);
			tiles[tile.x + tile.z*tileXCount] = tile;

			AstarProfiler.EndProfile ("Build Nodes");

#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Done");
#endif

			AstarProfiler.EndProfile ("Build Tile");
		}
コード例 #3
0
		/** Create a tile at tile index \a x , \a z from the mesh.
		 * \warning This implementation is not thread safe. It uses cached variables to improve performance
		 */
		NavmeshTile CreateTile (Voxelize vox, VoxelMesh mesh, int x, int z) {

			if (mesh.tris == null) throw new System.ArgumentNullException ("mesh.tris");
			if (mesh.verts == null) throw new System.ArgumentNullException ("mesh.verts");

			//Create a new navmesh tile and assign its settings
			var tile = new NavmeshTile();

			tile.x = x;
			tile.z = z;
			tile.w = 1;
			tile.d = 1;
			tile.tris = mesh.tris;
			tile.verts = mesh.verts;
			tile.bbTree = new BBTree();

			if (tile.tris.Length % 3 != 0) throw new System.ArgumentException ("Indices array's length must be a multiple of 3 (mesh.tris)");

			if (tile.verts.Length >= VertexIndexMask) throw new System.ArgumentException ("Too many vertices per tile (more than "+VertexIndexMask+")." +
				"\nTry enabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* Inspector");

			//Dictionary<Int3, int> firstVerts = new Dictionary<Int3, int> ();
			Dictionary<Int3, int> firstVerts = cachedInt3_int_dict;
			firstVerts.Clear();

			var compressedPointers = new int[tile.verts.Length];

			int count = 0;
			for (int i=0;i<tile.verts.Length;i++) {
				if (!firstVerts.ContainsKey(tile.verts[i])) {
					firstVerts.Add (tile.verts[i], count);
					compressedPointers[i] = count;
					tile.verts[count] = tile.verts[i];
					count++;
				} else {
					// There are some cases, rare but still there, that vertices are identical
					compressedPointers[i] = firstVerts[tile.verts[i]];
				}
			}

			for (int i=0;i<tile.tris.Length;i++) {
				tile.tris[i] = compressedPointers[tile.tris[i]];
			}

			var compressed = new Int3[count];
			for (int i=0;i<count;i++) compressed[i] = tile.verts[i];

			tile.verts = compressed;

			var nodes = new TriangleMeshNode[tile.tris.Length/3];
			tile.nodes = nodes;

			//Here we are faking a new graph
			//The tile is not added to any graphs yet, but to get the position querys from the nodes
			//to work correctly (not throw exceptions because the tile is not calculated) we fake a new graph
			//and direct the position queries directly to the tile
			int graphIndex = AstarPath.active.astarData.graphs.Length;

			TriangleMeshNode.SetNavmeshHolder (graphIndex, tile);

			//This index will be ORed to the triangle indices
			int tileIndex = x + z*tileXCount;
			tileIndex <<= TileIndexOffset;

			//Create nodes and assign triangle indices
			for (int i=0;i<nodes.Length;i++) {
				var node = new TriangleMeshNode(active);
				nodes[i] = node;
				node.GraphIndex = (uint)graphIndex;
				node.v0 = tile.tris[i*3+0] | tileIndex;
				node.v1 = tile.tris[i*3+1] | tileIndex;
				node.v2 = tile.tris[i*3+2] | tileIndex;

				//Degenerate triangles might occur, but they will not cause any large troubles anymore
				//if (Polygon.IsColinear (node.GetVertex(0), node.GetVertex(1), node.GetVertex(2))) {
				//	Debug.Log ("COLINEAR!!!!!!");
				//}

				//Make sure the triangle is clockwise
				if (!Polygon.IsClockwise (node.GetVertex(0), node.GetVertex(1), node.GetVertex(2))) {
					int tmp = node.v0;
					node.v0 = node.v2;
					node.v2 = tmp;
				}

				node.Walkable = true;
				node.Penalty = initialPenalty;
				node.UpdatePositionFromVertices();
			}

			tile.bbTree.RebuildFrom(nodes);
			CreateNodeConnections (tile.nodes);

			//Remove the fake graph
			TriangleMeshNode.SetNavmeshHolder (graphIndex, null);

			return tile;
		}
コード例 #4
0
		protected void ScanAllTiles (OnScanStatus statusCallback) {

#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Collecting Meshes");
#endif

			//----

			//Voxel grid size
			int gw = (int)(forcedBounds.size.x/cellSize + 0.5f);
			int gd = (int)(forcedBounds.size.z/cellSize + 0.5f);

			if (!useTiles) {
				tileSizeX = gw;
				tileSizeZ = gd;
			} else {
				tileSizeX = editorTileSize;
				tileSizeZ = editorTileSize;
			}

			//Number of tiles
			int tw = (gw + tileSizeX-1) / tileSizeX;
			int td = (gd + tileSizeZ-1) / tileSizeZ;

			tileXCount = tw;
			tileZCount = td;

			if (tileXCount * tileZCount > TileIndexMask+1) {
				throw new System.Exception ("Too many tiles ("+(tileXCount * tileZCount)+") maximum is "+(TileIndexMask+1)+
					"\nTry disabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* inspector.");
			}

			tiles = new NavmeshTile[tileXCount*tileZCount];

#if ASTARDEBUG
			System.Console.WriteLine ("Recast Graph -- Creating Voxel Base");
#endif

			// If this is true, just fill the graph with empty tiles
			if ( scanEmptyGraph ) {
				for (int z=0;z<td;z++) {
					for (int x=0;x<tw;x++) {
						tiles[z*tileXCount + x] = NewEmptyTile(x,z);
					}
				}
				return;
			}

			AstarProfiler.StartProfile ("Finding Meshes");
			List<ExtraMesh> extraMeshes;

#if !NETFX_CORE || UNITY_EDITOR
			System.Console.WriteLine ("Collecting Meshes");
#endif
			CollectMeshes (out extraMeshes, forcedBounds);

			AstarProfiler.EndProfile ("Finding Meshes");

			// A walkableClimb higher than walkableHeight can cause issues when generating the navmesh since then it can in some cases
			// Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with navmesh without links)
			// The editor scripts also enforce this but we enforce it here too just to be sure
			walkableClimb = Mathf.Min (walkableClimb, walkableHeight);

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

			vox.maxEdgeLength = maxEdgeLength;

			int lastInfoCallback = -1;
			var watch = System.Diagnostics.Stopwatch.StartNew();

			//Generate all tiles
			for (int z=0;z<td;z++) {
				for (int x=0;x<tw;x++) {

					int tileNum = z*tileXCount + x;
#if !NETFX_CORE || UNITY_EDITOR
					System.Console.WriteLine ("Generating Tile #"+(tileNum) + " of " + td*tw);
#endif

					//Call statusCallback only 10 times since it is very slow in the editor
					if (statusCallback != null && (tileNum*10/tiles.Length > lastInfoCallback || watch.ElapsedMilliseconds > 2000)) {
						lastInfoCallback = tileNum*10/tiles.Length;
						watch.Reset();
						watch.Start();

						statusCallback (new Progress (AstarMath.MapToRange (0.1f, 0.9f, tileNum/(float)tiles.Length), "Building Tile " + tileNum + "/" + tiles.Length));
					}

					BuildTileMesh (vox, x,z);
				}
			}

#if !NETFX_CORE
			System.Console.WriteLine ("Assigning Graph Indices");
#endif

			if (statusCallback != null) statusCallback (new Progress (0.9f, "Connecting tiles"));

			//Assign graph index to nodes
			uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex (this);

			GraphNodeDelegateCancelable del = delegate (GraphNode n) {
				n.GraphIndex = graphIndex;
				return true;
			};
			GetNodes (del);

			for (int z=0;z<td;z++) {
				for (int x=0;x<tw;x++) {
#if !NETFX_CORE
					System.Console.WriteLine ("Connecing Tile #"+(z*tileXCount + x) + " of " + td*tw);
#endif
					if (x < tw-1) ConnectTiles (tiles[x + z*tileXCount], tiles[x+1 + z*tileXCount]);
					if (z < td-1) ConnectTiles (tiles[x + z*tileXCount], tiles[x + (z+1)*tileXCount]);
				}
			}

			AstarProfiler.PrintResults ();
		}