mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-10-02 15:53:30 -05:00
add VRM scripts
This commit is contained in:
10
Scenes.meta
Normal file
10
Scenes.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c58829edf15349648929208fdb1c42ca
|
||||
folderAsset: yes
|
||||
timeCreated: 1519303162
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1710
Scenes/VRMRuntimeLoaderSample.unity
Normal file
1710
Scenes/VRMRuntimeLoaderSample.unity
Normal file
File diff suppressed because it is too large
Load Diff
8
Scenes/VRMRuntimeLoaderSample.unity.meta
Normal file
8
Scenes/VRMRuntimeLoaderSample.unity.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb6ff948a1027244a86abf24f3445a87
|
||||
timeCreated: 1520832729
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Scripts.meta
Normal file
10
Scripts.meta
Normal 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
10
Scripts/BlendShape.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0604208c180cfea44878a6962ab134b3
|
||||
folderAsset: yes
|
||||
timeCreated: 1517402736
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
79
Scripts/BlendShape/AIUEO.cs
Normal file
79
Scripts/BlendShape/AIUEO.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/BlendShape/AIUEO.cs.meta
Normal file
13
Scripts/BlendShape/AIUEO.cs.meta
Normal 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:
|
||||
78
Scripts/BlendShape/BlendShapeAvatar.cs
Normal file
78
Scripts/BlendShape/BlendShapeAvatar.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/BlendShape/BlendShapeAvatar.cs.meta
Normal file
13
Scripts/BlendShape/BlendShapeAvatar.cs.meta
Normal 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:
|
||||
77
Scripts/BlendShape/BlendShapeClip.cs
Normal file
77
Scripts/BlendShape/BlendShapeClip.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/BlendShape/BlendShapeClip.cs.meta
Normal file
13
Scripts/BlendShape/BlendShapeClip.cs.meta
Normal 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:
|
||||
52
Scripts/BlendShape/BlendShapeClipHandler.cs
Normal file
52
Scripts/BlendShape/BlendShapeClipHandler.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/BlendShape/BlendShapeClipHandler.cs.meta
Normal file
12
Scripts/BlendShape/BlendShapeClipHandler.cs.meta
Normal 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:
|
||||
96
Scripts/BlendShape/BlendShapeKey.cs
Normal file
96
Scripts/BlendShape/BlendShapeKey.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/BlendShape/BlendShapeKey.cs.meta
Normal file
12
Scripts/BlendShape/BlendShapeKey.cs.meta
Normal 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:
|
||||
116
Scripts/BlendShape/Blinker.cs
Normal file
116
Scripts/BlendShape/Blinker.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/BlendShape/Blinker.cs.meta
Normal file
13
Scripts/BlendShape/Blinker.cs.meta
Normal 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:
|
||||
10
Scripts/BlendShape/Editor.meta
Normal file
10
Scripts/BlendShape/Editor.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 269b9c7394d16084a9e55528470e2ac7
|
||||
folderAsset: yes
|
||||
timeCreated: 1517404636
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
71
Scripts/BlendShape/Editor/BlendShapeSlider.cs
Normal file
71
Scripts/BlendShape/Editor/BlendShapeSlider.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/BlendShape/Editor/BlendShapeSlider.cs.meta
Normal file
12
Scripts/BlendShape/Editor/BlendShapeSlider.cs.meta
Normal 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:
|
||||
157
Scripts/BlendShape/Editor/VRMBlendShapeManipulatorEditor.cs
Normal file
157
Scripts/BlendShape/Editor/VRMBlendShapeManipulatorEditor.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
360
Scripts/BlendShape/Editor/VRMBlnedShapeProxyEditor.cs
Normal file
360
Scripts/BlendShape/Editor/VRMBlnedShapeProxyEditor.cs
Normal 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
|
||||
}
|
||||
}
|
||||
13
Scripts/BlendShape/Editor/VRMBlnedShapeProxyEditor.cs.meta
Normal file
13
Scripts/BlendShape/Editor/VRMBlnedShapeProxyEditor.cs.meta
Normal 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:
|
||||
9
Scripts/BlendShape/VRMBlendShapeManipulator.cs
Normal file
9
Scripts/BlendShape/VRMBlendShapeManipulator.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public class VRMBlendShapeManipulator : MonoBehaviour
|
||||
{
|
||||
}
|
||||
}
|
||||
13
Scripts/BlendShape/VRMBlendShapeManipulator.cs.meta
Normal file
13
Scripts/BlendShape/VRMBlendShapeManipulator.cs.meta
Normal 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:
|
||||
93
Scripts/BlendShape/VRMBlendShapeProxy.cs
Normal file
93
Scripts/BlendShape/VRMBlendShapeProxy.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/BlendShape/VRMBlendShapeProxy.cs.meta
Normal file
13
Scripts/BlendShape/VRMBlendShapeProxy.cs.meta
Normal 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
9
Scripts/Editor.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41cb69128d62a4e4a9ffde396b606553
|
||||
folderAsset: yes
|
||||
timeCreated: 1521102890
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Scripts/Editor/Tests.meta
Normal file
9
Scripts/Editor/Tests.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0903c33afe5ef6346ad9526e78055293
|
||||
folderAsset: yes
|
||||
timeCreated: 1521799604
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Scripts/Editor/Tests/VRMBlendShapeKeyTest.cs
Normal file
24
Scripts/Editor/Tests/VRMBlendShapeKeyTest.cs
Normal 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)));
|
||||
}
|
||||
}
|
||||
12
Scripts/Editor/Tests/VRMBlendShapeKeyTest.cs.meta
Normal file
12
Scripts/Editor/Tests/VRMBlendShapeKeyTest.cs.meta
Normal 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:
|
||||
55
Scripts/Editor/VRMExportUnityPackage.cs
Normal file
55
Scripts/Editor/VRMExportUnityPackage.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Editor/VRMExportUnityPackage.cs.meta
Normal file
12
Scripts/Editor/VRMExportUnityPackage.cs.meta
Normal 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:
|
||||
30
Scripts/Editor/VRMMonoBehaviourComparator.cs
Normal file
30
Scripts/Editor/VRMMonoBehaviourComparator.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
12
Scripts/Editor/VRMMonoBehaviourComparator.cs.meta
Normal file
12
Scripts/Editor/VRMMonoBehaviourComparator.cs.meta
Normal 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
76
Scripts/Editor/VRMTest.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Editor/VRMTest.cs.meta
Normal file
12
Scripts/Editor/VRMTest.cs.meta
Normal 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
10
Scripts/FirstPerson.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a5c5355606c9da4cb354f3ae2ae4bae
|
||||
folderAsset: yes
|
||||
timeCreated: 1519278754
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Scripts/FirstPerson/Editor.meta
Normal file
9
Scripts/FirstPerson/Editor.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ee18512b5f8dd94a923348721614318
|
||||
folderAsset: yes
|
||||
timeCreated: 1520848598
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
Scripts/FirstPerson/Editor/RendererFirstPersonFlagsDrawer.cs
Normal file
40
Scripts/FirstPerson/Editor/RendererFirstPersonFlagsDrawer.cs
Normal 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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddbc54a3a83438841a744bf50b34ebb9
|
||||
timeCreated: 1520848617
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
210
Scripts/FirstPerson/VRMFirstPerson.cs
Normal file
210
Scripts/FirstPerson/VRMFirstPerson.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/FirstPerson/VRMFirstPerson.cs.meta
Normal file
13
Scripts/FirstPerson/VRMFirstPerson.cs.meta
Normal 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:
|
||||
91
Scripts/FirstPerson/VRMFirstPersonCameraManager.cs
Normal file
91
Scripts/FirstPerson/VRMFirstPersonCameraManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/FirstPerson/VRMFirstPersonCameraManager.cs.meta
Normal file
13
Scripts/FirstPerson/VRMFirstPersonCameraManager.cs.meta
Normal 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
10
Scripts/Format.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8dc6b68ffeda9f54494eee1cd17af615
|
||||
folderAsset: yes
|
||||
timeCreated: 1517308531
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Scripts/Format/Editor.meta
Normal file
10
Scripts/Format/Editor.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f24b76911d4f68844b9d12721ba1bb06
|
||||
folderAsset: yes
|
||||
timeCreated: 1517308497
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
212
Scripts/Format/Editor/VRMAssetWriter.cs
Normal file
212
Scripts/Format/Editor/VRMAssetWriter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/Editor/VRMAssetWriter.cs.meta
Normal file
12
Scripts/Format/Editor/VRMAssetWriter.cs.meta
Normal 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:
|
||||
219
Scripts/Format/Editor/VRMExporter.cs
Normal file
219
Scripts/Format/Editor/VRMExporter.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/Format/Editor/VRMExporter.cs.meta
Normal file
13
Scripts/Format/Editor/VRMExporter.cs.meta
Normal 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:
|
||||
124
Scripts/Format/Editor/VRMExporterMenu.cs
Normal file
124
Scripts/Format/Editor/VRMExporterMenu.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/Editor/VRMExporterMenu.cs.meta
Normal file
12
Scripts/Format/Editor/VRMExporterMenu.cs.meta
Normal 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:
|
||||
162
Scripts/Format/Editor/VRMHumanoidNormalizerMenu.cs
Normal file
162
Scripts/Format/Editor/VRMHumanoidNormalizerMenu.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/Editor/VRMHumanoidNormalizerMenu.cs.meta
Normal file
12
Scripts/Format/Editor/VRMHumanoidNormalizerMenu.cs.meta
Normal 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:
|
||||
54
Scripts/Format/Editor/VRMImporterMenu.cs
Normal file
54
Scripts/Format/Editor/VRMImporterMenu.cs
Normal 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
|
||||
13
Scripts/Format/Editor/VRMImporterMenu.cs.meta
Normal file
13
Scripts/Format/Editor/VRMImporterMenu.cs.meta
Normal 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:
|
||||
46
Scripts/Format/Editor/VRMVersionMenu.cs
Normal file
46
Scripts/Format/Editor/VRMVersionMenu.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/Format/Editor/VRMVersionMenu.cs.meta
Normal file
13
Scripts/Format/Editor/VRMVersionMenu.cs.meta
Normal 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:
|
||||
49
Scripts/Format/Editor/vrmAssetPostprocessor.cs
Normal file
49
Scripts/Format/Editor/vrmAssetPostprocessor.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/Editor/vrmAssetPostprocessor.cs.meta
Normal file
12
Scripts/Format/Editor/vrmAssetPostprocessor.cs.meta
Normal 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:
|
||||
15
Scripts/Format/VRMException.cs
Normal file
15
Scripts/Format/VRMException.cs
Normal 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))
|
||||
{ }
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/VRMException.cs.meta
Normal file
12
Scripts/Format/VRMException.cs.meta
Normal 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:
|
||||
57
Scripts/Format/VRMFormat.cs
Normal file
57
Scripts/Format/VRMFormat.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/Format/VRMFormat.cs.meta
Normal file
13
Scripts/Format/VRMFormat.cs.meta
Normal 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:
|
||||
661
Scripts/Format/VRMImporter.cs
Normal file
661
Scripts/Format/VRMImporter.cs
Normal 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
|
||||
}
|
||||
}
|
||||
13
Scripts/Format/VRMImporter.cs.meta
Normal file
13
Scripts/Format/VRMImporter.cs.meta
Normal 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:
|
||||
43
Scripts/Format/VRMImporterContext.cs
Normal file
43
Scripts/Format/VRMImporterContext.cs
Normal 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
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/VRMImporterContext.cs.meta
Normal file
12
Scripts/Format/VRMImporterContext.cs.meta
Normal 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:
|
||||
14
Scripts/Format/VRMVersion.cs
Normal file
14
Scripts/Format/VRMVersion.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
13
Scripts/Format/VRMVersion.cs.meta
Normal file
13
Scripts/Format/VRMVersion.cs.meta
Normal 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:
|
||||
146
Scripts/Format/glTF_VRM_BlendShape.cs
Normal file
146
Scripts/Format/glTF_VRM_BlendShape.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/Format/glTF_VRM_BlendShape.cs.meta
Normal file
13
Scripts/Format/glTF_VRM_BlendShape.cs.meta
Normal 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:
|
||||
85
Scripts/Format/glTF_VRM_FirstPerson.cs
Normal file
85
Scripts/Format/glTF_VRM_FirstPerson.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/Format/glTF_VRM_FirstPerson.cs.meta
Normal file
13
Scripts/Format/glTF_VRM_FirstPerson.cs.meta
Normal 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:
|
||||
221
Scripts/Format/glTF_VRM_Humanoid.cs
Normal file
221
Scripts/Format/glTF_VRM_Humanoid.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/Format/glTF_VRM_Humanoid.cs.meta
Normal file
13
Scripts/Format/glTF_VRM_Humanoid.cs.meta
Normal 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:
|
||||
183
Scripts/Format/glTF_VRM_Material.cs
Normal file
183
Scripts/Format/glTF_VRM_Material.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/glTF_VRM_Material.cs.meta
Normal file
12
Scripts/Format/glTF_VRM_Material.cs.meta
Normal 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:
|
||||
71
Scripts/Format/glTF_VRM_Meta.cs
Normal file
71
Scripts/Format/glTF_VRM_Meta.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/glTF_VRM_Meta.cs.meta
Normal file
12
Scripts/Format/glTF_VRM_Meta.cs.meta
Normal 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:
|
||||
75
Scripts/Format/glTF_VRM_SecondaryAnimation.cs
Normal file
75
Scripts/Format/glTF_VRM_SecondaryAnimation.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Format/glTF_VRM_SecondaryAnimation.cs.meta
Normal file
12
Scripts/Format/glTF_VRM_SecondaryAnimation.cs.meta
Normal 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
9
Scripts/Humanoid.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dabe3b8d8da159f43a06701a61a69dbf
|
||||
folderAsset: yes
|
||||
timeCreated: 1520410756
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
56
Scripts/Humanoid/VRMHumanoidDescription.cs
Normal file
56
Scripts/Humanoid/VRMHumanoidDescription.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/Humanoid/VRMHumanoidDescription.cs.meta
Normal file
12
Scripts/Humanoid/VRMHumanoidDescription.cs.meta
Normal 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
10
Scripts/LookAt.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7e29185fa574ef4eab4b030a279a64d
|
||||
folderAsset: yes
|
||||
timeCreated: 1518293103
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
63
Scripts/LookAt/CurveMapper.cs
Normal file
63
Scripts/LookAt/CurveMapper.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/LookAt/CurveMapper.cs.meta
Normal file
12
Scripts/LookAt/CurveMapper.cs.meta
Normal 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:
|
||||
10
Scripts/LookAt/Editor.meta
Normal file
10
Scripts/LookAt/Editor.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 458bc5bc1e011b247849b92ae7709de7
|
||||
folderAsset: yes
|
||||
timeCreated: 1518293117
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
60
Scripts/LookAt/Editor/VRMLookAtHeadEditor.cs
Normal file
60
Scripts/LookAt/Editor/VRMLookAtHeadEditor.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/LookAt/Editor/VRMLookAtHeadEditor.cs.meta
Normal file
13
Scripts/LookAt/Editor/VRMLookAtHeadEditor.cs.meta
Normal 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:
|
||||
36
Scripts/LookAt/LookTarget.cs
Normal file
36
Scripts/LookAt/LookTarget.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Scripts/LookAt/LookTarget.cs.meta
Normal file
12
Scripts/LookAt/LookTarget.cs.meta
Normal 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:
|
||||
38
Scripts/LookAt/Matrix4x4Extensions.cs
Normal file
38
Scripts/LookAt/Matrix4x4Extensions.cs
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/LookAt/Matrix4x4Extensions.cs.meta
Normal file
13
Scripts/LookAt/Matrix4x4Extensions.cs.meta
Normal 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:
|
||||
52
Scripts/LookAt/OffsetOnTransform.cs
Normal file
52
Scripts/LookAt/OffsetOnTransform.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Scripts/LookAt/OffsetOnTransform.cs.meta
Normal file
13
Scripts/LookAt/OffsetOnTransform.cs.meta
Normal 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
252
Scripts/LookAt/VRMLookAt.cs
Normal 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
Reference in New Issue
Block a user