Merge branch 'work_v041'

This commit is contained in:
ousttrue 2018-08-01 14:35:49 +09:00
commit c110041088
61 changed files with 1775 additions and 980 deletions

View File

@ -80,6 +80,10 @@ namespace VRM
public static BlendShapeKey CreateFrom(BlendShapeClip clip)
{
if (clip == null)
{
return default(BlendShapeKey);
}
return new BlendShapeKey(clip.BlendShapeName, clip.Preset);
}

View File

@ -74,19 +74,48 @@ namespace VRM
}
}
foreach(var binding in kv.Value.MaterialValues)
foreach (var binding in kv.Value.MaterialValues)
{
if (!m_materialSetterMap.ContainsKey(binding))
{
Material target;
if(m_materialMap.TryGetValue(binding.MaterialName, out target))
if (m_materialMap.TryGetValue(binding.MaterialName, out target))
{
m_materialSetterMap.Add(binding, x =>
if (binding.ValueName.EndsWith("_ST_S"))
{
var valueName = binding.ValueName.Substring(0, binding.ValueName.Length - 2);
Action<float> setter = value =>
{
var propValue = binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value;
var src = target.GetVector(valueName);
src.x = propValue.x; // horizontal only
src.z = propValue.z; // horizontal only
target.SetVector(valueName, src);
};
m_materialSetterMap.Add(binding, setter);
}
else if(binding.ValueName.EndsWith("_ST_T"))
{
var valueName = binding.ValueName.Substring(0, binding.ValueName.Length - 2);
Action<float> setter = value =>
{
var propValue = binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value;
var src = target.GetVector(valueName);
src.y = propValue.y; // vertical only
src.w = propValue.w; // vertical only
target.SetVector(valueName, src);
};
m_materialSetterMap.Add(binding, setter);
}
else
{
Action<float> vec4Setter = x =>
{
//target.SetBlendShapeWeight(binding.Index, x);
var propValue = binding.BaseValue + (binding.TargetValue - binding.BaseValue) * x;
target.SetColor(binding.ValueName, propValue);
});
};
m_materialSetterMap.Add(binding, vec4Setter);
}
}
else
{
@ -111,17 +140,17 @@ namespace VRM
foreach (var kv in m_blendShapeValueMap)
{
Action<float> setter;
if(m_blendShapeSetterMap.TryGetValue(kv.Key, out setter))
if (m_blendShapeSetterMap.TryGetValue(kv.Key, out setter))
{
setter(kv.Value);
}
}
m_blendShapeValueMap.Clear();
foreach(var kv in m_materialValueMap)
foreach (var kv in m_materialValueMap)
{
Action<float> setter;
if(m_materialSetterMap.TryGetValue(kv.Key, out setter))
if (m_materialSetterMap.TryGetValue(kv.Key, out setter))
{
setter(kv.Value);
}
@ -129,6 +158,15 @@ namespace VRM
m_materialValueMap.Clear();
}
public void SetValues(IEnumerable<KeyValuePair<BlendShapeKey, float>> values)
{
foreach (var kv in values)
{
SetValue(kv.Key, kv.Value, false);
}
Apply();
}
public void SetValue(BlendShapeKey key, float value, bool replace)
{
m_valueMap[key] = value;
@ -145,7 +183,7 @@ namespace VRM
{
// 値置き換え
Action<float> setter;
if(m_blendShapeSetterMap.TryGetValue(binding, out setter))
if (m_blendShapeSetterMap.TryGetValue(binding, out setter))
{
setter(binding.Weight * value);
}
@ -154,7 +192,7 @@ namespace VRM
{
// 積算
float acc;
if(m_blendShapeValueMap.TryGetValue(binding, out acc))
if (m_blendShapeValueMap.TryGetValue(binding, out acc))
{
m_blendShapeValueMap[binding] = acc + binding.Weight * value;
}

View File

@ -107,34 +107,7 @@ namespace VRM
m_coroutine = StartCoroutine(BlinkRoutine());
}
private void Update()
{
/*
var weight = BlendShapes.GetWeightByFlags(BlendShapeFlags.Eye, m_key);
if (m_coroutine != null)
{
if (weight > 0)
{
Debug.LogFormat("[Blinker] cancel: {0}", BlendShapes.Status);
// まばたきキャンセル
StopCoroutine(m_coroutine);
m_coroutine = null;
BlendShapes.Set(m_key, 0);
}
}
else
{
if (weight == 0)
{
Debug.Log("[Blinker] start");
// 開始
m_coroutine = StartCoroutine(BlinkRoutine());
}
}
*/
}
private void OnDisable()
private void OnDestroy()
{
if (m_coroutine != null)
{

View File

@ -29,8 +29,9 @@ namespace VRM
return;
}
path = path.ToUnityRelativePath();
Debug.LogFormat("{0}", path);
//Debug.LogFormat("{0}", path);
var clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.BlendShapeName = Path.GetFileNameWithoutExtension(path);
m_target.Clips.Add(clip);
clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(m_target));
AssetDatabase.CreateAsset(clip, path);
@ -47,7 +48,10 @@ namespace VRM
m_currentClip = value;
//ClearBlendShape();
Bake(m_currentClip.Values, m_currentClip.MaterialValues, 1.0f);
if (m_currentClip != null)
{
Bake(m_currentClip.Values, m_currentClip.MaterialValues, 1.0f);
}
}
}
@ -66,6 +70,12 @@ namespace VRM
base.OnEnable();
m_target = (BlendShapeAvatar)target;
// remove missing values
foreach(var x in m_target.Clips.Select((x, i) => new { i, x }).Where(x => x.x == null).Reverse())
{
m_target.Clips.RemoveAt(x.i);
}
CurrentClip = m_target.Clips[0];
}
@ -86,9 +96,12 @@ namespace VRM
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Select BlendShapeClip", EditorStyles.boldLabel);
var preset = GUILayout.SelectionGrid(m_preset, m_target.Clips
.Where(x => x != null)
.Select(x => BlendShapeKey.CreateFrom(x).ToString()).ToArray(), 4);
var array = m_target.Clips
.Select(x => x != null
? BlendShapeKey.CreateFrom(x).ToString()
: "null"
).ToArray();
var preset = GUILayout.SelectionGrid(m_preset, array, 4);
if (preset != m_preset)
{
CurrentClip = m_target.Clips[preset];
@ -116,7 +129,9 @@ namespace VRM
CurrentClip.Preset = (BlendShapePreset)EditorGUILayout.Popup("Preset", (int)CurrentClip.Preset, Presets);
GUI.enabled = false;
CurrentClip.BlendShapeName = EditorGUILayout.TextField("BlendShapeName", CurrentClip.BlendShapeName);
GUI.enabled = true;
var key = BlendShapeKey.CreateFrom(CurrentClip);
if (m_target.Clips.Where(x => key.Match(x)).Count() > 1)

View File

@ -52,11 +52,12 @@ namespace VRM
m_ValuesList = new ReorderableList(serializedObject, m_ValuesProp);
m_ValuesList.elementHeight = BlendShapeBindingHeight;
m_ValuesList.drawElementCallback =
(rect, index, isActive, isFocused) => {
(rect, index, isActive, isFocused) =>
{
var element = m_ValuesProp.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if(DrawBlendShapeBinding(rect, element, PreviewSceneManager))
if (DrawBlendShapeBinding(rect, element, PreviewSceneManager))
{
m_changed = true;
}
@ -66,11 +67,12 @@ namespace VRM
m_MaterialValuesList = new ReorderableList(serializedObject, m_MaterialValuesProp);
m_MaterialValuesList.elementHeight = MaterialValueBindingHeight;
m_MaterialValuesList.drawElementCallback =
(rect, index, isActive, isFocused) => {
(rect, index, isActive, isFocused) =>
{
var element = m_MaterialValuesProp.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if(DrawMaterialValueBinding(rect, element, PreviewSceneManager))
if (DrawMaterialValueBinding(rect, element, PreviewSceneManager))
{
m_changed = true;
}
@ -89,8 +91,9 @@ namespace VRM
m_changed = false;
EditorGUILayout.Space();
var previewSlider= EditorGUILayout.Slider("Preview Weight", m_previewSlider, 0, 1.0f);
if (previewSlider != m_previewSlider) {
var previewSlider = EditorGUILayout.Slider("Preview Weight", m_previewSlider, 0, 1.0f);
if (previewSlider != m_previewSlider)
{
m_previewSlider = previewSlider;
m_changed = true;
}
@ -110,7 +113,7 @@ namespace VRM
serializedObject.ApplyModifiedProperties();
if (m_changed && PreviewSceneManager!=null)
if (m_changed && PreviewSceneManager != null)
{
PreviewSceneManager.Bake(m_target.Values, m_target.MaterialValues, m_previewSlider);
}
@ -180,6 +183,8 @@ namespace VRM
{
y += height;
rect = new Rect(position.x, y, position.width, height);
// プロパティ名のポップアップ
int propIndex;
if (StringPopup(rect, property.FindPropertyRelative("ValueName"), materialItem.PropNames, out propIndex))
{
@ -188,6 +193,7 @@ namespace VRM
if (propIndex >= 0)
{
// 有効なプロパティ名が選択された
var propItem = materialItem.PropMap[materialItem.PropNames[propIndex]];
{
switch (propItem.PropertyType)
@ -205,6 +211,20 @@ namespace VRM
}
}
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
{
property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
// max
y += height;
rect = new Rect(position.x, y, position.width, height);
if (OffsetProp(rect, property.FindPropertyRelative("TargetValue")))
{
changed = true;
}
}
break;
}
}
}
@ -285,5 +305,20 @@ namespace VRM
return false;
}
}
static bool OffsetProp(Rect rect, SerializedProperty prop)
{
var oldValue = prop.vector4Value;
var newValue = EditorGUI.Vector4Field(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector4Value = newValue;
return true;
}
else
{
return false;
}
}
}
}

View File

@ -45,15 +45,28 @@ namespace VRM
for (int i = 0; i < ShaderUtil.GetPropertyCount(material.shader); ++i)
{
var propType = ShaderUtil.GetPropertyType(material.shader, i);
if (propType == ShaderUtil.ShaderPropertyType.Color)
var name = ShaderUtil.GetPropertyName(material.shader, i);
switch (propType)
{
var name = ShaderUtil.GetPropertyName(material.shader, i);
item.PropMap.Add(name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetColor(name),
});
propNames.Add(name);
case ShaderUtil.ShaderPropertyType.Color:
item.PropMap.Add(name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetColor(name),
});
propNames.Add(name);
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
name += "_ST";
item.PropMap.Add(name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(name);
break;
}
}
item.PropNames = propNames.ToArray();

View File

@ -42,7 +42,7 @@ namespace VRM
GameObject.DestroyImmediate(x.gameObject);
}
Debug.Log("new prefab. instanciate");
//Debug.Log("new prefab. instanciate");
// no previous instance detected, so now let's make a fresh one
// very important: this loads the PreviewInstance prefab and temporarily instantiates it into PreviewInstance
var go = GameObject.Instantiate(prefab,
@ -95,7 +95,7 @@ namespace VRM
dst = new Material(src);
map.Add(src, dst);
Debug.LogFormat("add material {0}", src.name);
//Debug.LogFormat("add material {0}", src.name);
materialNames.Add(src.name);
m_materialMap.Add(src.name, MaterialItem.Create(dst));
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using UnityEngine;
@ -52,13 +53,32 @@ namespace VRM
{
return GetValue(new BlendShapeKey(key));
}
public void SetValue(BlendShapeKey key, float value, bool apply = true)
[Obsolete("Use SetValues")]
public void SetValue(BlendShapeKey key, float value, bool apply)
{
if (m_merger != null)
{
m_merger.SetValue(key, value, apply);
}
}
public void SetValue(BlendShapeKey key, float value)
{
if (m_merger != null)
{
m_merger.SetValue(key, value, true);
}
}
public void SetValues(IEnumerable<KeyValuePair<BlendShapeKey, float>> values)
{
if (m_merger != null)
{
m_merger.SetValues(values);
}
}
public float GetValue(BlendShapeKey key)
{
if (m_merger == null)

View File

@ -46,7 +46,7 @@ public class VRMTest
public void VRMSimpleSceneTest()
{
var go = CreateSimpelScene();
var context = new VRMImporterContext(null);
var context = new VRMImporterContext();
try
{
@ -59,9 +59,9 @@ public class VRMTest
exporter.Export();
// import
context.ParseJson<glTF_VRM>(gltf.ToJson(), new SimpleStorage());
Debug.LogFormat("{0}", context.Json);
gltfImporter.Import<glTF>(context);
context.ParseJson(gltf.ToJson(), new SimpleStorage());
//Debug.LogFormat("{0}", context.Json);
gltfImporter.Load(context);
AssertAreEqual(go.transform, context.Root.transform);
}

View File

@ -121,7 +121,7 @@ namespace VRM
return FirstPersonFlag.Auto;
}
foreach(var x in context.VRM.extensions.VRM.firstPerson.meshAnnotations)
foreach(var x in context.GLTF.extensions.VRM.firstPerson.meshAnnotations)
{
if (x.mesh == index)
{

View File

@ -0,0 +1,101 @@
using System;
using UnityEditor;
using UnityEngine;
namespace VRM
{
[CustomEditor(typeof(VRMExportObject))]
public class VRMExportObjectEditor : Editor
{
SerializedProperty m_settings;
VRMExportObject m_target;
void OnEnable()
{
m_target = target as VRMExportObject;
m_settings = serializedObject.FindProperty("Settings");
}
public override void OnInspectorGUI()
{
//
// Editor
//
serializedObject.Update();
var before = m_target.Settings.Source;
EditorGUILayout.PropertyField(m_settings, true);
serializedObject.ApplyModifiedProperties();
//
//
//
var after = m_target.Settings.Source;
if (before != after)
{
m_target.Settings.InitializeFrom(after as GameObject);
}
bool canExport = m_target.Settings.Source != null;
foreach (var msg in m_target.Settings.CanExport())
{
canExport = false;
EditorGUILayout.HelpBox(msg, MessageType.Error);
}
if (canExport)
{
if (GUILayout.Button("Export"))
{
var path = EditorUtility.SaveFilePanel(
"Save vrm",
null,//Dir,
m_target.Settings.Source.name + ".vrm",
"vrm");
if (!string.IsNullOrEmpty(path))
{
var target = m_target;
EditorApplication.delayCall += () =>
{
target.Settings.Export(path);
};
}
}
}
}
class DisposableInstance : IDisposable
{
GameObject m_go;
public GameObject GameObject
{
get
{
return m_go;
}
}
public DisposableInstance(GameObject prefab)
{
m_go = GameObject.Instantiate(prefab);
}
public void Dispose()
{
if (m_go != null)
{
if (Application.isPlaying)
{
GameObject.Destroy(m_go);
}
else
{
GameObject.DestroyImmediate(m_go);
}
}
}
}
}
}

View File

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

View File

@ -1,10 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
using UniGLTF;
using System.IO;
using System.Text;
namespace VRM
@ -13,112 +9,37 @@ namespace VRM
{
const string EXTENSION = ".vrm";
public Animator Target;
VRMMeta m_meta;
public string Title;
public string Author;
public bool ForceTPose;
public bool PoseFreeze;
public VRMExportSettings m_settings = new VRMExportSettings();
public static void CreateWizard()
{
var wiz = ScriptableWizard.DisplayWizard<VRMExporterWizard>(
"VRM Exporter", "Export");
var go= Selection.activeObject as GameObject;
if (go != null)
{
wiz.Target = go.GetComponent<Animator>();
}
var go = Selection.activeObject as GameObject;
// update checkbox
var desc = wiz.Target.GetComponent<VRMHumanoidDescription>();
if (desc == null)
{
wiz.ForceTPose = true;
wiz.PoseFreeze = true;
}
var meta = wiz.Target.GetComponent<VRMMeta>();
if (meta != null && meta.Meta != null)
{
wiz.Title = meta.Meta.Title;
wiz.Author = meta.Meta.Author;
}
else
{
wiz.Title = wiz.Target.name;
}
wiz.m_settings.InitializeFrom(go);
wiz.OnWizardUpdate();
}
string m_dir;
string Dir
{
get
{
if (m_dir == null)
{
m_dir = Path.GetFullPath(Application.dataPath); ;
}
return m_dir;
}
set
{
m_dir = value;
}
}
void OnWizardCreate()
{
// save dialog
Debug.LogFormat("OnWizardCreate: {0}", Dir);
var path = EditorUtility.SaveFilePanel(
"Save vrm",
Dir,
Target.name + EXTENSION,
null,
m_settings.Source.name + EXTENSION,
EXTENSION.Substring(1));
if (string.IsNullOrEmpty(path))
{
return;
}
Dir = Path.GetDirectoryName(path);
// export
var target = Target.gameObject;
if (PoseFreeze)
{
Undo.RecordObjects(Target.transform.Traverse().ToArray(), "before normalize");
var map = new Dictionary<Transform, Transform>();
target = VRM.BoneNormalizer.Execute(Target.gameObject, map, ForceTPose);
VRMHumanoidNorimalizerMenu.CopyVRMComponents(Target.gameObject, target, map);
Undo.PerformUndo();
}
var sw = System.Diagnostics.Stopwatch.StartNew();
VRMExporter.Export(target, path, gltf => {
gltf.extensions.VRM.meta.title = Title;
gltf.extensions.VRM.meta.author = Author;
});
Debug.LogFormat("Export elapsed {0}", sw.Elapsed);
if (Target.gameObject!=target)
{
GameObject.DestroyImmediate(target);
}
if (path.StartsWithUnityAssetPath())
{
AssetDatabase.ImportAsset(path.ToUnityRelativePath());
}
m_settings.Export(path);
}
void OnWizardUpdate()
@ -127,38 +48,10 @@ namespace VRM
var helpBuilder = new StringBuilder();
var errorBuilder = new StringBuilder();
if (Target == null)
foreach(var msg in m_settings.CanExport())
{
isValid = false;
errorBuilder.Append("Require animator. ");
}
else if (Target.avatar == null)
{
isValid = false;
errorBuilder.Append("Require animator.avatar. ");
}
else if (!Target.avatar.isValid)
{
isValid = false;
errorBuilder.Append("Animator.avatar is not valid. ");
}
else if (!Target.avatar.isHuman)
{
isValid = false;
errorBuilder.Append("Animator.avatar is not humanoid. Please change model's AnimationType to humanoid. ");
}
if (string.IsNullOrEmpty(Title))
{
isValid = false;
errorBuilder.Append("Require Title. ");
}
if (string.IsNullOrEmpty(Author))
{
isValid = false;
errorBuilder.Append("Require Author. ");
errorBuilder.Append(msg);
}
helpString = helpBuilder.ToString();
@ -168,7 +61,7 @@ namespace VRM
public static class VRMExporterMenu
{
const string CONVERT_HUMANOID_KEY = "VRM/export humanoid";
const string CONVERT_HUMANOID_KEY = VRMVersion.VRM_VERSION + "/export humanoid";
[MenuItem(CONVERT_HUMANOID_KEY, true, 1)]
private static bool ExportValidate()

View File

@ -9,7 +9,7 @@ namespace VRM
{
public static class VRMHumanoidNorimalizerMenu
{
const string MENU_KEY = "VRM/freeze T-Pose";
const string MENU_KEY = VRMVersion.VRM_VERSION+"/freeze T-Pose";
[MenuItem(MENU_KEY, true, 1)]
private static bool ExportValidate()
{
@ -47,125 +47,12 @@ namespace VRM
private static void ExportFromMenu()
{
var go = Selection.activeObject as GameObject;
Undo.RecordObjects(go.transform.Traverse().ToArray(), "before normalize");
var map = new Dictionary<Transform, Transform>();
var normalized = VRM.BoneNormalizer.Execute(go, map, true);
CopyVRMComponents(go, normalized, map);
Undo.PerformUndo();
}
//
// トップレベルのMonoBehaviourを移植する
//
public static void CopyVRMComponents(GameObject go, GameObject root,
Dictionary<Transform, Transform> map)
{
{
// blendshape
var src = go.GetComponent<VRMBlendShapeProxy>();
if (src != null)
{
var dst = root.AddComponent<VRMBlendShapeProxy>();
dst.BlendShapeAvatar = src.BlendShapeAvatar;
}
}
var normalized = VRM.BoneNormalizer.Execute(go, true);
VRMExportSettings.CopyVRMComponents(go, normalized.Root, normalized.BoneMap);
Selection.activeGameObject = normalized.Root;
{
var secondary = go.transform.Find("secondary");
if (secondary == null)
{
secondary = go.transform;
}
var dstSecondary = root.transform.Find("secondary");
if (dstSecondary == null)
{
dstSecondary = new GameObject("secondary").transform;
dstSecondary.SetParent(root.transform, false);
}
// 揺れモノ
foreach (var src in go.transform.Traverse().Select(x => x.GetComponent<VRMSpringBoneColliderGroup>()).Where(x => x != null))
{
var dst = map[src.transform];
var dstColliderGroup = dst.gameObject.AddComponent<VRMSpringBoneColliderGroup>();
dstColliderGroup.Colliders = src.Colliders.Select(y =>
{
var offset = dst.worldToLocalMatrix.MultiplyPoint(src.transform.localToWorldMatrix.MultiplyPoint(y.Offset));
return new VRMSpringBoneColliderGroup.SphereCollider
{
Offset = offset,
Radius = y.Radius
};
}).ToArray();
}
foreach (var src in go.transform.Traverse().SelectMany(x => x.GetComponents<VRMSpringBone>()))
{
var dst = dstSecondary.gameObject.AddComponent<VRMSpringBone>();
dst.m_comment = src.m_comment;
dst.RootBones = src.RootBones.Select(x => map[x]).ToList();
if (src.ColliderGroups != null)
{
dst.ColliderGroups = src.ColliderGroups.Select(x => map[x.transform].GetComponent<VRMSpringBoneColliderGroup>()).ToArray();
}
}
}
{
// meta(obsolete)
var src = go.GetComponent<VRMMetaInformation>();
if (src != null)
{
src.CopyTo(root);
}
}
{
// meta
var src = go.GetComponent<VRMMeta>();
if (src != null)
{
var dst = root.AddComponent<VRMMeta>();
dst.Meta = src.Meta;
}
}
{
// firstPerson
var src = go.GetComponent<VRMFirstPerson>();
if (src != null)
{
src.CopyTo(root, map);
}
}
{
// lookAt
var src = go.GetComponent<VRMLookAt>();
if (src != null)
{
src.CopyTo(root, map);
}
}
{
// humanoid
var dst = root.AddComponent<VRMHumanoidDescription>();
var src = go.GetComponent<VRMHumanoidDescription>();
if (src != null)
{
dst.Avatar = src.Avatar;
dst.Description = src.Description;
}
else
{
var animator = go.GetComponent<Animator>();
if (animator != null)
{
dst.Avatar = animator.avatar;
}
}
}
Undo.RegisterCreatedObjectUndo(normalized.Root, "normalize");
}
}
}

View File

@ -1,6 +1,9 @@
using System.IO;
using System.Text;
using UniGLTF;
using UniJSON;
using UnityEditor;
using UnityEngine;
namespace VRM
{
@ -16,17 +19,14 @@ namespace VRM
public const int MINOR = {1};
public const string VERSION = ""{0}.{1}"";
public const string DecrementMenuName = ""VRM/Version({0}.{1}) Decrement"";
public const string IncrementMenuName = ""VRM/Version({0}.{1}) Increment"";
}}
}}
";
#if VRM_DEVELOP
[MenuItem(VRMVersion.IncrementMenuName)]
[MenuItem(VRMVersion.VRM_VERSION + "/Increment")]
#endif
public static void IncrementVersion()
static void IncrementVersion()
{
var source = string.Format(template, VRMVersion.MAJOR, VRMVersion.MINOR + 1);
File.WriteAllText(path, source);
@ -34,13 +34,33 @@ namespace VRM
}
#if VRM_DEVELOP
[MenuItem(VRMVersion.DecrementMenuName)]
[MenuItem(VRMVersion.VRM_VERSION + "/Decrement")]
#endif
public static void DecrementVersion()
static void DecrementVersion()
{
var source = string.Format(template, VRMVersion.MAJOR, VRMVersion.MINOR - 1);
File.WriteAllText(path, source);
AssetDatabase.Refresh();
}
#if VRM_DEVELOP
[MenuItem(VRMVersion.VRM_VERSION + "/Export JsonSchema")]
#endif
static void ExportJsonSchema()
{
var dir = UnityPath.FromFullpath(Application.dataPath + "/VRM/specification/0.0/schema");
dir.EnsureFolder();
var path = dir.Child("vrm.schema.json");
Debug.LogFormat("write SsonSchema: {0}", path.FullPath);
var schema = JsonSchema.FromType<glTF_VRM_extensions>();
var f = new JsonFormatter();
schema.ToJson(f);
var json = f.ToString();
File.WriteAllText(path.FullPath, json, Encoding.UTF8);
Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path.Value);
}
}
}

View File

@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Linq;
using UniGLTF;
using UnityEditor;
@ -16,49 +17,31 @@ namespace VRM
var ext = Path.GetExtension(path).ToLower();
if (ext == ".vrm")
{
var context = new VRMImporterContext(path);
context.ParseVrm(File.ReadAllBytes(context.Path));
//
// https://answers.unity.com/questions/647615/how-to-update-import-settings-for-newly-created-as.html
//
for (int i = 0; i < context.GLTF.textures.Count; ++i)
{
var x = context.GLTF.textures[i];
var image = context.GLTF.images[x.source];
if (string.IsNullOrEmpty(image.uri))
{
// glb buffer
var folder = context.GetAssetFolder(".Textures").AssetPathToFullPath();
if (!Directory.Exists(folder))
{
UnityEditor.AssetDatabase.CreateFolder(context.GLTF.baseDir, Path.GetFileNameWithoutExtension(context.Path) + ".Textures");
//Directory.CreateDirectory(folder);
}
var textureName = !string.IsNullOrEmpty(image.name) ? image.name: string.Format("buffer#{0:00}", i);
var png = Path.Combine(folder, textureName + ".png");
var byteSegment = context.GLTF.GetViewBytes(image.bufferView);
File.WriteAllBytes(png, byteSegment.ToArray());
var assetPath = png.ToUnityRelativePath();
//Debug.LogFormat("import asset {0}", assetPath);
UnityEditor.AssetDatabase.ImportAsset(assetPath, UnityEditor.ImportAssetOptions.ForceUpdate);
UnityEditor.AssetDatabase.Refresh();
image.uri = assetPath.Substring(context.GLTF.baseDir.Length + 1);
}
}
EditorApplication.delayCall += () =>
{
// delay and can import png texture
VRMImporter.LoadFromBytes(context);
context.SaveAsAsset();
context.Destroy(false);
};
ImportVrm(UnityPath.FromUnityPath(path));
}
}
}
static void ImportVrm(UnityPath path)
{
if (!path.IsUnderAssetsFolder)
{
throw new Exception();
}
var context = new VRMImporterContext(path);
context.ParseGlb(File.ReadAllBytes(path.FullPath));
var prefabPath = path.Parent.Child(path.FileNameWithoutExtension + ".prefab");
context.SaveTexturesAsPng(prefabPath);
EditorApplication.delayCall += () =>
{
// delay and can import png texture
VRMImporter.LoadFromBytes(context);
context.SaveAsAsset(prefabPath);
context.Destroy(false);
};
}
}
#endif
}

View File

@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UniGLTF;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace VRM
{
[Serializable]
public class VRMExportSettings
{
public GameObject Source;
public string Title;
public string Author;
public bool ForceTPose = true;
public bool PoseFreeze = true;
public IEnumerable<string> CanExport()
{
if (Source == null)
{
yield return "Require source";
yield break;
}
var animator = Source.GetComponent<Animator>();
if (animator == null)
{
yield return "Require animator. ";
}
else if (animator.avatar == null)
{
yield return "Require animator.avatar. ";
}
else if (!animator.avatar.isValid)
{
yield return "Animator.avatar is not valid. ";
}
else if (!animator.avatar.isHuman)
{
yield return "Animator.avatar is not humanoid. Please change model's AnimationType to humanoid. ";
}
if (string.IsNullOrEmpty(Title))
{
yield return "Require Title. ";
}
if (string.IsNullOrEmpty(Author))
{
yield return "Require Author. ";
}
}
public void InitializeFrom(GameObject go)
{
if (Source == go) return;
Source = go;
var desc = Source == null ? null : go.GetComponent<VRMHumanoidDescription>();
if (desc == null)
{
ForceTPose = true;
PoseFreeze = true;
}
else
{
ForceTPose = false;
PoseFreeze = false;
}
var meta = Source == null ? null : go.GetComponent<VRMMeta>();
if (meta != null && meta.Meta != null)
{
Title = meta.Meta.Title;
Author = meta.Meta.Author;
}
else
{
Title = go.name;
//Author = "";
}
}
//
// トップレベルのMonoBehaviourを移植する
//
public static void CopyVRMComponents(GameObject go, GameObject root,
Dictionary<Transform, Transform> map)
{
{
// blendshape
var src = go.GetComponent<VRMBlendShapeProxy>();
if (src != null)
{
var dst = root.AddComponent<VRMBlendShapeProxy>();
dst.BlendShapeAvatar = src.BlendShapeAvatar;
}
}
{
var secondary = go.transform.Find("secondary");
if (secondary == null)
{
secondary = go.transform;
}
var dstSecondary = root.transform.Find("secondary");
if (dstSecondary == null)
{
dstSecondary = new GameObject("secondary").transform;
dstSecondary.SetParent(root.transform, false);
}
// 揺れモノ
foreach (var src in go.transform.Traverse().Select(x => x.GetComponent<VRMSpringBoneColliderGroup>()).Where(x => x != null))
{
var dst = map[src.transform];
var dstColliderGroup = dst.gameObject.AddComponent<VRMSpringBoneColliderGroup>();
dstColliderGroup.Colliders = src.Colliders.Select(y =>
{
var offset = dst.worldToLocalMatrix.MultiplyPoint(src.transform.localToWorldMatrix.MultiplyPoint(y.Offset));
return new VRMSpringBoneColliderGroup.SphereCollider
{
Offset = offset,
Radius = y.Radius
};
}).ToArray();
}
foreach (var src in go.transform.Traverse().SelectMany(x => x.GetComponents<VRMSpringBone>()))
{
var dst = dstSecondary.gameObject.AddComponent<VRMSpringBone>();
dst.m_comment = src.m_comment;
dst.RootBones = src.RootBones.Select(x => map[x]).ToList();
if (src.ColliderGroups != null)
{
dst.ColliderGroups = src.ColliderGroups.Select(x => map[x.transform].GetComponent<VRMSpringBoneColliderGroup>()).ToArray();
}
}
}
{
// meta(obsolete)
var src = go.GetComponent<VRMMetaInformation>();
if (src != null)
{
src.CopyTo(root);
}
}
{
// meta
var src = go.GetComponent<VRMMeta>();
if (src != null)
{
var dst = root.AddComponent<VRMMeta>();
dst.Meta = src.Meta;
}
}
{
// firstPerson
var src = go.GetComponent<VRMFirstPerson>();
if (src != null)
{
src.CopyTo(root, map);
}
}
{
// lookAt
var src = go.GetComponent<VRMLookAt>();
if (src != null)
{
src.CopyTo(root, map);
}
}
{
// humanoid
var dst = root.AddComponent<VRMHumanoidDescription>();
var src = go.GetComponent<VRMHumanoidDescription>();
if (src != null)
{
dst.Avatar = src.Avatar;
dst.Description = src.Description;
}
else
{
var animator = go.GetComponent<Animator>();
if (animator != null)
{
dst.Avatar = animator.avatar;
}
}
}
}
public static bool IsPrefab(GameObject go)
{
return go.scene.name == null;
}
#if UNITY_EDITOR
struct RecordDisposer : IDisposable
{
public RecordDisposer(UnityEngine.Object[] objects, string msg)
{
Undo.RecordObjects(objects, msg);
}
public void Dispose()
{
Undo.PerformUndo();
}
}
public void Export(string path)
{
List<GameObject> destroy = new List<GameObject>();
try
{
Export(path, destroy);
}
finally
{
foreach (var x in destroy)
{
Debug.LogFormat("destroy: {0}", x.name);
GameObject.DestroyImmediate(x);
}
}
}
void Export(string path, List<GameObject> destroy)
{
var target = Source;
if (IsPrefab(target))
{
using (new RecordDisposer(Source.transform.Traverse().ToArray(), "before normalize"))
{
target = GameObject.Instantiate(target);
destroy.Add(target);
}
}
if (PoseFreeze)
{
using (new RecordDisposer(target.transform.Traverse().ToArray(), "before normalize"))
{
var normalized = BoneNormalizer.Execute(target, ForceTPose);
CopyVRMComponents(target, normalized.Root, normalized.BoneMap);
target = normalized.Root;
destroy.Add(target);
}
}
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var vrm = VRMExporter.Export(target);
vrm.extensions.VRM.meta.title = Title;
vrm.extensions.VRM.meta.author = Author;
var bytes = vrm.ToGlbBytes();
File.WriteAllBytes(path, bytes);
Debug.LogFormat("Export elapsed {0}", sw.Elapsed);
}
if (path.StartsWithUnityAssetPath())
{
AssetDatabase.ImportAsset(path.ToUnityRelativePath());
}
}
#endif
}
}

View File

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

View File

@ -0,0 +1,12 @@
using UnityEngine;
namespace VRM
{
[CreateAssetMenu(menuName = "VRM/ExportObject")]
public class VRMExportObject : ScriptableObject
{
[SerializeField]
public VRMExportSettings Settings = new VRMExportSettings();
}
}

View File

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

View File

@ -9,13 +9,12 @@ namespace VRM
{
public class VRMExporter : gltfExporter
{
public VRMExporter(glTF_VRM gltf) : base(gltf)
public VRMExporter(glTF gltf) : base(gltf)
{ }
public static glTF Export(GameObject go, string path = null, Action<glTF_VRM> callback=null)
public new static glTF Export(GameObject go)
{
var gltf = new glTF_VRM();
gltf.asset.generator = string.Format("UniVRM-{0}.{1}", VRMVersion.MAJOR, VRMVersion.MINOR);
var gltf = new glTF();
using (var exporter = new VRMExporter(gltf)
{
#if VRM_EXPORTER_USE_SPARSE
@ -25,22 +24,11 @@ namespace VRM
})
{
_Export(gltf, exporter, go);
if (callback != null)
{
callback(gltf);
}
if (!string.IsNullOrEmpty(path))
{
exporter.WriteTo(path);
}
}
return gltf;
}
public static void _Export(glTF_VRM gltf, VRMExporter exporter, GameObject go)
public static void _Export(glTF gltf, VRMExporter exporter, GameObject go)
{
exporter.Prepare(go);
exporter.Export();

View File

@ -3,6 +3,33 @@ using System.Collections.Generic;
using UniGLTF;
namespace UniGLTF
{
public partial class glTFUsedExtensions
{
[UsedExtension]
static string VRMGetUsedExtension
{
get
{
return "VRM";
}
}
}
public partial class glTF_extensions : ExtensionsBase<glTF_extensions>
{
public VRM.glTF_VRM_extensions VRM = new VRM.glTF_VRM_extensions();
[JsonSerializeMembers]
void VRMSerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => VRM);
}
}
}
namespace VRM
{
[Serializable]
@ -17,7 +44,7 @@ namespace VRM
public glTF_VRM_SecondaryAnimation secondaryAnimation = new glTF_VRM_SecondaryAnimation();
public List<glTF_VRM_Material> materialProperties = new List<glTF_VRM_Material>();
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => exporterVersion);
f.KeyValue(() => meta);
@ -28,32 +55,4 @@ namespace VRM
f.KeyValue(() => materialProperties);
}
}
[Serializable]
public class glTF_extensions : JsonSerializableBase
{
public glTF_VRM_extensions VRM = new glTF_VRM_extensions();
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => VRM);
}
}
[Serializable]
public class glTF_VRM : glTF
{
public glTF_extensions extensions = new glTF_extensions();
public List<string> extensionsUsed = new List<string>
{
"VRM",
};
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => extensionsUsed);
f.KeyValue(() => extensions);
base.SerializeMembers(f);
}
}
}

View File

@ -20,8 +20,8 @@ namespace VRM
public static GameObject LoadFromPath(string path)
{
var context = new VRMImporterContext(path);
context.ParseVrm(File.ReadAllBytes(path));
var context = new VRMImporterContext(UniGLTF.UnityPath.FromFullpath(path));
context.ParseGlb(File.ReadAllBytes(path));
LoadFromBytes(context);
return context.Root;
}
@ -29,37 +29,16 @@ namespace VRM
public static GameObject LoadFromBytes(Byte[] bytes)
{
var context = new VRMImporterContext();
context.ParseVrm(bytes);
context.ParseGlb(bytes);
LoadFromBytes(context);
return context.Root;
}
public static void LoadFromPath(VRMImporterContext context)
{
context.ParseVrm(File.ReadAllBytes(context.Path));
LoadFromBytes(context);
}
public static void LoadFromBytes(VRMImporterContext context)
{
context.CreateMaterial = VRMImporter.GetMaterialFunc(glTF_VRM_Material.Parse(context.Json));
gltfImporter.Import<glTF_VRM>(context);
if (string.IsNullOrEmpty(context.Path))
{
if (string.IsNullOrEmpty(context.VRM.extensions.VRM.meta.title))
{
context.Root.name = "VRM_LOADED";
}
else
{
context.Root.name = context.VRM.extensions.VRM.meta.title;
}
}
else
{
context.Root.name = Path.GetFileNameWithoutExtension(context.Path);
}
gltfImporter.Load(context);
OnLoadModel(context);
@ -82,7 +61,7 @@ namespace VRM
var CreateZWrite = gltfImporter.CreateMaterialFuncFromShader(new ShaderStore("VRM/UnlitTransparentZWrite"));
CreateMaterialFunc fallback = (ctx, i) =>
{
var vrm = ctx.GLTF as glTF_VRM;
var vrm = ctx.GLTF;
if (vrm != null && vrm.materials[i].name.ToLower().Contains("zwrite"))
{
// 一応。不要かも
@ -184,7 +163,7 @@ namespace VRM
LoadBlendShapeMaster(context);
VRMSpringUtility.LoadSecondary(context.Root.transform, context.Nodes,
context.VRM.extensions.VRM.secondaryAnimation);
context.GLTF.extensions.VRM.secondaryAnimation);
LoadFirstPerson(context);
return Unit.Default;
@ -213,7 +192,7 @@ namespace VRM
{
var firstPerson = context.Root.AddComponent<VRMFirstPerson>();
var gltfFirstPerson = context.VRM.extensions.VRM.firstPerson;
var gltfFirstPerson = context.GLTF.extensions.VRM.firstPerson;
if (gltfFirstPerson.firstPersonBone != -1)
{
firstPerson.FirstPersonBone = context.Nodes[gltfFirstPerson.firstPersonBone];
@ -236,7 +215,7 @@ namespace VRM
context.BlendShapeAvatar = ScriptableObject.CreateInstance<BlendShapeAvatar>();
context.BlendShapeAvatar.name = "BlendShape";
var blendShapeList = context.VRM.extensions.VRM.blendShapeMaster.blendShapeGroups;
var blendShapeList = context.GLTF.extensions.VRM.blendShapeMaster.blendShapeGroups;
if (blendShapeList != null && blendShapeList.Count > 0)
{
foreach (var x in blendShapeList)
@ -337,14 +316,14 @@ namespace VRM
[Obsolete]
private static void LoadHumanoidObsolete(VRMImporterContext context)
{
var parsed = context.Json.ParseAsJson()["extensions"]["VRM"];
var parsed = UniJSON.JsonParser.Parse(context.Json)["extensions"]["VRM"];
var skeleton = context.Root.transform.Traverse().Select(x => ToSkeletonBone(x)).ToArray();
var description = new HumanDescription
{
human = parsed[HUMANOID_KEY]["bones"]
.ObjectItems
.Select(x => new { x.Key, Index = x.Value.GetInt32() })
.Select(x => new { x.Key, Index = x.Value.Value.GetInt32() })
.Where(x => x.Index != -1)
.Select(x =>
{
@ -368,7 +347,7 @@ namespace VRM
};
context.HumanoidAvatar = AvatarBuilder.BuildHumanAvatar(context.Root, description);
context.HumanoidAvatar.name = Path.GetFileNameWithoutExtension(context.Path);
context.HumanoidAvatar.name = "VrmAvatar";
context.AvatarDescription = UniHumanoid.AvatarDescription.CreateFrom(description);
context.AvatarDescription.name = "AvatarDescription";
@ -386,10 +365,10 @@ namespace VRM
private static void LoadHumanoid(VRMImporterContext context)
{
context.AvatarDescription = context.VRM.extensions.VRM.humanoid.ToDescription(context.Nodes);
context.AvatarDescription = context.GLTF.extensions.VRM.humanoid.ToDescription(context.Nodes);
context.AvatarDescription.name = "AvatarDescription";
context.HumanoidAvatar = context.AvatarDescription.CreateAvatar(context.Root.transform);
context.HumanoidAvatar.name = Path.GetFileNameWithoutExtension(context.Path);
context.HumanoidAvatar.name = "VrmAvatar";
var humanoid = context.Root.AddComponent<VRMHumanoidDescription>();
humanoid.Avatar = context.HumanoidAvatar;
@ -487,63 +466,58 @@ namespace VRM
#if (NET_4_6 && UNITY_2017_1_OR_NEWER)
public static Task<GameObject> LoadVrmAsync(string path)
public static Task<GameObject> LoadVrmAsync(string path, bool show=true)
{
var context = new VRMImporterContext(path);
context.ParseVrm(File.ReadAllBytes(path));
return LoadVrmAsyncInternal(context).ToTask();
return LoadVrmAsyncInternal(context, show).ToTask();
}
public static Task<GameObject> LoadVrmAsync(Byte[] bytes)
public static Task<GameObject> LoadVrmAsync(Byte[] bytes, bool show=true)
{
var context = new VRMImporterContext();
context.ParseVrm(bytes);
return LoadVrmAsync(context);
return LoadVrmAsync(context, show);
}
public static Task<GameObject> LoadVrmAsync(VRMImporterContext ctx)
public static Task<GameObject> LoadVrmAsync(VRMImporterContext ctx, bool show=true)
{
return LoadVrmAsyncInternal(ctx).ToTask();
return LoadVrmAsyncInternal(ctx, show).ToTask();
}
#endif
public static void LoadVrmAsync(string path, Action<GameObject> onLoaded, Action<Exception> onError = null)
public static void LoadVrmAsync(string path, Action<GameObject> onLoaded, Action<Exception> onError = null, bool show = true)
{
var context = new VRMImporterContext(path);
context.ParseVrm(File.ReadAllBytes(path));
LoadVrmAsync(context, onLoaded, onError);
var context = new VRMImporterContext(UnityPath.FromFullpath(path));
context.ParseGlb(File.ReadAllBytes(path));
LoadVrmAsync(context, onLoaded, onError, show);
}
public static void LoadVrmAsync(Byte[] bytes, Action<GameObject> onLoaded, Action<Exception> onError = null)
public static void LoadVrmAsync(Byte[] bytes, Action<GameObject> onLoaded, Action<Exception> onError = null, bool show = true)
{
var context = new VRMImporterContext();
context.ParseVrm(bytes);
LoadVrmAsync(context, onLoaded, onError);
context.ParseGlb(bytes);
LoadVrmAsync(context, onLoaded, onError, show);
}
public static void LoadVrmAsync(VRMImporterContext ctx, Action<GameObject> onLoaded, Action<Exception> onError = null)
public static void LoadVrmAsync(VRMImporterContext ctx, Action<GameObject> onLoaded, Action<Exception> onError = null, bool show = true)
{
if (onError == null)
{
onError = Debug.LogError;
}
LoadVrmAsyncInternal(ctx)
LoadVrmAsyncInternal(ctx, show)
.Subscribe(Scheduler.MainThread, onLoaded, onError);
}
private static Schedulable<GameObject> LoadVrmAsyncInternal(VRMImporterContext ctx)
private static Schedulable<GameObject> LoadVrmAsyncInternal(VRMImporterContext ctx, bool show)
{
var schedulable = Schedulable.Create();
return schedulable
.AddTask(Scheduler.ThreadPool, () =>
{
ctx.GLTF.baseDir = Path.GetDirectoryName(ctx.Path);
return Unit.Default;
})
.ContinueWith(Scheduler.CurrentThread, _ =>
{
return glTF_VRM_Material.Parse(ctx.Json);
})
@ -588,18 +562,12 @@ namespace VRM
.ContinueWith(Scheduler.CurrentThread,
_ =>
{
/*
Debug.LogFormat("task end: {0}/{1}/{2}/{3}",
ctx.Textures.Count,
ctx.Materials.Count,
ctx.Meshes.Count,
ctx.Nodes.Count
);
*/
ctx.Root.name = Path.GetFileNameWithoutExtension(ctx.Path);
ctx.Root.name = "VRM";
// 非表示のメッシュを表示する
ctx.ShowMeshes();
if (show)
{
ctx.ShowMeshes();
}
return ctx.Root;
});

View File

@ -7,13 +7,8 @@ namespace VRM
{
public class VRMImporterContext : ImporterContext
{
public VRMImporterContext()
public VRMImporterContext(UnityPath gltfPath = default(UnityPath)) : base(gltfPath)
{
}
public VRMImporterContext(string path)
{
Path = path;
}
public UniHumanoid.AvatarDescription AvatarDescription;
@ -21,26 +16,13 @@ namespace VRM
public BlendShapeAvatar BlendShapeAvatar;
public VRMMetaObject Meta;
public glTF_VRM VRM
public VRMMetaObject ReadMeta(bool createThumbnail = false)
{
get
{
return (glTF_VRM)GLTF;
}
}
public void ParseVrm(byte[] bytes)
{
ParseGlb<glTF_VRM>(bytes);
}
public VRMMetaObject ReadMeta(bool createThumbnail=false)
{
var meta=ScriptableObject.CreateInstance<VRMMetaObject>();
var meta = ScriptableObject.CreateInstance<VRMMetaObject>();
meta.name = "Meta";
meta.ExporterVersion = VRM.extensions.VRM.exporterVersion;
meta.ExporterVersion = GLTF.extensions.VRM.exporterVersion;
var gltfMeta = VRM.extensions.VRM.meta;
var gltfMeta = GLTF.extensions.VRM.meta;
meta.Version = gltfMeta.version; // model version
meta.Author = gltfMeta.author;
meta.ContactInformation = gltfMeta.contactInformation;
@ -55,11 +37,11 @@ namespace VRM
else if (createThumbnail)
{
// 作成する(先行ロード用)
if(gltfMeta.texture >= 0 && gltfMeta.texture < VRM.textures.Count)
if (gltfMeta.texture >= 0 && gltfMeta.texture < GLTF.textures.Count)
{
var t = new TextureItem(VRM, gltfMeta.texture);
t.Process(VRM, Storage);
meta.Thumbnail=t.Texture;
var t = new TextureItem(GLTF, gltfMeta.texture);
t.Process(GLTF, Storage);
meta.Thumbnail = t.Texture;
}
}

View File

@ -4,11 +4,8 @@ namespace VRM
public static partial class VRMVersion
{
public const int MAJOR = 0;
public const int MINOR = 40;
public const int MINOR = 41;
public const string VERSION = "0.40";
public const string DecrementMenuName = "VRM/Version(0.40) Decrement";
public const string IncrementMenuName = "VRM/Version(0.40) Increment";
public const string VERSION = "0.41";
}
}

View File

@ -47,5 +47,7 @@ namespace VRM
return false;
}
}
public const string VRM_VERSION = "UniVRM-" + VERSION;
}
}

View File

@ -14,7 +14,7 @@ namespace VRM
public string propertyName;
public float[] targetValue;
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => materialName);
f.KeyValue(() => propertyName);
@ -29,7 +29,7 @@ namespace VRM
public int index = -1;
public float weight = 0;
protected override void SerializeMembers(UniGLTF.JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => mesh);
f.KeyValue(() => index);
@ -90,7 +90,7 @@ namespace VRM
public List<glTF_VRM_BlendShapeBind> binds = new List<glTF_VRM_BlendShapeBind>();
public List<glTF_VRM_MaterialValueBind> materialValues = new List<glTF_VRM_MaterialValueBind>();
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => name);
f.KeyValue(() => presetName);
@ -133,7 +133,7 @@ namespace VRM
blendShapeGroups.Add(group);
}
protected override void SerializeMembers(UniGLTF.JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => blendShapeGroups);
}

View File

@ -15,7 +15,7 @@ namespace VRM
public float xRange = 90.0f;
public float yRange = 10.0f;
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
if (curve != null)
{
@ -48,7 +48,7 @@ namespace VRM
public string firstPersonFlag;
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => mesh);
f.KeyValue(() => firstPersonFlag);
@ -85,7 +85,7 @@ namespace VRM
public glTF_VRM_DegreeMap lookAtVerticalDown = new glTF_VRM_DegreeMap();
public glTF_VRM_DegreeMap lookAtVerticalUp = new glTF_VRM_DegreeMap();
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => firstPersonBone);
f.KeyValue(() => firstPersonBoneOffset);

View File

@ -111,7 +111,7 @@ namespace VRM
public Vector3 center;
public float axisLength;
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.Key("bone"); f.Value((string)bone.ToString());
f.KeyValue(() => node);
@ -158,7 +158,7 @@ namespace VRM
}
}
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => humanBones);
f.KeyValue(() => armStretch);

View File

@ -3,19 +3,17 @@ using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UniJSON;
namespace VRM
{
[Serializable]
public class glTF_VRM_Material : UniGLTF.JsonSerializableBase
public class glTF_VRM_Material : JsonSerializableBase
{
public string name;
public string shader;
public int renderQueue=-1;
public int renderQueue = -1;
public Dictionary<string, float> floatProperties = new Dictionary<string, float>();
public Dictionary<string, float[]> vectorProperties = new Dictionary<string, float[]>();
@ -28,7 +26,7 @@ namespace VRM
// "Queue",
};
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => name);
f.KeyValue(() => renderQueue);
@ -59,7 +57,7 @@ namespace VRM
}
{
f.Key("keywordMap"); f.BeginMap();
foreach(var kv in keywordMap)
foreach (var kv in keywordMap)
{
f.Key(kv.Key); f.Value(kv.Value);
}
@ -77,17 +75,17 @@ namespace VRM
public static List<glTF_VRM_Material> Parse(string src)
{
var json = src.ParseAsJson()["extensions"]["VRM"]["materialProperties"];
var json = UniJSON.JsonParser.Parse(src)["extensions"]["VRM"]["materialProperties"];
var materials = json.DeserializeList<glTF_VRM_Material>();
var jsonItems = json.ListItems.ToArray();
for(int i=0; i<materials.Count; ++i)
var jsonItems = json.ArrayItems.ToArray();
for (int i = 0; i < materials.Count; ++i)
{
materials[i].floatProperties =
jsonItems[i]["floatProperties"].ObjectItems.ToDictionary(x => x.Key, x => x.Value.GetSingle());
materials[i].floatProperties =
jsonItems[i]["floatProperties"].ObjectItems.ToDictionary(x => x.Key, x => x.Value.Value.GetSingle());
materials[i].vectorProperties =
jsonItems[i]["vectorProperties"].ObjectItems.ToDictionary(x => x.Key, x =>
{
return x.Value.ListItems.Select(y => y.GetSingle()).ToArray();
return x.Value.ArrayItems.Select(y => y.Value.GetSingle()).ToArray();
});
materials[i].keywordMap =
jsonItems[i]["keywordMap"].ObjectItems.ToDictionary(x => x.Key, x => x.Value.GetBoolean());
@ -108,73 +106,87 @@ namespace VRM
renderQueue = m.renderQueue,
};
#if UNITY_EDITOR
for (int i = 0; i < ShaderUtil.GetPropertyCount(m.shader); ++i)
var prop = VRMPreShaderPropExporter.GetPropsForSupportedShader(m.shader.name);
if (prop == null)
{
var name = ShaderUtil.GetPropertyName(m.shader, i);
var propType = ShaderUtil.GetPropertyType(m.shader, i);
switch (propType)
{
case ShaderUtil.ShaderPropertyType.Color:
{
var value = m.GetColor(name).ToArray();
material.vectorProperties.Add(name, value);
}
break;
case ShaderUtil.ShaderPropertyType.Range:
case ShaderUtil.ShaderPropertyType.Float:
{
var value = m.GetFloat(name);
material.floatProperties.Add(name, value);
}
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
{
var texture = m.GetTexture(name);
if (texture != null)
{
var value = textures.IndexOf(texture);
if (value == -1)
{
Debug.LogFormat("not found {0}", texture.name);
}
else
{
material.textureProperties.Add(name, value);
}
}
// offset & scaling
var offset = m.GetTextureOffset(name);
var scaling = m.GetTextureScale(name);
material.vectorProperties.Add(name,
new float[] { offset.x, offset.y, scaling.x, scaling.y });
}
break;
case ShaderUtil.ShaderPropertyType.Vector:
{
var value = m.GetVector(name).ToArray();
material.vectorProperties.Add(name, value);
}
break;
default:
throw new NotImplementedException();
}
}
#else
Debug.LogWarning("cannot export material properties on runtime");
#if UNITY_EDITOR
// fallback
Debug.LogWarningFormat("Unsupported shader: {0}", m.shader.name);
prop = VRMPreShaderPropExporter.ShaderProps.FromShader(m.shader);
#endif
}
if (prop == null)
{
Debug.LogWarningFormat("Fail to export shader: {0}", m.shader.name);
}
else
{
// get properties
//material.SetProp(prop);
foreach (var kv in prop.Properties)
{
switch (kv.Value)
{
case VRMPreShaderPropExporter.ShaderPropertyType.Color:
{
var value = m.GetColor(kv.Key).ToArray();
material.vectorProperties.Add(kv.Key, value);
}
break;
case VRMPreShaderPropExporter.ShaderPropertyType.Range:
case VRMPreShaderPropExporter.ShaderPropertyType.Float:
{
var value = m.GetFloat(kv.Key);
material.floatProperties.Add(kv.Key, value);
}
break;
case VRMPreShaderPropExporter.ShaderPropertyType.TexEnv:
{
var texture = m.GetTexture(kv.Key);
if (texture != null)
{
var value = textures.IndexOf(texture);
if (value == -1)
{
Debug.LogFormat("not found {0}", texture.name);
}
else
{
material.textureProperties.Add(kv.Key, value);
}
}
// offset & scaling
var offset = m.GetTextureOffset(kv.Key);
var scaling = m.GetTextureScale(kv.Key);
material.vectorProperties.Add(kv.Key,
new float[] { offset.x, offset.y, scaling.x, scaling.y });
}
break;
case VRMPreShaderPropExporter.ShaderPropertyType.Vector:
{
var value = m.GetVector(kv.Key).ToArray();
material.vectorProperties.Add(kv.Key, value);
}
break;
default:
throw new NotImplementedException();
}
}
}
foreach (var keyword in m.shaderKeywords)
{
material.keywordMap.Add(keyword, m.IsKeywordEnabled(keyword));
}
foreach(var tag in TAGS)
foreach (var tag in TAGS)
{
var value = m.GetTag(tag, false);
if (!String.IsNullOrEmpty(value))

View File

@ -98,7 +98,7 @@ namespace VRM
public string otherLicenseUrl;
#endregion
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => version);

View File

@ -12,7 +12,7 @@ namespace VRM
public Vector3 offset;
public float radius;
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => offset);
f.KeyValue(() => radius);
@ -25,7 +25,7 @@ namespace VRM
public int node;
public List<glTF_VRM_SecondaryAnimationCollider> colliders = new List<glTF_VRM_SecondaryAnimationCollider>();
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => node);
f.KeyValue(() => colliders);
@ -46,7 +46,7 @@ namespace VRM
public int[] bones = new int[] { };
public int[] colliderGroups = new int[] { };
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => comment);
f.KeyValue(() => stiffiness);
@ -66,7 +66,7 @@ namespace VRM
public List<glTF_VRM_SecondaryAnimationGroup> boneGroups = new List<glTF_VRM_SecondaryAnimationGroup>();
public List<glTF_VRM_SecondaryAnimationColliderGroup> colliderGroups = new List<glTF_VRM_SecondaryAnimationColliderGroup>();
protected override void SerializeMembers(JsonFormatter f)
protected override void SerializeMembers(GLTFJsonFormatter f)
{
f.KeyValue(() => boneGroups);
f.KeyValue(() => colliderGroups);

View File

@ -19,7 +19,7 @@ namespace VRM
public void OnImported(VRMImporterContext context)
{
var gltfFirstPerson = context.VRM.extensions.VRM.firstPerson;
var gltfFirstPerson = context.GLTF.extensions.VRM.firstPerson;
Horizontal.Apply(gltfFirstPerson.lookAtHorizontalOuter);
VerticalDown.Apply(gltfFirstPerson.lookAtVerticalDown);
VerticalUp.Apply(gltfFirstPerson.lookAtVerticalUp);
@ -45,27 +45,27 @@ namespace VRM
if (yaw < 0)
{
// Left
m_propxy.SetValue(BlendShapePreset.LookRight, 0); // clear first
m_propxy.SetValue(BlendShapePreset.LookLeft, Horizontal.Map(-yaw));
m_propxy.SetValue(BlendShapePreset.LookRight, 0);
}
else
{
// Right
m_propxy.SetValue(BlendShapePreset.LookLeft, 0);
m_propxy.SetValue(BlendShapePreset.LookLeft, 0); // clear first
m_propxy.SetValue(BlendShapePreset.LookRight, Horizontal.Map(yaw));
}
if (pitch < 0)
{
// Down
m_propxy.SetValue(BlendShapePreset.LookUp, 0);
m_propxy.SetValue(BlendShapePreset.LookUp, 0); // clear first
m_propxy.SetValue(BlendShapePreset.LookDown, VerticalDown.Map(-pitch));
}
else
{
// Up
m_propxy.SetValue(BlendShapePreset.LookDown, 0); // clear first
m_propxy.SetValue(BlendShapePreset.LookUp, VerticalUp.Map(pitch));
m_propxy.SetValue(BlendShapePreset.LookDown, 0);
}
}
}

View File

@ -36,7 +36,7 @@ namespace VRM
RightEye = OffsetOnTransform.Create(animator.GetBoneTransform(HumanBodyBones.RightEye));
}
var gltfFirstPerson = context.VRM.extensions.VRM.firstPerson;
var gltfFirstPerson = context.GLTF.extensions.VRM.firstPerson;
HorizontalInner.Apply(gltfFirstPerson.lookAtHorizontalInner);
HorizontalOuter.Apply(gltfFirstPerson.lookAtHorizontalOuter);
VerticalDown.Apply(gltfFirstPerson.lookAtVerticalDown);

View File

@ -90,7 +90,7 @@ namespace VRM
Head = OffsetOnTransform.Create(head);
var gltfFirstPerson = context.VRM.extensions.VRM.firstPerson;
var gltfFirstPerson = context.GLTF.extensions.VRM.firstPerson;
switch (gltfFirstPerson.lookAtType)
{
case LookAtType.Bone:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 3fa98374d2ce76443981cc61ab80e6ce
folderAsset: yes
timeCreated: 1533035745
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UniGLTF;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace VRM
{
public class VRMPreExportShaderAttribute : Attribute { }
public static partial class VRMPreShaderPropExporter
{
public static string[] SupportedShaders = new string[]
{
"VRM/MToon",
"VRM/UnlitTexture",
"VRM/UnlitCutout",
"VRM/UnlitTransparent",
"VRM/UnlitTransparentZWrite",
};
#if UNITY_EDITOR
[MenuItem(VRMVersion.VRM_VERSION + "/PreExport ShaderProps")]
static void PreExport()
{
foreach (var shaderName in SupportedShaders)
{
var shader = Shader.Find(shaderName);
PreExport(shader);
}
}
static string EscapeShaderName(string name)
{
return name.Replace("/", "_").Replace(" ", "_");
}
const string EXPORT_DIR = "Assets/VRM/Scripts/PreExportShaderProps/";
static void PreExport(Shader shader)
{
var path = UnityPath.FromUnityPath(EXPORT_DIR + EscapeShaderName(shader.name) + ".cs");
Debug.LogFormat("PreExport: {0}", path.FullPath);
var props = ShaderProps.FromShader(shader);
File.WriteAllText(path.FullPath, props.ToString(shader.name));
}
#endif
public enum ShaderPropertyType
{
TexEnv,
Color,
Range,
Float,
Vector,
}
public class ShaderProps
{
public KeyValuePair<string, ShaderPropertyType>[] Properties;
#if UNITY_EDITOR
static ShaderPropertyType ConvType(ShaderUtil.ShaderPropertyType src)
{
switch (src)
{
case ShaderUtil.ShaderPropertyType.TexEnv: return ShaderPropertyType.TexEnv;
case ShaderUtil.ShaderPropertyType.Color: return ShaderPropertyType.Color;
case ShaderUtil.ShaderPropertyType.Float: return ShaderPropertyType.Float;
case ShaderUtil.ShaderPropertyType.Range: return ShaderPropertyType.Range;
case ShaderUtil.ShaderPropertyType.Vector: return ShaderPropertyType.Vector;
default: throw new NotImplementedException();
}
}
public static ShaderProps FromShader(Shader shader)
{
var properties = new List<KeyValuePair<string, ShaderPropertyType>>();
for (int i = 0; i < ShaderUtil.GetPropertyCount(shader); ++i)
{
var name = ShaderUtil.GetPropertyName(shader, i);
var propType = ShaderUtil.GetPropertyType(shader, i);
properties.Add(new KeyValuePair<string, ShaderPropertyType>(name, ConvType(propType)));
}
return new ShaderProps
{
Properties = properties.ToArray(),
};
}
public string ToString(string shaderName)
{
var list = new List<string>();
foreach(var kv in Properties)
{
list.Add(string.Format("new KeyValuePair<string, ShaderPropertyType>(\"{0}\", ShaderPropertyType.{1})\r\n", kv.Key, kv.Value));
}
return string.Format(@"using System.Collections.Generic;
namespace VRM
{{
public static partial class VRMPreShaderPropExporter
{{
[VRMPreExportShader]
static KeyValuePair<string, ShaderProps> {0}
{{
get
{{
return new KeyValuePair<string, ShaderProps>(
""{1}"",
new ShaderProps
{{
Properties = new KeyValuePair<string, ShaderPropertyType>[]{{
{2}
}}
}}
);
}}
}}
}}
}}
"
, EscapeShaderName(shaderName)
, shaderName
, String.Join(",", list.ToArray()));
}
#endif
}
#region Runtime
static Dictionary<string, ShaderProps> m_shaderPropMap;
public static ShaderProps GetPropsForSupportedShader(string shaderName)
{
if (m_shaderPropMap == null)
{
m_shaderPropMap = new Dictionary<string, ShaderProps>();
foreach (var prop in typeof(VRMPreShaderPropExporter).GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
{
if (prop.GetCustomAttributes(typeof(VRMPreExportShaderAttribute), true).Any())
{
var kv = (KeyValuePair<string, ShaderProps>)prop.GetValue(null, null);
m_shaderPropMap.Add(kv.Key, kv.Value);
}
}
}
ShaderProps props;
if (!m_shaderPropMap.TryGetValue(shaderName, out props))
{
return null;
}
return props;
}
#endregion
}
}

View File

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

View File

@ -0,0 +1,55 @@
using System.Collections.Generic;
namespace VRM
{
public static partial class VRMPreShaderPropExporter
{
[VRMPreExportShader]
static KeyValuePair<string, ShaderProps> VRM_MToon
{
get
{
return new KeyValuePair<string, ShaderProps>(
"VRM/MToon",
new ShaderProps
{
Properties = new KeyValuePair<string, ShaderPropertyType>[]{
new KeyValuePair<string, ShaderPropertyType>("_Cutoff", ShaderPropertyType.Range)
,new KeyValuePair<string, ShaderPropertyType>("_Color", ShaderPropertyType.Color)
,new KeyValuePair<string, ShaderPropertyType>("_ShadeColor", ShaderPropertyType.Color)
,new KeyValuePair<string, ShaderPropertyType>("_MainTex", ShaderPropertyType.TexEnv)
,new KeyValuePair<string, ShaderPropertyType>("_ShadeTexture", ShaderPropertyType.TexEnv)
,new KeyValuePair<string, ShaderPropertyType>("_BumpScale", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_BumpMap", ShaderPropertyType.TexEnv)
,new KeyValuePair<string, ShaderPropertyType>("_ReceiveShadowRate", ShaderPropertyType.Range)
,new KeyValuePair<string, ShaderPropertyType>("_ReceiveShadowTexture", ShaderPropertyType.TexEnv)
,new KeyValuePair<string, ShaderPropertyType>("_ShadeShift", ShaderPropertyType.Range)
,new KeyValuePair<string, ShaderPropertyType>("_ShadeToony", ShaderPropertyType.Range)
,new KeyValuePair<string, ShaderPropertyType>("_LightColorAttenuation", ShaderPropertyType.Range)
,new KeyValuePair<string, ShaderPropertyType>("_SphereAdd", ShaderPropertyType.TexEnv)
,new KeyValuePair<string, ShaderPropertyType>("_EmissionColor", ShaderPropertyType.Color)
,new KeyValuePair<string, ShaderPropertyType>("_EmissionMap", ShaderPropertyType.TexEnv)
,new KeyValuePair<string, ShaderPropertyType>("_OutlineWidthTexture", ShaderPropertyType.TexEnv)
,new KeyValuePair<string, ShaderPropertyType>("_OutlineWidth", ShaderPropertyType.Range)
,new KeyValuePair<string, ShaderPropertyType>("_OutlineScaledMaxDistance", ShaderPropertyType.Range)
,new KeyValuePair<string, ShaderPropertyType>("_OutlineColor", ShaderPropertyType.Color)
,new KeyValuePair<string, ShaderPropertyType>("_OutlineLightingMix", ShaderPropertyType.Range)
,new KeyValuePair<string, ShaderPropertyType>("_DebugMode", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_BlendMode", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_OutlineWidthMode", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_OutlineColorMode", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_CullMode", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_OutlineCullMode", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_SrcBlend", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_DstBlend", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_ZWrite", ShaderPropertyType.Float)
,new KeyValuePair<string, ShaderPropertyType>("_IsFirstSetup", ShaderPropertyType.Float)
}
}
);
}
}
}
}

View File

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

View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace VRM
{
public static partial class VRMPreShaderPropExporter
{
[VRMPreExportShader]
static KeyValuePair<string, ShaderProps> VRM_UnlitCutout
{
get
{
return new KeyValuePair<string, ShaderProps>(
"VRM/UnlitCutout",
new ShaderProps
{
Properties = new KeyValuePair<string, ShaderPropertyType>[]{
new KeyValuePair<string, ShaderPropertyType>("_MainTex", ShaderPropertyType.TexEnv)
,new KeyValuePair<string, ShaderPropertyType>("_Cutoff", ShaderPropertyType.Range)
}
}
);
}
}
}
}

View File

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

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace VRM
{
public static partial class VRMPreShaderPropExporter
{
[VRMPreExportShader]
static KeyValuePair<string, ShaderProps> VRM_UnlitTexture
{
get
{
return new KeyValuePair<string, ShaderProps>(
"VRM/UnlitTexture",
new ShaderProps
{
Properties = new KeyValuePair<string, ShaderPropertyType>[]{
new KeyValuePair<string, ShaderPropertyType>("_MainTex", ShaderPropertyType.TexEnv)
}
}
);
}
}
}
}

View File

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

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace VRM
{
public static partial class VRMPreShaderPropExporter
{
[VRMPreExportShader]
static KeyValuePair<string, ShaderProps> VRM_UnlitTransparent
{
get
{
return new KeyValuePair<string, ShaderProps>(
"VRM/UnlitTransparent",
new ShaderProps
{
Properties = new KeyValuePair<string, ShaderPropertyType>[]{
new KeyValuePair<string, ShaderPropertyType>("_MainTex", ShaderPropertyType.TexEnv)
}
}
);
}
}
}
}

View File

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

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace VRM
{
public static partial class VRMPreShaderPropExporter
{
[VRMPreExportShader]
static KeyValuePair<string, ShaderProps> VRM_UnlitTransparentZWrite
{
get
{
return new KeyValuePair<string, ShaderProps>(
"VRM/UnlitTransparentZWrite",
new ShaderProps
{
Properties = new KeyValuePair<string, ShaderPropertyType>[]{
new KeyValuePair<string, ShaderPropertyType>("_MainTex", ShaderPropertyType.TexEnv)
}
}
);
}
}
}
}

View File

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

View File

@ -0,0 +1,392 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniHumanoid;
using UnityEngine;
namespace VRM
{
public static class BoneNormalizer
{
/// <summary>
/// 回転とスケールを除去したヒエラルキーをコピーする
/// </summary>
/// <param name="src"></param>
/// <param name="dst"></param>
static void CopyAndBuild(Transform src, Transform dst, Dictionary<Transform, Transform> boneMap)
{
boneMap[src] = dst;
foreach (Transform child in src)
{
if (child.gameObject.activeSelf)
{
var dstChild = new GameObject(child.name);
dstChild.transform.SetParent(dst);
dstChild.transform.position = child.position; // copy position only
CopyAndBuild(child, dstChild.transform, boneMap);
}
}
}
static IEnumerable<Transform> Traverse(this Transform t)
{
yield return t;
foreach (Transform child in t)
{
foreach (var x in child.Traverse())
{
yield return x;
}
}
}
static void EnforceTPose(GameObject go)
{
var animator = go.GetComponent<Animator>();
if (animator == null)
{
throw new ArgumentException("Animator with avatar is required");
}
var avatar = animator.avatar;
if (avatar == null)
{
throw new ArgumentException("avatar is required");
}
if (!avatar.isValid)
{
throw new ArgumentException("invalid avatar");
}
if (!avatar.isHuman)
{
throw new ArgumentException("avatar is not human");
}
HumanPoseTransfer.SetTPose(avatar, go.transform);
}
static GameObject NormalizeHierarchy(GameObject go, Dictionary<Transform, Transform> boneMap)
{
//
// 回転・スケールの無いヒエラルキーをコピーする
//
var normalized = new GameObject(go.name + "(normalized)");
normalized.transform.position = go.transform.position;
CopyAndBuild(go.transform, normalized.transform, boneMap);
//
// 新しいヒエラルキーからAvatarを作る
//
{
var src = go.GetComponent<Animator>();
var map = Enum.GetValues(typeof(HumanBodyBones))
.Cast<HumanBodyBones>()
.Where(x => x != HumanBodyBones.LastBone)
.Select(x => new { Key = x, Value = src.GetBoneTransform(x) })
.Where(x => x.Value != null)
.ToDictionary(x => x.Key, x => boneMap[x.Value])
;
var animator = normalized.AddComponent<Animator>();
var vrmHuman = go.GetComponent<VRMHumanoidDescription>();
var avatarDescription = AvatarDescription.Create();
if (vrmHuman != null && vrmHuman.Description != null)
{
avatarDescription.armStretch = vrmHuman.Description.armStretch;
avatarDescription.legStretch = vrmHuman.Description.legStretch;
avatarDescription.upperArmTwist = vrmHuman.Description.upperArmTwist;
avatarDescription.lowerArmTwist = vrmHuman.Description.lowerArmTwist;
avatarDescription.upperLegTwist = vrmHuman.Description.upperLegTwist;
avatarDescription.lowerLegTwist = vrmHuman.Description.lowerLegTwist;
avatarDescription.feetSpacing = vrmHuman.Description.feetSpacing;
avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF;
}
avatarDescription.SetHumanBones(map);
var avatar = avatarDescription.CreateAvatar(normalized.transform);
avatar.name = go.name + ".normalized";
animator.avatar = avatar;
var humanPoseTransfer = normalized.AddComponent<HumanPoseTransfer>();
humanPoseTransfer.Avatar = avatar;
}
return normalized;
}
/// <summary>
/// srcのSkinnedMeshRendererを正規化して、dstにアタッチする
/// </summary>
/// <param name="src">正規化前のSkinnedMeshRendererのTransform</param>
/// <param name="dst">正規化後のSkinnedMeshRendererのTransform</param>
/// <param name="boneMap">正規化前のボーンから正規化後のボーンを得る</param>
static void NormalizeSkinnedMesh(Transform src, Transform dst, Dictionary<Transform, Transform> boneMap)
{
var srcRenderer = src.GetComponent<SkinnedMeshRenderer>();
if (srcRenderer == null
|| !srcRenderer.enabled
|| srcRenderer.sharedMesh == null
|| srcRenderer.sharedMesh.vertexCount == 0)
{
// 有効なSkinnedMeshRendererが無かった
return;
}
// clear blendShape
var srcMesh = srcRenderer.sharedMesh;
var originalSrcMesh = srcMesh;
for (int i = 0; i < srcMesh.blendShapeCount; ++i)
{
srcRenderer.SetBlendShapeWeight(i, 0);
}
var bones = srcRenderer.bones.Select(x => boneMap[x]).ToArray();
var hasBoneWeight = srcRenderer.bones!=null && srcRenderer.bones.Length > 0;
if (!hasBoneWeight)
{
// Before bake, bind no weight bones
//Debug.LogFormat("no weight: {0}", srcMesh.name);
srcMesh = srcMesh.Copy();
var bw = new BoneWeight
{
boneIndex0 = 0,
boneIndex1 = 0,
boneIndex2 = 0,
boneIndex3 = 0,
weight0 = 1.0f,
weight1 = 0.0f,
weight2 = 0.0f,
weight3 = 0.0f,
};
srcMesh.boneWeights = Enumerable.Range(0, srcMesh.vertexCount).Select(x => bw).ToArray();
srcMesh.bindposes = new Matrix4x4[] { Matrix4x4.identity };
srcRenderer.rootBone = srcRenderer.transform;
bones = new[] { boneMap[srcRenderer.transform] };
srcRenderer.bones = new[] { srcRenderer.transform };
srcRenderer.sharedMesh = srcMesh;
}
// BakeMesh
var mesh = srcMesh.Copy();
mesh.name = srcMesh.name + ".baked";
srcRenderer.BakeMesh(mesh);
mesh.boneWeights = srcMesh.boneWeights; // restore weights. clear when BakeMesh
// recalc bindposes
mesh.bindposes = bones.Select(x => x.worldToLocalMatrix * dst.transform.localToWorldMatrix).ToArray();
//var m = src.localToWorldMatrix; // include scaling
var m = default(Matrix4x4);
m.SetTRS(Vector3.zero, src.rotation, Vector3.one); // rotation only
mesh.ApplyMatrix(m);
//
// BlendShapes
//
var meshVertices = mesh.vertices;
var meshNormals = mesh.normals;
var meshTangents = mesh.tangents.Select(x => (Vector3)x).ToArray();
var _meshVertices = new Vector3[meshVertices.Length];
var _meshNormals = new Vector3[meshVertices.Length];
var _meshTangents = new Vector3[meshVertices.Length];
var blendShapeMesh = new Mesh();
for (int i = 0; i < srcMesh.blendShapeCount; ++i)
{
// check blendShape
srcRenderer.sharedMesh.GetBlendShapeFrameVertices(i, 0, _meshVertices, _meshNormals, _meshTangents);
var hasVertices = !_meshVertices.All(x => x == Vector3.zero);
var hasNormals = !_meshNormals.All(x => x == Vector3.zero);
var hasTangents = !_meshTangents.All(x => x == Vector3.zero);
srcRenderer.SetBlendShapeWeight(i, 100.0f);
srcRenderer.BakeMesh(blendShapeMesh);
if (blendShapeMesh.vertices.Length != mesh.vertices.Length)
{
throw new Exception("diffrent vertex count");
}
srcRenderer.SetBlendShapeWeight(i, 0);
Vector3[] vertices = null;
if (hasVertices)
{
vertices = blendShapeMesh.vertices;
// to delta
for (int j = 0; j < vertices.Length; ++j)
{
vertices[j] = m.MultiplyPoint(vertices[j]) - meshVertices[j];
}
}
else
{
vertices = new Vector3[mesh.vertexCount];
}
Vector3[] normals = null;
if (hasNormals)
{
normals = blendShapeMesh.normals;
// to delta
for (int j = 0; j < normals.Length; ++j)
{
normals[j] = m.MultiplyVector(normals[j]) - meshNormals[j];
}
}
else
{
normals = new Vector3[mesh.vertexCount];
}
Vector3[] tangents = null;
if (hasTangents)
{
tangents = blendShapeMesh.tangents.Select(x => (Vector3)x).ToArray();
// to delta
for (int j = 0; j < tangents.Length; ++j)
{
tangents[j] = m.MultiplyVector(tangents[j]) - meshTangents[j];
}
}
else
{
tangents = new Vector3[mesh.vertexCount];
}
var name = srcMesh.GetBlendShapeName(i);
if (string.IsNullOrEmpty(name))
{
name = String.Format("{0}", i);
}
var weight = srcMesh.GetBlendShapeFrameWeight(i, 0);
try
{
mesh.AddBlendShapeFrame(name,
weight,
vertices,
normals,
tangents
);
}
catch (Exception)
{
Debug.LogErrorFormat("fail to mesh.AddBlendShapeFrame {0}.{1}",
mesh.name,
srcMesh.GetBlendShapeName(i)
);
throw;
}
}
var dstRenderer = dst.gameObject.AddComponent<SkinnedMeshRenderer>();
dstRenderer.sharedMaterials = srcRenderer.sharedMaterials;
if (srcRenderer.rootBone != null)
{
dstRenderer.rootBone = boneMap[srcRenderer.rootBone];
}
dstRenderer.bones = bones;
dstRenderer.sharedMesh = mesh;
if (!hasBoneWeight)
{
// restore bones
srcRenderer.bones = new Transform[] { };
srcRenderer.sharedMesh = originalSrcMesh;
}
}
/// <summary>
///
/// </summary>
/// <param name="src"></param>
/// <param name="dst"></param>
static void NormalizeNoneSkinnedMesh(Transform src, Transform dst)
{
var srcFilter = src.GetComponent<MeshFilter>();
if (srcFilter == null
|| srcFilter.sharedMesh == null
|| srcFilter.sharedMesh.vertexCount == 0)
{
return;
}
var srcRenderer = src.GetComponent<MeshRenderer>();
if (srcRenderer == null || !srcRenderer.enabled)
{
return;
}
// Meshに乗っているボーンの姿勢を適用する
var dstFilter = dst.gameObject.AddComponent<MeshFilter>();
var dstMesh = srcFilter.sharedMesh.Copy();
dstMesh.ApplyRotationAndScale(src.localToWorldMatrix);
dstFilter.sharedMesh = dstMesh;
// Materialをコピー
var dstRenderer = dst.gameObject.AddComponent<MeshRenderer>();
dstRenderer.sharedMaterials = srcRenderer.sharedMaterials;
}
public struct NormalizedResult
{
public GameObject Root;
public Dictionary<Transform, Transform> BoneMap;
}
/// <summary>
/// モデルの正規化を実行する
/// </summary>
/// <param name="go">対象モデルのルート</param>
/// <param name="forceTPose">強制的にT-Pose化するか</param>
/// <returns>正規化済みのモデル</returns>
public static NormalizedResult Execute(GameObject go, bool forceTPose)
{
Dictionary<Transform, Transform> boneMap = new Dictionary<Transform, Transform>();
//
// T-Poseにする
//
if (forceTPose)
{
EnforceTPose(go);
}
//
// 正規化されたヒエラルキーを作る
//
var normalized = NormalizeHierarchy(go, boneMap);
//
// 各メッシュから回転・スケールを取り除いてBinding行列を再計算する
//
foreach (var src in go.transform.Traverse())
{
Transform dst;
if (!boneMap.TryGetValue(src, out dst))
{
continue;
}
NormalizeSkinnedMesh(src, dst, boneMap);
NormalizeNoneSkinnedMesh(src, dst);
}
return new NormalizedResult
{
Root = normalized,
BoneMap = boneMap
};
}
}
}

View File

@ -1,374 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniHumanoid;
using UnityEngine;
namespace VRM
{
public static class BoneNormalizer
{
/// <summary>
/// 回転とスケールを除去したヒエラルキーをコピーする
/// </summary>
/// <param name="src"></param>
/// <param name="dst"></param>
static void CopyAndBuild(Transform src, Transform dst, Dictionary<Transform, Transform> boneMap)
{
boneMap[src] = dst;
foreach (Transform child in src)
{
if (child.gameObject.activeSelf)
{
var dstChild = new GameObject(child.name);
dstChild.transform.SetParent(dst);
dstChild.transform.position = child.position; // copy position only
CopyAndBuild(child, dstChild.transform, boneMap);
}
}
}
static IEnumerable<Transform> Traverse(this Transform t)
{
yield return t;
foreach (Transform child in t)
{
foreach (var x in child.Traverse())
{
yield return x;
}
}
}
public static GameObject Execute(GameObject go, Dictionary<Transform, Transform> boneMap, bool forceTPose)
{
//
// T-Poseにする
//
if(forceTPose)
{
var animator = go.GetComponent<Animator>();
if (animator == null)
{
throw new ArgumentException("Animator with avatar is required");
}
var avatar = animator.avatar;
if (avatar == null)
{
throw new ArgumentException("avatar is required");
}
if (!avatar.isValid)
{
throw new ArgumentException("invalid avatar");
}
if (!avatar.isHuman)
{
throw new ArgumentException("avatar is not human");
}
HumanPoseTransfer.SetTPose(avatar, go.transform);
}
//
// 回転・スケールの無いヒエラルキーをコピーする
//
var normalized = new GameObject(go.name + "(normalized)");
normalized.transform.position = go.transform.position;
CopyAndBuild(go.transform, normalized.transform, boneMap);
//
// 新しいヒエラルキーからAvatarを作る
//
{
var src = go.GetComponent<Animator>();
var map = Enum.GetValues(typeof(HumanBodyBones))
.Cast<HumanBodyBones>()
.Where(x => x != HumanBodyBones.LastBone)
.Select(x => new { Key = x, Value = src.GetBoneTransform(x) })
.Where(x => x.Value != null)
.ToDictionary(x => x.Key, x => boneMap[x.Value])
;
var animator = normalized.AddComponent<Animator>();
var vrmHuman = go.GetComponent<VRMHumanoidDescription>();
var avatarDescription = AvatarDescription.Create();
if (vrmHuman != null && vrmHuman.Description != null)
{
avatarDescription.armStretch = vrmHuman.Description.armStretch;
avatarDescription.legStretch = vrmHuman.Description.legStretch;
avatarDescription.upperArmTwist = vrmHuman.Description.upperArmTwist;
avatarDescription.lowerArmTwist = vrmHuman.Description.lowerArmTwist;
avatarDescription.upperLegTwist = vrmHuman.Description.upperLegTwist;
avatarDescription.lowerLegTwist = vrmHuman.Description.lowerLegTwist;
avatarDescription.feetSpacing = vrmHuman.Description.feetSpacing;
avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF;
}
avatarDescription.SetHumanBones(map);
var avatar = avatarDescription.CreateAvatar(normalized.transform);
avatar.name = go.name + ".normalized";
animator.avatar = avatar;
var humanPoseTransfer = normalized.AddComponent<HumanPoseTransfer>();
humanPoseTransfer.Avatar = avatar;
}
//
// 各メッシュから回転・スケールを取り除いてBinding行列を再計算する
//
foreach (var src in go.transform.Traverse())
{
Transform dst;
if(!boneMap.TryGetValue(src, out dst))
{
continue;
}
{
//
// SkinnedMesh
//
var srcRenderer = src.GetComponent<SkinnedMeshRenderer>();
if (srcRenderer != null && srcRenderer.enabled
&& srcRenderer.sharedMesh!=null
&& srcRenderer.sharedMesh.vertexCount>0)
{
// clear blendShape
var srcMesh = srcRenderer.sharedMesh;
for (int i = 0; i < srcMesh.blendShapeCount; ++i)
{
srcRenderer.SetBlendShapeWeight(i, 0);
}
var mesh = new Mesh();
mesh.name = srcMesh.name + ".baked";
#if UNITY_2017_3_OR_NEWER
mesh.indexFormat = srcMesh.indexFormat;
#endif
srcRenderer.BakeMesh(mesh);
//var m = src.localToWorldMatrix;
var m = default(Matrix4x4);
m.SetTRS(Vector3.zero, src.rotation, Vector3.one);
mesh.vertices = mesh.vertices.Select(x => m.MultiplyPoint(x)).ToArray();
mesh.normals = mesh.normals.Select(x => m.MultiplyVector(x).normalized).ToArray();
mesh.uv = srcMesh.uv;
mesh.tangents = srcMesh.tangents;
mesh.subMeshCount = srcMesh.subMeshCount;
for (int i = 0; i < srcMesh.subMeshCount; ++i)
{
mesh.SetIndices(srcMesh.GetIndices(i), srcMesh.GetTopology(i), i);
}
// boneweights
var bones = srcRenderer.bones.Select(x => boneMap[x]).ToArray();
if (bones.Length > 0)
{
mesh.boneWeights = srcMesh.boneWeights;
}
else
{
Debug.LogFormat("no weight: {0}", srcMesh.name);
bones = new[] { boneMap[srcRenderer.transform] };
var bw = new BoneWeight
{
boneIndex0 = 0,
boneIndex1 = 0,
boneIndex2 = 0,
boneIndex3 = 0,
weight0 = 1.0f,
weight1 = 0.0f,
weight2 = 0.0f,
weight3 = 0.0f,
};
mesh.boneWeights = Enumerable.Range(0, srcMesh.vertexCount).Select(x => bw).ToArray();
}
var meshVertices = mesh.vertices;
var meshNormals = mesh.normals;
var meshTangents = mesh.tangents.Select(x => (Vector3)x).ToArray();
var _meshVertices = new Vector3[meshVertices.Length];
var _meshNormals = new Vector3[meshVertices.Length];
var _meshTangents = new Vector3[meshVertices.Length];
var blendShapeMesh = new Mesh();
for (int i = 0; i < srcMesh.blendShapeCount; ++i)
{
// check blendShape
srcRenderer.sharedMesh.GetBlendShapeFrameVertices(i, 0, _meshVertices, _meshNormals, _meshTangents);
var hasVertices = !_meshVertices.All(x => x == Vector3.zero);
var hasNormals = !_meshNormals.All(x => x == Vector3.zero);
var hasTangents = !_meshTangents.All(x => x == Vector3.zero);
srcRenderer.SetBlendShapeWeight(i, 100.0f);
srcRenderer.BakeMesh(blendShapeMesh);
if (blendShapeMesh.vertices.Length != mesh.vertices.Length)
{
throw new Exception("diffrent vertex count");
}
srcRenderer.SetBlendShapeWeight(i, 0);
Vector3[] vertices = null;
if (hasVertices)
{
vertices = blendShapeMesh.vertices;
// to delta
for (int j = 0; j < vertices.Length; ++j)
{
vertices[j] = m.MultiplyPoint(vertices[j]) - meshVertices[j];
}
}
else
{
vertices = new Vector3[mesh.vertexCount];
}
Vector3[] normals = null;
if (hasNormals)
{
normals = blendShapeMesh.normals;
// to delta
for (int j = 0; j < normals.Length; ++j)
{
normals[j] = m.MultiplyVector(normals[j]) - meshNormals[j];
}
}
else
{
normals = new Vector3[mesh.vertexCount];
}
Vector3[] tangents = null;
if (hasTangents)
{
tangents = blendShapeMesh.tangents.Select(x => (Vector3)x).ToArray();
// to delta
for (int j = 0; j < tangents.Length; ++j)
{
tangents[j] = m.MultiplyVector(tangents[j]) - meshTangents[j];
}
}
else
{
tangents = new Vector3[mesh.vertexCount];
}
var name = srcMesh.GetBlendShapeName(i);
if (string.IsNullOrEmpty(name))
{
name = String.Format("{0}", i);
}
var weight = srcMesh.GetBlendShapeFrameWeight(i, 0);
try
{
mesh.AddBlendShapeFrame(name,
weight,
vertices,
normals,
tangents
);
}
catch (Exception)
{
Debug.LogErrorFormat("fail to mesh.AddBlendShapeFrame {0}.{1}",
mesh.name,
srcMesh.GetBlendShapeName(i)
);
throw;
}
}
// recalc bindposes
mesh.bindposes = bones.Select(x =>
x.worldToLocalMatrix * dst.transform.localToWorldMatrix).ToArray();
mesh.RecalculateBounds();
var dstRenderer = dst.gameObject.AddComponent<SkinnedMeshRenderer>();
dstRenderer.sharedMaterials = srcRenderer.sharedMaterials;
dstRenderer.sharedMesh = mesh;
dstRenderer.bones = bones;
if (srcRenderer.rootBone != null)
{
dstRenderer.rootBone = boneMap[srcRenderer.rootBone];
}
}
}
{
//
// not SkinnedMesh
//
var srcFilter = src.GetComponent<MeshFilter>();
if (srcFilter != null
&& srcFilter.sharedMesh!=null
&& srcFilter.sharedMesh.vertexCount>0)
{
var srcRenderer = src.GetComponent<MeshRenderer>();
if (srcRenderer!=null && srcRenderer.enabled)
{
var dstFilter = dst.gameObject.AddComponent<MeshFilter>();
dstFilter.sharedMesh = TransformMesh(srcFilter.sharedMesh, src.localToWorldMatrix);
var dstRenderer = dst.gameObject.AddComponent<MeshRenderer>();
dstRenderer.sharedMaterials = srcRenderer.sharedMaterials;
}
}
}
}
return normalized;
}
static Mesh TransformMesh(Mesh src, Matrix4x4 m)
{
m.SetColumn(3, new Vector4(0, 0, 0, 1));
var mesh = new Mesh();
mesh.name = src.name + "(transformed)";
mesh.vertices = src.vertices.Select(x => m.MultiplyPoint(x)).ToArray();
if (src.normals != null && src.normals.Length > 0)
{
mesh.normals = src.normals.Select(x => m.MultiplyVector(x)).ToArray();
}
if (src.tangents != null && src.tangents.Length > 0)
{
mesh.tangents = src.tangents.Select(x =>
{
var t = m.MultiplyVector((Vector3)x);
return new Vector4(t.x, t.y, t.z, x.w);
}).ToArray();
}
if (src.colors != null && src.colors.Length > 0) mesh.colors = src.colors;
if (src.uv != null && src.uv.Length > 0) mesh.uv = src.uv;
if (src.uv2 != null && src.uv2.Length > 0) mesh.uv2 = src.uv2;
if (src.uv3 != null && src.uv3.Length > 0) mesh.uv3 = src.uv3;
if (src.uv4 != null && src.uv4.Length > 0) mesh.uv4 = src.uv4;
mesh.subMeshCount = src.subMeshCount;
for(int i=0; i<mesh.subMeshCount; ++i)
{
mesh.SetIndices(src.GetIndices(i), src.GetTopology(i), i);
}
return mesh;
}
}
}

View File

@ -0,0 +1,62 @@
using UnityEngine;
using System.Linq;
namespace VRM
{
public static class MeshExtensions
{
public static Mesh Copy(this Mesh src)
{
var dst = new Mesh();
dst.name = src.name + "(copy)";
#if UNITY_2017_3_OR_NEWER
dst.indexFormat = src.indexFormat;
#endif
dst.vertices = src.vertices;
dst.normals = src.normals;
dst.tangents = src.tangents;
dst.colors = src.colors;
dst.uv = src.uv;
dst.uv2 = src.uv2;
dst.uv3 = src.uv3;
dst.uv4 = src.uv4;
dst.boneWeights = src.boneWeights;
dst.bindposes = src.bindposes;
dst.subMeshCount = src.subMeshCount;
for (int i = 0; i < dst.subMeshCount; ++i)
{
dst.SetIndices(src.GetIndices(i), src.GetTopology(i), i);
}
dst.RecalculateBounds();
return dst;
}
public static void ApplyRotationAndScale(this Mesh src, Matrix4x4 m)
{
m.SetColumn(3, new Vector4(0, 0, 0, 1)); // remove translation
src.ApplyMatrix(m);
}
public static void ApplyMatrix(this Mesh src, Matrix4x4 m)
{
src.vertices = src.vertices.Select(x => m.MultiplyPoint(x)).ToArray();
if (src.normals != null && src.normals.Length > 0)
{
src.normals = src.normals.Select(x => m.MultiplyVector(x)).ToArray();
}
if (src.tangents != null && src.tangents.Length > 0)
{
src.tangents = src.tangents.Select(x =>
{
var t = m.MultiplyVector((Vector3)x);
return new Vector4(t.x, t.y, t.z, x.w);
}).ToArray();
}
}
}
}

View File

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

View File

@ -286,13 +286,16 @@ namespace VRM
{
foreach (var group in ColliderGroups)
{
foreach(var collider in group.Colliders)
if (group != null)
{
m_colliderList.Add(new SphereCollider
foreach (var collider in group.Colliders)
{
Position = group.transform.TransformPoint(collider.Offset),
Radius = collider.Radius,
});
m_colliderList.Add(new SphereCollider
{
Position = group.transform.TransformPoint(collider.Offset),
Radius = collider.Radius,
});
}
}
}
}

View File

@ -16,7 +16,7 @@ namespace VRM
{
#if UNITY_EDITOR
#region save
[MenuItem("VRM/SaveSpringBoneToJSON", validate = true)]
[MenuItem(VRMVersion.VRM_VERSION + "/SaveSpringBoneToJSON", validate = true)]
static bool SaveSpringBoneToJSONIsEnable()
{
var root = Selection.activeObject as GameObject;
@ -34,7 +34,7 @@ namespace VRM
return true;
}
[MenuItem("VRM/SaveSpringBoneToJSON")]
[MenuItem(VRMVersion.VRM_VERSION + "/SaveSpringBoneToJSON")]
static void SaveSpringBoneToJSON()
{
var path = EditorUtility.SaveFilePanel(
@ -62,7 +62,7 @@ namespace VRM
#endregion
#region load
[MenuItem("VRM/LoadSpringBoneFromJSON", true)]
[MenuItem(VRMVersion.VRM_VERSION + "/LoadSpringBoneFromJSON", true)]
static bool LoadSpringBoneFromJSONIsEnable()
{
var root = Selection.activeObject as GameObject;
@ -80,7 +80,7 @@ namespace VRM
return true;
}
[MenuItem("VRM/LoadSpringBoneFromJSON")]
[MenuItem(VRMVersion.VRM_VERSION + "/LoadSpringBoneFromJSON")]
static void LoadSpringBoneFromJSON()
{
var path = EditorUtility.OpenFilePanel(
@ -147,15 +147,8 @@ namespace VRM
stiffiness = spring.m_stiffnessForce,
hitRadius = spring.m_hitRadius,
colliderGroups = spring.ColliderGroups
.Select(x =>
{
var index = colliders.IndexOf(x);
if (index == -1)
{
throw new IndexOutOfRangeException();
}
return index;
})
.Select(x => colliders.IndexOf(x))
.Where(x => x != -1)
.ToArray(),
bones = spring.RootBones.Select(x => nodes.IndexOf(x)).ToArray(),
});
@ -177,7 +170,7 @@ namespace VRM
.SelectMany(x => x.GetComponents<Component>())
.Where(x => x is VRMSpringBone || x is VRMSpringBoneColliderGroup)
.ToArray();
foreach(var x in remove)
foreach (var x in remove)
{
if (Application.isPlaying)
{

@ -1 +1 @@
Subproject commit 1d41ff08c25707e9b789997368bac2971f2cd78e
Subproject commit e2d60e6ed73f549fe2e350d06a71762bfec9b95e

9
specification.meta Normal file
View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c41aa6d87463552448f681369e3ea7c3
folderAsset: yes
timeCreated: 1532940981
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

9
specification/0.0.meta Normal file
View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4aa7da6ed040f4f40bd5f8bdb752a04d
folderAsset: yes
timeCreated: 1532940981
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 51d82765b48dd464b8ee62daec6d6692
folderAsset: yes
timeCreated: 1532940981
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1 @@
{"title":"glTF_VRM_extensions","type":"object","properties":{"exporterVersion":{"title":"String","type":"string"},"meta":{"title":"glTF_VRM_Meta","type":"object","properties":{"title":{"title":"String","type":"string"},"version":{"title":"String","type":"string"},"author":{"title":"String","type":"string"},"contactInformation":{"title":"String","type":"string"},"reference":{"title":"String","type":"string"},"texture":{"title":"Int32","type":"integer"},"allowedUserName":{"title":"String","type":"string"},"violentUssageName":{"title":"String","type":"string"},"sexualUssageName":{"title":"String","type":"string"},"commercialUssageName":{"title":"String","type":"string"},"otherPermissionUrl":{"title":"String","type":"string"},"licenseName":{"title":"String","type":"string"},"otherLicenseUrl":{"title":"String","type":"string"}}},"humanoid":{"title":"glTF_VRM_Humanoid","type":"object","properties":{"humanBones":{"title":"List`1","type":"array"},"armStretch":{"title":"Single","type":"number"},"legStretch":{"title":"Single","type":"number"},"upperArmTwist":{"title":"Single","type":"number"},"lowerArmTwist":{"title":"Single","type":"number"},"upperLegTwist":{"title":"Single","type":"number"},"lowerLegTwist":{"title":"Single","type":"number"},"feetSpacing":{"title":"Single","type":"number"},"hasTranslationDoF":{"title":"Boolean","type":"bool"}}},"firstPerson":{"title":"glTF_VRM_Firstperson","type":"object","properties":{"firstPersonBone":{"title":"Int32","type":"integer"},"firstPersonBoneOffset":{"title":"Vector3","type":"array"},"meshAnnotations":{"title":"List`1","type":"array"},"lookAtTypeName":{"title":"String","type":"string"},"lookAtHorizontalInner":{"title":"glTF_VRM_DegreeMap","type":"object","properties":{"curve":{"title":"Single[]","type":"array"},"xRange":{"title":"Single","type":"number"},"yRange":{"title":"Single","type":"number"}}},"lookAtHorizontalOuter":{"title":"glTF_VRM_DegreeMap","type":"object","properties":{"curve":{"title":"Single[]","type":"array"},"xRange":{"title":"Single","type":"number"},"yRange":{"title":"Single","type":"number"}}},"lookAtVerticalDown":{"title":"glTF_VRM_DegreeMap","type":"object","properties":{"curve":{"title":"Single[]","type":"array"},"xRange":{"title":"Single","type":"number"},"yRange":{"title":"Single","type":"number"}}},"lookAtVerticalUp":{"title":"glTF_VRM_DegreeMap","type":"object","properties":{"curve":{"title":"Single[]","type":"array"},"xRange":{"title":"Single","type":"number"},"yRange":{"title":"Single","type":"number"}}}}},"blendShapeMaster":{"title":"glTF_VRM_BlendShapeMaster","type":"object","properties":{"blendShapeGroups":{"title":"List`1","type":"array"}}},"secondaryAnimation":{"title":"glTF_VRM_SecondaryAnimation","type":"object","properties":{"boneGroups":{"title":"List`1","type":"array"},"colliderGroups":{"title":"List`1","type":"array"}}},"materialProperties":{"title":"List`1","type":"array"}}}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f6d375d2085ef2e4392d858d5959330c
timeCreated: 1532941026
licenseType: Free
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant: