// Only grid cells connected vis cells with Walk movement flags will be treated as neighbours public void AddNeighbour(GridCell grid_cell) { if (AABB.Intersect(grid_cell.AABB, true) == null) return; if (Neighbours.Exists(x => x.cell.Equals(grid_cell))) return; bool any_cells_connected = false; MovementFlag connection_flags = MovementFlag.None; foreach (Cell our_cell in Cells) { foreach (Cell other_cell in grid_cell.Cells) { bool cells_connected = our_cell.AddNeighbour(other_cell); if (cells_connected) { any_cells_connected = true; MovementFlag flags = our_cell.Flags & other_cell.Flags; if (flags > connection_flags) connection_flags = flags; } } } if (any_cells_connected) { Neighbours.Add(new Neighbour(grid_cell, null, connection_flags)); grid_cell.Neighbours.Add(new Neighbour(this, null, connection_flags)); } }
public static void Render(Nav.GridCell cell, PointF trans, PaintEventArgs e, bool draw_connections, bool draw_id) { DrawRectangle(e.Graphics, Pens.Black, trans, cell.Min, cell.Max); if (draw_connections) { foreach (Nav.Cell.Neighbour neighbour in cell.Neighbours) { DrawLine(e.Graphics, GRID_CELL_CONNECTION_PEN, trans, cell.Center, neighbour.cell.Center); } } if (draw_id) { DrawString(e.Graphics, Brushes.Black, trans, cell.Min, cell.GlobalId.ToString() + " (" + cell.Id.ToString() + ")", 15); } }
public bool Load(string filename, bool clear = true, bool force_update_align_plane = false) { if (filename == null) { return(false); } Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; //using (new Profiler("[Nav] Loaded nav data [%t]")) { if (clear) { Clear(); } try { StreamReader stream = new StreamReader(filename); if (stream != null) { string line; GridCell g_cell = null; Vec3 cell_shrink_size = Vec3.Empty; HashSet <region_data> avoid_areas = new HashSet <region_data>(); while ((line = stream.ReadLine()) != null) { string[] data = line.Split(' '); if (data[0] == "g") { // add previous grid cell Add(g_cell, false); m_LastCellId = 0; g_cell = new GridCell(float.Parse(data[1]), float.Parse(data[2]), float.Parse(data[3]), float.Parse(data[4]), float.Parse(data[5]), float.Parse(data[6]), (data.Length > 7 ? int.Parse(data[7]) : m_LastGridCellId++)); } else if (data[0] == "n") { MovementFlag flags = MovementFlag.Walk | MovementFlag.Fly; if (data.Length > 7) { flags = (MovementFlag)int.Parse(data[7]); } Cell n_cell = new Cell(new Vec3(float.Parse(data[1]), float.Parse(data[2]), float.Parse(data[3])) - cell_shrink_size, new Vec3(float.Parse(data[4]), float.Parse(data[5]), float.Parse(data[6])) - cell_shrink_size, flags, m_LastCellId++); g_cell.Add(n_cell); } else if (data[0] == "r") { avoid_areas.Add(new region_data(new AABB(float.Parse(data[1]), float.Parse(data[2]), float.Parse(data[3]), float.Parse(data[4]), float.Parse(data[5]), float.Parse(data[6])), float.Parse(data[7]))); } } // add last grid cell Add(g_cell, false); if (force_update_align_plane) { using (new ReadLock(DataLock)) { foreach (Cell c in m_AllCells) { c.UpdateAlignPlane(); } } } Random rng = new Random(); GridCell random_grid_cell = null; random_grid_cell = m_GridCells.ElementAt(rng.Next(m_GridCells.Count)); NotifyOnNavDataChanged(); Regions = avoid_areas; Log("[Nav] Navmesh loaded."); stream.Close(); return(true); } } catch (Exception) { } Log("[Nav] Navmesh load failed!"); return(false); } }
internal void OnGridCellAdded(GridCell grid_cell, bool trigger_nav_data_change = true) { //using (new Profiler("[Nav] Nav data updated [{t}]")) using (new ReadLock(DataLock, true)) { // remove explore cells overlapping with grid cell List<ExploreCell> cells_to_validate = m_ExploreCells.FindAll(x => x.Overlaps2D(grid_cell)); using (new WriteLock(DataLock)) { foreach (ExploreCell explore_cell in cells_to_validate) { explore_cell.Detach(); m_ExploreCells.RemoveAll(x => x.GlobalId == explore_cell.GlobalId); m_ExploreCellsDistancer.Disconnect(explore_cell.GlobalId); } } // check if new explore cells should be added int x_min = (int)Math.Floor(grid_cell.Min.X / EXPLORE_CELL_SIZE); int y_min = (int)Math.Floor(grid_cell.Min.Y / EXPLORE_CELL_SIZE); int x_max = (int)Math.Ceiling(grid_cell.Max.X / EXPLORE_CELL_SIZE); int y_max = (int)Math.Ceiling(grid_cell.Max.Y / EXPLORE_CELL_SIZE); int explore_cells_generated = 0; for (int y_index = y_min; y_index < y_max; ++y_index) { for (int x_index = x_min; x_index < x_max; ++x_index) { AABB cell_aabb = new AABB(x_index * EXPLORE_CELL_SIZE, y_index * EXPLORE_CELL_SIZE, -10000, (x_index + 1) * EXPLORE_CELL_SIZE, (y_index + 1) * EXPLORE_CELL_SIZE, 10000); explore_cells_generated += GenerateExploreCells(cell_aabb); } } if (explore_cells_generated > 0) Navmesh.Log("[Nav] " + explore_cells_generated + " explore cell(s) generated"); } //due to performance reasons OnNavDataChange is not automatically called after adding each grid cell if (trigger_nav_data_change) OnNavDataChange(); }
private void UpdateRegions() { // copy current regions to avoid acquiring lock later HashSet <region_data> regions_copy = Regions; using (new WriteLock(DataLock)) //using (new Profiler($"Updatind {m_Regions.Count} regions took %t")) { foreach (var data in m_CellsOverlappedByRegions) { data.Value.areas.Clear(); } if (RegionsEnabled) { // update cells overlapped by avoid areas foreach (region_data region in regions_copy) { var g_cells = m_GridCells.Where(x => x.AABB.Overlaps2D(region.area)); List <Cell> cells = new List <Cell>(); // do not ignore disabled cells on purpose so we don't have to iterate separately over foreach (GridCell g_cell in g_cells) { cells.AddRange(g_cell.Cells.FindAll(x => !x.Replacement && x.HasFlags(MovementFlag.Walk) && x.AABB.Overlaps2D(region.area))); } foreach (Cell cell in cells) { overlapped_cell_data data = null; if (!m_CellsOverlappedByRegions.TryGetValue(cell.GlobalId, out data)) { m_CellsOverlappedByRegions[cell.GlobalId] = data = new overlapped_cell_data(cell); } data.areas.Add(new region_data(region)); } } } List <int> inactive_keys = new List <int>(); bool anything_changed = false; // perform rectangulation foreach (var item in m_CellsOverlappedByRegions) { // no longer overlapped if (item.Value.areas.Count == 0) { inactive_keys.Add(item.Key); } overlapped_cell_data data = item.Value; if (!data.areas.SetEquals(data.last_areas)) { anything_changed = true; GridCell parent_grid_cell = GetGridCell(data.replaced_cell.Center); // grid cell containing this replacement has been removed if (parent_grid_cell == null) { continue; } List <Cell> potential_neighbors = data.replaced_cell.Neighbours.Select(x => x.cell).ToList(); { // disable original cell if (data.last_areas.Count == 0) { data.replaced_cell.Disabled = true; } // remove previous replacement cells foreach (Cell replacement_cell in data.replacement_cells) { parent_grid_cell.Remove(replacement_cell); m_AllCells.Remove(replacement_cell); } data.replacement_cells.Clear(); } if (data.areas.Count > 0) { // generate new replacement cells data.replacement_cells.Add(data.replaced_cell); foreach (region_data region in data.areas) { List <Cell> cells_to_check = new List <Cell>(data.replacement_cells); foreach (Cell c in cells_to_check) { AABB[] extracted = c.AABB.Extract2D(region.area); if (extracted != null) { data.replacement_cells.Remove(c); for (int k = 0; k < extracted.Length; ++k) { // in case of negative movement cost treat this region as dynamic obstacle if (region.move_cost_mult < 0 && k == 0) { continue; } float movement_cost_mult = c.MovementCostMult; // first cell is always the one overlapped by region if (k == 0) { if (RegionsMoveCostMode == RegionsMode.Mult) { movement_cost_mult *= region.move_cost_mult; } else if (RegionsMoveCostMode == RegionsMode.Max) { if (movement_cost_mult < 1 && region.move_cost_mult < 1) { movement_cost_mult = Math.Min(region.move_cost_mult, movement_cost_mult); } else { movement_cost_mult = Math.Max(region.move_cost_mult, movement_cost_mult); } } } Cell r_cell = new Cell(extracted[k], data.replaced_cell.Flags, movement_cost_mult); r_cell.Replacement = true; data.replacement_cells.Add(r_cell); } } } } // try to connect new replacement cells with potential neighbors foreach (Cell replacement_cell in data.replacement_cells) { Vec3 border_point = null; foreach (Cell potential_neighbor in potential_neighbors) { replacement_cell.AddNeighbour(potential_neighbor, ref border_point); } parent_grid_cell.Cells.Add(replacement_cell); m_AllCells.Add(replacement_cell); // this cell is now potential neighbor as well, because can be interconnected with other replacement cells! potential_neighbors.Add(replacement_cell); } } item.Value.last_areas = new HashSet <region_data>(item.Value.areas); // enable original cell when no longer overlapped if (data.areas.Count == 0) { data.replaced_cell.Disabled = false; } m_CellsCache.Clear(); } } if (anything_changed) { NotifyOnNavDataChanged(); } // remove inactive data foreach (int key in inactive_keys) { m_CellsOverlappedByRegions.Remove(key); } } }
protected virtual void OnDeserialize(BinaryReader r) { using (new WriteLock(DataLock)) using (new WriteLock(InputLock)) { //using (new Profiler("Navmesh deserialization took %t")) { m_AllCells.Clear(); m_GridCells.Clear(); m_Regions.Clear(); m_CellsOverlappedByRegions.Clear(); Cell.CompareByGlobalId comp_by_global_id = new Cell.CompareByGlobalId(); int all_cells_count = r.ReadInt32(); // pre-allocate cells for (int i = 0; i < all_cells_count; ++i) { Cell cell = new Cell(0, 0, 0, 0, 0, 0, MovementFlag.None); cell.GlobalId = r.ReadInt32(); m_AllCells.Add(cell); } foreach (Cell cell in m_AllCells) { cell.Deserialize(m_AllCells, r); } Cell.LastCellGlobalId = r.ReadInt32(); int grid_cells_count = r.ReadInt32(); // pre-allocate grid cells for (int i = 0; i < grid_cells_count; ++i) { GridCell grid_cell = new GridCell(0, 0, 0, 0, 0, 0); grid_cell.GlobalId = r.ReadInt32(); m_GridCells.Add(grid_cell); } foreach (GridCell grid_cell in m_GridCells) { grid_cell.Deserialize(m_GridCells, m_AllCells, r); } GridCell.LastGridCellGlobalId = r.ReadInt32(); List <CellsPatch> patches = new List <CellsPatch>(); int patches_count = r.ReadInt32(); // pre-allocate patches for (int i = 0; i < patches_count; ++i) { CellsPatch patch = new CellsPatch(new HashSet <Cell>(), MovementFlag.None); patch.GlobalId = r.ReadInt32(); patches.Add(patch); } foreach (CellsPatch patch in patches) { patch.Deserialize(m_AllCells, r); } m_CellsPatches = new HashSet <CellsPatch>(patches); CellsPatch.LastCellsPatchGlobalId = r.ReadInt32(); int regions_count = r.ReadInt32(); for (int i = 0; i < regions_count; ++i) { m_Regions.Add(new region_data(r)); } int cells_overlapped_by_regions_count = r.ReadInt32(); for (int i = 0; i < cells_overlapped_by_regions_count; ++i) { int key = r.ReadInt32(); m_CellsOverlappedByRegions.Add(key, new overlapped_cell_data(m_AllCells, r)); } } } Log("[Nav] Navmesh deserialized."); }
public virtual void OnGridCellAdded(GridCell grid_cell) { }
public bool Load(string filename, bool clear = true) { if (filename == null) { return(false); } //using (new Profiler("[Nav] Loaded nav data [{t}]")) { if (clear) { Clear(); } try { StreamReader stream = new StreamReader(filename); if (stream != null) { string line; GridCell g_cell = null; Vec3 cell_shrink_size = Vec3.Empty; HashSet <region_data> avoid_areas = new HashSet <region_data>(); CultureInfo inv = (CultureInfo)CultureInfo.CurrentCulture.Clone(); inv.NumberFormat.CurrencyDecimalSeparator = ","; inv.NumberFormat.NumberDecimalSeparator = ","; while ((line = stream.ReadLine()) != null) { string[] data = line.Split(' '); if (data[0] == "g") { // add previous grid cell Add(g_cell, false); m_LastCellId = 0; g_cell = new GridCell(float.Parse(data[1], inv), float.Parse(data[2], inv), float.Parse(data[3], inv), float.Parse(data[4], inv), float.Parse(data[5], inv), float.Parse(data[6], inv), (data.Length > 7 ? int.Parse(data[7]) : m_LastGridCellId++)); } else if (data[0] == "n") { MovementFlag flags = MovementFlag.Walk | MovementFlag.Fly; if (data.Length > 7) { flags = (MovementFlag)int.Parse(data[7]); } Cell n_cell = new Cell(new Vec3(float.Parse(data[1], inv), float.Parse(data[2], inv), float.Parse(data[3], inv)) - cell_shrink_size, new Vec3(float.Parse(data[4], inv), float.Parse(data[5], inv), float.Parse(data[6], inv)) - cell_shrink_size, flags, m_LastCellId++); g_cell.Add(n_cell); } else if (data[0] == "r") { avoid_areas.Add(new region_data(new AABB(float.Parse(data[1], inv), float.Parse(data[2], inv), float.Parse(data[3], inv), float.Parse(data[4], inv), float.Parse(data[5], inv), float.Parse(data[6], inv)), float.Parse(data[7], inv))); } else if (data[0] == "s") { Navigator.CurrentPos = new Vec3(float.Parse(data[1], inv), float.Parse(data[2], inv), float.Parse(data[3], inv)); } else if (data[0] == "e") { Navigator.Destination = new Vec3(float.Parse(data[1], inv), float.Parse(data[2], inv), float.Parse(data[3], inv)); } } // add last grid cell Add(g_cell, false); Random rng = new Random(); GridCell random_grid_cell = null; random_grid_cell = m_GridCells[rng.Next(m_GridCells.Count)]; if (Explorator != null) { Explorator.OnNavDataChange(); } Regions = avoid_areas; Log("[Nav] Navmesh loaded."); stream.Close(); return(true); } } catch (Exception) { } Log("[Nav] Navmesh load failed!"); return(false); } }
protected virtual void OnDeserialize(BinaryReader r) { using (new WriteLock(DataLock)) using (new WriteLock(InputLock)) { //using (new Profiler("Navmesh deserialization took {t}")) { m_AllCells.Clear(); m_GridCells.Clear(); m_Regions.Clear(); m_CellsOverlappedByRegions.Clear(); Cell.CompareByGlobalId comp_by_global_id = new Cell.CompareByGlobalId(); int all_cells_count = r.ReadInt32(); // pre-allocate cells for (int i = 0; i < all_cells_count; ++i) { Cell cell = new Cell(0, 0, 0, 0, 0, 0, MovementFlag.None); cell.GlobalId = r.ReadInt32(); m_AllCells.Add(cell); } m_AllCells.Sort(comp_by_global_id); foreach (Cell cell in m_AllCells) { cell.Deserialize(m_AllCells, r); } Cell.LastCellGlobalId = r.ReadInt32(); int grid_cells_count = r.ReadInt32(); // pre-allocate grid cells for (int i = 0; i < grid_cells_count; ++i) { GridCell grid_cell = new GridCell(0, 0, 0, 0, 0, 0); grid_cell.GlobalId = r.ReadInt32(); m_GridCells.Add(grid_cell); } m_GridCells.Sort(comp_by_global_id); foreach (GridCell grid_cell in m_GridCells) { grid_cell.Deserialize(m_GridCells, m_AllCells, r); } GridCell.LastGridCellGlobalId = r.ReadInt32(); int regions_count = r.ReadInt32(); for (int i = 0; i < regions_count; ++i) { m_Regions.Add(new region_data(r)); } int cells_overlapped_by_regions_count = r.ReadInt32(); for (int i = 0; i < cells_overlapped_by_regions_count; ++i) { int key = r.ReadInt32(); m_CellsOverlappedByRegions.Add(key, new overlapped_cell_data(m_AllCells, r)); } Navigator.Deserialize(m_AllCells, r); if (Explorator != null) { Explorator.Deserialize(m_AllCells, r); } } } Log("[Nav] Navmesh deserialized."); }