add VRM scripts

This commit is contained in:
ousttrue
2018-03-27 16:40:11 +09:00
parent 77b59c75d7
commit 2b34694ab3
158 changed files with 10771 additions and 0 deletions

10
Scenes.meta Normal file
View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c58829edf15349648929208fdb1c42ca
folderAsset: yes
timeCreated: 1519303162
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cb6ff948a1027244a86abf24f3445a87
timeCreated: 1520832729
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

10
Scripts.meta Normal file
View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 2f85d3d3cb76658408fd936e3b798481
folderAsset: yes
timeCreated: 1517228412
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

10
Scripts/BlendShape.meta Normal file
View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0604208c180cfea44878a6962ab134b3
folderAsset: yes
timeCreated: 1517402736
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
using System.Collections;
using UnityEngine;
namespace VRM
{
public class AIUEO : MonoBehaviour
{
[SerializeField]
public VRMBlendShapeProxy BlendShapes;
private void Reset()
{
BlendShapes = GetComponent<VRMBlendShapeProxy>();
}
BlendShapeClipHandler m_appyerA;
BlendShapeClipHandler m_appyerI;
BlendShapeClipHandler m_appyerU;
BlendShapeClipHandler m_appyerE;
BlendShapeClipHandler m_appyerO;
Coroutine m_coroutine;
[SerializeField]
float m_wait = 0.5f;
private void Awake()
{
if (BlendShapes == null) BlendShapes = GetComponent<VRM.VRMBlendShapeProxy>();
if (BlendShapes == null) return;
if (BlendShapes.BlendShapeAvatar == null) return;
var avatar = BlendShapes.BlendShapeAvatar;
m_appyerA = new BlendShapeClipHandler(avatar.GetClip("A"), transform);
m_appyerI = new BlendShapeClipHandler(avatar.GetClip("I"), transform);
m_appyerU = new BlendShapeClipHandler(avatar.GetClip("U"), transform);
m_appyerE = new BlendShapeClipHandler(avatar.GetClip("E"), transform);
m_appyerO = new BlendShapeClipHandler(avatar.GetClip("O"), transform);
}
IEnumerator Routine()
{
while (true)
{
yield return new WaitForSeconds(1.0f);
if (m_appyerA != null) m_appyerA.Apply(1.0f);
yield return new WaitForSeconds(m_wait);
if (m_appyerA != null) m_appyerA.Apply(0);
if (m_appyerI != null) m_appyerI.Apply(1.0f);
yield return new WaitForSeconds(m_wait);
if (m_appyerI != null) m_appyerI.Apply(0);
if (m_appyerU != null) m_appyerU.Apply(1.0f);
yield return new WaitForSeconds(m_wait);
if (m_appyerU != null) m_appyerU.Apply(0);
if (m_appyerE != null) m_appyerE.Apply(1.0f);
yield return new WaitForSeconds(m_wait);
if (m_appyerE != null) m_appyerE.Apply(0);
if (m_appyerO != null) m_appyerO.Apply(1.0f);
yield return new WaitForSeconds(m_wait);
if (m_appyerO != null) m_appyerO.Apply(0);
}
}
private void OnEnable()
{
m_coroutine = StartCoroutine(Routine());
}
private void OnDisable()
{
StopCoroutine(m_coroutine);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: b62ca4f3096cada41938f18e4a02d8dc
timeCreated: 1517463794
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,78 @@
using UnityEngine;
using System.Linq;
using System;
using System.Collections.Generic;
namespace VRM
{
[CreateAssetMenu(menuName = "VRM/BlendShapeAvatar")]
public class BlendShapeAvatar : ScriptableObject
{
[SerializeField]
public List<BlendShapeClip> Clips = new List<BlendShapeClip>();
public void CreateDefault()
{
foreach (BlendShapePreset preset in Enum.GetValues(typeof(BlendShapePreset)))
{
if (preset != BlendShapePreset.Unknown)
{
var clip = GetClip(preset);
if (clip == null)
{
clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.name = preset.ToString();
clip.BlendShapeName = preset.ToString();
Clips.Add(clip);
}
}
}
}
public void SetClip(BlendShapeKey key, BlendShapeClip clip)
{
int index = -1;
try
{
index = Clips.FindIndex(x => key.Match(x));
}
catch (Exception)
{
}
if (index == -1)
{
Clips.Add(clip);
}
else
{
Clips[index] = clip;
}
}
public BlendShapeClip GetClip(BlendShapeKey key)
{
if (Clips == null) return null;
return Clips.FirstOrDefault(x => key.Match(x));
}
public BlendShapeClip GetClip(BlendShapePreset preset)
{
return GetClip(new BlendShapeKey(preset));
}
public BlendShapeClip GetClip(String name)
{
return GetClip(new BlendShapeKey(name));
}
public bool Apply(string name, Transform transform, float value)
{
var clip = GetClip(name);
if (clip == null) return false;
clip.Apply(transform, value);
return true;
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 329dca3bf78fcdd42b2df941673db76f
timeCreated: 1519195979
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,77 @@
using System;
using System.Linq;
using UnityEngine;
namespace VRM
{
[Serializable]
public struct BlendShapeBinding
{
public String RelativePath;
public int Index;
public float Weight;
}
/*
public struct BlendShapeKey
{
public BlendShapePreset Preset;
public String Name;
public BlendShapeKey(BlendShapePreset preset, String name)
{
Preset = preset;
Name = name.ToUpper();
}
public BlendShapeKey(BlendShapePreset preset)
{
Preset = preset;
Name = preset.ToString().ToUpper();
}
public BlendShapeKey(String name)
{
Preset = (BlendShapePreset)Enum.Parse(typeof(BlendShapePreset), name, true);
Name = name.ToUpper();
}
}
*/
[CreateAssetMenu(menuName = "VRM/BlendShapeClip")]
public class BlendShapeClip : ScriptableObject
{
[SerializeField]
public string BlendShapeName;
[SerializeField]
public BlendShapePreset Preset;
[SerializeField]
public BlendShapeBinding[] Values = new BlendShapeBinding[] { };
public void Apply(Transform root, float value)
{
if (Values != null)
{
foreach (var x in Values)
{
var target = root.Find(x.RelativePath);
if (target != null)
{
var sr = target.GetComponent<SkinnedMeshRenderer>();
sr.SetBlendShapeWeight(x.Index, x.Weight * value);
}
}
}
}
public static BlendShapeClip Create(BlendShapePreset preset)
{
var clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.Preset = preset;
clip.BlendShapeName = preset.ToString();
clip.name = preset.ToString();
return clip;
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 37562b39ff933b245ac2f35d87edbcd6
timeCreated: 1517402750
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
using UnityEngine;
using System.Linq;
namespace VRM
{
public class BlendShapeClipHandler
{
BlendShapeClip m_clip;
public BlendShapeClip Cilp
{
get { return m_clip; }
}
SkinnedMeshRenderer[] m_renderers;
public BlendShapeClipHandler(BlendShapeClip clip, Transform transform)
{
m_clip = clip;
if (m_clip != null && m_clip.Values!=null && transform != null)
{
m_renderers = m_clip.Values.Select(x =>
{
var target = UniGLTF.UnityExtensions.GetFromPath(transform, x.RelativePath);
return target.GetComponent<SkinnedMeshRenderer>();
})
.ToArray();
}
}
public float LastValue
{
get;
private set;
}
public void Apply(float value)
{
LastValue = value;
if (m_clip == null) return;
if (m_renderers == null) return;
for (int i = 0; i < m_clip.Values.Length; ++i)
{
var binding = m_clip.Values[i];
var target = m_renderers[i];
target.SetBlendShapeWeight(binding.Index, binding.Weight * value);
}
}
}
}

View File

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

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
namespace VRM
{
[Serializable]
public struct BlendShapeKey : IEquatable<BlendShapeKey>
{
public string Name;
public BlendShapePreset Preset;
string m_id;
string ID
{
get
{
if (string.IsNullOrEmpty(m_id))
{
if (Preset != BlendShapePreset.Unknown)
{
m_id = Preset.ToString().ToUpper();
}
else
{
m_id = Name;
}
}
return m_id;
}
}
public BlendShapeKey(string name) : this(name, BlendShapePreset.Unknown)
{
}
public BlendShapeKey(BlendShapePreset preset) : this(preset.ToString(), BlendShapePreset.Unknown)
{
}
public BlendShapeKey(string name, BlendShapePreset preset)
{
Name = name.ToUpper();
Preset = preset;
if (Preset != BlendShapePreset.Unknown)
{
m_id = Preset.ToString().ToUpper();
}
else
{
m_id = Name;
}
}
public override string ToString()
{
return ID;
}
public bool Equals(BlendShapeKey other)
{
/*
if (Preset != BlendShapePreset.Unknown && Preset == other.Preset)
{
// Unknown以外のPresetが一致したら一致とみなす
return true;
}
// .ToUpper()済み
return Name == other.Name;
*/
return ID == other.ID;
}
public class CustomerEqualityComparer : IEqualityComparer<BlendShapeKey>
{
public bool Equals(BlendShapeKey x, BlendShapeKey y)
{
return x.Equals(y);
}
public int GetHashCode(BlendShapeKey obj)
{
return obj.ToString().GetHashCode();
}
}
public static BlendShapeKey CreateFrom(BlendShapeClip clip)
{
return new BlendShapeKey(clip.BlendShapeName, clip.Preset);
}
public bool Match(BlendShapeClip clip)
{
return this.Equals(CreateFrom(clip));
}
}
}

View File

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

View File

@@ -0,0 +1,116 @@
using System.Collections;
using UnityEngine;
namespace VRM
{
public class Blinker : MonoBehaviour
{
[SerializeField]
public VRMBlendShapeProxy BlendShapes;
private void Reset()
{
BlendShapes = GetComponent<VRMBlendShapeProxy>();
}
[SerializeField]
float m_interVal = 5.0f;
[SerializeField]
float m_closingTime = 0.06f;
[SerializeField]
float m_openingSeconds = 0.03f;
[SerializeField]
float m_closeSeconds = 0.1f;
Coroutine m_coroutine;
static readonly string BLINK_NAME = BlendShapePreset.Blink.ToString();
IEnumerator BlinkRoutine()
{
while (true)
{
yield return new WaitForSeconds(Random.value * m_interVal);
// close
var value = 0.0f;
var closeSpeed = 1.0f / m_closeSeconds;
while (true)
{
value += Time.deltaTime * closeSpeed;
if (value >= 1.0f)
{
break;
}
BlendShapes.SetValue(BLINK_NAME, value);
yield return null;
}
BlendShapes.SetValue(BLINK_NAME, 1.0f);
// wait...
yield return new WaitForSeconds(m_closingTime);
// open
value = 1.0f;
var openSpeed = 1.0f / m_openingSeconds;
while (true)
{
value -= Time.deltaTime * openSpeed;
if (value < 0)
{
break;
}
BlendShapes.SetValue(BLINK_NAME, value);
yield return null;
}
BlendShapes.SetValue(BLINK_NAME, 0);
}
}
private void Start()
{
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()
{
if (m_coroutine != null)
{
StopCoroutine(m_coroutine);
m_coroutine = null;
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 69ea1f88e8d74e04e9341465573a4ef8
timeCreated: 1517463794
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 269b9c7394d16084a9e55528470e2ac7
folderAsset: yes
timeCreated: 1517404636
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
using UnityEditor;
using UnityEngine;
namespace VRM
{
class BlendShapeSlider
{
VRMBlendShapeProxy m_target;
BlendShapeKey m_key;
float? m_editorValue;
public BlendShapeSlider(VRMBlendShapeProxy target, BlendShapeKey key)
{
m_target = target;
m_key = key;
}
public void Slider()
{
if (m_target.BlendShapeAvatar == null)
{
return;
}
var oldClip = m_target.BlendShapeAvatar.GetClip(m_key);
/*var newClip = (BlendShapeClip)*/EditorGUILayout.ObjectField(m_key.ToString(), oldClip, typeof(BlendShapeClip), true);
/*
if (oldClip != newClip)
{
m_target.BlendShapeAvatar.SetClip(m_key, newClip);
}
*/
if (Application.isPlaying)
{
var oldValue = m_target.GetValue(m_key);
var newValue = EditorGUILayout.Slider(oldValue, 0, 1.0f);
if (oldValue != newValue)
{
m_target.SetValue(m_key, newValue);
}
}
else
{
var oldValue = m_editorValue.HasValue ? m_editorValue.Value : 0.0f;
var newValue = EditorGUILayout.Slider(oldValue, 0, 1.0f);
if (oldValue != newValue)
{
var clip = m_target.BlendShapeAvatar.GetClip(m_key);
if (clip != null)
{
clip.Apply(m_target.transform, newValue);
m_editorValue = newValue;
}
}
}
}
public void RestoreEditorValue()
{
if (m_editorValue.HasValue)
{
var clip = m_target.BlendShapeAvatar.GetClip(m_key);
if (clip != null)
{
clip.Apply(m_target.transform, m_editorValue.Value);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,157 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UniGLTF;
using System.IO;
namespace VRM
{
[CustomEditor(typeof(VRMBlendShapeManipulator))]
public class VRMBlendShapeManipulatorEditor : Editor
{
VRMBlendShapeManipulator m_target = null;
SkinnedMeshRenderer[] m_renderers;
void OnEnable()
{
m_target = (VRMBlendShapeManipulator)target;
m_renderers = m_target.transform
.Traverse()
.Select(x => x.GetComponent<SkinnedMeshRenderer>())
.Where(x => x != null)
.ToArray()
;
}
static string EscapeFilePath(string src)
{
return src
.Replace(">", ">")
.Replace("<", "<")
;
}
public override void OnInspectorGUI()
{
if (GUILayout.Button("Clear"))
{
ClearBlendShape();
}
if (GUILayout.Button("Create BlendShapeClip"))
{
CreateBlendShapeClip();
}
var clip = (BlendShapeClip)EditorGUILayout.ObjectField("Load clip", null, typeof(BlendShapeClip), false);
if (clip != null)
{
ClearBlendShape();
clip.Apply(m_target.transform, 1.0f);
}
EditorGUILayout.Space();
// sliders
foreach (var renderer in m_renderers)
{
var mesh = renderer.sharedMesh;
if (mesh != null)
{
var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, m_target.transform);
EditorGUILayout.LabelField(m_target.name+"/"+relativePath);
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var src = renderer.GetBlendShapeWeight(i);
var dst = EditorGUILayout.Slider(mesh.GetBlendShapeName(i), src, 0, 100.0f);
if (dst != src)
{
renderer.SetBlendShapeWeight(i, dst);
}
}
}
}
}
private void CreateBlendShapeClip()
{
var maxWeight = 0.0f;
var maxWeightName = "";
// weightのついたblendShapeを集める
var blendShapes = m_renderers.SelectMany(x =>
{
var mesh = x.sharedMesh;
var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(x.transform, m_target.transform);
var list = new List<BlendShapeBinding>();
if (mesh != null)
{
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var weight = x.GetBlendShapeWeight(i);
if (weight == 0)
{
continue;
}
var name = mesh.GetBlendShapeName(i);
if (weight > maxWeight)
{
maxWeightName = name;
maxWeight = weight;
}
list.Add(new BlendShapeBinding
{
Index = i,
RelativePath = relativePath,
Weight = weight
});
}
}
return list;
})
.ToArray()
;
var assetPath = string.Format("Assets/{0}.{1}.asset",
m_target.name,
maxWeightName);
var prefab = PrefabUtility.GetPrefabParent(m_target.gameObject);
if (prefab != null)
{
var prefabPath = AssetDatabase.GetAssetPath(prefab);
assetPath = string.Format("{0}/{1}.{2}.asset",
Path.GetDirectoryName(prefabPath),
Path.GetFileNameWithoutExtension(prefabPath),
maxWeightName);
}
assetPath = EscapeFilePath(assetPath);
var asset = ScriptableObject.CreateInstance<BlendShapeClip>();
asset.Values = blendShapes.ToArray();
Debug.LogFormat("create asset: {0}", assetPath);
AssetDatabase.CreateAsset(asset, assetPath);
Selection.objects = new UnityEngine.Object[] { AssetDatabase.LoadAssetAtPath(assetPath, typeof(BlendShapeClip)) };
}
private void ClearBlendShape()
{
foreach (var renderer in m_renderers)
{
var mesh = renderer.sharedMesh;
if (mesh != null)
{
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
renderer.SetBlendShapeWeight(i, 0);
}
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: e213c696e46ae2642b99797f2124a682
timeCreated: 1517404644
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,360 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEditor;
using UnityEngine;
namespace VRM
{
[CustomEditor(typeof(VRMBlendShapeProxy))]
public class VRMBlnedShapeProxyEditor : Editor
{
VRMBlendShapeProxy m_target;
SkinnedMeshRenderer[] m_renderers;
VRMLookAt m_lookAt;
#region Preview
class PreviewRenderer
{
PreviewRenderUtility m_previewRenderUtility;
public PreviewRenderer()
{
//true にすることでシーン内のゲームオブジェクトを描画できるようになる
m_previewRenderUtility = new PreviewRenderUtility(true);
#if UNITY_2017_3_OR_NEWER
//FieldOfView を 30 にするとちょうどいい見た目になる
m_previewRenderUtility.cameraFieldOfView = 30f;
//必要に応じて nearClipPlane と farClipPlane を設定
m_previewRenderUtility.camera.nearClipPlane = 0.3f;
m_previewRenderUtility.camera.farClipPlane = 1000;
#else
//FieldOfView を 30 にするとちょうどいい見た目になる
m_previewRenderUtility.m_CameraFieldOfView = 30f;
//必要に応じて nearClipPlane と farClipPlane を設定
m_previewRenderUtility.m_Camera.nearClipPlane = 0.3f;
m_previewRenderUtility.m_Camera.farClipPlane = 1000;
#endif
}
public Camera Camera
{
get
{
#if UNITY_2017_3_OR_NEWER
var previewCamera = m_previewRenderUtility.camera;
#else
var previewCamera = m_previewRenderUtility.m_Camera;
#endif
return previewCamera;
}
}
public void Render(Rect r, GUIStyle background)
{
var previewCamera = Camera;
if (previewCamera == null)
{
// 無い時がある
return;
}
m_previewRenderUtility.BeginPreview(r, background);
previewCamera.Render();
m_previewRenderUtility.EndAndDrawPreview(r);
}
}
PreviewRenderer m_preview;
public override bool HasPreviewGUI()
{
return true;
}
public override GUIContent GetPreviewTitle()
{
return new GUIContent("BlendShapeProxy");
}
public override void OnPreviewSettings()
{
/*
GUIStyle preLabel = new GUIStyle("preLabel");
GUIStyle preButton = new GUIStyle("preButton");
GUILayout.Label("ラベル", preLabel);
GUILayout.Button("ボタン", preButton);
*/
}
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
if (m_lookAt == null) return;
if (m_lookAt.Head.Transform == null) return;
m_lookAt.LookFace(m_preview.Camera.transform);
m_preview.Render(r, background);
//描画タイミングが少ないことによって
//カクつきがきになる時は Repaint を呼び出す(高負荷)
//Repaint ();
}
#endregion
void OnEnable()
{
m_target = (VRMBlendShapeProxy)target;
if (m_target.BlendShapeAvatar == null) return;
m_loadClip = m_target.BlendShapeAvatar.GetClip(BlendShapePreset.Neutral.ToString());
m_renderers = m_target.transform
.Traverse()
.Select(x => x.GetComponent<SkinnedMeshRenderer>())
.Where(x => x != null)
.ToArray()
;
m_sliders = m_target.BlendShapeAvatar.Clips
.Where(x => x!=null)
.Select(x => new BlendShapeSlider(m_target, BlendShapeKey.CreateFrom(x)))
.ToList()
;
m_lookAt = m_target.GetComponent<VRMLookAt>();
m_preview = new PreviewRenderer();
}
private void OnDisable()
{
if(m_mode==EditorMode.Editor)
{
ClearBlendShape();
}
}
enum EditorMode
{
Runtime,
Editor,
}
EditorMode m_mode;
static string[] MODES = ((EditorMode[])Enum.GetValues(typeof(EditorMode)))
.Select(x => x.ToString())
.ToArray();
public override void OnInspectorGUI()
{
/// xxxx
var mode =
(EditorMode)GUILayout.Toolbar((int)m_mode, MODES);
if (mode != m_mode)
{
m_mode = mode;
ClearBlendShape();
switch ((EditorMode)m_mode)
{
case EditorMode.Runtime:
if (Application.isPlaying)
{
m_target.Reload();
}
else
{
if (m_sliders != null)
{
foreach (var x in m_sliders)
{
x.RestoreEditorValue();
}
}
}
RuntimeInspector();
break;
case EditorMode.Editor:
Apply();
EditorInspector();
break;
}
}
else
{
switch ((EditorMode)m_mode)
{
case EditorMode.Runtime:
RuntimeInspector();
break;
case EditorMode.Editor:
EditorInspector();
break;
}
}
}
#region RuntimeInspector
List<BlendShapeSlider> m_sliders;
void RuntimeInspector()
{
base.OnInspectorGUI();
if (m_sliders != null)
{
foreach (var slider in m_sliders)
{
slider.Slider();
}
}
}
#endregion
#region EditorInspector
BlendShapeClip m_loadClip;
BlendShapeClip LoadClip
{
get { return m_loadClip; }
set
{
if (m_loadClip == value) return;
m_loadClip = value;
ClearBlendShape();
Apply();
}
}
void Apply()
{
if (m_loadClip != null)
{
m_loadClip.Apply(m_target.transform, 1.0f);
}
}
int m_preset;
void EditorInspector()
{
if (m_target.BlendShapeAvatar == null) return;
if (GUILayout.Button("Clear"))
{
ClearBlendShape();
}
/*
if (GUILayout.Button("Create BlendShapeClip"))
{
CreateBlendShapeClip();
}
*/
GUILayout.BeginHorizontal();
if (m_loadClip != null && GUILayout.Button("Apply"))
{
/*var loadClip = (BlendShapeClip)*/
EditorGUILayout.ObjectField("Load clip",
LoadClip, typeof(BlendShapeClip), false);
string maxWeightString;
m_loadClip.Values = GetBindings(out maxWeightString);
}
GUILayout.EndHorizontal();
// buttons
var preset = GUILayout.SelectionGrid(m_preset, m_target.BlendShapeAvatar.Clips
.Where(x => x!=null)
.Select(x => BlendShapeKey.CreateFrom(x).ToString()).ToArray(), 4);
if (preset != m_preset)
{
LoadClip = m_target.BlendShapeAvatar.Clips[preset];
m_preset = preset;
}
EditorGUILayout.Space();
// sliders
if (m_loadClip != null)
{
foreach (var renderer in m_renderers)
{
var mesh = renderer.sharedMesh;
if (mesh != null)
{
var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, m_target.transform);
EditorGUILayout.LabelField(m_target.name + "/" + relativePath);
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var src = renderer.GetBlendShapeWeight(i);
var dst = EditorGUILayout.Slider(mesh.GetBlendShapeName(i), src, 0, 100.0f);
if (dst != src)
{
renderer.SetBlendShapeWeight(i, dst);
}
}
}
}
}
}
BlendShapeBinding[] GetBindings(out string _maxWeightName)
{
var maxWeight = 0.0f;
var maxWeightName = "";
// weightのついたblendShapeを集める
var values= m_renderers.SelectMany(x =>
{
var mesh = x.sharedMesh;
var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(x.transform, m_target.transform);
var list = new List<BlendShapeBinding>();
if (mesh != null)
{
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var weight = x.GetBlendShapeWeight(i);
if (weight == 0)
{
continue;
}
var name = mesh.GetBlendShapeName(i);
if (weight > maxWeight)
{
maxWeightName = name;
maxWeight = weight;
}
list.Add(new BlendShapeBinding
{
Index = i,
RelativePath = relativePath,
Weight = weight
});
}
}
return list;
}).ToArray()
;
_maxWeightName = maxWeightName;
return values;
}
private void ClearBlendShape()
{
if (m_renderers == null) return;
foreach (var renderer in m_renderers)
{
var mesh = renderer.sharedMesh;
if (mesh != null)
{
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
renderer.SetBlendShapeWeight(i, 0);
}
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 0f9ca53e9d292ce48a7e6449a03734a9
timeCreated: 1517819499
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
using UnityEngine;
namespace VRM
{
public class VRMBlendShapeManipulator : MonoBehaviour
{
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 4829a7f37bfef244289c32551215632d
timeCreated: 1517403264
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace VRM
{
public class VRMBlendShapeProxy : MonoBehaviour
{
[SerializeField]
public BlendShapeAvatar BlendShapeAvatar;
Dictionary<BlendShapeKey, BlendShapeClipHandler> m_handlerMap;
public Dictionary<BlendShapeKey, BlendShapeClipHandler> CreateHandlerMap()
{
var handlerMap = new Dictionary<BlendShapeKey, BlendShapeClipHandler>(
new BlendShapeKey.CustomerEqualityComparer());
foreach (var x in BlendShapeAvatar.Clips)
{
handlerMap.Add(BlendShapeKey.CreateFrom(x), new BlendShapeClipHandler(x, transform));
;
}
return handlerMap;
}
private void Update()
{
if (BlendShapeAvatar != null)
{
if (m_handlerMap == null)
{
m_handlerMap = CreateHandlerMap();
}
}
}
public void Reload()
{
if (BlendShapeAvatar == null) return;
foreach (var kv in m_handlerMap)
{
SetValue(kv.Key, GetValue(kv.Key));
}
}
public void SetValue(BlendShapePreset key, float value)
{
SetValue(new BlendShapeKey(key), value);
}
public float GetValue(BlendShapePreset key)
{
return GetValue(new BlendShapeKey(key));
}
public void SetValue(String key, float value)
{
SetValue(new BlendShapeKey(key), value);
}
public float GetValue(String key)
{
return GetValue(new BlendShapeKey(key));
}
public void SetValue(BlendShapeKey key, float value)
{
if (m_handlerMap == null) return;
BlendShapeClipHandler handler;
if (m_handlerMap.TryGetValue(key, out handler))
{
handler.Apply(value);
}
}
public float GetValue(BlendShapeKey key)
{
if (m_handlerMap == null) return 0;
BlendShapeClipHandler handler;
if (!m_handlerMap.TryGetValue(key, out handler))
{
return 0;
}
return handler.LastValue;
}
public void ClearKeys()
{
if (m_handlerMap == null) return;
foreach(var kv in m_handlerMap)
{
kv.Value.Apply(0);
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 5b678c1df50cfb547990db24a32856da
timeCreated: 1517467747
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

9
Scripts/Editor.meta Normal file
View File

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

View File

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

View File

@@ -0,0 +1,24 @@
using NUnit.Framework;
using System.Collections.Generic;
using VRM;
public class VRMBlendShapeKeyTest
{
[Test]
public void KeyTest()
{
var key = new BlendShapeKey("Blink", BlendShapePreset.Blink);
Assert.AreEqual(key, new BlendShapeKey("blink"));
Assert.AreEqual(key, new BlendShapeKey(BlendShapePreset.Blink));
Assert.AreEqual(key, new BlendShapeKey("xxx", BlendShapePreset.Blink));
var dict = new Dictionary<BlendShapeKey, float>(new BlendShapeKey.CustomerEqualityComparer());
dict[new BlendShapeKey("xxx", BlendShapePreset.Blink)] = 1.0f;
Assert.IsTrue(dict.ContainsKey(new BlendShapeKey("blink")));
Assert.IsTrue(dict.ContainsKey(new BlendShapeKey(BlendShapePreset.Blink)));
Assert.IsTrue(dict.ContainsKey(new BlendShapeKey("xxx", BlendShapePreset.Blink)));
}
}

View File

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

View File

@@ -0,0 +1,55 @@
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public static class VRMExportUnityPackage
{
static string GetDesktop()
{
return Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "/VRM";
}
const string DATE_FORMAT = "yyyyMMdd";
const string PREFIX = "UniVRM";
static string GetPath()
{
var folder = GetDesktop();
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
//var date = DateTime.Today.ToString(DATE_FORMAT);
var path = string.Format("{0}/{1}-{2}.unitypackage",
folder,
PREFIX,
VRMVersion.VERSION
).Replace("\\", "/");
return path;
}
#if VRM_DEVELOP
[MenuItem("VRM/Export unitypackage")]
#endif
public static void CreateUnityPackage()
{
var path = GetPath();
if (File.Exists(path))
{
Debug.LogErrorFormat("{0} is already exists", path);
return;
}
AssetDatabase.ExportPackage("Assets/VRM", path, ExportPackageOptions.Recurse);
Debug.LogFormat("exported: {0}", path);
}
}
}

View File

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

View File

@@ -0,0 +1,30 @@
using System;
using UnityEngine;
using VRM;
public static class VRMMonoBehaviourComparator
{
public static bool AssertAreEquals(GameObject l, GameObject r)
{
return
AssertAreEquals<VRMMetaInformation>(l, r,
(x, y) => x[0].Equals(y[0]))
;
}
public static bool AssertAreEquals<T>(GameObject l, GameObject r, Func<T[], T[], bool> pred) where T : Component
{
var ll = l.GetComponents<T>();
var rr = r.GetComponents<T>();
if (ll.Length != rr.Length)
{
return false;
}
if (ll.Length == 0)
{
return true;
}
return pred(ll, rr);
}
}

View File

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

76
Scripts/Editor/VRMTest.cs Normal file
View File

@@ -0,0 +1,76 @@
using NUnit.Framework;
using System;
using UniGLTF;
using UnityEngine;
using VRM;
public class VRMTest
{
static GameObject CreateSimpelScene()
{
var root = new GameObject("gltfRoot").transform;
var scene = new GameObject("scene0").transform;
scene.SetParent(root, false);
scene.localPosition = new Vector3(1, 2, 3);
return root.gameObject;
}
void AssertAreEqual(Transform go, Transform other)
{
var lt = go.Traverse().GetEnumerator();
var rt = go.Traverse().GetEnumerator();
while (lt.MoveNext())
{
if (!rt.MoveNext())
{
throw new Exception("rt shorter");
}
MonoBehaviourComparator.
AssertAreEquals(lt.Current.gameObject, rt.Current.gameObject);
VRMMonoBehaviourComparator.
AssertAreEquals(lt.Current.gameObject, rt.Current.gameObject);
}
if (rt.MoveNext())
{
throw new Exception("rt longer");
}
}
[Test]
public void VRMSimpleSceneTest()
{
var go = CreateSimpelScene();
var context = new VRMImporterContext();
try
{
// export
var gltf = VRMExporter.Export(go);
using (var exporter = new gltfExporter(gltf))
{
exporter.Prepare(go);
exporter.Export();
// import
context.Json = gltf.ToJson();
Debug.LogFormat("{0}", context.Json);
gltfImporter.Import<glTF>(context, new ArraySegment<byte>());
AssertAreEqual(go.transform, context.Root.transform);
}
}
finally
{
//Debug.LogFormat("Destory, {0}", go.name);
GameObject.DestroyImmediate(go);
context.Destroy(true);
}
}
}

View File

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

10
Scripts/FirstPerson.meta Normal file
View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5a5c5355606c9da4cb354f3ae2ae4bae
folderAsset: yes
timeCreated: 1519278754
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
namespace VRM
{
public class VRMFirstPerson : MonoBehaviour
{
public const int FIRSTPERSON_ONLY_LAYER = 9;
public const int THIRDPERSON_ONLY_LAYER = 10;
[SerializeField]
public Transform FirstPersonBone;
[SerializeField]
public Vector3 FirstPersonOffset;
[Serializable]
public struct RendererFirstPersonFlags
{
public Renderer Renderer;
public FirstPersonFlag FirstPersonFlag;
public Mesh SharedMesh
{
get
{
var renderer = Renderer as SkinnedMeshRenderer;
if (renderer != null)
{
return renderer.sharedMesh;
}
var filter = Renderer.GetComponent<MeshFilter>();
if (filter != null)
{
return filter.sharedMesh;
}
return null;
}
}
}
[SerializeField]
public List<RendererFirstPersonFlags> Renderers = new List<RendererFirstPersonFlags>();
static IEnumerable<Transform> Traverse(Transform parent)
{
yield return parent;
foreach (Transform child in parent)
{
foreach (var x in Traverse(child))
{
yield return x;
}
}
}
public void CopyTo(GameObject _dst, Dictionary<Transform, Transform> map)
{
var dst = _dst.AddComponent<VRMFirstPerson>();
dst.FirstPersonBone = FirstPersonBone;
dst.FirstPersonOffset = FirstPersonOffset;
dst.Renderers = Renderers.Select(x =>
{
var renderer = map[x.Renderer.transform].GetComponent<Renderer>();
return new VRMFirstPerson.RendererFirstPersonFlags
{
Renderer = renderer,
FirstPersonFlag = x.FirstPersonFlag,
};
}).ToList();
}
public void SetDefault()
{
FirstPersonOffset = new Vector3(0, 0.06f, 0);
var animator = GetComponent<Animator>();
if (animator != null)
{
FirstPersonBone = animator.GetBoneTransform(HumanBodyBones.Head);
}
}
private void Reset()
{
TraverseRenderers();
}
public void TraverseRenderers(VRMImporterContext context = null)
{
Renderers = Traverse(transform)
.Select(x => x.GetComponent<Renderer>())
.Where(x => x != null)
.Select(x => new RendererFirstPersonFlags
{
Renderer = x,
FirstPersonFlag = context == null
? FirstPersonFlag.Auto
: GetFirstPersonFlag(context, x)
})
.ToList()
;
}
static FirstPersonFlag GetFirstPersonFlag(VRMImporterContext context, Renderer r)
{
var mesh = r.transform.GetSharedMesh();
if (mesh == null)
{
return FirstPersonFlag.Auto;
}
var index = context.Meshes.FindIndex(x => x.Mesh == mesh);
if (index == -1)
{
return FirstPersonFlag.Auto;
}
foreach(var x in context.VRM.extensions.VRM.firstPerson.meshAnnotations)
{
if (x.mesh == index)
{
return (FirstPersonFlag)Enum.Parse(typeof(FirstPersonFlag), x.firstPersonFlag, true);
}
}
return FirstPersonFlag.Auto;
}
void CreateHeadlessModel(Renderer _renderer, Transform EraseRoot)
{
var renderer = _renderer as SkinnedMeshRenderer;
if (renderer != null)
{
var go = new GameObject("_headless_" + renderer.name);
go.layer = FIRSTPERSON_ONLY_LAYER;
go.transform.SetParent(transform, false);
var m_eraseBones = renderer.bones.Select(x =>
{
var eb = new BoneMeshEraser.EraseBone
{
Bone = x,
};
if (EraseRoot != null)
{
// 首の子孫を消去
if (eb.Bone.Ancestor().Any(y => y == EraseRoot))
{
//Debug.LogFormat("erase {0}", x);
eb.Erase = true;
}
}
return eb;
})
.ToArray();
var bones = renderer.bones;
var eraseBones = m_eraseBones
.Where(x => x.Erase)
.Select(x => bones.IndexOf(x.Bone))
.ToArray();
var mesh = BoneMeshEraser.CreateErasedMesh(renderer.sharedMesh, eraseBones);
var erased = go.AddComponent<SkinnedMeshRenderer>();
erased.sharedMesh = mesh;
erased.sharedMaterials = renderer.sharedMaterials;
erased.bones = renderer.bones;
erased.rootBone = renderer.rootBone;
}
}
/// <summary>
/// 配下のモデルのレイヤー設定など
/// </summary>
public void Setup()
{
foreach (var x in Renderers)
{
switch (x.FirstPersonFlag)
{
case FirstPersonFlag.Auto:
x.Renderer.gameObject.layer = THIRDPERSON_ONLY_LAYER;
CreateHeadlessModel(x.Renderer, FirstPersonBone);
break;
case FirstPersonFlag.FirstPersonOnly:
x.Renderer.gameObject.layer = FIRSTPERSON_ONLY_LAYER;
break;
case FirstPersonFlag.ThirdPersonOnly:
x.Renderer.gameObject.layer = THIRDPERSON_ONLY_LAYER;
break;
case FirstPersonFlag.Both:
//x.Renderer.gameObject.layer = 0;
break;
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: dedba1309bdf12b42af2362f52eea134
timeCreated: 1519218333
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,91 @@
#pragma warning disable 0414, 0649
using UnityEngine;
using System.Linq;
using UnityEngine.UI;
using System;
namespace VRM
{
/// <summary>
/// ファーストパーソン向けLayer検討
///
/// * Deault LayerをFirstPersonレイヤーとして使う
/// * 9番にThirdPerson Layerを追加する
///
/// * FirstPersonCameraはCullingMaskでThirdPerson Layerを除外
/// * ThirdPersonCameraはCullingMaskでDefault Layerを除外
///
/// * それ以外のシーンオブジェクトはDefaultLayerとThirdPersonレイヤーの両方に所属するべし
/// * 首無しモデルはDefault Layerのみに所属するべし
/// * 首有りモデルはThirdPerson Layerのみに所属するべし
/// * コントローラーはDefault Layerがいいかも
/// * 鏡もDefault Layerがいいかも(カメラごとにRenderTargetを用意するのは煩雑)
/// </summary>
public class VRMFirstPersonCameraManager : MonoBehaviour
{
[Serializable]
class CameraWithRawImage
{
public Camera Camera;
public RenderTexture Texture;
public RawImage Image;
}
/// <summary>
/// FirstPerson
/// </summary>
[SerializeField]
CameraWithRawImage m_topLeft;
/// <summary>
/// ThirdPerson body
/// </summary>
[SerializeField]
CameraWithRawImage m_topRight;
/// <summary>
/// ThirdPerson head
/// </summary>
[SerializeField]
CameraWithRawImage m_bottomRight;
[SerializeField, Header("Cameras")]
Camera m_firstPersonCamera;
[SerializeField]
Camera[] m_thirdPersonCameras;
void Reset()
{
var cameras = GameObject.FindObjectsOfType<Camera>();
m_firstPersonCamera = Camera.main;
m_thirdPersonCameras = cameras.Where(x => x != m_firstPersonCamera).ToArray();
}
private void Update()
{
var halfWidth = Screen.width / 2;
var halfHeight = Screen.height / 2;
SetupRenderTarget(m_topLeft, halfWidth, halfHeight);
SetupRenderTarget(m_topRight, halfWidth, halfHeight);
SetupRenderTarget(m_bottomRight, halfWidth, halfHeight);
}
void SetupRenderTarget(CameraWithRawImage cameraWithImage, int w, int h)
{
if (cameraWithImage.Camera == null) return;
if (cameraWithImage.Image == null) return;
if (cameraWithImage.Texture == null
|| cameraWithImage.Texture.width != w
|| cameraWithImage.Texture.height != h
)
{
var texture = new RenderTexture(w, h, 16);
cameraWithImage.Texture = texture;
cameraWithImage.Camera.targetTexture = texture;
cameraWithImage.Image.texture = texture;
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 6b34fb2c039c3ce4881f04e476a2128f
timeCreated: 1519220439
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

10
Scripts/Format.meta Normal file
View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 8dc6b68ffeda9f54494eee1cd17af615
folderAsset: yes
timeCreated: 1517308531
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: f24b76911d4f68844b9d12721ba1bb06
folderAsset: yes
timeCreated: 1517308497
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UniGLTF;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public static class VRMAssetWriter
{
interface ISubAssetWriter
{
void WriteIfHas(GameObject go);
}
class SubAssetWriter<T>: ISubAssetWriter where T : UnityEngine.Object
{
HashSet<T> m_set = new HashSet<T>();
String m_assetPath;
public delegate IEnumerable<T> GetterFunc(GameObject go);
GetterFunc m_getter;
public SubAssetWriter(string assetPath, GetterFunc getter)
{
m_assetPath = assetPath;
m_getter = getter;
}
public void WriteIfHas(GameObject go)
{
foreach(var o in m_getter(go))
{
if (!m_set.Contains(o))
{
AssetDatabase.AddObjectToAsset(o, m_assetPath);
m_set.Add(o);
}
}
}
}
static IEnumerable<Mesh> GetMeshs(GameObject go)
{
var skinnedMesh = go.GetComponent<SkinnedMeshRenderer>();
if (skinnedMesh != null)
{
yield return skinnedMesh.sharedMesh;
}
var filter = go.GetComponent<MeshFilter>();
if (filter != null)
{
yield return filter.sharedMesh;
}
}
static IEnumerable<Material> GetMaterials(GameObject go)
{
var renderer = go.GetComponent<Renderer>();
if (renderer != null)
{
return renderer.sharedMaterials;
}
else
{
return Enumerable.Empty<Material>();
}
}
static IEnumerable<Texture2D> GetTextures(GameObject go)
{
foreach (var m in GetMaterials(go))
{
foreach(Texture2D x in m.GetTextures())
{
if (x != null)
{
yield return x;
}
}
}
}
static IEnumerable<Avatar> GetAvatars(GameObject go)
{
var animator = go.GetComponent<Animator>();
if(animator!=null && animator.avatar != null)
{
yield return animator.avatar;
}
}
static IEnumerable<BlendShapeClip> GetBlendShapeClips(GameObject go)
{
var proxy = go.GetComponent<VRMBlendShapeProxy>();
if (proxy != null && proxy.BlendShapeAvatar != null)
{
return proxy.BlendShapeAvatar.Clips;
}
else
{
return Enumerable.Empty<BlendShapeClip>();
}
}
static IEnumerable<BlendShapeAvatar> GetBlendShapeAvatars(GameObject go)
{
var proxy = go.GetComponent<VRMBlendShapeProxy>();
if (proxy != null && proxy.BlendShapeAvatar != null)
{
yield return proxy.BlendShapeAvatar;
}
}
static IEnumerable<UniHumanoid.AvatarDescription> GetAvatarDecriptions(GameObject go)
{
var humanoid = go.GetComponent<VRMHumanoidDescription>();
if (humanoid!=null && humanoid.Description != null)
{
yield return humanoid.Description;
}
else
{
var animator = go.GetComponent<Animator>();
if(animator!=null && animator.avatar)
{
var description= UniHumanoid.AvatarDescription.CreateFrom(animator.avatar);
if (description != null)
{
description.name = "AvatarDescription";
yield return description;
}
}
}
}
static IEnumerable<Texture2D> GetThumbnails(GameObject go)
{
var meta = go.GetComponent<VRMMetaInformation>();
if (meta != null && meta.Thumbnail != null)
{
yield return meta.Thumbnail;
}
}
static IEnumerable<UnityEngine.Object> GetSubAssets(String prefabPath)
{
return AssetDatabase.LoadAllAssetsAtPath(prefabPath);
}
public static void SaveAsPrefab(GameObject root, String path)
{
var prefabPath = path.ToUnityRelativePath();
Debug.LogFormat("SaveAsPrefab: {0}", prefabPath);
// clear subassets
if (File.Exists(prefabPath))
{
//Debug.LogFormat("Exist: {0}", m_prefabPath);
// clear subassets
foreach (var x in GetSubAssets(prefabPath))
{
if (x is Transform
|| x is GameObject)
{
continue;
}
GameObject.DestroyImmediate(x, true);
}
}
// add subassets
var writers = new ISubAssetWriter[]{
new SubAssetWriter<Texture2D>(prefabPath, GetTextures),
new SubAssetWriter<Material>(prefabPath, GetMaterials),
new SubAssetWriter<Mesh>(prefabPath, GetMeshs),
new SubAssetWriter<Avatar>(prefabPath, GetAvatars),
// VRM Objects
new SubAssetWriter<BlendShapeClip>(prefabPath, GetBlendShapeClips),
new SubAssetWriter<BlendShapeAvatar>(prefabPath, GetBlendShapeAvatars),
new SubAssetWriter<UniHumanoid.AvatarDescription>(prefabPath, GetAvatarDecriptions),
new SubAssetWriter<Texture2D>(prefabPath, GetThumbnails),
};
foreach (var x in root.transform.Traverse())
{
foreach (var writer in writers)
{
writer.WriteIfHas(x.gameObject);
}
}
///
/// create prefab, after subasset AssetDatabase.AddObjectToAsset
///
if (File.Exists(prefabPath))
{
//Debug.LogFormat("ReplacePrefab: {0}", m_prefabPath);
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
PrefabUtility.ReplacePrefab(root, prefab, ReplacePrefabOptions.ConnectToPrefab);
}
else
{
//Debug.LogFormat("CreatePrefab: {0}", m_prefabPath);
PrefabUtility.CreatePrefab(prefabPath, root, ReplacePrefabOptions.ConnectToPrefab);
}
}
}
}

View File

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

View File

@@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
namespace VRM
{
public class VRMExporter : gltfExporter
{
public VRMExporter(glTF_VRM gltf) : base(gltf)
{ }
public static glTF Export(GameObject go, string path = null)
{
var gltf = new glTF_VRM();
gltf.asset.generator = "Unity VRM Exporter";
using (var exporter = new VRMExporter(gltf))
{
_Export(gltf, exporter, go);
if (!string.IsNullOrEmpty(path))
{
exporter.WriteTo(path);
}
}
return gltf;
}
public static void _Export(glTF_VRM gltf, VRMExporter exporter, GameObject go)
{
exporter.Prepare(go);
exporter.Export();
// avatar
var animator = go.GetComponent<Animator>();
if (animator != null)
{
var humanoid = go.GetComponent<VRMHumanoidDescription>();
UniHumanoid.AvatarDescription description = null;
var nodes = go.transform.Traverse().Skip(1).ToList();
{
var isCreated = false;
if (humanoid != null)
{
description = humanoid.GetDescription(out isCreated);
}
if (description != null)
{
// use description
gltf.extensions.VRM.humanoid.Apply(description, nodes);
}
if (isCreated)
{
GameObject.DestroyImmediate(description);
}
}
{
// set humanoid bone mapping
var avatar = animator.avatar;
foreach (HumanBodyBones key in Enum.GetValues(typeof(HumanBodyBones)))
{
if (key == HumanBodyBones.LastBone)
{
break;
}
var transform = animator.GetBoneTransform(key);
if (transform != null)
{
gltf.extensions.VRM.humanoid.SetNodeIndex(key, nodes.IndexOf(transform));
}
}
}
}
// morph
var master = go.GetComponent<VRMBlendShapeProxy>();
if (master != null)
{
var avatar = master.BlendShapeAvatar;
if (avatar != null)
{
var meshes = exporter.Meshes;
foreach (var x in avatar.Clips)
{
gltf.extensions.VRM.blendShapeMaster.Add(x, exporter.Copy.transform, meshes);
}
}
}
// secondary
var secondary = exporter.Copy.transform.Find("secondary");
if (secondary == null)
{
secondary = exporter.Copy.transform;
}
var colliders = new List<VRMSpringBoneColliderGroup>();
foreach (var vrmColliderGroup in exporter.Copy.transform.Traverse().Select(x => x.GetComponent<VRMSpringBoneColliderGroup>()).Where(x => x != null))
{
colliders.Add(vrmColliderGroup);
var colliderGroup = new glTF_VRM_SecondaryAnimationColliderGroup
{
node = exporter.Nodes.IndexOf(vrmColliderGroup.transform)
};
colliderGroup.colliders = vrmColliderGroup.Colliders.Select(x =>
{
return new glTF_VRM_SecondaryAnimationCollider
{
offset = x.Offset,
radius = x.Radius,
};
}).ToList();
gltf.extensions.VRM.secondaryAnimation.colliderGroups.Add(colliderGroup);
}
foreach (var spring in secondary.GetComponents<VRMSpringBone>())
{
gltf.extensions.VRM.secondaryAnimation.boneGroups.Add(new glTF_VRM_SecondaryAnimationGroup
{
comment = spring.m_comment,
center = exporter.Nodes.IndexOf(spring.m_center),
dragForce = spring.m_dragForce,
gravityDir = spring.m_gravityDir,
gravityPower = spring.m_gravityPower,
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;
})
.ToArray(),
bones = spring.RootBones.Select(x => exporter.Nodes.IndexOf(x)).ToArray(),
});
}
// meta
var meta = exporter.Copy.GetComponent<VRMMetaInformation>();
if (meta != null)
{
gltf.extensions.VRM.meta.author = meta.Author;
gltf.extensions.VRM.meta.contactInformation = meta.ContactInformation;
gltf.extensions.VRM.meta.title = meta.Title;
if (meta.Thumbnail != null)
{
gltf.extensions.VRM.meta.texture = gltfExporter.ExportTexture(gltf, gltf.buffers.Count - 1, meta.Thumbnail);
}
gltf.extensions.VRM.meta.licenseType = meta.LicenseType;
gltf.extensions.VRM.meta.otherLicenseUrl = meta.OtherLicenseUrl;
gltf.extensions.VRM.meta.reference = meta.Reference;
}
// firstPerson
var firstPerson = exporter.Copy.GetComponent<VRMFirstPerson>();
if (firstPerson != null)
{
if (firstPerson.FirstPersonBone != null)
{
gltf.extensions.VRM.firstPerson.firstPersonBone = exporter.Nodes.IndexOf(firstPerson.FirstPersonBone);
gltf.extensions.VRM.firstPerson.firstPersonBoneOffset = firstPerson.FirstPersonOffset;
gltf.extensions.VRM.firstPerson.meshAnnotations = firstPerson.Renderers.Select(x => new glTF_VRM_MeshAnnotation
{
mesh = exporter.Meshes.IndexOf(x.SharedMesh),
firstPersonFlag = x.FirstPersonFlag.ToString(),
}).ToList();
}
// lookAt
{
var lookAtHead = exporter.Copy.GetComponent<VRMLookAtHead>();
var lookAt = exporter.Copy.GetComponent<VRMLookAt>();
if (lookAtHead != null)
{
var boneApplyer = exporter.Copy.GetComponent<VRMLookAtBoneApplyer>();
var blendShapeApplyer = exporter.Copy.GetComponent<VRMLookAtBlendShapeApplyer>();
if (boneApplyer != null)
{
gltf.extensions.VRM.firstPerson.lookAtHorizontalInner.Apply(boneApplyer.HorizontalInner);
gltf.extensions.VRM.firstPerson.lookAtHorizontalOuter.Apply(boneApplyer.HorizontalOuter);
gltf.extensions.VRM.firstPerson.lookAtVerticalDown.Apply(boneApplyer.VerticalDown);
gltf.extensions.VRM.firstPerson.lookAtVerticalUp.Apply(boneApplyer.VerticalUp);
}
else if (blendShapeApplyer != null)
{
// ToDo
throw new NotImplementedException();
}
}
else if (lookAt != null)
{
gltf.extensions.VRM.firstPerson.lookAtHorizontalInner.Apply(lookAt.HorizontalInner);
gltf.extensions.VRM.firstPerson.lookAtHorizontalOuter.Apply(lookAt.HorizontalOuter);
gltf.extensions.VRM.firstPerson.lookAtVerticalDown.Apply(lookAt.VerticalDown);
gltf.extensions.VRM.firstPerson.lookAtVerticalUp.Apply(lookAt.VerticalUp);
}
}
}
// materials
foreach (var m in exporter.Materials)
{
gltf.extensions.VRM.materialProperties.Add(glTF_VRM_Material.CreateFromMaterial(m, exporter.Textures));
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 73f9ea5f415aa1d449d6cd63aeaccb67
timeCreated: 1516612766
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,124 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UniGLTF;
using System.IO;
namespace VRM
{
public class VRMExporterWizard : ScriptableWizard
{
const string EXTENSION = ".vrm";
public GameObject Target;
public bool PoseNormalization;
public static void CreateWizard()
{
var wiz = ScriptableWizard.DisplayWizard<VRMExporterWizard>(
"VRM Exporter", "Export");
wiz.Target = Selection.activeObject as GameObject;
// update checkbox
var desc = wiz.Target.GetComponent<VRMHumanoidDescription>();
if (desc == null)
{
wiz.PoseNormalization = true;
}
}
static string m_dir = Path.GetFullPath(Application.dataPath);
void OnWizardCreate()
{
// save dialog
Debug.LogFormat("OnWizardCreate: {0}", m_dir);
var path = EditorUtility.SaveFilePanel(
"Save vrm",
m_dir,
Target.name + EXTENSION,
EXTENSION.Substring(1));
if (string.IsNullOrEmpty(path))
{
return;
}
m_dir = Path.GetDirectoryName(path);
// export
var target = Target;
if (PoseNormalization)
{
Undo.RecordObjects(Target.transform.Traverse().ToArray(), "before normalize");
var map = new Dictionary<Transform, Transform>();
target = VRM.BoneNormalizer.Execute(Target, map);
VRMHumanoidNorimalizerMenu.CopyVRMComponents(Target, target, map);
Undo.PerformUndo();
}
VRMExporter.Export(target, path);
if (Target!=target)
{
GameObject.DestroyImmediate(target);
}
if (path.StartsWithUnityAssetPath())
{
AssetDatabase.ImportAsset(path.ToUnityRelativePath());
}
}
void OnWizardUpdate()
{
helpString = @"select humanoid root(require Animator with valid humanoid avatar).
";
}
}
public static class VRMExporterMenu
{
const string CONVERT_HUMANOID_KEY = "VRM/export humanoid";
[MenuItem(CONVERT_HUMANOID_KEY, true, 1)]
private static bool ExportValidate()
{
var root = Selection.activeObject as GameObject;
if (root == null)
{
return false;
}
var animator = root.GetComponent<Animator>();
if (animator == null)
{
return false;
}
var avatar = animator.avatar;
if (avatar == null)
{
return false;
}
if (!avatar.isValid)
{
return false;
}
if (!avatar.isHuman)
{
return false;
}
return true;
}
[MenuItem(CONVERT_HUMANOID_KEY, false, 1)]
private static void ExportFromMenu()
{
VRMExporterWizard.CreateWizard();
}
}
}

View File

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

View File

@@ -0,0 +1,162 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UniGLTF;
namespace VRM
{
public static class VRMHumanoidNorimalizerMenu
{
const string MENU_KEY = "VRM/create normalized humanoid";
[MenuItem(MENU_KEY, true, 1)]
private static bool ExportValidate()
{
var root=Selection.activeObject as GameObject;
if (root == null)
{
return false;
}
var animator = root.GetComponent<Animator>();
if (animator == null)
{
return false;
}
var avatar = animator.avatar;
if (avatar == null)
{
return false;
}
if (!avatar.isValid) {
return false;
}
if (!avatar.isHuman)
{
return false;
}
return true;
}
[MenuItem(MENU_KEY, false, 1)]
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);
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 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 secondary.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
var src = go.GetComponent<VRMMetaInformation>();
if (src != null)
{
src.CopyTo(root);
}
}
{
// 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;
}
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,54 @@
#if USE_VRMIMPORTER_MENU
using System.IO;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public static class VRMImporterMenu
{
[MenuItem("VRM/import from external file")]
static void ImportMenu()
{
var path = EditorUtility.OpenFilePanel("open vrm", "", "vrm");
if (string.IsNullOrEmpty(path))
{
return;
}
var ext = Path.GetExtension(path).ToLower();
if (ext != ".vrm")
{
return;
}
if (path.StartsWithUnityAssetPath())
{
Debug.LogWarning("vrm in AssetFolder is imported automatically");
Selection.activeObject = AssetDatabase.LoadAssetAtPath<GameObject>(path.Replace(".vrm", ".prefab").ToUnityRelativePath());
return;
}
var root = VRMImporter.LoadFromPath(path);
if (!EditorApplication.isPlaying)
{
// save root as Asset
var prefabPath = EditorUtility.SaveFilePanel("save vrm prefab",
"Assets",
Path.GetFileNameWithoutExtension(path) + ".prefab",
"prefab"
);
if (!string.IsNullOrEmpty(prefabPath))
{
VRMAssetWriter.SaveAsPrefab(root, prefabPath);
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath.ToUnityRelativePath());
Selection.activeObject = prefab;
}
}
}
}
}
#endif

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: da5decfeae53a6444977caf85d09bc88
timeCreated: 1517153624
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,46 @@
using System.IO;
using UnityEditor;
namespace VRM
{
public static class VRMVersionMenu
{
const string path = "Assets/VRM/Scripts/Format/VRMVersion.cs";
const string template = @"
namespace VRM
{{
public static class VRMVersion
{{
public const int MAJOR = {0};
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)]
#endif
public static void IncrementVersion()
{
var source = string.Format(template, VRMVersion.MAJOR, VRMVersion.MINOR + 1);
File.WriteAllText(path, source);
AssetDatabase.Refresh();
}
#if VRM_DEVELOP
[MenuItem(VRMVersion.DecrementMenuName)]
#endif
public static void DecrementVersion()
{
var source = string.Format(template, VRMVersion.MAJOR, VRMVersion.MINOR - 1);
File.WriteAllText(path, source);
AssetDatabase.Refresh();
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 0c69abb555d67f647a7d3175e35e37d8
timeCreated: 1522130550
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public class vrmAssetPostprocessor : AssetPostprocessor
{
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (string path in importedAssets)
{
var ext = Path.GetExtension(path).ToLower();
if (ext == ".vrm")
{
VRMImporterContext context = null;
try
{
context=VRMImporter.LoadFromPath(path);
/*
var prefabPath = String.Format("{0}/{1}.prefab",
Path.GetDirectoryName(path),
Path.GetFileNameWithoutExtension(path));
VRMAssetWriter.SaveAsPrefab(context.Root, prefabPath);
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath.ToUnityRelativePath());
Selection.activeObject = prefab;
*/
context.SaveAsAsset();
context.Destroy(false);
}
catch(Exception ex)
{
Debug.LogError(ex);
if (context != null)
{
context.Destroy(true);
}
}
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,15 @@
using System;
namespace VRM
{
class VRMException : Exception
{
public VRMException()
{ }
public VRMException(string msg) : base(msg)
{ }
public VRMException(string msg, params object[] args) : base(string.Format(msg, args))
{ }
}
}

View File

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

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using UniGLTF;
namespace VRM
{
[Serializable]
public class glTF_VRM_extensions : JsonSerializableBase
{
public string version = VRMVersion.VERSION;
public glTF_VRM_Meta meta = new glTF_VRM_Meta();
public glTF_VRM_Humanoid humanoid = new glTF_VRM_Humanoid();
public glTF_VRM_Firstperson firstPerson = new glTF_VRM_Firstperson();
public glTF_VRM_BlendShapeMaster blendShapeMaster = new glTF_VRM_BlendShapeMaster();
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)
{
f.KeyValue(() => version);
f.KeyValue(() => meta);
f.KeyValue(() => humanoid);
f.KeyValue(() => firstPerson);
f.KeyValue(() => blendShapeMaster);
f.KeyValue(() => secondaryAnimation);
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(() => extensions);
base.SerializeMembers(f);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 20eca00211c61d047b586f852b818b8f
timeCreated: 1517308526
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,661 @@
using System;
using System.IO;
using System.Linq;
using UnityEngine;
using UniGLTF;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using UniTask;
namespace VRM
{
public static class VRMImporter
{
const string HUMANOID_KEY = "humanoid";
const string MATERIAL_KEY = "materialProperties";
public static VRMImporterContext LoadFromPath(string path)
{
return LoadFromBytes(File.ReadAllBytes(path), path);
}
public static VRMImporterContext LoadFromBytes(byte[] bytes, string path=null)
{
var context = new VRMImporterContext();
if (!string.IsNullOrEmpty(path))
{
context.Path = path;
}
var chunks = glbImporter.ParseGlbChanks(bytes);
if (chunks.Count != 2)
{
throw new Exception("unknown chunk count: " + chunks.Count);
}
if (chunks[0].ChunkType != GlbChunkType.JSON)
{
throw new Exception("chunk 0 is not JSON");
}
if (chunks[1].ChunkType != GlbChunkType.BIN)
{
throw new Exception("chunk 1 is not BIN");
}
var jsonBytes = chunks[0].Bytes;
var json = Encoding.UTF8.GetString(jsonBytes.Array, jsonBytes.Offset, jsonBytes.Count);
context.Json = json;
context.CreateMaterial = VRMImporter.GetMaterialFunc(glTF_VRM_Material.Parse(json));
gltfImporter.Import<glTF_VRM>(context, chunks[1].Bytes);
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);
}
OnLoadModel(context);
context.ShowMeshes();
return context;
}
public static CreateMaterialFunc GetMaterialFunc(List<glTF_VRM_Material> materials)
{
var fallback = gltfImporter.CreateMaterialFuncFromShader(Shader.Find("Standard"));
if (materials == null && materials.Count == 0)
{
return fallback;
}
return (ctx, i) =>
{
var item = materials[i];
var shaderName = item.shader;
var shader = Shader.Find(shaderName);
if (shader == null)
{
return fallback(ctx, i);
}
else
{
var material = new Material(shader);
material.name = item.name;
material.renderQueue = item.renderQueue;
foreach (var kv in item.floatProperties)
{
material.SetFloat(kv.Key, kv.Value);
}
foreach (var kv in item.vectorProperties)
{
var v = new Vector4(kv.Value[0], kv.Value[1], kv.Value[2], kv.Value[3]);
material.SetVector(kv.Key, v);
}
foreach (var kv in item.textureProperties)
{
material.SetTexture(kv.Key, ctx.Textures[kv.Value].Texture);
}
foreach (var kv in item.keywordMap)
{
if (kv.Value)
{
material.EnableKeyword(kv.Key);
}
else
{
material.DisableKeyword(kv.Key);
}
}
foreach (var kv in item.tagMap)
{
material.SetOverrideTag(kv.Key, kv.Value);
}
return material;
}
};
}
#region OnLoad
public static Unit OnLoadModel(VRMImporterContext context)
{
try
{
LoadHumanoidObsolete(context);
Debug.LogWarning("LoadHumanoidObsolete");
}
catch (Exception ex)
{
LoadHumanoid(context);
}
LoadBlendShapeMaster(context);
LoadSecondaryMotions(context);
LoadFirstPerson(context);
LoadMeta(context);
return Unit.Default;
}
static void LoadMeta(VRMImporterContext context)
{
var meta = context.Root.AddComponent<VRMMetaInformation>();
var gltfMeta = context.VRM.extensions.VRM.meta;
meta.Author = gltfMeta.author;
meta.ContactInformation = gltfMeta.contactInformation;
meta.Title = gltfMeta.title;
if (gltfMeta.texture != -1)
{
meta.Thumbnail = context.Textures[gltfMeta.texture].Texture;
}
else
{
// 作る
var lookAt = context.Root.GetComponent<VRMLookAtHead>();
var thumbnail = lookAt.CreateThumbnail();
thumbnail.name = "thumbnail";
meta.Thumbnail = thumbnail;
//context.Textures.Add(new TextureItem(thumbnail));
}
meta.LicenseType = gltfMeta.licenseType;
meta.OtherLicenseUrl = gltfMeta.otherLicenseUrl;
meta.Reference = gltfMeta.reference;
}
static void LoadFirstPerson(VRMImporterContext context)
{
var firstPerson = context.Root.AddComponent<VRMFirstPerson>();
var gltfFirstPerson = context.VRM.extensions.VRM.firstPerson;
if (gltfFirstPerson.firstPersonBone != -1)
{
firstPerson.FirstPersonBone = context.Nodes[gltfFirstPerson.firstPersonBone];
firstPerson.FirstPersonOffset = gltfFirstPerson.firstPersonBoneOffset;
}
else
{
// fallback
firstPerson.SetDefault();
}
firstPerson.TraverseRenderers(context);
// LookAt
var lookAtHead = context.Root.AddComponent<VRMLookAtHead>();
var lookAt = context.Root.AddComponent<VRMLookAtBoneApplyer>();
lookAt.HorizontalInner.Apply(gltfFirstPerson.lookAtHorizontalInner);
lookAt.HorizontalOuter.Apply(gltfFirstPerson.lookAtHorizontalOuter);
lookAt.VerticalDown.Apply(gltfFirstPerson.lookAtVerticalDown);
lookAt.VerticalUp.Apply(gltfFirstPerson.lookAtVerticalUp);
//lookAt.GetBones();
}
static void LoadSecondaryMotions(VRMImporterContext context)
{
var secondary = context.Root.transform.Find("secondary");
if (secondary == null)
{
secondary = new GameObject("secondary").transform;
secondary.SetParent(context.Root.transform, false);
}
var secondaryAnimation = context.VRM.extensions.VRM.secondaryAnimation;
var colliders = new List<VRMSpringBoneColliderGroup>();
foreach (var colliderGroup in secondaryAnimation.colliderGroups)
{
var vrmGroup = context.Nodes[colliderGroup.node].gameObject.AddComponent<VRMSpringBoneColliderGroup>();
vrmGroup.Colliders = colliderGroup.colliders.Select(x =>
{
return new VRMSpringBoneColliderGroup.SphereCollider
{
Offset = x.offset,
Radius = x.radius
};
}).ToArray();
colliders.Add(vrmGroup);
}
if (secondaryAnimation.boneGroups.Count > 0)
{
foreach (var boneGroup in secondaryAnimation.boneGroups)
{
var vrmBoneGroup = secondary.gameObject.AddComponent<VRMSpringBone>();
if (boneGroup.center != -1)
{
vrmBoneGroup.m_center = context.Nodes[boneGroup.center];
}
vrmBoneGroup.m_comment = boneGroup.comment;
vrmBoneGroup.m_dragForce = boneGroup.dragForce;
vrmBoneGroup.m_gravityDir = boneGroup.gravityDir;
vrmBoneGroup.m_gravityPower = boneGroup.gravityPower;
vrmBoneGroup.m_hitRadius = boneGroup.hitRadius;
vrmBoneGroup.m_stiffnessForce = boneGroup.stiffiness;
if (boneGroup.colliderGroups != null && boneGroup.colliderGroups.Any())
{
vrmBoneGroup.ColliderGroups = boneGroup.colliderGroups.Select(x => colliders[x]).ToArray();
}
vrmBoneGroup.RootBones = boneGroup.bones.Select(x => context.Nodes[x]).ToList();
}
}
else
{
secondary.gameObject.AddComponent<VRMSpringBone>();
}
}
static void LoadBlendShapeMaster(VRMImporterContext context)
{
context.BlendShapeAvatar = ScriptableObject.CreateInstance<BlendShapeAvatar>();
context.BlendShapeAvatar.name = "BlendShape";
var blendShapeList = context.VRM.extensions.VRM.blendShapeMaster.blendShapeGroups;
if (blendShapeList != null && blendShapeList.Count > 0)
{
foreach (var x in blendShapeList)
{
context.BlendShapeAvatar.Clips.Add(LoadBlendShapeBind(context, x));
}
}
else
{
// set default
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.Neutral));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.A));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.I));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.U));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.E));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.O));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.Blink));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.Joy));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.Angry));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.Sorrow));
context.BlendShapeAvatar.Clips.Add(BlendShapeClip.Create(BlendShapePreset.Fun));
}
var proxy = context.Root.AddComponent<VRMBlendShapeProxy>();
context.BlendShapeAvatar.CreateDefault();
proxy.BlendShapeAvatar = context.BlendShapeAvatar;
}
private static BlendShapeClip LoadBlendShapeBind(VRMImporterContext context,
glTF_VRM_BlendShapeGroup group)
{
var asset = ScriptableObject.CreateInstance<BlendShapeClip>();
var groupName = group.name;
var prefix = "BlendShape.";
while (groupName.StartsWith(prefix))
{
groupName = groupName.Substring(prefix.Length);
}
asset.name = "BlendShape." + groupName;
if (group != null)
{
asset.BlendShapeName = groupName;
asset.Preset = group.presetName.ToBlendShapePreset();
if (asset.Preset == BlendShapePreset.Unknown)
{
// fallback
asset.Preset = group.name.ToBlendShapePreset();
}
asset.Values = group.binds.Select(x =>
{
var mesh = context.Meshes[x.mesh].Mesh;
var node = context.Root.transform.Traverse().First(y => y.GetSharedMesh() == mesh);
var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(node, context.Root.transform);
return new BlendShapeBinding
{
//Name = name,
RelativePath = relativePath,
Index = x.index,
Weight = x.weight,
};
})
.ToArray();
}
return asset;
}
static String ToHumanBoneName(HumanBodyBones b)
{
foreach (var x in HumanTrait.BoneName)
{
if (x.Replace(" ", "") == b.ToString())
{
return x;
}
}
throw new KeyNotFoundException();
}
static SkeletonBone ToSkeletonBone(Transform t)
{
var sb = new SkeletonBone();
sb.name = t.name;
sb.position = t.localPosition;
sb.rotation = t.localRotation;
sb.scale = t.localScale;
return sb;
}
[Obsolete]
private static void LoadHumanoidObsolete(VRMImporterContext context)
{
var parsed = context.Json.ParseAsJson()["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() })
.Where(x => x.Index != -1)
.Select(x =>
{
var humanBone = (HumanBodyBones)Enum.Parse(typeof(HumanBodyBones), x.Key.ToUpperCamelCase());
var hb = new HumanBone
{
boneName = context.Nodes[x.Index].name,
humanName = ToHumanBoneName(humanBone)
};
hb.limit.useDefaultValues = true;
return hb;
}).ToArray(),
skeleton = skeleton,
lowerArmTwist = 0.5f,
upperArmTwist = 0.5f,
upperLegTwist = 0.5f,
lowerLegTwist = 0.5f,
armStretch = 0.05f,
legStretch = 0.05f,
feetSpacing = 0.0f,
};
context.HumanoidAvatar = AvatarBuilder.BuildHumanAvatar(context.Root, description);
context.HumanoidAvatar.name = Path.GetFileNameWithoutExtension(context.Path);
context.AvatarDescription = UniHumanoid.AvatarDescription.CreateFrom(description);
context.AvatarDescription.name = "AvatarDescription";
var humanoid = context.Root.AddComponent<VRMHumanoidDescription>();
humanoid.Avatar = context.HumanoidAvatar;
humanoid.Description = context.AvatarDescription;
var animator = context.Root.GetComponent<Animator>();
if (animator == null)
{
animator = context.Root.AddComponent<Animator>();
}
animator.avatar = context.HumanoidAvatar;
}
private static void LoadHumanoid(VRMImporterContext context)
{
context.AvatarDescription = context.VRM.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);
var humanoid = context.Root.AddComponent<VRMHumanoidDescription>();
humanoid.Avatar = context.HumanoidAvatar;
humanoid.Description = context.AvatarDescription;
var animator = context.Root.GetComponent<Animator>();
if (animator == null)
{
animator = context.Root.AddComponent<Animator>();
}
animator.avatar = context.HumanoidAvatar;
}
#endregion
#region LoadVrmAsync
static IEnumerator LoadTextures(VRMImporterContext context)
{
foreach (var gltfTexture in context.GLTF.textures)
{
var x = UniGLTF.gltfImporter.ImportTexture(context.GLTF, gltfTexture.source);
context.Textures.Add(x);
yield return null;
}
}
static IEnumerator LoadMaterials(VRMImporterContext context)
{
if (context.GLTF.materials == null || !context.GLTF.materials.Any())
{
context.Materials.Add(context.CreateMaterial(context, 0));
}
else
{
for (int i = 0; i < context.GLTF.materials.Count; ++i)
{
context.Materials.Add(context.CreateMaterial(context, i));
yield return null;
}
}
}
static IEnumerator LoadMeshes(VRMImporterContext context)
{
for (int i = 0; i < context.GLTF.meshes.Count; ++i)
{
var meshWithMaterials = gltfImporter.ImportMesh(context, i);
var mesh = meshWithMaterials.Mesh;
if (string.IsNullOrEmpty(mesh.name))
{
mesh.name = string.Format("UniGLTF import#{0}", i);
}
context.Meshes.Add(meshWithMaterials);
yield return null;
}
}
static IEnumerator LoadNodes(VRMImporterContext context)
{
foreach (var x in context.GLTF.nodes)
{
context.Nodes.Add(gltfImporter.ImportNode(x).transform);
yield return null;
}
}
static IEnumerator BuildHierarchy(VRMImporterContext context)
{
var nodes = new List<gltfImporter.TransformWithSkin>();
for (int i = 0; i < context.Nodes.Count; ++i)
{
nodes.Add(gltfImporter.BuildHierarchy(context, i));
yield return null;
}
gltfImporter.FixCoordinate(context, nodes);
// skinning
for (int i = 0; i < nodes.Count; ++i)
{
gltfImporter.SetupSkinning(context, nodes, i);
}
// connect root
context.Root = new GameObject("_root_");
foreach (var x in context.GLTF.rootnodes)
{
var t = nodes[x].Transform;
t.SetParent(context.Root.transform, false);
}
}
public static void LoadVrmAsync(Byte[] bytes, Action<GameObject> onLoaded)
{
var ctx = new VRMImporterContext();
LoadVrmAsync(ctx, bytes, onLoaded);
}
public static void LoadVrmAsync(string path, Action<GameObject> onLoaded)
{
var ctx = new VRMImporterContext
{
Path = path,
};
var schedule = Schedulable.Create();
schedule.AddTask(MainThreadDispatcher.Instance.ThreadScheduler, () =>
{
return File.ReadAllBytes(path);
})
.Subscribe(MainThreadDispatcher.Instance.UnityScheduler,
bytes =>
{
LoadVrmAsync(ctx, bytes, onLoaded);
},
ex =>
{
ctx.Destroy(true);
Debug.LogErrorFormat("Error in scheduler: {0}", ex);
})
;
}
public static void LoadVrmAsync(VRMImporterContext ctx, Byte[] bytes, Action<GameObject> onLoaded)
{
var schedule = Schedulable.Create();
schedule
.AddTask(MainThreadDispatcher.Instance.ThreadScheduler, () =>
{
return glbImporter.ParseGlbChanks(bytes);
})
.ContinueWith(MainThreadDispatcher.Instance.ThreadScheduler, chunks =>
{
if (chunks.Count != 2)
{
throw new Exception("unknown chunk count: " + chunks.Count);
}
if (chunks[0].ChunkType != GlbChunkType.JSON)
{
throw new Exception("chunk 0 is not JSON");
}
if (chunks[1].ChunkType != GlbChunkType.BIN)
{
throw new Exception("chunk 1 is not BIN");
}
var jsonBytes = chunks[0].Bytes;
ctx.Json = Encoding.UTF8.GetString(jsonBytes.Array, jsonBytes.Offset, jsonBytes.Count);
//var parsed = json.ParseAsJson();
ctx.GLTF = JsonUtility.FromJson<glTF_VRM>(ctx.Json);
if (ctx.GLTF.asset.version != "2.0")
{
throw new VRMException("unknown gltf version {0}", ctx.GLTF.asset.version);
}
ctx.GLTF.baseDir = Path.GetDirectoryName(ctx.Path);
foreach (var buffer in ctx.GLTF.buffers)
{
buffer.OpenStorage(ctx.GLTF.baseDir, chunks[1].Bytes);
}
return Unit.Default;
})
.Subscribe(MainThreadDispatcher.Instance.UnityScheduler,
_ =>
{
BuildAsync(ctx, onLoaded);
},
ex =>
{
ctx.Destroy(true);
Debug.LogErrorFormat("Error in scheduler: {0}", ex);
})
;
}
static void BuildAsync(VRMImporterContext ctx, Action<GameObject> onLoaded)
{
var schedulable = Schedulable.Create();
schedulable
.AddTask(MainThreadDispatcher.Instance.ThreadScheduler, () =>
{
return glTF_VRM_Material.Parse(ctx.Json);
})
.ContinueWith(MainThreadDispatcher.Instance.UnityScheduler, x =>
{
// material function
ctx.CreateMaterial = VRMImporter.GetMaterialFunc(x);
})
.OnExecute(MainThreadDispatcher.Instance.UnityScheduler, parent =>
{
// textures
for (int i = 0; i < ctx.GLTF.textures.Count; ++i)
{
var index = i;
parent.AddTask(MainThreadDispatcher.Instance.UnityScheduler,
() => gltfImporter.ImportTexture(ctx.GLTF, index))
.ContinueWith(MainThreadDispatcher.Instance.ThreadScheduler, x => ctx.Textures.Add(x));
}
})
.ContinueWithCoroutine(MainThreadDispatcher.Instance.UnityScheduler, () => LoadMaterials(ctx))
.OnExecute(MainThreadDispatcher.Instance.UnityScheduler, parent =>
{
// meshes
for (int i = 0; i < ctx.GLTF.meshes.Count; ++i)
{
var index = i;
parent.AddTask(MainThreadDispatcher.Instance.ThreadScheduler,
() => gltfImporter.ReadMesh(ctx, index))
.ContinueWith(MainThreadDispatcher.Instance.UnityScheduler, x => gltfImporter.BuildMesh(ctx, x))
.ContinueWith(MainThreadDispatcher.Instance.ThreadScheduler, x => ctx.Meshes.Add(x))
;
}
})
.ContinueWithCoroutine(MainThreadDispatcher.Instance.UnityScheduler, () => LoadNodes(ctx))
.ContinueWithCoroutine(MainThreadDispatcher.Instance.UnityScheduler, () => BuildHierarchy(ctx))
.ContinueWith(MainThreadDispatcher.Instance.UnityScheduler, _ => VRMImporter.OnLoadModel(ctx))
.Subscribe(MainThreadDispatcher.Instance.UnityScheduler,
_ =>
{
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.ShowMeshes();
onLoaded(ctx.Root);
}, ex =>
{
Debug.LogError(ex);
})
;
}
#endregion
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 7ec67bcfca2bdcc4484bcbdd1efe52f9
timeCreated: 1517153624
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
using System.Collections.Generic;
using UniGLTF;
using UnityEngine;
namespace VRM
{
public class VRMImporterContext : ImporterContext
{
public UniHumanoid.AvatarDescription AvatarDescription;
public Avatar HumanoidAvatar;
public BlendShapeAvatar BlendShapeAvatar;
public glTF_VRM VRM
{
get
{
return (glTF_VRM)GLTF;
}
}
#if UNITY_EDITOR
protected override IEnumerable<Object> ObjectsForSubAsset()
{
foreach (var x in base.ObjectsForSubAsset())
{
yield return x;
}
yield return AvatarDescription;
yield return HumanoidAvatar;
yield return BlendShapeAvatar;
if (BlendShapeAvatar != null && BlendShapeAvatar.Clips != null)
{
foreach (var x in BlendShapeAvatar.Clips)
{
yield return x;
}
}
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,14 @@
namespace VRM
{
public static class VRMVersion
{
public const int MAJOR = 0;
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";
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 93c46c8b64555f14bada9f5bb0b7761a
timeCreated: 1522130257
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,146 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UniGLTF;
using UnityEngine;
namespace VRM
{
/// <summary>
/// BlendShapeBind
/// </summary>
[Serializable]
public class glTF_VRM_BlendShapeBind : UniGLTF.JsonSerializableBase
{
public int mesh = -1;
public int index = -1;
public float weight = 0;
protected override void SerializeMembers(UniGLTF.JsonFormatter f)
{
f.KeyValue(() => mesh);
f.KeyValue(() => index);
f.KeyValue(() => weight);
}
public static glTF_VRM_BlendShapeBind Cerate(Transform root, List<Mesh> meshes, BlendShapeBinding binding)
{
var transform = UniGLTF.UnityExtensions.GetFromPath(root.transform, binding.RelativePath);
var renderer = transform.GetComponent<SkinnedMeshRenderer>();
var mesh = renderer.sharedMesh;
var meshIndex = meshes.IndexOf(mesh);
return new glTF_VRM_BlendShapeBind
{
mesh = meshIndex,
index = binding.Index,
weight = binding.Weight,
};
}
}
public enum BlendShapePreset
{
Unknown,
Neutral,
A,
I,
U,
E,
O,
Blink,
// 喜怒哀楽
Joy,
Angry,
Sorrow,
Fun,
// LookAt
LookUp,
LookDown,
LookLeft,
LookRight,
Blink_L,
Blink_R,
}
public static class BlendShpaePresetExtensions
{
/*
static string ToCamel(this string src)
{
if (string.IsNullOrEmpty(src)) return string.Empty;
return src.Substring(0, 1).ToUpper() + src.Substring(1);
}
*/
public static BlendShapePreset ToBlendShapePreset(this string preset)
{
try
{
return (BlendShapePreset)Enum.Parse(typeof(BlendShapePreset), preset, true);
}
catch (Exception)
{
return default(BlendShapePreset);
}
}
}
[Serializable]
public class glTF_VRM_BlendShapeGroup : UniGLTF.JsonSerializableBase
{
public string name;
public string presetName;
[Obsolete]
public string extendedName;
public List<glTF_VRM_BlendShapeBind> binds = new List<glTF_VRM_BlendShapeBind>();
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => name);
f.KeyValue(() => presetName);
/*
if (name == "extended")
{
f.KeyValue(() => extendedName);
}
f.Key("flags"); f.Value((int)flags);
*/
f.KeyValue(() => binds);
}
}
[Serializable]
public class glTF_VRM_BlendShapeMaster : UniGLTF.JsonSerializableBase
{
public List<glTF_VRM_BlendShapeGroup> blendShapeGroups = new List<glTF_VRM_BlendShapeGroup>();
public void Add(BlendShapeClip clip, Transform transform, List<Mesh> meshes)
{
var list = new List<glTF_VRM_BlendShapeBind>();
if (clip.Values != null)
{
list.AddRange(clip.Values.Select(y => glTF_VRM_BlendShapeBind.Cerate(transform, meshes.ToList(), y)));
}
var group = new glTF_VRM_BlendShapeGroup
{
name = clip.BlendShapeName,
presetName = clip.Preset.ToString().ToLower(),
binds = list,
};
blendShapeGroups.Add(group);
}
protected override void SerializeMembers(UniGLTF.JsonFormatter f)
{
f.KeyValue(() => blendShapeGroups);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 73f7fb3db66d55b4684333583a685757
timeCreated: 1517549334
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,85 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UniGLTF;
using UnityEngine;
namespace VRM
{
[Serializable]
public class glTF_VRM_DegreeMap : UniGLTF.JsonSerializableBase
{
// time, value, inTangent, outTangent
public float[] curve;
public float xRange = 90.0f;
public float yRange = 10.0f;
protected override void SerializeMembers(JsonFormatter f)
{
if (curve != null)
{
f.KeyValue(() => curve);
}
f.KeyValue(() => xRange);
f.KeyValue(() => yRange);
}
public void Apply(CurveMapper mapper)
{
curve = mapper.Curve.keys.SelectMany(x => new float[] { x.time, x.value, x.inTangent, x.outTangent }).ToArray();
xRange = mapper.CurveXRangeDegree;
yRange = mapper.CurveYRangeDegree;
}
}
public enum FirstPersonFlag
{
Auto, // Create headlessModel
Both, // Default layer
ThirdPersonOnly,
FirstPersonOnly,
}
[Serializable]
public class glTF_VRM_MeshAnnotation : JsonSerializableBase
{
public int mesh;
public string firstPersonFlag;
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => mesh);
f.KeyValue(() => firstPersonFlag);
}
}
[Serializable]
public class glTF_VRM_Firstperson : UniGLTF.JsonSerializableBase
{
public int firstPersonBone = -1;
public Vector3 firstPersonBoneOffset;
public List<glTF_VRM_MeshAnnotation> meshAnnotations = new List<glTF_VRM_MeshAnnotation>();
// lookat
public glTF_VRM_DegreeMap lookAtHorizontalInner = new glTF_VRM_DegreeMap();
public glTF_VRM_DegreeMap lookAtHorizontalOuter = new glTF_VRM_DegreeMap();
public glTF_VRM_DegreeMap lookAtVerticalDown = new glTF_VRM_DegreeMap();
public glTF_VRM_DegreeMap lookAtVerticalUp = new glTF_VRM_DegreeMap();
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => firstPersonBone);
f.KeyValue(() => firstPersonBoneOffset);
f.KeyValue(() => meshAnnotations);
f.KeyValue(() => lookAtHorizontalInner);
f.KeyValue(() => lookAtHorizontalOuter);
f.KeyValue(() => lookAtVerticalDown);
f.KeyValue(() => lookAtVerticalUp);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: fe59508ed49a5db4287c74e0061799b3
timeCreated: 1519282840
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,221 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
using UniHumanoid;
namespace VRM
{
public enum VRMBone
{
hips,
leftUpperLeg,
rightUpperLeg,
leftLowerLeg,
rightLowerLeg,
leftFoot,
rightFoot,
spine,
chest,
neck,
head,
leftShoulder,
rightShoulder,
leftUpperArm,
rightUpperArm,
leftLowerArm,
rightLowerArm,
leftHand,
rightHand,
leftToes,
rightToes,
leftEye,
rightEye,
jaw,
leftThumbProximal,
leftThumbIntermediate,
leftThumbDistal,
leftIndexProximal,
leftIndexIntermediate,
leftIndexDistal,
leftMiddleProximal,
leftMiddleIntermediate,
leftMiddleDistal,
leftRingProximal,
leftRingIntermediate,
leftRingDistal,
leftLittleProximal,
leftLittleIntermediate,
leftLittleDistal,
rightThumbProximal,
rightThumbIntermediate,
rightThumbDistal,
rightIndexProximal,
rightIndexIntermediate,
rightIndexDistal,
rightMiddleProximal,
rightMiddleIntermediate,
rightMiddleDistal,
rightRingProximal,
rightRingIntermediate,
rightRingDistal,
rightLittleProximal,
rightLittleIntermediate,
rightLittleDistal,
upperChest,
}
public static class VRMBoneExtensions
{
public static VRMBone FromHumanBodyBone(this HumanBodyBones human)
{
return (VRMBone)Enum.Parse(typeof(VRMBone), human.ToString(), true);
}
public static HumanBodyBones ToHumanBodyBone(this VRMBone bone)
{
return (HumanBodyBones)Enum.Parse(typeof(HumanBodyBones), bone.ToString(), true);
}
}
[Serializable]
public class glTF_VRM_HumanoidBone : JsonSerializableBase
{
public string bone;
public VRMBone vrmBone
{
set
{
bone = value.ToString();
}
get
{
return (VRMBone)Enum.Parse(typeof(VRMBone), bone, true);
}
}
public int node = -1;
public bool useDefaultValues = true;
public Vector3 min;
public Vector3 max;
public Vector3 center;
public float axisLength;
protected override void SerializeMembers(JsonFormatter f)
{
f.Key("bone"); f.Value((string)bone.ToString());
f.KeyValue(() => node);
f.KeyValue(() => useDefaultValues);
if (!useDefaultValues)
{
f.KeyValue(() => min);
f.KeyValue(() => max);
f.KeyValue(() => center);
f.KeyValue(() => axisLength);
}
}
}
[Serializable]
public class glTF_VRM_Humanoid : JsonSerializableBase
{
public List<glTF_VRM_HumanoidBone> humanBones = new List<glTF_VRM_HumanoidBone>();
public float armStretch = 0.05f;
public float legStretch = 0.05f;
public float upperArmTwist = 0.5f;
public float lowerArmTwist = 0.5f;
public float upperLegTwist = 0.5f;
public float lowerLegTwist = 0.5f;
public float feetSpacing = 0;
public bool hasTranslationDoF = false;
public void SetNodeIndex(HumanBodyBones _key, int node)
{
var key = _key.FromHumanBodyBone();
var index = humanBones.FindIndex(x => x.vrmBone == key);
if (index == -1 || humanBones[index] == null)
{
// not found
humanBones.Add(new glTF_VRM_HumanoidBone
{
vrmBone = key,
node = node
});
}
else
{
humanBones[index].node = node;
}
}
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => humanBones);
f.KeyValue(() => armStretch);
f.KeyValue(() => legStretch);
f.KeyValue(() => upperArmTwist);
f.KeyValue(() => lowerArmTwist);
f.KeyValue(() => upperLegTwist);
f.KeyValue(() => lowerLegTwist);
f.KeyValue(() => feetSpacing);
f.KeyValue(() => hasTranslationDoF);
}
public void Apply(UniHumanoid.AvatarDescription desc, List<Transform> nodes)
{
armStretch = desc.armStretch;
legStretch = desc.legStretch;
upperArmTwist = desc.upperArmTwist;
lowerArmTwist = desc.lowerArmTwist;
upperLegTwist = desc.upperLegTwist;
lowerLegTwist = desc.lowerArmTwist;
feetSpacing = desc.feetSpacing;
hasTranslationDoF = desc.hasTranslationDoF;
foreach (var x in desc.human)
{
var key = x.humanBone.FromHumanBodyBone();
var found = humanBones.FirstOrDefault(y => y.vrmBone == key);
if (found == null)
{
found = new glTF_VRM_HumanoidBone
{
vrmBone = key
};
humanBones.Add(found);
}
found.node = nodes.FindIndex(y => y.name == x.boneName);
found.useDefaultValues = x.useDefaultValues;
found.axisLength = x.axisLength;
found.center = x.center;
found.max = x.max;
found.min = x.min;
}
}
public UniHumanoid.AvatarDescription ToDescription(List<Transform> nodes)
{
var description = ScriptableObject.CreateInstance<UniHumanoid.AvatarDescription>();
description.upperLegTwist = upperLegTwist;
description.lowerLegTwist = lowerLegTwist;
description.upperArmTwist = upperArmTwist;
description.lowerArmTwist = lowerArmTwist;
description.armStretch = armStretch;
description.legStretch = legStretch;
description.hasTranslationDoF = hasTranslationDoF;
description.human = humanBones.Select(x => new UniHumanoid.BoneLimit
{
boneName = nodes[x.node].name,
useDefaultValues = x.useDefaultValues,
axisLength = x.axisLength,
center = x.center,
min = x.min,
max = x.max,
humanBone = x.vrmBone.ToHumanBodyBone(),
}).ToArray();
return description;
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 3f9f79a650473ec46b54bf693bd745d2
timeCreated: 1517549206
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace VRM
{
public class glTF_VRM_Material : UniGLTF.JsonSerializableBase
{
public string name;
public string shader;
public int renderQueue=-1;
public Dictionary<string, float> floatProperties = new Dictionary<string, float>();
public Dictionary<string, float[]> vectorProperties = new Dictionary<string, float[]>();
public Dictionary<string, int> textureProperties = new Dictionary<string, int>();
public Dictionary<string, bool> keywordMap = new Dictionary<string, bool>();
public Dictionary<string, string> tagMap = new Dictionary<string, string>();
static readonly string[] TAGS = new string[]{
"RenderType",
// "Queue",
};
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => name);
f.KeyValue(() => renderQueue);
f.KeyValue(() => shader);
{
f.Key("floatProperties"); f.BeginMap();
foreach (var kv in floatProperties)
{
f.Key(kv.Key); f.Value(kv.Value);
}
f.EndMap();
}
{
f.Key("vectorProperties"); f.BeginMap();
foreach (var kv in vectorProperties)
{
f.Key(kv.Key); f.Value(kv.Value.ToArray());
}
f.EndMap();
}
{
f.Key("textureProperties"); f.BeginMap();
foreach (var kv in textureProperties)
{
f.Key(kv.Key); f.Value(kv.Value);
}
f.EndMap();
}
{
f.Key("keywordMap"); f.BeginMap();
foreach(var kv in keywordMap)
{
f.Key(kv.Key); f.Value(kv.Value);
}
f.EndMap();
}
{
f.Key("tagMap"); f.BeginMap();
foreach (var kv in tagMap)
{
f.Key(kv.Key); f.Value(kv.Value);
}
f.EndMap();
}
}
public static List<glTF_VRM_Material> Parse(string src)
{
var json = src.ParseAsJson()["extensions"]["VRM"]["materialProperties"];
var materials = json.DeserializeList<glTF_VRM_Material>();
var jsonItems = json.ListItems.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].vectorProperties =
jsonItems[i]["vectorProperties"].ObjectItems.ToDictionary(x => x.Key, x =>
{
return x.Value.ListItems.Select(y => y.GetSingle()).ToArray();
});
materials[i].keywordMap =
jsonItems[i]["keywordMap"].ObjectItems.ToDictionary(x => x.Key, x => x.Value.GetBoolean());
materials[i].tagMap =
jsonItems[i]["tagMap"].ObjectItems.ToDictionary(x => x.Key, x => x.Value.GetString());
materials[i].textureProperties =
jsonItems[i]["textureProperties"].ObjectItems.ToDictionary(x => x.Key, x => x.Value.GetInt32());
}
return materials;
}
public static glTF_VRM_Material CreateFromMaterial(Material m, List<Texture2D> textures)
{
var material = new glTF_VRM_Material
{
name = m.name,
shader = m.shader.name,
renderQueue = m.renderQueue,
};
#if UNITY_EDITOR
for (int i = 0; i < ShaderUtil.GetPropertyCount(m.shader); ++i)
{
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) as Texture2D;
if (texture != null)
{
var value = textures.IndexOf(texture);
if (value == -1)
{
Debug.LogFormat("not found {0}", texture.name);
}
else
{
material.textureProperties.Add(name, value);
}
}
}
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");
#endif
foreach (var keyword in m.shaderKeywords)
{
material.keywordMap.Add(keyword, m.IsKeywordEnabled(keyword));
}
foreach(var tag in TAGS)
{
var value = m.GetTag(tag, false);
if (!String.IsNullOrEmpty(value))
{
material.tagMap.Add(tag, value);
}
}
return material;
}
}
}

View File

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

View File

@@ -0,0 +1,71 @@
using System;
using UniGLTF;
namespace VRM
{
public enum LicenseType
{
RedistributionProhibited,
CC0,
CC_BY,
CC_BY_NC,
CC_BY_SA,
CC_BY_NC_SA,
CC_BY_ND,
CC_BY_NC_ND,
Other
}
public static class LicenseTypeExtensions
{
public static LicenseType ToLicenseType(this string src)
{
try
{
return (LicenseType)Enum.Parse(typeof(LicenseType), src, true);
}
catch (Exception)
{
return default(LicenseType);
}
}
}
[Serializable]
public class glTF_VRM_Meta : JsonSerializableBase
{
public string author;
public string contactInformation;
public string licenseName;
public LicenseType licenseType
{
get
{
return licenseName.ToLicenseType();
}
set
{
licenseName = value.ToString();
}
}
public string otherLicenseUrl;
public string reference;
public string title;
public int texture = -1;
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => author);
f.KeyValue(() => contactInformation);
f.KeyValue(() => licenseName);
f.KeyValue(() => otherLicenseUrl);
f.KeyValue(() => reference);
f.KeyValue(() => title);
f.KeyValue(() => texture);
}
}
}

View File

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

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using UniGLTF;
using UnityEngine;
namespace VRM
{
[Serializable]
public class glTF_VRM_SecondaryAnimationCollider : JsonSerializableBase
{
public Vector3 offset;
public float radius;
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => offset);
f.KeyValue(() => radius);
}
}
[Serializable]
public class glTF_VRM_SecondaryAnimationColliderGroup : JsonSerializableBase
{
public int node;
public List<glTF_VRM_SecondaryAnimationCollider> colliders = new List<glTF_VRM_SecondaryAnimationCollider>();
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => node);
f.KeyValue(() => colliders);
}
}
[Serializable]
public class glTF_VRM_SecondaryAnimationGroup : JsonSerializableBase
{
public string comment;
public float stiffiness;
public float gravityPower;
public Vector3 gravityDir;
public float dragForce;
public int center;
public float hitRadius;
public int[] bones = new int[] { };
public int[] colliderGroups = new int[] { };
protected override void SerializeMembers(JsonFormatter f)
{
f.KeyValue(() => comment);
f.KeyValue(() => stiffiness);
f.KeyValue(() => gravityPower);
f.KeyValue(() => gravityDir);
f.KeyValue(() => dragForce);
f.KeyValue(() => center);
f.KeyValue(() => hitRadius);
f.KeyValue(() => bones);
f.KeyValue(() => colliderGroups);
}
}
[Serializable]
public class glTF_VRM_SecondaryAnimation : JsonSerializableBase
{
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)
{
f.KeyValue(() => boneGroups);
f.KeyValue(() => colliderGroups);
}
}
}

View File

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

9
Scripts/Humanoid.meta Normal file
View File

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

View File

@@ -0,0 +1,56 @@
using UnityEngine;
namespace VRM
{
public class VRMHumanoidDescription : MonoBehaviour
{
[SerializeField]
public Avatar Avatar;
[SerializeField]
public UniHumanoid.AvatarDescription Description;
public UniHumanoid.AvatarDescription GetDescription(out bool isCreated)
{
isCreated = false;
if (Description != null)
{
return Description;
}
#if UNITY_EDITOR
if (Avatar != null)
{
isCreated = true;
return UniHumanoid.AvatarDescription.CreateFrom(Avatar);
}
#endif
return null;
}
private void OnValidate()
{
if(Avatar!=null && (!Avatar.isValid || !Avatar.isHuman))
{
Avatar = null;
}
}
void Reset()
{
var animator = GetComponent<Animator>();
if (animator == null)
{
return;
}
Avatar = animator.avatar;
if (Avatar == null)
{
return;
}
}
}
}

View File

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

10
Scripts/LookAt.meta Normal file
View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a7e29185fa574ef4eab4b030a279a64d
folderAsset: yes
timeCreated: 1518293103
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace VRM
{
[Serializable]
public class CurveMapper
{
public AnimationCurve Curve = AnimationCurve.Linear(0, 0, 1.0f, 1.0f);
[Range(20.0f, 90.0f)]
public float CurveXRangeDegree = 90.0f;
[Range(0, 90.0f)]
public float CurveYRangeDegree = 10.0f;
public void OnValidate()
{
if (CurveXRangeDegree == 0)
{
CurveXRangeDegree = 90.0f;
}
}
public void Apply(glTF_VRM_DegreeMap degreeMap)
{
CurveXRangeDegree = degreeMap.xRange;
CurveYRangeDegree = degreeMap.yRange;
if (degreeMap.curve != null)
{
Curve = new AnimationCurve(ToKeys(degreeMap.curve).ToArray());
}
else
{
Curve = AnimationCurve.Linear(0, 0, 1.0f, 1.0f);
}
}
IEnumerable<Keyframe> ToKeys(float[] values)
{
for (int i = 0; i < values.Length; i += 4)
{
yield return new Keyframe(values[i], values[i + 1], values[i + 2], values[i + 3]);
}
}
public float Map(float src)
{
if (src < 0)
{
src = 0;
}
else if (src > CurveXRangeDegree)
{
src = CurveXRangeDegree;
}
return Curve.Evaluate(src / CurveXRangeDegree) * CurveYRangeDegree;
}
}
}

View File

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

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 458bc5bc1e011b247849b92ae7709de7
folderAsset: yes
timeCreated: 1518293117
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using UnityEditor;
using UnityEngine;
namespace VRM
{
[CustomEditor(typeof(VRMLookAtHead))]
public class VRMLookAtHeadEditor : Editor
{
VRMLookAtHead m_target;
void OnEnable()
{
m_target = (VRMLookAtHead)target;
}
const float RADIUS = 0.5f;
void OnSceneGUI()
{
if (!Application.isPlaying) return;
if (!m_target.DrawGizmo) return;
if (m_target.Target == null) return;
if (m_target.Head.Transform == null) return;
{
EditorGUI.BeginChangeCheck();
var newTargetPosition = Handles.PositionHandle(m_target.Target.position, Quaternion.identity);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(m_target.Target, "Change Look At Target Position");
m_target.Target.position = newTargetPosition;
}
}
Handles.color = new Color(1, 1, 1, 0.6f);
Handles.DrawDottedLine(m_target.Head.Transform.position, m_target.Target.position, 4.0f);
Handles.matrix = m_target.Head.InitialWorldMatrix;
Handles.Label(Vector3.zero, string.Format("Yaw: {0:0.}degree\nPitch: {1:0.}degree",
m_target.Yaw,
m_target.Pitch));
Handles.color = new Color(0, 1, 0, 0.2f);
Handles.DrawSolidArc(Vector3.zero,
m_target.Head.OffsetRotation.GetColumn(1),
m_target.Head.OffsetRotation.GetColumn(2),
m_target.Yaw,
RADIUS);
Handles.matrix = m_target.Head.InitialWorldMatrix * m_target.YawMatrix;
Handles.color = new Color(1, 0, 0, 0.2f);
Handles.DrawSolidArc(Vector3.zero,
m_target.Head.OffsetRotation.GetColumn(0),
m_target.Head.OffsetRotation.GetColumn(2),
-m_target.Pitch,
RADIUS);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 6c4b1a599166c48488a860a0017c884a
timeCreated: 1518293126
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
using UnityEngine;
using UniGLTF;
namespace VRM
{
public class LookTarget : MonoBehaviour
{
[SerializeField]
public Transform Target;
[SerializeField]
Vector3 m_offset = new Vector3(0, 0.05f, 0);
[SerializeField, Range(0, 3.0f)]
float m_distance = 0.7f;
public OffsetOnTransform m_offsetTransform;
void Update()
{
if (Target != m_offsetTransform.Transform)
{
m_offsetTransform = OffsetOnTransform.Create(Target);
}
var target = m_offsetTransform.Transform;
if (target != null)
{
var targetPosition = target.position + m_offset;
transform.position = targetPosition + (m_offsetTransform.WorldMatrix.ExtractRotation() * Vector3.forward) * m_distance;
transform.LookAt(targetPosition);
}
}
}
}

View File

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

View File

@@ -0,0 +1,38 @@
using System;
using UniGLTF;
using UnityEngine;
namespace VRM
{
public static class Matrix4x4Extensions
{
public static void CalcYawPitch(this Matrix4x4 m, Vector3 target, out float yaw, out float pitch)
{
var zaxis = Vector3.Project(target, m.GetColumn(2));
var yaxis = Vector3.Project(target, m.GetColumn(1));
var xaxis = Vector3.Project(target, m.GetColumn(0));
var yawPlusMinus = Vector3.Dot(xaxis, m.GetColumn(0)) > 0 ? 1.0f : -1.0f;
yaw = (float)Math.Atan2(xaxis.magnitude, zaxis.magnitude) * yawPlusMinus * Mathf.Rad2Deg;
var pitchPlusMinus = Vector3.Dot(yaxis, m.GetColumn(1)) > 0 ? 1.0f : -1.0f;
pitch = (float)Math.Atan2(yaxis.magnitude, (xaxis + zaxis).magnitude) * pitchPlusMinus * Mathf.Rad2Deg;
}
public static Quaternion YawPitchRotation(this Matrix4x4 m, float yaw, float pitch)
{
return Quaternion.AngleAxis(yaw, m.GetColumn(1)) * Quaternion.AngleAxis(-pitch, m.GetColumn(0));
}
public static Matrix4x4 RotationToWorldAxis(this Matrix4x4 m)
{
return UnityExtensions.Matrix4x4FromColumns(
m.MultiplyVector(Vector3.right),
m.MultiplyVector(Vector3.up),
m.MultiplyVector(Vector3.forward),
new Vector4(0, 0, 0, 1)
);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: ab9df13cea6667a4bbf488fc297bd014
timeCreated: 1518347175
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
using System;
using UnityEngine;
namespace VRM
{
[Serializable]
public struct OffsetOnTransform
{
public Transform Transform;
public Matrix4x4 OffsetRotation;
public Matrix4x4 WorldMatrix
{
get
{
if (Transform == null) return Matrix4x4.identity;
return Transform.localToWorldMatrix * OffsetRotation;
}
}
Matrix4x4 m_initialLocalMatrix;
public void Setup()
{
if (Transform == null) return;
m_initialLocalMatrix = Transform.parent.worldToLocalMatrix * Transform.localToWorldMatrix;
}
public Matrix4x4 InitialWorldMatrix
{
get
{
return Transform.parent.localToWorldMatrix * m_initialLocalMatrix;
}
}
public static OffsetOnTransform Create(Transform transform)
{
var coordinate = new OffsetOnTransform
{
Transform = transform
};
if (transform != null)
{
coordinate.OffsetRotation = transform.worldToLocalMatrix.RotationToWorldAxis();
}
return coordinate;
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: e8e294185b0b06148a057dc3a21cc55f
timeCreated: 1518347841
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

252
Scripts/LookAt/VRMLookAt.cs Normal file
View File

@@ -0,0 +1,252 @@
#pragma warning disable 0414, 0649
using System;
using System.Linq;
using System.Collections.Generic;
using UniGLTF;
using UnityEngine;
namespace VRM
{
public class VRMLookAt : MonoBehaviour
{
public bool DrawGizmo = true;
[SerializeField]
public bool UseUpdate = true;
[SerializeField]
public Transform Target;
[SerializeField]
public OffsetOnTransform LeftEye;
[SerializeField]
public OffsetOnTransform RightEye;
[SerializeField]
public OffsetOnTransform Head;
[SerializeField, Header("Degree Mapping")]
public CurveMapper HorizontalOuter = new CurveMapper();
[SerializeField]
public CurveMapper HorizontalInner = new CurveMapper();
[SerializeField]
public CurveMapper VerticalDown = new CurveMapper();
[SerializeField]
public CurveMapper VerticalUp = new CurveMapper();
public Texture2D CreateThumbnail()
{
var texture = new Texture2D(2048, 2048);
{
var go = new GameObject("ThumbCamera");
var camera = go.AddComponent<Camera>();
CreateThumbnail(camera, texture);
if (Application.isPlaying) { GameObject.Destroy(go); } else { GameObject.DestroyImmediate(go); }
}
return texture;
}
void CreateThumbnail(Camera camera, Texture2D dst)
{
RenderTexture currentRT = RenderTexture.active;
{
var renderTexture = new RenderTexture(dst.width, dst.height, 24);
camera.targetTexture = renderTexture;
RenderTexture.active = renderTexture;
LookFace(camera.transform);
camera.Render();
dst.ReadPixels(new Rect(0, 0, dst.width, dst.height), 0, 0);
RenderTexture.active = currentRT;
camera.targetTexture = null;
if (Application.isPlaying)
{
UnityEngine.Object.Destroy(renderTexture);
}
else
{
UnityEngine.Object.DestroyImmediate(renderTexture);
}
}
}
public void LookFace(Transform t)
{
if (Head.Transform == null) return;
var head = Head.Transform;
var headPosition = head.position + new Vector3(0, 0.05f, 0);
t.position = headPosition + Head.WorldMatrix.ExtractRotation() * new Vector3(0, 0, 0.7f);
t.LookAt(headPosition);
}
public void CopyTo(GameObject _dst, Dictionary<Transform, Transform> map)
{
var dst = _dst.AddComponent<VRMLookAt>();
dst.Target = Target;
dst.Head = OffsetOnTransform.Create(map[Head.Transform]);
dst.RightEye = OffsetOnTransform.Create(map[RightEye.Transform]);
dst.LeftEye = OffsetOnTransform.Create(map[LeftEye.Transform]);
dst.HorizontalOuter = HorizontalOuter;
dst.HorizontalInner = HorizontalInner;
dst.VerticalDown = VerticalDown;
dst.VerticalUp = VerticalUp;
}
private void Reset()
{
Target = Camera.main.transform;
GetBones();
}
private void OnValidate()
{
HorizontalInner.OnValidate();
HorizontalOuter.OnValidate();
VerticalUp.OnValidate();
VerticalDown.OnValidate();
}
public void GetBones()
{
var animator = GetComponent<Animator>();
if (animator != null)
{
LeftEye = OffsetOnTransform.Create(animator.GetBoneTransform(HumanBodyBones.LeftEye));
RightEye = OffsetOnTransform.Create(animator.GetBoneTransform(HumanBodyBones.RightEye));
Head = OffsetOnTransform.Create(animator.GetBoneTransform(HumanBodyBones.Head));
}
}
private void Awake()
{
Head.Setup();
LeftEye.Setup();
RightEye.Setup();
}
#region Gizmo
static void DrawMatrix(Matrix4x4 m, float size)
{
Gizmos.matrix = m;
Gizmos.color = Color.red;
Gizmos.DrawLine(Vector3.zero, Vector3.right * size);
Gizmos.color = Color.green;
Gizmos.DrawLine(Vector3.zero, Vector3.up * size);
Gizmos.color = Color.blue;
Gizmos.DrawLine(Vector3.zero, Vector3.forward * size);
}
const float SIZE = 0.5f;
private void OnDrawGizmos()
{
if (!DrawGizmo) return;
if (LeftEye.Transform != null & RightEye.Transform != null)
{
DrawMatrix(LeftEye.WorldMatrix, SIZE);
DrawMatrix(RightEye.WorldMatrix, SIZE);
}
else
{
DrawMatrix(Head.WorldMatrix, SIZE);
}
}
#endregion
static Matrix4x4 LookAtMatrixFromWorld(Vector3 from, Vector3 target)
{
var pos = new Vector4(from.x, from.y, from.z, 1);
return LookAtMatrix(UnityExtensions.Matrix4x4FromColumns(Vector3.right, Vector3.up, Vector3.forward, pos), target);
}
static Matrix4x4 LookAtMatrix(Vector3 up_vector, Vector3 localPosition)
{
var z_axis = localPosition.normalized;
var x_axis = Vector3.Cross(up_vector, z_axis).normalized;
var y_axis = Vector3.Cross(z_axis, x_axis).normalized;
return UnityExtensions.Matrix4x4FromColumns(x_axis, y_axis, z_axis, new Vector4(0, 0, 0, 1));
}
static Matrix4x4 LookAtMatrix(Matrix4x4 m, Vector3 target)
{
return LookAtMatrix(Vector3.up, m.inverse.MultiplyPoint(target));
}
public Matrix4x4 YawMatrix
{
get
{
var yaw = Quaternion.AngleAxis(Yaw, Head.OffsetRotation.GetColumn(1));
var m = default(Matrix4x4);
m.SetTRS(Vector3.zero, yaw, Vector3.one);
return m;
}
}
[SerializeField, Header("Debug")]
public float Yaw;
public float Pitch;
private void LateUpdate()
{
if (!UseUpdate) return;
if (Target == null) return;
LookWorldPosition(Target.position);
}
public void LookWorldPosition(Vector3 targetPosition)
{
var localPosition = Head.InitialWorldMatrix.inverse.MultiplyPoint(targetPosition);
Head.OffsetRotation.CalcYawPitch(localPosition, out Yaw, out Pitch);
ApplyRotations(Yaw, Pitch);
}
void ApplyRotations(float yaw, float pitch)
{
// horizontal
float leftYaw, rightYaw;
if (yaw < 0)
{
leftYaw = -HorizontalOuter.Map(-yaw);
rightYaw = -HorizontalInner.Map(-yaw);
}
else
{
rightYaw = HorizontalOuter.Map(yaw);
leftYaw = HorizontalInner.Map(yaw);
}
// vertical
if (pitch < 0)
{
pitch = -VerticalDown.Map(-pitch);
}
else
{
pitch = VerticalUp.Map(pitch);
}
// Apply
if (LeftEye.Transform != null && RightEye.Transform != null)
{
// 目に値を適用する
LeftEye.Transform.rotation = LeftEye.InitialWorldMatrix.ExtractRotation() * Head.OffsetRotation.YawPitchRotation(leftYaw, pitch);
RightEye.Transform.rotation = RightEye.InitialWorldMatrix.ExtractRotation() * Head.OffsetRotation.YawPitchRotation(rightYaw, pitch);
}
else if (Head.Transform != null)
{
// 頭に値を適用する
Head.Transform.rotation = Head.InitialWorldMatrix.ExtractRotation() * Head.OffsetRotation.YawPitchRotation(yaw, pitch);
}
}
}
}

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