/** 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"); }
/** 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; }