mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-08-24 03:25:41 -05:00
Merge pull request #1577 from ousttrue/fix/integrator_excludes
fix vrm mesh integration
This commit is contained in:
59
Assets/UniGLTF/Runtime/MeshUtility/MeshExclude.cs
Normal file
59
Assets/UniGLTF/Runtime/MeshUtility/MeshExclude.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF.MeshUtility
|
||||
{
|
||||
public class MeshExclude
|
||||
{
|
||||
List<Mesh> _excludes = new List<Mesh>();
|
||||
|
||||
public MeshExclude(IEnumerable<Mesh> excludes)
|
||||
{
|
||||
if (excludes != null)
|
||||
{
|
||||
_excludes.AddRange(excludes);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExcluded(SkinnedMeshRenderer smr)
|
||||
{
|
||||
if (smr == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (smr.sharedMesh == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (_excludes.Contains(smr.sharedMesh))
|
||||
{
|
||||
Debug.LogFormat("{0} has excluded", smr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsExcluded(MeshRenderer mr)
|
||||
{
|
||||
if (mr == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var filter = mr.GetComponent<MeshFilter>();
|
||||
if (filter == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (filter.sharedMesh == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (_excludes.Contains(filter.sharedMesh))
|
||||
{
|
||||
Debug.LogFormat("{0} has excluded", mr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Runtime/MeshUtility/MeshExclude.cs.meta
Normal file
11
Assets/UniGLTF/Runtime/MeshUtility/MeshExclude.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ff8f78a008cc074daa97b33c95a7b16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -6,6 +6,22 @@ namespace UniGLTF.MeshUtility
|
||||
{
|
||||
public static class MeshExtensions
|
||||
{
|
||||
public static Mesh GetMesh(this Renderer r)
|
||||
{
|
||||
if (r is SkinnedMeshRenderer smr)
|
||||
{
|
||||
return smr.sharedMesh;
|
||||
}
|
||||
if (r is MeshRenderer mr)
|
||||
{
|
||||
if (mr.GetComponent<MeshFilter>() is MeshFilter mf)
|
||||
{
|
||||
return mf.sharedMesh;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Mesh Copy(this Mesh src, bool copyBlendShape)
|
||||
{
|
||||
var dst = new Mesh();
|
||||
|
||||
@@ -4,10 +4,18 @@ using UnityEngine;
|
||||
namespace UniGLTF.MeshUtility
|
||||
{
|
||||
[System.Serializable]
|
||||
public class MeshMap
|
||||
{
|
||||
public List<Mesh> Sources = new List<Mesh>();
|
||||
public Mesh Integrated;
|
||||
}
|
||||
|
||||
public class MeshIntegrationResult
|
||||
{
|
||||
public List<SkinnedMeshRenderer> SourceSkinnedMeshRenderers = new List<SkinnedMeshRenderer>();
|
||||
public List<MeshRenderer> SourceMeshRenderers = new List<MeshRenderer>();
|
||||
public SkinnedMeshRenderer IntegratedRenderer;
|
||||
|
||||
public MeshMap MeshMap = new MeshMap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,16 @@ namespace UniGLTF.MeshUtility
|
||||
{
|
||||
public class MeshIntegrator
|
||||
{
|
||||
public struct SubMesh
|
||||
public const string INTEGRATED_MESH_NAME = "MeshesIntegrated";
|
||||
public const string INTEGRATED_MESH_BLENDSHAPE_NAME = "MeshesBlendShapeIntegrated";
|
||||
|
||||
struct SubMesh
|
||||
{
|
||||
public List<int> Indices;
|
||||
public Material Material;
|
||||
}
|
||||
|
||||
public class BlendShape
|
||||
class BlendShape
|
||||
{
|
||||
public int VertexOffset;
|
||||
public string Name;
|
||||
@@ -21,25 +24,18 @@ namespace UniGLTF.MeshUtility
|
||||
public Vector3[] Normals;
|
||||
public Vector3[] Tangents;
|
||||
}
|
||||
|
||||
// public List<SkinnedMeshRenderer> Renderers { get; private set; }
|
||||
public List<Vector3> Positions { get; private set; }
|
||||
public List<Vector3> Normals { get; private set; }
|
||||
public List<Vector2> UV { get; private set; }
|
||||
public List<Vector4> Tangents { get; private set; }
|
||||
public List<BoneWeight> BoneWeights { get; private set; }
|
||||
|
||||
public List<SubMesh> SubMeshes
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public List<Matrix4x4> BindPoses { get; private set; }
|
||||
public List<Transform> Bones { get; private set; }
|
||||
|
||||
public List<BlendShape> BlendShapes { get; private set; }
|
||||
public void AddBlendShapesToMesh(Mesh mesh)
|
||||
public MeshIntegrationResult Result { get; } = new MeshIntegrationResult();
|
||||
List<Vector3> Positions { get; } = new List<Vector3>();
|
||||
List<Vector3> Normals { get; } = new List<Vector3>();
|
||||
List<Vector2> UV { get; } = new List<Vector2>();
|
||||
List<Vector4> Tangents { get; } = new List<Vector4>();
|
||||
List<BoneWeight> BoneWeights { get; } = new List<BoneWeight>();
|
||||
List<SubMesh> SubMeshes { get; } = new List<SubMesh>();
|
||||
List<Matrix4x4> BindPoses { get; } = new List<Matrix4x4>();
|
||||
List<Transform> Bones { get; } = new List<Transform>();
|
||||
List<BlendShape> BlendShapes { get; } = new List<BlendShape>();
|
||||
void AddBlendShapesToMesh(Mesh mesh)
|
||||
{
|
||||
Dictionary<string, BlendShape> map = new Dictionary<string, BlendShape>();
|
||||
|
||||
@@ -74,24 +70,6 @@ namespace UniGLTF.MeshUtility
|
||||
}
|
||||
}
|
||||
|
||||
public MeshIntegrator()
|
||||
{
|
||||
// Renderers = new List<SkinnedMeshRenderer>();
|
||||
|
||||
Positions = new List<Vector3>();
|
||||
Normals = new List<Vector3>();
|
||||
UV = new List<Vector2>();
|
||||
Tangents = new List<Vector4>();
|
||||
BoneWeights = new List<BoneWeight>();
|
||||
|
||||
SubMeshes = new List<SubMesh>();
|
||||
|
||||
BindPoses = new List<Matrix4x4>();
|
||||
Bones = new List<Transform>();
|
||||
|
||||
BlendShapes = new List<BlendShape>();
|
||||
}
|
||||
|
||||
static BoneWeight AddBoneIndexOffset(BoneWeight bw, int boneIndexOffset)
|
||||
{
|
||||
if (bw.weight0 > 0) bw.boneIndex0 += boneIndexOffset;
|
||||
@@ -115,6 +93,8 @@ namespace UniGLTF.MeshUtility
|
||||
Debug.LogWarningFormat("{0} has no mesh", renderer.name);
|
||||
return;
|
||||
}
|
||||
Result.SourceMeshRenderers.Add(renderer);
|
||||
Result.MeshMap.Sources.Add(mesh);
|
||||
|
||||
var indexOffset = Positions.Count;
|
||||
var boneIndexOffset = Bones.Count;
|
||||
@@ -142,7 +122,7 @@ namespace UniGLTF.MeshUtility
|
||||
return;
|
||||
}
|
||||
var bindpose = bone.worldToLocalMatrix;
|
||||
|
||||
|
||||
BoneWeights.AddRange(Enumerable.Range(0, mesh.vertices.Length)
|
||||
.Select(x => new BoneWeight()
|
||||
{
|
||||
@@ -150,10 +130,10 @@ namespace UniGLTF.MeshUtility
|
||||
weight0 = 1,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
BindPoses.Add(bindpose);
|
||||
Bones.Add(bone);
|
||||
|
||||
|
||||
for (int i = 0; i < mesh.subMeshCount; ++i)
|
||||
{
|
||||
var indices = mesh.GetIndices(i).Select(x => x + indexOffset);
|
||||
@@ -182,8 +162,8 @@ namespace UniGLTF.MeshUtility
|
||||
Debug.LogWarningFormat("{0} has no mesh", renderer.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Renderers.Add(renderer);
|
||||
Result.SourceSkinnedMeshRenderers.Add(renderer);
|
||||
Result.MeshMap.Sources.Add(mesh);
|
||||
|
||||
var indexOffset = Positions.Count;
|
||||
var boneIndexOffset = Bones.Count;
|
||||
@@ -249,5 +229,55 @@ namespace UniGLTF.MeshUtility
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Intgrate(bool onlyBlendShapeRenderers)
|
||||
{
|
||||
var mesh = new Mesh();
|
||||
|
||||
if (Positions.Count > ushort.MaxValue)
|
||||
{
|
||||
Debug.LogFormat("exceed 65535 vertices: {0}", Positions.Count);
|
||||
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
}
|
||||
|
||||
mesh.vertices = Positions.ToArray();
|
||||
mesh.normals = Normals.ToArray();
|
||||
mesh.uv = UV.ToArray();
|
||||
mesh.tangents = Tangents.ToArray();
|
||||
mesh.boneWeights = BoneWeights.ToArray();
|
||||
mesh.subMeshCount = SubMeshes.Count;
|
||||
for (var i = 0; i < SubMeshes.Count; ++i)
|
||||
{
|
||||
mesh.SetIndices(SubMeshes[i].Indices.ToArray(), MeshTopology.Triangles, i);
|
||||
}
|
||||
mesh.bindposes = BindPoses.ToArray();
|
||||
|
||||
if (onlyBlendShapeRenderers)
|
||||
{
|
||||
AddBlendShapesToMesh(mesh);
|
||||
mesh.name = INTEGRATED_MESH_BLENDSHAPE_NAME;
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh.name = INTEGRATED_MESH_NAME;
|
||||
}
|
||||
|
||||
var meshNode = new GameObject();
|
||||
if (onlyBlendShapeRenderers)
|
||||
{
|
||||
meshNode.name = "MeshIntegrator(BlendShape)";
|
||||
}
|
||||
else
|
||||
{
|
||||
meshNode.name = "MeshIntegrator";
|
||||
}
|
||||
|
||||
var integrated = meshNode.AddComponent<SkinnedMeshRenderer>();
|
||||
integrated.sharedMesh = mesh;
|
||||
integrated.sharedMaterials = SubMeshes.Select(x => x.Material).ToArray();
|
||||
integrated.bones = Bones.ToArray();
|
||||
Result.IntegratedRenderer = integrated;
|
||||
Result.MeshMap.Integrated = mesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF.MeshUtility
|
||||
{
|
||||
public static class MeshIntegratorUtility
|
||||
{
|
||||
public const string INTEGRATED_MESH_NAME = "MeshesIntegrated";
|
||||
public const string INTEGRATED_MESH_BLENDSHAPE_NAME = "MeshesBlendShapeIntegrated";
|
||||
public static string INTEGRATED_MESH_NAME => MeshIntegrator.INTEGRATED_MESH_NAME;
|
||||
public static string INTEGRATED_MESH_BLENDSHAPE_NAME => MeshIntegrator.INTEGRATED_MESH_BLENDSHAPE_NAME;
|
||||
|
||||
/// <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)
|
||||
public static MeshIntegrationResult Integrate(GameObject go, bool onlyBlendShapeRenderers, IEnumerable<Mesh> excludes = null)
|
||||
{
|
||||
var result = new MeshIntegrationResult();
|
||||
var exclude = new MeshExclude(excludes);
|
||||
|
||||
var meshNode = new GameObject();
|
||||
if (onlyBlendShapeRenderers)
|
||||
{
|
||||
meshNode.name = "MeshIntegrator(BlendShape)";
|
||||
}
|
||||
else
|
||||
{
|
||||
meshNode.name = "MeshIntegrator";
|
||||
}
|
||||
meshNode.transform.SetParent(go.transform, false);
|
||||
|
||||
// レンダラから情報を集める
|
||||
var integrator = new MeshUtility.MeshIntegrator();
|
||||
|
||||
if (onlyBlendShapeRenderers)
|
||||
{
|
||||
foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, true))
|
||||
{
|
||||
if (exclude.IsExcluded(x))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
integrator.Push(x);
|
||||
result.SourceSkinnedMeshRenderers.Add(x);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, false))
|
||||
{
|
||||
if (exclude.IsExcluded(x))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
integrator.Push(x);
|
||||
result.SourceSkinnedMeshRenderers.Add(x);
|
||||
}
|
||||
|
||||
foreach (var x in EnumerateMeshRenderer(go.transform))
|
||||
{
|
||||
if (exclude.IsExcluded(x))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
integrator.Push(x);
|
||||
result.SourceMeshRenderers.Add(x);
|
||||
}
|
||||
}
|
||||
|
||||
var mesh = new Mesh();
|
||||
|
||||
if (integrator.Positions.Count > ushort.MaxValue)
|
||||
{
|
||||
Debug.LogFormat("exceed 65535 vertices: {0}", integrator.Positions.Count);
|
||||
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
}
|
||||
|
||||
mesh.vertices = integrator.Positions.ToArray();
|
||||
mesh.normals = integrator.Normals.ToArray();
|
||||
mesh.uv = integrator.UV.ToArray();
|
||||
mesh.tangents = integrator.Tangents.ToArray();
|
||||
mesh.boneWeights = integrator.BoneWeights.ToArray();
|
||||
mesh.subMeshCount = integrator.SubMeshes.Count;
|
||||
for (var i = 0; i < integrator.SubMeshes.Count; ++i)
|
||||
{
|
||||
mesh.SetIndices(integrator.SubMeshes[i].Indices.ToArray(), MeshTopology.Triangles, i);
|
||||
}
|
||||
mesh.bindposes = integrator.BindPoses.ToArray();
|
||||
|
||||
if (onlyBlendShapeRenderers)
|
||||
{
|
||||
integrator.AddBlendShapesToMesh(mesh);
|
||||
mesh.name = INTEGRATED_MESH_BLENDSHAPE_NAME;
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh.name = INTEGRATED_MESH_NAME;
|
||||
}
|
||||
|
||||
var integrated = meshNode.AddComponent<SkinnedMeshRenderer>();
|
||||
integrated.sharedMesh = mesh;
|
||||
integrated.sharedMaterials = integrator.SubMeshes.Select(x => x.Material).ToArray();
|
||||
integrated.bones = integrator.Bones.ToArray();
|
||||
result.IntegratedRenderer = integrated;
|
||||
|
||||
return result;
|
||||
integrator.Intgrate(onlyBlendShapeRenderers);
|
||||
integrator.Result.IntegratedRenderer.transform.SetParent(go.transform, false);
|
||||
return integrator.Result;
|
||||
}
|
||||
|
||||
public static IEnumerable<SkinnedMeshRenderer> EnumerateSkinnedMeshRenderer(Transform root, bool hasBlendShape)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -12,7 +11,8 @@ using Object = UnityEngine.Object;
|
||||
namespace VRM
|
||||
{
|
||||
/// <summary>
|
||||
/// 複数のメッシュをまとめて、BlendShapeClipに変更を反映する
|
||||
/// 複数のメッシュを統合する。
|
||||
/// VRM の場合、BlendShapeClip の調整が必用なのでこれも実行する。
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public static class MeshIntegratorEditor
|
||||
@@ -26,28 +26,11 @@ namespace VRM
|
||||
SkinnedMeshUtility.IsPrefab(Selection.activeObject);
|
||||
}
|
||||
|
||||
public static void Integrate()
|
||||
{
|
||||
var go = Selection.activeObject as GameObject;
|
||||
|
||||
Integrate(go);
|
||||
}
|
||||
|
||||
//[MenuItem("Assets/fooa", false)]
|
||||
//private static void Foo()
|
||||
//{
|
||||
// var go = Selection.activeObject as GameObject;
|
||||
|
||||
// Debug.Log(SkinnedMeshUtility.IsPrefab(go));
|
||||
//}
|
||||
|
||||
|
||||
public static List<MeshIntegrationResult> Integrate(GameObject prefab)
|
||||
public static List<MeshIntegrationResult> Integrate(GameObject prefab, UniGLTF.UnityPath writeAssetPath, IEnumerable<Mesh> excludes)
|
||||
{
|
||||
Undo.RecordObject(prefab, "Mesh Integration");
|
||||
var instance = SkinnedMeshUtility.InstantiatePrefab(prefab);
|
||||
|
||||
|
||||
var clips = new List<BlendShapeClip>();
|
||||
var proxy = instance.GetComponent<VRMBlendShapeProxy>();
|
||||
if (proxy != null && proxy.BlendShapeAvatar != null)
|
||||
@@ -63,17 +46,9 @@ namespace VRM
|
||||
BackupVrmPrefab(prefab);
|
||||
|
||||
// Execute
|
||||
var results = VRMMeshIntegratorUtility.Integrate(instance, clips);
|
||||
var results = VRMMeshIntegratorUtility.Integrate(instance, clips, excludes);
|
||||
|
||||
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
|
||||
// disable source renderer
|
||||
foreach (var res in results)
|
||||
{
|
||||
foreach (var renderer in res.SourceSkinnedMeshRenderers)
|
||||
@@ -89,8 +64,32 @@ namespace VRM
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result.IntegratedRenderer == null) continue;
|
||||
|
||||
var assetPath = writeAssetPath.Parent.Child($"{result.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}");
|
||||
Debug.LogFormat("CreateAsset: {0}", assetPath);
|
||||
assetPath.CreateAsset(result.IntegratedRenderer.sharedMesh);
|
||||
Undo.RegisterCreatedObjectUndo(result.IntegratedRenderer.gameObject, "Integrate Renderers");
|
||||
}
|
||||
|
||||
// Apply to Prefab
|
||||
SkinnedMeshUtility.ApplyChangesToPrefab(instance);
|
||||
var prefabPath = UniGLTF.UnityPath.FromUnityPath(AssetDatabase.GetAssetPath(prefab));
|
||||
if (prefabPath.Equals(writeAssetPath))
|
||||
{
|
||||
SkinnedMeshUtility.ApplyChangesToPrefab(instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrefabUtility.SaveAsPrefabAsset(instance, writeAssetPath.Value, out bool success);
|
||||
if (!success)
|
||||
{
|
||||
throw new System.Exception($"PrefabUtility.SaveAsPrefabAsset: {writeAssetPath}");
|
||||
}
|
||||
}
|
||||
|
||||
// destroy source renderers
|
||||
Object.DestroyImmediate(instance);
|
||||
|
||||
return results;
|
||||
@@ -143,17 +142,5 @@ namespace VRM
|
||||
assetPath = assetPath.Replace(@"\", @"/");
|
||||
return assetPath;
|
||||
}
|
||||
|
||||
private static void SaveMeshAsset(Mesh mesh, GameObject go, string name)
|
||||
{
|
||||
var assetPath = Path.Combine(GetRootPrefabPath(go), string.Format("{0}{1}",
|
||||
name,
|
||||
ASSET_SUFFIX
|
||||
));
|
||||
|
||||
Debug.LogFormat("CreateAsset: {0}", assetPath);
|
||||
AssetDatabase.CreateAsset(mesh, assetPath);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,16 @@ using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniGLTF.MeshUtility;
|
||||
using System.IO;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public class MeshIntegratorWizard : ScriptableWizard
|
||||
{
|
||||
const string MENU_KEY = "Assets/UnityEditorScripts/MeshIntegratorWizard";
|
||||
|
||||
[SerializeField]
|
||||
GameObject m_root;
|
||||
|
||||
[Header("Validation")]
|
||||
[SerializeField]
|
||||
Material[] m_uniqueMaterials;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace VRM
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if(!(obj is MaterialKey))
|
||||
if (!(obj is MaterialKey))
|
||||
{
|
||||
return base.Equals(obj);
|
||||
}
|
||||
@@ -57,13 +57,24 @@ namespace VRM
|
||||
[SerializeField]
|
||||
MaterialList[] m_duplicateMaterials;
|
||||
|
||||
[Header("Result")]
|
||||
public MeshIntegrationResult[] integrationResults;
|
||||
|
||||
[MenuItem(MENU_KEY)]
|
||||
static void CreateWizard()
|
||||
[Serializable]
|
||||
public class ExcludeItem
|
||||
{
|
||||
ScriptableWizard.DisplayWizard<MeshIntegratorWizard>("MeshIntegrator", "Integrate and close window", "Integrate");
|
||||
public Mesh Mesh;
|
||||
public bool Exclude;
|
||||
}
|
||||
|
||||
[Header("Options")]
|
||||
[SerializeField]
|
||||
List<ExcludeItem> m_excludes = new List<ExcludeItem>();
|
||||
|
||||
[Header("Result")]
|
||||
[SerializeField]
|
||||
MeshMap[] integrationResults;
|
||||
|
||||
public static void CreateWizard()
|
||||
{
|
||||
ScriptableWizard.DisplayWizard<MeshIntegratorWizard>("MeshIntegratorWizard", "Integrate and close window", "Integrate");
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
@@ -116,14 +127,17 @@ namespace VRM
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
Debug.Log("OnValidate");
|
||||
if (m_root == null)
|
||||
if (m_root == null
|
||||
|| !PrefabUtility.IsPartOfAnyPrefab(m_root) || m_root.transform.parent != null)
|
||||
{
|
||||
Debug.LogWarning("Invalidate");
|
||||
m_uniqueMaterials = new Material[] { };
|
||||
m_duplicateMaterials = new MaterialList[] { };
|
||||
m_excludes.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("OnValidate");
|
||||
m_uniqueMaterials = MeshIntegratorUtility.EnumerateSkinnedMeshRenderer(m_root.transform, false)
|
||||
.SelectMany(x => x.sharedMaterials)
|
||||
.Distinct()
|
||||
@@ -135,6 +149,33 @@ namespace VRM
|
||||
.Where(x => x.Materials.Length > 1)
|
||||
.ToArray()
|
||||
;
|
||||
|
||||
var exclude_map = new Dictionary<Mesh, ExcludeItem>();
|
||||
var excludes = new List<ExcludeItem>();
|
||||
foreach (var x in m_root.GetComponentsInChildren<Renderer>())
|
||||
{
|
||||
var mesh = x.GetMesh();
|
||||
if (mesh == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = new ExcludeItem { Mesh = mesh };
|
||||
excludes.Add(item);
|
||||
exclude_map[mesh] = item;
|
||||
}
|
||||
foreach (var x in m_excludes)
|
||||
{
|
||||
if (exclude_map.TryGetValue(x.Mesh, out ExcludeItem item))
|
||||
{
|
||||
// update
|
||||
item.Exclude = x.Exclude;
|
||||
}
|
||||
}
|
||||
m_excludes.Clear();
|
||||
foreach (var kv in exclude_map)
|
||||
{
|
||||
m_excludes.Add(kv.Value);
|
||||
}
|
||||
}
|
||||
|
||||
void OnWizardUpdate()
|
||||
@@ -150,7 +191,22 @@ namespace VRM
|
||||
return;
|
||||
}
|
||||
|
||||
integrationResults = MeshIntegratorEditor.Integrate(m_root).ToArray();
|
||||
var prefabPath = AssetDatabase.GetAssetPath(m_root);
|
||||
var path = EditorUtility.SaveFilePanel("save prefab", Path.GetDirectoryName(prefabPath), m_root.name, "prefab");
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var assetPath = UniGLTF.UnityPath.FromFullpath(path);
|
||||
if (!assetPath.IsUnderAssetsFolder)
|
||||
{
|
||||
Debug.LogWarning($"{path} is not asset path");
|
||||
return;
|
||||
}
|
||||
|
||||
var excludes = m_excludes.Where(x => x.Exclude).Select(x => x.Mesh);
|
||||
integrationResults = MeshIntegratorEditor.Integrate(m_root, assetPath, excludes).Select(x => x.MeshMap).ToArray();
|
||||
}
|
||||
|
||||
void OnWizardCreate()
|
||||
|
||||
@@ -27,11 +27,8 @@ namespace VRM
|
||||
[MenuItem(UserMenuPrefix + "/Freeze T-Pose", priority = 20)]
|
||||
private static void FreezeTPose() => VRMHumanoidNormalizerMenu.Normalize();
|
||||
|
||||
[MenuItem(UserMenuPrefix + "/MeshIntegration", validate = true)]
|
||||
private static bool MeshIntegrationValidation() => MeshIntegratorEditor.IntegrateValidation();
|
||||
|
||||
[MenuItem(UserMenuPrefix + "/MeshIntegration", priority = 21)]
|
||||
private static void MeshIntegration() => MeshIntegratorEditor.Integrate();
|
||||
[MenuItem(UserMenuPrefix + "/MeshIntegratorWizard", priority = 21)]
|
||||
private static void OpenMeshIntegratorWizard() => MeshIntegratorWizard.CreateWizard();
|
||||
|
||||
[MenuItem(UserMenuPrefix + "/Save SpringBone to JSON", validate = true)]
|
||||
private static bool SaveSpringBoneToJsonValidation() => VRMSpringBoneUtilityEditor.SaveSpringBoneToJsonValidation();
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace VRM
|
||||
if (avatar == null) return false;
|
||||
var clips = avatar.Clips;
|
||||
|
||||
var results = Integrate(vrmRootObject, clips);
|
||||
var results = Integrate(vrmRootObject, clips, null);
|
||||
if (results.Any(x => x.IntegratedRenderer == null)) return false;
|
||||
|
||||
foreach (var result in results)
|
||||
@@ -39,17 +39,17 @@ namespace VRM
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<UniGLTF.MeshUtility.MeshIntegrationResult> Integrate(GameObject root, List<BlendShapeClip> blendshapeClips)
|
||||
public static List<UniGLTF.MeshUtility.MeshIntegrationResult> Integrate(GameObject root, List<BlendShapeClip> blendshapeClips, IEnumerable<Mesh> excludes)
|
||||
{
|
||||
var result = new List<UniGLTF.MeshUtility.MeshIntegrationResult>();
|
||||
|
||||
var withoutBlendShape = MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: false);
|
||||
var withoutBlendShape = MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: false, excludes: excludes);
|
||||
if (withoutBlendShape.IntegratedRenderer != null)
|
||||
{
|
||||
result.Add(withoutBlendShape);
|
||||
}
|
||||
|
||||
var onlyBlendShape = MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: true);
|
||||
var onlyBlendShape = MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: true, excludes: excludes);
|
||||
if (onlyBlendShape.IntegratedRenderer != null)
|
||||
{
|
||||
result.Add(onlyBlendShape);
|
||||
|
||||
Reference in New Issue
Block a user