Rasagar/Library/PackageCache/com.unity.visualeffectgraph/Editor/Utilities/pCache/PCache.cs
2024-08-26 23:07:20 +03:00

609 lines
23 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Globalization;
using UnityEngine;
namespace UnityEditor.Experimental.VFX.Utility
{
class PCache
{
public List<PropertyDesc> properties;
public List<List<object>> buckets;
public int elementCount;
public enum Format
{
Ascii,
Binary
}
public PCache()
{
properties = new List<PropertyDesc>();
buckets = new List<List<object>>();
elementCount = 0;
}
public void Clear()
{
properties.Clear();
buckets.Clear();
elementCount = 0;
}
public void AddFloatProperty(string name)
{
properties.Add(new PropertyDesc()
{
Name = name,
ComponentIndex = 0,
ComponentName = name,
Type = "float"
});
buckets.Add(new List<object>());
}
public void AddVector2Property(string name)
{
properties.Add(new PropertyDesc()
{
Name = name + ".x",
ComponentIndex = 0,
ComponentName = name,
Type = "float"
});
properties.Add(new PropertyDesc()
{
Name = name + ".y",
ComponentIndex = 1,
ComponentName = name,
Type = "float"
});
buckets.Add(new List<object>());
buckets.Add(new List<object>());
}
public void AddVector3Property(string name)
{
properties.Add(new PropertyDesc()
{
Name = name + ".x",
ComponentIndex = 0,
ComponentName = name,
Type = "float"
});
properties.Add(new PropertyDesc()
{
Name = name + ".y",
ComponentIndex = 1,
ComponentName = name,
Type = "float"
}); properties.Add(new PropertyDesc()
{
Name = name + ".z",
ComponentIndex = 2,
ComponentName = name,
Type = "float"
});
buckets.Add(new List<object>());
buckets.Add(new List<object>());
buckets.Add(new List<object>());
}
public void AddVector4Property(string name)
{
properties.Add(new PropertyDesc()
{
Name = name + ".x",
ComponentIndex = 0,
ComponentName = name,
Type = "float"
});
properties.Add(new PropertyDesc()
{
Name = name + ".y",
ComponentIndex = 1,
ComponentName = name,
Type = "float"
});
properties.Add(new PropertyDesc()
{
Name = name + ".z",
ComponentIndex = 2,
ComponentName = name,
Type = "float"
});
properties.Add(new PropertyDesc()
{
Name = name + ".w",
ComponentIndex = 3,
ComponentName = name,
Type = "float"
});
buckets.Add(new List<object>());
buckets.Add(new List<object>());
buckets.Add(new List<object>());
buckets.Add(new List<object>());
}
public void AddColorProperty(string name)
{
properties.Add(new PropertyDesc()
{
Name = name + ".r",
ComponentIndex = 0,
ComponentName = name,
Type = "float"
});
properties.Add(new PropertyDesc()
{
Name = name + ".g",
ComponentIndex = 1,
ComponentName = name,
Type = "float"
});
properties.Add(new PropertyDesc()
{
Name = name + ".b",
ComponentIndex = 2,
ComponentName = name,
Type = "float"
});
properties.Add(new PropertyDesc()
{
Name = name + ".a",
ComponentIndex = 3,
ComponentName = name,
Type = "float"
});
buckets.Add(new List<object>());
buckets.Add(new List<object>());
buckets.Add(new List<object>());
buckets.Add(new List<object>());
}
public void SetFloatData(string property, List<float> data)
{
var prop = properties.FirstOrDefault(o => o.Name == property);
if (prop.Name != property)
throw new InvalidOperationException("Could not find property :" + property);
if (elementCount == 0 || data.Count == elementCount)
{
int index = properties.IndexOf(prop);
foreach (float f in data)
{
buckets[index].Add(f);
}
if (elementCount == 0) elementCount = data.Count;
}
else throw new InvalidOperationException("Need to set data corresponding to the actual element count :" + elementCount);
}
public void SetVector3Data(string component, List<float> dataX, List<float> dataY, List<float> dataZ)
{
SetFloatData(component + ".x", dataX);
SetFloatData(component + ".y", dataY);
SetFloatData(component + ".z", dataZ);
}
public void SetVector2Data(string component, List<Vector2> data)
{
var dataX = new List<float>();
var dataY = new List<float>();
foreach (var v in data)
{
dataX.Add(v.x);
dataY.Add(v.y);
}
SetFloatData(component + ".x", dataX);
SetFloatData(component + ".y", dataY);
}
public void SetVector3Data(string component, List<Vector3> data)
{
var dataX = new List<float>();
var dataY = new List<float>();
var dataZ = new List<float>();
foreach (var v in data)
{
dataX.Add(v.x);
dataY.Add(v.y);
dataZ.Add(v.z);
}
SetFloatData(component + ".x", dataX);
SetFloatData(component + ".y", dataY);
SetFloatData(component + ".z", dataZ);
}
public void SetColorData(string component, List<Vector4> data)
{
var dataX = new List<float>();
var dataY = new List<float>();
var dataZ = new List<float>();
var dataW = new List<float>();
foreach (var v in data)
{
dataX.Add(v.x);
dataY.Add(v.y);
dataZ.Add(v.z);
dataW.Add(v.w);
}
SetFloatData(component + ".r", dataX);
SetFloatData(component + ".g", dataY);
SetFloatData(component + ".b", dataZ);
SetFloatData(component + ".a", dataW);
}
public void SetVector4Data(string component, List<Vector4> data)
{
var dataX = new List<float>();
var dataY = new List<float>();
var dataZ = new List<float>();
var dataW = new List<float>();
foreach (var v in data)
{
dataX.Add(v.x);
dataY.Add(v.y);
dataZ.Add(v.z);
dataW.Add(v.w);
}
SetFloatData(component + ".x", dataX);
SetFloatData(component + ".y", dataY);
SetFloatData(component + ".z", dataZ);
SetFloatData(component + ".w", dataW);
}
public void SaveToFile(string filePath, Format format = Format.Binary)
{
if (string.IsNullOrEmpty(filePath))
throw new InvalidOperationException("Cannot SaveToFile with an empty filepath");
var directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
if (File.Exists(filePath))
File.Delete(filePath);
using (var binaryWriter = new BinaryWriter(File.Create(filePath)))
{
binaryWriter.Write(BuildHeaderString(format));
if (format == Format.Binary)
{
for (int i = 0; i < elementCount; i++)
{
for (int j = 0; j < properties.Count; j++)
{
var prop = properties[j];
switch (prop.Type)
{
case "char":
binaryWriter.Write((sbyte)buckets[j][i]); break;
case "uchar":
binaryWriter.Write((byte)buckets[j][i]); break;
case "short":
binaryWriter.Write((short)buckets[j][i]); break;
case "ushort":
binaryWriter.Write((ushort)buckets[j][i]); break;
case "int":
binaryWriter.Write((int)buckets[j][i]); break;
case "uint":
binaryWriter.Write((uint)buckets[j][i]); break;
case "float":
binaryWriter.Write((float)buckets[j][i]); break;
case "double":
binaryWriter.Write((double)buckets[j][i]); break;
}
}
}
}
else if (format == Format.Ascii)
{
var sb = new StringBuilder();
for (int i = 0; i < elementCount; i++)
{
for (int j = 0; j < properties.Count; j++)
{
var prop = properties[j];
switch (prop.Type)
{
case "char":
sb.Append(((sbyte)buckets[j][i]).ToString(CultureInfo.InvariantCulture)); break;
case "uchar":
sb.Append(((byte)buckets[j][i]).ToString(CultureInfo.InvariantCulture)); break;
case "short":
sb.Append(((short)buckets[j][i]).ToString(CultureInfo.InvariantCulture)); break;
case "ushort":
sb.Append(((ushort)buckets[j][i]).ToString(CultureInfo.InvariantCulture)); break;
case "int":
sb.Append(((int)buckets[j][i]).ToString(CultureInfo.InvariantCulture)); break;
case "uint":
sb.Append(((uint)buckets[j][i]).ToString(CultureInfo.InvariantCulture)); break;
case "float":
sb.Append(((float)buckets[j][i]).ToString(CultureInfo.InvariantCulture)); break;
case "double":
sb.Append(((double)buckets[j][i]).ToString(CultureInfo.InvariantCulture)); break;
}
sb.Append(j == properties.Count - 1 ? "\n" : " ");
}
binaryWriter.Write(sb.ToString().ToCharArray());
sb.Clear();
}
}
else throw new InvalidOperationException("Invalid format : " + format);
}
AssetDatabase.ImportAsset(filePath, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
}
private char[] BuildHeaderString(Format format)
{
StringBuilder b = new StringBuilder();
b.AppendLine("pcache");
b.AppendLine(string.Format("format {0} 1.0", GetFormatString(format)));
b.AppendLine("comment Exported with PCache.cs");
b.AppendLine(string.Format("elements {0}", elementCount));
foreach (var property in properties)
{
b.AppendLine(string.Format("property {0} {1}", property.Type, property.Name));
}
b.AppendLine("end_header");
return b.ToString().ToCharArray();
}
public static PCache FromFile(string filename)
{
PCache data = new PCache();
List<string> header;
long offset;
using (var stream = File.OpenRead(filename))
{
GetHeader(stream, out offset, out header);
}
if (header == null || header[0] != "pcache")
throw new Exception("Invalid header : missing magic number");
Format format = (Format)int.MaxValue;
data.elementCount = 0;
data.properties = new List<PropertyDesc>();
foreach (string line in header)
{
var words = line.Split(' ');
switch (words[0].ToLower())
{
case "comment": //do nothing
break;
case "format":
if (words.Length != 3) throw new Exception("Invalid format description :" + line);
switch (words[1])
{
case "ascii": format = Format.Ascii; break;
case "binary": format = Format.Binary; break;
default: throw new Exception("Invalid Format :" + words[1]);
}
break;
case "elements":
if (words.Length != 2) throw new Exception("Invalid element description :" + line);
if (!int.TryParse(words[1], out data.elementCount))
throw new Exception("Invalid element count :" + words[1]);
break;
case "property":
if (words.Length != 3) throw new Exception("Invalid property description :" + line);
string property = words[2];
string component = GetComponentName(property);
int idx = GetComponentIndex(property);
string type = words[1];
int stride = GetPropertySize(type);
if (stride == 0)
throw new Exception("Invalid Type for " + property + " property : " + type);
PropertyDesc prop = new PropertyDesc()
{
Name = property,
Type = type,
ComponentName = component,
ComponentIndex = idx,
Stride = stride
};
data.properties.Add(prop);
break;
case "end_header":
if (words.Length != 1) throw new Exception("Invalid end_header description :" + line);
break;
}
}
data.buckets = new List<List<object>>();
foreach (var property in data.properties)
{
data.buckets.Add(new List<object>(data.elementCount));
}
if (format == Format.Binary)
{
using (var binaryReader = new BinaryReader(File.OpenRead(filename)))
{
binaryReader.BaseStream.Seek(offset, SeekOrigin.Begin);
for (int i = 0; i < data.elementCount; i++)
{
for (int j = 0; j < data.properties.Count; j++)
{
var prop = data.properties[j];
switch (prop.Type)
{
case "short": data.buckets[j].Add(binaryReader.ReadInt16()); break;
case "ushort": data.buckets[j].Add(binaryReader.ReadUInt16()); break;
case "int": data.buckets[j].Add(binaryReader.ReadInt32()); break;
case "uint": data.buckets[j].Add(binaryReader.ReadUInt32()); break;
case "char": data.buckets[j].Add(binaryReader.ReadSByte()); break;
case "uchar": data.buckets[j].Add(binaryReader.ReadByte()); break;
case "float": data.buckets[j].Add(binaryReader.ReadSingle()); break;
case "double": data.buckets[j].Add(binaryReader.ReadDouble()); break;
}
}
}
}
}
else if (format == Format.Ascii)
{
string[] lines = null;
using (var reader = new StreamReader(File.OpenRead(filename)))
{
reader.BaseStream.Seek(offset, SeekOrigin.Begin);
lines = reader.ReadToEnd().Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
}
if (lines.Length != data.elementCount)
throw new InvalidOperationException(string.Format("Bad item amount, {0} expected in header, found {1}", data.elementCount, lines.Length));
for (int i = 0; i < data.elementCount; i++)
{
string line = lines[i];
string[] elements = line.Split(' ');
for (int j = 0; j < data.properties.Count; j++)
{
var prop = data.properties[j];
switch (prop.Type)
{
case "short": data.buckets[j].Add(short.Parse(elements[j], CultureInfo.InvariantCulture)); break;
case "ushort": data.buckets[j].Add(ushort.Parse(elements[j], CultureInfo.InvariantCulture)); break;
case "int": data.buckets[j].Add(int.Parse(elements[j], CultureInfo.InvariantCulture)); break;
case "uint": data.buckets[j].Add(uint.Parse(elements[j], CultureInfo.InvariantCulture)); break;
case "char": data.buckets[j].Add(sbyte.Parse(elements[j], CultureInfo.InvariantCulture)); break;
case "uchar": data.buckets[j].Add(byte.Parse(elements[j], CultureInfo.InvariantCulture)); break;
case "float": data.buckets[j].Add(float.Parse(elements[j], CultureInfo.InvariantCulture)); break;
case "double": data.buckets[j].Add(double.Parse(elements[j], CultureInfo.InvariantCulture)); break;
}
}
}
}
return data;
}
private static void GetHeader(Stream s, out long byteLength, out List<string> lines)
{
byteLength = 0;
bool found_end_header = false;
lines = new List<string>();
s.Seek(0, SeekOrigin.Begin);
using (var sr = new BinaryReader(s))
{
var sb = new StringBuilder();
do
{
bool newline = false;
do
{
char c = sr.ReadChar();
byteLength++;
if (c == '\r')
{
// skip
}
else if (c == '\n')
{
if (sb.Length > 0)
newline = true;
}
else sb.Append(c);
}
while (!newline);
string line = sb.ToString();
sb.Clear();
lines.Add(line);
if (line == "end_header") found_end_header = true;
}
while (!found_end_header);
}
}
private static string GetComponentName(string property)
{
string p = property.ToLower();
if (p.EndsWith(".x") ||
p.EndsWith(".y") ||
p.EndsWith(".z") ||
p.EndsWith(".w") ||
p.EndsWith(".r") ||
p.EndsWith(".g") ||
p.EndsWith(".b") ||
p.EndsWith(".a")) return property.Substring(0, property.Length - 2);
else return property;
}
private static int GetComponentIndex(string property)
{
string p = property.ToLower();
if (p.EndsWith(".x") || p.EndsWith(".r")) return 0;
else if (p.EndsWith(".y") || p.EndsWith(".g")) return 1;
else if (p.EndsWith(".z") || p.EndsWith(".b")) return 2;
else if (p.EndsWith(".w") || p.EndsWith(".a")) return 3;
else return 0;
}
private static string GetFormatString(Format format)
{
switch (format)
{
case Format.Ascii: return "ascii";
case Format.Binary: return "binary";
default: throw new InvalidOperationException("Invalid format");
}
}
private static int GetPropertySize(string type)
{
if (TypeSize.ContainsKey(type))
return TypeSize[type];
else
return 0;
}
private static Dictionary<string, int> TypeSize = new Dictionary<string, int>()
{
{ "char", 1 },
{ "uchar", 1 },
{ "short", 2 },
{ "ushort", 2 },
{ "int", 4 },
{ "uint", 4 },
{ "float", 4 },
{ "double", 8 },
};
public struct PropertyDesc
{
public string Name;
public string Type;
public string ComponentName;
public int ComponentIndex;
public int Stride;
}
}
}