Merge pull request #596 from ousttrue/feature/vrm10

Feature/vrm10
This commit is contained in:
ousttrue 2021-01-12 12:53:58 +09:00 committed by GitHub
commit a0003f2dc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
551 changed files with 39427 additions and 13 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e6ec48f8e3ffe6d4482a26fd34facfd6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
using System.IO;
using UniGLTF.JsonSchema;
using UniGLTF.JsonSchema.Schemas;
namespace GenerateUniGLTFSerialization
{
public static class DeserializerWriter
{
const string Begin = @"// This file is generated from JsonSchema. Don't modify this source code.
using UniJSON;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UniGLTF.Extensions.$0 {
public static class GltfDeserializer
{
public static bool TryGet(UniGLTF.glTFExtension src, out $0 extension)
{
if(src is UniGLTF.glTFExtensionImport extensions)
{
foreach(var kv in extensions.ObjectItems())
{
if(kv.Key.GetUtf8String() == $0.ExtensionNameUtf8)
{
extension = Deserialize(kv.Value);
return true;
}
}
}
extension = default;
return false;
}
";
const string End = @"
} // GltfDeserializer
} // UniGLTF
";
public static void Write(TextWriter w, JsonSchemaSource root, string rootName)
{
w.Write(Begin.Replace("$0", rootName));
root.Create(true, rootName).GenerateDeserializer(new TraverseContext(w), "Deserialize");
w.Write(End);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 60b1c040cdf4bad42a40c8467ae9923b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.IO;
using UniGLTF.JsonSchema;
using UniGLTF.JsonSchema.Schemas;
namespace GenerateUniGLTFSerialization
{
class FormatWriter
{
TextWriter m_w;
string m_prefix;
HashSet<string> m_used = new HashSet<string>();
FormatWriter(TextWriter writer, string prefix)
{
m_w = writer;
m_prefix = prefix;
}
// static string UpperCamelCase(string src)
// {
// if(string.IsNullOrEmpty(src))
// {
// return "";
// }
// return src.Substring(0, 1).ToUpper() + src.Substring(1);
// }
// string ClassName(string src)
// {
// if(string.IsNullOrEmpty(src))
// {
// // root
// return m_prefix;
// }
// else{
// return String.Join("", src.Split("__").Select(x => UpperCamelCase(x)));
// }
// }
const string EnumStringAttr = "[JsonSchema(EnumSerializationType = EnumSerializationType.AsString)]";
static (string, string) PropType(JsonSchemaBase schema)
{
switch (schema.JsonSchemaType)
{
case JsonSchemaType.String:
case JsonSchemaType.Boolean:
case JsonSchemaType.Integer:
case JsonSchemaType.Number:
case JsonSchemaType.Object:
case JsonSchemaType.Array:
return (null, schema.ValueType);
case JsonSchemaType.EnumString:
return (EnumStringAttr, schema.ValueType);
}
throw new NotImplementedException();
}
const string FieldIndent = " ";
void WriteObject(ObjectJsonSchema schema, string rootName = default)
{
if (m_used.Contains(schema.Title))
{
return;
}
m_used.Add(schema.Title);
var className = schema.Title;
m_w.Write($@"
public class {className}
{{
");
if (!string.IsNullOrEmpty(rootName))
{
var indent = " ";
m_w.WriteLine($"{indent}public const string ExtensionName = \"{rootName}\";");
m_w.WriteLine($"{indent}public static readonly Utf8String ExtensionNameUtf8 = Utf8String.From(ExtensionName);");
m_w.WriteLine();
}
var isFirst = true;
foreach (var kv in schema.Properties)
{
if (isFirst)
{
isFirst = false;
}
else
{
m_w.WriteLine();
}
if (!string.IsNullOrEmpty(kv.Value.Description))
{
m_w.WriteLine($"{FieldIndent}// {kv.Value.Description}");
}
var (attr, propType) = PropType(kv.Value);
if (!string.IsNullOrEmpty(attr))
{
m_w.WriteLine($"{FieldIndent}{attr}");
}
m_w.WriteLine($"{FieldIndent}public {propType} {kv.Key.ToUpperCamel()};");
}
// close class
m_w.WriteLine(" }");
}
void WriteEnumString(EnumStringJsonSchema schema)
{
if (m_used.Contains(schema.Title))
{
return;
}
m_used.Add(schema.Title);
var className = schema.Title;
m_w.Write($@"
public enum {className}
{{
");
foreach (var value in schema.Values)
{
m_w.WriteLine($" {value},");
}
// close
m_w.Write(@"
}
");
}
void Traverse(JsonSchemaSource source, string rootName = default)
{
foreach (var child in source.Children())
{
Traverse(child);
}
switch (source.type)
{
case JsonSchemaType.Object:
{
var schema = source.Create(true);
if (schema is ObjectJsonSchema obj)
{
if (!string.IsNullOrEmpty(rootName))
{
obj.Title = rootName;
}
WriteObject(obj, rootName);
}
else if (schema is ExtensionJsonSchema ext)
{
// WriteObject(ext);
}
else
{
throw new Exception();
}
}
break;
case JsonSchemaType.EnumString:
WriteEnumString(source.Create(true) as EnumStringJsonSchema);
break;
}
}
public static void Write(TextWriter w, JsonSchemaSource root, string rootName)
{
w.Write($@"// This file is generated from JsonSchema. Don't modify this source code.
using System;
using System.Collections.Generic;
using UniGLTF;
using UniJSON;
namespace UniGLTF.Extensions.{rootName}
{{
");
new FormatWriter(w, root.title).Traverse(root, rootName);
// close namespace
w.WriteLine("}");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1eff612a2dd70574db807fb58a21212b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,104 @@
using System;
using System.IO;
using System.Linq;
using UniGLTF.JsonSchema;
namespace GenerateUniGLTFSerialization
{
public class Generator
{
static void ClearFolder(DirectoryInfo dir)
{
Console.WriteLine($"clear: {dir}");
foreach (FileInfo file in dir.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo child in dir.GetDirectories())
{
child.Delete(true);
}
}
static string CleanupTitle(string title)
{
if (string.IsNullOrEmpty(title))
{
return title;
}
var splitted = title.Split().ToList();
if (splitted.Last() == "extension")
{
splitted.RemoveAt(splitted.Count - 1);
}
return string.Join("", splitted
.Where(x => x.Length > 0)
.Select(x => x.Substring(0, 1).ToUpper() + x.Substring(1)));
}
static string GetStem(string filename)
{
return filename.Split('.').First();
}
public static void GenerateTo(JsonSchemaSource root, DirectoryInfo dir, bool clearFolder)
{
// clear or create folder
if (dir.Exists)
{
if (dir.EnumerateFileSystemInfos().Any())
{
if (!clearFolder)
{
Console.WriteLine($"{dir} is not empty.");
return;
}
// clear
ClearFolder(dir);
}
}
else
{
Console.WriteLine($"create: {dir}");
dir.Create();
}
foreach (var s in root.Traverse())
{
// title を掃除
s.title = CleanupTitle(s.title);
}
{
var dst = Path.Combine(dir.FullName, "Format.g.cs");
Console.WriteLine(dst);
using (var w = new StringWriter())
{
FormatWriter.Write(w, root, GetStem(root.FilePath.Name));
File.WriteAllText(dst, w.ToString().Replace("\r\n", "\n"));
}
}
{
var dst = Path.Combine(dir.FullName, "Deserializer.g.cs");
Console.WriteLine(dst);
using (var w = new StringWriter())
{
DeserializerWriter.Write(w, root, GetStem(root.FilePath.Name));
File.WriteAllText(dst, w.ToString().Replace("\r\n", "\n"));
}
}
{
var dst = Path.Combine(dir.FullName, "Serializer.g.cs");
Console.WriteLine(dst);
using (var w = new StringWriter())
{
SerializerWriter.Write(w, root, GetStem(root.FilePath.Name));
File.WriteAllText(dst, w.ToString().Replace("\r\n", "\n"));
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 550a0a585e47bf44781b5c447169cc33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
using System.IO;
using UniGLTF.JsonSchema;
using UniGLTF.JsonSchema.Schemas;
namespace GenerateUniGLTFSerialization
{
public static class SerializerWriter
{
const string Begin = @"// This file is generated from JsonSchema. Don't modify this source code.
using System;
using System.Collections.Generic;
using System.Linq;
using UniJSON;
namespace UniGLTF.Extensions.$0 {
static public class GltfSerializer
{
public static void SerializeTo(ref UniGLTF.glTFExtension dst, $0 extension)
{
if (dst is glTFExtensionImport)
{
throw new NotImplementedException();
}
if (!(dst is glTFExtensionExport extensions))
{
extensions = new glTFExtensionExport();
dst = extensions;
}
var f = new JsonFormatter();
Serialize(f, extension);
extensions.Add($0.ExtensionName, f.GetStoreBytes());
}
";
const string End = @"
} // class
} // namespace
";
public static void Write(TextWriter w, JsonSchemaSource root, string rootName)
{
w.Write(Begin.Replace("$0", rootName));
root.Create(true, rootName).GenerateSerializer(new TraverseContext(w), "Serialize");
w.Write(End);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8875ecbd55657e148a7483e597058ab3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 734f2ba2c2ed11540ae52c5adb9900f7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,60 @@
using System.Collections.Generic;
namespace UniGLTF.JsonSchema
{
/// <summary>
/// JSON上の配列参照を列挙する
/// </summary>
public static class IndexTargets
{
public static Dictionary<string, string> Map = new Dictionary<string, string>{
{".accessors[].bufferView", ".bufferViews"},
{".accessors[].sparse.indices.bufferView", ".bufferViews"},
{".accessors[].sparse.values.bufferView", ".bufferViews"},
{".animations[].channels[].sampler", ".animations[{0}].samplers"},
{".animations[].channels[].target.node", ".nodes"},
{".animations[].samplers[].input", ".accessors"},
{".animations[].samplers[].output", ".accessors"},
{".bufferViews[].buffer", ".buffers"},
{".images[].bufferView", ".bufferViews"},
// {".materials[].extensions.KHR_materials_pbrSpecularGlossiness.diffuseTexture.index", ""},
// {".materials[].extensions.KHR_materials_pbrSpecularGlossiness.specularGlossinessTexture.index", ""},
{".materials[].pbrMetallicRoughness.baseColorTexture.index", ".textures"},
{".materials[].pbrMetallicRoughness.metallicRoughnessTexture.index", ".textures"},
{".materials[].normalTexture.index", ".textures"},
{".materials[].occlusionTexture.index", ".textures"},
{".materials[].emissiveTexture.index", ".textures"},
// {".meshes[].primitives[].extensions.KHR_draco_mesh_compression.bufferView", ""},
// {".meshes[].primitives[].extensions.KHR_draco_mesh_compression.attributes{}", ""},
{".meshes[].primitives[].attributes{}", ".accessors"},
{".meshes[].primitives[].indices", ".accessors"},
{".meshes[].primitives[].material", ".materials"},
{".meshes[].primitives[].targets[]{}", ".accessors"},
{".nodes[].camera", ".cameras"},
{".nodes[].children[]", ".nodes"},
{".nodes[].skin", ".skins"},
{".nodes[].mesh", ".meshes"},
{".scene", ".scenes"},
// {".scenes[].extensions.EXT_lights_image_based.light", ""},
{".scenes[].nodes[]", ".nodes"},
{".skins[].inverseBindMatrices", ".accessors"},
{".skins[].skeleton", ".nodes"},
{".skins[].joints[]", ".nodes"},
{".textures[].sampler", ".samplers"},
{".textures[].source", ".images"},
// VRM
{".extensions.VRM.humanoid.humanBones[].node", ".nodes"},
{".extensions.VRM.firstPerson.firstPersonBone", ".nodes"},
{".extensions.VRM.firstPerson.meshAnnotations[].mesh", ".meshes"},
{".extensions.VRM.blendShapeMaster.blendShapeGroups[].binds[].mesh", ".meshes"},
// {".extensions.VRM.blendShapeMaster.blendShapeGroups[].binds[].index", ".meshes[i].primitives[*].targets"},
// {".extensions.VRM.blendShapeMaster.blendShapeGroups[].materialValues[].materialName", ".materials"},
// {".extensions.VRM.blendShapeMaster.blendShapeGroups[].materialValues[].propertyName", ""},
{".extensions.VRM.secondaryAnimation.boneGroups[].center", ".nodes"},
{".extensions.VRM.secondaryAnimation.boneGroups[].bones[]", ".nodes"},
{".extensions.VRM.secondaryAnimation.boneGroups[].colliderGroups[]", ".extensions.VRM.secondaryAnimation.colliderGroups"},
{".extensions.VRM.secondaryAnimation.colliderGroups[].node", ".nodes"},
{".extensions.VRM.materialProperties[].textureProperties{}", ".textures"},
};
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6431a3223ebec22419298a43183912e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,408 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UniJSON;
namespace UniGLTF.JsonSchema
{
public class JsonSchemaParser
{
DirectoryInfo[] m_dir;
Dictionary<FileInfo, byte[]> m_cache = new Dictionary<FileInfo, byte[]>();
public JsonSchemaParser(params DirectoryInfo[] dir)
{
m_dir = dir;
}
public static JsonSchemaSource Parse(string root, string jsonPath = "")
{
// setup
var path = new FileInfo(root);
var parser = new JsonSchemaParser(path.Directory);
// traverse
return parser.Load(path.Name, jsonPath);
}
public JsonSchemaSource Load(string fileName, string jsonPath)
{
JsonSchemaSource loaded = null;
foreach (var dir in m_dir)
{
var path = Path.Combine(dir.FullName, fileName);
if (File.Exists(path))
{
loaded = Load(new FileInfo(path), jsonPath);
break;
}
}
if (loaded is null)
{
throw new FileNotFoundException(fileName);
}
return loaded;
}
public JsonSchemaSource Load(FileInfo path, string jsonPath)
{
if (!m_cache.TryGetValue(path, out byte[] bytes))
{
// Console.WriteLine($"load {path}");
bytes = File.ReadAllBytes(path.FullName);
m_cache.Add(path, bytes);
}
{
var jsonSchema = Parse(bytes.ParseAsJson(), jsonPath);
jsonSchema.FilePath = path;
return jsonSchema;
}
}
void MergeTo(JsonSchemaSource src, JsonSchemaSource dst)
{
if (string.IsNullOrEmpty(dst.title))
{
dst.title = src.title;
}
if (string.IsNullOrEmpty(dst.description))
{
dst.description = src.description;
}
if (src.type != JsonSchemaType.Unknown)
{
dst.type = src.type;
}
foreach (var kv in src.EnumerateProperties())
{
dst.AddProperty(kv.Key, kv.Value);
}
if (src.enumStringValues != null)
{
dst.enumStringValues = src.enumStringValues.ToArray();
}
}
JsonSchemaSource Parse(ListTreeNode<JsonValue> json, string jsonPath)
{
var source = new JsonSchemaSource
{
JsonPath = jsonPath,
};
foreach (var kv in json.ObjectItems())
{
switch (kv.Key.GetString())
{
case "$ref":
{
var reference = Load(kv.Value.GetString(), jsonPath);
MergeTo(reference, source);
break;
}
case "allOf":
// glTF では継承として使われる
{
var reference = AllOf(kv.Value, jsonPath);
MergeTo(reference, source);
break;
}
case "$schema":
break;
case "type":
source.type = (JsonSchemaType)Enum.Parse(typeof(JsonSchemaType), kv.Value.GetString(), true);
break;
case "title":
source.title = kv.Value.GetString();
break;
case "description":
source.description = kv.Value.GetString();
break;
case "gltf_detailedDescription":
source.gltfDetail = kv.Value.GetString();
break;
case "default":
break;
case "gltf_webgl":
break;
case "anyOf":
// glTF ではenumとして使われる
ParseAnyOfAsEnum(ref source, kv.Value);
break;
case "oneOf":
// TODO: union 的な
break;
case "not":
// TODO: プロパティの両立を禁止する、排他的な
break;
case "pattern":
source.pattern = kv.Value.GetString();
break;
case "format":
// TODO
break;
case "gltf_uriType":
// TODO
break;
case "minimum":
if (source.type != JsonSchemaType.Number && source.type != JsonSchemaType.Integer) throw new Exception();
source.minimum = kv.Value.GetDouble();
break;
case "exclusiveMinimum":
if (source.type != JsonSchemaType.Number && source.type != JsonSchemaType.Integer) throw new Exception();
source.exclusiveMinimum = kv.Value.GetBoolean(); // ?
break;
case "maximum":
if (source.type != JsonSchemaType.Number && source.type != JsonSchemaType.Integer) throw new Exception();
source.maximum = kv.Value.GetDouble();
break;
case "multipleOf":
if (source.type != JsonSchemaType.Number && source.type != JsonSchemaType.Integer) throw new Exception();
source.multipleOf = kv.Value.GetDouble();
break;
case "properties":
if (source.type != JsonSchemaType.Object) throw new Exception();
// source.properties = new Dictionary<string, JsonSchemaSource>();
foreach (var prop in kv.Value.ObjectItems())
{
var propJsonPath = $"{jsonPath}.{prop.Key.GetString()}";
var propSchema = Parse(prop.Value, propJsonPath);
if (propSchema is null)
{
if (source.baseSchema is null)
{
// add empty object. extras
source.AddProperty(prop.Key.GetString(), new JsonSchemaSource
{
JsonPath = propJsonPath,
type = JsonSchemaType.Object,
});
}
// else if (source.baseSchema.GetPropertyFromPath(propJsonPath))
// {
// // ok
// }
else
{
throw new Exception("unknown");
}
}
else
{
if (source.GetProperty(prop.Key.GetString()) == null)
{
source.AddProperty(prop.Key.GetString(), propSchema);
}
}
}
break;
case "required":
source.required = kv.Value.ArrayItems().Select(x => x.GetString()).ToArray();
break;
case "dependencies":
// Property間の依存関係
// TODO:
break;
case "additionalProperties":
if (source.type != JsonSchemaType.Object) throw new Exception();
if (kv.Value.Value.ValueType == ValueNodeType.Object)
{
source.additionalProperties = Parse(kv.Value, $"{jsonPath}{{}}");
}
else if (kv.Value.Value.ValueType == ValueNodeType.Boolean && kv.Value.GetBoolean() == false)
{
// skip. do nothing
}
else
{
throw new NotImplementedException();
}
break;
case "minProperties":
if (source.type != JsonSchemaType.Object) throw new Exception();
source.minProperties = kv.Value.GetInt32();
break;
case "items":
if (source.type != JsonSchemaType.Array) throw new Exception();
source.items = Parse(kv.Value, $"{jsonPath}[]");
break;
case "uniqueItems":
if (source.type != JsonSchemaType.Array) throw new Exception();
source.uniqueItems = kv.Value.GetBoolean();
break;
case "maxItems":
if (source.type != JsonSchemaType.Array) throw new Exception();
source.maxItems = kv.Value.GetInt32();
break;
case "minItems":
if (source.type != JsonSchemaType.Array) throw new Exception();
source.minItems = kv.Value.GetInt32();
break;
case "enum":
if (source.type == JsonSchemaType.String)
{
ParseStringEnum(ref source, kv.Value);
}
else if (source.type == JsonSchemaType.Integer
|| source.type == JsonSchemaType.Number)
{
throw new NotImplementedException();
}
else
{
throw new NotImplementedException();
}
break;
default:
Console.WriteLine($"unknown property: {kv.Key.GetString()} => {kv.Value}");
break;
}
}
return source;
}
void ParseStringEnum(ref JsonSchemaSource source, ListTreeNode<JsonValue> json)
{
source.enumStringValues = json.ArrayItems().Select(x => x.GetString()).ToArray();
source.type = JsonSchemaType.EnumString;
}
void ParseAnyOfAsEnum(ref JsonSchemaSource source, ListTreeNode<JsonValue> json)
{
List<int> values = new List<int>();
List<string> stringValues = new List<string>();
List<string> descriptions = new List<string>();
foreach (var v in json.ArrayItems())
{
foreach (var kv in v.ObjectItems())
{
switch (kv.Key.GetString())
{
case "enum":
{
int i = 0;
foreach (var a in kv.Value.ArrayItems())
{
switch (a.Value.ValueType)
{
case ValueNodeType.Number:
case ValueNodeType.Integer:
values.Add(a.GetInt32());
break;
case ValueNodeType.String:
stringValues.Add(a.GetString());
break;
default:
throw new NotImplementedException();
}
++i;
}
}
break;
case "description":
{
descriptions.Add(kv.Value.GetString());
}
break;
case "type":
break;
default:
throw new NotImplementedException();
}
}
}
if (stringValues.Count > 0)
{
if (values.Count == 0)
{
source.enumStringValues = stringValues.ToArray();
source.type = JsonSchemaType.EnumString;
return;
}
}
if (descriptions.Count == values.Count)
{
source.enumValues = new KeyValuePair<string, int>[values.Count];
for (int i = 0; i < values.Count; ++i)
{
source.enumValues[i] = new KeyValuePair<string, int>
(
descriptions[i],
values[i]
);
}
source.type = JsonSchemaType.Enum;
return;
}
throw new NotImplementedException();
}
JsonSchemaSource AllOf(ListTreeNode<JsonValue> json, string jsonPath)
{
string refValue = null;
int count = 0;
foreach (var a in json.ArrayItems())
{
foreach (var kv in a.ObjectItems())
{
if (kv.Key.GetString() != "$ref")
{
throw new NotImplementedException();
}
refValue = kv.Value.GetString();
++count;
}
}
if (count != 1)
{
throw new NotImplementedException();
}
var reference = Load(refValue, jsonPath);
return reference;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5540ebde94dc9924989c2c8066b736d7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,274 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace UniGLTF.JsonSchema
{
/// <summary>
/// 型が確定する前にパースして値を集める
/// </summary>
public class JsonSchemaSource
{
public FileInfo FilePath;
public string JsonPath;
public static (string, string) SplitParent(string jsonPath)
{
var splitted = jsonPath.Split('.');
return (String.Join(".", splitted.Take(splitted.Length - 1)), splitted[splitted.Length - 1]);
}
public void AddJsonPath(string jsonPath, JsonSchemaSource source)
{
var (parent, child) = SplitParent(jsonPath);
var parentSchema = this.Get(parent);
var materialExtensions = parentSchema;
source.JsonPath = jsonPath;
materialExtensions.AddProperty(child, source);
}
public JsonSchemaSource Get(string jsonPath)
{
if (JsonPath == jsonPath)
{
return this;
}
if (jsonPath.StartsWith(JsonPath))
{
foreach (var child in Children())
{
var found = child.Get(jsonPath);
if (found != null)
{
return found;
}
}
}
return null;
}
public JsonSchemaType type;
public string title;
public string description;
public string gltfDetail;
public JsonSchemaSource baseSchema;
#region Number
public double? minimum;
public bool exclusiveMinimum;
public double? maximum;
public double? multipleOf;
#endregion
#region String
public string pattern;
#endregion
#region Object
List<KeyValuePair<string, JsonSchemaSource>> m_properties;
public JsonSchemaSource GetProperty(string name, bool remove = false)
{
if (m_properties is null)
{
return null;
}
for (int i = 0; i < m_properties.Count; ++i)
{
if (m_properties[i].Key == name)
{
var found = m_properties[i].Value;
if (remove)
{
m_properties.RemoveAt(i);
}
return found;
}
}
return null;
}
public void AddProperty(string name, JsonSchemaSource prop)
{
if (name is null)
{
throw new ArgumentNullException();
}
if (prop.type == JsonSchemaType.Unknown)
{
if (name == "extensions" || name == "extras")
{
// return;
prop.type = JsonSchemaType.Object;
}
else
{
throw new NotImplementedException();
}
}
if (m_properties is null)
{
m_properties = new List<KeyValuePair<string, JsonSchemaSource>>();
}
if (m_properties.Any(x => x.Key == name))
{
throw new ArgumentException($"{name}: is already exist");
}
m_properties.Add(new KeyValuePair<string, JsonSchemaSource>(name, prop));
}
public IEnumerable<KeyValuePair<string, JsonSchemaSource>> EnumerateProperties()
{
if (m_properties != null)
{
foreach (var kv in m_properties)
{
yield return kv;
}
}
}
public string[] required;
#endregion
#region Dictionary
public JsonSchemaSource additionalProperties;
public int? minProperties;
#endregion
#region Array
public JsonSchemaSource items;
public int? minItems;
public int? maxItems;
public bool? uniqueItems;
#endregion
#region Enum
public KeyValuePair<string, int>[] enumValues;
public string[] enumStringValues;
#endregion
public IEnumerable<JsonSchemaSource> Children()
{
if (m_properties != null)
{
foreach (var kv in m_properties)
{
yield return kv.Value;
}
}
else if (additionalProperties != null)
{
yield return additionalProperties;
}
else if (items != null)
{
if (type != JsonSchemaType.Array)
{
throw new NotImplementedException();
}
yield return items;
}
}
public IEnumerable<JsonSchemaSource> Traverse()
{
yield return this;
if (m_properties != null)
{
foreach (var kv in m_properties)
{
foreach (var x in kv.Value.Traverse())
{
yield return x;
}
}
}
else if (additionalProperties != null)
{
foreach (var x in additionalProperties.Traverse())
{
yield return x;
}
}
else if (items != null)
{
foreach (var x in items.Traverse())
{
yield return x;
}
}
}
public Schemas.JsonSchemaBase Create(bool useUpperCamelName, string rootName = default)
{
// if (baseSchema != null)
// {
// baseSchema.MergeTo(this);
// }
if (baseSchema != null)
{
if (type == JsonSchemaType.Unknown)
{
type = baseSchema.type;
}
}
switch (type)
{
case JsonSchemaType.Object:
if (this.JsonPath.EndsWith(".extensions") || this.JsonPath.EndsWith(".extras"))
{
return new Schemas.ExtensionJsonSchema(this);
}
if ((m_properties != null && m_properties.Any()) || additionalProperties is null)
{
var obj = new Schemas.ObjectJsonSchema(this, useUpperCamelName);
if (!string.IsNullOrEmpty(rootName))
{
obj.Title = rootName;
}
return obj;
}
else
{
return new Schemas.DictionaryJsonSchema(this, useUpperCamelName);
}
case JsonSchemaType.Array:
return new Schemas.ArrayJsonSchema(this, useUpperCamelName);
case JsonSchemaType.Boolean:
return new Schemas.BoolJsonSchema(this);
case JsonSchemaType.String:
return new Schemas.StringJsonSchema(this);
case JsonSchemaType.Number:
return new Schemas.NumberJsonSchema(this);
case JsonSchemaType.Integer:
return new Schemas.IntegerJsonSchema(this);
case JsonSchemaType.Enum:
return new Schemas.EnumJsonSchema(this);
case JsonSchemaType.EnumString:
return new Schemas.EnumStringJsonSchema(this);
default:
return null;
}
}
public void Dump(string indent = "")
{
Console.WriteLine($"{indent}{JsonPath}: {type}");
foreach (var x in Children())
{
x.Dump(indent + " ");
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 901c2cd9dc849e94296e50965f438e0e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
namespace UniGLTF.JsonSchema
{
public enum JsonSchemaType
{
Unknown,
Object,
Array,
String,
Number,
Integer,
Boolean,
Enum,
EnumString,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e52b7d2274918294ebac38aba0c9fb1c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 707d9adcac44ca2409a9903f2e25f3df
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,151 @@
using System;
using System.IO;
namespace UniGLTF.JsonSchema.Schemas
{
public class ArrayJsonSchema : JsonSchemaBase
{
public readonly JsonSchemaBase Items;
public readonly bool UniqueItems;
public readonly int MinItems;
public readonly int? MaxItems;
public ArrayJsonSchema(in JsonSchemaSource source, bool useUpperCamelName) : base(source)
{
Items = source.items.Create(useUpperCamelName);
Items.IsArrayItem = true;
UniqueItems = source.uniqueItems.GetValueOrDefault();
MinItems = source.minItems.GetValueOrDefault();
if (source.maxItems.HasValue)
{
MaxItems = source.maxItems.Value;
}
}
public override bool IsInline => false;
bool ItemsIsPrimitiveType => Items is PrimitiveJsonSchemaBase;
public override string ValueType
{
get
{
if (ItemsIsPrimitiveType)
{
return $"{Items.ValueType}[]";
}
else
{
return $"List<{Items.ValueType}>";
}
}
}
public override string GenerateDeserializerCall(string callName, string argName)
{
return $"{callName}({argName})";
}
public override void GenerateDeserializer(TraverseContext writer, string callName)
{
if (writer.Used.Contains(callName))
{
return;
}
writer.Used.Add(callName);
var itemCallName = callName + "_ITEM";
if (ItemsIsPrimitiveType)
{
writer.Write(@"
public static $0 $2(ListTreeNode<JsonValue> parsed)
{
var value = new $1[parsed.GetArrayCount()];
int i=0;
foreach(var x in parsed.ArrayItems())
{
value[i++] = $3;
}
return value;
}
"
.Replace("$0", ValueType)
.Replace("$1", Items.ValueType)
.Replace("$2", callName)
.Replace("$3", Items.GenerateDeserializerCall(itemCallName, "x"))
);
}
else
{
writer.Write(@"
public static $0 $2(ListTreeNode<JsonValue> parsed)
{
var value = new $1();
foreach(var x in parsed.ArrayItems())
{
value.Add($3);
}
return value;
}
"
.Replace("$0", ValueType)
.Replace("$1", ValueType)
.Replace("$2", callName)
.Replace("$3", Items.GenerateDeserializerCall(itemCallName, "x"))
);
}
if (!Items.IsInline)
{
Items.GenerateDeserializer(writer, itemCallName);
}
}
public override string CreateSerializationCondition(string argName)
{
return $"{argName}!=null&&{argName}.Count()>={MinItems}";
}
public override string GenerateSerializerCall(string callName, string argName)
{
return $"{callName}(f, {argName})";
}
public override void GenerateSerializer(TraverseContext writer, string callName)
{
if (writer.Used.Contains(callName))
{
return;
}
writer.Used.Add(callName);
var itemCallName = callName + "_ITEM";
writer.Write($@"
public static void {callName}(JsonFormatter f, {ValueType} value)
{{
f.BeginList();
foreach(var item in value)
{{
"
);
writer.Write($"{Items.GenerateSerializerCall(itemCallName, "item")};\n");
writer.Write(@"
}
f.EndList();
}
");
if (!Items.IsInline)
{
Items.GenerateSerializer(writer, itemCallName);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dd2ee0b1bf849a94bb504ba32e107418
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,36 @@
using System;
namespace UniGLTF.JsonSchema.Schemas
{
public class DictionaryJsonSchema : JsonSchemaBase
{
public readonly JsonSchemaBase AdditionalProperties;
public readonly int MinProperties;
public DictionaryJsonSchema(in JsonSchemaSource source, bool useUpperCamelName) : base(source)
{
AdditionalProperties = source.additionalProperties.Create(useUpperCamelName);
MinProperties = source.minProperties.GetValueOrDefault();
}
public override string ValueType => throw new NotImplementedException();
public override bool IsInline => throw new NotImplementedException();
public override string CreateSerializationCondition(string argName)
{
throw new NotImplementedException();
}
public override string GenerateDeserializerCall(string callName, string argName)
{
throw new NotImplementedException();
}
public override string GenerateSerializerCall(string callName, string argName)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4aa7a0339438a054781dbdf96e97ae9e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,90 @@
using System;
using System.Linq;
namespace UniGLTF.JsonSchema.Schemas
{
public struct EnumValue
{
public string Name;
public int Value;
public override string ToString()
{
return $"{Name}={Value}";
}
}
public class EnumJsonSchema : JsonSchemaBase
{
public readonly EnumValue[] Values;
public EnumJsonSchema(in JsonSchemaSource source) : base(source)
{
Values = source.enumValues.Select(x => new EnumValue
{
Name = x.Key,
Value = x.Value
}).ToArray();
}
public override string ValueType => Title;
public override bool IsInline => true;
public override string GenerateDeserializerCall(string callName, string argName)
{
return $"({ValueType})argName";
}
public override string CreateSerializationCondition(string argName)
{
return "true";
}
public override string GenerateSerializerCall(string callName, string argName)
{
return $"f.Value((int){argName})";
}
public override string ToString()
{
var values = string.Join(", ", Values);
return $"{base.ToString()} {{{values}}}";
}
}
public class EnumStringJsonSchema : JsonSchemaBase
{
public readonly String[] Values;
public EnumStringJsonSchema(in JsonSchemaSource source) : base(source)
{
Values = source.enumStringValues;
}
public override string ValueType => Title;
public override bool IsInline => true;
public override string GenerateDeserializerCall(string callName, string argName)
{
return $"({ValueType})Enum.Parse(typeof({ValueType}), {argName}.GetString(), true)";
}
public override string CreateSerializationCondition(string argName)
{
return "true";
}
public override string GenerateSerializerCall(string callName, string argName)
{
return $"f.Value({argName}.ToString())";
}
public override string ToString()
{
var values = string.Join(", ", Values);
return $"{base.ToString()} {{{values}}}";
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1479130f877f4d24f8ff772a56ad631e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
namespace UniGLTF.JsonSchema.Schemas
{
/// <summary>
/// glTF の extensions, extras を処理するための専用クラス
/// </summary>
public class ExtensionJsonSchema : JsonSchemaBase
{
public ExtensionJsonSchema(in JsonSchemaSource source) : base(source)
{
}
public override string ValueType => "glTFExtension";
public override bool IsInline => true;
public override string GenerateDeserializerCall(string callName, string argName)
{
return $"new glTFExtensionImport({argName})";
}
public override string CreateSerializationCondition(string argName)
{
return $"{argName}!=null";
}
public override string GenerateSerializerCall(string callName, string argName)
{
return $"{argName}.Serialize(f)";
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 754fd3156f24fc648bfe7cb766cf88f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,104 @@
using System;
using System.IO;
using System.Text;
namespace UniGLTF.JsonSchema.Schemas
{
public abstract class JsonSchemaBase
{
public readonly string JsonPath;
public string ClassName => JsonPath
.Replace(".", "__")
.Replace("[]", "_ITEM")
.Replace("{}", "_PROP")
;
public readonly JsonSchemaType JsonSchemaType;
public string Title;
public readonly string Description;
/// HardCoding
public string HardCode;
public JsonSchemaBase(in JsonSchemaSource source)
{
JsonPath = source.JsonPath;
JsonSchemaType = source.type;
Title = source.title;
Description = source.description;
}
public override string ToString()
{
var sb = new StringBuilder();
sb.Append("[");
sb.Append(JsonSchemaType);
sb.Append("]");
if (!string.IsNullOrEmpty(Title))
{
sb.Append($" \"{Title}\"");
}
return sb.ToString();
}
public bool IsArrayItem;
/// <summary>
/// CSharpの型
/// </summary>
/// <value></value>
public abstract string ValueType { get; }
/// <summary>
/// Use or not GenerateDeserializer and GenerateSerializer
/// </summary>
public abstract bool IsInline { get; }
/// <summary>
/// Deserializer の呼び出し
/// </summary>
/// <param name="callName"></param>
/// <param name="argName"></param>
/// <returns></returns>
public abstract string GenerateDeserializerCall(string callName, string argName);
/// <summary>
/// Deserializer の実装
/// </summary>
/// <param name="writer"></param>
/// <param name="callName"></param>
public virtual void GenerateDeserializer(TraverseContext writer, string callName)
{
throw new NotImplementedException();
}
/// <summary>
/// Serialize 時に出力するか否か
///
/// * null や -1 などの無効な値のキーをスキップするために使う
///
/// </summary>
/// <param name="argName"></param>
/// <returns></returns>
public abstract string CreateSerializationCondition(string argName);
/// <summary>
/// Serializer の呼び出し
/// </summary>
/// <param name="callName"></param>
/// <param name="argName"></param>
/// <returns></returns>
public abstract string GenerateSerializerCall(string callName, string argName);
/// <summary>
/// Serializer 実装
/// </summary>
/// <param name="writer"></param>
/// <param name="callName"></param>
public virtual void GenerateSerializer(TraverseContext writer, string callName)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5dd0100b5b0d18f45ad1485ee493b439
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace UniGLTF.JsonSchema.Schemas
{
public static class JsonSchemaBaseExtensions
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e345d604804282c4881ea5b2e580b237
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace UniGLTF.JsonSchema.Schemas
{
public class ObjectJsonSchema : JsonSchemaBase
{
public string[] Required;
public readonly Dictionary<string, JsonSchemaBase> Properties = new Dictionary<string, JsonSchemaBase>();
bool m_useUpperCamelName;
public ObjectJsonSchema(in JsonSchemaSource source, bool useUpperCamelName) : base(source)
{
m_useUpperCamelName = useUpperCamelName;
foreach (var kv in source.EnumerateProperties())
{
var prop = kv.Value.Create(useUpperCamelName);
if (prop is null)
{
throw new NotImplementedException();
}
var key = kv.Key;
Properties.Add(key, prop);
}
Required = source.required;
}
public override string ToString()
{
var values = "";
if (Required != null && Required.Any())
{
values = $" require: {{{string.Join(", ", Required)}}}";
}
return $"{base.ToString()}{values}";
}
public override string ValueType => Title;
public override bool IsInline => false;
public override string GenerateDeserializerCall(string callName, string argName)
{
return $"{callName}({argName})";
}
public override void GenerateDeserializer(TraverseContext writer, string callName)
{
if (writer.Used.Contains(callName))
{
return;
}
writer.Used.Add(callName);
writer.Write(@"
public static $0 $2(ListTreeNode<JsonValue> parsed)
{
var value = new $0();
foreach(var kv in parsed.ObjectItems())
{
var key = kv.Key.GetString();
"
.Replace("$0", ValueType)
.Replace("$2", callName)
);
foreach (var kv in Properties)
{
writer.Write(@"
if(key==""$0""){
value.$2 = $1;
continue;
}
"
.Replace("$0", kv.Key)
.Replace("$1", kv.Value.GenerateDeserializerCall($"Deserialize_{kv.Key.ToUpperCamel()}", "kv.Value"))
.Replace("$2", kv.Key.ToUpperCamel())
);
}
writer.Write(@"
}
return value;
}
");
foreach (var kv in Properties)
{
if (!kv.Value.IsInline)
{
kv.Value.GenerateDeserializer(writer, $"Deserialize_{kv.Key.ToUpperCamel()}");
}
}
}
public override string CreateSerializationCondition(string argName)
{
return $"{argName}!=null";
}
/// <summary>
/// シリアライザーのコード生成
///
/// ObjectのFieldのみ値によって、出力するか否かの判定が必用。
///
/// 例: 空文字列は出力しない
///
/// </summary>
/// <param name="writer"></param>
/// <param name="callName"></param>
public override void GenerateSerializer(TraverseContext writer, string callName)
{
if (writer.Used.Contains(callName))
{
return;
}
writer.Used.Add(callName);
writer.Write($@"
public static void {callName}(JsonFormatter f, {ValueType} value)
{{
f.BeginMap();
"
);
foreach (var kv in Properties)
{
var valueName = $"value.{kv.Key.ToUpperCamel()}";
var condition = "";
writer.Write($@"
if({kv.Value.CreateSerializationCondition(valueName)}{condition}){{
f.Key(""{kv.Key}"");
{kv.Value.GenerateSerializerCall($"Serialize_{kv.Key.ToUpperCamel()}", valueName)};
}}
");
}
writer.Write(@"
f.EndMap();
}
");
foreach (var kv in Properties)
{
if (!kv.Value.IsInline)
{
kv.Value.GenerateSerializer(writer, $"Serialize_{kv.Key.ToUpperCamel()}");
}
}
}
public override string GenerateSerializerCall(string callName, string argName)
{
return $"{callName}(f, {argName})";
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae9dcedb49a42f9449672e00b34e8598
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,117 @@
using System;
namespace UniGLTF.JsonSchema.Schemas
{
public abstract class PrimitiveJsonSchemaBase : JsonSchemaBase
{
protected PrimitiveJsonSchemaBase(in JsonSchemaSource source) : base(source)
{ }
public override bool IsInline => true;
public override string CreateSerializationCondition(string argName)
{
if (IsArrayItem)
{
throw new NotImplementedException();
}
else
{
return $"{argName}.HasValue";
}
}
public override string GenerateSerializerCall(string callName, string argName)
{
if (IsArrayItem)
{
return $"f.Value({argName})";
}
else
{
return $"f.Value({argName}.GetValueOrDefault())";
}
}
}
public class BoolJsonSchema : PrimitiveJsonSchemaBase
{
public override string ValueType => IsArrayItem ? "bool" : "bool?";
public BoolJsonSchema(in JsonSchemaSource src) : base(src)
{
}
public override string GenerateDeserializerCall(string callName, string argName)
{
return $"{argName}.GetBoolean()";
}
}
public class IntegerJsonSchema : PrimitiveJsonSchemaBase
{
public readonly int? Minimum;
public readonly bool ExclusiveMinimum;
public readonly int? Maximum;
public readonly int? MultipleOf;
public string IndexTargetJsonPath;
public IntegerJsonSchema(in JsonSchemaSource source) : base(source)
{
if (source.minimum.HasValue)
{
Minimum = (int)source.minimum.Value;
}
ExclusiveMinimum = source.exclusiveMinimum;
if (source.maximum.HasValue)
{
Maximum = (int)source.maximum.Value;
}
if (source.multipleOf.HasValue)
{
MultipleOf = (int)source.multipleOf.Value;
}
}
public override string ValueType => IsArrayItem ? "int" : "int?";
public override string GenerateDeserializerCall(string callName, string argName)
{
return $"{argName}.GetInt32()";
}
}
public class NumberJsonSchema : PrimitiveJsonSchemaBase
{
public readonly double? Minimum;
public readonly bool ExclusiveMinimum;
public readonly double? Maximum;
public readonly double? MultipleOf;
public NumberJsonSchema(in JsonSchemaSource source) : base(source)
{
if (source.minimum.HasValue)
{
Minimum = source.minimum.Value;
}
ExclusiveMinimum = source.exclusiveMinimum;
if (source.maximum.HasValue)
{
Maximum = source.maximum.Value;
}
if (source.multipleOf.HasValue)
{
MultipleOf = source.multipleOf.Value;
}
}
public override string ValueType => IsArrayItem ? "float" : "float?";
public override string GenerateDeserializerCall(string callName, string argName)
{
return $"{argName}.GetSingle()";
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e64153bf38e165643bb501c5967734d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
namespace UniGLTF.JsonSchema.Schemas
{
public class StringJsonSchema : JsonSchemaBase
{
public readonly string Pattern;
public StringJsonSchema(in JsonSchemaSource source) : base(source)
{
Pattern = source.pattern;
}
public override string ValueType => "string";
public override bool IsInline => true;
public override string CreateSerializationCondition(string argName)
{
return $"!string.IsNullOrEmpty({argName})";
}
public override string GenerateDeserializerCall(string callName, string argName)
{
return $"{argName}.GetString()";
}
public override string GenerateSerializerCall(string callName, string argName)
{
return $"f.Value({argName})";
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5c3f6cf80367e542b1464a316fe868b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,22 @@
using System.IO;
using System.Collections.Generic;
namespace UniGLTF.JsonSchema.Schemas
{
public class TraverseContext
{
public readonly TextWriter Writer;
public readonly HashSet<string> Used = new HashSet<string>();
public TraverseContext(TextWriter writer)
{
Writer = writer;
}
public void Write(string s)
{
Writer.Write(s);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f169b3199e32ab24c82c6968e6113f14
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,10 @@
namespace UniGLTF.JsonSchema
{
public static class StringExtensions
{
public static string ToUpperCamel(this string key)
{
return key.Substring(0, 1).ToUpper() + key.Substring(1);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79c912c0da0179f42889a1746c8d4741
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -2,6 +2,7 @@
"name": "MeshUtility.Editor",
"references": [
"MeshUtility",
"VrmLib",
"ShaderProperty.Runtime"
],
"optionalUnityReferences": [],

View File

@ -99,13 +99,6 @@ namespace UniGLTF
return size;
}
public static Byte[] ToArray(this ArraySegment<byte> src)
{
var dst = new byte[src.Count];
Array.Copy(src.Array, src.Offset, dst, 0, src.Count);
return dst;
}
public static T[] SelectInplace<T>(this T[] src, Func<T, T> pred)
{
for (int i = 0; i < src.Length; ++i)
@ -116,7 +109,7 @@ namespace UniGLTF
}
public static void Copy<TFrom, TTo>(ArraySegment<TFrom> src, ArraySegment<TTo> dst)
where TFrom: struct
where TFrom : struct
where TTo : struct
{
var bytes = new byte[src.Count * Marshal.SizeOf(typeof(TFrom))];

View File

@ -11,7 +11,7 @@ namespace UniGLTF
public static readonly Utf8String ExtensionNameUtf8 = Utf8String.From(ExtensionName);
static readonly byte[] Raw = new byte[] { (byte)'{', (byte)'}' };
public static readonly byte[] Raw = new byte[] { (byte)'{', (byte)'}' };
public static glTFMaterial CreateDefault()
{

View File

@ -73,7 +73,7 @@ namespace UniGLTF
public static bool TryGet(glTFTextureInfo info, out glTF_KHR_texture_transform t)
{
if (info.extras is glTFExtensionImport imported)
if (info != null && info.extras is glTFExtensionImport imported)
{
foreach (var kv in imported.ObjectItems())
{

View File

@ -2,6 +2,9 @@ using System;
namespace VRM
{
/// <summary>
/// 0系では運用されなかった。基本的に凍結。今後も変更しない
/// </summary>
public class VRMSpecVersion
{
public const int Major = 0;

View File

@ -41,10 +41,13 @@ namespace VRM
if (glTF_VRM_extensions.TryDeserilize(GLTF.extensions, out glTF_VRM_extensions vrm))
{
VRM = vrm;
// override material importer
SetMaterialImporter(new VRMMaterialImporter(this, VRM.materialProperties));
}
else
{
throw new KeyNotFoundException("not vrm0");
}
// override material importer
SetMaterialImporter(new VRMMaterialImporter(this, VRM.materialProperties));
}
#region OnLoad

8
Assets/VRM10.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4fcd733d82a0cc54f9b4c88f8ee97155
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/VRM10/Editor.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7d2fdd9f95a38e641a1b3f56cf5eaf16
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fbb107c1888c945489ac2c567af3385e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 165ab9c04d7db604586dd5175eec153f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,26 @@
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
public class ReadOnlyAttribute : PropertyAttribute
{
}
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
public class ReadOnlyDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label, true);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 06a80a5d47e6b31458e2714ed692e1a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4dcc84ae9f5201a479ca8df8fc991824
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,298 @@
using MeshUtility;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// Prefabをインスタンス化してPreviewに表示する
///
/// * https://github.com/Unity-Technologies/UnityCsReference/blob/11bcfd801fccd2a52b09bb6fd636c1ddcc9f1705/Editor/Mono/Inspector/ModelInspector.cs
///
/// </summary>
public abstract class ExpressionEditorBase : Editor
{
/// <summary>
/// PreviewRenderUtilityを管理する。
///
/// * PreviewRenderUtility.m_cameraのUnityVersionによる切り分け
///
/// </summary>
PreviewFaceRenderer m_renderer;
/// <summary>
/// Prefabをインスタンス化したシーンを管理する。
///
/// * ExpressionのBake
/// * MaterialMorphの適用
/// * Previewカメラのコントロール
/// * Previewライティングのコントロール
///
/// </summary>
PreviewSceneManager m_scene;
protected PreviewSceneManager PreviewSceneManager
{
get { return m_scene; }
}
/// <summary>
/// Previewシーンに表示するPrefab
/// </summary>
GameObject m_prefab;
protected GameObject Prefab
{
get { return m_prefab; }
private set
{
if (m_prefab == value) return;
//Debug.LogFormat("Prefab = {0}", value);
m_prefab = value;
if (m_scene != null)
{
//Debug.LogFormat("OnDestroy");
GameObject.DestroyImmediate(m_scene.gameObject);
m_scene = null;
}
if (m_prefab != null)
{
m_scene = UniVRM10.PreviewSceneManager.GetOrCreate(m_prefab);
if (m_scene != null)
{
m_scene.gameObject.SetActive(false);
}
Bake();
}
}
}
protected abstract VRM10Expression CurrentExpression();
/// <summary>
/// Preview シーンに Expression を適用する
/// </summary>
protected void Bake()
{
if (m_scene != null)
{
//Debug.Log("Bake");
m_scene.Bake(CurrentExpression(), 1.0f);
}
}
protected virtual GameObject GetPrefab()
{
var assetPath = AssetDatabase.GetAssetPath(target);
if (string.IsNullOrEmpty(assetPath))
{
return null;
}
var mainObject = AssetDatabase.LoadMainAssetAtPath(assetPath);
if (mainObject != null)
{
//return mainObject;
}
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
if (prefab != null) return prefab;
var parent = UnityPath.FromUnityPath(assetPath).Parent;
var prefabPath = parent.Parent.Child(parent.FileNameWithoutExtension + ".prefab");
prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath.Value);
if (prefab != null) return prefab;
var parentParent = UnityPath.FromUnityPath(assetPath).Parent.Parent;
var vrmPath = parent.Parent.Child(parent.FileNameWithoutExtension + ".vrm");
prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(vrmPath.Value);
return prefab;
}
protected virtual void OnEnable()
{
m_renderer = new PreviewFaceRenderer();
Prefab = GetPrefab();
}
protected virtual void OnDisable()
{
if (m_renderer != null)
{
m_renderer.Dispose();
m_renderer = null;
}
}
protected virtual void OnDestroy()
{
if (m_scene != null)
{
//Debug.LogFormat("OnDestroy");
m_scene.Clean();
GameObject.DestroyImmediate(m_scene.gameObject);
m_scene = null;
}
}
protected static void Separator()
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
//GUILayout.Space();
GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
Prefab = (GameObject)EditorGUILayout.ObjectField("Preview Prefab", Prefab, typeof(GameObject), false);
//Separator();
}
private static int sliderHash = "Slider".GetHashCode();
float m_yaw = 180.0f;
float m_pitch;
Vector3 m_position = new Vector3(0, 0, -0.8f);
// very important to override this, it tells Unity to render an ObjectPreview at the bottom of the inspector
public override bool HasPreviewGUI() { return true; }
public RenderTexture PreviewTexture;
// the main ObjectPreview function... it's called constantly, like other IMGUI On*GUI() functions
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
// if this is happening, you have bigger problems
if (!ShaderUtil.hardwareSupportsRectRenderTexture)
{
if (Event.current.type == EventType.Repaint)
{
EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40f),
"Mesh preview requires\nrender texture support");
}
return;
}
var src = r;
var min = Mathf.Min(r.width, r.height);
r.width = min;
r.height = min;
r.x = src.x + (src.width - min) / 2;
r.y = src.y + (src.height - min) / 2;
//previewDir = Drag2D(previewDir, r);
{
int controlId = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
Event e = Event.current;
switch (e.GetTypeForControl(controlId))
{
case EventType.MouseDown:
if (r.Contains(e.mousePosition) && (double)r.width > 50.0)
{
GUIUtility.hotControl = controlId;
e.Use();
EditorGUIUtility.SetWantsMouseJumping(1);
break;
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == controlId)
GUIUtility.hotControl = 0;
EditorGUIUtility.SetWantsMouseJumping(0);
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == controlId)
{
if (e.button == 2)
{
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height);
m_position.x -= shift.x;
m_position.y += shift.y;
e.Use();
GUI.changed = true;
}
else if (
e.button == 0 ||
e.button == 1)
{
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height) * 140f;
m_yaw += shift.x;
m_pitch += shift.y;
m_pitch = Mathf.Clamp(m_pitch, -90f, 90f);
e.Use();
GUI.changed = true;
}
break;
}
break;
case EventType.ScrollWheel:
//Debug.LogFormat("wheel: {0}", current.delta);
if (r.Contains(e.mousePosition))
{
if (e.delta.y > 0)
{
m_position.z *= 1.1f;
Repaint();
}
else if (e.delta.y < 0)
{
m_position.z *= 0.9f;
Repaint();
}
}
break;
}
//return scrollPosition;
}
//Debug.LogFormat("{0}", previewDir);
if (Event.current.type != EventType.Repaint)
{
// if we don't need to update yet, then don't
return;
}
if (m_renderer != null && m_scene != null)
{
PreviewTexture = m_renderer.Render(r, background, m_scene, m_yaw, m_pitch, m_position) as RenderTexture;
if (PreviewTexture != null)
{
// draw the RenderTexture in the ObjectPreview pane
GUI.DrawTexture(r, PreviewTexture, ScaleMode.StretchToFill, false);
}
}
}
public override string GetInfoString()
{
var expression = CurrentExpression();
if (expression == null)
{
return "no expression";
}
var key = ExpressionKey.CreateFromClip(expression);
if (key.Preset != VrmLib.ExpressionPreset.Custom)
{
return string.Format("Preset: {0}", key.Preset);
}
else
{
return string.Format("Custom: {0}", key.Name);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a64fbc26ee3c274fbfd9bce32f0b5dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,245 @@
using System;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
public static class ExpressionEditorHelper
{
public static bool StringPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = Array.IndexOf(options, prop.stringValue);
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.stringValue = options[newIndex];
return true;
}
else
{
return false;
}
}
public static T EnumPopup<T>(Rect rect, T value) where T : Enum
{
return (T)EditorGUI.EnumPopup(rect, (Enum)value);
}
public static bool IntPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = prop.intValue;
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.intValue = newIndex;
return true;
}
else
{
return false;
}
}
public static bool FloatSlider(Rect rect, SerializedProperty prop, float maxValue)
{
var oldValue = prop.floatValue;
var newValue = EditorGUI.Slider(rect, prop.floatValue, 0, 100f);
if (newValue != oldValue)
{
prop.floatValue = newValue;
return true;
}
else
{
return false;
}
}
public static bool ColorProp(Rect rect, SerializedProperty prop)
{
var oldValue = (Color)prop.vector4Value;
var newValue = EditorGUI.ColorField(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector4Value = newValue;
return true;
}
else
{
return false;
}
}
public static bool UVProp(Rect rect, SerializedProperty prop)
{
var oldValue = prop.vector2Value;
var newValue = EditorGUI.Vector2Field(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector2Value = newValue;
return true;
}
else
{
return false;
}
}
static Rect AdvanceRect(ref float x, float y, float w, float h)
{
var rect = new Rect(x, y, w, h);
x += w;
return rect;
}
static float[] v2 = new float[2];
static GUIContent[] l2 = new GUIContent[]{
new GUIContent("x"),
new GUIContent("y")
};
static Vector4 TilingOffset(Rect rect, string label, Vector4 src)
{
/*
var style = new GUIStyle()
{
alignment = TextAnchor.MiddleRight,
};
*/
var quad = (rect.width - 56);
var x = rect.x;
//EditorGUIUtility.labelWidth = 18;
EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Tiling");
v2[0] = src.x;
v2[1] = src.y;
EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
src.x = v2[0];
src.y = v2[1];
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
//src.y = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.y);
rect.y += EditorGUIUtility.singleLineHeight;
x = rect.x;
EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Offset");
v2[0] = src.z;
v2[1] = src.w;
EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
src.z = v2[0];
src.w = v2[1];
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad * 2, rect.height), "Offset X", style);
//src.z = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "X", src.z);
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
//src.w = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.w);
return src;
}
static bool OffsetProp(Rect rect, SerializedProperty prop)
{
var oldValue = prop.vector4Value;
//var newValue = EditorGUI.Vector4Field(rect, prop.displayName, oldValue);
var newValue = TilingOffset(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector4Value = newValue;
return true;
}
else
{
return false;
}
}
}
/// https://gist.github.com/gszauer/7799899
public class TextureScale
{
private static Color[] texColors;
private static Color[] newColors;
private static int w;
private static float ratioX;
private static float ratioY;
private static int w2;
public static void Scale(Texture2D tex, int newWidth, int newHeight)
{
texColors = tex.GetPixels();
newColors = new Color[newWidth * newHeight];
ratioX = 1.0f / ((float)newWidth / (tex.width - 1));
ratioY = 1.0f / ((float)newHeight / (tex.height - 1));
w = tex.width;
w2 = newWidth;
BilinearScale(0, newHeight);
tex.Resize(newWidth, newHeight);
tex.SetPixels(newColors);
tex.Apply();
}
private static void BilinearScale(int start, int end)
{
for (var y = start; y < end; y++)
{
int yFloor = (int)Mathf.Floor(y * ratioY);
var y1 = yFloor * w;
var y2 = (yFloor + 1) * w;
var yw = y * w2;
for (var x = 0; x < w2; x++)
{
int xFloor = (int)Mathf.Floor(x * ratioX);
var xLerp = x * ratioX - xFloor;
newColors[yw + x] = ColorLerpUnclamped(ColorLerpUnclamped(texColors[y1 + xFloor], texColors[y1 + xFloor + 1], xLerp),
ColorLerpUnclamped(texColors[y2 + xFloor], texColors[y2 + xFloor + 1], xLerp),
y * ratioY - yFloor);
}
}
}
private static Color ColorLerpUnclamped(Color c1, Color c2, float value)
{
return new Color(c1.r + (c2.r - c1.r) * value,
c1.g + (c2.g - c1.g) * value,
c1.b + (c2.b - c1.b) * value,
c1.a + (c2.a - c1.a) * value);
}
/// http://light11.hatenadiary.com/entry/2018/04/19/194015
public static Texture2D GetResized(Texture2D texture, int width, int height)
{
// リサイズ後のサイズを持つRenderTextureを作成して書き込む
var rt = RenderTexture.GetTemporary(width, height);
Graphics.Blit(texture, rt);
// リサイズ後のサイズを持つTexture2Dを作成してRenderTextureから書き込む
var preRT = RenderTexture.active;
RenderTexture.active = rt;
var ret = new Texture2D(width, height);
ret.ReadPixels(new Rect(0, 0, width, height), 0, 0);
ret.Apply();
RenderTexture.active = preRT;
RenderTexture.ReleaseTemporary(rt);
return ret;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dfbaf4fb19a693244b0fde44542b18b8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,138 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace UniVRM10
{
/// <summary>
/// ExpressionAvatarEditorの部品
/// </summary>
class ExpressionClipSelector
{
VRM10ExpressionAvatar m_avatar;
int m_mode;
static readonly string[] MODES = new string[]{
"Button",
"List"
};
ReorderableExpressionList m_clipList;
public VRM10Expression GetSelected()
{
if (m_avatar == null || m_avatar.Clips == null || m_avatar.Clips.Count == 0)
{
return null;
}
if (m_selectedIndex < 0 || m_selectedIndex >= m_avatar.Clips.Count)
{
return null;
}
return m_avatar.Clips[m_selectedIndex];
}
public event Action<VRM10Expression> Selected;
void RaiseSelected(int index)
{
m_clipList.Select(index);
var clip = GetSelected();
var handle = Selected;
if (handle == null)
{
return;
}
handle(clip);
}
int m_selectedIndex;
int SelectedIndex
{
get { return m_selectedIndex; }
set
{
// これで更新するべし
if (m_selectedIndex == value) return;
m_selectedIndex = value;
RaiseSelected(value);
}
}
public ExpressionClipSelector(VRM10ExpressionAvatar avatar, SerializedObject serializedObject)
{
avatar.RemoveNullClip();
m_avatar = avatar;
var prop = serializedObject.FindProperty("Clips");
m_clipList = new ReorderableExpressionList(serializedObject, prop, avatar);
m_clipList.Selected += (selected) =>
{
SelectedIndex = avatar.Clips.IndexOf(selected);
};
}
public void GUI()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Select Expression", EditorStyles.boldLabel);
m_mode = GUILayout.Toolbar(m_mode, MODES);
switch (m_mode)
{
case 0:
SelectGUI();
break;
case 1:
m_clipList.GUI();
break;
default:
throw new NotImplementedException();
}
}
void SelectGUI()
{
if (m_avatar != null && m_avatar.Clips != null)
{
var array = m_avatar.Clips
.Select(x => x != null
? ExpressionKey.CreateFromClip(x).ToString()
: "null"
).ToArray();
SelectedIndex = GUILayout.SelectionGrid(SelectedIndex, array, 4);
}
if (GUILayout.Button("Add Expression"))
{
var dir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(m_avatar));
var path = EditorUtility.SaveFilePanel(
"Create Expression",
dir,
string.Format("Expression#{0}.asset", m_avatar.Clips.Count),
"asset");
if (!string.IsNullOrEmpty(path))
{
var clip = VRM10ExpressionAvatar.CreateExpression(path.ToUnityRelativePath());
//clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(target));
m_avatar.Clips.Add(clip);
}
}
}
public void DuplicateWarn()
{
var key = ExpressionKey.CreateFromClip(GetSelected());
if (m_avatar.Clips.Where(x => key.Match(x)).Count() > 1)
{
EditorGUILayout.HelpBox("duplicate clip: " + key, MessageType.Error);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d4d5da207577eb48ad767ff59bcf8b4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,168 @@
using System;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// based
///
/// * https://gist.github.com/radiatoryang/a2282d44ba71848e498bb2e03da98991
/// </summary>
/// <summary>
/// PreviewRenderUtilityを管理する
/// PreviewSceneをレンダリングする
/// </summary>
public class PreviewFaceRenderer : IDisposable
{
PreviewRenderUtility m_previewUtility;
public Camera PreviewCamera
{
get
{
#if UNITY_2017_1_OR_NEWER
return m_previewUtility.camera;
#else
return m_previewUtility.m_Camera;
#endif
}
}
public Light[] PreviewLights
{
get
{
#if UNITY_2017_1_OR_NEWER
return m_previewUtility.lights;
#else
return m_previewUtility.m_Light;
#endif
}
}
public void SetAmbientColor(Color color)
{
#if UNITY_2017_1_OR_NEWER
m_previewUtility.ambientColor = color;
#else
// ?
#endif
}
public PreviewFaceRenderer()
{
m_previewUtility = new PreviewRenderUtility();
foreach (var light in PreviewLights)
{
if (light == null) continue;
light.intensity = 0f;
}
if (PreviewLights.Length > 0 && PreviewLights[0] != null)
{
PreviewLights[0].intensity = 1f;
PreviewLights[0].transform.rotation = Quaternion.Euler(20f, 200f, 0);
PreviewLights[0].color = new Color(1f, 1f, 1f, 1f);
}
SetAmbientColor(new Color(0.1f, 0.1f, 0.1f, 1f));
}
class FogScope : IDisposable
{
bool fog;
public FogScope()
{
fog = RenderSettings.fog; // ... let's remember the current fog setting...
// we are technically rendering everything in the scene, so scene fog might affect it...
Unsupported.SetRenderSettingsUseFogNoDirty(false); // ... and then temporarily turn it off
}
public void Dispose()
{
Unsupported.SetRenderSettingsUseFogNoDirty(fog);
}
}
//const float FACTOR = 0.1f;
public Texture Render(Rect r, GUIStyle background, PreviewSceneManager scene,
float yaw, float pitch, Vector3 position)
{
if (scene == null) return null;
using (var fog = new FogScope())
{
m_previewUtility.BeginPreview(r, background); // set up the PreviewRenderUtility's mini internal scene
// setup the ObjectPreview's camera
scene.SetupCamera(PreviewCamera, scene.TargetPosition, yaw, pitch, position);
foreach (var item in scene.EnumRenderItems)
{
// now, actually render out the RenderTexture
//RenderMeshPreview(previewMesh, skinMeshRender.sharedMaterials);
// submesh support, in case the mesh is made of multiple parts
int subMeshCount = item.Mesh.subMeshCount;
for (int i = 0; i < subMeshCount; i++)
{
m_previewUtility.DrawMesh(item.Mesh,
item.Position, item.Rotation,
item.Materials[i], i);
}
}
// VERY IMPORTANT: this manually tells the camera to render and produce the render texture
PreviewCamera.Render();
//m_previewUtility.Render(false, false);
// reset the scene's fog from before
return m_previewUtility.EndPreview();
}
}
#region IDisposable Support
private bool disposedValue = false; // 重複する呼び出しを検出するには
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: マネージ状態を破棄します (マネージ オブジェクト)。
if (this.m_previewUtility != null)
{
this.m_previewUtility.Cleanup();
this.m_previewUtility = null;
}
}
// TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下のファイナライザーをオーバーライドします。
// TODO: 大きなフィールドを null に設定します。
disposedValue = true;
}
}
// TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします。
// ~PreviewFaceRenderer() {
// // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
// Dispose(false);
// }
// このコードは、破棄可能なパターンを正しく実装できるように追加されました。
public void Dispose()
{
// このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
Dispose(true);
// TODO: 上のファイナライザーがオーバーライドされる場合は、次の行のコメントを解除してください。
// GC.SuppressFinalize(this);
}
#endregion
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9de8d16a3d572ea4da3fc4a69ba61964
timeCreated: 1522931965
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,83 @@
using System;
using System.IO;
using UnityEditor;
using UnityEditorInternal;
namespace UniVRM10
{
class ReorderableExpressionList
{
ReorderableList m_list;
public event Action<VRM10Expression> Selected;
void RaiseSelected(VRM10Expression selected)
{
var handler = Selected;
if (handler == null)
{
return;
}
handler(selected);
}
public void Select(int index)
{
m_list.index = index;
}
public ReorderableExpressionList(SerializedObject serializedObject, SerializedProperty prop, UnityEngine.Object target)
{
m_list = new ReorderableList(serializedObject, prop);
m_list.drawHeaderCallback = (rect) =>
EditorGUI.LabelField(rect, "Expressions");
m_list.drawElementCallback = (rect, index, isActive, isFocused) =>
{
var element = prop.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
EditorGUI.PropertyField(rect, element);
};
m_list.onAddCallback += (list) =>
{
// Add slot
prop.arraySize++;
// select last item
list.index = prop.arraySize - 1;
// get last item
var element = prop.GetArrayElementAtIndex(list.index);
element.objectReferenceValue = null;
var dir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(target));
var path = EditorUtility.SaveFilePanel(
"Create Expression",
dir,
string.Format("Expression#{0}.asset", list.count),
"asset");
if (!string.IsNullOrEmpty(path))
{
var clip = VRM10ExpressionAvatar.CreateExpression(path.ToUnityRelativePath());
//clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(target));
element.objectReferenceValue = clip;
}
};
m_list.onSelectCallback += (list) =>
{
var a = list.serializedProperty;
var selected = a.GetArrayElementAtIndex(list.index);
RaiseSelected((VRM10Expression)selected.objectReferenceValue);
};
//m_clipList.onCanRemoveCallback += list => true;
}
public void GUI()
{
m_list.DoLayoutList();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5a5707cea4226d40af879c4c9778002
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,87 @@
using System;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace UniVRM10
{
public class ReorderableMaterialColorBindingList
{
ReorderableList m_list;
SerializedProperty m_serializedProperty;
bool m_changed;
public ReorderableMaterialColorBindingList(SerializedObject serializedObject, string[] materialNames, int height)
{
m_serializedProperty = serializedObject.FindProperty(nameof(VRM10Expression.MaterialColorBindings));
m_list = new ReorderableList(serializedObject, m_serializedProperty);
m_list.elementHeight = height * 3;
m_list.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = m_serializedProperty.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if (DrawMaterialValueBinding(rect, element, materialNames, height))
{
m_changed = true;
}
};
}
///
/// Material List のElement描画
///
static bool DrawMaterialValueBinding(Rect position, SerializedProperty property,
string[] materialNames, int height)
{
bool changed = false;
if (materialNames != null)
{
// Material を選択する
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int materialIndex;
if (ExpressionEditorHelper.StringPopup(rect, property.FindPropertyRelative(nameof(MaterialColorBinding.MaterialName)), materialNames, out materialIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
// 対象のプロパティを enum から選択する
var bindTypeProp = property.FindPropertyRelative("BindType");
var bindTypes = (VrmLib.MaterialBindType[])Enum.GetValues(typeof(VrmLib.MaterialBindType));
var bindType = bindTypes[bindTypeProp.enumValueIndex];
var newBindType = ExpressionEditorHelper.EnumPopup(rect, bindType);
if (newBindType != bindType)
{
bindTypeProp.enumValueIndex = Array.IndexOf(bindTypes, newBindType);
changed = true;
}
// 目標の色
y += height;
rect = new Rect(position.x, y, position.width, height);
if (ExpressionEditorHelper.ColorProp(rect, property.FindPropertyRelative(nameof(MaterialColorBinding.TargetValue))))
{
changed = true;
}
}
return changed;
}
public bool Draw()
{
m_changed = false;
m_list.DoLayoutList();
if (GUILayout.Button("Clear MaterialColor"))
{
m_changed = true;
m_serializedProperty.arraySize = 0;
}
return m_changed;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6f6fcbf32b1c674aa717d3a7daabf23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,95 @@
using System;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace UniVRM10
{
public class ReorderableMaterialUVBindingList
{
ReorderableList m_list;
SerializedProperty m_serializedProperty;
bool m_changed;
public ReorderableMaterialUVBindingList(SerializedObject serializedObject, string[] materialNames, int height)
{
m_serializedProperty = serializedObject.FindProperty(nameof(VRM10Expression.MaterialUVBindings));
m_list = new ReorderableList(serializedObject, m_serializedProperty);
m_list.elementHeight = height * 3;
m_list.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = m_serializedProperty.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if (DrawMaterialUVBinding(rect, element, materialNames, height))
{
m_changed = true;
}
};
m_list.onAddCallback = (m_list) =>
{
// first add one element
m_serializedProperty.arraySize++;
// then get that element
var newIndex = m_serializedProperty.arraySize - 1;
var newElement = m_serializedProperty.GetArrayElementAtIndex(newIndex);
// now reset all properties like
var scaling = newElement.FindPropertyRelative(nameof(MaterialUVBinding.Scaling));
scaling.vector2Value = Vector2.one;
};
}
///
/// Material List のElement描画
///
static bool DrawMaterialUVBinding(Rect position, SerializedProperty property,
string[] materialNames, int height)
{
bool changed = false;
if (materialNames != null)
{
// Materialを選択する
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int materialIndex;
if (ExpressionEditorHelper.StringPopup(rect, property.FindPropertyRelative(nameof(MaterialUVBinding.MaterialName)), materialNames, out materialIndex))
{
changed = true;
}
// offset
y += height;
rect = new Rect(position.x, y, position.width, height);
if (ExpressionEditorHelper.UVProp(rect, property.FindPropertyRelative(nameof(MaterialUVBinding.Offset))))
{
changed = true;
}
// scale
y += height;
rect = new Rect(position.x, y, position.width, height);
if (ExpressionEditorHelper.UVProp(rect, property.FindPropertyRelative(nameof(MaterialUVBinding.Scaling))))
{
changed = true;
}
}
return changed;
}
public bool Draw()
{
m_changed = false;
m_list.DoLayoutList();
if (GUILayout.Button("Clear MaterialUV"))
{
m_changed = true;
m_serializedProperty.arraySize = 0;
}
return m_changed;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38d1e54e633fb654c812d4718e27c71b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,118 @@
using System;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace UniVRM10
{
public class ReorderableMorphTargetBindingList
{
ReorderableList m_ValuesList;
SerializedProperty m_valuesProp;
bool m_changed;
public ReorderableMorphTargetBindingList(SerializedObject serializedObject, PreviewSceneManager previewSceneManager, int height)
{
m_valuesProp = serializedObject.FindProperty(nameof(VRM10Expression.MorphTargetBindings));
m_ValuesList = new ReorderableList(serializedObject, m_valuesProp);
m_ValuesList.elementHeight = height * 3;
m_ValuesList.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = m_valuesProp.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if (DrawMorphTargetBinding(rect, element, previewSceneManager, height))
{
m_changed = true;
}
};
}
///
/// MorphTarget List のElement描画
///
static bool DrawMorphTargetBinding(Rect position, SerializedProperty property,
PreviewSceneManager scene, int height)
{
bool changed = false;
if (scene != null)
{
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int pathIndex;
if (ExpressionEditorHelper.StringPopup(rect, property.FindPropertyRelative(nameof(MorphTargetBinding.RelativePath)), scene.SkinnedMeshRendererPathList, out pathIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
int morphTargetIndex;
if (ExpressionEditorHelper.IntPopup(rect, property.FindPropertyRelative(nameof(MorphTargetBinding.Index)), scene.GetBlendShapeNames(pathIndex), out morphTargetIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
if (ExpressionEditorHelper.FloatSlider(rect, property.FindPropertyRelative(nameof(MorphTargetBinding.Weight)), 100))
{
changed = true;
}
}
return changed;
}
public void SetValues(MorphTargetBinding[] bindings)
{
m_valuesProp.ClearArray();
m_valuesProp.arraySize = bindings.Length;
for (int i = 0; i < bindings.Length; ++i)
{
var item = m_valuesProp.GetArrayElementAtIndex(i);
var endProperty = item.GetEndProperty();
while (item.NextVisible(true))
{
if (SerializedProperty.EqualContents(item, endProperty))
{
break;
}
switch (item.name)
{
case nameof(MorphTargetBinding.RelativePath):
item.stringValue = bindings[i].RelativePath;
break;
case nameof(MorphTargetBinding.Index):
item.intValue = bindings[i].Index;
break;
case nameof(MorphTargetBinding.Weight):
item.floatValue = bindings[i].Weight;
break;
default:
throw new Exception();
}
}
}
}
public bool Draw()
{
m_changed = false;
m_ValuesList.DoLayoutList();
if (GUILayout.Button("Clear BlendShape"))
{
m_changed = true;
m_valuesProp.arraySize = 0;
}
return m_changed;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b82126a68b53be745936ee0115e3708a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,299 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// Expression カスタムエディタの実装
/// </summary>
public class SerializedExpressionEditor
{
VRM10Expression m_targetObject;
SerializedObject m_serializedObject;
#region Properties
SerializedProperty m_thumbnail;
SerializedProperty m_expressionNameProp;
SerializedProperty m_presetProp;
SerializedProperty m_isBinaryProp;
public bool IsBinary => m_isBinaryProp.boolValue;
SerializedProperty m_ignoreBlinkProp;
SerializedProperty m_ignoreLookAtProp;
SerializedProperty m_ignoreMouthProp;
#endregion
ReorderableMorphTargetBindingList m_morphTargetBindings;
ReorderableMaterialColorBindingList m_materialColorBindings;
ReorderableMaterialUVBindingList m_materialUVBindings;
#region Editor values
bool m_changed;
public struct EditorStatus
{
public int Mode;
public bool MorphTargetFoldout;
public bool AdvancedFoldout;
public static EditorStatus Default => new EditorStatus
{
MorphTargetFoldout = true,
};
}
EditorStatus m_status = EditorStatus.Default;
public EditorStatus Status => m_status;
static string[] MODES = new[]{
"MorphTarget",
"Material Color",
"Material UV"
};
PreviewMeshItem[] m_items;
#endregion
public SerializedExpressionEditor(SerializedObject serializedObject,
PreviewSceneManager previewSceneManager) : this(
serializedObject, (VRM10Expression)serializedObject.targetObject, previewSceneManager, EditorStatus.Default)
{ }
public SerializedExpressionEditor(VRM10Expression expression,
PreviewSceneManager previewSceneManager, EditorStatus status) : this(
new SerializedObject(expression), expression, previewSceneManager, status)
{ }
public SerializedExpressionEditor(SerializedObject serializedObject, VRM10Expression targetObject,
PreviewSceneManager previewSceneManager, EditorStatus status)
{
m_status = status;
this.m_serializedObject = serializedObject;
this.m_targetObject = targetObject;
m_expressionNameProp = serializedObject.FindProperty("expressionName");
m_presetProp = serializedObject.FindProperty("Preset");
m_isBinaryProp = serializedObject.FindProperty("IsBinary");
m_ignoreBlinkProp = serializedObject.FindProperty("IgnoreBlink");
m_ignoreLookAtProp = serializedObject.FindProperty("IgnoreLookAt");
m_ignoreMouthProp = serializedObject.FindProperty("IgnoreMouth");
m_morphTargetBindings = new ReorderableMorphTargetBindingList(serializedObject, previewSceneManager, 20);
m_materialColorBindings = new ReorderableMaterialColorBindingList(serializedObject, previewSceneManager?.MaterialNames, 20);
m_materialUVBindings = new ReorderableMaterialUVBindingList(serializedObject, previewSceneManager?.MaterialNames, 20);
m_items = previewSceneManager.EnumRenderItems
.Where(x => x.SkinnedMeshRenderer != null)
.ToArray();
}
public bool Draw(out VRM10Expression bakeValue)
{
m_changed = false;
m_serializedObject.Update();
// Readonly の Expression 参照
GUI.enabled = false;
EditorGUILayout.ObjectField("Current clip",
m_targetObject, typeof(VRM10Expression), false);
GUI.enabled = true;
EditorGUILayout.PropertyField(m_expressionNameProp, true);
EditorGUILayout.PropertyField(m_presetProp, true);
m_status.MorphTargetFoldout = CustomUI.Foldout(Status.MorphTargetFoldout, "MorphTarget");
if (Status.MorphTargetFoldout)
{
EditorGUI.indentLevel++;
var changed = MorphTargetBindsGUI();
if (changed)
{
string maxWeightName;
var bindings = GetBindings(out maxWeightName);
m_morphTargetBindings.SetValues(bindings);
m_changed = true;
}
EditorGUI.indentLevel--;
}
m_status.AdvancedFoldout = CustomUI.Foldout(Status.AdvancedFoldout, "Advanced");
if (Status.AdvancedFoldout)
{
EditorGUI.indentLevel++;
// v0.45 Added. Binary flag
EditorGUILayout.PropertyField(m_isBinaryProp, true);
// v1.0 Ignore State
EditorGUILayout.PropertyField(m_ignoreBlinkProp, true);
EditorGUILayout.PropertyField(m_ignoreLookAtProp, true);
EditorGUILayout.PropertyField(m_ignoreMouthProp, true);
EditorGUILayout.Space();
m_status.Mode = GUILayout.Toolbar(Status.Mode, MODES);
switch (Status.Mode)
{
case 0:
// MorphTarget
{
if (m_morphTargetBindings.Draw())
{
m_changed = true;
}
}
break;
case 1:
// Material
{
if (m_materialColorBindings.Draw())
{
m_changed = true;
}
}
break;
case 2:
// MaterialUV
{
if (m_materialUVBindings.Draw())
{
m_changed = true;
}
}
break;
}
EditorGUI.indentLevel--;
}
m_serializedObject.ApplyModifiedProperties();
bakeValue = m_targetObject;
return m_changed;
}
List<bool> m_meshFolds = new List<bool>();
bool MorphTargetBindsGUI()
{
bool changed = false;
int foldIndex = 0;
// すべてのSkinnedMeshRendererを列挙する
foreach (var renderer in m_items.Select(x => x.SkinnedMeshRenderer))
{
var mesh = renderer.sharedMesh;
if (mesh != null && mesh.blendShapeCount > 0)
{
//var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, m_target.transform);
//EditorGUILayout.LabelField(m_target.name + "/" + item.Path);
if (foldIndex >= m_meshFolds.Count)
{
m_meshFolds.Add(false);
}
m_meshFolds[foldIndex] = EditorGUILayout.Foldout(m_meshFolds[foldIndex], renderer.name);
if (m_meshFolds[foldIndex])
{
//EditorGUI.indentLevel += 1;
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var src = renderer.GetBlendShapeWeight(i);
var dst = EditorGUILayout.Slider(mesh.GetBlendShapeName(i), src, 0, 100.0f);
if (dst != src)
{
renderer.SetBlendShapeWeight(i, dst);
changed = true;
}
}
//EditorGUI.indentLevel -= 1;
}
++foldIndex;
}
}
return changed;
}
MorphTargetBinding[] GetBindings(out string _maxWeightName)
{
var maxWeight = 0.0f;
var maxWeightName = "";
// weightのついたblendShapeを集める
var values = m_items
.SelectMany(x =>
{
var mesh = x.SkinnedMeshRenderer.sharedMesh;
var relativePath = x.Path;
var list = new List<MorphTargetBinding>();
if (mesh != null)
{
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var weight = x.SkinnedMeshRenderer.GetBlendShapeWeight(i);
if (weight == 0)
{
continue;
}
var name = mesh.GetBlendShapeName(i);
if (weight > maxWeight)
{
maxWeightName = name;
maxWeight = weight;
}
list.Add(new MorphTargetBinding
{
Index = i,
RelativePath = relativePath,
Weight = weight
});
}
}
return list;
}).ToArray()
;
_maxWeightName = maxWeightName;
return values;
}
}
/// http://tips.hecomi.com/entry/2016/10/15/004144
public static class CustomUI
{
public static bool Foldout(bool display, string title)
{
var style = new GUIStyle("ShurikenModuleTitle");
style.font = new GUIStyle(EditorStyles.label).font;
style.border = new RectOffset(15, 7, 4, 4);
style.fixedHeight = 22;
style.contentOffset = new Vector2(20f, -2f);
var rect = GUILayoutUtility.GetRect(16f, 22f, style);
GUI.Box(rect, title, style);
var e = Event.current;
var toggleRect = new Rect(rect.x + 4f, rect.y + 2f, 13f, 13f);
if (e.type == EventType.Repaint)
{
EditorStyles.foldout.Draw(toggleRect, false, false, display, false);
}
if (e.type == EventType.MouseDown && rect.Contains(e.mousePosition))
{
display = !display;
e.Use();
}
return display;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b0f700f0a01ce24dbc7f7d0b684cc03
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,78 @@
using UnityEditor;
namespace UniVRM10
{
[CustomEditor(typeof(VRM10ExpressionAvatar))]
public class ExpressionAvatarEditor : ExpressionEditorBase
{
/// <summary>
/// ExpressionAvatar から 編集対象の Expression を選択する
/// </summary>
ExpressionClipSelector m_selector;
ExpressionClipSelector Selector
{
get
{
if (m_selector == null)
{
m_selector = new ExpressionClipSelector((VRM10ExpressionAvatar)target, serializedObject);
m_selector.Selected += OnSelected;
OnSelected(m_selector.GetSelected());
}
return m_selector;
}
}
/// <summary>
/// 選択中の Expression のエディタ
/// </summary>
SerializedExpressionEditor m_serializedEditor;
protected override VRM10Expression CurrentExpression()
{
return Selector.GetSelected();
}
void OnSelected(VRM10Expression clip)
{
if (PreviewSceneManager == null)
{
m_serializedEditor = null;
return;
}
if (clip != null)
{
// select clip
var status = SerializedExpressionEditor.EditorStatus.Default;
if (m_serializedEditor != null)
{
status = m_serializedEditor.Status;
}
m_serializedEditor = new SerializedExpressionEditor(clip, PreviewSceneManager, status);
}
else
{
// clear selection
m_serializedEditor = null;
PreviewSceneManager.Bake(default, 1.0f);
}
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
// selector
Selector.GUI();
// editor
if (m_serializedEditor != null)
{
Separator();
m_serializedEditor.Draw(out VRM10Expression bakeValue);
PreviewSceneManager.Bake(bakeValue, 1.0f);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 821786654431c304187c2c85ebea264f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,123 @@
using System;
using System.IO;
using MeshUtility;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
[CustomEditor(typeof(VRM10Expression))]
public class ExpressionEditor : ExpressionEditorBase
{
SerializedExpressionEditor m_serializedEditor;
VRM10Expression m_target;
protected override VRM10Expression CurrentExpression()
{
return m_target;
}
protected override GameObject GetPrefab()
{
return m_target.Prefab;
}
protected override void OnEnable()
{
m_target = (VRM10Expression)target;
base.OnEnable();
}
float m_previewSlider = 1.0f;
static Texture2D SaveResizedImage(RenderTexture rt, UnityPath path, int size)
{
var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
RenderTexture.active = rt;
tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
tex.Apply();
//TextureScale.Scale(tex, size, size);
tex = TextureScale.GetResized(tex, size, size);
byte[] bytes;
switch (path.Extension.ToLower())
{
case ".png":
bytes = tex.EncodeToPNG();
break;
case ".jpg":
bytes = tex.EncodeToJPG();
break;
default:
throw new Exception();
}
if (Application.isPlaying)
{
UnityEngine.Object.Destroy(tex);
}
else
{
UnityEngine.Object.DestroyImmediate(tex);
}
File.WriteAllBytes(path.FullPath, bytes);
path.ImportAsset();
return path.LoadAsset<Texture2D>();
}
public override void OnInspectorGUI()
{
if (PreviewSceneManager == null)
{
return;
}
serializedObject.Update();
if (m_serializedEditor == null)
{
m_serializedEditor = new SerializedExpressionEditor(serializedObject, PreviewSceneManager);
}
EditorGUILayout.BeginHorizontal();
var changed = false;
EditorGUILayout.BeginVertical();
base.OnInspectorGUI();
EditorGUILayout.LabelField("Preview Weight");
var previewSlider = EditorGUILayout.Slider(m_previewSlider, 0, 1.0f);
EditorGUILayout.EndVertical();
if (m_serializedEditor.IsBinary)
{
previewSlider = Mathf.Round(previewSlider);
}
if (previewSlider != m_previewSlider)
{
m_previewSlider = previewSlider;
changed = true;
}
EditorGUILayout.EndHorizontal();
Separator();
// EditorGUILayout.Space();
if (m_serializedEditor.Draw(out VRM10Expression bakeValue))
{
changed = true;
}
if (changed && PreviewSceneManager != null)
{
PreviewSceneManager.Bake(bakeValue, m_previewSlider);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0565a023d1e0a114691e3e6f10e59039
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 33642286ccbb52542afb65da8ab0a667
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
[CustomPropertyDrawer(typeof(RendererFirstPersonFlags))]
public class RendererFirstPersonFlagsDrawer : PropertyDrawer
{
static Rect LeftSide(Rect position, float width)
{
return new Rect(position.x, position.y, position.width-width, position.height);
}
static Rect RightSide(Rect position, float width)
{
return new Rect(position.x + (position.width-width), position.y, width, position.height);
}
public override void OnGUI(Rect position,
SerializedProperty property, GUIContent label)
{
var rendererProp = property.FindPropertyRelative("Renderer");
var flagProp = property.FindPropertyRelative("FirstPersonFlag");
const float WIDTH = 140.0f;
EditorGUI.PropertyField(LeftSide(position, WIDTH), rendererProp, new GUIContent(""), true);
EditorGUI.PropertyField(RightSide(position, WIDTH), flagProp, new GUIContent(""), true);
}
/*
public override float GetPropertyHeight(SerializedProperty property,
GUIContent label)
{
return 60.0f;
}
*/
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: eba8708502533514b9fe5211d55eb7df
timeCreated: 1520848617
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1ac8223d120f39b4a9734061b3a598aa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,59 @@
using System;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
struct CustomInspector : IDisposable
{
SerializedObject serializedObject;
int m_depth;
Func<SerializedProperty, bool> m_callback;
public CustomInspector(SerializedObject so, int depth = 0, Func<SerializedProperty, bool> callback = null)
{
m_depth = depth;
m_callback = callback;
serializedObject = so;
serializedObject.Update();
}
public void OnInspectorGUI()
{
int currentDepth = 0;
for (var iterator = serializedObject.GetIterator(); iterator.NextVisible(true);)
{
var isCollapsed = currentDepth < iterator.depth;
if (isCollapsed)
{
continue;
}
#if DEBUG
// Debug.Log($"{iterator.propertyPath}({iterator.propertyType})");
#endif
if (m_callback is null || !m_callback(iterator))
{
EditorGUI.indentLevel = iterator.depth + m_depth;
EditorGUILayout.PropertyField(iterator, false);
}
if (iterator.isExpanded)
{
currentDepth = iterator.depth + 1;
}
else
{
currentDepth = iterator.depth;
}
}
}
public void Dispose()
{
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2539e35b249361d40aad74d271dcb34e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,81 @@
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
[CustomEditor(typeof(VRMSpringBoneColliderGroup))]
public class VRMSpringBoneColliderGroupEditor : Editor
{
VRMSpringBoneColliderGroup m_target;
private void OnEnable()
{
m_target = (VRMSpringBoneColliderGroup)target;
}
private void OnSceneGUI()
{
Undo.RecordObject(m_target, "VRMSpringBoneColliderGroupEditor");
Handles.matrix = m_target.transform.localToWorldMatrix;
Gizmos.color = Color.green;
bool changed = false;
foreach (var x in m_target.Colliders)
{
var offset = Handles.PositionHandle(x.Offset, Quaternion.identity);
if (offset != x.Offset)
{
changed = true;
x.Offset = offset;
}
}
if (changed)
{
EditorUtility.SetDirty(m_target);
}
}
[MenuItem("CONTEXT/VRM10SpringBoneColliderGroup/X Mirror")]
private static void InvertOffsetX(MenuCommand command)
{
var target = command.context as VRMSpringBoneColliderGroup;
if (target == null) return;
Undo.RecordObject(target, "X Mirror");
foreach (var sphereCollider in target.Colliders)
{
var offset = sphereCollider.Offset;
offset.x *= -1f;
sphereCollider.Offset = offset;
}
}
[MenuItem("CONTEXT/VRM10SpringBoneColliderGroup/Sort Colliders by Radius")]
private static void SortByRadius(MenuCommand command)
{
var target = command.context as VRMSpringBoneColliderGroup;
if (target == null) return;
Undo.RecordObject(target, "Sort Colliders by Radius");
target.Colliders = target.Colliders.OrderBy(x => -x.Radius).ToArray();
}
[MenuItem("CONTEXT/VRM10SpringBoneColliderGroup/Sort Colliders by Offset Y")]
private static void SortByOffsetY(MenuCommand command)
{
var target = command.context as VRMSpringBoneColliderGroup;
if (target == null) return;
Undo.RecordObject(target, "Sort Colliders by Offset Y");
target.Colliders = target.Colliders.OrderBy(x => -x.Offset.y).ToArray();
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a092aa1d5a21feb4db6523a3284ca561
timeCreated: 1519735770
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,56 @@
using System.Collections.Generic;
using UnityEditor.IMGUI.Controls;
namespace UniVRM10
{
class VRMSpringBoneTreeView : TreeView
{
VRM10Controller m_root;
Dictionary<int, VRMSpringBone> m_boneMap = new Dictionary<int, VRMSpringBone>();
public bool TryGetSpringBone(int id, out VRMSpringBone bone)
{
return m_boneMap.TryGetValue(id, out bone);
}
public VRMSpringBoneTreeView(TreeViewState treeViewState, VRM10Controller root)
: base(treeViewState)
{
m_root = root;
Reload();
}
protected override bool CanMultiSelect(TreeViewItem item)
{
return false;
}
protected override TreeViewItem BuildRoot()
{
m_boneMap.Clear();
var root = new TreeViewItem { id = 0, depth = -1, displayName = m_root.name };
var allItems = new List<TreeViewItem>();
var bones = m_root.GetComponentsInChildren<VRMSpringBone>();
var id = 1;
for (int i = 0; i < bones.Length; ++i, ++id)
{
var bone = bones[i];
m_boneMap.Add(id, bone);
allItems.Add(new TreeViewItem
{
id = id,
displayName = bone.name,
});
}
// Utility method that initializes the TreeViewItem.children and -parent for all items.
SetupParentsAndChildrenFromDepths(root, allItems);
// Return root of the tree
return root;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cd352799c630a8d49813a5c4d937a847
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,111 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace UniVRM10
{
public class VRMSpringBoneWindow : EditorWindow
{
[MenuItem("VRM/UniVRM-" + UniVRM10.VRMVersion.VERSION + "/VRMSpringBoneWindow")]
public static void ShowEditorWindow()
{
var wnd = GetWindow<VRMSpringBoneWindow>();
wnd.titleContent = new GUIContent("VRMSpringBoneWindow");
}
void OnEnable()
{
minSize = new Vector2(100, 100);
maxSize = new Vector2(4000, 4000);
Selection.selectionChanged += OnSelectionChanged;
}
void OnDisable()
{
Selection.selectionChanged -= OnSelectionChanged;
}
VRM10Controller m_currentRoot;
[SerializeField]
TreeViewState m_treeViewState;
VRMSpringBoneTreeView m_treeView;
Rect m_treeRect;
Rect m_inspectorRect;
Vector2 m_inspectorScrollPos;
void OnGUI()
{
if (m_treeView is null)
{
return;
}
// bone selector
Rect fullRect = GUILayoutUtility.GetRect(0, 100000, 0, 10000);
var treeRect = new Rect(fullRect.x, fullRect.y, fullRect.width, EditorGUIUtility.singleLineHeight * 3);
var inspectorRect = new Rect(fullRect.x, treeRect.y + treeRect.height, fullRect.width, fullRect.height - treeRect.height);
if (Event.current.type == EventType.Repaint)
{
m_treeRect = treeRect;
m_inspectorRect = inspectorRect;
// Debug.Log($"{m_treeRect}, {m_inspectorRect}");
}
m_treeView.OnGUI(m_treeRect);
GUILayout.BeginArea(m_inspectorRect);
m_inspectorScrollPos = GUILayout.BeginScrollView(m_inspectorScrollPos, GUI.skin.box);
if (m_treeViewState.selectedIDs.Any())
{
// selected な SpringBone の Inspector を描画する
if (m_treeView.TryGetSpringBone(m_treeViewState.selectedIDs[0], out VRMSpringBone bone))
{
// Debug.Log(bone);
using (var inspector = new CustomInspector(new SerializedObject(bone)))
{
inspector.OnInspectorGUI();
}
}
}
GUILayout.EndScrollView();
GUILayout.EndArea();
}
void OnSelectionChanged()
{
var go = Selection.activeObject as GameObject;
if (go is null)
{
return;
}
var meta = go.GetComponentInParent<VRM10Controller>();
if (meta == m_currentRoot)
{
return;
}
m_currentRoot = meta;
if (m_currentRoot is null)
{
m_treeViewState = null;
m_treeView = null;
}
else
{
// update treeview
Debug.Log(m_currentRoot);
m_treeViewState = new TreeViewState();
m_treeView = new VRMSpringBoneTreeView(m_treeViewState, m_currentRoot);
}
Repaint();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6903dd580e905a243b9841fafe3871bb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,226 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using System.Linq;
namespace UniVRM10
{
[CustomEditor(typeof(VRM10Controller))]
public class VRM10ControllerEditor : Editor
{
VRM10Controller m_target;
SkinnedMeshRenderer[] m_renderers;
Dictionary<ExpressionKey, float> m_expressionKeyWeights = new Dictionary<ExpressionKey, float>();
public class ExpressionSlider
{
Dictionary<ExpressionKey, float> m_expressionKeys;
ExpressionKey m_key;
public ExpressionSlider(Dictionary<ExpressionKey, float> expressionKeys, ExpressionKey key)
{
m_expressionKeys = expressionKeys;
m_key = key;
}
public KeyValuePair<ExpressionKey, float> Slider()
{
var oldValue = m_expressionKeys[m_key];
var enable = GUI.enabled;
GUI.enabled = Application.isPlaying;
var newValue = EditorGUILayout.Slider(m_key.ToString(), oldValue, 0, 1.0f);
GUI.enabled = enable;
return new KeyValuePair<ExpressionKey, float>(m_key, newValue);
}
}
List<ExpressionSlider> m_sliders;
void OnEnable()
{
m_target = (VRM10Controller)target;
if (m_target.Expression.ExpressionAvatar != null && m_target.Expression.ExpressionAvatar.Clips != null)
{
m_expressionKeyWeights = m_target.Expression.ExpressionAvatar.Clips.ToDictionary(x => ExpressionKey.CreateFromClip(x), x => 0.0f);
m_sliders = m_target.Expression.ExpressionAvatar.Clips
.Where(x => x != null)
.Select(x => new ExpressionSlider(m_expressionKeyWeights, ExpressionKey.CreateFromClip(x)))
.ToList()
;
}
if (m_target.Meta)
{
m_metaEditor = Editor.CreateEditor(m_target.Meta);
}
m_controller = serializedObject.FindProperty(nameof(m_target.Controller));
m_expression = serializedObject.FindProperty(nameof(m_target.Expression));
m_lookAt = serializedObject.FindProperty(nameof(m_target.LookAt));
m_firstPerson = serializedObject.FindProperty(nameof(m_target.FirstPerson));
m_asset = serializedObject.FindProperty(nameof(m_target.ModelAsset));
}
void OnDisable()
{
if (m_metaEditor)
{
UnityEditor.Editor.DestroyImmediate(m_metaEditor);
m_metaEditor = null;
}
}
enum Tabs
{
Meta,
Controller,
Expression,
LookAt,
FirstPerson,
Assets,
}
Tabs _tab;
Editor m_metaEditor;
SerializedProperty m_controller;
SerializedProperty m_expression;
SerializedProperty m_lookAt;
SerializedProperty m_firstPerson;
SerializedProperty m_asset;
public override void OnInspectorGUI()
{
{
var backup = GUI.enabled;
GUI.enabled = true;
_tab = MeshUtility.TabBar.OnGUI(_tab);
GUI.enabled = backup;
}
serializedObject.Update();
// base.OnInspectorGUI();
switch (_tab)
{
case Tabs.Meta:
m_metaEditor?.OnInspectorGUI();
break;
case Tabs.Controller:
RecursiveProperty(m_controller);
break;
case Tabs.Expression:
ExpressionGUI();
RecursiveProperty(m_expression);
break;
case Tabs.LookAt:
RecursiveProperty(m_lookAt);
break;
case Tabs.FirstPerson:
RecursiveProperty(m_firstPerson);
break;
case Tabs.Assets:
RecursiveProperty(m_asset);
break;
}
serializedObject.ApplyModifiedProperties();
}
void ExpressionGUI()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("IgnoreStatus", EditorStyles.boldLabel);
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.Toggle("Ignore Blink", m_target.Expression.IgnoreBlink);
EditorGUILayout.Toggle("Ignore Look At", m_target.Expression.IgnoreLookAt);
EditorGUILayout.Toggle("Ignore Mouth", m_target.Expression.IgnoreMouth);
EditorGUI.EndDisabledGroup();
if (!Application.isPlaying)
{
EditorGUILayout.HelpBox("Enable when playing", MessageType.Info);
}
if (m_target.Expression.ExpressionAvatar == null)
{
return;
}
if (m_sliders != null)
{
var sliders = m_sliders.Select(x => x.Slider());
foreach (var slider in sliders)
{
m_expressionKeyWeights[slider.Key] = slider.Value;
}
m_target.Expression.SetValues(m_expressionKeyWeights.Select(x => new KeyValuePair<ExpressionKey, float>(x.Key, x.Value)));
}
}
static void RecursiveProperty(SerializedProperty property)
{
var depth = property.depth;
var iterator = property.Copy();
for (var enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false)
{
if (iterator.depth < depth)
return;
depth = iterator.depth;
using (new EditorGUI.DisabledScope("m_Script" == iterator.propertyPath))
EditorGUILayout.PropertyField(iterator, true);
}
}
void OnSceneGUI()
{
OnSceneGUIOffset();
if (!Application.isPlaying)
{
// offset
var p = m_target.LookAt.OffsetFromHead;
Handles.Label(m_target.Head.position, $"fromHead: [{p.x:0.00}, {p.y:0.00}, {p.z:0.00}]");
}
else
{
m_target.LookAt.OnSceneGUILookAt(m_target.Head);
}
}
void OnSceneGUIOffset()
{
var component = target as VRM10Controller;
if (!component.LookAt.DrawGizmo)
{
return;
}
var head = component.Head;
if (head == null)
{
return;
}
EditorGUI.BeginChangeCheck();
var worldOffset = head.localToWorldMatrix.MultiplyPoint(component.LookAt.OffsetFromHead);
worldOffset = Handles.PositionHandle(worldOffset, head.rotation);
Handles.DrawDottedLine(head.position, worldOffset, 5);
Handles.SphereHandleCap(0, head.position, Quaternion.identity, 0.01f, Event.current.type);
Handles.SphereHandleCap(0, worldOffset, Quaternion.identity, 0.01f, Event.current.type);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(component, "Changed FirstPerson");
component.LookAt.OffsetFromHead = head.worldToLocalMatrix.MultiplyPoint(worldOffset);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1aa2c8486d14a1f4cbe81ff2fb5d0438
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,61 @@
using System.IO;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// JsonSchema から vrm10 のシリアライザーを生成する。
///
/// * https://github.com/KhronosGroup/glTF
/// * https://github.com/vrm-c/vrm-specification
///
/// を UniVRM の隣に clone しておくこと。
///
/// * UniVRM
/// * glTF
/// * vrm-specification
///
/// </summary>
public static class Menu
{
[MenuItem("VRM/UniVRM-" + UniVRM10.VRMVersion.VERSION + "/Generate from JsonSchema")]
public static void Main()
{
var root = new DirectoryInfo(Path.GetFullPath(Path.Combine(Application.dataPath, "../../")));
var gltf = new FileInfo(Path.Combine(root.FullName, "glTF/specification/2.0/schema/glTF.schema.json"));
var args = new string[]
{
// VRMC_vrm
"vrm-specification/specification/VRMC_vrm-1.0_draft/schema/VRMC_vrm.schema.json",
"UniVRM/Assets/VRM10/Runtime/Format/Vrm", // dst
// VRMC_constraints
"vrm-specification/specification/VRMC_constraints-1.0_draft/schema/VRMC_constraints.schema.json",
"UniVRM/Assets/VRM10/Runtime/Format/Constraints", // dst
//
"vrm-specification/specification/VRMC_materials_mtoon-1.0_draft/schema/VRMC_materials_mtoon.schema.json",
"UniVRM/Assets/VRM10/Runtime/Format/MaterialsMToon", // dst
//
"vrm-specification/specification/VRMC_node_collider_1.0_draft/schema/VRMC_node_collider.json",
"UniVRM/Assets/VRM10/Runtime/Format/NodeCollider", // dst
//
"vrm-specification/specification/VRMC_springBone-1.0_draft/schema/VRMC_springBone.schema.json",
"UniVRM/Assets/VRM10/Runtime/Format/SpringBone", // dst
};
for (int i = 0; i < args.Length; i += 2)
{
var extensionSchemaPath = new FileInfo(Path.Combine(root.FullName, args[i]));
var parser = new UniGLTF.JsonSchema.JsonSchemaParser(gltf.Directory, extensionSchemaPath.Directory);
var extensionSchema = parser.Load(extensionSchemaPath, "");
// extensionSchema.Dump();
var dst = new DirectoryInfo(Path.Combine(root.FullName, args[i + 1]));
Debug.Log(dst);
GenerateUniGLTFSerialization.Generator.GenerateTo(extensionSchema, dst, clearFolder: true);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f8755f9d806837408cd04ebb587489c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
/// <summary>
///
/// </summary>
public static class PackageResource
{
/// <summary>
/// Local時のAssetPath
/// </summary>
public const string LocalBase = "Assets/VRM10";
/// <summary>
/// UPM参照時のAssetPath
/// </summary>
public const string PackageBase = "Packages/com.vrmc.univrm";
/// <summary>
/// Try local then try package.
/// </summary>
/// <param name="relpath"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T ResourceLocalOrUPM<T>(string relpath) where T : UnityEngine.Object
{
var path = $"{LocalBase}/{relpath}";
var asset = AssetDatabase.LoadAssetAtPath<T>(path);
if (asset is null)
{
// Debug.LogWarning($"fail to LoadAssetAtPath: {path}");
path = $"{PackageResource.PackageBase}/{relpath}";
asset = AssetDatabase.LoadAssetAtPath<T>(path);
}
// if (asset is null)
// {
// Debug.LogWarning($"fail to LoadAssetAtPath: {path}");
// }
return asset;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d0de6998a8026c4ca69e7a146b135f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d8ff621b39332124aa9ebb273ff09a89
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
{
"name": "VRM10.Editor",
"references": [
"VRM10",
"VrmLib",
"MeshUtility",
"MeshUtility.Editor",
"UniGLTF.Editor"
],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6aae90b2207f6fe4fa38ba53e3ab92ef
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,285 @@
using UnityEditor;
using UnityEngine;
using MeshUtility.M17N;
namespace UniVRM10
{
[CustomEditor(typeof(VRM10MetaObject))]
public class VRMM10etaObjectEditor : Editor
{
class ValidateProperty
{
public SerializedProperty m_prop;
public delegate (string, MessageType) Validator(SerializedProperty prop);
Validator m_validator;
public ValidateProperty(SerializedProperty prop, Validator validator)
{
m_prop = prop;
m_validator = validator;
}
public void OnGUI()
{
// var old = m_prop.stringValue;
if (m_prop.propertyType == SerializedPropertyType.Generic)
{
if (m_prop.arrayElementType != null)
{
EditorGUILayout.LabelField(m_prop.name);
var depth = m_prop.depth;
var iterator = m_prop.Copy();
for (var enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false)
{
if (iterator.depth < depth)
break;
depth = iterator.depth;
// using (new EditorGUI.DisabledScope("m_Script" == iterator.propertyPath))
EditorGUILayout.PropertyField(iterator, true);
}
}
else
{
throw new System.NotImplementedException();
}
}
else
{
EditorGUILayout.PropertyField(m_prop);
}
var (msg, msgType) = m_validator(m_prop);
if (!string.IsNullOrEmpty(msg))
{
EditorGUILayout.HelpBox(msg, msgType);
}
// return old != m_prop.stringValue;
}
}
VRM10MetaObject m_target;
SerializedProperty m_Script;
SerializedProperty m_exporterVersion;
SerializedProperty m_thumbnail;
ValidateProperty m_title;
ValidateProperty m_version;
ValidateProperty m_author;
ValidateProperty m_contact;
ValidateProperty m_reference;
SerializedProperty m_AllowedUser;
SerializedProperty m_ViolentUssage;
SerializedProperty m_SexualUssage;
SerializedProperty m_CommercialUssage;
SerializedProperty m_PoliticalOrReligiousUsage;
SerializedProperty m_OtherPermissionUrl;
SerializedProperty m_LicenseType;
SerializedProperty m_OtherLicenseUrl;
static string RequiredMessage(string name)
{
switch (MeshUtility.M17N.Getter.Lang)
{
case MeshUtility.M17N.Languages.ja:
return $"必須項目。{name} を入力してください";
case MeshUtility.M17N.Languages.en:
return $"{name} is required";
default:
throw new System.NotImplementedException();
}
}
private void OnEnable()
{
if (target == null)
{
return;
}
m_target = (VRM10MetaObject)target;
m_Script = serializedObject.FindProperty("m_Script");
m_exporterVersion = serializedObject.FindProperty(nameof(m_target.ExporterVersion));
m_thumbnail = serializedObject.FindProperty(nameof(m_target.Thumbnail));
m_title = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.Name)), prop =>
{
if (string.IsNullOrEmpty(prop.stringValue))
{
return (RequiredMessage(prop.name), MessageType.Error);
}
return ("", MessageType.None);
});
m_version = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.Version)), prop =>
{
// if (string.IsNullOrEmpty(prop.stringValue))
// {
// return (RequiredMessage(prop.name), MessageType.Error);
// }
return ("", MessageType.None);
});
m_author = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.Authors)), prop =>
{
if (prop.arraySize == 0)
{
return (RequiredMessage(prop.name), MessageType.Error);
}
return ("", MessageType.None);
});
m_contact = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.ContactInformation)), prop =>
{
return ("", MessageType.None);
});
m_reference = new ValidateProperty(serializedObject.FindProperty(nameof(m_target.Reference)), prop =>
{
return ("", MessageType.None);
});
m_AllowedUser = serializedObject.FindProperty(nameof(m_target.AllowedUser));
m_ViolentUssage = serializedObject.FindProperty(nameof(m_target.ViolentUsage));
m_SexualUssage = serializedObject.FindProperty(nameof(m_target.SexualUsage));
m_CommercialUssage = serializedObject.FindProperty(nameof(m_target.CommercialUsage));
m_PoliticalOrReligiousUsage = serializedObject.FindProperty(nameof(m_target.PoliticalOrReligiousUsage));
m_OtherPermissionUrl = serializedObject.FindProperty(nameof(m_target.OtherLicenseUrl));
// m_LicenseType = serializedObject.FindProperty(nameof(m_target.));
m_OtherLicenseUrl = serializedObject.FindProperty(nameof(m_target.OtherLicenseUrl));
}
enum MessageKeys
{
[LangMsg(Languages.ja, "アバターの人格に関する許諾範囲")]
[LangMsg(Languages.en, "Personation / Characterization Permission")]
PERSONATION,
[LangMsg(Languages.ja, "アバターに人格を与えることの許諾範囲")]
[LangMsg(Languages.en, "A person who can perform with this avatar")]
ALLOWED_USER,
[LangMsg(Languages.ja, "このアバターを用いて暴力表現を演じることの許可")]
[LangMsg(Languages.en, "Violent acts using this avatar")]
VIOLENT_USAGE,
[LangMsg(Languages.ja, "このアバターを用いて性的表現を演じることの許可")]
[LangMsg(Languages.en, "Sexuality acts using this avatar")]
SEXUAL_USAGE,
[LangMsg(Languages.ja, "商用利用の許可")]
[LangMsg(Languages.en, "For commercial use")]
COMMERCIAL_USAGE,
[LangMsg(Languages.ja, "再配布・改変に関する許諾範囲")]
[LangMsg(Languages.en, "Redistribution / Modifications License")]
REDISTRIBUTION_MODIFICATIONS,
// [LangMsg(Languages.ja, "")]
// [LangMsg(Languages.en, "")]
}
static string Msg(MessageKeys key)
{
return MeshUtility.M17N.Getter.Msg(key);
}
bool m_foldoutInfo = true;
bool m_foldoutPermission = true;
bool m_foldoutDistribution = true;
public override void OnInspectorGUI()
{
serializedObject.Update();
if (VRMVersion.IsNewer(m_exporterVersion.stringValue))
{
EditorGUILayout.HelpBox("Check UniVRM new version. https://github.com/dwango/UniVRM/releases", MessageType.Warning);
}
// texture
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.BeginVertical();
GUI.enabled = false;
EditorGUILayout.PropertyField(m_exporterVersion);
GUI.enabled = true;
EditorGUILayout.PropertyField(m_thumbnail);
EditorGUILayout.EndVertical();
m_thumbnail.objectReferenceValue = TextureField("", (Texture2D)m_thumbnail.objectReferenceValue, 100);
}
EditorGUILayout.EndHorizontal();
m_foldoutInfo = EditorGUILayout.Foldout(m_foldoutInfo, "Information");
if (m_foldoutInfo)
{
m_title.OnGUI();
m_version.OnGUI();
m_author.OnGUI();
m_contact.OnGUI();
m_reference.OnGUI();
}
// EditorGUILayout.LabelField("License ", EditorStyles.boldLabel);
m_foldoutPermission = EditorGUILayout.Foldout(m_foldoutPermission, Msg(MessageKeys.PERSONATION));
if (m_foldoutPermission)
{
var backup = EditorGUIUtility.labelWidth;
RightFixedPropField(m_AllowedUser, Msg(MessageKeys.ALLOWED_USER));
RightFixedPropField(m_ViolentUssage, Msg(MessageKeys.VIOLENT_USAGE));
RightFixedPropField(m_SexualUssage, Msg(MessageKeys.SEXUAL_USAGE));
RightFixedPropField(m_CommercialUssage, Msg(MessageKeys.COMMERCIAL_USAGE));
EditorGUILayout.PropertyField(m_OtherPermissionUrl, new GUIContent("Other License Url"));
EditorGUIUtility.labelWidth = backup;
}
m_foldoutDistribution = EditorGUILayout.Foldout(m_foldoutDistribution, Msg(MessageKeys.REDISTRIBUTION_MODIFICATIONS));
if (m_foldoutDistribution)
{
// var licenseType = m_LicenseType;
// EditorGUILayout.PropertyField(licenseType);
// if ((LicenseType)licenseType.intValue == LicenseType.Other)
// {
// EditorGUILayout.PropertyField(m_OtherLicenseUrl);
// }
}
serializedObject.ApplyModifiedProperties();
}
static (Rect, Rect) FixedRight(Rect r, int width)
{
if (width > r.width)
{
width = (int)r.width;
}
return (
new Rect(r.x, r.y, r.width - width, r.height),
new Rect(r.x + r.width - width, r.y, width, r.height)
);
}
static void RightFixedPropField(SerializedProperty prop, string label)
{
var r = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.Height(EditorGUIUtility.singleLineHeight));
var (left, right) = FixedRight(r, 64);
// Debug.Log($"{left}, {right}");
EditorGUI.LabelField(left, label);
EditorGUI.PropertyField(right, prop, new GUIContent(""), false);
}
private static Texture2D TextureField(string name, Texture2D texture, int size)
{
GUILayout.BeginHorizontal();
var style = new GUIStyle(GUI.skin.label);
style.alignment = TextAnchor.UpperCenter;
//style.fixedWidth = size;
GUILayout.Label(name, style);
var result = (Texture2D)EditorGUILayout.ObjectField(texture, typeof(Texture2D), false, GUILayout.Width(size), GUILayout.Height(size));
GUILayout.EndVertical();
return result;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 825701d5bd059444ca9fb17f5e283608
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,356 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using MeshUtility;
using UnityEditor;
using UnityEngine;
using VrmLib;
namespace UniVRM10
{
public class VRM10ExportDialog : EditorWindow
{
const string CONVERT_HUMANOID_KEY = VRMVersion.MENU + "/Export VRM-1.0";
[MenuItem(CONVERT_HUMANOID_KEY, false, 1)]
private static void ExportFromMenu()
{
var window = (VRM10ExportDialog)GetWindow(typeof(VRM10ExportDialog));
window.titleContent = new GUIContent("VRM-1.0 Exporter");
window.Show();
}
enum Tabs
{
Meta,
Mesh,
ExportSettings,
}
Tabs _tab;
MeshUtility.ExporterDialogState m_state;
VRM10MetaObject m_meta;
VRM10MetaObject Meta
{
get { return m_meta; }
set
{
if (value != null && AssetDatabase.IsSubAsset(value))
{
Debug.Log("copy VRM10MetaObject");
value.CopyTo(m_tmpMeta);
return;
}
if (m_meta == value)
{
return;
}
if (m_metaEditor != null)
{
UnityEditor.Editor.DestroyImmediate(m_metaEditor);
m_metaEditor = null;
}
m_meta = value;
}
}
VRM10MetaObject m_tmpMeta;
Editor m_metaEditor;
void OnEnable()
{
// Debug.Log("OnEnable");
Undo.willFlushUndoRecord += Repaint;
Selection.selectionChanged += Repaint;
m_tmpMeta = ScriptableObject.CreateInstance<VRM10MetaObject>();
m_state = new MeshUtility.ExporterDialogState();
m_state.ExportRootChanged += (root) =>
{
// update meta
if (root == null)
{
Meta = null;
}
else
{
var controller = root.GetComponent<VRM10Controller>();
if (controller != null)
{
Meta = controller.Meta;
}
else
{
Meta = null;
}
// default setting
// m_settings.PoseFreeze =
// MeshUtility.Validators.HumanoidValidator.HasRotationOrScale(root)
// || m_meshes.Meshes.Any(x => x.ExportBlendShapeCount > 0 && !x.HasSkinning)
// ;
}
Repaint();
};
m_state.ExportRoot = Selection.activeObject as GameObject;
}
void OnDisable()
{
m_state.Dispose();
// Debug.Log("OnDisable");
Selection.selectionChanged -= Repaint;
Undo.willFlushUndoRecord -= Repaint;
}
public delegate Vector2 BeginVerticalScrollViewFunc(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options);
static BeginVerticalScrollViewFunc s_func;
static BeginVerticalScrollViewFunc BeginVerticalScrollView
{
get
{
if (s_func == null)
{
var methods = typeof(EditorGUILayout).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Where(x => x.Name == "BeginVerticalScrollView").ToArray();
var method = methods.First(x => x.GetParameters()[1].ParameterType == typeof(bool));
s_func = (BeginVerticalScrollViewFunc)method.CreateDelegate(typeof(BeginVerticalScrollViewFunc));
}
return s_func;
}
}
private Vector2 m_ScrollPosition;
IEnumerable<MeshUtility.Validator> ValidatorFactory()
{
yield return MeshUtility.Validators.HierarchyValidator.Validate;
if (!m_state.ExportRoot)
{
yield break;
}
// MeshUtility.Validators.HumanoidValidator.EnableFreeze = false;
// yield return MeshUtility.Validators.HumanoidValidator.Validate;
// yield return VRMExporterValidator.Validate;
// yield return VRMSpringBoneValidator.Validate;
// var firstPerson = m_state.ExportRoot.GetComponent<VRMFirstPerson>();
// if (firstPerson != null)
// {
// yield return firstPerson.Validate;
// }
// var proxy = m_state.ExportRoot.GetComponent<VRMBlendShapeProxy>();
// if (proxy != null)
// {
// yield return proxy.Validate;
// }
var meta = Meta ? Meta : m_tmpMeta;
yield return meta.Validate;
}
private void OnGUI()
{
// ArgumentException: Getting control 1's position in a group with only 1 controls when doing repaint Aborting
// Validation により GUI の表示項目が変わる場合があるので、
// EventType.Layout と EventType.Repaint 間で内容が変わらないようしている。
if (Event.current.type == EventType.Layout)
{
m_state.Validate(ValidatorFactory());
}
EditorGUIUtility.labelWidth = 150;
// lang
MeshUtility.M17N.Getter.OnGuiSelectLang();
EditorGUILayout.LabelField("ExportRoot");
{
m_state.ExportRoot = (GameObject)EditorGUILayout.ObjectField(m_state.ExportRoot, typeof(GameObject), true);
}
// Render contents using Generic Inspector GUI
m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box");
GUIUtility.GetControlID(645789, FocusType.Passive);
bool modified = ScrollArea();
EditorGUILayout.EndScrollView();
// Create and Other Buttons
{
// errors
GUILayout.BeginVertical();
// GUILayout.FlexibleSpace();
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUI.enabled = m_state.Validations.All(x => x.CanExport);
if (GUILayout.Button("Export", GUILayout.MinWidth(100)))
{
OnExportClicked(m_state.ExportRoot);
Close();
GUIUtility.ExitGUI();
}
GUI.enabled = true;
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
GUILayout.Space(8);
if (modified)
{
m_state.Invalidate();
}
}
bool ScrollArea()
{
//
// Validation
//
foreach (var v in m_state.Validations)
{
v.DrawGUI();
if (v.ErrorLevel == MeshUtility.ErrorLevels.Critical)
{
// Export UI を表示しない
return false;
}
}
if (m_tmpMeta == null)
{
// disabled
return false;
}
// tabbar
_tab = MeshUtility.TabBar.OnGUI(_tab);
switch (_tab)
{
case Tabs.Meta:
if (m_metaEditor == null)
{
if (m_meta != null)
{
m_metaEditor = Editor.CreateEditor(Meta);
}
else
{
m_metaEditor = Editor.CreateEditor(m_tmpMeta);
}
}
m_metaEditor.OnInspectorGUI();
break;
case Tabs.ExportSettings:
// m_settingsInspector.OnInspectorGUI();
break;
case Tabs.Mesh:
// m_meshesInspector.OnInspectorGUI();
break;
}
return true;
}
string m_logLabel;
const string EXTENSION = ".vrm";
private static string m_lastExportDir;
void OnExportClicked(GameObject root)
{
m_logLabel = "";
string directory;
if (string.IsNullOrEmpty(m_lastExportDir))
directory = Directory.GetParent(Application.dataPath).ToString();
else
directory = m_lastExportDir;
// save dialog
var path = EditorUtility.SaveFilePanel(
"Save vrm",
directory,
root.name + EXTENSION,
EXTENSION.Substring(1));
if (string.IsNullOrEmpty(path))
{
return;
}
m_lastExportDir = Path.GetDirectoryName(path).Replace("\\", "/");
m_logLabel += $"export...\n";
try
{
var exporter = new UniVRM10.RuntimeVrmConverter();
var model = exporter.ToModelFrom10(root, Meta ? Meta : m_tmpMeta);
// if (MeshUtility.Validators.HumanoidValidator.HasRotationOrScale(root))
// {
// // 正規化
// m_logLabel += $"normalize...\n";
// var modifier = new ModelModifier(model);
// modifier.SkinningBake();
// }
// 右手系に変換
m_logLabel += $"convert to right handed coordinate...\n";
model.ConvertCoordinate(VrmLib.Coordinates.Gltf, ignoreVrm: false);
var exportedBytes = GetGlb(model);
m_logLabel += $"write to {path}...\n";
File.WriteAllBytes(path, exportedBytes);
Debug.Log("exportedBytes: " + exportedBytes.Length);
var assetPath = ToAssetPath(path);
if (!string.IsNullOrEmpty(assetPath))
{
AssetDatabase.ImportAsset(assetPath);
}
}
catch (Exception ex)
{
m_logLabel += ex.ToString();
// rethrow
throw;
}
}
static byte[] GetGlb(VrmLib.Model model)
{
// export vrm-1.0
var exporter = new UniVRM10.Vrm10Exporter();
var option = new VrmLib.ExportArgs
{
// vrm = false
};
var glbBytes10 = exporter.Export(model, option);
// ?
var glb10 = VrmLib.Glb.Parse(glbBytes10);
return glb10.ToBytes();
}
static string ToAssetPath(string path)
{
var assetPath = UnityPath.FromFullpath(path);
return assetPath.Value;
}
}
}

Some files were not shown because too many files have changed in this diff Show More