private void DynamicNavigationMeshSystemOnNavigationMeshUpdatedUpdated(object sender, NavigationMeshUpdatedEventArgs eventArgs) { var newNavigationMesh = eventArgs.BuildResult.NavigationMesh; NavigationMeshData data; if (eventArgs.OldNavigationMesh != null && loadedNavigationMeshes.TryGetValue(eventArgs.OldNavigationMesh, out data)) { // Move to new navigation mesh loadedNavigationMeshes.Remove(eventArgs.OldNavigationMesh); loadedNavigationMeshes.Add(newNavigationMesh, data); data.NavigationMesh = newNavigationMesh; // Replace tiles in recast navigation mesh for all loaded groups var updatedLayers = eventArgs.BuildResult.UpdatedLayers.ToDictionary(x => x.GroupId); var oldGroupKeys = data.LoadedGroups.Keys.ToList(); foreach (var oldGroupKey in oldGroupKeys) { var loadedGroup = data.LoadedGroups[oldGroupKey]; // See if this layer was updated NavigationMeshLayerUpdateInfo layerUpdateInfo; if (!updatedLayers.TryGetValue(oldGroupKey, out layerUpdateInfo)) { continue; } // Check if the new navigation mesh contains this layer // if it does not, that means it was removed completely and we // will remove all the loaded tiles in the loop below NavigationMeshLayer newLayer = null; newNavigationMesh.Layers.TryGetValue(oldGroupKey, out newLayer); foreach (var updatedTileCoord in layerUpdateInfo.UpdatedTiles) { NavigationMeshTile newTile = null; if (newLayer != null) { if (!newLayer.Tiles.TryGetValue(updatedTileCoord, out newTile)) { continue; } } // Either add the tile if it is contained in the new navigation mesh or // try to remove it if it does not if (newTile != null) { loadedGroup.RecastNavigationMesh.AddOrReplaceTile(newTile.Data); } else { loadedGroup.RecastNavigationMesh.RemoveTile(updatedTileCoord); } } } } // Update loaded navigation meshes for components that are useing it, // in case a group was added var componentsToUpdate = ComponentDatas.Values.Where(x => x.Component.NavigationMesh == null).ToArray(); foreach (var component in componentsToUpdate) { UpdateNavigationMesh(component); } }
private NavigationMeshDebugVisual CreateDebugVisual(NavigationMesh navigationMesh, NavigationMesh previousNavigationMesh) { NavigationMeshDebugVisual ret = new NavigationMeshDebugVisual(); ret.DebugEntity = new Entity($"Debug entity for navigation mesh"); // Create a visual for every layer with a separate color using (var layers = navigationMesh.Layers.GetEnumerator()) { while (layers.MoveNext()) { Model model = new Model(); var currentLayer = layers.Current.Value; var currentId = layers.Current.Key; NavigationMeshDisplayGroup displayGroup; if (!groupDisplaySettings.TryGetValue(currentId, out displayGroup)) { continue; // No display settings for this group } model.Add(displayGroup.Material); model.Add(displayGroup.HighlightMaterial); foreach (var p in currentLayer.Tiles) { bool updated = true; NavigationMeshTile tile = p.Value; // Extract vertex data List <Vector3> tileVertexList = new List <Vector3>(); List <int> tileIndexList = new List <int>(); if (!tile.GetTileVertices(tileVertexList, tileIndexList)) { continue; } // Check if updated NavigationMeshLayer sourceLayer; if (previousNavigationMesh != null && previousNavigationMesh.Layers.TryGetValue(currentId, out sourceLayer)) { NavigationMeshTile oldTile = sourceLayer.FindTile(p.Key); if (oldTile != null && oldTile.Data.SequenceEqual(tile.Data)) { updated = false; } } // Stack layers vertically Vector3 offset = new Vector3(0.0f, LayerHeightMultiplier * displayGroup.Index, 0.0f); // Calculate mesh bounding box from navigation mesh points BoundingBox bb = BoundingBox.Empty; List <VertexPositionNormalTexture> meshVertices = new List <VertexPositionNormalTexture>(); for (int i = 0; i < tileVertexList.Count; i++) { Vector3 position = tileVertexList[i] + offset; BoundingBox.Merge(ref bb, ref position, out bb); VertexPositionNormalTexture vert = new VertexPositionNormalTexture(); vert.Position = position; vert.Normal = Vector3.UnitY; vert.TextureCoordinate = new Vector2(0.5f, 0.5f); meshVertices.Add(vert); } MeshDraw draw; using (var meshData = new GeometricMeshData <VertexPositionNormalTexture>(meshVertices.ToArray(), tileIndexList.ToArray(), true)) { GeometricPrimitive primitive = new GeometricPrimitive(game.GraphicsDevice, meshData); ret.GeneratedDynamicPrimitives.Add(primitive); draw = primitive.ToMeshDraw(); } Mesh mesh = new Mesh { Draw = draw, MaterialIndex = updated ? 1 : 0, BoundingBox = bb }; model.Add(mesh); } // Create an entity per layer var layerEntity = new Entity($"Navigation group {currentId}"); // Add a new model component var modelComponent = new ModelComponent(model); layerEntity.Add(modelComponent); modelComponent.Enabled = displayGroup.IsVisible; ret.ModelComponents.Add(currentId, modelComponent); ret.DebugEntity.AddChild(layerEntity); } } return(ret); }
/// <summary> /// Builds a single tile for a given layer without adding it /// </summary> /// <param name="inputVertices">Input vertex data for the input mesh</param> /// <param name="inputIndices">Input index data for the input mesh</param> /// <param name="boundingBox">Bounding box of the tile</param> /// <param name="tileCoordinate">Tile coordinate to of the tile to build</param> /// <returns>Teh tile that was built</returns> private unsafe NavigationMeshTile BuildTileInternal(Vector3[] inputVertices, int[] inputIndices, BoundingBox boundingBox, Point tileCoordinate) { // Turn settings into native structure format NavigationAgentSettings agentSettings = AgentSettings; NavigationMeshTile tile = new NavigationMeshTile(); // Initialize navigation builder IntPtr nav = Navigation.CreateBuilder(); // Turn build settings into native structure format Navigation.BuildSettings internalBuildSettings = new Navigation.BuildSettings { // Tile settings BoundingBox = boundingBox, TilePosition = tileCoordinate, TileSize = BuildSettings.TileSize, // General build settings CellHeight = BuildSettings.CellHeight, CellSize = BuildSettings.CellSize, RegionMinArea = BuildSettings.MinRegionArea, RegionMergeArea = BuildSettings.RegionMergeArea, EdgeMaxLen = BuildSettings.MaxEdgeLen, EdgeMaxError = BuildSettings.MaxEdgeError, DetailSampleDist = BuildSettings.DetailSamplingDistance, DetailSampleMaxError = BuildSettings.MaxDetailSamplingError, // Agent settings AgentHeight = agentSettings.Height, AgentRadius = agentSettings.Radius, AgentMaxClimb = agentSettings.MaxClimb, AgentMaxSlope = agentSettings.MaxSlope.Degrees }; Navigation.SetSettings(nav, new IntPtr(&internalBuildSettings)); // Generate mesh Navigation.GeneratedData data; IntPtr ret = Navigation.Build(nav, inputVertices.ToArray(), inputVertices.Length, inputIndices.ToArray(), inputIndices.Length); Navigation.GeneratedData* dataPtr = (Navigation.GeneratedData*)ret; data = *dataPtr; // Copy output data on success if (data.Success) { List<Vector3> outputVerts = new List<Vector3>(); if (data.NumNavmeshVertices > 0) { Vector3* navmeshVerts = (Vector3*)data.NavmeshVertices; for (int j = 0; j < data.NumNavmeshVertices; j++) { outputVerts.Add(navmeshVerts[j]); } tile.MeshVertices = outputVerts.ToArray(); } // Copy the generated navigationMesh data tile.Data = new byte[data.NavmeshDataLength]; Marshal.Copy(data.NavmeshData, tile.Data, 0, data.NavmeshDataLength); } // Cleanup builder Navigation.DestroyBuilder(nav); return tile; }