public void ExportRecastInputMesh(TerrainTile tile, string filename) { if (File.Exists(filename)) { return; // skip existing files } var verts = tile.TerrainVertices; var indices = tile.TerrainIndices; var start = DateTime.Now; Console.Write("Writing input file {0}...", filename); using (var file = new StreamWriter(filename)) { foreach (var vertex in verts) { var v = vertex; RecastUtil.TransformWoWCoordsToRecastCoords(ref v); file.WriteLine("v {0} {1} {2}", v.X, v.Y, v.Z); } // write faces var terrain = tile.Terrain; for (var i = 0; i < indices.Length; i += 3) { // ignore triangles that are completely submerged in a liquid var v1 = verts[indices[i]]; var v2 = verts[indices[i + 1]]; var v3 = verts[indices[i + 2]]; if (terrain.GetLiquidType(v1) != LiquidType.None && terrain.GetLiquidType(v2) != LiquidType.None && terrain.GetLiquidType(v3) != LiquidType.None) { var triBottom = Single.MaxValue; var triTop = Single.MinValue; triBottom = Math.Min(triBottom, v1.Z); triBottom = Math.Min(triBottom, v2.Z); triBottom = Math.Min(triBottom, v3.Z); triTop = Math.Max(triTop, v1.Z); triTop = Math.Max(triTop, v2.Z); triTop = Math.Max(triTop, v3.Z); var triHeightAvg = (triTop - triBottom) * 0.5f; var liqHeight = terrain.GetLiquidHeight(v1); var avgTriLiqDepth = liqHeight - triHeightAvg; if (avgTriLiqDepth > 1.0f) { continue; } } //file.WriteLine("f {0} {1} {2}", indices[i] + 1, indices[i + 1] + 1, indices[i + 2] + 1); file.WriteLine("f {0} {1} {2}", indices[i + 2] + 1, indices[i + 1] + 1, indices[i] + 1); } } Console.WriteLine("Done. - Exported {0} triangles in: {1:0.000}s", indices.Length / 3, (DateTime.Now - start).TotalSeconds); }
/// <summary> /// Put it all together /// </summary> private unsafe void SmashMesh( int userId, int vertComponentCount, int polyCount, IntPtr vertComponentsPtr, int totalPolyIndexCount, // count of all vertex indices in all polys IntPtr pIndexCountsPtr, IntPtr pIndicesPtr, IntPtr pNeighborsPtr, IntPtr pFlagsPtr, IntPtr polyAreasAndTypesPtr ) { //var coords = GetTileCoords(userId); //var tile = Terrain.GetTile(coords); //if (tile == null) //{ // throw new Exception("Invalid tile: " + id); //} // read native data var vertComponents = (float *)vertComponentsPtr; var pIndexCounts = (byte *)pIndexCountsPtr; var pIndices = (uint *)pIndicesPtr; var pNeighbors = (uint *)pNeighborsPtr; var pFlags = (ushort *)pFlagsPtr; var polyAreasAndTypes = (byte *)polyAreasAndTypesPtr; // create vertices array var indices = new int[totalPolyIndexCount]; var vertCount = vertComponentCount / 3; List <Vector3> vertexList; using (LargeObjectPools.Vector3ListPool.Borrow(out vertexList)) { var min = new Vector3(Single.MaxValue, Single.MaxValue, Single.MaxValue); var max = new Vector3(Single.MinValue, Single.MinValue, Single.MinValue); for (int i = 0, v = 0; i < vertCount; i++, v += 3) { var vert = new Vector3(vertComponents[v], vertComponents[v + 1], vertComponents[v + 2]); RecastUtil.TransformRecastCoordsToWoWCoords(ref vert); vertexList.Add(vert); // expand bounding box min.X = Math.Min(min.X, vert.X); min.Y = Math.Min(min.Y, vert.Y); min.Z = Math.Min(min.Z, vert.Z); max.X = Math.Max(max.X, vert.X); max.Y = Math.Max(max.Y, vert.Y); max.Z = Math.Max(max.Z, vert.Z); } Min = min; Max = max; var vertices = EliminateDoubleVertices(vertexList, pIndices, totalPolyIndexCount); // polygon first pass -> Create polygons var polys = new NavMeshPolygon[polyCount]; var polyEdgeIndex = 0; var p = 0; for (var i = 0; i < polyCount; i++) { var polyIndexCount = pIndexCounts[i]; var poly = polys[i] = new NavMeshPolygon(); poly.Indices = new int[polyIndexCount]; poly.Neighbors = new int[polyIndexCount]; Debug.Assert(3 == polyIndexCount); for (var j = 0; j < polyIndexCount; j++) { var idx = (int)pIndices[polyEdgeIndex + j]; indices[p++] = idx; poly.Indices[j] = idx; poly.Neighbors[j] = -1; Debug.Assert(poly.Indices[j] >= 0 && poly.Indices[j] < vertCount); } // switch first and third index because our collision test needs the triangles to go in the other direction var tmp = poly.Indices[0]; indices[p - 3] = poly.Indices[0] = poly.Indices[2]; indices[p - 1] = poly.Indices[2] = tmp; polyEdgeIndex += polyIndexCount; } // polygon second pass -> Initialize neighbors polyEdgeIndex = 0; var undecidedNeighborRelations = new List <Tuple <int, int> >(); for (var i = 0; i < polyCount; i++) { var poly = polys[i]; var polyIndexCount = pIndexCounts[i]; var a = poly.Indices[0]; var b = poly.Indices[1]; var c = poly.Indices[2]; for (var j = 0; j < polyIndexCount; j++) { var neighbor = (int)pNeighbors[polyEdgeIndex + j]; if (neighbor == -1) { continue; } var neighborPoly = polys[neighbor]; // sort the neighbor poly into the array of neighbors, correctly var a2 = neighborPoly.Indices[0]; var b2 = neighborPoly.Indices[1]; var c2 = neighborPoly.Indices[2]; var nCount = 0; var mask = 0; if (a == a2 || a == b2 || a == c2) { // some vertex matches the first vertex of the triangle nCount++; mask |= TerrainUtil.TrianglePointA; } if (b == a2 || b == b2 || b == c2) { // some vertex matches the second vertex of the triangle nCount++; mask |= TerrainUtil.TrianglePointB; } if (c == a2 || c == b2 || c == c2) { // some vertex matches the third vertex of the triangle nCount++; mask |= TerrainUtil.TrianglePointC; } //var va = vertices[a]; //var vb = vertices[b]; //var vc = vertices[c]; //var ua = vertices[a2]; //var ub = vertices[b2]; //var uc = vertices[c2]; //var vs = new List<Vector3>() { va, vb, vc }; //var us = new List<Vector3>() { ua, ub, uc }; //var mc = 0; //for (var ii = 0; ii < vs.Count; ii++) //{ // var vv = vs[ii]; // for (var jj = 0; jj < us.Count; jj++) // { // var uu = us[jj]; // if (vv.Equals(uu)) // { // mc++; // break; // } // } //} //Debug.Assert(mc == 2); //Debug.Assert(nCount == 2); if (nCount < 2) { undecidedNeighborRelations.Add(Tuple.Create(i, neighbor)); } var edge = TerrainUtil.GetEdge(mask); if (edge != -1) { poly.Neighbors[edge] = neighbor; } } polyEdgeIndex += polyIndexCount; } // these relations are incorrect for some reason // the actual neighbor that is to replace the undecided neighbor is also in this set, though for (var i = 0; i < undecidedNeighborRelations.Count; i++) { var rel = undecidedNeighborRelations[i]; var poly = polys[rel.Item1]; var tri = poly.GetTriangle(vertices); for (var j = 0; j < undecidedNeighborRelations.Count; j++) { if (i == j) { continue; } var index2 = undecidedNeighborRelations[j].Item1; var poly2 = polys[index2]; var tri2 = poly2.GetTriangle(vertices); var sharedMask = tri.GetSharedEdgeMask(tri2); var edge = TerrainUtil.GetEdge(sharedMask); if (edge != -1) { // the two share an edge poly.Neighbors[edge] = index2; break; } } } // make sure, the neighbor relation is symmetric for (var i = 0; i < polys.Length; i++) { var poly = polys[i]; for (int j = 0; j < poly.Neighbors.Length; j++) { var neighbor = poly.Neighbors[j]; if (neighbor == -1) { continue; } var neighborPoly = polys[neighbor]; var found = false; for (var k = 0; k < neighborPoly.Neighbors.Length; k++) { var neighneigh = neighborPoly.Neighbors[k]; if (neighneigh == i) { found = true; break; } } if (!found) { // neighbor of poly does not have poly as neighor // find the edge and insert the neighbor var tri = poly.GetTriangle(vertices); var tri2 = neighborPoly.GetTriangle(vertices); var sharedMask = tri2.GetSharedEdgeMask(tri); var edge = TerrainUtil.GetEdge(sharedMask); Debug.Assert(neighborPoly.Neighbors[edge] == -1); neighborPoly.Neighbors[edge] = i; break; } } } // create new NavMesh object Tile.NavMesh = new NavMesh(Tile, polys, vertices, indices); } }