using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using UniJSON; using UnityEditor; using UnityEngine; namespace UniGLTF { public static class SerializerGenerator { const BindingFlags FIELD_FLAGS = BindingFlags.Instance | BindingFlags.Public; static string OutPath { get { return Path.Combine(UnityEngine.Application.dataPath, "VRM/UniGLTF/Scripts/IO/FormatterExtensionsGltf.g.cs"); } } /// /// AOT向けにシリアライザを生成する /// [MenuItem(VRM.VRMVersion.MENU + "/Generate Serializer")] static void GenerateSerializer() { var path = OutPath; using (var g = new Generator(path)) { var rootType = typeof(glTF); g.Generate(rootType, "gltf"); } } class Generator : IDisposable { String m_path; Stream m_s; StreamWriter m_w; static Dictionary s_snipets = new Dictionary { {"gltf/animations", "if(value.animations!=null && value.animations.Count>0)" }, {"gltf/cameras", "if(value.cameras!=null && value.cameras.Count>0)" }, {"gltf/bufferViews[]/byteStride", "if(false)" }, {"gltf/bufferViews[]/target", "if(value.target!=0)" }, {"gltf/animations[]/channels[]/target", "if(value!=null)" }, {"gltf/accessors[]/sparse", "if(value.sparse!=null && value.sparse.count>0)"}, {"gltf/meshes[]/primitives[]/targets", "if(value.targets!=null && value.targets.Count>0)" }, {"gltf/meshes[]/primitives[]/targets[]/POSITION", "if(value.POSITION!=-1)" }, {"gltf/meshes[]/primitives[]/targets[]/NORMAL", "if(value.NORMAL!=-1)" }, {"gltf/meshes[]/primitives[]/targets[]/TANGENT", "if(value.TANGENT!=-1)" }, {"gltf/meshes[]/primitives[]/attributes/POSITION", "if(value.POSITION!=-1)"}, {"gltf/meshes[]/primitives[]/attributes/NORMAL", "if(value.NORMAL!=-1)"}, {"gltf/meshes[]/primitives[]/attributes/TANGENT", "if(value.TANGENT!=-1)"}, {"gltf/meshes[]/primitives[]/attributes/TEXCOORD_0", "if(value.TEXCOORD_0!=-1)"}, {"gltf/meshes[]/primitives[]/attributes/COLOR_0", "if(value.COLOR_0!=-1)"}, {"gltf/meshes[]/primitives[]/attributes/JOINTS_0", "if(value.JOINTS_0!=-1)"}, {"gltf/meshes[]/primitives[]/attributes/WEIGHTS_0", "if(value.WEIGHTS_0!=-1)"}, {"gltf/meshes[]/primitives[]/extras", "if(value.extras!=null && value.extras.targetNames!=null && value.extras.targetNames.Count>0)"}, {"gltf/materials[]/alphaCutoff", "if(!string.IsNullOrEmpty(value.alphaMode))" }, {"gltf/nodes[]/camera", "if(value.camera!=-1)"}, {"gltf/nodes[]/mesh", "if(value.mesh!=-1)"}, {"gltf/nodes[]/skin", "if(value.skin!=-1)"}, {"gltf/nodes[]/children", "if(value.children != null && value.children.Length>0)"}, {"gltf/skins[]/skeleton", "if(value.skeleton!=-1)"}, {"gltf/extensionsRequired", "if(false)"}, {"gltf/extensions/VRM/humanoid/humanBones[]/axisLength", "if(value.axisLength>0)"}, {"gltf/extensions/VRM/humanoid/humanBones[]/center", "if(value.center!=Vector3.zero)"}, {"gltf/extensions/VRM/humanoid/humanBones[]/max", "if(value.max!=Vector3.zero)"}, {"gltf/extensions/VRM/humanoid/humanBones[]/min", "if(value.min!=Vector3.zero)"}, }; public Generator(string path) { m_path = path; m_s = File.Open(path, FileMode.Create); m_w = new StreamWriter(m_s, Encoding.UTF8); // begin m_w.Write(@" using System; using System.Collections.Generic; using UniJSON; using UnityEngine; using VRM; namespace UniGLTF { static public class IFormatterExtensionsGltf { "); } public void Dispose() { // end m_w.Write(@" } // class } // namespace "); m_w.Dispose(); m_s.Dispose(); UnityPath.FromFullpath(m_path).ImportAsset(); } HashSet m_used = new HashSet { typeof(object), }; public void Generate(Type t, string path, int level = 0) { if (m_used.Contains(t)) { // 処理済み return; } m_used.Add(t); // primitive try { var mi = typeof(IFormatter).GetMethod("Value", new Type[] { t }); if (mi != null) { m_w.Write(@" public static void GenSerialize(this IFormatter f, $0 value) { f.Value(value); } ".Replace("$0", t.Name)); return; } } catch (AmbiguousMatchException) { // skip } if (t.IsEnum) { m_w.Write(@" public static void GenSerialize(this IFormatter f, $0 value) { f.Value((int)value); } ".Replace("$0", t.Name)); } else if (t.IsArray) { var et = t.GetElementType(); m_w.Write(@" /// $1 public static void GenSerialize(this IFormatter f, $0[] value) { f.BeginList(value.Length); foreach (var x in value) { f.GenSerialize(x); } f.EndList(); } " .Replace("$0", et.Name) .Replace("$1", path) ); Generate(et, path + "[]", level + 1); } else if (t.IsGenericType) { if (t.GetGenericTypeDefinition() == typeof(List<>)) { var et = t.GetGenericArguments()[0]; m_w.Write(@" /// $1 public static void GenSerialize(this IFormatter f, List<$0> value) { f.BeginList(value.Count); foreach (var x in value) { f.GenSerialize(x); } f.EndList(); } " .Replace("$0", et.Name) .Replace("$1", path)); Generate(et, path + "[]", level + 1); } else if (t.GetGenericTypeDefinition() == typeof(Dictionary<,>) && t.GetGenericArguments()[0] == typeof(string)) { var et = t.GetGenericArguments()[1]; m_w.Write(@" /// $1 public static void GenSerialize(this IFormatter f, Dictionary value) { f.BeginMap(value.Count); foreach (var kv in value) { f.Key(kv.Key); f.GenSerialize(kv.Value); } f.EndMap(); } " .Replace("$0", et.Name) .Replace("$1", path)); Generate(et, path + "{}", level + 1); } else { Debug.LogWarningFormat("unknown type: {0}", t); } } else { Debug.LogFormat("{0}({1})", path, t.Name); m_w.Write(@" /// $1 public static void GenSerialize(this IFormatter f, $0 value) { f.BeginMap(0); // dummy " .Replace("$0", t.Name) .Replace("$1", path) ); foreach (var fi in t.GetFields(FIELD_FLAGS)) { if (fi.FieldType == typeof(object)) { continue; } if (fi.IsLiteral && !fi.IsInitOnly) { continue; } if (fi.FieldType == typeof(string) || fi.FieldType.IsEnum || fi.FieldType.IsArray || fi.FieldType.IsGenericType) { } else if(fi.FieldType == typeof(glTF_KHR_materials_unlit)) { } else if (fi.FieldType.IsClass && fi.FieldType.GetFields(FIELD_FLAGS).Length == 0) { continue; } var snipet = fi.FieldType.IsClass ? "if(value." + fi.Name + "!=null)" : ""; var value = default(string); if (s_snipets.TryGetValue(path + "/" + fi.Name, out value)) { snipet = value; } m_w.Write(@" $1 { f.Key(""$0""); f.GenSerialize(value.$0); } " .Replace("$0", fi.Name) .Replace("$1", snipet) ); } m_w.Write(@" f.EndMap(); } "); foreach (var fi in t.GetFields(FIELD_FLAGS)) { Generate(fi.FieldType, path + "/" + fi.Name, level + 1); } } } } } }