mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-14 14:29:52 -05:00
Merge pull request #523 from ousttrue/update_meshutility
MeshIntegrator と MeshNormalizer を MeshUtility に移動
This commit is contained in:
commit
da5406aefa
|
|
@ -6,7 +6,7 @@ using UnityEditor;
|
|||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRM
|
||||
namespace MeshUtility
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(BoneMeshEraser.EraseBone))]
|
||||
public class EraseBoneDrawer : PropertyDrawer
|
||||
|
|
@ -51,7 +51,7 @@ namespace VRM
|
|||
[SerializeField]
|
||||
BoneMeshEraser.EraseBone[] m_eraseBones;
|
||||
|
||||
[MenuItem(SkinnedMeshUtility.MENU_KEY + "BoneMeshEraser Wizard", priority = SkinnedMeshUtility.MENU_PRIORITY)]
|
||||
[MenuItem(MeshUtility.MENU_PARENT + "BoneMeshEraser Wizard", priority = MeshUtility.MENU_PRIORITY)]
|
||||
static void CreateWizard()
|
||||
{
|
||||
ScriptableWizard.DisplayWizard<BoneMeshEraserWizard>("BoneMeshEraser", "Erase triangles by bone", "Erase");
|
||||
|
|
@ -143,7 +143,7 @@ namespace VRM
|
|||
var bones = m_skinnedMesh.bones;
|
||||
var eraseBones = m_eraseBones
|
||||
.Where(x => x.Erase)
|
||||
.Select(x => bones.IndexOf(x.Bone))
|
||||
.Select(x => Array.IndexOf(bones, x.Bone))
|
||||
.ToArray();
|
||||
|
||||
var meshNode = new GameObject("BoneMeshEraser");
|
||||
|
|
@ -168,7 +168,7 @@ namespace VRM
|
|||
|
||||
// save mesh to Assets
|
||||
var assetPath = string.Format("{0}{1}", go.name, ASSET_SUFFIX);
|
||||
var prefab = SkinnedMeshUtility.GetPrefab(go);
|
||||
var prefab = MeshUtility.GetPrefab(go);
|
||||
if (prefab != null)
|
||||
{
|
||||
var prefabPath = AssetDatabase.GetAssetPath(prefab);
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"name": "MeshUtility",
|
||||
"references": [],
|
||||
"name": "MeshUtility.Editor",
|
||||
"references": [
|
||||
"MeshUtility"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
|
@ -6,12 +6,24 @@ using UnityEditor;
|
|||
|
||||
namespace MeshUtility
|
||||
{
|
||||
public class MeshUtility : MonoBehaviour
|
||||
public class MeshUtility
|
||||
{
|
||||
public const string MENU_PARENT = "Mesh Utility/";
|
||||
public const int MENU_PRIORITY = 11;
|
||||
|
||||
private const string ASSET_SUFFIX = ".mesh.asset";
|
||||
private const string MENU_NAME = "Mesh Utility/Separate Skinned Meshes Contained BlendShape";
|
||||
private const string MENU_NAME = MENU_PARENT + "Separate Skinned Meshes Contained BlendShape";
|
||||
private static readonly Vector3 ZERO_MOVEMENT = Vector3.zero;
|
||||
|
||||
public static Object GetPrefab(GameObject instance)
|
||||
{
|
||||
#if UNITY_2018_2_OR_NEWER
|
||||
return PrefabUtility.GetCorrespondingObjectFromSource(instance);
|
||||
#else
|
||||
return PrefabUtility.GetPrefabParent(go);
|
||||
#endif
|
||||
}
|
||||
|
||||
private enum BlendShapeLogic
|
||||
{
|
||||
WithBlendShape,
|
||||
|
|
@ -51,7 +63,7 @@ namespace MeshUtility
|
|||
|
||||
private static void SeparationProcessing(GameObject go)
|
||||
{
|
||||
var outputObject = Instantiate(go);
|
||||
var outputObject = GameObject.Instantiate(go);
|
||||
var skinnedMeshRenderers = outputObject.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
foreach (var skinnedMeshRenderer in skinnedMeshRenderers)
|
||||
{
|
||||
|
|
@ -127,7 +139,7 @@ namespace MeshUtility
|
|||
// put the mesh without BlendShape in a new SkinnedMeshRenderer
|
||||
var srcGameObject = skinnedMeshRendererInput.gameObject;
|
||||
var srcTransform = skinnedMeshRendererInput.transform.parent;
|
||||
var targetObjectForMeshWithoutBS = Instantiate(srcGameObject);
|
||||
var targetObjectForMeshWithoutBS = GameObject.Instantiate(srcGameObject);
|
||||
targetObjectForMeshWithoutBS.name = srcGameObject.name + "_WithoutBlendShape";
|
||||
targetObjectForMeshWithoutBS.transform.SetParent(srcTransform);
|
||||
var skinnedMeshRendererWithoutBS = targetObjectForMeshWithoutBS.GetComponent<SkinnedMeshRenderer>();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
Mesh processing tool in Unity platform.
|
||||
|
||||
## MeshSeparator
|
||||
## Utilities
|
||||
|
||||
### MeshSeparator
|
||||
|
||||
Separate the target mesh into different categories based on given conditions.
|
||||
|
||||
|
|
@ -10,6 +12,15 @@ Currently support BlendShape mesh separation. See [documentation](Documentation/
|
|||
|
||||
<img src="Documentation/images/blendshape_separator.jpg" width="300">
|
||||
|
||||
### MeshIntegrator
|
||||
|
||||
Integrate all mesh in hierarchy.
|
||||
|
||||
### MeshNormalizer
|
||||
|
||||
Bake hierarchy. This is VRM normalize backend.
|
||||
MeshNormalizer can do blendShape bake.
|
||||
|
||||
## Import MeshUtility
|
||||
|
||||
There are two ways to import MeshUtility into a Unity project.
|
||||
|
|
|
|||
8
Assets/MeshUtility/Runtime.meta
Normal file
8
Assets/MeshUtility/Runtime.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fc6ddf08077eac64fb9e33738e1c314d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UniGLTF;
|
||||
// using UniGLTF;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
|
||||
namespace VRM
|
||||
namespace MeshUtility
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public class VRMBindposeGizmo : MonoBehaviour
|
||||
public class BindposeGizmo : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
Mesh m_target;
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRM
|
||||
namespace MeshUtility
|
||||
{
|
||||
public static class BoneMeshEraser
|
||||
{
|
||||
|
|
@ -151,18 +150,6 @@ namespace VRM
|
|||
return mesh;
|
||||
}
|
||||
|
||||
public static int IndexOf(this Transform[] list, Transform target)
|
||||
{
|
||||
for (int i = 0; i < list.Length; ++i)
|
||||
{
|
||||
if (list[i] == target)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static IEnumerable<Transform> Ancestor(this Transform t)
|
||||
{
|
||||
yield return t;
|
||||
|
|
@ -1,16 +1,41 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniHumanoid;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRM
|
||||
namespace MeshUtility
|
||||
{
|
||||
public static class BoneNormalizer
|
||||
{
|
||||
public delegate Avatar CreateAvatarFunc(GameObject original, GameObject normalized, Dictionary<Transform, Transform> boneMap);
|
||||
|
||||
static (GameObject, Dictionary<Transform, Transform>) NormalizeHierarchy(GameObject go, CreateAvatarFunc createAvatar)
|
||||
{
|
||||
var boneMap = new Dictionary<Transform, Transform>();
|
||||
|
||||
//
|
||||
// 回転・スケールの無いヒエラルキーをコピーする
|
||||
//
|
||||
var normalized = new GameObject(go.name + "(normalized)");
|
||||
normalized.transform.position = go.transform.position;
|
||||
CopyAndBuild(go.transform, normalized.transform, boneMap);
|
||||
|
||||
//
|
||||
// 新しいヒエラルキーからAvatarを作る
|
||||
//
|
||||
{
|
||||
var animator = normalized.AddComponent<Animator>();
|
||||
var avatar = createAvatar(go, normalized, boneMap);
|
||||
avatar.name = go.name + ".normalized";
|
||||
animator.avatar = avatar;
|
||||
}
|
||||
|
||||
return (normalized, boneMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回転とスケールを除去したヒエラルキーをコピーする
|
||||
/// 回転とスケールを除去したヒエラルキーをコピーする。
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <param name="dst"></param>
|
||||
|
|
@ -31,100 +56,6 @@ namespace VRM
|
|||
}
|
||||
}
|
||||
|
||||
static IEnumerable<Transform> Traverse(this Transform t)
|
||||
{
|
||||
yield return t;
|
||||
foreach (Transform child in t)
|
||||
{
|
||||
foreach (var x in child.Traverse())
|
||||
{
|
||||
yield return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EnforceTPose(GameObject go)
|
||||
{
|
||||
var animator = go.GetComponent<Animator>();
|
||||
if (animator == null)
|
||||
{
|
||||
throw new ArgumentException("Animator with avatar is required");
|
||||
}
|
||||
|
||||
var avatar = animator.avatar;
|
||||
if (avatar == null)
|
||||
{
|
||||
throw new ArgumentException("avatar is required");
|
||||
}
|
||||
|
||||
if (!avatar.isValid)
|
||||
{
|
||||
throw new ArgumentException("invalid avatar");
|
||||
}
|
||||
|
||||
if (!avatar.isHuman)
|
||||
{
|
||||
throw new ArgumentException("avatar is not human");
|
||||
}
|
||||
|
||||
HumanPoseTransfer.SetTPose(avatar, go.transform);
|
||||
}
|
||||
|
||||
static GameObject NormalizeHierarchy(GameObject go, Dictionary<Transform, Transform> boneMap)
|
||||
{
|
||||
//
|
||||
// 回転・スケールの無いヒエラルキーをコピーする
|
||||
//
|
||||
var normalized = new GameObject(go.name + "(normalized)");
|
||||
normalized.transform.position = go.transform.position;
|
||||
CopyAndBuild(go.transform, normalized.transform, boneMap);
|
||||
|
||||
//
|
||||
// 新しいヒエラルキーからAvatarを作る
|
||||
//
|
||||
{
|
||||
var src = go.GetComponent<Animator>();
|
||||
|
||||
var srcHumanBones = Enum.GetValues(typeof(HumanBodyBones))
|
||||
.Cast<HumanBodyBones>()
|
||||
.Where(x => x != HumanBodyBones.LastBone)
|
||||
.Select(x => new { Key = x, Value = src.GetBoneTransform(x) })
|
||||
.Where(x => x.Value != null)
|
||||
;
|
||||
|
||||
var map =
|
||||
srcHumanBones
|
||||
.Where(x => boneMap.ContainsKey(x.Value))
|
||||
.ToDictionary(x => x.Key, x => boneMap[x.Value])
|
||||
;
|
||||
|
||||
var animator = normalized.AddComponent<Animator>();
|
||||
var vrmHuman = go.GetComponent<VRMHumanoidDescription>();
|
||||
var avatarDescription = AvatarDescription.Create();
|
||||
if (vrmHuman != null && vrmHuman.Description != null)
|
||||
{
|
||||
avatarDescription.armStretch = vrmHuman.Description.armStretch;
|
||||
avatarDescription.legStretch = vrmHuman.Description.legStretch;
|
||||
avatarDescription.upperArmTwist = vrmHuman.Description.upperArmTwist;
|
||||
avatarDescription.lowerArmTwist = vrmHuman.Description.lowerArmTwist;
|
||||
avatarDescription.upperLegTwist = vrmHuman.Description.upperLegTwist;
|
||||
avatarDescription.lowerLegTwist = vrmHuman.Description.lowerLegTwist;
|
||||
avatarDescription.feetSpacing = vrmHuman.Description.feetSpacing;
|
||||
avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF;
|
||||
}
|
||||
avatarDescription.SetHumanBones(map);
|
||||
var avatar = avatarDescription.CreateAvatar(normalized.transform);
|
||||
|
||||
avatar.name = go.name + ".normalized";
|
||||
animator.avatar = avatar;
|
||||
|
||||
var humanPoseTransfer = normalized.AddComponent<HumanPoseTransfer>();
|
||||
humanPoseTransfer.Avatar = avatar;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
class BlendShapeReport
|
||||
{
|
||||
string m_name;
|
||||
|
|
@ -233,7 +164,7 @@ namespace VRM
|
|||
if (boneMap.TryGetValue(srcBone, out Transform dstBone))
|
||||
{
|
||||
// 対応するボーンが存在する
|
||||
var dstIndex = dstBones.IndexOf(dstBone);
|
||||
var dstIndex = Array.IndexOf(dstBones, dstBone);
|
||||
if (dstIndex == -1)
|
||||
{
|
||||
// ありえない。バグ
|
||||
|
|
@ -539,45 +470,20 @@ namespace VRM
|
|||
dstRenderer.sharedMaterials = srcRenderer.sharedMaterials;
|
||||
}
|
||||
|
||||
public struct NormalizedResult
|
||||
{
|
||||
public GameObject Root;
|
||||
public Dictionary<Transform, Transform> BoneMap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// モデルの正規化を実行する
|
||||
/// 回転とスケールを除去したヒエラルキーのコピーを作成する(MeshをBakeする)
|
||||
/// </summary>
|
||||
/// <param name="go">対象モデルのルート</param>
|
||||
/// <param name="forceTPose">強制的にT-Pose化するか</param>
|
||||
/// <returns>正規化済みのモデル</returns>
|
||||
public static GameObject Execute(GameObject go, bool forceTPose, bool clearBlendShapeBeforeNormalize)
|
||||
/// <param name="go">対象のヒエラルキーのルート</param>
|
||||
/// <param name="clearBlendShapeBeforeNormalize">BlendShapeを0クリアするか否か。false の場合 BlendShape の現状を Bake する</param>
|
||||
/// <param name="createAvatar">Avatarを作る関数</param>
|
||||
/// <returns></returns>
|
||||
public static (GameObject, Dictionary<Transform, Transform>) Execute(GameObject go,
|
||||
bool clearBlendShapeBeforeNormalize, CreateAvatarFunc createAvatar)
|
||||
{
|
||||
Dictionary<Transform, Transform> boneMap = new Dictionary<Transform, Transform>();
|
||||
|
||||
//
|
||||
// T-Poseにする
|
||||
//
|
||||
if (forceTPose)
|
||||
{
|
||||
var hips = go.GetComponent<Animator>().GetBoneTransform(HumanBodyBones.Hips);
|
||||
var hipsPosition = hips.position;
|
||||
var hipsRotation = hips.rotation;
|
||||
try
|
||||
{
|
||||
EnforceTPose(go);
|
||||
}
|
||||
finally
|
||||
{
|
||||
hips.position = hipsPosition; // restore hipsPosition
|
||||
hips.rotation = hipsRotation;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 正規化されたヒエラルキーを作る
|
||||
//
|
||||
var normalized = NormalizeHierarchy(go, boneMap);
|
||||
var (normalized, boneMap) = NormalizeHierarchy(go, createAvatar);
|
||||
|
||||
//
|
||||
// 各メッシュから回転・スケールを取り除いてBinding行列を再計算する
|
||||
|
|
@ -595,137 +501,7 @@ namespace VRM
|
|||
NormalizeNoneSkinnedMesh(src, dst);
|
||||
}
|
||||
|
||||
CopyVRMComponents(go, normalized, boneMap);
|
||||
|
||||
// return new NormalizedResult
|
||||
// {
|
||||
// Root = normalized,
|
||||
// BoneMap = boneMap
|
||||
// };
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// VRMを構成するコンポーネントをコピーする。
|
||||
/// </summary>
|
||||
/// <param name="go">コピー元</param>
|
||||
/// <param name="root">コピー先</param>
|
||||
/// <param name="map">コピー元とコピー先の対応関係</param>
|
||||
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.GetComponentsInChildren<VRMSpringBoneColliderGroup>())
|
||||
{
|
||||
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.GetComponentsInChildren<VRMSpringBone>())
|
||||
{
|
||||
// Copy VRMSpringBone
|
||||
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;
|
||||
if (src.m_center != null)
|
||||
{
|
||||
dst.m_center = map[src.m_center];
|
||||
}
|
||||
|
||||
dst.RootBones = src.RootBones.Select(x => map[x]).ToList();
|
||||
dst.m_hitRadius = src.m_hitRadius;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (normalized, boneMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
using System.Linq;
|
||||
|
||||
|
||||
namespace VRM
|
||||
namespace MeshUtility
|
||||
{
|
||||
public static class MeshExtensions
|
||||
{
|
||||
13
Assets/MeshUtility/Runtime/MeshIntegrationResult.cs
Normal file
13
Assets/MeshUtility/Runtime/MeshIntegrationResult.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeshUtility
|
||||
{
|
||||
[System.Serializable]
|
||||
public class MeshIntegrationResult
|
||||
{
|
||||
public List<SkinnedMeshRenderer> SourceSkinnedMeshRenderers = new List<SkinnedMeshRenderer>();
|
||||
public List<MeshRenderer> SourceMeshRenderers = new List<MeshRenderer>();
|
||||
public SkinnedMeshRenderer IntegratedRenderer;
|
||||
}
|
||||
}
|
||||
11
Assets/MeshUtility/Runtime/MeshIntegrationResult.cs.meta
Normal file
11
Assets/MeshUtility/Runtime/MeshIntegrationResult.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e1c66a21d479b3e4a92eedd622d27f4f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
namespace MeshUtility
|
||||
{
|
||||
public class MeshIntegrator
|
||||
{
|
||||
|
|
@ -1,108 +1,21 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
namespace MeshUtility
|
||||
{
|
||||
public static class MeshIntegratorUtility
|
||||
{
|
||||
[System.Serializable]
|
||||
public class MeshIntegrationResult
|
||||
{
|
||||
public List<SkinnedMeshRenderer> SourceSkinnedMeshRenderers = new List<SkinnedMeshRenderer>();
|
||||
public List<MeshRenderer> SourceMeshRenderers = new List<MeshRenderer>();
|
||||
public SkinnedMeshRenderer IntegratedRenderer;
|
||||
}
|
||||
|
||||
public static bool IntegrateRuntime(GameObject vrmRootObject)
|
||||
{
|
||||
if (vrmRootObject == null) return false;
|
||||
var proxy = vrmRootObject.GetComponent<VRMBlendShapeProxy>();
|
||||
if (proxy == null) return false;
|
||||
var avatar = proxy.BlendShapeAvatar;
|
||||
if (avatar == null) return false;
|
||||
var clips = avatar.Clips;
|
||||
|
||||
var results = Integrate(vrmRootObject, clips);
|
||||
if (results.Any(x => x.IntegratedRenderer == null)) return false;
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
foreach (var renderer in result.SourceSkinnedMeshRenderers)
|
||||
{
|
||||
Object.Destroy(renderer);
|
||||
}
|
||||
|
||||
foreach (var renderer in result.SourceMeshRenderers)
|
||||
{
|
||||
Object.Destroy(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<MeshIntegrationResult> Integrate(GameObject root, List<BlendShapeClip> blendshapeClips)
|
||||
{
|
||||
var result = new List<MeshIntegratorUtility.MeshIntegrationResult>();
|
||||
|
||||
var withoutBlendShape = IntegrateInternal(root, onlyBlendShapeRenderers: false);
|
||||
if (withoutBlendShape.IntegratedRenderer != null)
|
||||
{
|
||||
result.Add(withoutBlendShape);
|
||||
}
|
||||
|
||||
var onlyBlendShape = IntegrateInternal(root, onlyBlendShapeRenderers: true);
|
||||
if (onlyBlendShape.IntegratedRenderer != null)
|
||||
{
|
||||
result.Add(onlyBlendShape);
|
||||
FollowBlendshapeRendererChange(blendshapeClips, onlyBlendShape, root);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void FollowBlendshapeRendererChange(List<BlendShapeClip> clips, MeshIntegrationResult result, GameObject root)
|
||||
{
|
||||
if (clips == null || result == null || result.IntegratedRenderer == null || root == null) return;
|
||||
|
||||
var rendererDict = result.SourceSkinnedMeshRenderers
|
||||
.ToDictionary(x => x.transform.RelativePathFrom(root.transform), x => x);
|
||||
|
||||
var dstPath = result.IntegratedRenderer.transform.RelativePathFrom(root.transform);
|
||||
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
if (clip == null) continue;
|
||||
|
||||
for (var i = 0; i < clip.Values.Length; ++i)
|
||||
{
|
||||
var val = clip.Values[i];
|
||||
if (rendererDict.ContainsKey(val.RelativePath))
|
||||
{
|
||||
var srcRenderer = rendererDict[val.RelativePath];
|
||||
var name = srcRenderer.sharedMesh.GetBlendShapeName(val.Index);
|
||||
var newIndex = result.IntegratedRenderer.sharedMesh.GetBlendShapeIndex(name);
|
||||
|
||||
val.RelativePath = dstPath;
|
||||
val.Index = newIndex;
|
||||
}
|
||||
|
||||
clip.Values[i] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MeshIntegrationResult IntegrateInternal(GameObject go, bool onlyBlendShapeRenderers)
|
||||
/// <summary>
|
||||
/// go を root としたヒエラルキーから Renderer を集めて、統合された Mesh 作成する
|
||||
/// </summary>
|
||||
/// <param name="go"></param>
|
||||
/// <param name="onlyBlendShapeRenderers">BlendShapeを保持するSkinnedMeshRendererのみ/BlendShapeを保持しないSkinnedMeshRenderer + MeshRenderer</param>
|
||||
/// <returns></returns>
|
||||
public static MeshIntegrationResult Integrate(GameObject go, bool onlyBlendShapeRenderers)
|
||||
{
|
||||
var result = new MeshIntegrationResult();
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
#else
|
||||
return result;
|
||||
#endif
|
||||
|
||||
|
||||
var meshNode = new GameObject();
|
||||
if (onlyBlendShapeRenderers)
|
||||
{
|
||||
|
|
@ -115,7 +28,7 @@ namespace VRM
|
|||
meshNode.transform.SetParent(go.transform, false);
|
||||
|
||||
// レンダラから情報を集める
|
||||
var integrator = new MeshIntegrator();
|
||||
var integrator = new MeshUtility.MeshIntegrator();
|
||||
|
||||
if (onlyBlendShapeRenderers)
|
||||
{
|
||||
|
|
@ -145,12 +58,8 @@ namespace VRM
|
|||
|
||||
if (integrator.Positions.Count > ushort.MaxValue)
|
||||
{
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
Debug.LogFormat("exceed 65535 vertices: {0}", integrator.Positions.Count);
|
||||
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
#else
|
||||
throw new NotImplementedException(String.Format("exceed 65535 vertices: {0}", integrator.Positions.Count.ToString()));
|
||||
#endif
|
||||
}
|
||||
|
||||
mesh.vertices = integrator.Positions.ToArray();
|
||||
|
|
@ -175,10 +84,10 @@ namespace VRM
|
|||
integrated.sharedMaterials = integrator.SubMeshes.Select(x => x.Material).ToArray();
|
||||
integrated.bones = integrator.Bones.ToArray();
|
||||
result.IntegratedRenderer = integrated;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<SkinnedMeshRenderer> EnumerateSkinnedMeshRenderer(Transform root, bool hasBlendShape)
|
||||
{
|
||||
foreach (var x in Traverse(root))
|
||||
|
|
@ -201,7 +110,7 @@ namespace VRM
|
|||
{
|
||||
var renderer = x.GetComponent<MeshRenderer>();
|
||||
var filter = x.GetComponent<MeshFilter>();
|
||||
|
||||
|
||||
if (renderer != null &&
|
||||
filter != null &&
|
||||
renderer.gameObject.activeInHierarchy &&
|
||||
3
Assets/MeshUtility/Runtime/MeshUtility.asmdef
Normal file
3
Assets/MeshUtility/Runtime/MeshUtility.asmdef
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"name": "MeshUtility"
|
||||
}
|
||||
7
Assets/MeshUtility/Runtime/MeshUtility.asmdef.meta
Normal file
7
Assets/MeshUtility/Runtime/MeshUtility.asmdef.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 71ab1919192903d44971eedbc26b24d1
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
319
Assets/MeshUtility/Runtime/UnityExtensions.cs
Normal file
319
Assets/MeshUtility/Runtime/UnityExtensions.cs
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
|
||||
namespace MeshUtility
|
||||
{
|
||||
public struct PosRot
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
|
||||
public static PosRot FromGlobalTransform(Transform t)
|
||||
{
|
||||
return new PosRot
|
||||
{
|
||||
Position = t.position,
|
||||
Rotation = t.rotation,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class BlendShape
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public BlendShape(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public List<Vector3> Positions = new List<Vector3>();
|
||||
public List<Vector3> Normals = new List<Vector3>();
|
||||
public List<Vector3> Tangents = new List<Vector3>();
|
||||
}
|
||||
|
||||
public static class UnityExtensions
|
||||
{
|
||||
public static Vector4 ReverseZ(this Vector4 v)
|
||||
{
|
||||
return new Vector4(v.x, v.y, -v.z, v.w);
|
||||
}
|
||||
|
||||
public static Vector3 ReverseZ(this Vector3 v)
|
||||
{
|
||||
return new Vector3(v.x, v.y, -v.z);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static Vector2 ReverseY(this Vector2 v)
|
||||
{
|
||||
return new Vector2(v.x, -v.y);
|
||||
}
|
||||
|
||||
public static Vector2 ReverseUV(this Vector2 v)
|
||||
{
|
||||
return new Vector2(v.x, 1.0f - v.y);
|
||||
}
|
||||
|
||||
public static Quaternion ReverseZ(this Quaternion q)
|
||||
{
|
||||
float angle;
|
||||
Vector3 axis;
|
||||
q.ToAngleAxis(out angle, out axis);
|
||||
return Quaternion.AngleAxis(-angle, ReverseZ(axis));
|
||||
}
|
||||
|
||||
public static Matrix4x4 Matrix4x4FromColumns(Vector4 c0, Vector4 c1, Vector4 c2, Vector4 c3)
|
||||
{
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
return new Matrix4x4(c0, c1, c2, c3);
|
||||
#else
|
||||
var m = default(Matrix4x4);
|
||||
m.SetColumn(0, c0);
|
||||
m.SetColumn(1, c1);
|
||||
m.SetColumn(2, c2);
|
||||
m.SetColumn(3, c3);
|
||||
return m;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Matrix4x4 Matrix4x4FromRotation(Quaternion q)
|
||||
{
|
||||
#if UNITY_2017_1_OR_NEWER
|
||||
return Matrix4x4.Rotate(q);
|
||||
#else
|
||||
var m = default(Matrix4x4);
|
||||
m.SetTRS(Vector3.zero, q, Vector3.one);
|
||||
return m;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Matrix4x4 ReverseZ(this Matrix4x4 m)
|
||||
{
|
||||
m.SetTRS(m.ExtractPosition().ReverseZ(), m.ExtractRotation().ReverseZ(), m.ExtractScale());
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Matrix4x4 MatrixFromArray(float[] values)
|
||||
{
|
||||
var m = new Matrix4x4();
|
||||
m.m00 = values[0];
|
||||
m.m10 = values[1];
|
||||
m.m20 = values[2];
|
||||
m.m30 = values[3];
|
||||
m.m01 = values[4];
|
||||
m.m11 = values[5];
|
||||
m.m21 = values[6];
|
||||
m.m31 = values[7];
|
||||
m.m02 = values[8];
|
||||
m.m12 = values[9];
|
||||
m.m22 = values[10];
|
||||
m.m32 = values[11];
|
||||
m.m03 = values[12];
|
||||
m.m13 = values[13];
|
||||
m.m23 = values[14];
|
||||
m.m33 = values[15];
|
||||
return m;
|
||||
}
|
||||
|
||||
// https://forum.unity.com/threads/how-to-assign-matrix4x4-to-transform.121966/
|
||||
public static Quaternion ExtractRotation(this Matrix4x4 matrix)
|
||||
{
|
||||
Vector3 forward;
|
||||
forward.x = matrix.m02;
|
||||
forward.y = matrix.m12;
|
||||
forward.z = matrix.m22;
|
||||
|
||||
Vector3 upwards;
|
||||
upwards.x = matrix.m01;
|
||||
upwards.y = matrix.m11;
|
||||
upwards.z = matrix.m21;
|
||||
|
||||
return Quaternion.LookRotation(forward, upwards);
|
||||
}
|
||||
|
||||
public static Vector3 ExtractPosition(this Matrix4x4 matrix)
|
||||
{
|
||||
Vector3 position;
|
||||
position.x = matrix.m03;
|
||||
position.y = matrix.m13;
|
||||
position.z = matrix.m23;
|
||||
return position;
|
||||
}
|
||||
|
||||
public static Vector3 ExtractScale(this Matrix4x4 matrix)
|
||||
{
|
||||
Vector3 scale;
|
||||
scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
|
||||
scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
|
||||
scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
|
||||
return scale;
|
||||
}
|
||||
|
||||
public static string RelativePathFrom(this Transform self, Transform root)
|
||||
{
|
||||
var path = new List<String>();
|
||||
for (var current = self; current != null; current = current.parent)
|
||||
{
|
||||
if (current == root)
|
||||
{
|
||||
return String.Join("/", path.ToArray());
|
||||
}
|
||||
|
||||
path.Insert(0, current.name);
|
||||
}
|
||||
|
||||
throw new Exception("no RelativePath");
|
||||
}
|
||||
|
||||
public static Transform GetChildByName(this Transform self, string childName)
|
||||
{
|
||||
foreach (Transform child in self)
|
||||
{
|
||||
if (child.name == childName)
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
|
||||
public static Transform GetFromPath(this Transform self, string path)
|
||||
{
|
||||
var current = self;
|
||||
|
||||
var split = path.Split('/');
|
||||
|
||||
foreach (var childName in split)
|
||||
{
|
||||
current = current.GetChildByName(childName);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
public static IEnumerable<Transform> GetChildren(this Transform self)
|
||||
{
|
||||
foreach (Transform child in self)
|
||||
{
|
||||
yield return child;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Transform> Traverse(this Transform t)
|
||||
{
|
||||
yield return t;
|
||||
foreach (Transform x in t)
|
||||
{
|
||||
foreach (Transform y in x.Traverse())
|
||||
{
|
||||
yield return y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use FindDescendant(name)")]
|
||||
public static Transform FindDescenedant(this Transform t, string name)
|
||||
{
|
||||
return FindDescendant(t, name);
|
||||
}
|
||||
|
||||
public static Transform FindDescendant(this Transform t, string name)
|
||||
{
|
||||
return t.Traverse().First(x => x.name == name);
|
||||
}
|
||||
|
||||
public static IEnumerable<Transform> Ancestors(this Transform t)
|
||||
{
|
||||
yield return t;
|
||||
if (t.parent != null)
|
||||
{
|
||||
foreach (Transform x in t.parent.Ancestors())
|
||||
{
|
||||
yield return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static float[] ToArray(this Quaternion q)
|
||||
{
|
||||
return new float[] { q.x, q.y, q.z, q.w };
|
||||
}
|
||||
|
||||
public static float[] ToArray(this Vector3 v)
|
||||
{
|
||||
return new float[] { v.x, v.y, v.z };
|
||||
}
|
||||
|
||||
public static float[] ToArray(this Vector4 v)
|
||||
{
|
||||
return new float[] { v.x, v.y, v.z, v.w };
|
||||
}
|
||||
|
||||
public static float[] ToArray(this Color c)
|
||||
{
|
||||
return new float[] { c.r, c.g, c.b, c.a };
|
||||
}
|
||||
|
||||
public static void ReverseZRecursive(this Transform root)
|
||||
{
|
||||
var globalMap = root.Traverse().ToDictionary(x => x, x => PosRot.FromGlobalTransform(x));
|
||||
|
||||
foreach (var x in root.Traverse())
|
||||
{
|
||||
x.position = globalMap[x].Position.ReverseZ();
|
||||
x.rotation = globalMap[x].Rotation.ReverseZ();
|
||||
}
|
||||
}
|
||||
|
||||
public static Mesh GetSharedMesh(this Transform t)
|
||||
{
|
||||
var meshFilter = t.GetComponent<MeshFilter>();
|
||||
if (meshFilter != null)
|
||||
{
|
||||
return meshFilter.sharedMesh;
|
||||
}
|
||||
|
||||
var skinnedMeshRenderer = t.GetComponent<SkinnedMeshRenderer>();
|
||||
if (skinnedMeshRenderer != null)
|
||||
{
|
||||
return skinnedMeshRenderer.sharedMesh;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Material[] GetSharedMaterials(this Transform t)
|
||||
{
|
||||
var renderer = t.GetComponent<Renderer>();
|
||||
if (renderer != null)
|
||||
{
|
||||
return renderer.sharedMaterials;
|
||||
}
|
||||
|
||||
return new Material[] { };
|
||||
}
|
||||
|
||||
public static bool Has<T>(this Transform transform, T t) where T : Component
|
||||
{
|
||||
return transform.GetComponent<T>() == t;
|
||||
}
|
||||
|
||||
public static T GetOrAddComponent<T>(this GameObject go) where T : Component
|
||||
{
|
||||
var c = go.GetComponent<T>();
|
||||
if (c != null)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
return go.AddComponent<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/MeshUtility/Runtime/UnityExtensions.cs.meta
Normal file
11
Assets/MeshUtility/Runtime/UnityExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5294813527b3278458026afc820dd63d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
{
|
||||
"name" : "com.vrmc.meshutility",
|
||||
"displayName" : "MeshUtility",
|
||||
"version" : "0.0.1",
|
||||
"unity" : "2018.4",
|
||||
"description" : "MeshUtility is a package for mesh separation, etc. \n\nCheck out the latest information here: <https://github.com/vrm-c/UniVRM/tree/master/Assets/MeshUtility>",
|
||||
"keywords" : [ "mesh" ],
|
||||
"author" : { "name": "VRM Consortium" }
|
||||
}
|
||||
{
|
||||
"name": "com.vrmc.meshutility",
|
||||
"version": "0.59.0",
|
||||
"displayName": "MeshUtility",
|
||||
"unity": "2018.4",
|
||||
"description": "MeshUtility is a package for mesh separation, etc. \n\nCheck out the latest information here: <https://github.com/vrm-c/UniVRM/tree/master/Assets/MeshUtility>",
|
||||
"keywords": [
|
||||
"mesh"
|
||||
],
|
||||
"author": {
|
||||
"name": "VRM Consortium"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
"VRM",
|
||||
"VRM.Samples",
|
||||
"UniVRM.Editor.Tests",
|
||||
"UniJSON"
|
||||
"UniJSON",
|
||||
"MeshUtility"
|
||||
],
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
|
|
@ -13,5 +14,9 @@
|
|||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ using System.IO;
|
|||
using UniGLTF;
|
||||
using UniJSON;
|
||||
using UnityEngine;
|
||||
|
||||
using MeshUtility;
|
||||
|
||||
namespace VRM.Samples
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5294813527b3278458026afc820dd63d
|
||||
timeCreated: 1515606586
|
||||
licenseType: Free
|
||||
guid: bbcda9130f35803408b216dbc6be05b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
|
|||
|
|
@ -131,9 +131,18 @@ namespace VRM.DevOnly.PackageExporter
|
|||
}
|
||||
|
||||
[MenuItem(VRMVersion.MENU + "/Export unitypackage")]
|
||||
static void CreateUnityPackageWithoutBuild()
|
||||
{
|
||||
var folder = GetProjectRoot();
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
CreateUnityPackages(folder);
|
||||
}
|
||||
|
||||
public static void CreateUnityPackageWithBuild()
|
||||
{
|
||||
//var folder = GetDesktop();
|
||||
var folder = GetProjectRoot();
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
|
|
@ -194,6 +203,7 @@ namespace VRM.DevOnly.PackageExporter
|
|||
List = new []{
|
||||
new GlobList("Assets/VRM"),
|
||||
new GlobList("Assets/VRMShaders"),
|
||||
new GlobList("Assets/MeshUtility"),
|
||||
}
|
||||
},
|
||||
new PackageInfo("UniVRM-samples")
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ namespace VRM
|
|||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
var copyMesh = mesh.Copy(copyBlendShape: false);
|
||||
var copyMesh = MeshUtility.MeshExtensions.Copy(mesh, copyBlendShape: false);
|
||||
// 使われている BlendShape だけをコピーする
|
||||
foreach (var i in usedBlendshapeIndexArray)
|
||||
{
|
||||
|
|
@ -182,7 +182,7 @@ namespace VRM
|
|||
if (settings.PoseFreeze)
|
||||
{
|
||||
// BoneNormalizer.Execute は Copy を作って正規化する。UNDO無用
|
||||
target = BoneNormalizer.Execute(target, settings.ForceTPose, false);
|
||||
target = VRMBoneNormalizer.Execute(target, settings.ForceTPose, false);
|
||||
destroy.Add(target);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ namespace VRM
|
|||
|
||||
static bool EnableRenderer(Renderer renderer)
|
||||
{
|
||||
if (renderer.transform.Ancestor().Any(x => !x.gameObject.activeSelf))
|
||||
if (renderer.transform.GetComponentsInParent<Transform>().Any(x => !x.gameObject.activeSelf))
|
||||
{
|
||||
// 自分か祖先に !activeSelf がいる
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace VRM
|
|||
var go = Selection.activeObject as GameObject;
|
||||
|
||||
// BoneNormalizer.Execute はコピーを正規化する。UNDO無用
|
||||
Selection.activeGameObject = BoneNormalizer.Execute(go, true, false);
|
||||
Selection.activeGameObject = VRMBoneNormalizer.Execute(go, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,24 @@ namespace VRM
|
|||
}}
|
||||
}}
|
||||
";
|
||||
|
||||
const string MeshUtilityPath = "Assets/MeshUtility/package.json";
|
||||
const string MeshUtilityTemplate = @"{{
|
||||
{{
|
||||
""name"": ""com.vrmc.meshutility"",
|
||||
""version"": ""{0}.{1}.{2}"",
|
||||
""displayName"": ""MeshUtility"",
|
||||
""unity"": ""2018.4"",
|
||||
""description"": ""MeshUtility is a package for mesh separation, etc. \n\nCheck out the latest information here: <https://github.com/vrm-c/UniVRM/tree/master/Assets/MeshUtility>"",
|
||||
""keywords"": [
|
||||
""mesh""
|
||||
],
|
||||
""author"": {{
|
||||
""name"": ""VRM Consortium""
|
||||
}}
|
||||
}}
|
||||
";
|
||||
|
||||
const string VRMPackagePath = "Assets/VRM/package.json";
|
||||
const string VRMPackageTemplate = @"{{
|
||||
""name"": ""com.vrmc.univrm"",
|
||||
|
|
@ -54,7 +72,8 @@ namespace VRM
|
|||
""name"": ""VRM Consortium""
|
||||
}},
|
||||
""dependencies"": {{
|
||||
""com.vrmc.vrmshaders"": ""{0}.{1}.{2}""
|
||||
""com.vrmc.vrmshaders"": ""{0}.{1}.{2}"",
|
||||
""com.vrmc.meshutility"": ""{0}.{1}.{2}""
|
||||
}}
|
||||
}}
|
||||
";
|
||||
|
|
@ -96,6 +115,10 @@ namespace VRM
|
|||
values[0],
|
||||
values[1],
|
||||
values[2]), utf8);
|
||||
File.WriteAllText(MeshUtilityPath, string.Format(MeshUtilityTemplate,
|
||||
values[0],
|
||||
values[1],
|
||||
values[2]), utf8);
|
||||
File.WriteAllText(VRMPackagePath, string.Format(VRMPackageTemplate,
|
||||
values[0],
|
||||
values[1],
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ using Object = UnityEngine.Object;
|
|||
namespace VRM
|
||||
{
|
||||
/// <summary>
|
||||
/// 複数のメッシュをまとめる
|
||||
/// 複数のメッシュをまとめて、BlendShapeClipに変更を反映する
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public static class MeshIntegratorEditor
|
||||
|
|
@ -39,12 +39,12 @@ namespace VRM
|
|||
private static void Foo()
|
||||
{
|
||||
var go = Selection.activeObject as GameObject;
|
||||
|
||||
|
||||
Debug.Log(SkinnedMeshUtility.IsPrefab(go));
|
||||
}
|
||||
|
||||
|
||||
public static List<MeshIntegratorUtility.MeshIntegrationResult> Integrate(GameObject prefab)
|
||||
public static List<MeshUtility.MeshIntegrationResult> Integrate(GameObject prefab)
|
||||
{
|
||||
Undo.RecordObject(prefab, "Mesh Integration");
|
||||
var instance = SkinnedMeshUtility.InstantiatePrefab(prefab);
|
||||
|
|
@ -63,18 +63,18 @@ namespace VRM
|
|||
|
||||
// Backup Exists
|
||||
BackupVrmPrefab(prefab);
|
||||
|
||||
|
||||
// Execute
|
||||
var results = MeshIntegratorUtility.Integrate(instance, clips);
|
||||
var results = VRMMeshIntegratorUtility.Integrate(instance, clips);
|
||||
|
||||
foreach (var res in results)
|
||||
{
|
||||
if (res.IntegratedRenderer == null) continue;
|
||||
|
||||
|
||||
SaveMeshAsset(res.IntegratedRenderer.sharedMesh, instance, res.IntegratedRenderer.gameObject.name);
|
||||
Undo.RegisterCreatedObjectUndo(res.IntegratedRenderer.gameObject, "Integrate Renderers");
|
||||
}
|
||||
|
||||
|
||||
// destroy source renderers
|
||||
foreach (var res in results)
|
||||
{
|
||||
|
|
@ -90,31 +90,31 @@ namespace VRM
|
|||
renderer.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Apply to Prefab
|
||||
SkinnedMeshUtility.ApplyChangesToPrefab(instance);
|
||||
Object.DestroyImmediate(instance);
|
||||
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void BackupVrmPrefab(GameObject rootPrefab)
|
||||
{
|
||||
var proxy = rootPrefab.GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
var srcAvatar = proxy.BlendShapeAvatar;
|
||||
var dstAvatar = (BlendShapeAvatar) BackupAsset(srcAvatar, rootPrefab);
|
||||
|
||||
var clipMapper = srcAvatar.Clips.ToDictionary(x => x, x => (BlendShapeClip) BackupAsset(x, rootPrefab));
|
||||
var srcAvatar = proxy.BlendShapeAvatar;
|
||||
var dstAvatar = (BlendShapeAvatar)BackupAsset(srcAvatar, rootPrefab);
|
||||
|
||||
var clipMapper = srcAvatar.Clips.ToDictionary(x => x, x => (BlendShapeClip)BackupAsset(x, rootPrefab));
|
||||
dstAvatar.Clips = clipMapper.Values.ToList();
|
||||
|
||||
|
||||
var dstPrefab = BackupAsset(rootPrefab, rootPrefab);
|
||||
var dstInstance = SkinnedMeshUtility.InstantiatePrefab(dstPrefab);
|
||||
dstInstance.GetComponent<VRMBlendShapeProxy>().BlendShapeAvatar = dstAvatar;
|
||||
SkinnedMeshUtility.ApplyChangesToPrefab(dstInstance);
|
||||
Object.DestroyImmediate(dstInstance);
|
||||
}
|
||||
|
||||
|
||||
private static T BackupAsset<T>(T asset, GameObject rootPrefab) where T : UnityEngine.Object
|
||||
{
|
||||
var srcAssetPath = UnityPath.FromAsset(asset);
|
||||
|
|
@ -124,7 +124,7 @@ namespace VRM
|
|||
var backupPath = UnityPath.FromAsset(rootPrefab).Parent.Child(backupDir);
|
||||
backupPath.EnsureFolder();
|
||||
var dstAssetPath = backupPath.Child(assetName);
|
||||
|
||||
|
||||
AssetDatabase.CopyAsset(srcAssetPath.Value, dstAssetPath.Value);
|
||||
return dstAssetPath.LoadAsset<T>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ namespace VRM
|
|||
MaterialList[] m_duplicateMaterials;
|
||||
|
||||
[Header("Result")]
|
||||
public MeshIntegratorUtility.MeshIntegrationResult[] integrationResults;
|
||||
public MeshUtility.MeshIntegrationResult[] integrationResults;
|
||||
|
||||
[MenuItem(MENU_KEY)]
|
||||
static void CreateWizard()
|
||||
|
|
@ -124,7 +124,7 @@ namespace VRM
|
|||
return;
|
||||
}
|
||||
|
||||
m_uniqueMaterials = MeshIntegratorUtility.EnumerateSkinnedMeshRenderer(m_root.transform, false)
|
||||
m_uniqueMaterials = MeshUtility.MeshIntegratorUtility.EnumerateSkinnedMeshRenderer(m_root.transform, false)
|
||||
.SelectMany(x => x.sharedMaterials)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace VRM
|
|||
var src = new Mesh();
|
||||
src.AddBlendShapeFrame("blendShape", 100.0f, null, null, null);
|
||||
|
||||
var dst = src.Copy(true);
|
||||
var dst = MeshUtility.MeshExtensions.Copy(src, true);
|
||||
|
||||
MeshEquals(src, dst);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ namespace VRM
|
|||
map.Add(null, new GameObject("null"));
|
||||
// map.Add(new GameObject("c"), null); // ありえないので Exception にしてある
|
||||
var boneWeights = map.CreateBoneWeight(64).ToArray();
|
||||
var newBoneWeight = BoneNormalizer.MapBoneWeight(boneWeights, map.Map,
|
||||
var newBoneWeight = MeshUtility.BoneNormalizer.MapBoneWeight(boneWeights, map.Map,
|
||||
map.SrcBones.ToArray(), map.DstBones.ToArray());
|
||||
|
||||
// 正常系
|
||||
|
|
@ -74,7 +74,7 @@ namespace VRM
|
|||
map.Add(null, new GameObject("null"));
|
||||
// map.Add(new GameObject("c"), null); // ありえないので Exception にしてある
|
||||
var boneWeights = map.CreateBoneWeight(64).ToArray();
|
||||
var newBoneWeight = BoneNormalizer.MapBoneWeight(boneWeights, map.Map,
|
||||
var newBoneWeight = MeshUtility.BoneNormalizer.MapBoneWeight(boneWeights, map.Map,
|
||||
map.SrcBones.ToArray(), map.DstBones.ToArray());
|
||||
|
||||
// 4 つめが 0 になる
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
"references": [
|
||||
"VRM",
|
||||
"UniJSON",
|
||||
"UniVRM.Editor"
|
||||
"UniVRM.Editor",
|
||||
"MeshUtility"
|
||||
],
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
"references": [
|
||||
"VRM",
|
||||
"UniJSON",
|
||||
"UniHumanoid"
|
||||
"UniHumanoid",
|
||||
"MeshUtility"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ namespace VRM
|
|||
var eraseBones = bones.Select((x, i) =>
|
||||
{
|
||||
// 祖先に削除対象が存在するか
|
||||
bool erase = x.Ancestor().Any(y => y == eraseRoot);
|
||||
bool erase = x.GetComponentsInParent<Transform>().Any(y => y == eraseRoot);
|
||||
return new
|
||||
{
|
||||
i,
|
||||
|
|
@ -229,7 +229,7 @@ namespace VRM
|
|||
renderer.gameObject.layer = THIRDPERSON_ONLY_LAYER;
|
||||
|
||||
// 削除対象のボーンに対するウェイトを保持する三角形を除外して、一人称用のモデルを複製する
|
||||
var headlessMesh = BoneMeshEraser.CreateErasedMesh(renderer.sharedMesh, eraseBones);
|
||||
var headlessMesh = MeshUtility.BoneMeshEraser.CreateErasedMesh(renderer.sharedMesh, eraseBones);
|
||||
if (headlessMesh.triangles.Length == 0)
|
||||
{
|
||||
// 一人称用のmeshには描画すべき部分が無い(全部削除された)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ namespace VRM
|
|||
public static partial class VRMVersion
|
||||
{
|
||||
public const int MAJOR = 0;
|
||||
public const int MINOR = 58;
|
||||
public const int PATCH = 1;
|
||||
public const string VERSION = "0.58.1";
|
||||
public const int MINOR = 59;
|
||||
public const int PATCH = 0;
|
||||
public const string VERSION = "0.59.0";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,238 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniHumanoid;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public static class VRMBoneNormalizer
|
||||
{
|
||||
static void EnforceTPose(GameObject go)
|
||||
{
|
||||
var animator = go.GetComponent<Animator>();
|
||||
if (animator == null)
|
||||
{
|
||||
throw new ArgumentException("Animator with avatar is required");
|
||||
}
|
||||
|
||||
var avatar = animator.avatar;
|
||||
if (avatar == null)
|
||||
{
|
||||
throw new ArgumentException("avatar is required");
|
||||
}
|
||||
|
||||
if (!avatar.isValid)
|
||||
{
|
||||
throw new ArgumentException("invalid avatar");
|
||||
}
|
||||
|
||||
if (!avatar.isHuman)
|
||||
{
|
||||
throw new ArgumentException("avatar is not human");
|
||||
}
|
||||
|
||||
HumanPoseTransfer.SetTPose(avatar, go.transform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// モデルの正規化を実行する
|
||||
/// </summary>
|
||||
/// <param name="go">対象モデルのルート</param>
|
||||
/// <param name="forceTPose">強制的にT-Pose化するか</param>
|
||||
/// <returns>正規化済みのモデル</returns>
|
||||
public static GameObject Execute(GameObject go, bool forceTPose, bool clearBlendShapeBeforeNormalize)
|
||||
{
|
||||
//
|
||||
// T-Poseにする
|
||||
//
|
||||
if (forceTPose)
|
||||
{
|
||||
var hips = go.GetComponent<Animator>().GetBoneTransform(HumanBodyBones.Hips);
|
||||
var hipsPosition = hips.position;
|
||||
var hipsRotation = hips.rotation;
|
||||
try
|
||||
{
|
||||
EnforceTPose(go);
|
||||
}
|
||||
finally
|
||||
{
|
||||
hips.position = hipsPosition; // restore hipsPosition
|
||||
hips.rotation = hipsRotation;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 正規化されたヒエラルキーを作る
|
||||
//
|
||||
var (normalized, bMap) = MeshUtility.BoneNormalizer.Execute(go, clearBlendShapeBeforeNormalize, (_src, dst, boneMap) =>
|
||||
{
|
||||
var src = _src.GetComponent<Animator>();
|
||||
|
||||
var srcHumanBones = Enum.GetValues(typeof(HumanBodyBones))
|
||||
.Cast<HumanBodyBones>()
|
||||
.Where(x => x != HumanBodyBones.LastBone)
|
||||
.Select(x => new { Key = x, Value = src.GetBoneTransform(x) })
|
||||
.Where(x => x.Value != null)
|
||||
;
|
||||
|
||||
var map =
|
||||
srcHumanBones
|
||||
.Where(x => boneMap.ContainsKey(x.Value))
|
||||
.ToDictionary(x => x.Key, x => boneMap[x.Value])
|
||||
;
|
||||
|
||||
var animator = dst.AddComponent<Animator>();
|
||||
var vrmHuman = go.GetComponent<VRMHumanoidDescription>();
|
||||
var avatarDescription = AvatarDescription.Create();
|
||||
if (vrmHuman != null && vrmHuman.Description != null)
|
||||
{
|
||||
avatarDescription.armStretch = vrmHuman.Description.armStretch;
|
||||
avatarDescription.legStretch = vrmHuman.Description.legStretch;
|
||||
avatarDescription.upperArmTwist = vrmHuman.Description.upperArmTwist;
|
||||
avatarDescription.lowerArmTwist = vrmHuman.Description.lowerArmTwist;
|
||||
avatarDescription.upperLegTwist = vrmHuman.Description.upperLegTwist;
|
||||
avatarDescription.lowerLegTwist = vrmHuman.Description.lowerLegTwist;
|
||||
avatarDescription.feetSpacing = vrmHuman.Description.feetSpacing;
|
||||
avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF;
|
||||
}
|
||||
avatarDescription.SetHumanBones(map);
|
||||
var avatar = avatarDescription.CreateAvatar(dst.transform);
|
||||
return avatar;
|
||||
});
|
||||
|
||||
// humanPoseTransfer
|
||||
// var animator = normalized.GetComponent<Animator>();
|
||||
// var humanPoseTransfer = normalized.AddComponent<HumanPoseTransfer>();
|
||||
// humanPoseTransfer.Avatar = animator.avatar;
|
||||
|
||||
CopyVRMComponents(go, normalized, bMap);
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// VRMを構成するコンポーネントをコピーする。
|
||||
/// </summary>
|
||||
/// <param name="go">コピー元</param>
|
||||
/// <param name="root">コピー先</param>
|
||||
/// <param name="map">コピー元とコピー先の対応関係</param>
|
||||
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.GetComponentsInChildren<VRMSpringBoneColliderGroup>())
|
||||
{
|
||||
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.GetComponentsInChildren<VRMSpringBone>())
|
||||
{
|
||||
// Copy VRMSpringBone
|
||||
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;
|
||||
if (src.m_center != null)
|
||||
{
|
||||
dst.m_center = map[src.m_center];
|
||||
}
|
||||
|
||||
dst.RootBones = src.RootBones.Select(x => map[x]).ToList();
|
||||
dst.m_hitRadius = src.m_hitRadius;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7c0aa7232aa52244aa546dcabf17fadd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
/// <summary>
|
||||
/// Meshを統合し、統合後のMeshのBlendShapeの変化をVRMのBlendShapeClipに反映する
|
||||
/// </summary>
|
||||
public static class VRMMeshIntegratorUtility
|
||||
{
|
||||
public static bool IntegrateRuntime(GameObject vrmRootObject)
|
||||
{
|
||||
if (vrmRootObject == null) return false;
|
||||
var proxy = vrmRootObject.GetComponent<VRMBlendShapeProxy>();
|
||||
if (proxy == null) return false;
|
||||
var avatar = proxy.BlendShapeAvatar;
|
||||
if (avatar == null) return false;
|
||||
var clips = avatar.Clips;
|
||||
|
||||
var results = Integrate(vrmRootObject, clips);
|
||||
if (results.Any(x => x.IntegratedRenderer == null)) return false;
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
foreach (var renderer in result.SourceSkinnedMeshRenderers)
|
||||
{
|
||||
Object.Destroy(renderer);
|
||||
}
|
||||
|
||||
foreach (var renderer in result.SourceMeshRenderers)
|
||||
{
|
||||
Object.Destroy(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<MeshUtility.MeshIntegrationResult> Integrate(GameObject root, List<BlendShapeClip> blendshapeClips)
|
||||
{
|
||||
var result = new List<MeshUtility.MeshIntegrationResult>();
|
||||
|
||||
var withoutBlendShape = MeshUtility.MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: false);
|
||||
if (withoutBlendShape.IntegratedRenderer != null)
|
||||
{
|
||||
result.Add(withoutBlendShape);
|
||||
}
|
||||
|
||||
var onlyBlendShape = MeshUtility.MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: true);
|
||||
if (onlyBlendShape.IntegratedRenderer != null)
|
||||
{
|
||||
result.Add(onlyBlendShape);
|
||||
FollowBlendshapeRendererChange(blendshapeClips, onlyBlendShape, root);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void FollowBlendshapeRendererChange(List<BlendShapeClip> clips, MeshUtility.MeshIntegrationResult result, GameObject root)
|
||||
{
|
||||
if (clips == null || result == null || result.IntegratedRenderer == null || root == null) return;
|
||||
|
||||
var rendererDict = result.SourceSkinnedMeshRenderers
|
||||
.ToDictionary(x => x.transform.RelativePathFrom(root.transform), x => x);
|
||||
|
||||
var dstPath = result.IntegratedRenderer.transform.RelativePathFrom(root.transform);
|
||||
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
if (clip == null) continue;
|
||||
|
||||
for (var i = 0; i < clip.Values.Length; ++i)
|
||||
{
|
||||
var val = clip.Values[i];
|
||||
if (rendererDict.ContainsKey(val.RelativePath))
|
||||
{
|
||||
var srcRenderer = rendererDict[val.RelativePath];
|
||||
var name = srcRenderer.sharedMesh.GetBlendShapeName(val.Index);
|
||||
var newIndex = result.IntegratedRenderer.sharedMesh.GetBlendShapeIndex(name);
|
||||
|
||||
val.RelativePath = dstPath;
|
||||
val.Index = newIndex;
|
||||
}
|
||||
|
||||
clip.Values[i] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8eebeb093136b7f429c0e9e7295816b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -6,10 +6,15 @@
|
|||
"UniUnlit",
|
||||
"UniHumanoid",
|
||||
"UniJSON",
|
||||
"ShaderProperty.Runtime"
|
||||
"ShaderProperty.Runtime",
|
||||
"MeshUtility"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "com.vrmc.univrm",
|
||||
"version": "0.58.1",
|
||||
"version": "0.59.0",
|
||||
"displayName": "VRM",
|
||||
"description": "VRM importer",
|
||||
"unity": "2018.4",
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
"name": "VRM Consortium"
|
||||
},
|
||||
"dependencies": {
|
||||
"com.vrmc.vrmshaders": "0.58.1"
|
||||
"com.vrmc.vrmshaders": "0.59.0",
|
||||
"com.vrmc.meshutility": "0.59.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "com.vrmc.vrmshaders",
|
||||
"version": "0.58.1",
|
||||
"version": "0.59.0",
|
||||
"displayName": "VRM Shaders",
|
||||
"description": "VRM Shaders",
|
||||
"unity": "2018.4",
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
m_EditorVersion: 2018.4.23f1
|
||||
m_EditorVersion: 2018.4.25f1
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user