/// <summary> /// Returns a path that prevents collisions with the navmesh, but floats freely otherwise /// </summary> /// <param name="zone"></param> /// <param name="start">Start in GlobalXYZ</param> /// <param name="end">End in GlobalXYZ</param> /// <returns></returns> public static PathPoint[] GetPathStraight(Zone2 zone, Vector3 start, Vector3 end, dtPolyFlags includeFilter = dtPolyFlags.ALL ^ dtPolyFlags.DISABLED, dtPolyFlags excludeFilter = 0, float polyExtX = 64.0f, float polyExtY = 64.0f, float polyExtZ = 256.0f, dtStraightPathOptions options = dtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS) { if (!_loadedZones.ContainsKey(zone.ID)) { return(null); } var ptrs = _loadedZones[zone.ID]; var startFloats = (start + zAxis * 8).ToRecastFloats(); var endFloats = (end + zAxis * 8).ToRecastFloats(); var numNodes = 0; var buffer = new float[MAX_POLY * 3]; var flags = new dtPolyFlags[MAX_POLY]; var filter = new[] { includeFilter, excludeFilter }; PathStraight(ptrs.queryPtr, startFloats, endFloats, new Vector3(polyExtX, polyExtY, polyExtZ).ToRecastFloats(), filter, options, ref numNodes, buffer, flags); var points = new PathPoint[numNodes]; var positions = Vector3ArrayFromRecastFloats(buffer, numNodes); for (var i = 0; i < numNodes; i++) { points[i].Position = positions[i]; points[i].Flags = flags[i]; } return(points); }
/// <summary> /// Creates a new heightmap /// </summary> /// <param name="zone"></param> public HeightMap(Zone2 zone) { _terrainMap = zone.LoadTerrainMap(); _terrainFactor = zone.TerrainMapScaleFactor; _offsetMap = zone.LoadOffsetMap(); _offsetFactor = zone.OffsetMapScaleFactor; _loc2Px = 1.0f * (_size = _terrainMap.GetLength(0)) / zone.Width; Log.Debug("Loaded heightmap for {0}", zone); }
/// <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); } } }
public static bool SetPolyFlags(Zone2 zone, uint polyRef, dtPolyFlags flags) { if (!_loadedZones.ContainsKey(zone.ID)) { return(false); } var ptrs = _loadedZones[zone.ID]; var status = SetPolyFlags(ptrs.meshPtr, polyRef, flags); if ((status & dtStatus.DT_FAILURE) == dtStatus.DT_FAILURE) { return(false); } return(true); }
/// <summary> /// Loads all world related data /// </summary> public static void Init() { Log.Normal("Loading World ..."); // Regions foreach (var reg in Region.LoadRegions()) { _regions.Add(reg.ID, reg); } // Zones foreach (var zone in Zone2.LoadZones(_regions)) { _zones.Add(zone.ID, zone); } // Navmeshs NavmeshMgr.InitRecast(); }
/// <summary> /// Loads all zones and associates them with the appropriate regions /// </summary> /// <param name="regions"></param> /// <returns></returns> public static IEnumerable <Zone2> LoadZones(Dictionary <ushort, Region> regions) { IniFile ini; using (Stream fs = ClientData.ZonesDat) ini = new IniFile(fs); // Load Zones var newZones = from entry in ini.Topics where entry.Name.StartsWith("zone") && entry.Items["enabled"] != "0" select new { ID = ushort.Parse(entry.Name.Substring("zone".Length)), Data = entry.Items }; int zones = 0; foreach (var entry in newZones) { ushort regionID = ushort.Parse(entry.Data["region"]); if (!regions.ContainsKey(regionID)) { regions.Add(regionID, new Region(regionID, "")); // some zones miss region entries } Region region = regions[regionID]; ushort zoneID = entry.ID; string name = entry.Data["name"]; ushort proxy = 0; if (entry.Data.ContainsKey("proxy_zone")) { proxy = ushort.Parse(entry.Data["proxy_zone"]); } int w = int.Parse(entry.Data["width"]) * 8192; int h = int.Parse(entry.Data["height"]) * 8192; int xoff = int.Parse(entry.Data["region_offset_x"]) * 8192; int yoff = int.Parse(entry.Data["region_offset_y"]) * 8192; var type = (eZoneType)int.Parse(entry.Data.ContainsKey("type") ? entry.Data["type"] : "0"); var zone = new Zone2(region, zoneID, name, xoff, yoff, w, h, type, proxy); region.Add(zoneID, zone); yield return(zone); zones++; } Log.Normal("Loaded {0} zones in {1} regions!", zones, regions.Count); }
public static bool GetPolyAt(Zone2 zone, Vector3 center, Vector3 extents, dtPolyFlags includeFilter, dtPolyFlags excludeFilter, ref uint polyRef, ref Vector3 point) { if (!_loadedZones.ContainsKey(zone.ID)) { return(false); } float[] outPoint = new float[3]; var ptrs = _loadedZones[zone.ID]; var status = GetPolyAt(ptrs.queryPtr, center.ToRecastFloats(), extents.ToRecastFloats(), new dtPolyFlags[] { includeFilter, excludeFilter }, ref polyRef, outPoint); point = new Vector3(outPoint[0] * INV_FACTOR, outPoint[2] * INV_FACTOR, outPoint[1] * INV_FACTOR); if ((status & dtStatus.DT_FAILURE) == dtStatus.DT_FAILURE) { return(false); } return(true); }
/// <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 static bool QueryPolygons(Zone2 zone, Vector3 center, Vector3 polyPickExt, dtPolyFlags includeFlags, dtPolyFlags excludeFlags, ref uint[] results, int maxResults = 32) { if (!_loadedZones.ContainsKey(zone.ID)) { return(false); } var resultIdBuffer = new uint[maxResults]; var resultCount = 0; var ptrs = _loadedZones[zone.ID]; var status = QueryPolygons(ptrs.queryPtr, center.ToRecastFloats(), polyPickExt.ToRecastFloats(), new dtPolyFlags[] { includeFlags, excludeFlags }, resultIdBuffer, ref resultCount, maxResults); if ((status & dtStatus.DT_FAILURE) == dtStatus.DT_FAILURE) { return(false); } results = new uint[resultCount]; Array.Copy(resultIdBuffer, results, resultCount); return(true); }
/// <summary> /// Returns the closest point on the navmesh (UNTESTED! EXPERIMENTAL! WILL GO SUPERNOVA ON USE! MAYBE!?) /// </summary> public static Vector3?GetClosestPoint(Zone2 zone, Vector3 position, float xRange = 256f, float yRange = 256f, float zRange = 256f) { if (!_loadedZones.ContainsKey(zone.ID)) { return(position); } var ptrs = _loadedZones[zone.ID]; var center = (position + zAxis * 8).ToRecastFloats(); var outVec = new float[3]; var defaultInclude = (dtPolyFlags.ALL ^ dtPolyFlags.DISABLED); var defaultExclude = (dtPolyFlags)0; var filter = new dtPolyFlags[] { defaultInclude, defaultExclude }; var polyPickEx = new Vector3(xRange, yRange, zRange).ToRecastFloats(); FindClosestPoint(ptrs.queryPtr, center, polyPickEx, filter, outVec); var result = new Vector3(outVec[0] * INV_FACTOR, outVec[2] * INV_FACTOR, outVec[1] * INV_FACTOR); return(result == Vector3.Zero ? null : (Vector3?)result); }
/// <summary> /// Returns a random point on the navmesh around the given position /// </summary> /// <param name="zone">Zone</param> /// <param name="position">Start in GlobalXYZ</param> /// <param name="radius">End in GlobalXYZ</param> /// <returns></returns> public static Vector3 GetRandomPoint(Zone2 zone, Vector3 position, float radius) { if (!_loadedZones.ContainsKey(zone.ID)) { return(Vector3.Zero); } var ptrs = _loadedZones[zone.ID]; var center = (position + zAxis * 8).ToRecastFloats(); var cradius = (radius * CONVERSION_FACTOR); var outVec = new float[3]; var defaultInclude = (dtPolyFlags.ALL ^ dtPolyFlags.DISABLED); var defaultExclude = (dtPolyFlags)0; var filter = new dtPolyFlags[] { defaultInclude, defaultExclude }; var polyPickEx = new float[3] { 2.0f, 4.0f, 2.0f }; FindRandomPointAroundCircle(ptrs.queryPtr, center, cradius, polyPickEx, filter, outVec); return(new Vector3(outVec[0] * INV_FACTOR, outVec[2] * INV_FACTOR, outVec[1] * INV_FACTOR)); }
/// <summary> /// Builds the navmesh for the specified zone /// </summary> public static void BuildNavMesh(Zone2 z) { if (z.Name == "ArtOutside" || z.Name == "ArtInside") { Log.Normal("Skipping zone {0} because it has name {1}", z, z.Name); return; } if (z.ProxyZone != 0) { Log.Normal("Skipping zone {0} because it has a proxy zone id {1}", z, z.ProxyZone); return; } string obj = z.ObjFile; string nav = z.NavFile.Replace(".gz", ""); for (int i = 0; i <= 1; i++) { // Create .obj if (i == 1) { LoadZoneMesh(z); } else { Unload(z); } DateTime start = DateTime.Now; Log.Normal("Building navmesh for zone {0} (pass={1})...", z, i); if (File.Exists(obj)) { File.Delete(obj); } using (var exp = new Zone2Obj(z)) exp.Export(); Unload(z); if (Program.Arguments.ExportObjOnly) { return; } // .obj -> .nav //if (i == 0) { Log.Normal("Running buildnav.exe for {0}", z.Name); Process buildnav = Process.Start("buildnav.exe", Util.MakeProcessArguments(new[] { obj.Replace(".obj", ".geomset"), nav })); buildnav.PriorityClass = ProcessPriorityClass.BelowNormal; buildnav.WaitForExit(); if (buildnav.ExitCode > 0) { throw new InvalidOperationException("buildnav.exe failed with " + buildnav.ExitCode); } if (!File.Exists(nav)) { Log.Error("Did not generate navmesh for file {0} for unknown reasons", nav); } else if (new FileInfo(nav).Length < 2048) { // empty mesh Log.Warn("{0} was empty :(", nav); File.Delete(nav); } } Log.Normal("Zone {0} finished in {1}", z, DateTime.Now - start); lock (_loadedZones) { if (_loadedZones.ContainsKey(z.ID)) { _loadedZones.Remove(z.ID); } } } }
/// <summary> /// True if pathing is enabled for the specified region /// </summary> public static bool IsPathingEnabled(Zone2 zone) { return(_loadedZones.ContainsKey(zone.ID)); }