MeshIntegratorの非VRM部分をMeshUtilityに移動

This commit is contained in:
ousttrue
2020-08-25 19:52:23 +09:00
parent 6bb0bc2cb5
commit a3ca5ff563
10 changed files with 147 additions and 252 deletions

View 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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e1c66a21d479b3e4a92eedd622d27f4f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -6,14 +6,6 @@ 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;
}
/// <summary>
/// go を root としたヒエラルキーから Renderer を集めて、統合された Mesh 作成する
/// </summary>

View File

@@ -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>();
}

View File

@@ -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();

View File

@@ -3,7 +3,8 @@
"references": [
"VRM",
"UniJSON",
"UniHumanoid"
"UniHumanoid",
"MeshUtility"
],
"optionalUnityReferences": [],
"includePlatforms": [

View File

@@ -1,222 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
namespace VRM
{
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)
{
var result = new MeshIntegrationResult();
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))
{
integrator.Push(x);
result.SourceSkinnedMeshRenderers.Add(x);
}
}
else
{
foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, false))
{
integrator.Push(x);
result.SourceSkinnedMeshRenderers.Add(x);
}
foreach (var x in EnumerateMeshRenderer(go.transform))
{
integrator.Push(x);
result.SourceMeshRenderers.Add(x);
}
}
var mesh = new Mesh();
mesh.name = "integrated";
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);
}
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;
}
public static IEnumerable<SkinnedMeshRenderer> EnumerateSkinnedMeshRenderer(Transform root, bool hasBlendShape)
{
foreach (var x in Traverse(root))
{
var renderer = x.GetComponent<SkinnedMeshRenderer>();
if (renderer != null &&
renderer.gameObject.activeInHierarchy &&
renderer.sharedMesh != null &&
renderer.enabled &&
renderer.sharedMesh.blendShapeCount > 0 == hasBlendShape)
{
yield return renderer;
}
}
}
public static IEnumerable<MeshRenderer> EnumerateMeshRenderer(Transform root)
{
foreach (var x in Traverse(root))
{
var renderer = x.GetComponent<MeshRenderer>();
var filter = x.GetComponent<MeshFilter>();
if (renderer != null &&
filter != null &&
renderer.gameObject.activeInHierarchy &&
filter.sharedMesh != null)
{
yield return renderer;
}
}
}
private static IEnumerable<Transform> Traverse(Transform parent)
{
if (parent.gameObject.activeSelf)
{
yield return parent;
foreach (Transform child in parent)
{
foreach (var x in Traverse(child))
{
yield return x;
}
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: a982d9d30c0145038245b0214dc2f2e4
timeCreated: 1560190306

View File

@@ -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;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8eebeb093136b7f429c0e9e7295816b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: