void InitializeTileInfo()
        {
            // Voxel grid size
            int totalVoxelWidth = (int)(forcedBoundsSize.x / cellSize + 0.5f);
            int totalVoxelDepth = (int)(forcedBoundsSize.z / cellSize + 0.5f);

            if (!useTiles)
            {
                tileSizeX = totalVoxelWidth;
                tileSizeZ = totalVoxelDepth;
            }
            else
            {
                tileSizeX = editorTileSize;
                tileSizeZ = editorTileSize;
            }

            // Number of tiles
            tileXCount = (totalVoxelWidth + tileSizeX - 1) / tileSizeX;
            tileZCount = (totalVoxelDepth + tileSizeZ - 1) / tileSizeZ;

            if (tileXCount * tileZCount > TileIndexMask + 1)
            {
                throw new System.Exception("Too many tiles (" + (tileXCount * tileZCount) + ") maximum is " + (TileIndexMask + 1) +
                                           "\nTry disabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* inspector.");
            }

            tiles = new NavmeshTile[tileXCount * tileZCount];
        }
示例#2
0
        void IUpdatableGraph.UpdateArea(GraphUpdateObject guo)
        {
            // Figure out which tiles are affected
            // Expand TileBorderSizeInWorldUnits voxels in all directions to make sure
            // all tiles that could be affected by the update are recalculated.
            var affectedTiles = GetTouchingTiles(guo.bounds, TileBorderSizeInWorldUnits);

            if (!guo.updatePhysics)
            {
                for (int z = affectedTiles.ymin; z <= affectedTiles.ymax; z++)
                {
                    for (int x = affectedTiles.xmin; x <= affectedTiles.xmax; x++)
                    {
                        NavmeshTile tile = tiles[z * tileXCount + x];
                        NavMeshGraph.UpdateArea(guo, tile);
                    }
                }
                return;
            }

            Voxelize vox = globalVox;

            if (vox == null)
            {
                throw new System.InvalidOperationException("No Voxelizer object. UpdateAreaInit should have been called before this function.");
            }

            AstarProfiler.StartProfile("Build Tiles");

            // Build the new tiles
            for (int x = affectedTiles.xmin; x <= affectedTiles.xmax; x++)
            {
                for (int z = affectedTiles.ymin; z <= affectedTiles.ymax; z++)
                {
                    stagingTiles.Add(BuildTileMesh(vox, x, z));
                }
            }

            uint graphIndex = (uint)AstarPath.active.data.GetGraphIndex(this);

            // Set the correct graph index
            for (int i = 0; i < stagingTiles.Count; i++)
            {
                NavmeshTile tile  = stagingTiles[i];
                GraphNode[] nodes = tile.nodes;

                for (int j = 0; j < nodes.Length; j++)
                {
                    nodes[j].GraphIndex = graphIndex;
                }
            }

            for (int i = 0; i < vox.inputMeshes.Count; i++)
            {
                vox.inputMeshes[i].Pool();
            }
            ListPool <RasterizationMesh> .Release(ref vox.inputMeshes);

            AstarProfiler.EndProfile("Build Tiles");
        }
示例#3
0
 private NavmeshTile CreateTile(Voxelize vox, VoxelMesh mesh, int x, int z, int threadIndex)
 {
     if (mesh.tris == null)
     {
         throw new ArgumentNullException("mesh.tris");
     }
     if (mesh.verts == null)
     {
         throw new ArgumentNullException("mesh.verts");
     }
     if (mesh.tris.Length % 3 != 0)
     {
         throw new ArgumentException("Indices array's length must be a multiple of 3 (mesh.tris)");
     }
     if (mesh.verts.Length >= 4095)
     {
         if (this.tileXCount * this.tileZCount == 1)
         {
             throw new ArgumentException("Too many vertices per tile (more than " + 4095 + ").\n<b>Try enabling tiling in the recast graph settings.</b>\n");
         }
         throw new ArgumentException("Too many vertices per tile (more than " + 4095 + ").\n<b>Try reducing tile size or enabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* Inspector</b>");
     }
     else
     {
         NavmeshTile navmeshTile = new NavmeshTile
         {
             x      = x,
             z      = z,
             w      = 1,
             d      = 1,
             tris   = mesh.tris,
             bbTree = new BBTree()
         };
         navmeshTile.vertsInGraphSpace = Utility.RemoveDuplicateVertices(mesh.verts, navmeshTile.tris);
         navmeshTile.verts             = (Int3[])navmeshTile.vertsInGraphSpace.Clone();
         this.transform.Transform(navmeshTile.verts);
         uint num = (uint)(this.active.data.graphs.Length + threadIndex);
         if (num > 255u)
         {
             throw new Exception("Graph limit reached. Multithreaded recast calculations cannot be done because a few scratch graph indices are required.");
         }
         int num2 = x + z * this.tileXCount;
         num2 <<= 12;
         TriangleMeshNode.SetNavmeshHolder((int)num, navmeshTile);
         object active = this.active;
         lock (active)
         {
             navmeshTile.nodes = base.CreateNodes(navmeshTile.tris, num2, num);
         }
         navmeshTile.bbTree.RebuildFrom(navmeshTile.nodes);
         NavmeshBase.CreateNodeConnections(navmeshTile.nodes);
         TriangleMeshNode.SetNavmeshHolder((int)num, null);
         return(navmeshTile);
     }
 }
示例#4
0
        protected void GenerateTileFromInputMesh(AStarPathfindingWalkableArea walkArea)
        {
            if (null == walkArea)
            {
                EB.Debug.LogWarning("GenerateTileFromInputMesh: walkArea is empty");
                return;
            }

            Mesh inputMesh = walkArea.gameObject.GetComponent <MeshFilter>().sharedMesh;

            if (null == inputMesh)
            {
                EB.Debug.LogWarning("GenerateTileFromInputMesh: inputMesh is empty");
                return;
            }

            List <Vector3> allPoints = new List <Vector3>(inputMesh.vertices);
            Bounds         bounds    = GameUtils.CalculateBounds(allPoints);

            forcedBoundsCenter = walkArea.transform.TransformPoint(bounds.center);
            forcedBoundsSize   = bounds.size;

            // this section changes the size of the bounds and tile, so that we always get one single tile
            SetUpNavMeshToFitOnOneTile();
            CalculateNumberOfTiles(ref tileXCount, ref tileZCount);
            Debug.Assert(tileXCount == 1 && tileZCount == 1, "Their should be only one tile when using a prebuilt nav mesh");

            tiles = new NavmeshTile[tileXCount * tileZCount];             // only one tile

            // ignore this setting
            scanEmptyGraph = false;

            // the Vector3 vertices in the mesh need to be converted to the APP Int3 format
            Int3[] Int3Verts = new Int3[inputMesh.vertices.Length];
            for (int i = 0; i < Int3Verts.Length; ++i)
            {
                Vector3 tempVert = inputMesh.vertices[i];
                tempVert = walkArea.transform.TransformPoint(tempVert);                 // get the world space position, rather than local space

                Int3Verts[i] = (Int3)tempVert;
            }
            tiles[0] = CreateTile(inputMesh.triangles, Int3Verts, 0, 0);             // our single tile

            //Assign graph index to nodes
            uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex(this);
            GraphNodeDelegateCancelable del = delegate(GraphNode n)
            {
                n.GraphIndex = graphIndex;
                return(true);
            };

            GetNodes(del);
        }
        protected override IEnumerable <Progress> ScanInternal()
        {
            cachedSourceMeshBoundsMin = sourceMesh != null ? sourceMesh.bounds.min : Vector3.zero;
            transform  = CalculateTransform();
            tileZCount = tileXCount = 1;
            tiles      = new NavmeshTile[tileZCount * tileXCount];
            TriangleMeshNode.SetNavmeshHolder(AstarPath.active.data.GetGraphIndex(this), this);

            if (sourceMesh == null)
            {
                FillWithEmptyTiles();
                yield break;
            }

            yield return(new Progress(0.0f, "Transforming Vertices"));

            forcedBoundsSize = sourceMesh.bounds.size * scale;
            Vector3[] vectorVertices = sourceMesh.vertices;
            var       intVertices    = ListPool <Int3> .Claim(vectorVertices.Length);

            var matrix = Matrix4x4.TRS(-sourceMesh.bounds.min * scale, Quaternion.identity, Vector3.one * scale);

            // Convert the vertices to integer coordinates and also position them in graph space
            // so that the minimum of the bounding box of the mesh is at the origin
            // (the vertices will later be transformed to world space)
            for (int i = 0; i < vectorVertices.Length; i++)
            {
                intVertices.Add((Int3)matrix.MultiplyPoint3x4(vectorVertices[i]));
            }

            yield return(new Progress(0.1f, "Compressing Vertices"));

            // Remove duplicate vertices
            Int3[] compressedVertices  = null;
            int[]  compressedTriangles = null;
            Polygon.CompressMesh(intVertices, new List <int>(sourceMesh.triangles), out compressedVertices,
                                 out compressedTriangles);
            ListPool <Int3> .Release(ref intVertices);

            yield return(new Progress(0.2f, "Building Nodes"));

            ReplaceTile(0, 0, compressedVertices, compressedTriangles);

            // Signal that tiles have been recalculated to the navmesh cutting system.
            navmeshUpdateData.OnRecalculatedTiles(tiles);
            if (OnRecalculatedTiles != null)
            {
                OnRecalculatedTiles(tiles.Clone() as NavmeshTile[]);
            }
        }
        public override IEnumerable <Progress> ScanInternal()
        {
            transform  = CalculateTransform();
            tileZCount = tileXCount = 1;
            tiles      = new NavmeshTile[tileZCount * tileXCount];
            TriangleMeshNode.SetNavmeshHolder(AstarPath.active.data.GetGraphIndex(this), this);

            if (sourceMesh == null)
            {
                FillWithEmptyTiles();
                yield break;
            }

            yield return(new Progress(0.0f, "Transforming Vertices"));

            forcedBoundsSize = sourceMesh.bounds.size * scale;
            Vector3[] vectorVertices = sourceMesh.vertices;
            var       intVertices    = ListPool <Int3> .Claim(vectorVertices.Length);

            var matrix = Matrix4x4.TRS(-sourceMesh.bounds.min * scale, Quaternion.identity, Vector3.one * scale);

            // Convert the vertices to integer coordinates and also position them in graph space
            // so that the minimum of the bounding box of the mesh is at the origin
            // (the vertices will later be transformed to world space)
            for (int i = 0; i < vectorVertices.Length; i++)
            {
                intVertices.Add((Int3)matrix.MultiplyPoint3x4(vectorVertices[i]));
            }

            yield return(new Progress(0.1f, "Compressing Vertices"));

            // Remove duplicate vertices
            Int3[] compressedVertices  = null;
            int[]  compressedTriangles = null;
            Polygon.CompressMesh(intVertices, new List <int>(sourceMesh.triangles), out compressedVertices, out compressedTriangles);
            ListPool <Int3> .Release(intVertices);

            yield return(new Progress(0.2f, "Building Nodes"));

            ReplaceTile(0, 0, compressedVertices, compressedTriangles);

            // This may be used by the TileHandlerHelper script to update the tiles
            // while taking NavmeshCuts into account after the graph has been completely recalculated.
            if (OnRecalculatedTiles != null)
            {
                OnRecalculatedTiles(tiles.Clone() as NavmeshTile[]);
            }
        }
示例#7
0
        void IUpdatableGraph.UpdateArea(GraphUpdateObject guo)
        {
            IntRect touchingTiles = base.GetTouchingTiles(guo.bounds);

            if (!guo.updatePhysics)
            {
                for (int i = touchingTiles.ymin; i <= touchingTiles.ymax; i++)
                {
                    for (int j = touchingTiles.xmin; j <= touchingTiles.xmax; j++)
                    {
                        NavmeshTile graph = this.tiles[i * this.tileXCount + j];
                        NavMeshGraph.UpdateArea(guo, graph);
                    }
                }
                return;
            }
            Voxelize voxelize = this.globalVox;

            if (voxelize == null)
            {
                throw new InvalidOperationException("No Voxelizer object. UpdateAreaInit should have been called before this function.");
            }
            for (int k = touchingTiles.xmin; k <= touchingTiles.xmax; k++)
            {
                for (int l = touchingTiles.ymin; l <= touchingTiles.ymax; l++)
                {
                    this.stagingTiles.Add(this.BuildTileMesh(voxelize, k, l, 0));
                }
            }
            uint graphIndex = (uint)AstarPath.active.data.GetGraphIndex(this);

            for (int m = 0; m < this.stagingTiles.Count; m++)
            {
                NavmeshTile navmeshTile = this.stagingTiles[m];
                GraphNode[] nodes       = navmeshTile.nodes;
                for (int n = 0; n < nodes.Length; n++)
                {
                    nodes[n].GraphIndex = graphIndex;
                }
            }
            for (int num = 0; num < voxelize.inputMeshes.Count; num++)
            {
                voxelize.inputMeshes[num].Pool();
            }
            ListPool <RasterizationMesh> .Release(voxelize.inputMeshes);

            voxelize.inputMeshes = null;
        }
示例#8
0
        /** Creates an outline of the navmesh for use in OnDrawGizmos in the editor */
        public static void CreateNavmeshOutlineVisualization(NavmeshTile tile, GraphGizmoHelper helper)
        {
            var sharedEdges = new bool[3];

            for (int j = 0; j < tile.nodes.Length; j++)
            {
                sharedEdges[0] = sharedEdges[1] = sharedEdges[2] = false;

                var node = tile.nodes[j];
                for (int c = 0; c < node.connections.Length; c++)
                {
                    var other = node.connections[c].node as TriangleMeshNode;

                    // Loop through neighbours to figure out which edges are shared
                    if (other != null && other.GraphIndex == node.GraphIndex)
                    {
                        for (int v = 0; v < 3; v++)
                        {
                            for (int v2 = 0; v2 < 3; v2++)
                            {
                                if (node.GetVertexIndex(v) == other.GetVertexIndex((v2 + 1) % 3) && node.GetVertexIndex((v + 1) % 3) == other.GetVertexIndex(v2))
                                {
                                    // Found a shared edge with the other node
                                    sharedEdges[v] = true;
                                    v = 3;
                                    break;
                                }
                            }
                        }
                    }
                }

                var color = helper.NodeColor(node);
                for (int v = 0; v < 3; v++)
                {
                    if (!sharedEdges[v])
                    {
                        helper.builder.DrawLine((Vector3)node.GetVertex(v), (Vector3)node.GetVertex((v + 1) % 3), color);
                    }
                }
            }
        }
示例#9
0
 // Token: 0x0600262B RID: 9771 RVA: 0x001A7DD0 File Offset: 0x001A5FD0
 void IUpdatableGraph.UpdateAreaPost(GraphUpdateObject guo)
 {
     for (int i = 0; i < this.stagingTiles.Count; i++)
     {
         NavmeshTile navmeshTile  = this.stagingTiles[i];
         int         num          = navmeshTile.x + navmeshTile.z * this.tileXCount;
         NavmeshTile navmeshTile2 = this.tiles[num];
         for (int j = 0; j < navmeshTile2.nodes.Length; j++)
         {
             navmeshTile2.nodes[j].Destroy();
         }
         this.tiles[num] = navmeshTile;
     }
     for (int k = 0; k < this.stagingTiles.Count; k++)
     {
         NavmeshTile tile = this.stagingTiles[k];
         base.ConnectTileWithNeighbours(tile, false);
     }
     if (this.OnRecalculatedTiles != null)
     {
         this.OnRecalculatedTiles(this.stagingTiles.ToArray());
     }
     this.stagingTiles.Clear();
 }
示例#10
0
        protected NavmeshTile CreateTile(int[] tris, Int3[] verts, int x, int z)
        {
#if BNICKSON_UPDATED
            if (tris == null)
            {
                throw new System.ArgumentNullException("The mesh must be valid. tris is null.");
            }
            if (verts == null)
            {
                throw new System.ArgumentNullException("The mesh must be valid. verts is null.");
            }

            //Create a new navmesh tile and assign its settings
            var tile = new NavmeshTile();

            tile.x      = x;
            tile.z      = z;
            tile.w      = 1;
            tile.d      = 1;
            tile.tris   = tris;
            tile.verts  = verts;
            tile.bbTree = new BBTree();
#endif

            if (tile.tris.Length % 3 != 0)
            {
                throw new System.ArgumentException("Indices array's length must be a multiple of 3 (mesh.tris)");
            }

            if (tile.verts.Length >= VertexIndexMask)
            {
                throw new System.ArgumentException("Too many vertices per tile (more than " + VertexIndexMask + ")." +
                                                   "\nTry enabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* Inspector");
            }

            //Dictionary<Int3, int> firstVerts = new Dictionary<Int3, int> ();
            Dictionary <Int3, int> firstVerts = cachedInt3_int_dict;
            firstVerts.Clear();

            var compressedPointers = new int[tile.verts.Length];

            int count = 0;
            for (int i = 0; i < tile.verts.Length; i++)
            {
                try
                {
                    firstVerts.Add(tile.verts[i], count);
                    compressedPointers[i] = count;
                    tile.verts[count]     = tile.verts[i];
                    count++;
                }
                catch
                {
                    //There are some cases, rare but still there, that vertices are identical
                    compressedPointers[i] = firstVerts[tile.verts[i]];
                }
            }

            for (int i = 0; i < tile.tris.Length; i++)
            {
                tile.tris[i] = compressedPointers[tile.tris[i]];
            }

            var compressed = new Int3[count];
            for (int i = 0; i < count; i++)
            {
                compressed[i] = tile.verts[i];
            }

            tile.verts = compressed;

            var nodes = new TriangleMeshNode[tile.tris.Length / 3];
            tile.nodes = nodes;

            //Here we are faking a new graph
            //The tile is not added to any graphs yet, but to get the position querys from the nodes
            //to work correctly (not throw exceptions because the tile is not calculated) we fake a new graph
            //and direct the position queries directly to the tile
            int graphIndex = AstarPath.active.astarData.graphs.Length;

            TriangleMeshNode.SetNavmeshHolder(graphIndex, tile);

            //This index will be ORed to the triangle indices
            int tileIndex = x + z * tileXCount;
            tileIndex <<= TileIndexOffset;

            //Create nodes and assign triangle indices
            for (int i = 0; i < nodes.Length; i++)
            {
                var node = new TriangleMeshNode(active);
                nodes[i]        = node;
                node.GraphIndex = (uint)graphIndex;
                node.v0         = tile.tris[i * 3 + 0] | tileIndex;
                node.v1         = tile.tris[i * 3 + 1] | tileIndex;
                node.v2         = tile.tris[i * 3 + 2] | tileIndex;

                //Degenerate triangles might occur, but they will not cause any large troubles anymore
                //if (Polygon.IsColinear (node.GetVertex(0), node.GetVertex(1), node.GetVertex(2))) {
                //	Debug.Log ("COLINEAR!!!!!!");
                //}

                //Make sure the triangle is clockwise
                if (!VectorMath.IsClockwiseXZ(node.GetVertex(0), node.GetVertex(1), node.GetVertex(2)))
                {
                    int tmp = node.v0;
                    node.v0 = node.v2;
                    node.v2 = tmp;
                }

                node.Walkable = true;
                node.Penalty  = initialPenalty;
                node.UpdatePositionFromVertices();
                tile.bbTree.Insert(node);
            }

            CreateNodeConnections(tile.nodes);

            //Remove the fake graph
            TriangleMeshNode.SetNavmeshHolder(graphIndex, null);

            return(tile);
        }
示例#11
0
        /** Create a tile at tile index \a x, \a z from the mesh.
         * \version Since version 3.7.6 the implementation is thread safe
         */
        NavmeshTile CreateTile(Voxelize vox, VoxelMesh mesh, int x, int z, int threadIndex)
        {
            if (mesh.tris == null)
            {
                throw new System.ArgumentNullException("mesh.tris");
            }
            if (mesh.verts == null)
            {
                throw new System.ArgumentNullException("mesh.verts");
            }
            if (mesh.tris.Length % 3 != 0)
            {
                throw new System.ArgumentException("Indices array's length must be a multiple of 3 (mesh.tris)");
            }
            if (mesh.verts.Length >= VertexIndexMask)
            {
                if (tileXCount * tileZCount == 1)
                {
                    throw new System.ArgumentException("Too many vertices per tile (more than " + VertexIndexMask + ")." +
                                                       "\n<b>Try enabling tiling in the recast graph settings.</b>\n");
                }
                else
                {
                    throw new System.ArgumentException("Too many vertices per tile (more than " + VertexIndexMask + ")." +
                                                       "\n<b>Try reducing tile size or enabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* Inspector</b>");
                }
            }

            // Create a new navmesh tile and assign its settings
            var tile = new NavmeshTile {
                x      = x,
                z      = z,
                w      = 1,
                d      = 1,
                tris   = mesh.tris,
                bbTree = new BBTree(),
                graph  = this,
            };

            tile.vertsInGraphSpace = Utility.RemoveDuplicateVertices(mesh.verts, tile.tris);
            tile.verts             = (Int3[])tile.vertsInGraphSpace.Clone();
            transform.Transform(tile.verts);

            // Here we are faking a new graph
            // The tile is not added to any graphs yet, but to get the position queries from the nodes
            // to work correctly (not throw exceptions because the tile is not calculated) we fake a new graph
            // and direct the position queries directly to the tile
            // The thread index is added to make sure that if multiple threads are calculating tiles at the same time
            // they will not use the same temporary graph index
            uint temporaryGraphIndex = (uint)(active.data.graphs.Length + threadIndex);

            if (temporaryGraphIndex > GraphNode.MaxGraphIndex)
            {
                // Multithreaded tile calculations use fake graph indices, see above.
                throw new System.Exception("Graph limit reached. Multithreaded recast calculations cannot be done because a few scratch graph indices are required.");
            }

            TriangleMeshNode.SetNavmeshHolder((int)temporaryGraphIndex, tile);
            // We need to lock here because creating nodes is not thread safe
            // and we may be doing this from multiple threads at the same time
            tile.nodes = new TriangleMeshNode[tile.tris.Length / 3];
            lock (active) {
                CreateNodes(tile.nodes, tile.tris, x + z * tileXCount, temporaryGraphIndex);
            }

            tile.bbTree.RebuildFrom(tile.nodes);
            CreateNodeConnections(tile.nodes);
            // Remove the fake graph
            TriangleMeshNode.SetNavmeshHolder((int)temporaryGraphIndex, null);

            return(tile);
        }
示例#12
0
        protected NavmeshTile BuildTileMesh(Voxelize vox, int x, int z, int threadIndex = 0)
        {
            AstarProfiler.StartProfile("Build Tile");
            AstarProfiler.StartProfile("Init");

            vox.borderSize   = TileBorderSizeInVoxels;
            vox.forcedBounds = CalculateTileBoundsWithBorder(x, z);
            vox.width        = tileSizeX + vox.borderSize * 2;
            vox.depth        = tileSizeZ + vox.borderSize * 2;

            if (!useTiles && relevantGraphSurfaceMode == RelevantGraphSurfaceMode.OnlyForCompletelyInsideTile)
            {
                // This best reflects what the user would actually want
                vox.relevantGraphSurfaceMode = RelevantGraphSurfaceMode.RequireForAll;
            }
            else
            {
                vox.relevantGraphSurfaceMode = relevantGraphSurfaceMode;
            }

            vox.minRegionSize = Mathf.RoundToInt(minRegionSize / (cellSize * cellSize));

            AstarProfiler.EndProfile("Init");


            // Init voxelizer
            vox.Init();
            vox.VoxelizeInput(transform, CalculateTileBoundsWithBorder(x, z));

            AstarProfiler.StartProfile("Filter Ledges");


            vox.FilterLedges(vox.voxelWalkableHeight, vox.voxelWalkableClimb, vox.cellSize, vox.cellHeight);

            AstarProfiler.EndProfile("Filter Ledges");

            AstarProfiler.StartProfile("Filter Low Height Spans");
            vox.FilterLowHeightSpans(vox.voxelWalkableHeight, vox.cellSize, vox.cellHeight);
            AstarProfiler.EndProfile("Filter Low Height Spans");

            vox.BuildCompactField();
            vox.BuildVoxelConnections();
            vox.ErodeWalkableArea(CharacterRadiusInVoxels);
            vox.BuildDistanceField();
            vox.BuildRegions();

            var cset = new VoxelContourSet();

            vox.BuildContours(contourMaxError, 1, cset, Voxelize.RC_CONTOUR_TESS_WALL_EDGES | Voxelize.RC_CONTOUR_TESS_TILE_EDGES);

            VoxelMesh mesh;

            vox.BuildPolyMesh(cset, 3, out mesh);

            AstarProfiler.StartProfile("Build Nodes");

            // Position the vertices correctly in graph space (all tiles are laid out on the xz plane with the (0,0) tile at the origin)
            for (int i = 0; i < mesh.verts.Length; i++)
            {
                mesh.verts[i] *= Int3.Precision;
            }
            vox.transformVoxel2Graph.Transform(mesh.verts);

            NavmeshTile tile = CreateTile(vox, mesh, x, z, threadIndex);

            AstarProfiler.EndProfile("Build Nodes");

            AstarProfiler.EndProfile("Build Tile");
            return(tile);
        }
示例#13
0
        protected void ScanAllTiles(OnScanStatus statusCallback)
        {
#if ASTARDEBUG
            System.Console.WriteLine("Recast Graph -- Collecting Meshes");
#endif

#if BNICKSON_UPDATED
            editorTileSize = (int)(EditorVars.GridSize / cellSize);
#endif

            //----

            //Voxel grid size
            int gw = (int)(forcedBounds.size.x / cellSize + 0.5f);
            int gd = (int)(forcedBounds.size.z / cellSize + 0.5f);

            if (!useTiles)
            {
                tileSizeX = gw;
                tileSizeZ = gd;
            }
            else
            {
                tileSizeX = editorTileSize;
                tileSizeZ = editorTileSize;
            }

            //Number of tiles
            int tw = (gw + tileSizeX - 1) / tileSizeX;
            int td = (gd + tileSizeZ - 1) / tileSizeZ;

            tileXCount = tw;
            tileZCount = td;

            if (tileXCount * tileZCount > TileIndexMask + 1)
            {
                throw new System.Exception("Too many tiles (" + (tileXCount * tileZCount) + ") maximum is " + (TileIndexMask + 1) +
                                           "\nTry disabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* inspector.");
            }

            tiles = new NavmeshTile[tileXCount * tileZCount];

#if ASTARDEBUG
            System.Console.WriteLine("Recast Graph -- Creating Voxel Base");
#endif

            // If this is true, just fill the graph with empty tiles
            if (scanEmptyGraph)
            {
                for (int z = 0; z < td; z++)
                {
                    for (int x = 0; x < tw; x++)
                    {
                        tiles[z * tileXCount + x] = NewEmptyTile(x, z);
                    }
                }
                return;
            }

            AstarProfiler.StartProfile("Finding Meshes");
            List <ExtraMesh> extraMeshes;

#if !NETFX_CORE || UNITY_EDITOR
            System.Console.WriteLine("Collecting Meshes");
#endif
            CollectMeshes(out extraMeshes, forcedBounds);

            AstarProfiler.EndProfile("Finding Meshes");

            // A walkableClimb higher than walkableHeight can cause issues when generating the navmesh since then it can in some cases
            // Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with navmesh without links)
            // The editor scripts also enforce this but we enforce it here too just to be sure
            walkableClimb = Mathf.Min(walkableClimb, walkableHeight);

            //Create the voxelizer and set all settings
            var vox = new Voxelize(cellHeight, cellSize, walkableClimb, walkableHeight, maxSlope);
            vox.inputExtraMeshes = extraMeshes;

            vox.maxEdgeLength = maxEdgeLength;

            int lastInfoCallback = -1;
            var watch            = System.Diagnostics.Stopwatch.StartNew();

            //Generate all tiles
            for (int z = 0; z < td; z++)
            {
                for (int x = 0; x < tw; x++)
                {
                    int tileNum = z * tileXCount + x;
#if !NETFX_CORE || UNITY_EDITOR
                    System.Console.WriteLine("Generating Tile #" + (tileNum) + " of " + td * tw);
#endif

                    //Call statusCallback only 10 times since it is very slow in the editor
                    if (statusCallback != null && (tileNum * 10 / tiles.Length > lastInfoCallback || watch.ElapsedMilliseconds > 2000))
                    {
                        lastInfoCallback = tileNum * 10 / tiles.Length;
                        watch.Reset();
                        watch.Start();

                        statusCallback(new Progress(Mathf.Lerp(0.1f, 0.9f, tileNum / (float)tiles.Length), "Building Tile " + tileNum + "/" + tiles.Length));
                    }

                    BuildTileMesh(vox, x, z);
                }
            }

#if !NETFX_CORE
            System.Console.WriteLine("Assigning Graph Indices");
#endif

            if (statusCallback != null)
            {
                statusCallback(new Progress(0.9f, "Connecting tiles"));
            }

            //Assign graph index to nodes
            uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex(this);

            GraphNodeDelegateCancelable del = delegate(GraphNode n) {
                n.GraphIndex = graphIndex;
                return(true);
            };
            GetNodes(del);

#if BNICKSON_UPDATED
#if DEBUG
            if (useCenterTileOnly && (3 != tileXCount || 3 != tileZCount))
            {
                EB.Debug.LogError("RecastGenerator.ScanAllTiles() : Incorrect amount of tiles generated if ceneter tile is all that is required");
            }
#endif

            int centerXTile = (tileXCount / 2);
            int centerZTile = (tileZCount / 2);
#endif

            for (int z = 0; z < td; z++)
            {
                for (int x = 0; x < tw; x++)
                {
#if BNICKSON_UPDATED
                    // if we're only using the center tile, and this is not the center tile
                    if (useCenterTileOnly && !(centerZTile == z && centerXTile == x))
                    {
                        continue;
                    }
#endif

#if !NETFX_CORE
                    System.Console.WriteLine("Connecing Tile #" + (z * tileXCount + x) + " of " + td * tw);
#endif
                    if (x < tw - 1)
                    {
                        ConnectTiles(tiles[x + z * tileXCount], tiles[x + 1 + z * tileXCount]);
                    }
                    if (z < td - 1)
                    {
                        ConnectTiles(tiles[x + z * tileXCount], tiles[x + (z + 1) * tileXCount]);
                    }
                }
            }

            AstarProfiler.PrintResults();
        }
示例#14
0
        /// <summary>Exports the INavmesh graph to a .obj file</summary>
        public static void ExportToFile(RecastGraph target)
        {
            //INavmesh graph = (INavmesh)target;
            if (target == null)
            {
                return;
            }

            NavmeshTile[] tiles = target.GetTiles();

            if (tiles == null)
            {
                if (EditorUtility.DisplayDialog("Scan graph before exporting?", "The graph does not contain any mesh data. Do you want to scan it?", "Ok", "Cancel"))
                {
                    AstarPathEditor.MenuScan();
                    tiles = target.GetTiles();
                    if (tiles == null)
                    {
                        return;
                    }
                }
                else
                {
                    return;
                }
            }

            string path = EditorUtility.SaveFilePanel("Export .obj", "", "navmesh.obj", "obj");

            if (path == "")
            {
                return;
            }

            //Generate .obj
            var sb = new System.Text.StringBuilder();

            string name = System.IO.Path.GetFileNameWithoutExtension(path);

            sb.Append("g ").Append(name).AppendLine();

            //Vertices start from 1
            int vCount = 1;

            //Define single texture coordinate to zero
            sb.Append("vt 0 0\n");

            for (int t = 0; t < tiles.Length; t++)
            {
                NavmeshTile tile = tiles[t];

                if (tile == null)
                {
                    continue;
                }

                Int3[] vertices = tile.verts;

                //Write vertices
                for (int i = 0; i < vertices.Length; i++)
                {
                    var v = (Vector3)vertices[i];
                    sb.Append(string.Format("v {0} {1} {2}\n", -v.x, v.y, v.z));
                }

                //Write triangles
                TriangleMeshNode[] nodes = tile.nodes;
                for (int i = 0; i < nodes.Length; i++)
                {
                    TriangleMeshNode node = nodes[i];
                    if (node == null)
                    {
                        Debug.LogError("Node was null or no TriangleMeshNode. Critical error. Graph type " + target.GetType().Name);
                        return;
                    }
                    if (node.GetVertexArrayIndex(0) < 0 || node.GetVertexArrayIndex(0) >= vertices.Length)
                    {
                        throw new System.Exception("ERR");
                    }

                    sb.Append(string.Format("f {0}/1 {1}/1 {2}/1\n", (node.GetVertexArrayIndex(0) + vCount), (node.GetVertexArrayIndex(1) + vCount), (node.GetVertexArrayIndex(2) + vCount)));
                }

                vCount += vertices.Length;
            }

            string obj = sb.ToString();

            using (var sw = new System.IO.StreamWriter(path))
            {
                sw.Write(obj);
            }
        }
示例#15
0
        /** Generate connections between the two tiles.
         * The tiles must be adjacent.
         */
        protected void ConnectTiles(NavmeshTile tile1, NavmeshTile tile2)
        {
            if (tile1 == null)
            {
                return;                           //throw new System.ArgumentNullException ("tile1");
            }
            if (tile2 == null)
            {
                return;                           //throw new System.ArgumentNullException ("tile2");
            }
            if (tile1.nodes == null)
            {
                throw new System.ArgumentException("tile1 does not contain any nodes");
            }
            if (tile2.nodes == null)
            {
                throw new System.ArgumentException("tile2 does not contain any nodes");
            }

            int t1x = Mathf.Clamp(tile2.x, tile1.x, tile1.x + tile1.w - 1);
            int t2x = Mathf.Clamp(tile1.x, tile2.x, tile2.x + tile2.w - 1);
            int t1z = Mathf.Clamp(tile2.z, tile1.z, tile1.z + tile1.d - 1);
            int t2z = Mathf.Clamp(tile1.z, tile2.z, tile2.z + tile2.d - 1);

            int coord, altcoord;
            int t1coord, t2coord;

            float tcs;

            if (t1x == t2x)
            {
                coord    = 2;
                altcoord = 0;
                t1coord  = t1z;
                t2coord  = t2z;
                tcs      = tileSizeZ * cellSize;
            }
            else if (t1z == t2z)
            {
                coord    = 0;
                altcoord = 2;
                t1coord  = t1x;
                t2coord  = t2x;
                tcs      = tileSizeX * cellSize;
            }
            else
            {
                throw new System.ArgumentException("Tiles are not adjacent (neither x or z coordinates match)");
            }

            if (Math.Abs(t1coord - t2coord) != 1)
            {
                EB.Debug.Log("{0} {1} {2} {3}\n{5} {6} {7} {8}\n{9} {10} {11} {12}", tile1.x, tile1.z, tile1.w, tile1.d,
                             tile2.x, tile2.z, tile2.w, tile2.d, t1x, t1z, t2x, t2z);
                throw new System.ArgumentException("Tiles are not adjacent (tile coordinates must differ by exactly 1. Got '" + t1coord + "' and '" + t2coord + "')");
            }

            //Midpoint between the two tiles
            int midpoint = (int)Math.Round((Math.Max(t1coord, t2coord) * tcs + forcedBounds.min[coord]) * Int3.Precision);

#if ASTARDEBUG
            Vector3 v1 = new Vector3(-100, 0, -100);
            Vector3 v2 = new Vector3(100, 0, 100);
            v1[coord] = midpoint * Int3.PrecisionFactor;
            v2[coord] = midpoint * Int3.PrecisionFactor;

            Debug.DrawLine(v1, v2, Color.magenta);
#endif

#if BNICKSON_UPDATED
            // different triangle link height tolerance based on whether we're linking tiles generated for random levels or not
            float heightToleranceSquared = generateFromInputMesh ? GameUtils.Square(heightZoneLinkTolerance) : GameUtils.Square(walkableClimb);
#endif
            TriangleMeshNode[] nodes1 = tile1.nodes;
            TriangleMeshNode[] nodes2 = tile2.nodes;

            //Find adjacent nodes on the border between the tiles
            for (int i = 0; i < nodes1.Length; i++)
            {
                TriangleMeshNode node = nodes1[i];
                int av = node.GetVertexCount();

                for (int a = 0; a < av; a++)
                {
                    Int3 ap1 = node.GetVertex(a);
                    Int3 ap2 = node.GetVertex((a + 1) % av);
#if BNICKSON_UPDATED
                    if (ap1[coord] == midpoint && ap2[coord] == midpoint)                     // this could be given a little bit of tolerance
#else
                    if (Math.Abs(ap1[coord] - midpoint) < 2 && Math.Abs(ap2[coord] - midpoint) < 2)
#endif
                    {
#if ASTARDEBUG
                        Debug.DrawLine((Vector3)ap1, (Vector3)ap2, Color.red);
#endif

                        int minalt = Math.Min(ap1[altcoord], ap2[altcoord]);
                        int maxalt = Math.Max(ap1[altcoord], ap2[altcoord]);

                        //Degenerate edge
                        if (minalt == maxalt)
                        {
                            continue;
                        }

                        for (int j = 0; j < nodes2.Length; j++)
                        {
                            TriangleMeshNode other = nodes2[j];
                            int bv = other.GetVertexCount();
                            for (int b = 0; b < bv; b++)
                            {
                                Int3 bp1 = other.GetVertex(b);
                                Int3 bp2 = other.GetVertex((b + 1) % av);
#if BNICKSON_UPDATED
                                if (bp1[coord] == midpoint && bp2[coord] == midpoint)                                 // this could be given a little bit of tolerance
#else
                                if (Math.Abs(bp1[coord] - midpoint) < 2 && Math.Abs(bp2[coord] - midpoint) < 2)
#endif
                                {
                                    int minalt2 = Math.Min(bp1[altcoord], bp2[altcoord]);
                                    int maxalt2 = Math.Max(bp1[altcoord], bp2[altcoord]);

                                    //Degenerate edge
                                    if (minalt2 == maxalt2)
                                    {
                                        continue;
                                    }

                                    if (maxalt > minalt2 && minalt < maxalt2)
                                    {
                                        //Adjacent

                                        //Test shortest distance between the segments (first test if they are equal since that is much faster)
                                        if ((ap1 == bp1 && ap2 == bp2) || (ap1 == bp2 && ap2 == bp1) ||
#if BNICKSON_UPDATED
                                            VectorMath.SqrDistanceSegmentSegment((Vector3)ap1, (Vector3)ap2, (Vector3)bp1, (Vector3)bp2) < heightToleranceSquared)                                             // different height tolerances based on generating from input mesh or not
#else
                                            VectorMath.SqrDistanceSegmentSegment((Vector3)ap1, (Vector3)ap2, (Vector3)bp1, (Vector3)bp2) < walkableClimb * walkableClimb)
#endif
                                        {
                                            uint cost = (uint)(node.position - other.position).costMagnitude;

                                            node.AddConnection(other, cost);
                                            other.AddConnection(node, cost);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
示例#16
0
        // generate a nav mesh with one tile per zone
        protected void GenerateTilesForZones(AStarPathfindingWalkableArea[] walkAreas)
        {
            if (null == walkAreas || 0 == walkAreas.Length)
            {
                EB.Debug.LogWarning("GenerateTilesForZones: walkAreas is empty");
                return;
            }

            LevelHelper levelHelper = GameObject.FindObjectOfType(typeof(LevelHelper)) as LevelHelper;

            if (null == levelHelper)
            {
                EB.Debug.LogWarning("GenerateTilesForZones: LevelHelper not found");
                return;
            }

            Vector3 NavMeshBoundingBoxMin;
            Vector3 NavMeshBoundingBoxMax;

            levelHelper.CalculateAllZonesMinMax(out NavMeshBoundingBoxMin, out NavMeshBoundingBoxMax);

            // set the entire bounds and center of the recast graph
            forcedBoundsSize   = NavMeshBoundingBoxMax - NavMeshBoundingBoxMin;
            forcedBoundsCenter = NavMeshBoundingBoxMin + (forcedBoundsSize * 0.5f);

            tileSizeX = tileSizeZ = (int)(EditorVars.GridSize / cellSize);             // this line sets the tile size so that each tile will hold the size of a zone
            CalculateNumberOfTiles(ref tileXCount, ref tileZCount);

#if DEBUG
            int correctTilesXCount = (Mathf.CeilToInt(forcedBoundsSize.x / EditorVars.GridSize));             // this would fail if GridSize is less than 1f
            int correctTilesZCount = (Mathf.CeilToInt(forcedBoundsSize.z / EditorVars.GridSize));             // this would fail if GridSize is less than 1f
            if (correctTilesXCount != tileXCount || correctTilesZCount != tileZCount)
            {
                EB.Debug.LogError("Incorrect number of tiles generated");
            }
#endif
            tiles = new NavmeshTile[tileXCount * tileZCount];

            // ignore this setting
            scanEmptyGraph = false;

            Vector3 tempZoneMin = new Vector3();
            Vector3 tempZoneMax = new Vector3();

            // go over all the walkable areas and put there mesh into a tile
            foreach (AStarPathfindingWalkableArea walk in walkAreas)
            {
                ZoneDescriptor zoneDescriptor = (ZoneDescriptor)GameUtils.FindFirstComponentUpwards <ZoneDescriptor>(walk.transform);
                if (zoneDescriptor != null)
                {
                    ZoneDescriptor.CalculateZoneMinAndMax(ref tempZoneMin, ref tempZoneMax, zoneDescriptor.gameObject.transform);

                    // 1f is added to avoid floating point inacuracy (avoids 63.999/64 = 0, 64.99/64=1 which is correct)
                    int z = (int)(((tempZoneMin.z + 1f) - NavMeshBoundingBoxMin.z) / EditorVars.GridSize);
                    int x = (int)(((tempZoneMin.x + 1f) - NavMeshBoundingBoxMin.x) / EditorVars.GridSize);

                    MeshFilter meshFilter = walk.gameObject.GetComponent <MeshFilter>();
                    if (null != meshFilter.sharedMesh)
                    {
                        // the Vector3 vertices in the mesh need to be converted to the APP Int3 format
                        Int3[] Int3Verts = new Int3[meshFilter.sharedMesh.vertices.Length];
                        for (int i = 0; i < Int3Verts.Length; ++i)
                        {
                            Vector3 tempVert = new Vector3(meshFilter.sharedMesh.vertices[i].x, meshFilter.sharedMesh.vertices[i].y, meshFilter.sharedMesh.vertices[i].z);
                            tempVert = walk.transform.TransformPoint(tempVert);                             // get the world space position, rather than local space

                            // clamp the verts to the edges of the zone boundaries if they are close, this is so that the different tiles are linked together accurately
                            const float Tol = 0.01f;
                            tempVert.x = (tempVert.x <= tempZoneMin.x + Tol) ? tempZoneMin.x : tempVert.x;
                            tempVert.x = (tempVert.x >= tempZoneMax.x - Tol) ? tempZoneMax.x : tempVert.x;

                            tempVert.z = (tempVert.z <= tempZoneMin.z + Tol) ? tempZoneMin.z : tempVert.z;
                            tempVert.z = (tempVert.z >= tempZoneMax.z - Tol) ? tempZoneMax.z : tempVert.z;

                            Int3Verts[i] = (Int3)tempVert;
                        }
                        NavmeshTile tile = CreateTile(meshFilter.sharedMesh.triangles, Int3Verts, x, z);
                        tiles[Convert2DArrayCoordTo1DArrayCoord(x, z, tileXCount)] = tile;
                    }
                }
            }

            //Assign graph index to nodes
            uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex(this);
            GraphNodeDelegateCancelable del = delegate(GraphNode n)
            {
                n.GraphIndex = graphIndex;
                return(true);
            };
            GetNodes(del);

            // connect each tile to one and other
            for (int z = 0; z < tileZCount; z++)
            {
                for (int x = 0; x < tileXCount; x++)
                {
                    // make sure all the tiles which might be considered, have tiles created
                    CreateAndAddEmptyTileIfNonExists(x, z);
                    CreateAndAddEmptyTileIfNonExists(x + 1, z);
                    CreateAndAddEmptyTileIfNonExists(x, z + 1);

                    if (x < tileXCount - 1)
                    {
                        ConnectTiles(tiles[Convert2DArrayCoordTo1DArrayCoord(x, z, tileXCount)], tiles[Convert2DArrayCoordTo1DArrayCoord(x + 1, z, tileXCount)]);
                    }
                    if (z < tileZCount - 1)
                    {
                        ConnectTiles(tiles[Convert2DArrayCoordTo1DArrayCoord(x, z, tileXCount)], tiles[Convert2DArrayCoordTo1DArrayCoord(x, z + 1, tileXCount)]);
                    }
                }
            }
        }
示例#17
0
        /** Creates a mesh of the surfaces of the navmesh for use in OnDrawGizmos in the editor */
        public static void CreateNavmeshSurfaceVisualization(this NavmeshBase navmeshBase, NavmeshTile tile, GraphGizmoHelper helper)
        {
            // Vertex array might be a bit larger than necessary, but that's ok
            var vertices = PF.ArrayPool <Vector3> .Claim(tile.nodes.Length *3);

            var colors = PF.ArrayPool <Color> .Claim(tile.nodes.Length *3);

            for (int j = 0; j < tile.nodes.Length; j++)
            {
                var  node = tile.nodes[j];
                Int3 v0, v1, v2;
                node.GetVertices(out v0, out v1, out v2);
                vertices[j * 3 + 0] = (Vector3)v0;
                vertices[j * 3 + 1] = (Vector3)v1;
                vertices[j * 3 + 2] = (Vector3)v2;

                var color = helper.NodeColor(node);
                colors[j * 3 + 0] = colors[j * 3 + 1] = colors[j * 3 + 2] = color;
            }

            if (navmeshBase.showMeshSurface)
            {
                helper.DrawTriangles(vertices, colors, tile.nodes.Length);
            }
            if (navmeshBase.showMeshOutline)
            {
                helper.DrawWireTriangles(vertices, colors, tile.nodes.Length);
            }

            // Return lists to the pool
            PF.ArrayPool <Vector3> .Release(ref vertices);

            PF.ArrayPool <Color> .Release(ref colors);
        }