Move UniVRM related files into Assets/*. Move Scripts/Editor to Editor in UniVRM

This commit is contained in:
yutopp 2018-12-28 22:35:24 +09:00
parent 6df13b68e3
commit f3e130d346
237 changed files with 10892 additions and 10883 deletions

9
Assets/UniVRM.meta Normal file
View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cd93bd4f117e1e54db244c5cadbef691
folderAsset: yes
timeCreated: 1546003999
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,21 +1,21 @@
using NUnit.Framework;
using UnityEngine;
namespace VRM
{
public class EnumUtilTest
{
[Test]
public void EnumUtilTestSimplePasses()
{
Assert.AreEqual(default(HumanBodyBones), EnumUtil.TryParseOrDefault<HumanBodyBones>("xxx"));
#if UNITY_5_6_OR_NEWER
Assert.AreEqual(HumanBodyBones.UpperChest, EnumUtil.TryParseOrDefault<HumanBodyBones>("upperchest"));
#else
Assert.AreEqual(default(HumanBodyBones), EnumUtil.TryParseOrDefault<HumanBodyBones>("upperchest"));
#endif
}
}
}
using NUnit.Framework;
using UnityEngine;
namespace VRM
{
public class EnumUtilTest
{
[Test]
public void EnumUtilTestSimplePasses()
{
Assert.AreEqual(default(HumanBodyBones), EnumUtil.TryParseOrDefault<HumanBodyBones>("xxx"));
#if UNITY_5_6_OR_NEWER
Assert.AreEqual(HumanBodyBones.UpperChest, EnumUtil.TryParseOrDefault<HumanBodyBones>("upperchest"));
#else
Assert.AreEqual(default(HumanBodyBones), EnumUtil.TryParseOrDefault<HumanBodyBones>("upperchest"));
#endif
}
}
}

View File

@ -1,64 +1,64 @@
using NUnit.Framework;
using UnityEngine;
using System.Linq;
namespace VRM
{
public class MeshTests
{
public static void MeshEquals(Mesh l, Mesh r)
{
#if UNITY_2017_3_OR_NEWER
Assert.AreEqual(l.indexFormat, r.indexFormat);
#endif
Assert.True(l.vertices.SequenceEqual(r.vertices));
Assert.True(l.normals.SequenceEqual(r.normals));
Assert.True(l.tangents.SequenceEqual(r.tangents));
Assert.True(l.uv.SequenceEqual(r.uv));
Assert.True(l.uv2.SequenceEqual(r.uv2));
Assert.True(l.uv3.SequenceEqual(r.uv3));
Assert.True(l.uv4.SequenceEqual(r.uv4));
Assert.True(l.colors.SequenceEqual(r.colors));
Assert.True(l.boneWeights.SequenceEqual(r.boneWeights));
Assert.True(l.bindposes.SequenceEqual(r.bindposes));
Assert.AreEqual(l.subMeshCount, r.subMeshCount);
for (int i = 0; i < l.subMeshCount; ++i)
{
Assert.True(l.GetIndices(i).SequenceEqual(r.GetIndices(i)));
}
Assert.AreEqual(l.blendShapeCount, r.blendShapeCount);
for (int i = 0; i < l.blendShapeCount; ++i)
{
Assert.AreEqual(l.GetBlendShapeName(i), r.GetBlendShapeName(i));
Assert.AreEqual(l.GetBlendShapeFrameCount(i), r.GetBlendShapeFrameCount(i));
Assert.AreEqual(l.GetBlendShapeFrameWeight(i, 0), r.GetBlendShapeFrameWeight(i, 0));
var lv = l.vertices;
var ln = l.vertices;
var lt = l.vertices;
var rv = r.vertices;
var rn = r.vertices;
var rt = r.vertices;
l.GetBlendShapeFrameVertices(i, 0, lv, ln, lt);
r.GetBlendShapeFrameVertices(i, 0, rv, rn, rt);
Assert.True(lv.SequenceEqual(rv));
Assert.True(ln.SequenceEqual(rn));
Assert.True(lt.SequenceEqual(rt));
}
}
[Test]
public void MeshCopyTest()
{
var src = new Mesh();
src.AddBlendShapeFrame("blendShape", 100.0f, null, null, null);
var dst = src.Copy(true);
MeshEquals(src, dst);
}
}
}
using NUnit.Framework;
using UnityEngine;
using System.Linq;
namespace VRM
{
public class MeshTests
{
public static void MeshEquals(Mesh l, Mesh r)
{
#if UNITY_2017_3_OR_NEWER
Assert.AreEqual(l.indexFormat, r.indexFormat);
#endif
Assert.True(l.vertices.SequenceEqual(r.vertices));
Assert.True(l.normals.SequenceEqual(r.normals));
Assert.True(l.tangents.SequenceEqual(r.tangents));
Assert.True(l.uv.SequenceEqual(r.uv));
Assert.True(l.uv2.SequenceEqual(r.uv2));
Assert.True(l.uv3.SequenceEqual(r.uv3));
Assert.True(l.uv4.SequenceEqual(r.uv4));
Assert.True(l.colors.SequenceEqual(r.colors));
Assert.True(l.boneWeights.SequenceEqual(r.boneWeights));
Assert.True(l.bindposes.SequenceEqual(r.bindposes));
Assert.AreEqual(l.subMeshCount, r.subMeshCount);
for (int i = 0; i < l.subMeshCount; ++i)
{
Assert.True(l.GetIndices(i).SequenceEqual(r.GetIndices(i)));
}
Assert.AreEqual(l.blendShapeCount, r.blendShapeCount);
for (int i = 0; i < l.blendShapeCount; ++i)
{
Assert.AreEqual(l.GetBlendShapeName(i), r.GetBlendShapeName(i));
Assert.AreEqual(l.GetBlendShapeFrameCount(i), r.GetBlendShapeFrameCount(i));
Assert.AreEqual(l.GetBlendShapeFrameWeight(i, 0), r.GetBlendShapeFrameWeight(i, 0));
var lv = l.vertices;
var ln = l.vertices;
var lt = l.vertices;
var rv = r.vertices;
var rn = r.vertices;
var rt = r.vertices;
l.GetBlendShapeFrameVertices(i, 0, lv, ln, lt);
r.GetBlendShapeFrameVertices(i, 0, rv, rn, rt);
Assert.True(lv.SequenceEqual(rv));
Assert.True(ln.SequenceEqual(rn));
Assert.True(lt.SequenceEqual(rt));
}
}
[Test]
public void MeshCopyTest()
{
var src = new Mesh();
src.AddBlendShapeFrame("blendShape", 100.0f, null, null, null);
var dst = src.Copy(true);
MeshEquals(src, dst);
}
}
}

View File

@ -1,27 +1,27 @@
using NUnit.Framework;
using System.Collections.Generic;
using VRM;
namespace 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>();
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)));
}
}
}
using NUnit.Framework;
using System.Collections.Generic;
using VRM;
namespace 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>();
dict[new BlendShapeKey("xxx", BlendShapePreset.Blink)] = 1.0f;
Assert.IsTrue(dict.ContainsKey(new BlendShapeKey("blink")));
Assert.IsTrue(dict.ContainsKey(new BlendShapeKey(BlendShapePreset.Blink)));
Assert.IsTrue(dict.ContainsKey(new BlendShapeKey("xxx", BlendShapePreset.Blink)));
}
}
}

View File

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

View File

@ -1,61 +1,61 @@
Shader "VRM/UnlitTransparentZWrite"
{
Properties {
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader {
Tags {"Queue"="AlphaTest+150" "IgnoreProjector"="True" "RenderType"="Transparent"}
LOD 100
ZWrite On
Blend SrcAlpha OneMinusSrcAlpha
Shader "VRM/UnlitTransparentZWrite"
{
Properties {
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader {
Tags {"Queue"="AlphaTest+150" "IgnoreProjector"="True" "RenderType"="Transparent"}
LOD 100
ZWrite On
Blend SrcAlpha OneMinusSrcAlpha
BlendOp Add, Max
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.texcoord);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.texcoord);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}

View File

@ -1,137 +1,137 @@
using UnityEngine;
using System.Linq;
using System;
using System.Collections.Generic;
using UniGLTF;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace VRM
{
[CreateAssetMenu(menuName = "VRM/BlendShapeAvatar")]
public class BlendShapeAvatar : ScriptableObject
{
[SerializeField]
public List<BlendShapeClip> Clips = new List<BlendShapeClip>();
/// <summary>
/// NullのClipを削除して詰める
/// </summary>
public void RemoveNullClip()
{
if (Clips == null)
{
return;
}
for (int i = Clips.Count - 1; i >= 0; --i)
{
if (Clips[i] == null)
{
Clips.RemoveAt(i);
}
}
}
#if UNITY_EDITOR
[ContextMenu("Restore")]
void Restore()
{
var assetPath = UnityPath.FromAsset(this);
if (assetPath.IsNull)
{
return;
}
foreach (var x in assetPath.Parent.ChildFiles)
{
var clip = UnityEditor.AssetDatabase.LoadAssetAtPath<BlendShapeClip>(x.Value);
if (clip == null) continue;
if (!Clips.Contains(clip))
{
Clips.Add(clip);
}
Debug.LogFormat("{0}", clip.name);
}
Clips = Clips.OrderBy(x => BlendShapeKey.CreateFrom(x)).ToList();
}
static public BlendShapeClip CreateBlendShapeClip(string path)
{
//Debug.LogFormat("{0}", path);
var clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.BlendShapeName = Path.GetFileNameWithoutExtension(path);
AssetDatabase.CreateAsset(clip, path);
AssetDatabase.ImportAsset(path);
return clip;
//Clips.Add(clip);
//EditorUtility.SetDirty(this);
//AssetDatabase.SaveAssets();
}
#endif
/// <summary>
/// Unknown以外で存在しないものを全て作る
/// </summary>
public void CreateDefaultPreset()
{
foreach (var preset in ((BlendShapePreset[])Enum.GetValues(typeof(BlendShapePreset)))
.Where(x => x != BlendShapePreset.Unknown))
{
CreateDefaultPreset(preset);
}
}
void CreateDefaultPreset(BlendShapePreset preset)
{
var clip = GetClip(preset);
if (clip != null) return;
clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.name = preset.ToString();
clip.BlendShapeName = preset.ToString();
clip.Preset = preset;
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));
}
}
}
using UnityEngine;
using System.Linq;
using System;
using System.Collections.Generic;
using UniGLTF;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace VRM
{
[CreateAssetMenu(menuName = "VRM/BlendShapeAvatar")]
public class BlendShapeAvatar : ScriptableObject
{
[SerializeField]
public List<BlendShapeClip> Clips = new List<BlendShapeClip>();
/// <summary>
/// NullのClipを削除して詰める
/// </summary>
public void RemoveNullClip()
{
if (Clips == null)
{
return;
}
for (int i = Clips.Count - 1; i >= 0; --i)
{
if (Clips[i] == null)
{
Clips.RemoveAt(i);
}
}
}
#if UNITY_EDITOR
[ContextMenu("Restore")]
void Restore()
{
var assetPath = UnityPath.FromAsset(this);
if (assetPath.IsNull)
{
return;
}
foreach (var x in assetPath.Parent.ChildFiles)
{
var clip = UnityEditor.AssetDatabase.LoadAssetAtPath<BlendShapeClip>(x.Value);
if (clip == null) continue;
if (!Clips.Contains(clip))
{
Clips.Add(clip);
}
Debug.LogFormat("{0}", clip.name);
}
Clips = Clips.OrderBy(x => BlendShapeKey.CreateFrom(x)).ToList();
}
static public BlendShapeClip CreateBlendShapeClip(string path)
{
//Debug.LogFormat("{0}", path);
var clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.BlendShapeName = Path.GetFileNameWithoutExtension(path);
AssetDatabase.CreateAsset(clip, path);
AssetDatabase.ImportAsset(path);
return clip;
//Clips.Add(clip);
//EditorUtility.SetDirty(this);
//AssetDatabase.SaveAssets();
}
#endif
/// <summary>
/// Unknown以外で存在しないものを全て作る
/// </summary>
public void CreateDefaultPreset()
{
foreach (var preset in ((BlendShapePreset[])Enum.GetValues(typeof(BlendShapePreset)))
.Where(x => x != BlendShapePreset.Unknown))
{
CreateDefaultPreset(preset);
}
}
void CreateDefaultPreset(BlendShapePreset preset)
{
var clip = GetClip(preset);
if (clip != null) return;
clip = ScriptableObject.CreateInstance<BlendShapeClip>();
clip.name = preset.ToString();
clip.BlendShapeName = preset.ToString();
clip.Preset = preset;
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));
}
}
}

View File

@ -1,121 +1,121 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VRM
{
///
/// A.Value * A.Weight + B.Value * B.Weight ...
///
class BlendShapeBindingMerger
{
class DictionaryKeyBlendShapeBindingComparer : IEqualityComparer<BlendShapeBinding>
{
public bool Equals(BlendShapeBinding x, BlendShapeBinding y)
{
return x.RelativePath == y.RelativePath
&& x.Index == y.Index;
}
public int GetHashCode(BlendShapeBinding obj)
{
return obj.RelativePath.GetHashCode() + obj.Index;
}
}
private static DictionaryKeyBlendShapeBindingComparer comparer = new DictionaryKeyBlendShapeBindingComparer();
/// <summary>
/// BlendShapeの適用値を蓄積する
/// </summary>
/// <typeparam name="BlendShapeBinding"></typeparam>
/// <typeparam name="float"></typeparam>
/// <returns></returns>
Dictionary<BlendShapeBinding, float> m_blendShapeValueMap = new Dictionary<BlendShapeBinding, float>(comparer);
/// <summary>
///
/// </summary>
/// <returns></returns>
Dictionary<BlendShapeBinding, Action<float>> m_blendShapeSetterMap = new Dictionary<BlendShapeBinding, Action<float>>(comparer);
public BlendShapeBindingMerger(Dictionary<BlendShapeKey, BlendShapeClip> clipMap, Transform root)
{
foreach (var kv in clipMap)
{
foreach (var binding in kv.Value.Values)
{
if (!m_blendShapeSetterMap.ContainsKey(binding))
{
var _target = root.Find(binding.RelativePath);
SkinnedMeshRenderer target = null;
if (_target != null)
{
target = _target.GetComponent<SkinnedMeshRenderer>();
}
if (target != null)
{
if (binding.Index >= 0 && binding.Index < target.sharedMesh.blendShapeCount)
{
m_blendShapeSetterMap.Add(binding, x =>
{
target.SetBlendShapeWeight(binding.Index, x);
});
}
else
{
Debug.LogWarningFormat("Invalid blendshape binding: {0}: {1}", target.name, binding);
}
}
else
{
Debug.LogWarningFormat("SkinnedMeshRenderer: {0} not found", binding.RelativePath);
}
}
}
}
}
public void ImmediatelySetValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.Values)
{
Action<float> setter;
if (m_blendShapeSetterMap.TryGetValue(binding, out setter))
{
setter(binding.Weight * value);
}
}
}
public void AccumulateValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.Values)
{
float acc;
if (m_blendShapeValueMap.TryGetValue(binding, out acc))
{
m_blendShapeValueMap[binding] = acc + binding.Weight * value;
}
else
{
m_blendShapeValueMap[binding] = binding.Weight * value;
}
}
}
public void Apply()
{
foreach (var kv in m_blendShapeValueMap)
{
Action<float> setter;
if (m_blendShapeSetterMap.TryGetValue(kv.Key, out setter))
{
setter(kv.Value);
}
}
m_blendShapeValueMap.Clear();
}
}
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VRM
{
///
/// A.Value * A.Weight + B.Value * B.Weight ...
///
class BlendShapeBindingMerger
{
class DictionaryKeyBlendShapeBindingComparer : IEqualityComparer<BlendShapeBinding>
{
public bool Equals(BlendShapeBinding x, BlendShapeBinding y)
{
return x.RelativePath == y.RelativePath
&& x.Index == y.Index;
}
public int GetHashCode(BlendShapeBinding obj)
{
return obj.RelativePath.GetHashCode() + obj.Index;
}
}
private static DictionaryKeyBlendShapeBindingComparer comparer = new DictionaryKeyBlendShapeBindingComparer();
/// <summary>
/// BlendShapeの適用値を蓄積する
/// </summary>
/// <typeparam name="BlendShapeBinding"></typeparam>
/// <typeparam name="float"></typeparam>
/// <returns></returns>
Dictionary<BlendShapeBinding, float> m_blendShapeValueMap = new Dictionary<BlendShapeBinding, float>(comparer);
/// <summary>
///
/// </summary>
/// <returns></returns>
Dictionary<BlendShapeBinding, Action<float>> m_blendShapeSetterMap = new Dictionary<BlendShapeBinding, Action<float>>(comparer);
public BlendShapeBindingMerger(Dictionary<BlendShapeKey, BlendShapeClip> clipMap, Transform root)
{
foreach (var kv in clipMap)
{
foreach (var binding in kv.Value.Values)
{
if (!m_blendShapeSetterMap.ContainsKey(binding))
{
var _target = root.Find(binding.RelativePath);
SkinnedMeshRenderer target = null;
if (_target != null)
{
target = _target.GetComponent<SkinnedMeshRenderer>();
}
if (target != null)
{
if (binding.Index >= 0 && binding.Index < target.sharedMesh.blendShapeCount)
{
m_blendShapeSetterMap.Add(binding, x =>
{
target.SetBlendShapeWeight(binding.Index, x);
});
}
else
{
Debug.LogWarningFormat("Invalid blendshape binding: {0}: {1}", target.name, binding);
}
}
else
{
Debug.LogWarningFormat("SkinnedMeshRenderer: {0} not found", binding.RelativePath);
}
}
}
}
}
public void ImmediatelySetValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.Values)
{
Action<float> setter;
if (m_blendShapeSetterMap.TryGetValue(binding, out setter))
{
setter(binding.Weight * value);
}
}
}
public void AccumulateValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.Values)
{
float acc;
if (m_blendShapeValueMap.TryGetValue(binding, out acc))
{
m_blendShapeValueMap[binding] = acc + binding.Weight * value;
}
else
{
m_blendShapeValueMap[binding] = binding.Weight * value;
}
}
}
public void Apply()
{
foreach (var kv in m_blendShapeValueMap)
{
Action<float> setter;
if (m_blendShapeSetterMap.TryGetValue(kv.Key, out setter))
{
setter(kv.Value);
}
}
m_blendShapeValueMap.Clear();
}
}
}

View File

@ -1,187 +1,187 @@
using System;
using UnityEngine;
namespace VRM
{
[Serializable]
public struct BlendShapeBinding : IEquatable<BlendShapeBinding>
{
public String RelativePath;
public int Index;
public float Weight;
public override string ToString()
{
return string.Format("{0}[{1}]=>{2}", RelativePath, Index, Weight);
}
public bool Equals(BlendShapeBinding other)
{
return string.Equals(RelativePath, other.RelativePath) && Index == other.Index && Weight.Equals(other.Weight);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is BlendShapeBinding && Equals((BlendShapeBinding)obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (RelativePath != null ? RelativePath.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ Index;
hashCode = (hashCode * 397) ^ Weight.GetHashCode();
return hashCode;
}
}
}
[Serializable]
public struct MaterialValueBinding : IEquatable<MaterialValueBinding>
{
public String MaterialName;
public String ValueName;
public Vector4 TargetValue;
public Vector4 BaseValue;
public bool Equals(MaterialValueBinding other)
{
return string.Equals(MaterialName, other.MaterialName) && string.Equals(ValueName, other.ValueName) && TargetValue.Equals(other.TargetValue) && BaseValue.Equals(other.BaseValue);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is MaterialValueBinding && Equals((MaterialValueBinding)obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (MaterialName != null ? MaterialName.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (ValueName != null ? ValueName.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ TargetValue.GetHashCode();
hashCode = (hashCode * 397) ^ BaseValue.GetHashCode();
return hashCode;
}
}
}
[CreateAssetMenu(menuName = "VRM/BlendShapeClip")]
public class BlendShapeClip : ScriptableObject
{
#if UNITY_EDITOR
/// <summary>
/// Preview 用のObject参照
/// </summary>
[SerializeField]
GameObject m_prefab;
public GameObject Prefab
{
set { m_prefab = value; }
get
{
if (m_prefab == null)
{
var assetPath = UnityEditor.AssetDatabase.GetAssetPath(this);
if (!string.IsNullOrEmpty(assetPath))
{
// if asset is subasset of prefab
m_prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
if (m_prefab == null)
{
var parent = UniGLTF.UnityPath.FromAsset(this).Parent;
var prefabPath = parent.Parent.Child(parent.FileNameWithoutExtension + ".prefab");
m_prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath.Value);
}
}
}
return m_prefab;
}
}
/// <summary>
/// Apply BlendShape for Preview
/// </summary>
/// <param name="root"></param>
/// <param name="value"></param>
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>();
if (sr != null)
{
sr.SetBlendShapeWeight(x.Index, x.Weight * value);
}
}
}
}
/*
if (MaterialValues != null)
{
foreach (var x in MaterialValues)
{
var target = root.Find(x.RelativePath);
if (target != null)
{
var sr = target.GetComponent<SkinnedMeshRenderer>();
if (sr != null)
{
var m = sr.sharedMaterials[x.Index];
var color = x.BaseValue + (x.TargetValue - x.BaseValue) * value;
m.SetColor(x.ValueName, color);
}
}
}
}
*/
}
#endif
/// <summary>
/// BlendShapePresetがUnknown場合の識別子
/// </summary>
[SerializeField]
public string BlendShapeName = "";
/// <summary>
/// BlendShapePresetを識別する。Unknownの場合は、BlendShapeNameで識別する
/// </summary>
[SerializeField]
public BlendShapePreset Preset;
/// <summary>
/// BlendShapeに対する参照(index ベース)
/// </summary>
/// <value></value>
[SerializeField]
public BlendShapeBinding[] Values = new BlendShapeBinding[] { };
/// <summary>
/// マテリアルに対する参照(名前ベース)
/// </summary>
/// <value></value>
[SerializeField]
public MaterialValueBinding[] MaterialValues = new MaterialValueBinding[] { };
/// <summary>
/// UniVRM-0.45: trueの場合、このBlendShapeClipは0と1の間の中間値を取らない。四捨五入する
/// </summary>
[SerializeField]
public bool IsBinary;
// [SerializeField]
// public Texture2D Thumbnail;
}
}
using System;
using UnityEngine;
namespace VRM
{
[Serializable]
public struct BlendShapeBinding : IEquatable<BlendShapeBinding>
{
public String RelativePath;
public int Index;
public float Weight;
public override string ToString()
{
return string.Format("{0}[{1}]=>{2}", RelativePath, Index, Weight);
}
public bool Equals(BlendShapeBinding other)
{
return string.Equals(RelativePath, other.RelativePath) && Index == other.Index && Weight.Equals(other.Weight);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is BlendShapeBinding && Equals((BlendShapeBinding)obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (RelativePath != null ? RelativePath.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ Index;
hashCode = (hashCode * 397) ^ Weight.GetHashCode();
return hashCode;
}
}
}
[Serializable]
public struct MaterialValueBinding : IEquatable<MaterialValueBinding>
{
public String MaterialName;
public String ValueName;
public Vector4 TargetValue;
public Vector4 BaseValue;
public bool Equals(MaterialValueBinding other)
{
return string.Equals(MaterialName, other.MaterialName) && string.Equals(ValueName, other.ValueName) && TargetValue.Equals(other.TargetValue) && BaseValue.Equals(other.BaseValue);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is MaterialValueBinding && Equals((MaterialValueBinding)obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (MaterialName != null ? MaterialName.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (ValueName != null ? ValueName.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ TargetValue.GetHashCode();
hashCode = (hashCode * 397) ^ BaseValue.GetHashCode();
return hashCode;
}
}
}
[CreateAssetMenu(menuName = "VRM/BlendShapeClip")]
public class BlendShapeClip : ScriptableObject
{
#if UNITY_EDITOR
/// <summary>
/// Preview 用のObject参照
/// </summary>
[SerializeField]
GameObject m_prefab;
public GameObject Prefab
{
set { m_prefab = value; }
get
{
if (m_prefab == null)
{
var assetPath = UnityEditor.AssetDatabase.GetAssetPath(this);
if (!string.IsNullOrEmpty(assetPath))
{
// if asset is subasset of prefab
m_prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
if (m_prefab == null)
{
var parent = UniGLTF.UnityPath.FromAsset(this).Parent;
var prefabPath = parent.Parent.Child(parent.FileNameWithoutExtension + ".prefab");
m_prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath.Value);
}
}
}
return m_prefab;
}
}
/// <summary>
/// Apply BlendShape for Preview
/// </summary>
/// <param name="root"></param>
/// <param name="value"></param>
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>();
if (sr != null)
{
sr.SetBlendShapeWeight(x.Index, x.Weight * value);
}
}
}
}
/*
if (MaterialValues != null)
{
foreach (var x in MaterialValues)
{
var target = root.Find(x.RelativePath);
if (target != null)
{
var sr = target.GetComponent<SkinnedMeshRenderer>();
if (sr != null)
{
var m = sr.sharedMaterials[x.Index];
var color = x.BaseValue + (x.TargetValue - x.BaseValue) * value;
m.SetColor(x.ValueName, color);
}
}
}
}
*/
}
#endif
/// <summary>
/// BlendShapePresetがUnknown場合の識別子
/// </summary>
[SerializeField]
public string BlendShapeName = "";
/// <summary>
/// BlendShapePresetを識別する。Unknownの場合は、BlendShapeNameで識別する
/// </summary>
[SerializeField]
public BlendShapePreset Preset;
/// <summary>
/// BlendShapeに対する参照(index ベース)
/// </summary>
/// <value></value>
[SerializeField]
public BlendShapeBinding[] Values = new BlendShapeBinding[] { };
/// <summary>
/// マテリアルに対する参照(名前ベース)
/// </summary>
/// <value></value>
[SerializeField]
public MaterialValueBinding[] MaterialValues = new MaterialValueBinding[] { };
/// <summary>
/// UniVRM-0.45: trueの場合、このBlendShapeClipは0と1の間の中間値を取らない。四捨五入する
/// </summary>
[SerializeField]
public bool IsBinary;
// [SerializeField]
// public Texture2D Thumbnail;
}
}

View File

@ -1,56 +1,56 @@
using UnityEngine;
using System.Linq;
using System;
namespace VRM
{
[Obsolete("Use VRMBlendShapeProxy")]
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];
if (binding.Index >= 0 && binding.Index < target.sharedMesh.blendShapeCount)
{
target.SetBlendShapeWeight(binding.Index, binding.Weight * value);
}
}
}
}
}
using UnityEngine;
using System.Linq;
using System;
namespace VRM
{
[Obsolete("Use VRMBlendShapeProxy")]
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];
if (binding.Index >= 0 && binding.Index < target.sharedMesh.blendShapeCount)
{
target.SetBlendShapeWeight(binding.Index, binding.Weight * value);
}
}
}
}
}

View File

@ -1,105 +1,105 @@
using System;
namespace VRM
{
[Serializable]
public struct BlendShapeKey : IEquatable<BlendShapeKey>, IComparable<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)
{
return ID == other.ID;
}
public override bool Equals(object obj)
{
if (obj is BlendShapeKey)
{
return Equals((BlendShapeKey)obj);
}
else
{
return false;
}
}
public override int GetHashCode()
{
return ID.GetHashCode();
}
public static BlendShapeKey CreateFrom(BlendShapeClip clip)
{
if (clip == null)
{
return default(BlendShapeKey);
}
return new BlendShapeKey(clip.BlendShapeName, clip.Preset);
}
public bool Match(BlendShapeClip clip)
{
return this.Equals(CreateFrom(clip));
}
public int CompareTo(BlendShapeKey other)
{
if (Preset != other.Preset)
{
return Preset - other.Preset;
}
return 0;
}
}
}
using System;
namespace VRM
{
[Serializable]
public struct BlendShapeKey : IEquatable<BlendShapeKey>, IComparable<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)
{
return ID == other.ID;
}
public override bool Equals(object obj)
{
if (obj is BlendShapeKey)
{
return Equals((BlendShapeKey)obj);
}
else
{
return false;
}
}
public override int GetHashCode()
{
return ID.GetHashCode();
}
public static BlendShapeKey CreateFrom(BlendShapeClip clip)
{
if (clip == null)
{
return default(BlendShapeKey);
}
return new BlendShapeKey(clip.BlendShapeName, clip.Preset);
}
public bool Match(BlendShapeClip clip)
{
return this.Equals(CreateFrom(clip));
}
public int CompareTo(BlendShapeKey other)
{
if (Preset != other.Preset)
{
return Preset - other.Preset;
}
return 0;
}
}
}

View File

@ -1,149 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
namespace VRM
{
/// <summary>
/// ブレンドシェイプを蓄えてまとめて適用するクラス
/// </summary>
class BlendShapeMerger
{
/// <summary>
/// Key からBlendShapeClipを得る
/// </summary>
Dictionary<BlendShapeKey, BlendShapeClip> m_clipMap;
/// <summary>
/// BlendShape のWeightを記録する
/// </summary>
Dictionary<BlendShapeKey, float> m_valueMap;
BlendShapeBindingMerger m_blendShapeBindingMerger;
MaterialValueBindingMerger m_materialValueBindingMerger;
public BlendShapeMerger(IEnumerable<BlendShapeClip> clips, Transform root)
{
m_clipMap = clips.ToDictionary(x => BlendShapeKey.CreateFrom(x), x => x);
m_valueMap = new Dictionary<BlendShapeKey, float>();
m_blendShapeBindingMerger = new BlendShapeBindingMerger(m_clipMap, root);
m_materialValueBindingMerger = new MaterialValueBindingMerger(m_clipMap, root);
}
/*
public void Clear()
{
foreach (var kv in m_valueMap.ToArray())
{
SetValue(kv.Key, kv.Value, false);
}
Apply();
}
*/
/// <summary>
/// 蓄積した値を適用する
/// </summary>
public void Apply()
{
m_blendShapeBindingMerger.Apply();
m_materialValueBindingMerger.Apply();
}
/// <summary>
/// まとめて反映する。1フレームに1回呼び出されることを想定
/// </summary>
/// <param name="values"></param>
public void SetValues(IEnumerable<KeyValuePair<BlendShapeKey, float>> values)
{
foreach (var kv in values)
{
AccumulateValue(kv.Key, kv.Value);
}
Apply();
}
/// <summary>
/// 即時に反映しない。後にApplyによって反映する
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AccumulateValue(BlendShapeKey key, float value)
{
m_valueMap[key] = value;
BlendShapeClip clip;
if (!m_clipMap.TryGetValue(key, out clip))
{
return;
}
if (clip.IsBinary)
{
value = Mathf.Round(value);
}
m_blendShapeBindingMerger.AccumulateValue(clip, value);
m_materialValueBindingMerger.AccumulateValue(clip, value);
}
/// <summary>
/// 即時に反映する
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void ImmediatelySetValue(BlendShapeKey key, float value)
{
m_valueMap[key] = value;
BlendShapeClip clip;
if (!m_clipMap.TryGetValue(key, out clip))
{
return;
}
if (clip.IsBinary)
{
value = Mathf.Round(value);
}
m_blendShapeBindingMerger.ImmediatelySetValue(clip, value);
m_materialValueBindingMerger.ImmediatelySetValue(clip, value);
}
public void SetValue(BlendShapeKey key, float value, bool immediately)
{
if (immediately)
{
ImmediatelySetValue(key, value);
}
else
{
AccumulateValue(key, value);
}
}
public float GetValue(BlendShapeKey key)
{
float value;
if (!m_valueMap.TryGetValue(key, out value))
{
return 0;
}
return value;
}
public void RestoreMaterialInitialValues(IEnumerable<BlendShapeClip> clips)
{
m_materialValueBindingMerger.RestoreMaterialInitialValues(clips);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
namespace VRM
{
/// <summary>
/// ブレンドシェイプを蓄えてまとめて適用するクラス
/// </summary>
class BlendShapeMerger
{
/// <summary>
/// Key からBlendShapeClipを得る
/// </summary>
Dictionary<BlendShapeKey, BlendShapeClip> m_clipMap;
/// <summary>
/// BlendShape のWeightを記録する
/// </summary>
Dictionary<BlendShapeKey, float> m_valueMap;
BlendShapeBindingMerger m_blendShapeBindingMerger;
MaterialValueBindingMerger m_materialValueBindingMerger;
public BlendShapeMerger(IEnumerable<BlendShapeClip> clips, Transform root)
{
m_clipMap = clips.ToDictionary(x => BlendShapeKey.CreateFrom(x), x => x);
m_valueMap = new Dictionary<BlendShapeKey, float>();
m_blendShapeBindingMerger = new BlendShapeBindingMerger(m_clipMap, root);
m_materialValueBindingMerger = new MaterialValueBindingMerger(m_clipMap, root);
}
/*
public void Clear()
{
foreach (var kv in m_valueMap.ToArray())
{
SetValue(kv.Key, kv.Value, false);
}
Apply();
}
*/
/// <summary>
/// 蓄積した値を適用する
/// </summary>
public void Apply()
{
m_blendShapeBindingMerger.Apply();
m_materialValueBindingMerger.Apply();
}
/// <summary>
/// まとめて反映する。1フレームに1回呼び出されることを想定
/// </summary>
/// <param name="values"></param>
public void SetValues(IEnumerable<KeyValuePair<BlendShapeKey, float>> values)
{
foreach (var kv in values)
{
AccumulateValue(kv.Key, kv.Value);
}
Apply();
}
/// <summary>
/// 即時に反映しない。後にApplyによって反映する
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AccumulateValue(BlendShapeKey key, float value)
{
m_valueMap[key] = value;
BlendShapeClip clip;
if (!m_clipMap.TryGetValue(key, out clip))
{
return;
}
if (clip.IsBinary)
{
value = Mathf.Round(value);
}
m_blendShapeBindingMerger.AccumulateValue(clip, value);
m_materialValueBindingMerger.AccumulateValue(clip, value);
}
/// <summary>
/// 即時に反映する
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void ImmediatelySetValue(BlendShapeKey key, float value)
{
m_valueMap[key] = value;
BlendShapeClip clip;
if (!m_clipMap.TryGetValue(key, out clip))
{
return;
}
if (clip.IsBinary)
{
value = Mathf.Round(value);
}
m_blendShapeBindingMerger.ImmediatelySetValue(clip, value);
m_materialValueBindingMerger.ImmediatelySetValue(clip, value);
}
public void SetValue(BlendShapeKey key, float value, bool immediately)
{
if (immediately)
{
ImmediatelySetValue(key, value);
}
else
{
AccumulateValue(key, value);
}
}
public float GetValue(BlendShapeKey key)
{
float value;
if (!m_valueMap.TryGetValue(key, out value))
{
return 0;
}
return value;
}
public void RestoreMaterialInitialValues(IEnumerable<BlendShapeClip> clips)
{
m_materialValueBindingMerger.RestoreMaterialInitialValues(clips);
}
}
}

View File

@ -1,119 +1,119 @@
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;
protected Coroutine m_coroutine;
//static readonly string BLINK_NAME = BlendShapePreset.Blink.ToString();
float m_nextRequest;
bool m_request;
public bool Request
{
get { return m_request; }
set
{
if (Time.time < m_nextRequest)
{
return;
}
m_request = value;
m_nextRequest = Time.time + 1.0f;
}
}
protected IEnumerator BlinkRoutine()
{
while (true)
{
var waitTime = Time.time + Random.value * m_interVal;
while (waitTime > Time.time)
{
if (Request)
{
m_request = false;
break;
}
yield return null;
}
// close
var value = 0.0f;
var closeSpeed = 1.0f / m_closeSeconds;
while (true)
{
value += Time.deltaTime * closeSpeed;
if (value >= 1.0f)
{
break;
}
BlendShapes.ImmediatelySetValue(BlendShapePreset.Blink, value);
yield return null;
}
BlendShapes.ImmediatelySetValue(BlendShapePreset.Blink, 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.ImmediatelySetValue(BlendShapePreset.Blink, value);
yield return null;
}
BlendShapes.ImmediatelySetValue(BlendShapePreset.Blink, 0);
}
}
private void Awake()
{
if (BlendShapes == null) BlendShapes = GetComponent<VRM.VRMBlendShapeProxy>();
}
private void OnEnable()
{
m_coroutine = StartCoroutine(BlinkRoutine());
}
private void OnDisable()
{
if (m_coroutine != null)
{
StopCoroutine(m_coroutine);
m_coroutine = null;
}
}
}
}
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;
protected Coroutine m_coroutine;
//static readonly string BLINK_NAME = BlendShapePreset.Blink.ToString();
float m_nextRequest;
bool m_request;
public bool Request
{
get { return m_request; }
set
{
if (Time.time < m_nextRequest)
{
return;
}
m_request = value;
m_nextRequest = Time.time + 1.0f;
}
}
protected IEnumerator BlinkRoutine()
{
while (true)
{
var waitTime = Time.time + Random.value * m_interVal;
while (waitTime > Time.time)
{
if (Request)
{
m_request = false;
break;
}
yield return null;
}
// close
var value = 0.0f;
var closeSpeed = 1.0f / m_closeSeconds;
while (true)
{
value += Time.deltaTime * closeSpeed;
if (value >= 1.0f)
{
break;
}
BlendShapes.ImmediatelySetValue(BlendShapePreset.Blink, value);
yield return null;
}
BlendShapes.ImmediatelySetValue(BlendShapePreset.Blink, 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.ImmediatelySetValue(BlendShapePreset.Blink, value);
yield return null;
}
BlendShapes.ImmediatelySetValue(BlendShapePreset.Blink, 0);
}
}
private void Awake()
{
if (BlendShapes == null) BlendShapes = GetComponent<VRM.VRMBlendShapeProxy>();
}
private void OnEnable()
{
m_coroutine = StartCoroutine(BlinkRoutine());
}
private void OnDisable()
{
if (m_coroutine != null)
{
StopCoroutine(m_coroutine);
m_coroutine = null;
}
}
}
}

View File

@ -1,159 +1,159 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UniGLTF;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace VRM
{
[CustomEditor(typeof(BlendShapeAvatar))]
public class BlendShapeAvatarEditor : PreviewEditor
{
ReorderableList m_clipList;
BlendShapeClipSelector m_selector;
SerializedBlendShapeEditor m_clipEditor;
protected override PreviewSceneManager.BakeValue GetBakeValue()
{
var clip = m_selector.Selected;
var value = new PreviewSceneManager.BakeValue();
if (clip != null)
{
value.BlendShapeBindings = clip.Values;
value.MaterialValueBindings = clip.MaterialValues;
value.Weight = 1.0f;
}
return value;
}
void OnSelected(BlendShapeClip clip)
{
if (PreviewSceneManager == null)
{
m_clipEditor = null;
}
else if (clip != null)
{
m_clipEditor = new SerializedBlendShapeEditor(clip, PreviewSceneManager);
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
{
BlendShapeBindings = clip.Values,
MaterialValueBindings = clip.MaterialValues,
Weight = 1.0f
});
}
else
{
m_clipEditor = null;
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue());
}
}
protected override void OnEnable()
{
m_selector = new BlendShapeClipSelector((BlendShapeAvatar)target, OnSelected);
var prop = serializedObject.FindProperty("Clips");
m_clipList = new ReorderableList(serializedObject, prop);
m_clipList.drawHeaderCallback = (rect) =>
EditorGUI.LabelField(rect, "BlendShapeClips");
m_clipList.elementHeight = BlendShapeClipDrawer.Height;
m_clipList.drawElementCallback = (rect, index, isActive, isFocused) =>
{
var element = prop.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
EditorGUI.PropertyField(rect, element);
};
m_clipList.onAddCallback += (list) =>
{
// Add slot
prop.arraySize++;
// select last item
list.index = prop.arraySize - 1;
// get last item
var element = prop.GetArrayElementAtIndex(list.index);
element.objectReferenceValue = null;
var dir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(target));
var path = EditorUtility.SaveFilePanel(
"Create BlendShapeClip",
dir,
string.Format("BlendShapeClip#{0}.asset", list.count),
"asset");
if (!string.IsNullOrEmpty(path))
{
var clip = BlendShapeAvatar.CreateBlendShapeClip(path.ToUnityRelativePath());
//clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(target));
element.objectReferenceValue = clip;
}
};
m_clipList.onSelectCallback += (list) =>
{
var a = list.serializedProperty;
var selected = a.GetArrayElementAtIndex(list.index);
OnSelected((BlendShapeClip)selected.objectReferenceValue);
};
//m_clipList.onCanRemoveCallback += list => true;
base.OnEnable();
OnSelected(m_selector.Selected);
}
int m_mode;
static readonly string[] MODES = new string[]{
"Editor",
"List"
};
public override void OnInspectorGUI()
{
serializedObject.Update();
base.OnInspectorGUI();
m_mode = GUILayout.Toolbar(m_mode, MODES);
switch (m_mode)
{
case 0:
m_selector.SelectGUI();
if (m_clipEditor != null)
{
Separator();
var result = m_clipEditor.Draw();
if (result.Changed)
{
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
{
BlendShapeBindings = result.BlendShapeBindings,
MaterialValueBindings = result.MaterialValueBindings,
Weight = 1.0f,
});
}
}
break;
case 1:
m_clipList.DoLayoutList();
break;
default:
throw new NotImplementedException();
}
serializedObject.ApplyModifiedProperties();
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UniGLTF;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace VRM
{
[CustomEditor(typeof(BlendShapeAvatar))]
public class BlendShapeAvatarEditor : PreviewEditor
{
ReorderableList m_clipList;
BlendShapeClipSelector m_selector;
SerializedBlendShapeEditor m_clipEditor;
protected override PreviewSceneManager.BakeValue GetBakeValue()
{
var clip = m_selector.Selected;
var value = new PreviewSceneManager.BakeValue();
if (clip != null)
{
value.BlendShapeBindings = clip.Values;
value.MaterialValueBindings = clip.MaterialValues;
value.Weight = 1.0f;
}
return value;
}
void OnSelected(BlendShapeClip clip)
{
if (PreviewSceneManager == null)
{
m_clipEditor = null;
}
else if (clip != null)
{
m_clipEditor = new SerializedBlendShapeEditor(clip, PreviewSceneManager);
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
{
BlendShapeBindings = clip.Values,
MaterialValueBindings = clip.MaterialValues,
Weight = 1.0f
});
}
else
{
m_clipEditor = null;
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue());
}
}
protected override void OnEnable()
{
m_selector = new BlendShapeClipSelector((BlendShapeAvatar)target, OnSelected);
var prop = serializedObject.FindProperty("Clips");
m_clipList = new ReorderableList(serializedObject, prop);
m_clipList.drawHeaderCallback = (rect) =>
EditorGUI.LabelField(rect, "BlendShapeClips");
m_clipList.elementHeight = BlendShapeClipDrawer.Height;
m_clipList.drawElementCallback = (rect, index, isActive, isFocused) =>
{
var element = prop.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
EditorGUI.PropertyField(rect, element);
};
m_clipList.onAddCallback += (list) =>
{
// Add slot
prop.arraySize++;
// select last item
list.index = prop.arraySize - 1;
// get last item
var element = prop.GetArrayElementAtIndex(list.index);
element.objectReferenceValue = null;
var dir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(target));
var path = EditorUtility.SaveFilePanel(
"Create BlendShapeClip",
dir,
string.Format("BlendShapeClip#{0}.asset", list.count),
"asset");
if (!string.IsNullOrEmpty(path))
{
var clip = BlendShapeAvatar.CreateBlendShapeClip(path.ToUnityRelativePath());
//clip.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GetAssetPath(target));
element.objectReferenceValue = clip;
}
};
m_clipList.onSelectCallback += (list) =>
{
var a = list.serializedProperty;
var selected = a.GetArrayElementAtIndex(list.index);
OnSelected((BlendShapeClip)selected.objectReferenceValue);
};
//m_clipList.onCanRemoveCallback += list => true;
base.OnEnable();
OnSelected(m_selector.Selected);
}
int m_mode;
static readonly string[] MODES = new string[]{
"Editor",
"List"
};
public override void OnInspectorGUI()
{
serializedObject.Update();
base.OnInspectorGUI();
m_mode = GUILayout.Toolbar(m_mode, MODES);
switch (m_mode)
{
case 0:
m_selector.SelectGUI();
if (m_clipEditor != null)
{
Separator();
var result = m_clipEditor.Draw();
if (result.Changed)
{
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
{
BlendShapeBindings = result.BlendShapeBindings,
MaterialValueBindings = result.MaterialValueBindings,
Weight = 1.0f,
});
}
}
break;
case 1:
m_clipList.DoLayoutList();
break;
default:
throw new NotImplementedException();
}
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@ -1,170 +1,170 @@
using System;
using System.IO;
using UniGLTF;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace VRM
{
[CustomEditor(typeof(BlendShapeClip))]
public class BlendShapeClipEditor : PreviewEditor
{
SerializedBlendShapeEditor m_serializedEditor;
BlendShapeClip m_target;
protected override PreviewSceneManager.BakeValue GetBakeValue()
{
return new PreviewSceneManager.BakeValue
{
BlendShapeBindings = m_target.Values,
MaterialValueBindings = m_target.MaterialValues,
Weight = 1.0f,
};
}
//SerializedProperty m_thumbnailProp;
SerializedProperty m_isBinaryProp;
protected override GameObject GetPrefab()
{
return m_target.Prefab;
}
protected override void OnEnable()
{
m_target = (BlendShapeClip)target;
base.OnEnable();
}
float m_previewSlider = 1.0f;
static Texture2D SaveResizedImage(RenderTexture rt, UnityPath path, int size)
{
var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
RenderTexture.active = rt;
tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
tex.Apply();
//TextureScale.Scale(tex, size, size);
tex = TextureScale.GetResized(tex, size, size);
byte[] bytes;
switch (path.Extension.ToLower())
{
case ".png":
bytes = tex.EncodeToPNG();
break;
case ".jpg":
bytes = tex.EncodeToJPG();
break;
default:
throw new Exception();
}
if (Application.isPlaying)
{
UnityEngine.Object.Destroy(tex);
}
else
{
UnityEngine.Object.DestroyImmediate(tex);
}
File.WriteAllBytes(path.FullPath, bytes);
path.ImportAsset();
return path.LoadAsset<Texture2D>();
}
public override void OnInspectorGUI()
{
if (PreviewSceneManager == null)
{
return;
}
serializedObject.Update();
if (m_serializedEditor == null)
{
m_serializedEditor = new SerializedBlendShapeEditor(serializedObject, PreviewSceneManager);
//m_thumbnailProp = serializedObject.FindProperty("Thumbnail");
m_isBinaryProp = serializedObject.FindProperty("IsBinary");
}
int thumbnailSize = 96;
EditorGUILayout.BeginHorizontal();
/*
var objectReferenceValue = EditorGUILayout.ObjectField(m_thumbnailProp.objectReferenceValue, typeof(Texture), false,
GUILayout.Width(thumbnailSize), GUILayout.Height(thumbnailSize));
if (m_thumbnailProp.objectReferenceValue != objectReferenceValue)
{
m_thumbnailProp.objectReferenceValue = objectReferenceValue;
serializedObject.ApplyModifiedProperties();
}
*/
var changed = false;
EditorGUILayout.BeginVertical();
base.OnInspectorGUI();
EditorGUILayout.LabelField("Preview Weight");
var previewSlider = EditorGUILayout.Slider(m_previewSlider, 0, 1.0f);
GUI.enabled = PreviewTexture != null;
/*
if (GUILayout.Button("save thumbnail"))
{
//var ext = "jpg";
var ext = "png";
var asset = UnityPath.FromAsset(target);
var path = EditorUtility.SaveFilePanel(
"save thumbnail",
asset.Parent.FullPath,
string.Format("{0}.{1}", asset.FileNameWithoutExtension, ext),
ext);
if (!string.IsNullOrEmpty(path))
{
var thumbnail = SaveResizedImage(PreviewTexture, UnityPath.FromFullpath(path),
BlendShapeClipDrawer.ThumbnailSize);
m_thumbnailProp.objectReferenceValue = thumbnail;
serializedObject.ApplyModifiedProperties();
}
}
*/
GUI.enabled = true;
EditorGUILayout.EndVertical();
if (m_isBinaryProp.boolValue)
{
previewSlider = Mathf.Round(previewSlider);
}
if (previewSlider != m_previewSlider)
{
m_previewSlider = previewSlider;
changed = true;
}
EditorGUILayout.EndHorizontal();
Separator();
EditorGUILayout.Space();
var result = m_serializedEditor.Draw();
if ((changed || result.Changed) && PreviewSceneManager != null)
{
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
{
BlendShapeBindings = result.BlendShapeBindings,
MaterialValueBindings = result.MaterialValueBindings,
Weight = m_previewSlider
});
}
}
public override string GetInfoString()
{
return BlendShapeKey.CreateFrom((BlendShapeClip)target).ToString();
}
}
}
using System;
using System.IO;
using UniGLTF;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace VRM
{
[CustomEditor(typeof(BlendShapeClip))]
public class BlendShapeClipEditor : PreviewEditor
{
SerializedBlendShapeEditor m_serializedEditor;
BlendShapeClip m_target;
protected override PreviewSceneManager.BakeValue GetBakeValue()
{
return new PreviewSceneManager.BakeValue
{
BlendShapeBindings = m_target.Values,
MaterialValueBindings = m_target.MaterialValues,
Weight = 1.0f,
};
}
//SerializedProperty m_thumbnailProp;
SerializedProperty m_isBinaryProp;
protected override GameObject GetPrefab()
{
return m_target.Prefab;
}
protected override void OnEnable()
{
m_target = (BlendShapeClip)target;
base.OnEnable();
}
float m_previewSlider = 1.0f;
static Texture2D SaveResizedImage(RenderTexture rt, UnityPath path, int size)
{
var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
RenderTexture.active = rt;
tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
tex.Apply();
//TextureScale.Scale(tex, size, size);
tex = TextureScale.GetResized(tex, size, size);
byte[] bytes;
switch (path.Extension.ToLower())
{
case ".png":
bytes = tex.EncodeToPNG();
break;
case ".jpg":
bytes = tex.EncodeToJPG();
break;
default:
throw new Exception();
}
if (Application.isPlaying)
{
UnityEngine.Object.Destroy(tex);
}
else
{
UnityEngine.Object.DestroyImmediate(tex);
}
File.WriteAllBytes(path.FullPath, bytes);
path.ImportAsset();
return path.LoadAsset<Texture2D>();
}
public override void OnInspectorGUI()
{
if (PreviewSceneManager == null)
{
return;
}
serializedObject.Update();
if (m_serializedEditor == null)
{
m_serializedEditor = new SerializedBlendShapeEditor(serializedObject, PreviewSceneManager);
//m_thumbnailProp = serializedObject.FindProperty("Thumbnail");
m_isBinaryProp = serializedObject.FindProperty("IsBinary");
}
int thumbnailSize = 96;
EditorGUILayout.BeginHorizontal();
/*
var objectReferenceValue = EditorGUILayout.ObjectField(m_thumbnailProp.objectReferenceValue, typeof(Texture), false,
GUILayout.Width(thumbnailSize), GUILayout.Height(thumbnailSize));
if (m_thumbnailProp.objectReferenceValue != objectReferenceValue)
{
m_thumbnailProp.objectReferenceValue = objectReferenceValue;
serializedObject.ApplyModifiedProperties();
}
*/
var changed = false;
EditorGUILayout.BeginVertical();
base.OnInspectorGUI();
EditorGUILayout.LabelField("Preview Weight");
var previewSlider = EditorGUILayout.Slider(m_previewSlider, 0, 1.0f);
GUI.enabled = PreviewTexture != null;
/*
if (GUILayout.Button("save thumbnail"))
{
//var ext = "jpg";
var ext = "png";
var asset = UnityPath.FromAsset(target);
var path = EditorUtility.SaveFilePanel(
"save thumbnail",
asset.Parent.FullPath,
string.Format("{0}.{1}", asset.FileNameWithoutExtension, ext),
ext);
if (!string.IsNullOrEmpty(path))
{
var thumbnail = SaveResizedImage(PreviewTexture, UnityPath.FromFullpath(path),
BlendShapeClipDrawer.ThumbnailSize);
m_thumbnailProp.objectReferenceValue = thumbnail;
serializedObject.ApplyModifiedProperties();
}
}
*/
GUI.enabled = true;
EditorGUILayout.EndVertical();
if (m_isBinaryProp.boolValue)
{
previewSlider = Mathf.Round(previewSlider);
}
if (previewSlider != m_previewSlider)
{
m_previewSlider = previewSlider;
changed = true;
}
EditorGUILayout.EndHorizontal();
Separator();
EditorGUILayout.Space();
var result = m_serializedEditor.Draw();
if ((changed || result.Changed) && PreviewSceneManager != null)
{
PreviewSceneManager.Bake(new PreviewSceneManager.BakeValue
{
BlendShapeBindings = result.BlendShapeBindings,
MaterialValueBindings = result.MaterialValueBindings,
Weight = m_previewSlider
});
}
}
public override string GetInfoString()
{
return BlendShapeKey.CreateFrom((BlendShapeClip)target).ToString();
}
}
}

View File

@ -1,341 +1,341 @@
using System;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public static class BlendShapeClipEditorHelper
{
///
/// BlendShape List のElement描画
///
public static bool DrawBlendShapeBinding(Rect position, SerializedProperty property,
PreviewSceneManager scene)
{
bool changed = false;
if (scene != null)
{
var height = 16;
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int pathIndex;
if (StringPopup(rect, property.FindPropertyRelative("RelativePath"), scene.SkinnedMeshRendererPathList, out pathIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
int blendShapeIndex;
if (IntPopup(rect, property.FindPropertyRelative("Index"), scene.GetBlendShapeNames(pathIndex), out blendShapeIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
if (FloatSlider(rect, property.FindPropertyRelative("Weight"), 100))
{
changed = true;
}
}
return changed;
}
///
/// Material List のElement描画
///
public static bool DrawMaterialValueBinding(Rect position, SerializedProperty property,
PreviewSceneManager scene)
{
bool changed = false;
if (scene != null)
{
var height = 16;
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int materialIndex;
if (StringPopup(rect, property.FindPropertyRelative("MaterialName"), scene.MaterialNames, out materialIndex))
{
changed = true;
}
if (materialIndex >= 0)
{
var materialItem = scene.GetMaterialItem(scene.MaterialNames[materialIndex]);
if (materialItem != null)
{
y += height;
rect = new Rect(position.x, y, position.width, height);
// プロパティ名のポップアップ
int propIndex;
if (StringPopup(rect, property.FindPropertyRelative("ValueName"), materialItem.PropNames, out propIndex))
{
changed = true;
}
if (propIndex >= 0)
{
// 有効なプロパティ名が選択された
var propItem = materialItem.PropMap[materialItem.PropNames[propIndex]];
{
switch (propItem.PropertyType)
{
case ShaderUtil.ShaderPropertyType.Color:
{
property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
// max
y += height;
rect = new Rect(position.x, y, position.width, height);
if (ColorProp(rect, property.FindPropertyRelative("TargetValue")))
{
changed = true;
}
}
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
{
property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
// max
y += height;
rect = new Rect(position.x, y, position.width, height);
if (OffsetProp(rect, property.FindPropertyRelative("TargetValue")))
{
changed = true;
}
}
break;
}
}
}
}
}
}
return changed;
}
#region Private
static bool StringPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = Array.IndexOf(options, prop.stringValue);
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.stringValue = options[newIndex];
return true;
}
else
{
return false;
}
}
static bool IntPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = prop.intValue;
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.intValue = newIndex;
return true;
}
else
{
return false;
}
}
static bool FloatSlider(Rect rect, SerializedProperty prop, float maxValue)
{
var oldValue = prop.floatValue;
var newValue = EditorGUI.Slider(rect, prop.floatValue, 0, 100f);
if (newValue != oldValue)
{
prop.floatValue = newValue;
return true;
}
else
{
return false;
}
}
static bool ColorProp(Rect rect, SerializedProperty prop)
{
var oldValue = (Color)prop.vector4Value;
var newValue = EditorGUI.ColorField(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector4Value = newValue;
return true;
}
else
{
return false;
}
}
static Rect AdvanceRect(ref float x, float y, float w, float h)
{
var rect = new Rect(x, y, w, h);
x += w;
return rect;
}
static float[] v2 = new float[2];
static GUIContent[] l2 = new GUIContent[]{
new GUIContent("x"),
new GUIContent("y")
};
static Vector4 TilingOffset(Rect rect, string label, Vector4 src)
{
/*
var style = new GUIStyle()
{
alignment = TextAnchor.MiddleRight,
};
*/
var quad = (rect.width - 56);
var x = rect.x;
//EditorGUIUtility.labelWidth = 18;
EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Tiling");
v2[0] = src.x;
v2[1] = src.y;
EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
src.x = v2[0];
src.y = v2[1];
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
//src.y = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.y);
rect.y += EditorGUIUtility.singleLineHeight;
x = rect.x;
EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Offset");
v2[0] = src.z;
v2[1] = src.w;
EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
src.z = v2[0];
src.w = v2[1];
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad * 2, rect.height), "Offset X", style);
//src.z = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "X", src.z);
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
//src.w = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.w);
return src;
}
static bool OffsetProp(Rect rect, SerializedProperty prop)
{
var oldValue = prop.vector4Value;
//var newValue = EditorGUI.Vector4Field(rect, prop.displayName, oldValue);
var newValue = TilingOffset(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector4Value = newValue;
return true;
}
else
{
return false;
}
}
#endregion
}
/// https://gist.github.com/gszauer/7799899
public class TextureScale
{
private static Color[] texColors;
private static Color[] newColors;
private static int w;
private static float ratioX;
private static float ratioY;
private static int w2;
public static void Scale(Texture2D tex, int newWidth, int newHeight)
{
texColors = tex.GetPixels();
newColors = new Color[newWidth * newHeight];
ratioX = 1.0f / ((float)newWidth / (tex.width - 1));
ratioY = 1.0f / ((float)newHeight / (tex.height - 1));
w = tex.width;
w2 = newWidth;
BilinearScale(0, newHeight);
tex.Resize(newWidth, newHeight);
tex.SetPixels(newColors);
tex.Apply();
}
private static void BilinearScale(int start, int end)
{
for (var y = start; y < end; y++)
{
int yFloor = (int)Mathf.Floor(y * ratioY);
var y1 = yFloor * w;
var y2 = (yFloor + 1) * w;
var yw = y * w2;
for (var x = 0; x < w2; x++)
{
int xFloor = (int)Mathf.Floor(x * ratioX);
var xLerp = x * ratioX - xFloor;
newColors[yw + x] = ColorLerpUnclamped(ColorLerpUnclamped(texColors[y1 + xFloor], texColors[y1 + xFloor + 1], xLerp),
ColorLerpUnclamped(texColors[y2 + xFloor], texColors[y2 + xFloor + 1], xLerp),
y * ratioY - yFloor);
}
}
}
private static Color ColorLerpUnclamped(Color c1, Color c2, float value)
{
return new Color(c1.r + (c2.r - c1.r) * value,
c1.g + (c2.g - c1.g) * value,
c1.b + (c2.b - c1.b) * value,
c1.a + (c2.a - c1.a) * value);
}
/// http://light11.hatenadiary.com/entry/2018/04/19/194015
public static Texture2D GetResized(Texture2D texture, int width, int height)
{
// リサイズ後のサイズを持つRenderTextureを作成して書き込む
var rt = RenderTexture.GetTemporary(width, height);
Graphics.Blit(texture, rt);
// リサイズ後のサイズを持つTexture2Dを作成してRenderTextureから書き込む
var preRT = RenderTexture.active;
RenderTexture.active = rt;
var ret = new Texture2D(width, height);
ret.ReadPixels(new Rect(0, 0, width, height), 0, 0);
ret.Apply();
RenderTexture.active = preRT;
RenderTexture.ReleaseTemporary(rt);
return ret;
}
}
}
using System;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public static class BlendShapeClipEditorHelper
{
///
/// BlendShape List のElement描画
///
public static bool DrawBlendShapeBinding(Rect position, SerializedProperty property,
PreviewSceneManager scene)
{
bool changed = false;
if (scene != null)
{
var height = 16;
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int pathIndex;
if (StringPopup(rect, property.FindPropertyRelative("RelativePath"), scene.SkinnedMeshRendererPathList, out pathIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
int blendShapeIndex;
if (IntPopup(rect, property.FindPropertyRelative("Index"), scene.GetBlendShapeNames(pathIndex), out blendShapeIndex))
{
changed = true;
}
y += height;
rect = new Rect(position.x, y, position.width, height);
if (FloatSlider(rect, property.FindPropertyRelative("Weight"), 100))
{
changed = true;
}
}
return changed;
}
///
/// Material List のElement描画
///
public static bool DrawMaterialValueBinding(Rect position, SerializedProperty property,
PreviewSceneManager scene)
{
bool changed = false;
if (scene != null)
{
var height = 16;
var y = position.y;
var rect = new Rect(position.x, y, position.width, height);
int materialIndex;
if (StringPopup(rect, property.FindPropertyRelative("MaterialName"), scene.MaterialNames, out materialIndex))
{
changed = true;
}
if (materialIndex >= 0)
{
var materialItem = scene.GetMaterialItem(scene.MaterialNames[materialIndex]);
if (materialItem != null)
{
y += height;
rect = new Rect(position.x, y, position.width, height);
// プロパティ名のポップアップ
int propIndex;
if (StringPopup(rect, property.FindPropertyRelative("ValueName"), materialItem.PropNames, out propIndex))
{
changed = true;
}
if (propIndex >= 0)
{
// 有効なプロパティ名が選択された
var propItem = materialItem.PropMap[materialItem.PropNames[propIndex]];
{
switch (propItem.PropertyType)
{
case ShaderUtil.ShaderPropertyType.Color:
{
property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
// max
y += height;
rect = new Rect(position.x, y, position.width, height);
if (ColorProp(rect, property.FindPropertyRelative("TargetValue")))
{
changed = true;
}
}
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
{
property.FindPropertyRelative("BaseValue").vector4Value = propItem.DefaultValues;
// max
y += height;
rect = new Rect(position.x, y, position.width, height);
if (OffsetProp(rect, property.FindPropertyRelative("TargetValue")))
{
changed = true;
}
}
break;
}
}
}
}
}
}
return changed;
}
#region Private
static bool StringPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = Array.IndexOf(options, prop.stringValue);
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.stringValue = options[newIndex];
return true;
}
else
{
return false;
}
}
static bool IntPopup(Rect rect, SerializedProperty prop, string[] options, out int newIndex)
{
if (options == null)
{
newIndex = -1;
return false;
}
var oldIndex = prop.intValue;
newIndex = EditorGUI.Popup(rect, oldIndex, options);
if (newIndex != oldIndex && newIndex >= 0 && newIndex < options.Length)
{
prop.intValue = newIndex;
return true;
}
else
{
return false;
}
}
static bool FloatSlider(Rect rect, SerializedProperty prop, float maxValue)
{
var oldValue = prop.floatValue;
var newValue = EditorGUI.Slider(rect, prop.floatValue, 0, 100f);
if (newValue != oldValue)
{
prop.floatValue = newValue;
return true;
}
else
{
return false;
}
}
static bool ColorProp(Rect rect, SerializedProperty prop)
{
var oldValue = (Color)prop.vector4Value;
var newValue = EditorGUI.ColorField(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector4Value = newValue;
return true;
}
else
{
return false;
}
}
static Rect AdvanceRect(ref float x, float y, float w, float h)
{
var rect = new Rect(x, y, w, h);
x += w;
return rect;
}
static float[] v2 = new float[2];
static GUIContent[] l2 = new GUIContent[]{
new GUIContent("x"),
new GUIContent("y")
};
static Vector4 TilingOffset(Rect rect, string label, Vector4 src)
{
/*
var style = new GUIStyle()
{
alignment = TextAnchor.MiddleRight,
};
*/
var quad = (rect.width - 56);
var x = rect.x;
//EditorGUIUtility.labelWidth = 18;
EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Tiling");
v2[0] = src.x;
v2[1] = src.y;
EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
src.x = v2[0];
src.y = v2[1];
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
//src.y = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.y);
rect.y += EditorGUIUtility.singleLineHeight;
x = rect.x;
EditorGUI.LabelField(AdvanceRect(ref x, rect.y, 40, rect.height), "Offset");
v2[0] = src.z;
v2[1] = src.w;
EditorGUI.MultiFloatField(AdvanceRect(ref x, rect.y, quad, rect.height), l2, v2);
src.z = v2[0];
src.w = v2[1];
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad * 2, rect.height), "Offset X", style);
//src.z = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "X", src.z);
//EditorGUI.LabelField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", style);
//src.w = EditorGUI.FloatField(AdvanceRect(ref x, rect.y, quad, rect.height), "Y", src.w);
return src;
}
static bool OffsetProp(Rect rect, SerializedProperty prop)
{
var oldValue = prop.vector4Value;
//var newValue = EditorGUI.Vector4Field(rect, prop.displayName, oldValue);
var newValue = TilingOffset(rect, prop.displayName, oldValue);
if (newValue != oldValue)
{
prop.vector4Value = newValue;
return true;
}
else
{
return false;
}
}
#endregion
}
/// https://gist.github.com/gszauer/7799899
public class TextureScale
{
private static Color[] texColors;
private static Color[] newColors;
private static int w;
private static float ratioX;
private static float ratioY;
private static int w2;
public static void Scale(Texture2D tex, int newWidth, int newHeight)
{
texColors = tex.GetPixels();
newColors = new Color[newWidth * newHeight];
ratioX = 1.0f / ((float)newWidth / (tex.width - 1));
ratioY = 1.0f / ((float)newHeight / (tex.height - 1));
w = tex.width;
w2 = newWidth;
BilinearScale(0, newHeight);
tex.Resize(newWidth, newHeight);
tex.SetPixels(newColors);
tex.Apply();
}
private static void BilinearScale(int start, int end)
{
for (var y = start; y < end; y++)
{
int yFloor = (int)Mathf.Floor(y * ratioY);
var y1 = yFloor * w;
var y2 = (yFloor + 1) * w;
var yw = y * w2;
for (var x = 0; x < w2; x++)
{
int xFloor = (int)Mathf.Floor(x * ratioX);
var xLerp = x * ratioX - xFloor;
newColors[yw + x] = ColorLerpUnclamped(ColorLerpUnclamped(texColors[y1 + xFloor], texColors[y1 + xFloor + 1], xLerp),
ColorLerpUnclamped(texColors[y2 + xFloor], texColors[y2 + xFloor + 1], xLerp),
y * ratioY - yFloor);
}
}
}
private static Color ColorLerpUnclamped(Color c1, Color c2, float value)
{
return new Color(c1.r + (c2.r - c1.r) * value,
c1.g + (c2.g - c1.g) * value,
c1.b + (c2.b - c1.b) * value,
c1.a + (c2.a - c1.a) * value);
}
/// http://light11.hatenadiary.com/entry/2018/04/19/194015
public static Texture2D GetResized(Texture2D texture, int width, int height)
{
// リサイズ後のサイズを持つRenderTextureを作成して書き込む
var rt = RenderTexture.GetTemporary(width, height);
Graphics.Blit(texture, rt);
// リサイズ後のサイズを持つTexture2Dを作成してRenderTextureから書き込む
var preRT = RenderTexture.active;
RenderTexture.active = rt;
var ret = new Texture2D(width, height);
ret.ReadPixels(new Rect(0, 0, width, height), 0, 0);
ret.Apply();
RenderTexture.active = preRT;
RenderTexture.ReleaseTemporary(rt);
return ret;
}
}
}

View File

@ -1,271 +1,271 @@
using UnityEditor;
using UnityEngine;
using UnityEditorInternal;
using System;
using System.Linq;
using System.Collections.Generic;
namespace VRM
{
/// <summary>
/// Prefabをインスタンス化してPreviewに表示する
///
/// * https://github.com/Unity-Technologies/UnityCsReference/blob/11bcfd801fccd2a52b09bb6fd636c1ddcc9f1705/Editor/Mono/Inspector/ModelInspector.cs
///
/// </summary>
public abstract class PreviewEditor : Editor
{
/// <summary>
/// PreviewRenderUtilityを管理する。
///
/// * PreviewRenderUtility.m_cameraのUnityVersionによる切り分け
///
/// </summary>
PreviewFaceRenderer m_renderer;
/// <summary>
/// Prefabをインスタンス化したシーンを管理する。
///
/// * BlendShapeのBake
/// * MaterialMorphの適用
/// * Previewカメラのコントロール
/// * Previewライティングのコントロール
///
/// </summary>
PreviewSceneManager m_scene;
protected PreviewSceneManager PreviewSceneManager
{
get { return m_scene; }
}
/// <summary>
/// Previewシーンに表示するPrefab
/// </summary>
GameObject m_prefab;
protected GameObject Prefab
{
get { return m_prefab; }
private set
{
if (m_prefab == value) return;
//Debug.LogFormat("Prefab = {0}", value);
m_prefab = value;
if (m_scene != null)
{
//Debug.LogFormat("OnDestroy");
GameObject.DestroyImmediate(m_scene.gameObject);
m_scene = null;
}
if (m_prefab != null)
{
m_scene = VRM.PreviewSceneManager.GetOrCreate(m_prefab);
if (m_scene != null)
{
m_scene.gameObject.SetActive(false);
}
Bake();
}
}
}
protected abstract PreviewSceneManager.BakeValue GetBakeValue();
/// <summary>
/// Preview シーンに BlendShape と MaterialValue を適用する
/// </summary>
protected void Bake()
{
if (m_scene != null)
{
//Debug.Log("Bake");
m_scene.Bake(GetBakeValue());
}
}
protected virtual GameObject GetPrefab()
{
var assetPath = AssetDatabase.GetAssetPath(target);
if (string.IsNullOrEmpty(assetPath))
{
return null;
}
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
if (prefab == null)
{
var parent = UniGLTF.UnityPath.FromUnityPath(assetPath).Parent;
var prefabPath = parent.Parent.Child(parent.FileNameWithoutExtension + ".prefab");
prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath.Value);
}
return prefab;
}
protected virtual void OnEnable()
{
m_renderer = new PreviewFaceRenderer();
Prefab = GetPrefab();
}
protected virtual void OnDisable()
{
if (m_renderer != null)
{
m_renderer.Dispose();
m_renderer = null;
}
}
protected virtual void OnDestroy()
{
if (m_scene != null)
{
//Debug.LogFormat("OnDestroy");
m_scene.Clean();
GameObject.DestroyImmediate(m_scene.gameObject);
m_scene = null;
}
}
protected static void Separator()
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
//GUILayout.Space();
GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
Prefab = (GameObject)EditorGUILayout.ObjectField("Preview Prefab", Prefab, typeof(GameObject), false);
//Separator();
}
private static int sliderHash = "Slider".GetHashCode();
float m_yaw = 180.0f;
float m_pitch;
Vector3 m_position = new Vector3(0, 0, -0.8f);
// very important to override this, it tells Unity to render an ObjectPreview at the bottom of the inspector
public override bool HasPreviewGUI() { return true; }
public RenderTexture PreviewTexture;
// the main ObjectPreview function... it's called constantly, like other IMGUI On*GUI() functions
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
// if this is happening, you have bigger problems
if (!ShaderUtil.hardwareSupportsRectRenderTexture)
{
if (Event.current.type == EventType.Repaint)
{
EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40f),
"Mesh preview requires\nrender texture support");
}
return;
}
var src = r;
var min = Mathf.Min(r.width, r.height);
r.width = min;
r.height = min;
r.x = src.x + (src.width - min) / 2;
r.y = src.y + (src.height - min) / 2;
//previewDir = Drag2D(previewDir, r);
{
int controlId = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
Event e = Event.current;
switch (e.GetTypeForControl(controlId))
{
case EventType.MouseDown:
if (r.Contains(e.mousePosition) && (double)r.width > 50.0)
{
GUIUtility.hotControl = controlId;
e.Use();
EditorGUIUtility.SetWantsMouseJumping(1);
break;
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == controlId)
GUIUtility.hotControl = 0;
EditorGUIUtility.SetWantsMouseJumping(0);
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == controlId)
{
if (e.button == 2)
{
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height);
m_position.x -= shift.x;
m_position.y += shift.y;
e.Use();
GUI.changed = true;
}
else if (
e.button == 0 ||
e.button == 1)
{
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height) * 140f;
m_yaw += shift.x;
m_pitch += shift.y;
m_pitch = Mathf.Clamp(m_pitch, -90f, 90f);
e.Use();
GUI.changed = true;
}
break;
}
break;
case EventType.ScrollWheel:
//Debug.LogFormat("wheel: {0}", current.delta);
if (r.Contains(e.mousePosition))
{
if (e.delta.y > 0)
{
m_position.z *= 1.1f;
Repaint();
}
else if (e.delta.y < 0)
{
m_position.z *= 0.9f;
Repaint();
}
}
break;
}
//return scrollPosition;
}
//Debug.LogFormat("{0}", previewDir);
if (Event.current.type != EventType.Repaint)
{
// if we don't need to update yet, then don't
return;
}
if (m_renderer != null && m_scene != null)
{
PreviewTexture = m_renderer.Render(r, background, m_scene, m_yaw, m_pitch, m_position) as RenderTexture;
if (PreviewTexture != null)
{
// draw the RenderTexture in the ObjectPreview pane
GUI.DrawTexture(r, PreviewTexture, ScaleMode.StretchToFill, false);
}
}
}
}
}
using UnityEditor;
using UnityEngine;
using UnityEditorInternal;
using System;
using System.Linq;
using System.Collections.Generic;
namespace VRM
{
/// <summary>
/// Prefabをインスタンス化してPreviewに表示する
///
/// * https://github.com/Unity-Technologies/UnityCsReference/blob/11bcfd801fccd2a52b09bb6fd636c1ddcc9f1705/Editor/Mono/Inspector/ModelInspector.cs
///
/// </summary>
public abstract class PreviewEditor : Editor
{
/// <summary>
/// PreviewRenderUtilityを管理する。
///
/// * PreviewRenderUtility.m_cameraのUnityVersionによる切り分け
///
/// </summary>
PreviewFaceRenderer m_renderer;
/// <summary>
/// Prefabをインスタンス化したシーンを管理する。
///
/// * BlendShapeのBake
/// * MaterialMorphの適用
/// * Previewカメラのコントロール
/// * Previewライティングのコントロール
///
/// </summary>
PreviewSceneManager m_scene;
protected PreviewSceneManager PreviewSceneManager
{
get { return m_scene; }
}
/// <summary>
/// Previewシーンに表示するPrefab
/// </summary>
GameObject m_prefab;
protected GameObject Prefab
{
get { return m_prefab; }
private set
{
if (m_prefab == value) return;
//Debug.LogFormat("Prefab = {0}", value);
m_prefab = value;
if (m_scene != null)
{
//Debug.LogFormat("OnDestroy");
GameObject.DestroyImmediate(m_scene.gameObject);
m_scene = null;
}
if (m_prefab != null)
{
m_scene = VRM.PreviewSceneManager.GetOrCreate(m_prefab);
if (m_scene != null)
{
m_scene.gameObject.SetActive(false);
}
Bake();
}
}
}
protected abstract PreviewSceneManager.BakeValue GetBakeValue();
/// <summary>
/// Preview シーンに BlendShape と MaterialValue を適用する
/// </summary>
protected void Bake()
{
if (m_scene != null)
{
//Debug.Log("Bake");
m_scene.Bake(GetBakeValue());
}
}
protected virtual GameObject GetPrefab()
{
var assetPath = AssetDatabase.GetAssetPath(target);
if (string.IsNullOrEmpty(assetPath))
{
return null;
}
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
if (prefab == null)
{
var parent = UniGLTF.UnityPath.FromUnityPath(assetPath).Parent;
var prefabPath = parent.Parent.Child(parent.FileNameWithoutExtension + ".prefab");
prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath.Value);
}
return prefab;
}
protected virtual void OnEnable()
{
m_renderer = new PreviewFaceRenderer();
Prefab = GetPrefab();
}
protected virtual void OnDisable()
{
if (m_renderer != null)
{
m_renderer.Dispose();
m_renderer = null;
}
}
protected virtual void OnDestroy()
{
if (m_scene != null)
{
//Debug.LogFormat("OnDestroy");
m_scene.Clean();
GameObject.DestroyImmediate(m_scene.gameObject);
m_scene = null;
}
}
protected static void Separator()
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
//GUILayout.Space();
GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
Prefab = (GameObject)EditorGUILayout.ObjectField("Preview Prefab", Prefab, typeof(GameObject), false);
//Separator();
}
private static int sliderHash = "Slider".GetHashCode();
float m_yaw = 180.0f;
float m_pitch;
Vector3 m_position = new Vector3(0, 0, -0.8f);
// very important to override this, it tells Unity to render an ObjectPreview at the bottom of the inspector
public override bool HasPreviewGUI() { return true; }
public RenderTexture PreviewTexture;
// the main ObjectPreview function... it's called constantly, like other IMGUI On*GUI() functions
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
// if this is happening, you have bigger problems
if (!ShaderUtil.hardwareSupportsRectRenderTexture)
{
if (Event.current.type == EventType.Repaint)
{
EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40f),
"Mesh preview requires\nrender texture support");
}
return;
}
var src = r;
var min = Mathf.Min(r.width, r.height);
r.width = min;
r.height = min;
r.x = src.x + (src.width - min) / 2;
r.y = src.y + (src.height - min) / 2;
//previewDir = Drag2D(previewDir, r);
{
int controlId = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
Event e = Event.current;
switch (e.GetTypeForControl(controlId))
{
case EventType.MouseDown:
if (r.Contains(e.mousePosition) && (double)r.width > 50.0)
{
GUIUtility.hotControl = controlId;
e.Use();
EditorGUIUtility.SetWantsMouseJumping(1);
break;
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == controlId)
GUIUtility.hotControl = 0;
EditorGUIUtility.SetWantsMouseJumping(0);
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == controlId)
{
if (e.button == 2)
{
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height);
m_position.x -= shift.x;
m_position.y += shift.y;
e.Use();
GUI.changed = true;
}
else if (
e.button == 0 ||
e.button == 1)
{
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height) * 140f;
m_yaw += shift.x;
m_pitch += shift.y;
m_pitch = Mathf.Clamp(m_pitch, -90f, 90f);
e.Use();
GUI.changed = true;
}
break;
}
break;
case EventType.ScrollWheel:
//Debug.LogFormat("wheel: {0}", current.delta);
if (r.Contains(e.mousePosition))
{
if (e.delta.y > 0)
{
m_position.z *= 1.1f;
Repaint();
}
else if (e.delta.y < 0)
{
m_position.z *= 0.9f;
Repaint();
}
}
break;
}
//return scrollPosition;
}
//Debug.LogFormat("{0}", previewDir);
if (Event.current.type != EventType.Repaint)
{
// if we don't need to update yet, then don't
return;
}
if (m_renderer != null && m_scene != null)
{
PreviewTexture = m_renderer.Render(r, background, m_scene, m_yaw, m_pitch, m_position) as RenderTexture;
if (PreviewTexture != null)
{
// draw the RenderTexture in the ObjectPreview pane
GUI.DrawTexture(r, PreviewTexture, ScaleMode.StretchToFill, false);
}
}
}
}
}

View File

@ -1,159 +1,159 @@
using System;
using UnityEditor;
using UnityEngine;
namespace VRM
{
/// <summary>
/// based
///
/// * https://gist.github.com/radiatoryang/a2282d44ba71848e498bb2e03da98991
/// </summary>
/// <summary>
/// PreviewRenderUtilityを管理する
/// PreviewSceneをレンダリングする
/// </summary>
public class PreviewFaceRenderer : IDisposable
{
PreviewRenderUtility m_previewUtility;
public Camera PreviewCamera
{
get
{
#if UNITY_2017_1_OR_NEWER
return m_previewUtility.camera;
#else
return m_previewUtility.m_Camera;
#endif
}
}
public Light[] PreviewLights
{
get
{
#if UNITY_2017_1_OR_NEWER
return m_previewUtility.lights;
#else
return m_previewUtility.m_Light;
#endif
}
}
public void SetAmbientColor(Color color)
{
#if UNITY_2017_1_OR_NEWER
m_previewUtility.ambientColor = color;
#else
// ?
#endif
}
public PreviewFaceRenderer()
{
m_previewUtility = new PreviewRenderUtility();
PreviewLights[0].intensity = 1.4f;
PreviewLights[0].transform.rotation = Quaternion.Euler(40f, 190f, 0);
PreviewLights[1].intensity = 1.4f;
SetAmbientColor(new Color(.1f, .1f, .1f, 0));
}
class FogScope : IDisposable
{
bool fog;
public FogScope()
{
fog = RenderSettings.fog; // ... let's remember the current fog setting...
// we are technically rendering everything in the scene, so scene fog might affect it...
Unsupported.SetRenderSettingsUseFogNoDirty(false); // ... and then temporarily turn it off
}
public void Dispose()
{
Unsupported.SetRenderSettingsUseFogNoDirty(fog);
}
}
//const float FACTOR = 0.1f;
public Texture Render(Rect r, GUIStyle background, PreviewSceneManager scene,
float yaw, float pitch, Vector3 position)
{
if (scene == null) return null;
using (var fog = new FogScope())
{
m_previewUtility.BeginPreview(r, background); // set up the PreviewRenderUtility's mini internal scene
// setup the ObjectPreview's camera
scene.SetupCamera(PreviewCamera, scene.TargetPosition, yaw, pitch, position);
foreach (var item in scene.EnumRenderItems)
{
// now, actually render out the RenderTexture
//RenderMeshPreview(previewMesh, skinMeshRender.sharedMaterials);
// submesh support, in case the mesh is made of multiple parts
int subMeshCount = item.Mesh.subMeshCount;
for (int i = 0; i < subMeshCount; i++)
{
m_previewUtility.DrawMesh(item.Mesh,
item.Position, item.Rotation,
item.Materials[i], i);
}
}
// VERY IMPORTANT: this manually tells the camera to render and produce the render texture
PreviewCamera.Render();
//m_previewUtility.Render(false, false);
// reset the scene's fog from before
return m_previewUtility.EndPreview();
}
}
#region IDisposable Support
private bool disposedValue = false; // 重複する呼び出しを検出するには
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: マネージ状態を破棄します (マネージ オブジェクト)。
if (this.m_previewUtility != null)
{
this.m_previewUtility.Cleanup();
this.m_previewUtility = null;
}
}
// TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下のファイナライザーをオーバーライドします。
// TODO: 大きなフィールドを null に設定します。
disposedValue = true;
}
}
// TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします。
// ~PreviewFaceRenderer() {
// // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
// Dispose(false);
// }
// このコードは、破棄可能なパターンを正しく実装できるように追加されました。
public void Dispose()
{
// このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
Dispose(true);
// TODO: 上のファイナライザーがオーバーライドされる場合は、次の行のコメントを解除してください。
// GC.SuppressFinalize(this);
}
#endregion
}
}
using System;
using UnityEditor;
using UnityEngine;
namespace VRM
{
/// <summary>
/// based
///
/// * https://gist.github.com/radiatoryang/a2282d44ba71848e498bb2e03da98991
/// </summary>
/// <summary>
/// PreviewRenderUtilityを管理する
/// PreviewSceneをレンダリングする
/// </summary>
public class PreviewFaceRenderer : IDisposable
{
PreviewRenderUtility m_previewUtility;
public Camera PreviewCamera
{
get
{
#if UNITY_2017_1_OR_NEWER
return m_previewUtility.camera;
#else
return m_previewUtility.m_Camera;
#endif
}
}
public Light[] PreviewLights
{
get
{
#if UNITY_2017_1_OR_NEWER
return m_previewUtility.lights;
#else
return m_previewUtility.m_Light;
#endif
}
}
public void SetAmbientColor(Color color)
{
#if UNITY_2017_1_OR_NEWER
m_previewUtility.ambientColor = color;
#else
// ?
#endif
}
public PreviewFaceRenderer()
{
m_previewUtility = new PreviewRenderUtility();
PreviewLights[0].intensity = 1.4f;
PreviewLights[0].transform.rotation = Quaternion.Euler(40f, 190f, 0);
PreviewLights[1].intensity = 1.4f;
SetAmbientColor(new Color(.1f, .1f, .1f, 0));
}
class FogScope : IDisposable
{
bool fog;
public FogScope()
{
fog = RenderSettings.fog; // ... let's remember the current fog setting...
// we are technically rendering everything in the scene, so scene fog might affect it...
Unsupported.SetRenderSettingsUseFogNoDirty(false); // ... and then temporarily turn it off
}
public void Dispose()
{
Unsupported.SetRenderSettingsUseFogNoDirty(fog);
}
}
//const float FACTOR = 0.1f;
public Texture Render(Rect r, GUIStyle background, PreviewSceneManager scene,
float yaw, float pitch, Vector3 position)
{
if (scene == null) return null;
using (var fog = new FogScope())
{
m_previewUtility.BeginPreview(r, background); // set up the PreviewRenderUtility's mini internal scene
// setup the ObjectPreview's camera
scene.SetupCamera(PreviewCamera, scene.TargetPosition, yaw, pitch, position);
foreach (var item in scene.EnumRenderItems)
{
// now, actually render out the RenderTexture
//RenderMeshPreview(previewMesh, skinMeshRender.sharedMaterials);
// submesh support, in case the mesh is made of multiple parts
int subMeshCount = item.Mesh.subMeshCount;
for (int i = 0; i < subMeshCount; i++)
{
m_previewUtility.DrawMesh(item.Mesh,
item.Position, item.Rotation,
item.Materials[i], i);
}
}
// VERY IMPORTANT: this manually tells the camera to render and produce the render texture
PreviewCamera.Render();
//m_previewUtility.Render(false, false);
// reset the scene's fog from before
return m_previewUtility.EndPreview();
}
}
#region IDisposable Support
private bool disposedValue = false; // 重複する呼び出しを検出するには
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: マネージ状態を破棄します (マネージ オブジェクト)。
if (this.m_previewUtility != null)
{
this.m_previewUtility.Cleanup();
this.m_previewUtility = null;
}
}
// TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下のファイナライザーをオーバーライドします。
// TODO: 大きなフィールドを null に設定します。
disposedValue = true;
}
}
// TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします。
// ~PreviewFaceRenderer() {
// // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
// Dispose(false);
// }
// このコードは、破棄可能なパターンを正しく実装できるように追加されました。
public void Dispose()
{
// このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
Dispose(true);
// TODO: 上のファイナライザーがオーバーライドされる場合は、次の行のコメントを解除してください。
// GC.SuppressFinalize(this);
}
#endregion
}
}

View File

@ -1,309 +1,309 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace VRM
{
public class SerializedBlendShapeEditor
{
BlendShapeClip m_targetObject;
SerializedObject m_serializedObject;
#region Properties
SerializedProperty m_thumbnail;
SerializedProperty m_blendShapeNameProp;
SerializedProperty m_presetProp;
SerializedProperty m_isBinaryProp;
#endregion
#region BlendShapeBind
public static int BlendShapeBindingHeight = 60;
ReorderableList m_ValuesList;
SerializedProperty m_valuesProp;
#endregion
#region MaterialValueBind
const int MaterialValueBindingHeight = 90;
ReorderableList m_MaterialValuesList;
SerializedProperty m_materialsProp;
#endregion
#region Editor values
float m_previewSlider = 1.0f;
bool m_changed;
int m_mode;
static string[] MODES = new[]{
"BlendShape",
"BlendShape List",
"Material List"
};
MeshPreviewItem[] m_items;
#endregion
public SerializedBlendShapeEditor(SerializedObject serializedObject,
PreviewSceneManager previewSceneManager) : this(
serializedObject, (BlendShapeClip)serializedObject.targetObject, previewSceneManager)
{ }
public SerializedBlendShapeEditor(BlendShapeClip blendShapeClip,
PreviewSceneManager previewSceneManager) : this(
new SerializedObject(blendShapeClip), blendShapeClip, previewSceneManager)
{ }
public SerializedBlendShapeEditor(SerializedObject serializedObject, BlendShapeClip targetObject,
PreviewSceneManager previewSceneManager)
{
this.m_serializedObject = serializedObject;
this.m_targetObject = targetObject;
//m_thumbnail = serializedObject.FindProperty("Thumbnail");
m_blendShapeNameProp = serializedObject.FindProperty("BlendShapeName");
m_presetProp = serializedObject.FindProperty("Preset");
m_isBinaryProp = serializedObject.FindProperty("IsBinary");
m_valuesProp = serializedObject.FindProperty("Values");
m_ValuesList = new ReorderableList(serializedObject, m_valuesProp);
m_ValuesList.elementHeight = BlendShapeBindingHeight;
m_ValuesList.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = m_valuesProp.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if (BlendShapeClipEditorHelper.DrawBlendShapeBinding(rect, element, previewSceneManager))
{
m_changed = true;
}
};
m_materialsProp = serializedObject.FindProperty("MaterialValues");
m_MaterialValuesList = new ReorderableList(serializedObject, m_materialsProp);
m_MaterialValuesList.elementHeight = MaterialValueBindingHeight;
m_MaterialValuesList.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = m_materialsProp.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if (BlendShapeClipEditorHelper.DrawMaterialValueBinding(rect, element, previewSceneManager))
{
m_changed = true;
}
};
m_items = previewSceneManager.EnumRenderItems
.Where(x => x.SkinnedMeshRenderer != null)
.ToArray();
}
public struct DrawResult
{
public bool Changed;
public BlendShapeBinding[] BlendShapeBindings;
public MaterialValueBinding[] MaterialValueBindings;
}
public DrawResult Draw()
{
m_changed = false;
m_serializedObject.Update();
// Readonly のBlendShapeClip参照
GUI.enabled = false;
EditorGUILayout.ObjectField("Current clip",
m_targetObject, typeof(BlendShapeClip), false);
GUI.enabled = true;
EditorGUILayout.PropertyField(m_blendShapeNameProp, true);
EditorGUILayout.PropertyField(m_presetProp, true);
// v0.45 Added. Binary flag
EditorGUILayout.PropertyField(m_isBinaryProp, true);
EditorGUILayout.Space();
//m_mode = EditorGUILayout.Popup("SourceType", m_mode, MODES);
m_mode = GUILayout.Toolbar(m_mode, MODES);
switch (m_mode)
{
case 0:
{
ClipGUI();
}
break;
case 1:
{
if (GUILayout.Button("Clear"))
{
m_changed = true;
m_valuesProp.arraySize = 0;
}
m_ValuesList.DoLayoutList();
}
break;
case 2:
{
if (GUILayout.Button("Clear"))
{
m_changed = true;
m_materialsProp.arraySize = 0;
}
m_MaterialValuesList.DoLayoutList();
}
break;
}
m_serializedObject.ApplyModifiedProperties();
return new DrawResult
{
Changed = m_changed,
BlendShapeBindings = m_targetObject.Values,
MaterialValueBindings = m_targetObject.MaterialValues
};
}
void ClipGUI()
{
var changed = BlendShapeBindsGUI();
if (changed)
{
string maxWeightName;
var bindings = GetBindings(out maxWeightName);
m_valuesProp.ClearArray();
m_valuesProp.arraySize = bindings.Length;
for (int i = 0; i < bindings.Length; ++i)
{
var item = m_valuesProp.GetArrayElementAtIndex(i);
var endProperty = item.GetEndProperty();
while (item.NextVisible(true))
{
if (SerializedProperty.EqualContents(item, endProperty))
{
break;
}
switch (item.name)
{
case "RelativePath":
item.stringValue = bindings[i].RelativePath;
break;
case "Index":
item.intValue = bindings[i].Index;
break;
case "Weight":
item.floatValue = bindings[i].Weight;
break;
default:
throw new Exception();
}
}
}
m_changed = true;
}
}
List<bool> m_meshFolds = new List<bool>();
bool BlendShapeBindsGUI()
{
bool changed = false;
int foldIndex = 0;
// すべてのSkinnedMeshRendererを列挙する
foreach (var renderer in m_items.Select(x => x.SkinnedMeshRenderer))
{
var mesh = renderer.sharedMesh;
if (mesh != null && mesh.blendShapeCount > 0)
{
//var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, m_target.transform);
//EditorGUILayout.LabelField(m_target.name + "/" + item.Path);
if (foldIndex >= m_meshFolds.Count)
{
m_meshFolds.Add(false);
}
m_meshFolds[foldIndex] = EditorGUILayout.Foldout(m_meshFolds[foldIndex], renderer.name);
if (m_meshFolds[foldIndex])
{
//EditorGUI.indentLevel += 1;
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var src = renderer.GetBlendShapeWeight(i);
var dst = EditorGUILayout.Slider(mesh.GetBlendShapeName(i), src, 0, 100.0f);
if (dst != src)
{
renderer.SetBlendShapeWeight(i, dst);
changed = true;
}
}
//EditorGUI.indentLevel -= 1;
}
++foldIndex;
}
}
return changed;
}
BlendShapeBinding[] GetBindings(out string _maxWeightName)
{
var maxWeight = 0.0f;
var maxWeightName = "";
// weightのついたblendShapeを集める
var values = m_items
.SelectMany(x =>
{
var mesh = x.SkinnedMeshRenderer.sharedMesh;
var relativePath = x.Path;
var list = new List<BlendShapeBinding>();
if (mesh != null)
{
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var weight = x.SkinnedMeshRenderer.GetBlendShapeWeight(i);
if (weight == 0)
{
continue;
}
var name = mesh.GetBlendShapeName(i);
if (weight > maxWeight)
{
maxWeightName = name;
maxWeight = weight;
}
list.Add(new BlendShapeBinding
{
Index = i,
RelativePath = relativePath,
Weight = weight
});
}
}
return list;
}).ToArray()
;
_maxWeightName = maxWeightName;
return values;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace VRM
{
public class SerializedBlendShapeEditor
{
BlendShapeClip m_targetObject;
SerializedObject m_serializedObject;
#region Properties
SerializedProperty m_thumbnail;
SerializedProperty m_blendShapeNameProp;
SerializedProperty m_presetProp;
SerializedProperty m_isBinaryProp;
#endregion
#region BlendShapeBind
public static int BlendShapeBindingHeight = 60;
ReorderableList m_ValuesList;
SerializedProperty m_valuesProp;
#endregion
#region MaterialValueBind
const int MaterialValueBindingHeight = 90;
ReorderableList m_MaterialValuesList;
SerializedProperty m_materialsProp;
#endregion
#region Editor values
float m_previewSlider = 1.0f;
bool m_changed;
int m_mode;
static string[] MODES = new[]{
"BlendShape",
"BlendShape List",
"Material List"
};
MeshPreviewItem[] m_items;
#endregion
public SerializedBlendShapeEditor(SerializedObject serializedObject,
PreviewSceneManager previewSceneManager) : this(
serializedObject, (BlendShapeClip)serializedObject.targetObject, previewSceneManager)
{ }
public SerializedBlendShapeEditor(BlendShapeClip blendShapeClip,
PreviewSceneManager previewSceneManager) : this(
new SerializedObject(blendShapeClip), blendShapeClip, previewSceneManager)
{ }
public SerializedBlendShapeEditor(SerializedObject serializedObject, BlendShapeClip targetObject,
PreviewSceneManager previewSceneManager)
{
this.m_serializedObject = serializedObject;
this.m_targetObject = targetObject;
//m_thumbnail = serializedObject.FindProperty("Thumbnail");
m_blendShapeNameProp = serializedObject.FindProperty("BlendShapeName");
m_presetProp = serializedObject.FindProperty("Preset");
m_isBinaryProp = serializedObject.FindProperty("IsBinary");
m_valuesProp = serializedObject.FindProperty("Values");
m_ValuesList = new ReorderableList(serializedObject, m_valuesProp);
m_ValuesList.elementHeight = BlendShapeBindingHeight;
m_ValuesList.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = m_valuesProp.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if (BlendShapeClipEditorHelper.DrawBlendShapeBinding(rect, element, previewSceneManager))
{
m_changed = true;
}
};
m_materialsProp = serializedObject.FindProperty("MaterialValues");
m_MaterialValuesList = new ReorderableList(serializedObject, m_materialsProp);
m_MaterialValuesList.elementHeight = MaterialValueBindingHeight;
m_MaterialValuesList.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = m_materialsProp.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 2;
if (BlendShapeClipEditorHelper.DrawMaterialValueBinding(rect, element, previewSceneManager))
{
m_changed = true;
}
};
m_items = previewSceneManager.EnumRenderItems
.Where(x => x.SkinnedMeshRenderer != null)
.ToArray();
}
public struct DrawResult
{
public bool Changed;
public BlendShapeBinding[] BlendShapeBindings;
public MaterialValueBinding[] MaterialValueBindings;
}
public DrawResult Draw()
{
m_changed = false;
m_serializedObject.Update();
// Readonly のBlendShapeClip参照
GUI.enabled = false;
EditorGUILayout.ObjectField("Current clip",
m_targetObject, typeof(BlendShapeClip), false);
GUI.enabled = true;
EditorGUILayout.PropertyField(m_blendShapeNameProp, true);
EditorGUILayout.PropertyField(m_presetProp, true);
// v0.45 Added. Binary flag
EditorGUILayout.PropertyField(m_isBinaryProp, true);
EditorGUILayout.Space();
//m_mode = EditorGUILayout.Popup("SourceType", m_mode, MODES);
m_mode = GUILayout.Toolbar(m_mode, MODES);
switch (m_mode)
{
case 0:
{
ClipGUI();
}
break;
case 1:
{
if (GUILayout.Button("Clear"))
{
m_changed = true;
m_valuesProp.arraySize = 0;
}
m_ValuesList.DoLayoutList();
}
break;
case 2:
{
if (GUILayout.Button("Clear"))
{
m_changed = true;
m_materialsProp.arraySize = 0;
}
m_MaterialValuesList.DoLayoutList();
}
break;
}
m_serializedObject.ApplyModifiedProperties();
return new DrawResult
{
Changed = m_changed,
BlendShapeBindings = m_targetObject.Values,
MaterialValueBindings = m_targetObject.MaterialValues
};
}
void ClipGUI()
{
var changed = BlendShapeBindsGUI();
if (changed)
{
string maxWeightName;
var bindings = GetBindings(out maxWeightName);
m_valuesProp.ClearArray();
m_valuesProp.arraySize = bindings.Length;
for (int i = 0; i < bindings.Length; ++i)
{
var item = m_valuesProp.GetArrayElementAtIndex(i);
var endProperty = item.GetEndProperty();
while (item.NextVisible(true))
{
if (SerializedProperty.EqualContents(item, endProperty))
{
break;
}
switch (item.name)
{
case "RelativePath":
item.stringValue = bindings[i].RelativePath;
break;
case "Index":
item.intValue = bindings[i].Index;
break;
case "Weight":
item.floatValue = bindings[i].Weight;
break;
default:
throw new Exception();
}
}
}
m_changed = true;
}
}
List<bool> m_meshFolds = new List<bool>();
bool BlendShapeBindsGUI()
{
bool changed = false;
int foldIndex = 0;
// すべてのSkinnedMeshRendererを列挙する
foreach (var renderer in m_items.Select(x => x.SkinnedMeshRenderer))
{
var mesh = renderer.sharedMesh;
if (mesh != null && mesh.blendShapeCount > 0)
{
//var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, m_target.transform);
//EditorGUILayout.LabelField(m_target.name + "/" + item.Path);
if (foldIndex >= m_meshFolds.Count)
{
m_meshFolds.Add(false);
}
m_meshFolds[foldIndex] = EditorGUILayout.Foldout(m_meshFolds[foldIndex], renderer.name);
if (m_meshFolds[foldIndex])
{
//EditorGUI.indentLevel += 1;
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var src = renderer.GetBlendShapeWeight(i);
var dst = EditorGUILayout.Slider(mesh.GetBlendShapeName(i), src, 0, 100.0f);
if (dst != src)
{
renderer.SetBlendShapeWeight(i, dst);
changed = true;
}
}
//EditorGUI.indentLevel -= 1;
}
++foldIndex;
}
}
return changed;
}
BlendShapeBinding[] GetBindings(out string _maxWeightName)
{
var maxWeight = 0.0f;
var maxWeightName = "";
// weightのついたblendShapeを集める
var values = m_items
.SelectMany(x =>
{
var mesh = x.SkinnedMeshRenderer.sharedMesh;
var relativePath = x.Path;
var list = new List<BlendShapeBinding>();
if (mesh != null)
{
for (int i = 0; i < mesh.blendShapeCount; ++i)
{
var weight = x.SkinnedMeshRenderer.GetBlendShapeWeight(i);
if (weight == 0)
{
continue;
}
var name = mesh.GetBlendShapeName(i);
if (weight > maxWeight)
{
maxWeightName = name;
maxWeight = weight;
}
list.Add(new BlendShapeBinding
{
Index = i,
RelativePath = relativePath,
Weight = weight
});
}
}
return list;
}).ToArray()
;
_maxWeightName = maxWeightName;
return values;
}
}
}

View File

@ -1,71 +1,71 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using System.Linq;
namespace VRM
{
[CustomEditor(typeof(VRMBlendShapeProxy))]
public class VRMBlnedShapeProxyEditor : Editor
{
VRMBlendShapeProxy m_target;
SkinnedMeshRenderer[] m_renderers;
public class BlendShapeSlider
{
VRMBlendShapeProxy m_target;
BlendShapeKey m_key;
public BlendShapeSlider(VRMBlendShapeProxy target, BlendShapeKey key)
{
m_target = target;
m_key = key;
}
public KeyValuePair<BlendShapeKey, float> Slider()
{
var oldValue = m_target.GetValue(m_key);
var enable = GUI.enabled;
GUI.enabled = Application.isPlaying;
var newValue = EditorGUILayout.Slider(m_key.ToString(), oldValue, 0, 1.0f);
GUI.enabled = enable;
return new KeyValuePair<BlendShapeKey, float>(m_key, newValue);
}
}
List<BlendShapeSlider> m_sliders;
void OnEnable()
{
m_target = (VRMBlendShapeProxy)target;
if (m_target.BlendShapeAvatar != null && m_target.BlendShapeAvatar.Clips != null)
{
m_sliders = m_target.BlendShapeAvatar.Clips
.Where(x => x != null)
.Select(x => new BlendShapeSlider(m_target, BlendShapeKey.CreateFrom(x)))
.ToList()
;
}
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (!Application.isPlaying)
{
EditorGUILayout.HelpBox("Enable when playing", MessageType.Info);
}
if (m_target.BlendShapeAvatar == null)
{
return;
}
if (m_sliders != null)
{
m_target.SetValues(m_sliders.Select(x => x.Slider()));
}
}
}
}
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using System.Linq;
namespace VRM
{
[CustomEditor(typeof(VRMBlendShapeProxy))]
public class VRMBlnedShapeProxyEditor : Editor
{
VRMBlendShapeProxy m_target;
SkinnedMeshRenderer[] m_renderers;
public class BlendShapeSlider
{
VRMBlendShapeProxy m_target;
BlendShapeKey m_key;
public BlendShapeSlider(VRMBlendShapeProxy target, BlendShapeKey key)
{
m_target = target;
m_key = key;
}
public KeyValuePair<BlendShapeKey, float> Slider()
{
var oldValue = m_target.GetValue(m_key);
var enable = GUI.enabled;
GUI.enabled = Application.isPlaying;
var newValue = EditorGUILayout.Slider(m_key.ToString(), oldValue, 0, 1.0f);
GUI.enabled = enable;
return new KeyValuePair<BlendShapeKey, float>(m_key, newValue);
}
}
List<BlendShapeSlider> m_sliders;
void OnEnable()
{
m_target = (VRMBlendShapeProxy)target;
if (m_target.BlendShapeAvatar != null && m_target.BlendShapeAvatar.Clips != null)
{
m_sliders = m_target.BlendShapeAvatar.Clips
.Where(x => x != null)
.Select(x => new BlendShapeSlider(m_target, BlendShapeKey.CreateFrom(x)))
.ToList()
;
}
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (!Application.isPlaying)
{
EditorGUILayout.HelpBox("Enable when playing", MessageType.Info);
}
if (m_target.BlendShapeAvatar == null)
{
return;
}
if (m_sliders != null)
{
m_target.SetValues(m_sliders.Select(x => x.Slider()));
}
}
}
}

View File

@ -1,282 +1,282 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UniGLTF;
namespace VRM
{
///
/// Base + (A.Target - Base) * A.Weight + (B.Target - Base) * B.Weight ...
///
class MaterialValueBindingMerger
{
struct DictionaryKeyMaterialValueBindingComparer : IEqualityComparer<MaterialValueBinding>
{
public bool Equals(MaterialValueBinding x, MaterialValueBinding y)
{
return x.TargetValue == y.TargetValue && x.BaseValue == y.BaseValue && x.MaterialName == y.MaterialName && x.ValueName == y.ValueName;
}
public int GetHashCode(MaterialValueBinding obj)
{
return obj.GetHashCode();
}
}
static DictionaryKeyMaterialValueBindingComparer comparer = new DictionaryKeyMaterialValueBindingComparer();
/// <summary>
/// 名前とmaterialのマッピング
/// </summary>
Dictionary<string, Material> m_materialMap = new Dictionary<string, Material>();
delegate void Setter(float value, bool firstValue);
/// <summary>
/// MaterialValueの適用値を蓄積する
/// </summary>
/// <typeparam name="MaterialValueBinding"></typeparam>
/// <typeparam name="float"></typeparam>
/// <returns></returns>
Dictionary<MaterialValueBinding, float> m_materialValueMap = new Dictionary<MaterialValueBinding, float>(comparer);
Dictionary<MaterialValueBinding, Setter> m_materialSetterMap = new Dictionary<MaterialValueBinding, Setter>(comparer);
//BlendShapeClip[] m_clips;
public MaterialValueBindingMerger(Dictionary<BlendShapeKey, BlendShapeClip> clipMap, Transform root)
{
//m_clips = clipMap.Values.ToArray();
foreach (var x in root.Traverse())
{
var renderer = x.GetComponent<Renderer>();
if (renderer != null)
{
foreach (var y in renderer.sharedMaterials.Where(y => y != null))
{
if (!string.IsNullOrEmpty(y.name))
{
if (!m_materialMap.ContainsKey(y.name))
{
m_materialMap.Add(y.name, y);
}
}
}
}
}
foreach (var kv in clipMap)
{
foreach (var binding in kv.Value.MaterialValues)
{
if (!m_materialSetterMap.ContainsKey(binding))
{
Material target;
if (m_materialMap.TryGetValue(binding.MaterialName, out target))
{
if (binding.ValueName.EndsWith("_ST_S"))
{
var valueName = binding.ValueName.Substring(0, binding.ValueName.Length - 2);
Setter setter = (value, firstValue) =>
{
var propValue = firstValue
? (binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value)
: (target.GetVector(valueName) + (binding.TargetValue - binding.BaseValue) * value)
;
var src = target.GetVector(valueName);
src.x = propValue.x; // horizontal only
src.z = propValue.z; // horizontal only
target.SetVector(valueName, src);
};
m_materialSetterMap.Add(binding, setter);
}
else if (binding.ValueName.EndsWith("_ST_T"))
{
var valueName = binding.ValueName.Substring(0, binding.ValueName.Length - 2);
Setter setter = (value, firstValue) =>
{
var propValue = firstValue
? (binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value)
: (target.GetVector(valueName) + (binding.TargetValue - binding.BaseValue) * value)
;
var src = target.GetVector(valueName);
src.y = propValue.y; // vertical only
src.w = propValue.w; // vertical only
target.SetVector(valueName, src);
};
m_materialSetterMap.Add(binding, setter);
}
else
{
Setter vec4Setter = (value, firstValue) =>
{
var propValue = firstValue
? (binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value)
: (target.GetVector(binding.ValueName) + (binding.TargetValue - binding.BaseValue) * value)
;
target.SetColor(binding.ValueName, propValue);
};
m_materialSetterMap.Add(binding, vec4Setter);
}
}
else
{
Debug.LogWarningFormat("material: {0} not found", binding.MaterialName);
}
}
}
}
}
public void RestoreMaterialInitialValues(IEnumerable<BlendShapeClip> clips)
{
if (m_materialMap != null)
{
foreach (var x in clips)
{
foreach (var y in x.MaterialValues)
{
// restore values
Material material;
if (m_materialMap.TryGetValue(y.MaterialName, out material))
{
var valueName = y.ValueName;
if (valueName.EndsWith("_ST_S")
|| valueName.EndsWith("_ST_T"))
{
valueName = valueName.Substring(0, valueName.Length - 2);
}
material.SetColor(valueName, y.BaseValue);
}
else
{
Debug.LogWarningFormat("{0} not found", y.MaterialName);
}
}
}
}
}
public void ImmediatelySetValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.MaterialValues)
{
Setter setter;
if (m_materialSetterMap.TryGetValue(binding, out setter))
{
setter(value, true);
}
}
}
public void AccumulateValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.MaterialValues)
{
// 積算
float acc;
if (m_materialValueMap.TryGetValue(binding, out acc))
{
m_materialValueMap[binding] = acc + value;
}
else
{
m_materialValueMap[binding] = value;
}
}
}
struct MaterialTarget : IEquatable<MaterialTarget>
{
public string MaterialName;
public string ValueName;
public bool Equals(MaterialTarget other)
{
return MaterialName == other.MaterialName
&& ValueName == other.ValueName;
}
public override bool Equals(object obj)
{
if (obj is MaterialTarget)
{
return Equals((MaterialTarget)obj);
}
else
{
return false;
}
}
public override int GetHashCode()
{
if (MaterialName == null || ValueName == null)
{
return 0;
}
return MaterialName.GetHashCode() + ValueName.GetHashCode();
}
public static MaterialTarget Create(MaterialValueBinding binding)
{
return new MaterialTarget
{
MaterialName=binding.MaterialName,
ValueName=binding.ValueName
};
}
}
HashSet<MaterialTarget> m_used = new HashSet<MaterialTarget>();
public void Apply()
{
// clear
//RestoreMaterialInitialValues(m_clips);
m_used.Clear();
// (binding.Value-Base) * weight を足す
foreach (var kv in m_materialValueMap)
{
var key = MaterialTarget.Create(kv.Key);
if (!m_used.Contains(key))
{
// restore value
Material material;
if (m_materialMap.TryGetValue(key.MaterialName, out material))
{
var value = kv.Key.BaseValue;
var valueName = key.ValueName;
if (valueName.EndsWith("_ST_S"))
{
valueName = valueName.Substring(0, valueName.Length - 2);
var v=material.GetVector(valueName);
value.y = v.y;
value.w = v.w;
}
else if (valueName.EndsWith("_ST_T"))
{
valueName = valueName.Substring(0, valueName.Length - 2);
var v = material.GetVector(valueName);
value.x = v.x;
value.z = v.z;
}
material.SetColor(valueName, value);
}
m_used.Add(key);
}
Setter setter;
if (m_materialSetterMap.TryGetValue(kv.Key, out setter))
{
setter(kv.Value, false);
}
}
m_materialValueMap.Clear();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UniGLTF;
namespace VRM
{
///
/// Base + (A.Target - Base) * A.Weight + (B.Target - Base) * B.Weight ...
///
class MaterialValueBindingMerger
{
struct DictionaryKeyMaterialValueBindingComparer : IEqualityComparer<MaterialValueBinding>
{
public bool Equals(MaterialValueBinding x, MaterialValueBinding y)
{
return x.TargetValue == y.TargetValue && x.BaseValue == y.BaseValue && x.MaterialName == y.MaterialName && x.ValueName == y.ValueName;
}
public int GetHashCode(MaterialValueBinding obj)
{
return obj.GetHashCode();
}
}
static DictionaryKeyMaterialValueBindingComparer comparer = new DictionaryKeyMaterialValueBindingComparer();
/// <summary>
/// 名前とmaterialのマッピング
/// </summary>
Dictionary<string, Material> m_materialMap = new Dictionary<string, Material>();
delegate void Setter(float value, bool firstValue);
/// <summary>
/// MaterialValueの適用値を蓄積する
/// </summary>
/// <typeparam name="MaterialValueBinding"></typeparam>
/// <typeparam name="float"></typeparam>
/// <returns></returns>
Dictionary<MaterialValueBinding, float> m_materialValueMap = new Dictionary<MaterialValueBinding, float>(comparer);
Dictionary<MaterialValueBinding, Setter> m_materialSetterMap = new Dictionary<MaterialValueBinding, Setter>(comparer);
//BlendShapeClip[] m_clips;
public MaterialValueBindingMerger(Dictionary<BlendShapeKey, BlendShapeClip> clipMap, Transform root)
{
//m_clips = clipMap.Values.ToArray();
foreach (var x in root.Traverse())
{
var renderer = x.GetComponent<Renderer>();
if (renderer != null)
{
foreach (var y in renderer.sharedMaterials.Where(y => y != null))
{
if (!string.IsNullOrEmpty(y.name))
{
if (!m_materialMap.ContainsKey(y.name))
{
m_materialMap.Add(y.name, y);
}
}
}
}
}
foreach (var kv in clipMap)
{
foreach (var binding in kv.Value.MaterialValues)
{
if (!m_materialSetterMap.ContainsKey(binding))
{
Material target;
if (m_materialMap.TryGetValue(binding.MaterialName, out target))
{
if (binding.ValueName.EndsWith("_ST_S"))
{
var valueName = binding.ValueName.Substring(0, binding.ValueName.Length - 2);
Setter setter = (value, firstValue) =>
{
var propValue = firstValue
? (binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value)
: (target.GetVector(valueName) + (binding.TargetValue - binding.BaseValue) * value)
;
var src = target.GetVector(valueName);
src.x = propValue.x; // horizontal only
src.z = propValue.z; // horizontal only
target.SetVector(valueName, src);
};
m_materialSetterMap.Add(binding, setter);
}
else if (binding.ValueName.EndsWith("_ST_T"))
{
var valueName = binding.ValueName.Substring(0, binding.ValueName.Length - 2);
Setter setter = (value, firstValue) =>
{
var propValue = firstValue
? (binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value)
: (target.GetVector(valueName) + (binding.TargetValue - binding.BaseValue) * value)
;
var src = target.GetVector(valueName);
src.y = propValue.y; // vertical only
src.w = propValue.w; // vertical only
target.SetVector(valueName, src);
};
m_materialSetterMap.Add(binding, setter);
}
else
{
Setter vec4Setter = (value, firstValue) =>
{
var propValue = firstValue
? (binding.BaseValue + (binding.TargetValue - binding.BaseValue) * value)
: (target.GetVector(binding.ValueName) + (binding.TargetValue - binding.BaseValue) * value)
;
target.SetColor(binding.ValueName, propValue);
};
m_materialSetterMap.Add(binding, vec4Setter);
}
}
else
{
Debug.LogWarningFormat("material: {0} not found", binding.MaterialName);
}
}
}
}
}
public void RestoreMaterialInitialValues(IEnumerable<BlendShapeClip> clips)
{
if (m_materialMap != null)
{
foreach (var x in clips)
{
foreach (var y in x.MaterialValues)
{
// restore values
Material material;
if (m_materialMap.TryGetValue(y.MaterialName, out material))
{
var valueName = y.ValueName;
if (valueName.EndsWith("_ST_S")
|| valueName.EndsWith("_ST_T"))
{
valueName = valueName.Substring(0, valueName.Length - 2);
}
material.SetColor(valueName, y.BaseValue);
}
else
{
Debug.LogWarningFormat("{0} not found", y.MaterialName);
}
}
}
}
}
public void ImmediatelySetValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.MaterialValues)
{
Setter setter;
if (m_materialSetterMap.TryGetValue(binding, out setter))
{
setter(value, true);
}
}
}
public void AccumulateValue(BlendShapeClip clip, float value)
{
foreach (var binding in clip.MaterialValues)
{
// 積算
float acc;
if (m_materialValueMap.TryGetValue(binding, out acc))
{
m_materialValueMap[binding] = acc + value;
}
else
{
m_materialValueMap[binding] = value;
}
}
}
struct MaterialTarget : IEquatable<MaterialTarget>
{
public string MaterialName;
public string ValueName;
public bool Equals(MaterialTarget other)
{
return MaterialName == other.MaterialName
&& ValueName == other.ValueName;
}
public override bool Equals(object obj)
{
if (obj is MaterialTarget)
{
return Equals((MaterialTarget)obj);
}
else
{
return false;
}
}
public override int GetHashCode()
{
if (MaterialName == null || ValueName == null)
{
return 0;
}
return MaterialName.GetHashCode() + ValueName.GetHashCode();
}
public static MaterialTarget Create(MaterialValueBinding binding)
{
return new MaterialTarget
{
MaterialName=binding.MaterialName,
ValueName=binding.ValueName
};
}
}
HashSet<MaterialTarget> m_used = new HashSet<MaterialTarget>();
public void Apply()
{
// clear
//RestoreMaterialInitialValues(m_clips);
m_used.Clear();
// (binding.Value-Base) * weight を足す
foreach (var kv in m_materialValueMap)
{
var key = MaterialTarget.Create(kv.Key);
if (!m_used.Contains(key))
{
// restore value
Material material;
if (m_materialMap.TryGetValue(key.MaterialName, out material))
{
var value = kv.Key.BaseValue;
var valueName = key.ValueName;
if (valueName.EndsWith("_ST_S"))
{
valueName = valueName.Substring(0, valueName.Length - 2);
var v=material.GetVector(valueName);
value.y = v.y;
value.w = v.w;
}
else if (valueName.EndsWith("_ST_T"))
{
valueName = valueName.Substring(0, valueName.Length - 2);
var v = material.GetVector(valueName);
value.x = v.x;
value.z = v.z;
}
material.SetColor(valueName, value);
}
m_used.Add(key);
}
Setter setter;
if (m_materialSetterMap.TryGetValue(kv.Key, out setter))
{
setter(kv.Value, false);
}
}
m_materialValueMap.Clear();
}
}
}

View File

@ -1,236 +1,236 @@
using System.Linq;
using UnityEngine;
using UniGLTF;
using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace VRM
{
#if UNITY_EDITOR
[Serializable]
public struct PropItem
{
public ShaderUtil.ShaderPropertyType PropertyType;
public Vector4 DefaultValues;
}
#endif
[Serializable]
public class MaterialItem
{
public Material Material { get; private set; }
#if UNITY_EDITOR
public Dictionary<string, PropItem> PropMap = new Dictionary<string, PropItem>();
public string[] PropNames
{
get;
private set;
}
#endif
public static MaterialItem Create(Material material)
{
var item = new MaterialItem
{
Material = material
};
#if UNITY_EDITOR
var propNames = new List<string>();
for (int i = 0; i < ShaderUtil.GetPropertyCount(material.shader); ++i)
{
var propType = ShaderUtil.GetPropertyType(material.shader, i);
var name = ShaderUtil.GetPropertyName(material.shader, i);
switch (propType)
{
case ShaderUtil.ShaderPropertyType.Color:
// 色
item.PropMap.Add(name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetColor(name),
});
propNames.Add(name);
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
// テクスチャ
{
name += "_ST";
item.PropMap.Add(name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(name);
}
// 縦横分離用
{
var st_name = name + "_S";
item.PropMap.Add(st_name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(st_name);
}
{
var st_name = name + "_T";
item.PropMap.Add(st_name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(st_name);
}
break;
}
}
item.PropNames = propNames.ToArray();
#endif
return item;
}
}
[Serializable]
public class MeshPreviewItem
{
public string Path
{
get;
private set;
}
public SkinnedMeshRenderer SkinnedMeshRenderer
{
get;
private set;
}
public Mesh Mesh
{
get;
private set;
}
public string[] BlendShapeNames
{
get;
private set;
}
public int BlendShapeCount
{
get { return BlendShapeNames.Length; }
}
public Material[] Materials
{
get;
private set;
}
Transform m_transform;
public Vector3 Position
{
get { return m_transform.position; }
}
public Quaternion Rotation
{
get { return m_transform.rotation; }
}
MeshPreviewItem(string path, Transform transform, Material[] materials)
{
Path = path;
m_transform = transform;
Materials = materials;
}
public void Bake(IEnumerable<BlendShapeBinding> values, float weight)
{
if (SkinnedMeshRenderer == null) return;
// Update baked mesh
if (values != null)
{
// clear
for (int i = 0; i < BlendShapeCount; ++i)
{
SkinnedMeshRenderer.SetBlendShapeWeight(i, 0);
}
foreach (var x in values)
{
if (x.RelativePath == Path)
{
if (x.Index >= 0 && x.Index < SkinnedMeshRenderer.sharedMesh.blendShapeCount)
{
SkinnedMeshRenderer.SetBlendShapeWeight(x.Index, x.Weight * weight);
}
else
{
Debug.LogWarningFormat("Out of range {0}: 0 <= {1} < {2}",
SkinnedMeshRenderer.name,
x.Index,
SkinnedMeshRenderer.sharedMesh.blendShapeCount);
}
}
}
}
SkinnedMeshRenderer.BakeMesh(Mesh);
}
public static MeshPreviewItem Create(Transform t, Transform root,
Func<Material, Material> getOrCreateMaterial)
{
//Debug.Log("create");
var meshFilter = t.GetComponent<MeshFilter>();
var meshRenderer = t.GetComponent<MeshRenderer>();
var skinnedMeshRenderer = t.GetComponent<SkinnedMeshRenderer>();
if (meshFilter != null && meshRenderer != null)
{
// copy
meshRenderer.sharedMaterials = meshRenderer.sharedMaterials.Select(x => getOrCreateMaterial(x)).ToArray();
return new MeshPreviewItem(t.RelativePathFrom(root), t, meshRenderer.sharedMaterials)
{
Mesh = meshFilter.sharedMesh
};
}
else if (skinnedMeshRenderer != null)
{
// copy
skinnedMeshRenderer.sharedMaterials = skinnedMeshRenderer.sharedMaterials.Select(x => getOrCreateMaterial(x)).ToArray();
if (skinnedMeshRenderer.sharedMesh.blendShapeCount > 0)
{
// bake required
var sharedMesh = skinnedMeshRenderer.sharedMesh;
return new MeshPreviewItem(t.RelativePathFrom(root), t, skinnedMeshRenderer.sharedMaterials)
{
SkinnedMeshRenderer = skinnedMeshRenderer,
Mesh = new Mesh(), // for bake
BlendShapeNames = Enumerable.Range(0, sharedMesh.blendShapeCount).Select(x => sharedMesh.GetBlendShapeName(x)).ToArray()
};
}
else
{
return new MeshPreviewItem(t.RelativePathFrom(root), t, skinnedMeshRenderer.sharedMaterials)
{
Mesh = skinnedMeshRenderer.sharedMesh,
};
}
}
else
{
return null;
}
}
}
}
using System.Linq;
using UnityEngine;
using UniGLTF;
using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace VRM
{
#if UNITY_EDITOR
[Serializable]
public struct PropItem
{
public ShaderUtil.ShaderPropertyType PropertyType;
public Vector4 DefaultValues;
}
#endif
[Serializable]
public class MaterialItem
{
public Material Material { get; private set; }
#if UNITY_EDITOR
public Dictionary<string, PropItem> PropMap = new Dictionary<string, PropItem>();
public string[] PropNames
{
get;
private set;
}
#endif
public static MaterialItem Create(Material material)
{
var item = new MaterialItem
{
Material = material
};
#if UNITY_EDITOR
var propNames = new List<string>();
for (int i = 0; i < ShaderUtil.GetPropertyCount(material.shader); ++i)
{
var propType = ShaderUtil.GetPropertyType(material.shader, i);
var name = ShaderUtil.GetPropertyName(material.shader, i);
switch (propType)
{
case ShaderUtil.ShaderPropertyType.Color:
// 色
item.PropMap.Add(name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetColor(name),
});
propNames.Add(name);
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
// テクスチャ
{
name += "_ST";
item.PropMap.Add(name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(name);
}
// 縦横分離用
{
var st_name = name + "_S";
item.PropMap.Add(st_name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(st_name);
}
{
var st_name = name + "_T";
item.PropMap.Add(st_name, new PropItem
{
PropertyType = propType,
DefaultValues = material.GetVector(name),
});
propNames.Add(st_name);
}
break;
}
}
item.PropNames = propNames.ToArray();
#endif
return item;
}
}
[Serializable]
public class MeshPreviewItem
{
public string Path
{
get;
private set;
}
public SkinnedMeshRenderer SkinnedMeshRenderer
{
get;
private set;
}
public Mesh Mesh
{
get;
private set;
}
public string[] BlendShapeNames
{
get;
private set;
}
public int BlendShapeCount
{
get { return BlendShapeNames.Length; }
}
public Material[] Materials
{
get;
private set;
}
Transform m_transform;
public Vector3 Position
{
get { return m_transform.position; }
}
public Quaternion Rotation
{
get { return m_transform.rotation; }
}
MeshPreviewItem(string path, Transform transform, Material[] materials)
{
Path = path;
m_transform = transform;
Materials = materials;
}
public void Bake(IEnumerable<BlendShapeBinding> values, float weight)
{
if (SkinnedMeshRenderer == null) return;
// Update baked mesh
if (values != null)
{
// clear
for (int i = 0; i < BlendShapeCount; ++i)
{
SkinnedMeshRenderer.SetBlendShapeWeight(i, 0);
}
foreach (var x in values)
{
if (x.RelativePath == Path)
{
if (x.Index >= 0 && x.Index < SkinnedMeshRenderer.sharedMesh.blendShapeCount)
{
SkinnedMeshRenderer.SetBlendShapeWeight(x.Index, x.Weight * weight);
}
else
{
Debug.LogWarningFormat("Out of range {0}: 0 <= {1} < {2}",
SkinnedMeshRenderer.name,
x.Index,
SkinnedMeshRenderer.sharedMesh.blendShapeCount);
}
}
}
}
SkinnedMeshRenderer.BakeMesh(Mesh);
}
public static MeshPreviewItem Create(Transform t, Transform root,
Func<Material, Material> getOrCreateMaterial)
{
//Debug.Log("create");
var meshFilter = t.GetComponent<MeshFilter>();
var meshRenderer = t.GetComponent<MeshRenderer>();
var skinnedMeshRenderer = t.GetComponent<SkinnedMeshRenderer>();
if (meshFilter != null && meshRenderer != null)
{
// copy
meshRenderer.sharedMaterials = meshRenderer.sharedMaterials.Select(x => getOrCreateMaterial(x)).ToArray();
return new MeshPreviewItem(t.RelativePathFrom(root), t, meshRenderer.sharedMaterials)
{
Mesh = meshFilter.sharedMesh
};
}
else if (skinnedMeshRenderer != null)
{
// copy
skinnedMeshRenderer.sharedMaterials = skinnedMeshRenderer.sharedMaterials.Select(x => getOrCreateMaterial(x)).ToArray();
if (skinnedMeshRenderer.sharedMesh.blendShapeCount > 0)
{
// bake required
var sharedMesh = skinnedMeshRenderer.sharedMesh;
return new MeshPreviewItem(t.RelativePathFrom(root), t, skinnedMeshRenderer.sharedMaterials)
{
SkinnedMeshRenderer = skinnedMeshRenderer,
Mesh = new Mesh(), // for bake
BlendShapeNames = Enumerable.Range(0, sharedMesh.blendShapeCount).Select(x => sharedMesh.GetBlendShapeName(x)).ToArray()
};
}
else
{
return new MeshPreviewItem(t.RelativePathFrom(root), t, skinnedMeshRenderer.sharedMaterials)
{
Mesh = skinnedMeshRenderer.sharedMesh,
};
}
}
else
{
return null;
}
}
}
}

View File

@ -1,306 +1,306 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using System.Reflection;
using System;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UniGLTF;
namespace VRM
{
/// <summary>
/// プレビュー向けのシーンを管理する
/// </summary>
public class PreviewSceneManager : MonoBehaviour
{
public GameObject Prefab;
#if UNITY_EDITOR
public static PreviewSceneManager GetOrCreate(GameObject prefab)
{
if (prefab == null)
{
return null;
}
PreviewSceneManager manager = null;
// if we already instantiated a PreviewInstance previously but just lost the reference, then use that same instance instead of making a new one
var managers = GameObject.FindObjectsOfType<PreviewSceneManager>();
foreach (var x in managers)
{
if (x.Prefab == prefab)
{
Debug.LogFormat("find {0}", manager);
return manager;
}
Debug.LogFormat("destroy {0}", x);
GameObject.DestroyImmediate(x.gameObject);
}
//Debug.Log("new prefab. instanciate");
// no previous instance detected, so now let's make a fresh one
// very important: this loads the PreviewInstance prefab and temporarily instantiates it into PreviewInstance
var go = GameObject.Instantiate(prefab,
prefab.transform.position,
prefab.transform.rotation
);
go.name = "__PREVIEW_SCENE_MANGER__";
manager = go.AddComponent<PreviewSceneManager>();
manager.Initialize(prefab);
// HideFlags are special editor-only settings that let you have *secret* GameObjects in a scene, or to tell Unity not to save that temporary GameObject as part of the scene
foreach (var x in go.transform.Traverse())
{
x.gameObject.hideFlags = HideFlags.None
| HideFlags.DontSave
//| HideFlags.DontSaveInBuild
#if VRM_DEVELOP
#else
| HideFlags.HideAndDontSave
#endif
;
}
return manager;
}
#endif
public void Clean()
{
foreach (var kv in m_materialMap)
{
UnityEngine.Object.DestroyImmediate(kv.Value.Material);
}
}
private void Initialize(GameObject prefab)
{
//Debug.LogFormat("[PreviewSceneManager.Initialize] {0}", prefab);
Prefab = prefab;
var materialNames = new List<string>();
var map = new Dictionary<Material, Material>();
Func<Material, Material> getOrCreateMaterial = src =>
{
if (src == null) return null;
if (string.IsNullOrEmpty(src.name)) return null; // !
Material dst;
if (!map.TryGetValue(src, out dst))
{
dst = new Material(src);
map.Add(src, dst);
//Debug.LogFormat("add material {0}", src.name);
materialNames.Add(src.name);
m_materialMap.Add(src.name, MaterialItem.Create(dst));
}
return dst;
};
m_meshes = transform.Traverse()
.Select(x => MeshPreviewItem.Create(x, transform, getOrCreateMaterial))
.Where(x => x != null)
.ToArray()
;
MaterialNames = materialNames.ToArray();
m_blendShapeMeshes = m_meshes
.Where(x => x.SkinnedMeshRenderer != null
&& x.SkinnedMeshRenderer.sharedMesh.blendShapeCount > 0)
.ToArray();
//Bake(values, materialValues);
m_rendererPathList = m_meshes.Select(x => x.Path).ToArray();
m_skinnedMeshRendererPathList = m_meshes
.Where(x => x.SkinnedMeshRenderer != null)
.Select(x => x.Path)
.ToArray();
var animator = GetComponent<Animator>();
if (animator != null)
{
var head = animator.GetBoneTransform(HumanBodyBones.Head);
if (head != null)
{
m_target = head;
}
}
}
MeshPreviewItem[] m_meshes;
MeshPreviewItem[] m_blendShapeMeshes;
public IEnumerable<MeshPreviewItem> EnumRenderItems
{
get
{
if (m_meshes != null)
{
foreach (var x in m_meshes)
{
yield return x;
}
}
}
}
public string[] MaterialNames
{
get;
private set;
}
Dictionary<string, MaterialItem> m_materialMap = new Dictionary<string, MaterialItem>();
string[] m_rendererPathList;
public string[] RendererPathList
{
get { return m_rendererPathList; }
}
string[] m_skinnedMeshRendererPathList;
public string[] SkinnedMeshRendererPathList
{
get { return m_skinnedMeshRendererPathList; }
}
public string[] GetBlendShapeNames(int blendShapeMeshIndex)
{
if (blendShapeMeshIndex >= 0 && blendShapeMeshIndex < m_blendShapeMeshes.Length)
{
var item = m_blendShapeMeshes[blendShapeMeshIndex];
return item.BlendShapeNames;
}
return null;
}
public MaterialItem GetMaterialItem(string materialName)
{
MaterialItem item;
if (!m_materialMap.TryGetValue(materialName, out item))
{
return null;
}
return item;
}
public Transform m_target;
public Vector3 TargetPosition
{
get
{
if (m_target == null)
{
return new Vector3(0, 1.4f, 0);
}
return m_target.position + new Vector3(0, 0.1f, 0);
}
}
#if UNITY_EDITOR
public struct BakeValue
{
public IEnumerable<BlendShapeBinding> BlendShapeBindings;
public IEnumerable<MaterialValueBinding> MaterialValueBindings;
public float Weight;
}
Bounds m_bounds;
public void Bake(BakeValue bake)
{
//
// Bake BlendShape
//
m_bounds = default(Bounds);
if (m_meshes != null)
{
foreach (var x in m_meshes)
{
x.Bake(bake.BlendShapeBindings, bake.Weight);
m_bounds.Expand(x.Mesh.bounds.size);
}
}
//
// Update Material
//
if (bake.MaterialValueBindings != null && m_materialMap != null)
{
// clear
//Debug.LogFormat("clear material");
foreach (var kv in m_materialMap)
{
foreach (var _kv in kv.Value.PropMap)
{
kv.Value.Material.SetColor(_kv.Key, _kv.Value.DefaultValues);
}
}
foreach (var x in bake.MaterialValueBindings)
{
MaterialItem item;
if (m_materialMap.TryGetValue(x.MaterialName, out item))
{
//Debug.Log("set material");
PropItem prop;
if (item.PropMap.TryGetValue(x.ValueName, out prop))
{
var valueName = x.ValueName;
if (valueName.EndsWith("_ST_S")
|| valueName.EndsWith("_ST_T"))
{
valueName = valueName.Substring(0, valueName.Length - 2);
}
var value = item.Material.GetVector(valueName);
//Debug.LogFormat("{0} => {1}", valueName, x.TargetValue);
value += ((x.TargetValue - x.BaseValue) * bake.Weight);
item.Material.SetColor(valueName, value);
}
}
}
}
}
#endif
/// <summary>
/// カメラパラメーターを決める
/// </summary>
/// <param name="camera"></param>
public void SetupCamera(Camera camera, Vector3 target, float yaw, float pitch, Vector3 position)
{
camera.backgroundColor = Color.gray;
camera.clearFlags = CameraClearFlags.Color;
// projection
//float magnitude = m_bounds.extents.magnitude * 0.5f;
//float distance = magnitude;
//var distance = target.magnitude;
camera.fieldOfView = 27f;
camera.nearClipPlane = 0.3f;
camera.farClipPlane = -position.z /*+ magnitude*/ * 2.1f;
var t = Matrix4x4.Translate(position);
var r = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(pitch, yaw, 0), Vector3.one);
// 回転してから移動
var m = r * t;
camera.transform.position = target + m.ExtractPosition();
camera.transform.rotation = m.ExtractRotation();
//camera.transform.LookAt(target);
//previewLayer のみ表示する
//camera.cullingMask = 1 << PreviewLayer;
}
}
}
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using System.Reflection;
using System;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UniGLTF;
namespace VRM
{
/// <summary>
/// プレビュー向けのシーンを管理する
/// </summary>
public class PreviewSceneManager : MonoBehaviour
{
public GameObject Prefab;
#if UNITY_EDITOR
public static PreviewSceneManager GetOrCreate(GameObject prefab)
{
if (prefab == null)
{
return null;
}
PreviewSceneManager manager = null;
// if we already instantiated a PreviewInstance previously but just lost the reference, then use that same instance instead of making a new one
var managers = GameObject.FindObjectsOfType<PreviewSceneManager>();
foreach (var x in managers)
{
if (x.Prefab == prefab)
{
Debug.LogFormat("find {0}", manager);
return manager;
}
Debug.LogFormat("destroy {0}", x);
GameObject.DestroyImmediate(x.gameObject);
}
//Debug.Log("new prefab. instanciate");
// no previous instance detected, so now let's make a fresh one
// very important: this loads the PreviewInstance prefab and temporarily instantiates it into PreviewInstance
var go = GameObject.Instantiate(prefab,
prefab.transform.position,
prefab.transform.rotation
);
go.name = "__PREVIEW_SCENE_MANGER__";
manager = go.AddComponent<PreviewSceneManager>();
manager.Initialize(prefab);
// HideFlags are special editor-only settings that let you have *secret* GameObjects in a scene, or to tell Unity not to save that temporary GameObject as part of the scene
foreach (var x in go.transform.Traverse())
{
x.gameObject.hideFlags = HideFlags.None
| HideFlags.DontSave
//| HideFlags.DontSaveInBuild
#if VRM_DEVELOP
#else
| HideFlags.HideAndDontSave
#endif
;
}
return manager;
}
#endif
public void Clean()
{
foreach (var kv in m_materialMap)
{
UnityEngine.Object.DestroyImmediate(kv.Value.Material);
}
}
private void Initialize(GameObject prefab)
{
//Debug.LogFormat("[PreviewSceneManager.Initialize] {0}", prefab);
Prefab = prefab;
var materialNames = new List<string>();
var map = new Dictionary<Material, Material>();
Func<Material, Material> getOrCreateMaterial = src =>
{
if (src == null) return null;
if (string.IsNullOrEmpty(src.name)) return null; // !
Material dst;
if (!map.TryGetValue(src, out dst))
{
dst = new Material(src);
map.Add(src, dst);
//Debug.LogFormat("add material {0}", src.name);
materialNames.Add(src.name);
m_materialMap.Add(src.name, MaterialItem.Create(dst));
}
return dst;
};
m_meshes = transform.Traverse()
.Select(x => MeshPreviewItem.Create(x, transform, getOrCreateMaterial))
.Where(x => x != null)
.ToArray()
;
MaterialNames = materialNames.ToArray();
m_blendShapeMeshes = m_meshes
.Where(x => x.SkinnedMeshRenderer != null
&& x.SkinnedMeshRenderer.sharedMesh.blendShapeCount > 0)
.ToArray();
//Bake(values, materialValues);
m_rendererPathList = m_meshes.Select(x => x.Path).ToArray();
m_skinnedMeshRendererPathList = m_meshes
.Where(x => x.SkinnedMeshRenderer != null)
.Select(x => x.Path)
.ToArray();
var animator = GetComponent<Animator>();
if (animator != null)
{
var head = animator.GetBoneTransform(HumanBodyBones.Head);
if (head != null)
{
m_target = head;
}
}
}
MeshPreviewItem[] m_meshes;
MeshPreviewItem[] m_blendShapeMeshes;
public IEnumerable<MeshPreviewItem> EnumRenderItems
{
get
{
if (m_meshes != null)
{
foreach (var x in m_meshes)
{
yield return x;
}
}
}
}
public string[] MaterialNames
{
get;
private set;
}
Dictionary<string, MaterialItem> m_materialMap = new Dictionary<string, MaterialItem>();
string[] m_rendererPathList;
public string[] RendererPathList
{
get { return m_rendererPathList; }
}
string[] m_skinnedMeshRendererPathList;
public string[] SkinnedMeshRendererPathList
{
get { return m_skinnedMeshRendererPathList; }
}
public string[] GetBlendShapeNames(int blendShapeMeshIndex)
{
if (blendShapeMeshIndex >= 0 && blendShapeMeshIndex < m_blendShapeMeshes.Length)
{
var item = m_blendShapeMeshes[blendShapeMeshIndex];
return item.BlendShapeNames;
}
return null;
}
public MaterialItem GetMaterialItem(string materialName)
{
MaterialItem item;
if (!m_materialMap.TryGetValue(materialName, out item))
{
return null;
}
return item;
}
public Transform m_target;
public Vector3 TargetPosition
{
get
{
if (m_target == null)
{
return new Vector3(0, 1.4f, 0);
}
return m_target.position + new Vector3(0, 0.1f, 0);
}
}
#if UNITY_EDITOR
public struct BakeValue
{
public IEnumerable<BlendShapeBinding> BlendShapeBindings;
public IEnumerable<MaterialValueBinding> MaterialValueBindings;
public float Weight;
}
Bounds m_bounds;
public void Bake(BakeValue bake)
{
//
// Bake BlendShape
//
m_bounds = default(Bounds);
if (m_meshes != null)
{
foreach (var x in m_meshes)
{
x.Bake(bake.BlendShapeBindings, bake.Weight);
m_bounds.Expand(x.Mesh.bounds.size);
}
}
//
// Update Material
//
if (bake.MaterialValueBindings != null && m_materialMap != null)
{
// clear
//Debug.LogFormat("clear material");
foreach (var kv in m_materialMap)
{
foreach (var _kv in kv.Value.PropMap)
{
kv.Value.Material.SetColor(_kv.Key, _kv.Value.DefaultValues);
}
}
foreach (var x in bake.MaterialValueBindings)
{
MaterialItem item;
if (m_materialMap.TryGetValue(x.MaterialName, out item))
{
//Debug.Log("set material");
PropItem prop;
if (item.PropMap.TryGetValue(x.ValueName, out prop))
{
var valueName = x.ValueName;
if (valueName.EndsWith("_ST_S")
|| valueName.EndsWith("_ST_T"))
{
valueName = valueName.Substring(0, valueName.Length - 2);
}
var value = item.Material.GetVector(valueName);
//Debug.LogFormat("{0} => {1}", valueName, x.TargetValue);
value += ((x.TargetValue - x.BaseValue) * bake.Weight);
item.Material.SetColor(valueName, value);
}
}
}
}
}
#endif
/// <summary>
/// カメラパラメーターを決める
/// </summary>
/// <param name="camera"></param>
public void SetupCamera(Camera camera, Vector3 target, float yaw, float pitch, Vector3 position)
{
camera.backgroundColor = Color.gray;
camera.clearFlags = CameraClearFlags.Color;
// projection
//float magnitude = m_bounds.extents.magnitude * 0.5f;
//float distance = magnitude;
//var distance = target.magnitude;
camera.fieldOfView = 27f;
camera.nearClipPlane = 0.3f;
camera.farClipPlane = -position.z /*+ magnitude*/ * 2.1f;
var t = Matrix4x4.Translate(position);
var r = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(pitch, yaw, 0), Vector3.one);
// 回転してから移動
var m = r * t;
camera.transform.position = target + m.ExtractPosition();
camera.transform.rotation = m.ExtractRotation();
//camera.transform.LookAt(target);
//previewLayer のみ表示する
//camera.cullingMask = 1 << PreviewLayer;
}
}
}

View File

@ -1,201 +1,201 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VRM
{
[DisallowMultipleComponent]
public class VRMBlendShapeProxy : MonoBehaviour, IVRMComponent
{
[SerializeField]
public BlendShapeAvatar BlendShapeAvatar;
public void OnImported(VRMImporterContext context)
{
throw new NotImplementedException();
}
BlendShapeMerger m_merger;
private void OnDestroy()
{
if (m_merger != null)
{
m_merger.RestoreMaterialInitialValues(BlendShapeAvatar.Clips);
}
}
private void Start()
{
if (BlendShapeAvatar != null)
{
if (m_merger == null)
{
m_merger = new BlendShapeMerger(BlendShapeAvatar.Clips, transform);
}
}
}
/// <summary>
/// Immediately SetValue
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void ImmediatelySetValue(BlendShapeKey key, float value)
{
if (m_merger != null)
{
m_merger.ImmediatelySetValue(key, value);
}
}
/// <summary>
/// AccumulateValue. After, Should call Apply
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AccumulateValue(BlendShapeKey key, float value)
{
if (m_merger != null)
{
m_merger.AccumulateValue(key, value);
}
}
/// <summary>
/// Get a blendShape value
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public float GetValue(BlendShapeKey key)
{
if (m_merger == null)
{
return 0;
}
return m_merger.GetValue(key);
}
public IEnumerable<KeyValuePair<BlendShapeKey, float>> GetValues()
{
if (m_merger != null && BlendShapeAvatar != null)
{
foreach (var clip in BlendShapeAvatar.Clips)
{
var key = BlendShapeKey.CreateFrom(clip);
yield return new KeyValuePair<BlendShapeKey, float>(key, m_merger.GetValue(key));
}
}
}
/// <summary>
/// Set blendShape values immediate.
/// </summary>
/// <param name="values"></param>
public void SetValues(IEnumerable<KeyValuePair<BlendShapeKey, float>> values)
{
if (m_merger != null)
{
m_merger.SetValues(values);
}
}
/// <summary>
/// Apply blendShape values that use SetValue apply=false
/// </summary>
public void Apply()
{
if (m_merger != null)
{
m_merger.Apply();
}
}
}
public static class VRMBlendShapeProxyExtensions
{
public static float GetValue(this VRMBlendShapeProxy proxy, BlendShapePreset key)
{
return proxy.GetValue(new BlendShapeKey(key));
}
public static float GetValue(this VRMBlendShapeProxy proxy, String key)
{
return proxy.GetValue(new BlendShapeKey(key));
}
[Obsolete("Use ImmediatelySetValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, BlendShapePreset key, float value)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
public static void ImmediatelySetValue(this VRMBlendShapeProxy proxy, BlendShapePreset key, float value)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
public static void AccumulateValue(this VRMBlendShapeProxy proxy, BlendShapePreset key, float value)
{
proxy.AccumulateValue(new BlendShapeKey(key), value);
}
[Obsolete("Use ImmediatelySetValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, String key, float value)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
public static void ImmediatelySetValue(this VRMBlendShapeProxy proxy, String key, float value)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
public static void AccumulateValue(this VRMBlendShapeProxy proxy, String key, float value)
{
proxy.AccumulateValue(new BlendShapeKey(key), value);
}
[Obsolete("Use ImmediatelySetValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, BlendShapeKey key, float value)
{
proxy.ImmediatelySetValue(key, value);
}
[Obsolete("Use ImmediatelySetValue or AccumulateValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, BlendShapePreset key, float value, bool apply)
{
if (apply)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
else
{
proxy.AccumulateValue(new BlendShapeKey(key), value);
}
}
[Obsolete("Use ImmediatelySetValue or AccumulateValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, String key, float value, bool apply)
{
if (apply)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
else
{
proxy.AccumulateValue(new BlendShapeKey(key), value);
}
}
[Obsolete("Use ImmediatelySetValue or AccumulateValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, BlendShapeKey key, float value, bool apply)
{
if (apply)
{
proxy.ImmediatelySetValue(key, value);
}
else
{
proxy.AccumulateValue(key, value);
}
}
}
}
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VRM
{
[DisallowMultipleComponent]
public class VRMBlendShapeProxy : MonoBehaviour, IVRMComponent
{
[SerializeField]
public BlendShapeAvatar BlendShapeAvatar;
public void OnImported(VRMImporterContext context)
{
throw new NotImplementedException();
}
BlendShapeMerger m_merger;
private void OnDestroy()
{
if (m_merger != null)
{
m_merger.RestoreMaterialInitialValues(BlendShapeAvatar.Clips);
}
}
private void Start()
{
if (BlendShapeAvatar != null)
{
if (m_merger == null)
{
m_merger = new BlendShapeMerger(BlendShapeAvatar.Clips, transform);
}
}
}
/// <summary>
/// Immediately SetValue
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void ImmediatelySetValue(BlendShapeKey key, float value)
{
if (m_merger != null)
{
m_merger.ImmediatelySetValue(key, value);
}
}
/// <summary>
/// AccumulateValue. After, Should call Apply
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AccumulateValue(BlendShapeKey key, float value)
{
if (m_merger != null)
{
m_merger.AccumulateValue(key, value);
}
}
/// <summary>
/// Get a blendShape value
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public float GetValue(BlendShapeKey key)
{
if (m_merger == null)
{
return 0;
}
return m_merger.GetValue(key);
}
public IEnumerable<KeyValuePair<BlendShapeKey, float>> GetValues()
{
if (m_merger != null && BlendShapeAvatar != null)
{
foreach (var clip in BlendShapeAvatar.Clips)
{
var key = BlendShapeKey.CreateFrom(clip);
yield return new KeyValuePair<BlendShapeKey, float>(key, m_merger.GetValue(key));
}
}
}
/// <summary>
/// Set blendShape values immediate.
/// </summary>
/// <param name="values"></param>
public void SetValues(IEnumerable<KeyValuePair<BlendShapeKey, float>> values)
{
if (m_merger != null)
{
m_merger.SetValues(values);
}
}
/// <summary>
/// Apply blendShape values that use SetValue apply=false
/// </summary>
public void Apply()
{
if (m_merger != null)
{
m_merger.Apply();
}
}
}
public static class VRMBlendShapeProxyExtensions
{
public static float GetValue(this VRMBlendShapeProxy proxy, BlendShapePreset key)
{
return proxy.GetValue(new BlendShapeKey(key));
}
public static float GetValue(this VRMBlendShapeProxy proxy, String key)
{
return proxy.GetValue(new BlendShapeKey(key));
}
[Obsolete("Use ImmediatelySetValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, BlendShapePreset key, float value)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
public static void ImmediatelySetValue(this VRMBlendShapeProxy proxy, BlendShapePreset key, float value)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
public static void AccumulateValue(this VRMBlendShapeProxy proxy, BlendShapePreset key, float value)
{
proxy.AccumulateValue(new BlendShapeKey(key), value);
}
[Obsolete("Use ImmediatelySetValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, String key, float value)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
public static void ImmediatelySetValue(this VRMBlendShapeProxy proxy, String key, float value)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
public static void AccumulateValue(this VRMBlendShapeProxy proxy, String key, float value)
{
proxy.AccumulateValue(new BlendShapeKey(key), value);
}
[Obsolete("Use ImmediatelySetValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, BlendShapeKey key, float value)
{
proxy.ImmediatelySetValue(key, value);
}
[Obsolete("Use ImmediatelySetValue or AccumulateValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, BlendShapePreset key, float value, bool apply)
{
if (apply)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
else
{
proxy.AccumulateValue(new BlendShapeKey(key), value);
}
}
[Obsolete("Use ImmediatelySetValue or AccumulateValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, String key, float value, bool apply)
{
if (apply)
{
proxy.ImmediatelySetValue(new BlendShapeKey(key), value);
}
else
{
proxy.AccumulateValue(new BlendShapeKey(key), value);
}
}
[Obsolete("Use ImmediatelySetValue or AccumulateValue")]
public static void SetValue(this VRMBlendShapeProxy proxy, BlendShapeKey key, float value, bool apply)
{
if (apply)
{
proxy.ImmediatelySetValue(key, value);
}
else
{
proxy.AccumulateValue(key, value);
}
}
}
}

View File

@ -1,20 +1,20 @@
using System;
namespace VRM
{
public static class EnumUtil
{
public static T TryParseOrDefault<T>(string src, T defaultValue=default(T)) where T : struct
{
try
{
return (T)Enum.Parse(typeof(T), src, true);
}
catch (Exception)
{
return defaultValue;
}
}
}
}
using System;
namespace VRM
{
public static class EnumUtil
{
public static T TryParseOrDefault<T>(string src, T defaultValue=default(T)) where T : struct
{
try
{
return (T)Enum.Parse(typeof(T), src, true);
}
catch (Exception)
{
return defaultValue;
}
}
}
}

View File

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

View File

@ -1,28 +1,28 @@
using UnityEditor;
namespace VRM
{
[CustomEditor(typeof(VRMFirstPerson))]
class VRMFirstPersonEditor : Editor
{
void OnSceneGUI()
{
var component = target as VRMFirstPerson;
var head = component.FirstPersonBone;
if (head == null)
{
return;
}
var worldOffset = head.localToWorldMatrix.MultiplyPoint(component.FirstPersonOffset);
worldOffset = Handles.PositionHandle(worldOffset, head.rotation);
Handles.Label(worldOffset, "FirstPersonOffset");
component.FirstPersonOffset = head.worldToLocalMatrix.MultiplyPoint(worldOffset);
}
}
}
using UnityEditor;
namespace VRM
{
[CustomEditor(typeof(VRMFirstPerson))]
class VRMFirstPersonEditor : Editor
{
void OnSceneGUI()
{
var component = target as VRMFirstPerson;
var head = component.FirstPersonBone;
if (head == null)
{
return;
}
var worldOffset = head.localToWorldMatrix.MultiplyPoint(component.FirstPersonOffset);
worldOffset = Handles.PositionHandle(worldOffset, head.rotation);
Handles.Label(worldOffset, "FirstPersonOffset");
component.FirstPersonOffset = head.worldToLocalMatrix.MultiplyPoint(worldOffset);
}
}
}

View File

@ -1,265 +1,265 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
namespace VRM
{
public class VRMFirstPerson : MonoBehaviour
{
// If no layer names are set, use the default layer IDs.
// Otherwise use the two Unity layers called "VRMFirstPersonOnly" and "VRMThirdPersonOnly".
public static bool TriedSetupLayer = false;
public static int FIRSTPERSON_ONLY_LAYER = 9;
public static 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.GLTF.extensions.VRM.firstPerson.meshAnnotations)
{
if (x.mesh == index)
{
return EnumUtil.TryParseOrDefault<FirstPersonFlag>(x.firstPersonFlag);
}
}
return FirstPersonFlag.Auto;
}
void CreateHeadlessModel(Renderer _renderer, Transform EraseRoot)
{
{
var renderer = _renderer as SkinnedMeshRenderer;
if (renderer != null)
{
CreateHeadlessModelForSkinnedMeshRenderer(renderer, EraseRoot);
return;
}
}
{
var renderer = _renderer as MeshRenderer;
if (renderer != null)
{
CreateHeadlessModelForMeshRenderer(renderer, EraseRoot);
return;
}
}
// ここには来ない
}
public static void SetupLayers()
{
if (!TriedSetupLayer) {
TriedSetupLayer = true;
int layer = LayerMask.NameToLayer("VRMFirstPersonOnly");
FIRSTPERSON_ONLY_LAYER = (layer == -1) ? FIRSTPERSON_ONLY_LAYER : layer;
layer = LayerMask.NameToLayer("VRMThirdPersonOnly");
THIRDPERSON_ONLY_LAYER = (layer == -1) ? THIRDPERSON_ONLY_LAYER : layer;
}
}
private static void CreateHeadlessModelForMeshRenderer(MeshRenderer renderer, Transform eraseRoot)
{
if (renderer.transform.Ancestors().Any(x => x == eraseRoot))
{
// 祖先に削除ボーンが居る
SetupLayers();
renderer.gameObject.layer = THIRDPERSON_ONLY_LAYER;
}
else
{
// 特に変更しない => 両方表示
}
}
private static void CreateHeadlessModelForSkinnedMeshRenderer(SkinnedMeshRenderer renderer, Transform eraseRoot)
{
SetupLayers();
renderer.gameObject.layer = THIRDPERSON_ONLY_LAYER;
var go = new GameObject("_headless_" + renderer.name);
go.layer = FIRSTPERSON_ONLY_LAYER;
go.transform.SetParent(renderer.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;
}
bool m_done;
/// <summary>
/// 配下のモデルのレイヤー設定など
/// </summary>
public void Setup()
{
SetupLayers();
if (m_done) return;
m_done = true;
foreach (var x in Renderers)
{
switch (x.FirstPersonFlag)
{
case FirstPersonFlag.Auto:
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;
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
namespace VRM
{
public class VRMFirstPerson : MonoBehaviour
{
// If no layer names are set, use the default layer IDs.
// Otherwise use the two Unity layers called "VRMFirstPersonOnly" and "VRMThirdPersonOnly".
public static bool TriedSetupLayer = false;
public static int FIRSTPERSON_ONLY_LAYER = 9;
public static 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.GLTF.extensions.VRM.firstPerson.meshAnnotations)
{
if (x.mesh == index)
{
return EnumUtil.TryParseOrDefault<FirstPersonFlag>(x.firstPersonFlag);
}
}
return FirstPersonFlag.Auto;
}
void CreateHeadlessModel(Renderer _renderer, Transform EraseRoot)
{
{
var renderer = _renderer as SkinnedMeshRenderer;
if (renderer != null)
{
CreateHeadlessModelForSkinnedMeshRenderer(renderer, EraseRoot);
return;
}
}
{
var renderer = _renderer as MeshRenderer;
if (renderer != null)
{
CreateHeadlessModelForMeshRenderer(renderer, EraseRoot);
return;
}
}
// ここには来ない
}
public static void SetupLayers()
{
if (!TriedSetupLayer) {
TriedSetupLayer = true;
int layer = LayerMask.NameToLayer("VRMFirstPersonOnly");
FIRSTPERSON_ONLY_LAYER = (layer == -1) ? FIRSTPERSON_ONLY_LAYER : layer;
layer = LayerMask.NameToLayer("VRMThirdPersonOnly");
THIRDPERSON_ONLY_LAYER = (layer == -1) ? THIRDPERSON_ONLY_LAYER : layer;
}
}
private static void CreateHeadlessModelForMeshRenderer(MeshRenderer renderer, Transform eraseRoot)
{
if (renderer.transform.Ancestors().Any(x => x == eraseRoot))
{
// 祖先に削除ボーンが居る
SetupLayers();
renderer.gameObject.layer = THIRDPERSON_ONLY_LAYER;
}
else
{
// 特に変更しない => 両方表示
}
}
private static void CreateHeadlessModelForSkinnedMeshRenderer(SkinnedMeshRenderer renderer, Transform eraseRoot)
{
SetupLayers();
renderer.gameObject.layer = THIRDPERSON_ONLY_LAYER;
var go = new GameObject("_headless_" + renderer.name);
go.layer = FIRSTPERSON_ONLY_LAYER;
go.transform.SetParent(renderer.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;
}
bool m_done;
/// <summary>
/// 配下のモデルのレイヤー設定など
/// </summary>
public void Setup()
{
SetupLayers();
if (m_done) return;
m_done = true;
foreach (var x in Renderers)
{
switch (x.FirstPersonFlag)
{
case FirstPersonFlag.Auto:
CreateHeadlessModel(x.Renderer, FirstPersonBone);
break;
case FirstPersonFlag.FirstPersonOnly:
x.Renderer.gameObject.layer = FIRSTPERSON_ONLY_LAYER;
break;
case FirstPersonFlag.ThirdPersonOnly:
x.Renderer.gameObject.layer = THIRDPERSON_ONLY_LAYER;
break;
case FirstPersonFlag.Both:
//x.Renderer.gameObject.layer = 0;
break;
}
}
}
}
}

View File

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

View File

@ -1,214 +1,214 @@
#if false
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);
}
}
}
}
#if false
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);
}
}
}
}
#endif

View File

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

View File

@ -1,90 +1,90 @@
using System.Text;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public class VRMExporterWizard : ScriptableWizard
{
const string EXTENSION = ".vrm";
VRMMeta m_meta;
public VRMExportSettings m_settings = new VRMExportSettings();
public static void CreateWizard()
{
var wiz = ScriptableWizard.DisplayWizard<VRMExporterWizard>(
"VRM Exporter", "Export");
var go = Selection.activeObject as GameObject;
// update checkbox
wiz.m_settings.InitializeFrom(go);
wiz.OnWizardUpdate();
}
void OnWizardCreate()
{
// save dialog
var path = EditorUtility.SaveFilePanel(
"Save vrm",
null,
m_settings.Source.name + EXTENSION,
EXTENSION.Substring(1));
if (string.IsNullOrEmpty(path))
{
return;
}
// export
m_settings.Export(path);
}
void OnWizardUpdate()
{
isValid = true;
var helpBuilder = new StringBuilder();
var errorBuilder = new StringBuilder();
foreach(var msg in m_settings.CanExport())
{
isValid = false;
errorBuilder.Append(msg);
}
helpString = helpBuilder.ToString();
errorString = errorBuilder.ToString();
}
}
public static class VRMExporterMenu
{
const string CONVERT_HUMANOID_KEY = VRMVersion.VRM_VERSION + "/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;
}
return true;
}
[MenuItem(CONVERT_HUMANOID_KEY, false, 1)]
private static void ExportFromMenu()
{
VRMExporterWizard.CreateWizard();
}
}
}
using System.Text;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public class VRMExporterWizard : ScriptableWizard
{
const string EXTENSION = ".vrm";
VRMMeta m_meta;
public VRMExportSettings m_settings = new VRMExportSettings();
public static void CreateWizard()
{
var wiz = ScriptableWizard.DisplayWizard<VRMExporterWizard>(
"VRM Exporter", "Export");
var go = Selection.activeObject as GameObject;
// update checkbox
wiz.m_settings.InitializeFrom(go);
wiz.OnWizardUpdate();
}
void OnWizardCreate()
{
// save dialog
var path = EditorUtility.SaveFilePanel(
"Save vrm",
null,
m_settings.Source.name + EXTENSION,
EXTENSION.Substring(1));
if (string.IsNullOrEmpty(path))
{
return;
}
// export
m_settings.Export(path);
}
void OnWizardUpdate()
{
isValid = true;
var helpBuilder = new StringBuilder();
var errorBuilder = new StringBuilder();
foreach(var msg in m_settings.CanExport())
{
isValid = false;
errorBuilder.Append(msg);
}
helpString = helpBuilder.ToString();
errorString = errorBuilder.ToString();
}
}
public static class VRMExporterMenu
{
const string CONVERT_HUMANOID_KEY = VRMVersion.VRM_VERSION + "/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;
}
return true;
}
[MenuItem(CONVERT_HUMANOID_KEY, false, 1)]
private static void ExportFromMenu()
{
VRMExporterWizard.CreateWizard();
}
}
}

View File

@ -1,61 +1,61 @@
using System.Linq;
using UnityEditor;
using UnityEngine;
using UniGLTF;
namespace VRM
{
public static class VRMHumanoidNorimalizerMenu
{
const string MENU_KEY = VRMVersion.VRM_VERSION + "/Freeze T-Pose";
[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;
GameObject normalizedRoot = null;
using (new VRMExportSettings.RecordDisposer(go.transform.Traverse().ToArray(), "before normalize"))
{
var normalized = BoneNormalizer.Execute(go, true, false);
VRMExportSettings.CopyVRMComponents(go, normalized.Root, normalized.BoneMap);
normalizedRoot = normalized.Root;
}
Selection.activeGameObject = normalizedRoot;
}
}
}
using System.Linq;
using UnityEditor;
using UnityEngine;
using UniGLTF;
namespace VRM
{
public static class VRMHumanoidNorimalizerMenu
{
const string MENU_KEY = VRMVersion.VRM_VERSION + "/Freeze T-Pose";
[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;
GameObject normalizedRoot = null;
using (new VRMExportSettings.RecordDisposer(go.transform.Traverse().ToArray(), "before normalize"))
{
var normalized = BoneNormalizer.Execute(go, true, false);
VRMExportSettings.CopyVRMComponents(go, normalized.Root, normalized.BoneMap);
normalizedRoot = normalized.Root;
}
Selection.activeGameObject = normalizedRoot;
}
}
}

View File

@ -1,67 +1,67 @@
using System.IO;
using UnityEditor;
using UnityEngine;
using UniGLTF;
namespace VRM
{
public static class VRMImporterMenu
{
[MenuItem(VRMVersion.VRM_VERSION + "/Import", priority = 1)]
static void ImportMenu()
{
var path = EditorUtility.OpenFilePanel("open vrm", "", "vrm");
if (string.IsNullOrEmpty(path))
{
return;
}
if (Application.isPlaying)
{
// load into scene
var context = new VRMImporterContext();
context.Load(path);
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();
Selection.activeGameObject = context.Root;
}
else
{
if (path.StartsWithUnityAssetPath())
{
Debug.LogWarningFormat("disallow import from folder under the Assets");
return;
}
var assetPath = EditorUtility.SaveFilePanel("save prefab", "Assets", Path.GetFileNameWithoutExtension(path), "prefab");
if (string.IsNullOrEmpty(path))
{
return;
}
if (!assetPath.StartsWithUnityAssetPath())
{
Debug.LogWarningFormat("out of asset path: {0}", assetPath);
return;
}
// import as asset
var prefabPath = UnityPath.FromUnityPath(assetPath);
var context = new VRMImporterContext();
context.ParseGlb(File.ReadAllBytes(path));
context.ExtranctImages(prefabPath);
EditorApplication.delayCall += () =>
{
//
// after textures imported
//
context.Load();
context.SaveAsAsset(prefabPath);
context.EditorDestroyRoot();
};
}
}
}
}
using System.IO;
using UnityEditor;
using UnityEngine;
using UniGLTF;
namespace VRM
{
public static class VRMImporterMenu
{
[MenuItem(VRMVersion.VRM_VERSION + "/Import", priority = 1)]
static void ImportMenu()
{
var path = EditorUtility.OpenFilePanel("open vrm", "", "vrm");
if (string.IsNullOrEmpty(path))
{
return;
}
if (Application.isPlaying)
{
// load into scene
var context = new VRMImporterContext();
context.Load(path);
context.ShowMeshes();
context.EnableUpdateWhenOffscreen();
Selection.activeGameObject = context.Root;
}
else
{
if (path.StartsWithUnityAssetPath())
{
Debug.LogWarningFormat("disallow import from folder under the Assets");
return;
}
var assetPath = EditorUtility.SaveFilePanel("save prefab", "Assets", Path.GetFileNameWithoutExtension(path), "prefab");
if (string.IsNullOrEmpty(path))
{
return;
}
if (!assetPath.StartsWithUnityAssetPath())
{
Debug.LogWarningFormat("out of asset path: {0}", assetPath);
return;
}
// import as asset
var prefabPath = UnityPath.FromUnityPath(assetPath);
var context = new VRMImporterContext();
context.ParseGlb(File.ReadAllBytes(path));
context.ExtranctImages(prefabPath);
EditorApplication.delayCall += () =>
{
//
// after textures imported
//
context.Load();
context.SaveAsAsset(prefabPath);
context.EditorDestroyRoot();
};
}
}
}
}

View File

@ -1,156 +1,156 @@
using System;
using System.IO;
using System.Text;
using UniGLTF;
using UniJSON;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public static class VRMVersionMenu
{
const string path = "Assets/VRM/Scripts/Format/VRMVersion.cs";
const string template = @"
namespace VRM
{{
public static partial class VRMVersion
{{
public const int MAJOR = {0};
public const int MINOR = {1};
public const string VERSION = ""{0}.{1}"";
}}
}}
";
#if VRM_DEVELOP
[MenuItem(VRMVersion.VRM_VERSION + "/Increment")]
#endif
static void IncrementVersion()
{
var source = string.Format(template, VRMVersion.MAJOR, VRMVersion.MINOR + 1);
File.WriteAllText(path, source);
AssetDatabase.Refresh();
}
#if VRM_DEVELOP
[MenuItem(VRMVersion.VRM_VERSION + "/Decrement")]
#endif
static void DecrementVersion()
{
var source = string.Format(template, VRMVersion.MAJOR, VRMVersion.MINOR - 1);
File.WriteAllText(path, source);
AssetDatabase.Refresh();
}
static string GetTitle(ListTreeNode<JsonValue> node)
{
try
{
var titleNode = node["title"];
if (titleNode.IsString())
{
return titleNode.GetString();
}
}
catch(Exception)
{
}
return "";
}
static void TraverseItem(ListTreeNode<JsonValue> node, JsonFormatter f, UnityPath dir)
{
var title = GetTitle(node);
if (string.IsNullOrEmpty(title))
{
Traverse(node, f, dir);
}
else
{
// ref
f.BeginMap();
f.Key("$ref");
var fileName = string.Format("{0}.schema.json", title);
f.Value(fileName);
f.EndMap();
// new formatter
{
var subFormatter = new JsonFormatter(4);
subFormatter.BeginMap();
foreach (var _kv in node.ObjectItems())
{
subFormatter.Key(_kv.Key.GetUtf8String());
Traverse(_kv.Value, subFormatter, dir);
}
subFormatter.EndMap();
var subJson = subFormatter.ToString();
var path = dir.Child(fileName);
File.WriteAllText(path.FullPath, subJson);
}
}
}
static void Traverse(ListTreeNode<JsonValue> node, JsonFormatter f, UnityPath dir)
{
if (node.IsArray())
{
f.BeginList();
foreach (var x in node.ArrayItems())
{
TraverseItem(x, f, dir);
}
f.EndList();
}
else if (node.IsMap())
{
f.BeginMap();
foreach (var kv in node.ObjectItems())
{
f.Key(kv.Key.GetUtf8String());
TraverseItem(kv.Value, f, dir);
}
f.EndMap();
}
else
{
f.Value(node);
}
}
static UnityPath SplitAndWriteJson(ListTreeNode<JsonValue> parsed, UnityPath dir)
{
var f = new JsonFormatter(4);
Traverse(parsed, f, dir);
var json = f.ToString();
var path = dir.Child("vrm.schema.json");
Debug.LogFormat("write JsonSchema: {0}", path.FullPath);
File.WriteAllText(path.FullPath, json);
return path;
}
#if VRM_DEVELOP
[MenuItem(VRMVersion.VRM_VERSION + "/Export JsonSchema")]
#endif
static void ExportJsonSchema()
{
var schema = JsonSchema.FromType<glTF_VRM_extensions>();
var f = new JsonFormatter(2);
schema.ToJson(f);
var json = f.ToString();
var dir = UnityPath.FromFullpath(Application.dataPath + "/VRM/specification/0.0/schema");
dir.EnsureFolder();
var path = SplitAndWriteJson(JsonParser.Parse(json), dir);
Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path.Value);
}
}
}
using System;
using System.IO;
using System.Text;
using UniGLTF;
using UniJSON;
using UnityEditor;
using UnityEngine;
namespace VRM
{
public static class VRMVersionMenu
{
const string path = "Assets/VRM/Scripts/Format/VRMVersion.cs";
const string template = @"
namespace VRM
{{
public static partial class VRMVersion
{{
public const int MAJOR = {0};
public const int MINOR = {1};
public const string VERSION = ""{0}.{1}"";
}}
}}
";
#if VRM_DEVELOP
[MenuItem(VRMVersion.VRM_VERSION + "/Increment")]
#endif
static void IncrementVersion()
{
var source = string.Format(template, VRMVersion.MAJOR, VRMVersion.MINOR + 1);
File.WriteAllText(path, source);
AssetDatabase.Refresh();
}
#if VRM_DEVELOP
[MenuItem(VRMVersion.VRM_VERSION + "/Decrement")]
#endif
static void DecrementVersion()
{
var source = string.Format(template, VRMVersion.MAJOR, VRMVersion.MINOR - 1);
File.WriteAllText(path, source);
AssetDatabase.Refresh();
}
static string GetTitle(ListTreeNode<JsonValue> node)
{
try
{
var titleNode = node["title"];
if (titleNode.IsString())
{
return titleNode.GetString();
}
}
catch(Exception)
{
}
return "";
}
static void TraverseItem(ListTreeNode<JsonValue> node, JsonFormatter f, UnityPath dir)
{
var title = GetTitle(node);
if (string.IsNullOrEmpty(title))
{
Traverse(node, f, dir);
}
else
{
// ref
f.BeginMap();
f.Key("$ref");
var fileName = string.Format("{0}.schema.json", title);
f.Value(fileName);
f.EndMap();
// new formatter
{
var subFormatter = new JsonFormatter(4);
subFormatter.BeginMap();
foreach (var _kv in node.ObjectItems())
{
subFormatter.Key(_kv.Key.GetUtf8String());
Traverse(_kv.Value, subFormatter, dir);
}
subFormatter.EndMap();
var subJson = subFormatter.ToString();
var path = dir.Child(fileName);
File.WriteAllText(path.FullPath, subJson);
}
}
}
static void Traverse(ListTreeNode<JsonValue> node, JsonFormatter f, UnityPath dir)
{
if (node.IsArray())
{
f.BeginList();
foreach (var x in node.ArrayItems())
{
TraverseItem(x, f, dir);
}
f.EndList();
}
else if (node.IsMap())
{
f.BeginMap();
foreach (var kv in node.ObjectItems())
{
f.Key(kv.Key.GetUtf8String());
TraverseItem(kv.Value, f, dir);
}
f.EndMap();
}
else
{
f.Value(node);
}
}
static UnityPath SplitAndWriteJson(ListTreeNode<JsonValue> parsed, UnityPath dir)
{
var f = new JsonFormatter(4);
Traverse(parsed, f, dir);
var json = f.ToString();
var path = dir.Child("vrm.schema.json");
Debug.LogFormat("write JsonSchema: {0}", path.FullPath);
File.WriteAllText(path.FullPath, json);
return path;
}
#if VRM_DEVELOP
[MenuItem(VRMVersion.VRM_VERSION + "/Export JsonSchema")]
#endif
static void ExportJsonSchema()
{
var schema = JsonSchema.FromType<glTF_VRM_extensions>();
var f = new JsonFormatter(2);
schema.ToJson(f);
var json = f.ToString();
var dir = UnityPath.FromFullpath(Application.dataPath + "/VRM/specification/0.0/schema");
dir.EnsureFolder();
var path = SplitAndWriteJson(JsonParser.Parse(json), dir);
Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path.Value);
}
}
}

View File

@ -1,51 +1,51 @@
using System;
using System.IO;
using System.Linq;
using UniGLTF;
using UnityEditor;
namespace VRM
{
#if !VRM_STOP_ASSETPOSTPROCESSOR
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")
{
ImportVrm(UnityPath.FromUnityPath(path));
}
}
}
static void ImportVrm(UnityPath path)
{
if (!path.IsUnderAssetsFolder)
{
throw new Exception();
}
var context = new VRMImporterContext();
context.ParseGlb(File.ReadAllBytes(path.FullPath));
var prefabPath = path.Parent.Child(path.FileNameWithoutExtension + ".prefab");
// save texture assets !
context.ExtranctImages(prefabPath);
EditorApplication.delayCall += () =>
{
//
// after textures imported
//
context.Load();
context.SaveAsAsset(prefabPath);
context.EditorDestroyRoot();
};
}
}
#endif
}
using System;
using System.IO;
using System.Linq;
using UniGLTF;
using UnityEditor;
namespace VRM
{
#if !VRM_STOP_ASSETPOSTPROCESSOR
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")
{
ImportVrm(UnityPath.FromUnityPath(path));
}
}
}
static void ImportVrm(UnityPath path)
{
if (!path.IsUnderAssetsFolder)
{
throw new Exception();
}
var context = new VRMImporterContext();
context.ParseGlb(File.ReadAllBytes(path.FullPath));
var prefabPath = path.Parent.Child(path.FileNameWithoutExtension + ".prefab");
// save texture assets !
context.ExtranctImages(prefabPath);
EditorApplication.delayCall += () =>
{
//
// after textures imported
//
context.Load();
context.SaveAsAsset(prefabPath);
context.EditorDestroyRoot();
};
}
}
#endif
}

View File

@ -1,15 +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))
{ }
}
}
using System;
namespace VRM
{
class VRMException : Exception
{
public VRMException()
{ }
public VRMException(string msg) : base(msg)
{ }
public VRMException(string msg, params object[] args) : base(string.Format(msg, args))
{ }
}
}

View File

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

View File

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

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