using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System.Globalization; using UnityEngine; #if UNITY_2020_2_OR_NEWER using UnityEditor.AssetImporters; #else using UnityEditor.Experimental.AssetImporters; #endif namespace UnityEditor.Experimental.VFX.Utility { [ScriptedImporter(1, "pcache")] class PointCacheImporter : ScriptedImporter { public static T[] SubArray(T[] data, int index, int length) { T[] result = new T[length]; Array.Copy(data, index, result, 0, length); return result; } class InProperty { public string PropertyType; public string Name; public int Index; public OutProperty OutProperty; public InProperty(string propertyType, string name, int index, OutProperty outProperty) { PropertyType = propertyType; Name = name; Index = index; OutProperty = outProperty; } } class OutProperty { public string PropertyType; public string Name; public int Size; public OutProperty(string propertyType, string name, int size) { PropertyType = propertyType; Name = name; Size = size; } } public static void GetHeader(Stream s, out long byteLength, out List lines) { byteLength = 0; bool found_end_header = false; lines = new List(); s.Seek(0, SeekOrigin.Begin); BinaryReader sr = new BinaryReader(s); do { StringBuilder sb = new StringBuilder(); bool newline = false; do { char c = sr.ReadChar(); byteLength++; if ((c == '\n' || c == '\r') && sb.Length > 0) newline = true; else sb.Append(c); } while (!newline); string line = sb.ToString(); lines.Add(line); if (line == "end_header") found_end_header = true; } while (!found_end_header); } public override void OnImportAsset(AssetImportContext ctx) { try { PCache pcache = PCache.FromFile(ctx.assetPath); PointCacheAsset cache = ScriptableObject.CreateInstance(); cache.name = "PointCache"; ctx.AddObjectToAsset("PointCache", cache); ctx.SetMainObject(cache); List inProperties = new List(); Dictionary outProperties = new Dictionary(); Dictionary surfaces = new Dictionary(); foreach (var prop in pcache.properties) { OutProperty p_out; if (outProperties.ContainsKey(prop.ComponentName)) { p_out = outProperties[prop.ComponentName]; p_out.Size = Math.Max(p_out.Size, prop.ComponentIndex + 1); } else { p_out = new OutProperty(prop.Type, prop.ComponentName, prop.ComponentIndex + 1); outProperties.Add(prop.ComponentName, p_out); } inProperties.Add(new InProperty(prop.Type, prop.Name, prop.ComponentIndex, p_out)); } int width, height; FindBestSize(pcache.elementCount, out width, out height); // Output Surface Creation foreach (var kvp in outProperties) { TextureFormat surfaceFormat = TextureFormat.Alpha8; switch (kvp.Value.PropertyType) { case "uchar": if (kvp.Value.Size == 1) surfaceFormat = TextureFormat.Alpha8; else surfaceFormat = TextureFormat.RGBA32; break; case "float": if (kvp.Value.Size == 1) surfaceFormat = TextureFormat.RHalf; else surfaceFormat = TextureFormat.RGBAHalf; break; default: throw new NotImplementedException("Types other than uchar/float are not supported yet"); } Texture2D surface = new Texture2D(width, height, surfaceFormat, false); surface.name = kvp.Key; surfaces.Add(kvp.Value, surface); } cache.PointCount = pcache.elementCount; cache.surfaces = new Texture2D[surfaces.Count]; Dictionary outValues = new Dictionary(); foreach (var kvp in outProperties) outValues.Add(kvp.Value, new Color()); for (int i = 0; i < pcache.elementCount; i++) { int idx = 0; foreach (var prop in inProperties) { float val = 0.0f; switch (prop.PropertyType) { case "uchar": val = Mathf.Clamp01(((byte)pcache.buckets[idx][i]) / 255.0f); break; case "float": val = ((float)pcache.buckets[idx][i]); break; default: throw new NotImplementedException("Types other than uchar/float are not supported yet"); } SetPropValue(prop.Index, outValues, prop.OutProperty, val); idx++; } foreach (var kvp in outProperties) { surfaces[kvp.Value].SetPixel(i % width, i / width, outValues[kvp.Value]); } } int k = 0; foreach (var kvp in surfaces) { kvp.Value.Apply(); ctx.AddObjectToAsset(kvp.Key.Name, kvp.Value); cache.surfaces[k] = kvp.Value; k++; } } catch (System.Exception e) { Debug.LogException(e); } } private void SetPropValue(int index, Dictionary data, OutProperty prop, float val) { Color c = data[prop]; switch (index) { case 0: if (prop.Size == 1 && prop.PropertyType == "int") c.a = val; else c.r = val; break; case 1: c.g = val; break; case 2: c.b = val; break; case 3: c.a = val; break; } data[prop] = c; } private void FindBestSize(int count, out int width, out int height) { float r = Mathf.Sqrt(count); width = (int)Mathf.Ceil(r); height = width; } } }