mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-04-22 06:57:25 -05:00
Move UniVRM related files into Assets/*. Move Scripts/Editor to Editor in UniVRM
This commit is contained in:
parent
6df13b68e3
commit
f3e130d346
9
Assets/UniVRM.meta
Normal file
9
Assets/UniVRM.meta
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cd93bd4f117e1e54db244c5cadbef691
|
||||
folderAsset: yes
|
||||
timeCreated: 1546003999
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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))
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
Loading…
Reference in New Issue
Block a user