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); }