mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-12 05:24:14 -05:00
commit
a0003f2dc2
8
Assets/UniGLTF/Editor/Generator.meta
Normal file
8
Assets/UniGLTF/Editor/Generator.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e6ec48f8e3ffe6d4482a26fd34facfd6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
52
Assets/UniGLTF/Editor/Generator/DeserializerWriter.cs
Normal file
52
Assets/UniGLTF/Editor/Generator/DeserializerWriter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/Generator/DeserializerWriter.cs.meta
Normal file
11
Assets/UniGLTF/Editor/Generator/DeserializerWriter.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 60b1c040cdf4bad42a40c8467ae9923b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
194
Assets/UniGLTF/Editor/Generator/FormatWriter.cs
Normal file
194
Assets/UniGLTF/Editor/Generator/FormatWriter.cs
Normal 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("}");
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/Generator/FormatWriter.cs.meta
Normal file
11
Assets/UniGLTF/Editor/Generator/FormatWriter.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1eff612a2dd70574db807fb58a21212b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
104
Assets/UniGLTF/Editor/Generator/Generator.cs
Normal file
104
Assets/UniGLTF/Editor/Generator/Generator.cs
Normal 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/Generator/Generator.cs.meta
Normal file
11
Assets/UniGLTF/Editor/Generator/Generator.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 550a0a585e47bf44781b5c447169cc33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
52
Assets/UniGLTF/Editor/Generator/SerializerWriter.cs
Normal file
52
Assets/UniGLTF/Editor/Generator/SerializerWriter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/Generator/SerializerWriter.cs.meta
Normal file
11
Assets/UniGLTF/Editor/Generator/SerializerWriter.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8875ecbd55657e148a7483e597058ab3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/UniGLTF/Editor/JsonSchema.meta
Normal file
8
Assets/UniGLTF/Editor/JsonSchema.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 734f2ba2c2ed11540ae52c5adb9900f7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
60
Assets/UniGLTF/Editor/JsonSchema/IndexTargets.cs
Normal file
60
Assets/UniGLTF/Editor/JsonSchema/IndexTargets.cs
Normal 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"},
|
||||
};
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/JsonSchema/IndexTargets.cs.meta
Normal file
11
Assets/UniGLTF/Editor/JsonSchema/IndexTargets.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6431a3223ebec22419298a43183912e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
408
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaParser.cs
Normal file
408
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaParser.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaParser.cs.meta
Normal file
11
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaParser.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5540ebde94dc9924989c2c8066b736d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
274
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaSource.cs
Normal file
274
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaSource.cs
Normal 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 + " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaSource.cs.meta
Normal file
11
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaSource.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 901c2cd9dc849e94296e50965f438e0e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
15
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaType.cs
Normal file
15
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaType.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
namespace UniGLTF.JsonSchema
|
||||
{
|
||||
public enum JsonSchemaType
|
||||
{
|
||||
Unknown,
|
||||
Object,
|
||||
Array,
|
||||
String,
|
||||
Number,
|
||||
Integer,
|
||||
Boolean,
|
||||
Enum,
|
||||
EnumString,
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaType.cs.meta
Normal file
11
Assets/UniGLTF/Editor/JsonSchema/JsonSchemaType.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e52b7d2274918294ebac38aba0c9fb1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/UniGLTF/Editor/JsonSchema/Schemas.meta
Normal file
8
Assets/UniGLTF/Editor/JsonSchema/Schemas.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 707d9adcac44ca2409a9903f2e25f3df
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
151
Assets/UniGLTF/Editor/JsonSchema/Schemas/ArrayJsonSchema.cs
Normal file
151
Assets/UniGLTF/Editor/JsonSchema/Schemas/ArrayJsonSchema.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dd2ee0b1bf849a94bb504ba32e107418
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4aa7a0339438a054781dbdf96e97ae9e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
90
Assets/UniGLTF/Editor/JsonSchema/Schemas/EnumJsonSchema.cs
Normal file
90
Assets/UniGLTF/Editor/JsonSchema/Schemas/EnumJsonSchema.cs
Normal 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}}}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1479130f877f4d24f8ff772a56ad631e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 754fd3156f24fc648bfe7cb766cf88f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
104
Assets/UniGLTF/Editor/JsonSchema/Schemas/JsonSchemaBase.cs
Normal file
104
Assets/UniGLTF/Editor/JsonSchema/Schemas/JsonSchemaBase.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5dd0100b5b0d18f45ad1485ee493b439
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UniGLTF.JsonSchema.Schemas
|
||||
{
|
||||
public static class JsonSchemaBaseExtensions
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e345d604804282c4881ea5b2e580b237
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
165
Assets/UniGLTF/Editor/JsonSchema/Schemas/ObjectJsonSchema.cs
Normal file
165
Assets/UniGLTF/Editor/JsonSchema/Schemas/ObjectJsonSchema.cs
Normal 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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ae9dcedb49a42f9449672e00b34e8598
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
117
Assets/UniGLTF/Editor/JsonSchema/Schemas/PrimitiveJsonSchema.cs
Normal file
117
Assets/UniGLTF/Editor/JsonSchema/Schemas/PrimitiveJsonSchema.cs
Normal 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()";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e64153bf38e165643bb501c5967734d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
31
Assets/UniGLTF/Editor/JsonSchema/Schemas/StringJsonSchema.cs
Normal file
31
Assets/UniGLTF/Editor/JsonSchema/Schemas/StringJsonSchema.cs
Normal 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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b5c3f6cf80367e542b1464a316fe868b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
Assets/UniGLTF/Editor/JsonSchema/Schemas/TraverseContext.cs
Normal file
22
Assets/UniGLTF/Editor/JsonSchema/Schemas/TraverseContext.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f169b3199e32ab24c82c6968e6113f14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Assets/UniGLTF/Editor/JsonSchema/StringExtensions.cs
Normal file
10
Assets/UniGLTF/Editor/JsonSchema/StringExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Editor/JsonSchema/StringExtensions.cs.meta
Normal file
11
Assets/UniGLTF/Editor/JsonSchema/StringExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 79c912c0da0179f42889a1746c8d4741
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
"name": "MeshUtility.Editor",
|
||||
"references": [
|
||||
"MeshUtility",
|
||||
"VrmLib",
|
||||
"ShaderProperty.Runtime"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
|
|
|
|||
|
|
@ -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))];
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ using System;
|
|||
|
||||
namespace VRM
|
||||
{
|
||||
/// <summary>
|
||||
/// 0系では運用されなかった。基本的に凍結。今後も変更しない
|
||||
/// </summary>
|
||||
public class VRMSpecVersion
|
||||
{
|
||||
public const int Major = 0;
|
||||
|
|
|
|||
|
|
@ -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
8
Assets/VRM10.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4fcd733d82a0cc54f9b4c88f8ee97155
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/VRM10/Editor.meta
Normal file
8
Assets/VRM10/Editor.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7d2fdd9f95a38e641a1b3f56cf5eaf16
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/VRM10/Editor/Components.meta
Normal file
8
Assets/VRM10/Editor/Components.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fbb107c1888c945489ac2c567af3385e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/VRM10/Editor/Components/Attribute.meta
Normal file
8
Assets/VRM10/Editor/Components/Attribute.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 165ab9c04d7db604586dd5175eec153f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
26
Assets/VRM10/Editor/Components/Attribute/ReadOnlyDrawer.cs
Normal file
26
Assets/VRM10/Editor/Components/Attribute/ReadOnlyDrawer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 06a80a5d47e6b31458e2714ed692e1a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/VRM10/Editor/Components/Expression.meta
Normal file
8
Assets/VRM10/Editor/Components/Expression.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4dcc84ae9f5201a479ca8df8fc991824
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6a64fbc26ee3c274fbfd9bce32f0b5dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dfbaf4fb19a693244b0fde44542b18b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
138
Assets/VRM10/Editor/Components/Expression/ExpressionSelector.cs
Normal file
138
Assets/VRM10/Editor/Components/Expression/ExpressionSelector.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6d4d5da207577eb48ad767ff59bcf8b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
168
Assets/VRM10/Editor/Components/Expression/PreviewFaceRenderer.cs
Normal file
168
Assets/VRM10/Editor/Components/Expression/PreviewFaceRenderer.cs
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9de8d16a3d572ea4da3fc4a69ba61964
|
||||
timeCreated: 1522931965
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a5a5707cea4226d40af879c4c9778002
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e6f6fcbf32b1c674aa717d3a7daabf23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 38d1e54e633fb654c812d4718e27c71b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b82126a68b53be745936ee0115e3708a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7b0f700f0a01ce24dbc7f7d0b684cc03
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 821786654431c304187c2c85ebea264f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0565a023d1e0a114691e3e6f10e59039
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/VRM10/Editor/Components/FirstPerson.meta
Normal file
8
Assets/VRM10/Editor/Components/FirstPerson.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 33642286ccbb52542afb65da8ab0a667
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eba8708502533514b9fe5211d55eb7df
|
||||
timeCreated: 1520848617
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/VRM10/Editor/Components/SpringBone.meta
Normal file
8
Assets/VRM10/Editor/Components/SpringBone.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1ac8223d120f39b4a9734061b3a598aa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
59
Assets/VRM10/Editor/Components/SpringBone/CustomInspector.cs
Normal file
59
Assets/VRM10/Editor/Components/SpringBone/CustomInspector.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2539e35b249361d40aad74d271dcb34e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a092aa1d5a21feb4db6523a3284ca561
|
||||
timeCreated: 1519735770
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cd352799c630a8d49813a5c4d937a847
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
111
Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneWindow.cs
Normal file
111
Assets/VRM10/Editor/Components/SpringBone/VRMSpringBoneWindow.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6903dd580e905a243b9841fafe3871bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
226
Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs
Normal file
226
Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs.meta
Normal file
11
Assets/VRM10/Editor/Components/VRM10ControllerEditor.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1aa2c8486d14a1f4cbe81ff2fb5d0438
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
61
Assets/VRM10/Editor/GeneratorMenu.cs
Normal file
61
Assets/VRM10/Editor/GeneratorMenu.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/VRM10/Editor/GeneratorMenu.cs.meta
Normal file
11
Assets/VRM10/Editor/GeneratorMenu.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7f8755f9d806837408cd04ebb587489c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
44
Assets/VRM10/Editor/PackageResource.cs
Normal file
44
Assets/VRM10/Editor/PackageResource.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/VRM10/Editor/PackageResource.cs.meta
Normal file
11
Assets/VRM10/Editor/PackageResource.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6d0de6998a8026c4ca69e7a146b135f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/VRM10/Editor/ScriptedImporter.meta
Normal file
8
Assets/VRM10/Editor/ScriptedImporter.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d8ff621b39332124aa9ebb273ff09a89
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Assets/VRM10/Editor/VRM10.Editor.asmdef
Normal file
20
Assets/VRM10/Editor/VRM10.Editor.asmdef
Normal 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": []
|
||||
}
|
||||
7
Assets/VRM10/Editor/VRM10.Editor.asmdef.meta
Normal file
7
Assets/VRM10/Editor/VRM10.Editor.asmdef.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6aae90b2207f6fe4fa38ba53e3ab92ef
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
285
Assets/VRM10/Editor/VRM10MetaObjectEditor.cs
Normal file
285
Assets/VRM10/Editor/VRM10MetaObjectEditor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/VRM10/Editor/VRM10MetaObjectEditor.cs.meta
Normal file
11
Assets/VRM10/Editor/VRM10MetaObjectEditor.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 825701d5bd059444ca9fb17f5e283608
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
356
Assets/VRM10/Editor/Vrm10ExportDialog.cs
Normal file
356
Assets/VRM10/Editor/Vrm10ExportDialog.cs
Normal 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
Loading…
Reference in New Issue
Block a user