public static void SaveToFile(string filename, FoliageData data)
        {
            string path = Path.Combine(Application.streamingAssetsPath, filename);

            Debug.Log("Saving runtime grass to file: " + path);

            // Write the grass runtime to the file
            if (File.Exists(path))
            {
                File.Delete(path);
            }

            using (BinaryWriter writer = new BinaryWriter(new BufferedStream(File.Open(path, FileMode.OpenOrCreate))))
            {
                writer.Write(FoliageGlobals.DISK_IDENTIFIER);
                writer.Write(FoliageGlobals.DISK_VERSION);

                // Remove any empty data so that we don't have any empty cells that we're going to have to test for
                data.RemoveEmptyData();

                WriteFoliageData(writer, data);
            }

            // Just print out some information
#if UNITY_EDITOR
            {
                int instances = data.GetInstanceCount();
                Debug.Log(string.Format("Written foliage data with [{0} Cells] and [{1} Instances].", data.m_FoliageData.Count, instances));
            }
#endif
        }
        private static void WriteFoliageData(BinaryWriter a, FoliageData data)
        {
            Dictionary <int, FoliageCellData> cellData = data.m_FoliageData;

            // Write the count of entries
            a.Write(cellData.Count);

            foreach (int key in cellData.Keys)
            {
                WriteFoliageCellData(a, key, cellData[key]);
            }
        }
        private static void ReadFoliageData(BinaryReader a, FoliageData data)
        {
            // Read the count of entries
            int entries = a.ReadInt32();

            for (int i = 0; i < entries; i++)
            {
                int             key;
                FoliageCellData cellData;

                // Read the cell data
                ReadFoliageCellData(a, out key, out cellData);

                // Add it to the list
                data.m_FoliageData.Add(key, cellData);
            }
        }
        /**
         * Loads the edited grass that was added at edit time. It can even be an empty file,
         * since we can add all the grass data that we need at runtime.
         *
         * @param filename
         *          Filename where we should load the data from
         * @param loadRuntimeDataOnly
         *          To be used at runtime only. If this is 'true' it will not load the whole hierarchy
         *          but only the runtime use intended data. Set this to false when loading from the
         *          'FoliagePainter' that is intended for edit time use and 'false' when loading from
         *          the 'FoliageRenderer' that is intended for runtime use
         */
        public static FoliageData LoadFromFileEditTime(string filename)
        {
            string path = Path.Combine(Application.streamingAssetsPath, filename);

            Debug.Log("Loading runtime foliage from file: " + path);

            FoliageData data = new FoliageData();

            // Ensure the file exists
            if (File.Exists(path))
            {
                // Read the runtime grass from a file
                // using (BinaryReader reader = new BinaryReader(new MemoryStream(File.ReadAllBytes(path))))
                using (BinaryReader reader = new BinaryReader(new BufferedStream(File.OpenRead(path))))
                {
                    ulong ID      = reader.ReadUInt64();
                    int   version = reader.ReadInt32();

                    if (ID == FoliageGlobals.DISK_IDENTIFIER)
                    {
                        Debug.Log(string.Format("Reading file with identifier: {0} and version: {1}", ID, version));
                        ReadFoliageData(reader, data);
                    }
                    else
                    {
                        Debug.LogError("Foliage data has been tampered with! Delete it!");
                    }
                }
            }
            else
            {
                Debug.LogWarning("Warning, no grass file data exists! Save the grass!");
            }

#if UNITY_EDITOR
            {
                int instances = data.GetInstanceCount();
                Debug.Log(string.Format("Read foliage data with [{0} Cells] and [{1} Instances].", data.m_FoliageData.Count, instances));
            }
#endif

            return(data);
        }
        /**
         * Load from file the runtime version of the data
         */
        public static FoliageDataRuntime LoadFromFileRuntime(string filename)
        {
            FoliageData data = LoadFromFileEditTime(filename);

            // Build the runtime data from the edit time data
            FoliageDataRuntime runtime = new FoliageDataRuntime();

            foreach (var hashedCell in data.m_FoliageData)
            {
                FoliageCellData        editCell    = hashedCell.Value;
                FoliageCellDataRuntime runtimeCell = new FoliageCellDataRuntime();

                // Set the data. Note that we only need the extended bounds
                runtimeCell.m_Bounds   = editCell.m_BoundsExtended;
                runtimeCell.m_Position = editCell.m_Position;

                // Build the tree instance data
                int idx = -1;
                runtimeCell.m_TypeHashLocationsRuntime = new FoliageKeyValuePair <int, FoliageTuple <FoliageInstance[]> > [editCell.m_TypeHashLocationsEditor.Count];
                foreach (var instances in editCell.m_TypeHashLocationsEditor)
                {
                    idx++;

                    List <FoliageInstance> allTreeInstances = new List <FoliageInstance>();
                    var labeledInstances = instances.Value;

                    // Build all the data from the labeled data
                    foreach (List <FoliageInstance> inst in labeledInstances.Values)
                    {
                        allTreeInstances.AddRange(inst);
                    }

                    // We will build the world matrix for trees
                    for (int i = 0; i < allTreeInstances.Count; i++)
                    {
                        FoliageInstance inst = allTreeInstances[i];
                        inst.BuildWorldMatrix();
                        allTreeInstances[i] = inst;
                    }

                    // Don't forget to trim all excess instances!
                    allTreeInstances.TrimExcess();

                    #if UNITY_EDITOR
                    if (allTreeInstances.Count == 0)
                    {
                        Debug.Assert(false, "Count 0!");
                    }
                    #endif

                    runtimeCell.m_TypeHashLocationsRuntime[idx] = new FoliageKeyValuePair <int, FoliageTuple <FoliageInstance[]> >(instances.Key, new FoliageTuple <FoliageInstance[]>(allTreeInstances.ToArray()));
                }

                // Build the grass instance data from the subdivided cells
                List <FoliageKeyValuePair <int, FoliageCellSubdividedDataRuntime> > foliageCellDataSubdivided = new List <FoliageKeyValuePair <int, FoliageCellSubdividedDataRuntime> >(editCell.m_FoliageDataSubdivided.Count);

                foreach (var hashedSubdividedCell in editCell.m_FoliageDataSubdivided)
                {
                    FoliageCellSubdividedData        editSubdividedCell    = hashedSubdividedCell.Value;
                    FoliageCellSubdividedDataRuntime runtimeSubdividedCell = new FoliageCellSubdividedDataRuntime();

                    // Set the data
                    runtimeSubdividedCell.m_Bounds   = editSubdividedCell.m_Bounds;
                    runtimeSubdividedCell.m_Position = editSubdividedCell.m_Position;

                    idx = -1;
                    runtimeSubdividedCell.m_TypeHashLocationsRuntime = new FoliageKeyValuePair <int, FoliageTuple <Matrix4x4[][]> > [editSubdividedCell.m_TypeHashLocationsEditor.Count];
                    foreach (var instances in editSubdividedCell.m_TypeHashLocationsEditor)
                    {
                        idx++;

                        List <FoliageInstance> allGrassInstances = new List <FoliageInstance>();
                        var labeledInstances = instances.Value;

                        foreach (List <FoliageInstance> inst in labeledInstances.Values)
                        {
                            allGrassInstances.AddRange(inst);
                        }

                        #if UNITY_EDITOR
                        if (allGrassInstances.Count == 0)
                        {
                            Debug.Assert(false, "Count 0!");
                        }
                        #endif

                        // Build the multi-array data
                        int           ranges  = Mathf.CeilToInt(allGrassInstances.Count / (float)FoliageGlobals.RENDER_BATCH_SIZE);
                        Matrix4x4[][] batches = new Matrix4x4[ranges][];

                        for (int i = 0; i < ranges; i++)
                        {
                            List <FoliageInstance> range = allGrassInstances.GetRange(i * FoliageGlobals.RENDER_BATCH_SIZE,
                                                                                      i * FoliageGlobals.RENDER_BATCH_SIZE + FoliageGlobals.RENDER_BATCH_SIZE > allGrassInstances.Count
                                    ? allGrassInstances.Count - i * FoliageGlobals.RENDER_BATCH_SIZE
                                    : FoliageGlobals.RENDER_BATCH_SIZE);

                            batches[i] = range.ConvertAll <Matrix4x4>((x) => x.GetWorldTransform()).ToArray();
                        }

                        // Set the data
                        runtimeSubdividedCell.m_TypeHashLocationsRuntime[idx] = new FoliageKeyValuePair <int, FoliageTuple <Matrix4x4[][]> >(instances.Key, new FoliageTuple <Matrix4x4[][]>(batches));
                    }

                    // Add the subdivided runtime cell
                    foliageCellDataSubdivided.Add(new FoliageKeyValuePair <int, FoliageCellSubdividedDataRuntime>(hashedSubdividedCell.Key, runtimeSubdividedCell));
                }

                // Build the subdivided data
                runtimeCell.m_FoliageDataSubdivided = foliageCellDataSubdivided.ToArray();


                // Add the runtime cell
                runtime.m_FoliageData.Add(hashedCell.Key, runtimeCell);
            }

            // Good for GC
            data = null;

            return(runtime);
        }