mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-23 19:36:18 -05:00
Merge branch 'work_v041'
This commit is contained in:
commit
c110041088
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
101
Scripts/Format/Editor/VRMExportObjectEditor.cs
Normal file
101
Scripts/Format/Editor/VRMExportObjectEditor.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/Editor/VRMExportObjectEditor.cs.meta
Normal file
12
Scripts/Format/Editor/VRMExportObjectEditor.cs.meta
Normal 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:
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
284
Scripts/Format/VRMExporSettings.cs
Normal file
284
Scripts/Format/VRMExporSettings.cs
Normal 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
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/VRMExporSettings.cs.meta
Normal file
12
Scripts/Format/VRMExporSettings.cs.meta
Normal 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:
|
||||
12
Scripts/Format/VRMExportObject.cs
Normal file
12
Scripts/Format/VRMExportObject.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
[CreateAssetMenu(menuName = "VRM/ExportObject")]
|
||||
public class VRMExportObject : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
public VRMExportSettings Settings = new VRMExportSettings();
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/VRMExportObject.cs.meta
Normal file
12
Scripts/Format/VRMExportObject.cs.meta
Normal 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:
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,5 +47,7 @@ namespace VRM
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public const string VRM_VERSION = "UniVRM-" + VERSION;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
9
Scripts/PreExportShaderProps.meta
Normal file
9
Scripts/PreExportShaderProps.meta
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3fa98374d2ce76443981cc61ab80e6ce
|
||||
folderAsset: yes
|
||||
timeCreated: 1533035745
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
168
Scripts/PreExportShaderProps/VRMPreShaderPropExporter.cs
Normal file
168
Scripts/PreExportShaderProps/VRMPreShaderPropExporter.cs
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 50935dd2f9f3fa445a687f30d4dd663b
|
||||
timeCreated: 1533035131
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
55
Scripts/PreExportShaderProps/VRM_MToon.cs
Normal file
55
Scripts/PreExportShaderProps/VRM_MToon.cs
Normal 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)
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/PreExportShaderProps/VRM_MToon.cs.meta
Normal file
12
Scripts/PreExportShaderProps/VRM_MToon.cs.meta
Normal 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:
|
||||
27
Scripts/PreExportShaderProps/VRM_UnlitCutout.cs
Normal file
27
Scripts/PreExportShaderProps/VRM_UnlitCutout.cs
Normal 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)
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/PreExportShaderProps/VRM_UnlitCutout.cs.meta
Normal file
12
Scripts/PreExportShaderProps/VRM_UnlitCutout.cs.meta
Normal 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:
|
||||
26
Scripts/PreExportShaderProps/VRM_UnlitTexture.cs
Normal file
26
Scripts/PreExportShaderProps/VRM_UnlitTexture.cs
Normal 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)
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/PreExportShaderProps/VRM_UnlitTexture.cs.meta
Normal file
12
Scripts/PreExportShaderProps/VRM_UnlitTexture.cs.meta
Normal 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:
|
||||
26
Scripts/PreExportShaderProps/VRM_UnlitTransparent.cs
Normal file
26
Scripts/PreExportShaderProps/VRM_UnlitTransparent.cs
Normal 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)
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/PreExportShaderProps/VRM_UnlitTransparent.cs.meta
Normal file
12
Scripts/PreExportShaderProps/VRM_UnlitTransparent.cs.meta
Normal 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:
|
||||
26
Scripts/PreExportShaderProps/VRM_UnlitTransparentZWrite.cs
Normal file
26
Scripts/PreExportShaderProps/VRM_UnlitTransparentZWrite.cs
Normal 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)
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 165ec79b7aac1564a850fb3d3d19396e
|
||||
timeCreated: 1533041756
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
392
Scripts/SkinnedMeshUtility/BoneNormalizer.cs
Normal file
392
Scripts/SkinnedMeshUtility/BoneNormalizer.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
Scripts/SkinnedMeshUtility/MeshExtensions.cs
Normal file
62
Scripts/SkinnedMeshUtility/MeshExtensions.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/SkinnedMeshUtility/MeshExtensions.cs.meta
Normal file
12
Scripts/SkinnedMeshUtility/MeshExtensions.cs.meta
Normal 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:
|
||||
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
2
UniGLTF
2
UniGLTF
|
|
@ -1 +1 @@
|
|||
Subproject commit 1d41ff08c25707e9b789997368bac2971f2cd78e
|
||||
Subproject commit e2d60e6ed73f549fe2e350d06a71762bfec9b95e
|
||||
9
specification.meta
Normal file
9
specification.meta
Normal 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
9
specification/0.0.meta
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4aa7da6ed040f4f40bd5f8bdb752a04d
|
||||
folderAsset: yes
|
||||
timeCreated: 1532940981
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
specification/0.0/schema.meta
Normal file
9
specification/0.0/schema.meta
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 51d82765b48dd464b8ee62daec6d6692
|
||||
folderAsset: yes
|
||||
timeCreated: 1532940981
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1
specification/0.0/schema/vrm.schema.json
Normal file
1
specification/0.0/schema/vrm.schema.json
Normal 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"}}}
|
||||
8
specification/0.0/schema/vrm.schema.json.meta
Normal file
8
specification/0.0/schema/vrm.schema.json.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6d375d2085ef2e4392d858d5959330c
|
||||
timeCreated: 1532941026
|
||||
licenseType: Free
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in New Issue
Block a user