private static bool inCone(int i, int n, int[] verts, int pj, int[] vertpj) { int pi = i * 4; int pi1 = RecastMesh.next(i, n) * 4; int pin1 = RecastMesh.prev(i, n) * 4; int[] pverts = new int[4 * 4]; for (int g = 0; g < 4; g++) { pverts[g] = verts[pi + g]; pverts[4 + g] = verts[pi1 + g]; pverts[8 + g] = verts[pin1 + g]; pverts[12 + g] = vertpj[pj + g]; } pi = 0; pi1 = 4; pin1 = 8; pj = 12; // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ]. if (RecastMesh.leftOn(pverts, pin1, pi, pi1)) { return(RecastMesh.left(pverts, pi, pj, pin1) && RecastMesh.left(pverts, pj, pi, pi1)); } // Assume (i-1,i,i+1) not collinear. // else P[i] is reflex. return(!(RecastMesh.leftOn(pverts, pi, pj, pi1) && RecastMesh.leftOn(pverts, pj, pi, pin1))); }
/// <summary> /// Unloads a zone /// </summary> /// <param name="z"></param> public static void Unload(Zone2 z) { lock (_loadedZones) { if (_loadedZones.ContainsKey(z.ID)) { RecastMesh m = _loadedZones[z.ID]; if (m != null) { FreeNavMesh(m.meshPtr, m.queryPtr); } _loadedZones.Remove(z.ID); } } }
private static bool intersectSegCountour(int d0, int d1, int i, int n, int[] verts, int[] d0verts, int[] d1verts) { // For each edge (k,k+1) of P int[] pverts = new int[4 * 4]; for (int g = 0; g < 4; g++) { pverts[g] = d0verts[d0 + g]; pverts[4 + g] = d1verts[d1 + g]; } d0 = 0; d1 = 4; for (int k = 0; k < n; k++) { int k1 = RecastMesh.next(k, n); // Skip edges incident to i. if (i == k || i == k1) { continue; } int p0 = k * 4; int p1 = k1 * 4; for (int g = 0; g < 4; g++) { pverts[8 + g] = verts[p0 + g]; pverts[12 + g] = verts[p1 + g]; } p0 = 8; p1 = 12; if (RecastMesh.vequal(pverts, d0, p0) || RecastMesh.vequal(pverts, d1, p0) || RecastMesh.vequal(pverts, d0, p1) || RecastMesh.vequal(pverts, d1, p1)) { continue; } if (RecastMesh.intersect(pverts, d0, d1, p0, p1)) { return(true); } } return(false); }
private static void removeDegenerateSegments(List <int> simplified) { // Remove adjacent vertices which are equal on xz-plane, // or else the triangulator will get confused. int npts = simplified.Count / 4; for (int i = 0; i < npts; ++i) { int ni = RecastMesh.next(i, npts); // if (vequal(&simplified[i*4], &simplified[ni*4])) if (simplified[i * 4] == simplified[ni * 4] && simplified[i * 4 + 2] == simplified[ni * 4 + 2]) { // Degenerate segment, remove. simplified.RemoveAt(i * 4); simplified.RemoveAt(i * 4); simplified.RemoveAt(i * 4); simplified.RemoveAt(i * 4); npts--; } } }
/// <summary> /// Gets or loads a recast mesh /// </summary> private static RecastMesh LoadZoneMesh(Zone2 zone) { lock (_loadedZones) { if (_loadedZones.ContainsKey(zone.ID)) { return(_loadedZones[zone.ID]); } RecastMesh mesh = null; try { string file = zone.NavFile; if (!File.Exists(file)) { return(null); // no navmesh available } file = Path.GetFullPath(file); // not sure if c dll can load relative stuff IntPtr meshPtr = IntPtr.Zero; IntPtr queryPtr = IntPtr.Zero; if (!LoadNavMesh(file, ref meshPtr, ref queryPtr)) { Log.Error("Loading NavMesh failed for {0}!", zone); return(null); } if (meshPtr == IntPtr.Zero || queryPtr == IntPtr.Zero) { Log.Error("Loading NavMesh failed for {0}! (Pointer was zero!)", zone); return(null); } Log.Normal("Loading NavMesh sucessful for region {0}", zone); return(mesh = new RecastMesh(meshPtr, queryPtr)); } finally { _loadedZones.Add(zone.ID, mesh); } } }
public virtual RecastBuilderResult build(InputGeom geom, RecastBuilderConfig bcfg) { RecastConfig cfg = bcfg.cfg; Context ctx = new Context(); CompactHeightfield chf = buildCompactHeightfield(geom, bcfg, ctx); // Partition the heightfield so that we can use simple algorithm later // to triangulate the walkable areas. // There are 3 martitioning methods, each with some pros and cons: // 1) Watershed partitioning // - the classic Recast partitioning // - creates the nicest tessellation // - usually slowest // - partitions the heightfield into nice regions without holes or // overlaps // - the are some corner cases where this method creates produces holes // and overlaps // - holes may appear when a small obstacles is close to large open area // (triangulation can handle this) // - overlaps may occur if you have narrow spiral corridors (i.e // stairs), this make triangulation to fail // * generally the best choice if you precompute the nacmesh, use this // if you have large open areas // 2) Monotone partioning // - fastest // - partitions the heightfield into regions without holes and overlaps // (guaranteed) // - creates long thin polygons, which sometimes causes paths with // detours // * use this if you want fast navmesh generation // 3) Layer partitoining // - quite fast // - partitions the heighfield into non-overlapping regions // - relies on the triangulation code to cope with holes (thus slower // than monotone partitioning) // - produces better triangles than monotone partitioning // - does not have the corner cases of watershed partitioning // - can be slow and create a bit ugly tessellation (still better than // monotone) // if you have large open areas with small obstacles (not a problem if // you use tiles) // * good choice to use for tiled navmesh with medium and small sized // tiles if (cfg.partitionType == PartitionType.WATERSHED) { // Prepare for region partitioning, by calculating distance field // along the walkable surface. RecastRegion.buildDistanceField(ctx, chf); // Partition the walkable surface into simple regions without holes. RecastRegion.buildRegions(ctx, chf, bcfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea); } else if (cfg.partitionType == PartitionType.MONOTONE) { // Partition the walkable surface into simple regions without holes. // Monotone partitioning does not need distancefield. RecastRegion.buildRegionsMonotone(ctx, chf, bcfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea); } else { // Partition the walkable surface into simple regions without holes. RecastRegion.buildLayerRegions(ctx, chf, bcfg.borderSize, cfg.minRegionArea); } // // Step 5. Trace and simplify region contours. // // Create contours. ContourSet cset = RecastContour.buildContours(ctx, chf, cfg.maxSimplificationError, cfg.maxEdgeLen, RecastConstants.RC_CONTOUR_TESS_WALL_EDGES); // // Step 6. Build polygons mesh from contours. // PolyMesh pmesh = RecastMesh.buildPolyMesh(ctx, cset, cfg.maxVertsPerPoly); // // Step 7. Create detail mesh which allows to access approximate height // on each polygon. // PolyMeshDetail dmesh = RecastMeshDetail.buildPolyMeshDetail(ctx, pmesh, chf, cfg.detailSampleDist, cfg.detailSampleMaxError); return(new RecastBuilderResult(this, pmesh, dmesh)); }
/// <summary> /// /// </summary> public void BuildNavigationMesh(ContentManager content) { NavConfig.CellSize = 0.3f; NavConfig.CellHeight = 0.2f; NavConfig.WalkableSlopeAngle = 45f; NavConfig.WalkableHeight = (int)Math.Ceiling((2f / NavConfig.CellHeight)); NavConfig.WalkableClimb = (int)Math.Floor(0.9f / NavConfig.CellHeight); NavConfig.WalkableRadius = (int)Math.Ceiling(0.6f / NavConfig.CellSize); NavConfig.MaxEdgeLen = (int)(12f / NavConfig.CellSize); NavConfig.MaxSimplificationError = 1.3f; NavConfig.MinRegionArea = (int)8 * 8; NavConfig.MergeRegionArea = (int)20 * 20; NavConfig.DetailSampleDist = 6f; NavConfig.DetailSampleMaxError = 1f; NavConfig.MaxVertsPerPoly = 3; var indices = new List <int>(); var vertices = new List <Vector3>(); /* * foreach ( var factory in Nodes ) { * if (!string.IsNullOrWhiteSpace(factory.Model.ScenePath)) { * * var scene = content.Load<Scene>( factory.Model.ScenePath ); * * var nodeCount = scene.Nodes.Count; * * var worldMatricies = new Matrix[nodeCount]; * * scene.ComputeAbsoluteTransforms( worldMatricies ); * * for ( int i=0; i<scene.Nodes.Count; i++) { * * var worldMatrix = worldMatricies[i] * factory.WorldMatrix; * * var node = scene.Nodes[i]; * * if (node.MeshIndex<0) { * continue; * } * * var mesh = scene.Meshes[ node.MeshIndex ]; * * indices.AddRange( mesh.GetIndices( vertices.Count ) ); * * vertices.AddRange( mesh.Vertices.Select( v1 => Vector3.TransformCoordinate( v1.Position, worldMatrix ) ) ); * } * } * } */ var rcmesh = new RecastMesh(); rcmesh.Indices = indices.ToArray(); rcmesh.Vertices = vertices.ToArray(); var context = new BuildContext(false); SourceNavigationMesh = rcmesh; NavigationMesh = RecastBuilder.BuildNavigationMesh(rcmesh, NavConfig, context, true); var cs = NavConfig.CellSize; var ch = NavConfig.CellHeight; var t = NavigationMesh; navVertices = new Vector3[t.VerticesCount]; navIndices = new int[t.PolysCount * 3]; Vector3 origin = NavConfig.BMin; for (int i = 0; i < t.PolysCount; i++) { for (int j = 0; j < 3; j++) { var index = t.Polys[i * 6 + j]; navIndices[i * 3 + j] = index; ushort a = t.Verts[index * 3]; ushort b = t.Verts[index * 3 + 1]; ushort c = t.Verts[index * 3 + 2]; Vector3 vertex = origin + new Vector3(a * cs, b * ch, c * cs); navVertices[index] = vertex; } } }