Merge pull request #2180 from ousttrue/fix/refactor_mesh_integration

[vrm1.0] impl refactor mesh integration
This commit is contained in:
ousttrue 2023-10-31 16:45:55 +09:00 committed by GitHub
commit e896c6c91b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 180 additions and 86 deletions

View File

@ -90,7 +90,7 @@ namespace UniGLTF.MeshUtility
// write mesh asset.
foreach (var result in results)
{
var mesh = result.IntegratedRenderer.sharedMesh;
var mesh = result.MeshMap.Integrated;
var assetPath = GetMeshWritePath(mesh);
Debug.LogFormat("CreateAsset: {0}", assetPath);
AssetDatabase.CreateAsset(mesh, assetPath);

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
@ -144,5 +145,33 @@ namespace UniGLTF.MeshUtility
return (normalized, boneMap);
}
public static void WriteBackResult(GameObject go, GameObject normalized, Dictionary<Transform, Transform> boneMap)
{
Func<Transform, Transform> getSrc = dst =>
{
foreach (var (k, v) in boneMap)
{
if (v == dst)
{
return k;
}
}
throw new NotImplementedException();
};
foreach (var (src, dst) in boneMap)
{
src.localPosition = dst.localPosition;
src.localRotation = dst.localRotation;
src.localScale = dst.localScale;
var srcR = src.GetComponent<SkinnedMeshRenderer>();
var dstR = dst.GetComponent<SkinnedMeshRenderer>();
if (srcR != null && dstR != null)
{
srcR.sharedMesh = dstR.sharedMesh;
srcR.bones = dstR.bones.Select(x => getSrc(x)).ToArray();
}
}
}
}
}

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
using UnityEngine;
namespace UniGLTF.MeshUtility
{
public class MeshIntegrationGroup
{
/// <summary>
/// FirstPerson flag
/// TODO: enum
/// </summary>
public string Name;
public List<Renderer> Renderers = new List<Renderer>();
}
}

View File

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

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UniGLTF.MeshUtility
@ -8,14 +9,32 @@ namespace UniGLTF.MeshUtility
{
public List<Mesh> Sources = new List<Mesh>();
public Mesh Integrated;
public Material[] SharedMaterials;
public Transform[] Bones;
}
public class MeshIntegrationResult
{
public List<SkinnedMeshRenderer> SourceSkinnedMeshRenderers = new List<SkinnedMeshRenderer>();
public List<MeshRenderer> SourceMeshRenderers = new List<MeshRenderer>();
public MeshMap MeshMap = new MeshMap();
public SkinnedMeshRenderer IntegratedRenderer;
public MeshMap MeshMap = new MeshMap();
public void AddIntegratedRendererTo(GameObject parent)
{
var go = new GameObject(MeshMap.Integrated.name);
go.transform.SetParent(parent.transform, false);
var smr = go.AddComponent<SkinnedMeshRenderer>();
smr.sharedMesh = MeshMap.Integrated;
smr.sharedMaterials = MeshMap.SharedMaterials;
smr.bones = MeshMap.Bones;
IntegratedRenderer = smr;
}
public void DestroySourceRenderer()
{
throw new NotImplementedException();
}
}
}

View File

@ -6,9 +6,10 @@ namespace UniGLTF.MeshUtility
{
public class MeshIntegrator
{
public const string INTEGRATED_MESH_WITHOUT_BLENDSHAPE_NAME = "Integrated(WithoutBlendShape)";
public const string INTEGRATED_MESH_WITH_BLENDSHAPE_NAME = "Integrated(WithBlendShape)";
public const string INTEGRATED_MESH_ALL_NAME = "Integrated(All)";
private MeshIntegrator()
{
}
struct SubMesh
{
@ -81,7 +82,7 @@ namespace UniGLTF.MeshUtility
return bw;
}
public void Push(MeshRenderer renderer)
void Push(MeshRenderer renderer)
{
var meshFilter = renderer.GetComponent<MeshFilter>();
if (meshFilter == null)
@ -232,7 +233,24 @@ namespace UniGLTF.MeshUtility
}
}
public MeshIntegrationResult Integrate(MeshEnumerateOption onlyBlendShapeRenderers)
public static MeshIntegrationResult Integrate(MeshIntegrationGroup group, bool useBlendShape)
{
var integrator = new MeshUtility.MeshIntegrator();
foreach (var x in group.Renderers)
{
if (x is SkinnedMeshRenderer smr)
{
integrator.Push(smr);
}
else if (x is MeshRenderer mr)
{
integrator.Push(mr);
}
}
return integrator.Integrate(group.Name, useBlendShape);
}
public MeshIntegrationResult Integrate(string name, bool useBlendShape)
{
var mesh = new Mesh();
@ -253,57 +271,14 @@ namespace UniGLTF.MeshUtility
mesh.SetIndices(SubMeshes[i].Indices.ToArray(), MeshTopology.Triangles, i);
}
mesh.bindposes = BindPoses.ToArray();
// blendshape
switch (onlyBlendShapeRenderers)
if (useBlendShape)
{
case MeshEnumerateOption.OnlyWithBlendShape:
{
AddBlendShapesToMesh(mesh);
mesh.name = INTEGRATED_MESH_WITH_BLENDSHAPE_NAME;
break;
}
case MeshEnumerateOption.All:
{
AddBlendShapesToMesh(mesh);
mesh.name = INTEGRATED_MESH_ALL_NAME;
break;
}
case MeshEnumerateOption.OnlyWithoutBlendShape:
{
mesh.name = INTEGRATED_MESH_WITHOUT_BLENDSHAPE_NAME;
break;
}
AddBlendShapesToMesh(mesh);
}
mesh.name = name;
// meshName
var meshNode = new GameObject();
switch (onlyBlendShapeRenderers)
{
case MeshEnumerateOption.OnlyWithBlendShape:
{
meshNode.name = INTEGRATED_MESH_WITH_BLENDSHAPE_NAME;
break;
}
case MeshEnumerateOption.OnlyWithoutBlendShape:
{
meshNode.name = INTEGRATED_MESH_WITHOUT_BLENDSHAPE_NAME;
break;
}
case MeshEnumerateOption.All:
{
meshNode.name = INTEGRATED_MESH_ALL_NAME;
break;
}
}
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.SharedMaterials = SubMeshes.Select(x => x.Material).ToArray();
Result.MeshMap.Bones = Bones.ToArray();
Result.MeshMap.Integrated = mesh;
return Result;
}

View File

@ -1,11 +1,14 @@
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace UniGLTF.MeshUtility
{
public static class MeshIntegratorUtility
{
public const string INTEGRATED_MESH_WITHOUT_BLENDSHAPE_NAME = "Integrated(WithoutBlendShape)";
public const string INTEGRATED_MESH_WITH_BLENDSHAPE_NAME = "Integrated(WithBlendShape)";
public const string INTEGRATED_MESH_ALL_NAME = "Integrated(All)";
/// <summary>
/// go を root としたヒエラルキーから Renderer を集めて、統合された Mesh 作成する
/// </summary>
@ -22,32 +25,39 @@ namespace UniGLTF.MeshUtility
{
var exclude = new MeshExclude(excludes);
var integrator = new MeshUtility.MeshIntegrator();
var group = new MeshIntegrationGroup();
bool useBlendShape = false;
switch (onlyBlendShapeRenderers)
{
case MeshEnumerateOption.OnlyWithBlendShape:
{
group.Name = INTEGRATED_MESH_WITH_BLENDSHAPE_NAME;
useBlendShape = true;
foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, onlyBlendShapeRenderers))
{
if (exclude.IsExcluded(x))
{
continue;
}
integrator.Push(x);
group.Renderers.Add(x);
}
break;
}
case MeshEnumerateOption.OnlyWithoutBlendShape:
{
group.Name = INTEGRATED_MESH_WITHOUT_BLENDSHAPE_NAME;
useBlendShape = false;
foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, onlyBlendShapeRenderers))
{
if (exclude.IsExcluded(x))
{
continue;
}
integrator.Push(x);
group.Renderers.Add(x);
}
foreach (var x in EnumerateMeshRenderer(go.transform))
@ -56,7 +66,7 @@ namespace UniGLTF.MeshUtility
{
continue;
}
integrator.Push(x);
group.Renderers.Add(x);
}
break;
@ -64,13 +74,16 @@ namespace UniGLTF.MeshUtility
case MeshEnumerateOption.All:
{
group.Name = INTEGRATED_MESH_ALL_NAME;
useBlendShape = true;
foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, onlyBlendShapeRenderers))
{
if (exclude.IsExcluded(x))
{
continue;
}
integrator.Push(x);
group.Renderers.Add(x);
}
foreach (var x in EnumerateMeshRenderer(go.transform))
@ -79,14 +92,14 @@ namespace UniGLTF.MeshUtility
{
continue;
}
integrator.Push(x);
group.Renderers.Add(x);
}
break;
}
}
return integrator.Integrate(onlyBlendShapeRenderers);
return MeshIntegrator.Integrate(group, useBlendShape);
}
public static IEnumerable<SkinnedMeshRenderer> EnumerateSkinnedMeshRenderer(Transform root, MeshEnumerateOption hasBlendShape)
@ -177,7 +190,7 @@ namespace UniGLTF.MeshUtility
// Add integrated
foreach (var result in results)
{
result.IntegratedRenderer.transform.SetParent(copy.transform, false);
result.AddIntegratedRendererTo(copy);
}
}
}

View File

@ -306,5 +306,30 @@ namespace UniHumanoid
avatar.name = "created";
return avatar;
}
public static Avatar RecreateAvatar(Animator src)
{
if (src == null)
{
throw new ArgumentNullException("src");
}
var srcHumanBones = CachedEnum.GetValues<HumanBodyBones>()
.Where(x => x != HumanBodyBones.LastBone)
.Select(x => new { Key = x, Value = src.GetBoneTransform(x) })
.Where(x => x.Value != null)
;
var map =
srcHumanBones
.ToDictionary(x => x.Key, x => x.Value)
;
var avatarDescription = UniHumanoid.AvatarDescription.Create();
avatarDescription.SetHumanBones(map);
var avatar = avatarDescription.CreateAvatar(src.transform);
avatar.name = "created";
return avatar;
}
}
}

View File

@ -21,7 +21,7 @@ namespace VRM
{
return clips;
}
var result = results.FirstOrDefault(x => x.IntegratedRenderer.sharedMesh.blendShapeCount > 0);
var result = results.FirstOrDefault(x => x.MeshMap.Integrated.blendShapeCount > 0);
if (result == null)
{
return clips;

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using UniGLTF.MeshUtility;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
@ -38,7 +39,7 @@ namespace UniVRM10
_meshUti = meshUtility;
_splitter = new VerticalSplitter(editor, 50, 50);
_groupList = new ReorderableList(_meshUti.MeshIntegrationGroups, typeof(Vrm10MeshUtility.MeshIntegrationGroup));
_groupList = new ReorderableList(_meshUti.MeshIntegrationGroups, typeof(MeshIntegrationGroup));
_groupList.drawHeaderCallback = (Rect rect) =>
{
GUI.Label(rect, "Integration group");

View File

@ -48,16 +48,6 @@ namespace UniVRM10
/// </summary>
public bool FreezeRotation = false;
public class MeshIntegrationGroup
{
/// <summary>
/// FirstPerson flag
/// TODO: enum
/// </summary>
public string Name;
public List<Renderer> Renderers = new List<Renderer>();
}
public List<MeshIntegrationGroup> MeshIntegrationGroups = new List<MeshIntegrationGroup>();
/// <summary>
@ -145,23 +135,39 @@ namespace UniVRM10
// TODO: update: spring
// TODO: update: constraint
// TODO: update: firstPoint offset
// write back normalized transform to boneMap
BoneNormalizer.WriteBackResult(go, normalized, boneMap);
if (Application.isPlaying)
{
GameObject.Destroy(normalized);
}
else
{
GameObject.DestroyImmediate(normalized);
}
var newAvatar = AvatarDescription.CreateAvatarForCopyHierarchy(go.GetComponent<Animator>(), normalized, boneMap);
var newAnimator = normalized.GetOrAddComponent<Animator>();
newAnimator.avatar = newAvatar;
// TODO: write back normalized transform to boneMap
var animator = go.GetComponent<Animator>();
var newAvatar = AvatarDescription.RecreateAvatar(animator);
animator.avatar = newAvatar;
// TODO: integration
foreach (var group in MeshIntegrationGroups)
{
foreach (var renderer in group.Renderers)
var result = MeshIntegrator.Integrate(group, true);
// TODO: firstperson
// TODO: split
if (SplitByBlendShape)
{
// var withBlendShape, withoutBlendShape
}
else
{
}
}
// TODO: split
// TODO: remove old renderer
result.AddIntegratedRendererTo(go);
}
}
}
}