Beispiel #1
0
		/** Builds a polygon mesh from a contour set.
		 *
		 * \param cset contour set to build a mesh from.
		 * \param nvp Maximum allowed vertices per polygon. \warning Currently locked to 3.
		 * \param mesh Results will be written to this mesh.
		 */
		public void BuildPolyMesh (VoxelContourSet cset, int nvp, out VoxelMesh mesh) {
			
			AstarProfiler.StartProfile ("Build Poly Mesh");
			
			nvp = 3;
			
			int maxVertices = 0;
			int maxTris = 0;
			int maxVertsPerCont = 0;
			
			for (int i = 0; i < cset.conts.Count; i++) {
				
				// Skip null contours.
				if (cset.conts[i].nverts < 3) continue;
				
				maxVertices += cset.conts[i].nverts;
				maxTris += cset.conts[i].nverts - 2;
				maxVertsPerCont = AstarMath.Max (maxVertsPerCont, cset.conts[i].nverts);
			}
			
			if (maxVertices >= 65534)
			{
				Debug.LogWarning ("To many vertices for unity to render - Unity might screw up rendering, but hopefully the navmesh will work ok");
				//mesh = new VoxelMesh ();
				//yield break;
				//return;
			}
			
			/** \todo Could be cached to avoid allocations */
			Int3[] verts = new Int3[maxVertices];
			/** \todo Could be cached to avoid allocations */
			int[] polys = new int[maxTris*nvp];
			
			Memory.MemSet<int> (polys, 0xff, sizeof(int));
			
			int[] indices = new int[maxVertsPerCont];
			
			int[] tris = new int[maxVertsPerCont*3];
			
			int vertexIndex = 0;
			int polyIndex = 0;
			
			for (int i=0;i<cset.conts.Count;i++) {
				
				VoxelContour cont = cset.conts[i];
				
				//Skip null contours
				if (cont.nverts < 3) {
					continue;
				}
				
				for (int j=0; j < cont.nverts;j++) {
					indices[j] = j;
					cont.verts[j*4+2] /= voxelArea.width;
				}
				
				int ntris = Triangulate (cont.nverts, cont.verts, ref indices, ref tris);
				
				int startIndex = vertexIndex;
				for (int j=0;j<ntris*3; polyIndex++, j++) {
					//@Error sometimes
					polys[polyIndex] = tris[j]+startIndex;
				}
				
				for (int j=0;j<cont.nverts; vertexIndex++, j++) {
					verts[vertexIndex] = new Int3(cont.verts[j*4],cont.verts[j*4+1],cont.verts[j*4+2]);
				}
			}
			
			mesh = new VoxelMesh ();
			//yield break;
			Int3[] trimmedVerts = new Int3[vertexIndex];
			for (int i=0;i<vertexIndex;i++) {
				trimmedVerts[i] = verts[i];
			}
			
			int[] trimmedTris = new int[polyIndex];
			
			System.Buffer.BlockCopy (polys, 0, trimmedTris, 0, polyIndex*sizeof(int));
			
			mesh.verts = trimmedVerts;
			mesh.tris = trimmedTris;
			
			// Some debugging
			/*for (int i=0;i<mesh.tris.Length/3;i++) {
				
				int p = i*3;
				
				int p1 = mesh.tris[p];
				int p2 = mesh.tris[p+1];
				int p3 = mesh.tris[p+2];
				
				//Debug.DrawLine (ConvertPosCorrZ (mesh.verts[p1].x,mesh.verts[p1].y,mesh.verts[p1].z),ConvertPosCorrZ (mesh.verts[p2].x,mesh.verts[p2].y,mesh.verts[p2].z),Color.yellow);
				//Debug.DrawLine (ConvertPosCorrZ (mesh.verts[p1].x,mesh.verts[p1].y,mesh.verts[p1].z),ConvertPosCorrZ (mesh.verts[p3].x,mesh.verts[p3].y,mesh.verts[p3].z),Color.yellow);
				//Debug.DrawLine (ConvertPosCorrZ (mesh.verts[p3].x,mesh.verts[p3].y,mesh.verts[p3].z),ConvertPosCorrZ (mesh.verts[p2].x,mesh.verts[p2].y,mesh.verts[p2].z),Color.yellow);
	
				//Debug.DrawLine (ConvertPosCorrZ (verts[p1],0,verts[p1+2]),ConvertPosCorrZ (verts[p2],0,verts[p2+2]),Color.blue);
				//Debug.DrawLine (ConvertPosCorrZ (verts[p1],0,verts[p1+2]),ConvertPosCorrZ (verts[p3],0,verts[p3+2]),Color.blue);
				//Debug.DrawLine (ConvertPosCorrZ (verts[p2],0,verts[p2+2]),ConvertPosCorrZ (verts[p3],0,verts[p3+2]),Color.blue);
	
			}*/
			
			AstarProfiler.EndProfile ("Build Poly Mesh");
			
		}
		/** Releases contents of a contour set to caches */
		static void ReleaseContours (VoxelContourSet cset) {
			for (int i=0;i<cset.conts.Count;i++) {
				VoxelContour cont = cset.conts[i];
				ReleaseIntArr(cont.verts);
				ReleaseIntArr(cont.rverts);
			}
			cset.conts = null;
		}
		public void BuildContours (float maxError, int maxEdgeLength, VoxelContourSet cset, int buildFlags) {
			
			AstarProfiler.StartProfile ("Build Contours");
			
			AstarProfiler.StartProfile ("- Init");
			int w = voxelArea.width;
			int d = voxelArea.depth;
			
			int wd = w*d;
			
			//cset.bounds = voxelArea.bounds;
			
			int maxContours = Mathf.Max (8/*Max Regions*/,8);
			
			
			//cset.conts = new VoxelContour[maxContours];
			List<VoxelContour> contours = new List<VoxelContour>(maxContours);
			
			AstarProfiler.EndProfile ("- Init");
			AstarProfiler.StartProfile ("- Mark Boundaries");
			
			//cset.nconts = 0;
			
			//NOTE: This array may contain any data, but since we explicitly set all data in it before we use it, it's OK.
			ushort[] flags = voxelArea.tmpUShortArr;
			if (flags.Length < voxelArea.compactSpanCount) {
				flags = voxelArea.tmpUShortArr = new ushort[voxelArea.compactSpanCount];
			}
			
			// Mark boundaries. (@?)
			for (int z=0;z < wd;z += voxelArea.width) {
				for (int x=0;x < voxelArea.width;x++) {
					
					CompactVoxelCell c = voxelArea.compactCells[x+z];
					
					for (int i= (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
						
						ushort res = 0;
						CompactVoxelSpan s = voxelArea.compactSpans[i];
						
						if (s.reg == 0 || (s.reg & BorderReg) == BorderReg) {
							flags[i] = 0;
							continue;
						}
						
						for (int dir=0;dir < 4; dir++) {
							int r = 0;
							
							if (s.GetConnection (dir) != NotConnected) {
								int nx = x + voxelArea.DirectionX[dir];
								int nz = z + voxelArea.DirectionZ[dir];
								
								int ni = (int)voxelArea.compactCells[nx+nz].index + s.GetConnection (dir);
								r = voxelArea.compactSpans[ni].reg;
								
								
							}
							
							//@TODO - Why isn't this inside the previous IF
							if (r == s.reg) {
								res |= (ushort)(1 << dir);
								
							}
						}
						
						//Inverse, mark non connected edges.
						flags[i] = (ushort)(res ^ 0xf);
							
					}
					
				}
			}
			
			AstarProfiler.EndProfile ("- Mark Boundaries");
			
			AstarProfiler.StartProfile ("- Simplify Contours");
			List<int> verts = ListPool<int>.Claim(256);//new List<int> (256);
			List<int> simplified = ListPool<int>.Claim(64);//new List<int> (64);
			
			for (int z=0;z < wd;z += voxelArea.width) {
				for (int x=0;x < voxelArea.width;x++) {
					
					CompactVoxelCell c = voxelArea.compactCells[x+z];
					
					for (int i= (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
						
						//CompactVoxelSpan s = voxelArea.compactSpans[i];
						
						if (flags[i] == 0 || flags[i] == 0xf)
						{
							flags[i] = 0;
							continue;
						}
						
						int reg = voxelArea.compactSpans[i].reg;
						
						if (reg == 0 || (reg & BorderReg) == BorderReg) {
							continue;
						}
						
						int area = voxelArea.areaTypes[i];
						
						verts.Clear ();
						simplified.Clear ();
						
						WalkContour(x, z, i, flags, verts);
						
						SimplifyContour(verts, simplified, maxError, maxEdgeLength, buildFlags);
						RemoveDegenerateSegments (simplified);
						
						VoxelContour contour = new VoxelContour ();
						contour.verts = ClaimIntArr(simplified.Count,false);//simplified.ToArray ();
						for (int j=0;j<simplified.Count;j++) contour.verts[j] = simplified[j];
#if ASTAR_RECAST_INCLUDE_RAW_VERTEX_CONTOUR
						//Not used at the moment, just debug stuff
						contour.rverts = ClaimIntArr(verts.Count);
						for (int j=0;j<verts.Count;j++) contour.rverts[j] = verts[j];
#endif
						contour.nverts = simplified.Count/4;
						contour.reg = reg;
						contour.area = area;
						
						contours.Add (contour);
						
						#if ASTARDEBUG
						for (int q=0, j = (simplified.Count/4)-1;q<(simplified.Count/4);j=q, q++) {
				
							int i4 = q*4;
							int j4 = j*4;
							
							Vector3 p1 = Vector3.Scale (
							new Vector3 (
								simplified[i4+0],
								simplified[i4+1],
								(simplified[i4+2]/(float)voxelArea.width)
							),
							cellScale)
							+voxelOffset;
							
							Vector3 p2 = Vector3.Scale (
							new Vector3 (
								simplified[j4+0],
								simplified[j4+1],
								(simplified[j4+2]/(float)voxelArea.width)
							)
							,cellScale)
							+voxelOffset;
							
							
							if (CalcAreaOfPolygon2D(contour.verts, contour.nverts) > 0) {
								Debug.DrawLine (p1,p2,AstarMath.IntToColor (reg,0.5F));
							} else {
								Debug.DrawLine (p1,p2,Color.red);
								
							}
							
						}
						#endif
					}
				}
			}
			
			ListPool<int>.Release(verts);
			ListPool<int>.Release(simplified);
			
			AstarProfiler.EndProfile ("- Simplify Contours");
			
			AstarProfiler.StartProfile ("- Fix Contours");
			
			// Check and merge droppings.
			// Sometimes the previous algorithms can fail and create several contours
			// per area. This pass will try to merge the holes into the main region.
			for (int i = 0; i < contours.Count; i++)
			{
				VoxelContour cont = contours[i];
				// Check if the contour is would backwards.
				if (CalcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
				{
					// Find another contour which has the same region ID.
					int mergeIdx = -1;
					for (int j = 0; j < contours.Count; j++)
					{
						if (i == j) continue;
						if (contours[j].nverts > 0 && contours[j].reg == cont.reg)
						{
							// Make sure the polygon is correctly oriented.
							if (CalcAreaOfPolygon2D(contours[j].verts, contours[j].nverts) > 0)
							{
								mergeIdx = j;
								break;
							}
						}
					}
					if (mergeIdx == -1)
					{
						Debug.LogError ("rcBuildContours: Could not find merge target for bad contour "+i+".");
					}
					else
					{
						// Debugging
						//Debug.LogWarning ("Fixing contour");

						VoxelContour mcont = contours[mergeIdx];
						// Merge by closest points.
						int ia = 0, ib = 0;
						GetClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ref ia, ref ib);
						
						if (ia == -1 || ib == -1)
						{
							Debug.LogWarning ("rcBuildContours: Failed to find merge points for "+i+" and "+mergeIdx+".");
							continue;
						}
						
#if ASTARDEBUG
						int p4 = ia*4;
						int p42 = ib*4;
							
						Vector3 p12 = Vector3.Scale (
							new Vector3 (
								mcont.verts[p4+0],
								mcont.verts[p4+1],
								(mcont.verts[p4+2]/(float)voxelArea.width)
							),
							cellScale)
							+voxelOffset;
							
							Vector3 p22 = Vector3.Scale (
							new Vector3 (
								cont.verts[p42+0],
								cont.verts[p42+1],
								(cont.verts[p42+2]/(float)voxelArea.width)
							)
							,cellScale)
							+voxelOffset;
						
						Debug.DrawLine (p12,p22,Color.green);
#endif
						
						if (!MergeContours(ref mcont, ref cont, ia, ib))
						{
							Debug.LogWarning ("rcBuildContours: Failed to merge contours "+i+" and "+mergeIdx+".");
							continue;
						}
						
						contours[mergeIdx] = mcont;
						contours[i] = cont;
						
						#if ASTARDEBUG
						Debug.Log (mcont.nverts);
						
						for (int q=0, j = (mcont.nverts)-1;q<(mcont.nverts);j=q, q++) {
							int i4 = q*4;
							int j4 = j*4;
							
							Vector3 p1 = Vector3.Scale (
							new Vector3 (
								mcont.verts[i4+0],
								mcont.verts[i4+1],
								(mcont.verts[i4+2]/(float)voxelArea.width)
							),
							cellScale)
							+voxelOffset;
							
							Vector3 p2 = Vector3.Scale (
							new Vector3 (
								mcont.verts[j4+0],
								mcont.verts[j4+1],
								(mcont.verts[j4+2]/(float)voxelArea.width)
							)
							,cellScale)
							+voxelOffset;
							
							Debug.DrawLine (p1,p2,Color.red);
						//}
						}
						#endif
					}
				}
			}
		
			cset.conts = contours;
			
			AstarProfiler.EndProfile ("- Fix Contours");
			
			AstarProfiler.EndProfile ("Build Contours");
		}
		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");
		}