/// <summary>
        /// Creates a new chunky mesh
        /// </summary>
        /// <param name="triangles">Triangles</param>
        /// <param name="trisPerChunk">Triangles per chunk</param>
        /// <param name="cm">The resulting chunky mesh</param>
        /// <returns>Returns true if the chunky mesh were correctly created</returns>
        private static bool CreateChunkyTriMesh(IEnumerable <Triangle> triangles, int trisPerChunk, out ChunkyTriMesh cm)
        {
            Triangle[] tris    = triangles.ToArray();
            int        ntris   = tris.Count();
            int        nchunks = (ntris + trisPerChunk - 1) / trisPerChunk;

            cm = new ChunkyTriMesh
            {
                Triangles = tris,
                Nodes     = new ChunkyTriMeshNode[nchunks * 4],
                Tris      = new int[ntris],
                NTris     = ntris,
            };

            // Build tree
            BoundsItem[] items = new BoundsItem[ntris];

            for (int i = 0; i < ntris; i++)
            {
                var t = tris[i];

                // Calc triangle XZ bounds.
                var bbox = BoundingBox.FromPoints(t.GetVertices());

                items[i].i    = i;
                items[i].bmin = new Vector2(bbox.Minimum.X, bbox.Minimum.Z);
                items[i].bmax = new Vector2(bbox.Maximum.X, bbox.Maximum.Z);
            }

            int curNode = 0;
            int curTri  = 0;

            Subdivide(
                items,
                0, ntris, trisPerChunk,
                ref curNode, cm.Nodes, nchunks * 4,
                ref curTri, cm.Tris, tris);

            cm.NNodes = curNode;

            // Calc max tris per node.
            cm.MaxTrisPerChunk = 0;
            for (int i = 0; i < cm.NNodes; ++i)
            {
                var node = cm.Nodes[i];

                bool isLeaf = node.I >= 0;
                if (!isLeaf)
                {
                    continue;
                }
                if (node.N > cm.MaxTrisPerChunk)
                {
                    cm.MaxTrisPerChunk = node.N;
                }
            }

            return(true);
        }
        /// <summary>
        /// Loads the graph from a file
        /// </summary>
        /// <param name="fileName">File name</param>
        public override IGraph Load(string fileName)
        {
            // Initialize the input data
            this.ChunkyMesh = ChunkyTriMesh.Build(this);

            // Load file
            var graph = GraphFile.Load(fileName);

            // Set input data
            graph.Input = this;

            return(graph);
        }
        /// <summary>
        /// Creates a new graph from current geometry input
        /// </summary>
        /// <param name="settings">Settings</param>
        /// <returns>Returns the new graph</returns>
        public override IGraph CreateGraph(PathFinderSettings settings)
        {
            // Prepare input data
            this.ChunkyMesh = ChunkyTriMesh.Build(this);

            // Create graph
            var graph = new Graph()
            {
                Input    = this,
                Settings = settings as BuildSettings,
            };

            // Generate navigation meshes and gueries for each agent
            foreach (var agent in graph.Settings.Agents)
            {
                var nm      = NavMesh.Build(this, graph.Settings, agent);
                var mmQuery = new NavMeshQuery();
                mmQuery.Init(nm, graph.Settings.MaxNodes);

                graph.MeshQueryDictionary.Add(agent, mmQuery);
            }

            return(graph);
        }
 /// <summary>
 /// Refresh
 /// </summary>
 public override void Refresh()
 {
     // Recreate the input data
     this.ChunkyMesh = ChunkyTriMesh.Build(this);
 }