WIP split by blendshape

This commit is contained in:
ousttrue
2023-11-07 19:21:03 +09:00
parent d4ca89562e
commit 8deb17d18c
7 changed files with 177 additions and 62 deletions

View File

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

View File

@@ -1,40 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UniGLTF.MeshUtility
{
[System.Serializable]
public class MeshMap
public struct DrawCount
{
public List<Mesh> Sources = new List<Mesh>();
public Mesh Integrated;
public Material[] SharedMaterials;
public Transform[] Bones;
public int Count;
public Material Material;
}
[Serializable]
public class MeshInfo
{
public Mesh Mesh;
public List<DrawCount> SubMeshes = new List<DrawCount>();
public SkinnedMeshRenderer IntegratedRenderer;
public void AddIntegratedRendererTo(GameObject parent, Transform[] bones)
{
var go = new GameObject(Mesh.name);
go.transform.SetParent(parent.transform, false);
var smr = go.AddComponent<SkinnedMeshRenderer>();
smr.sharedMesh = Mesh;
smr.sharedMaterials = SubMeshes.Where(x => x.Count > 0).Select(x => x.Material).ToArray();
smr.bones = bones;
IntegratedRenderer = smr;
}
}
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 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 List<Mesh> Sources = new List<Mesh>();
public MeshInfo Integrated;
public MeshInfo IntegratedNoBlendShape;
public Transform[] Bones;
public void DestroySourceRenderer()
{
throw new NotImplementedException();
}
public void AddIntegratedRendererTo(GameObject parent)
{
if (Integrated != null)
{
Integrated.AddIntegratedRendererTo(parent, Bones);
}
if (IntegratedNoBlendShape != null)
{
IntegratedNoBlendShape.AddIntegratedRendererTo(parent, Bones);
}
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using UnityEngine;
using UnityEngine.UIElements;
@@ -118,7 +119,7 @@ namespace UniGLTF.MeshUtility
return;
}
Result.SourceMeshRenderers.Add(renderer);
Result.MeshMap.Sources.Add(mesh);
Result.Sources.Add(mesh);
var indexOffset = Positions.Count;
@@ -190,7 +191,7 @@ namespace UniGLTF.MeshUtility
return;
}
Result.SourceSkinnedMeshRenderers.Add(renderer);
Result.MeshMap.Sources.Add(mesh);
Result.Sources.Add(mesh);
var indexOffset = Positions.Count;
// var boneIndexOffset = Bones.Count;
@@ -279,48 +280,129 @@ namespace UniGLTF.MeshUtility
return integrator.Integrate(group.Name, op);
}
public MeshIntegrationResult Integrate(string name, BlendShapeOperation op)
delegate bool TriangleFilter(int i0, int i1, int i2);
Mesh CreateMesh(string name, List<DrawCount> dst, TriangleFilter filter)
{
if (op == BlendShapeOperation.Split)
{
throw new NotImplementedException();
}
var mesh = new Mesh();
mesh.name = name;
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)
for (var submesh = 0; submesh < SubMeshes.Count; ++submesh)
{
mesh.SetIndices(SubMeshes[i].Indices.ToArray(), MeshTopology.Triangles, i);
if (filter != null)
{
var indices = SubMeshes[submesh].Indices.ToArray();
var filtered = new List<int>();
for (int i = 0; i < indices.Length; i += 3)
{
var i0 = indices[i];
var i1 = indices[i + 1];
var i2 = indices[i + 2];
if (filter(i0, i1, i2))
{
filtered.Add(i0);
filtered.Add(i1);
filtered.Add(i2);
}
}
mesh.SetIndices(filtered.ToArray(), MeshTopology.Triangles, submesh);
dst.Add(new DrawCount
{
Count = filtered.Count,
Material = SubMeshes[submesh].Material,
});
}
else
{
// use all triangle
var indices = SubMeshes[submesh].Indices;
mesh.SetIndices(indices.ToArray(), MeshTopology.Triangles, submesh);
dst.Add(new DrawCount
{
Count = indices.Count,
Material = SubMeshes[submesh].Material,
});
}
}
return mesh;
}
public MeshIntegrationResult Integrate(string name, BlendShapeOperation op)
{
if (_Bones.Count != _BindPoses.Count)
{
throw new ArgumentException();
}
Debug.Log($"Bones: {_Bones.Count}");
mesh.bindposes = _BindPoses.ToArray();
if (op == BlendShapeOperation.Use)
bool useSplit = false;
var used = new bool[Positions.Count];
if (op == BlendShapeOperation.Split)
{
AddBlendShapesToMesh(mesh);
foreach (var x in BlendShapes)
{
for (int i = 0; i < x.Positions.Length; ++i)
{
if (x.Positions[i] != Vector3.zero)
{
used[i] = true;
}
}
}
var count = used.Count(x => x);
useSplit = count > 0 && count < used.Length;
}
mesh.name = name;
Result.MeshMap.SharedMaterials = SubMeshes.Select(x => x.Material).ToArray();
Result.MeshMap.Bones = _Bones.ToArray();
Result.MeshMap.Integrated = mesh;
if (useSplit)
{
Result.Integrated = new MeshInfo();
var mesh = CreateMesh(name, Result.Integrated.SubMeshes,
(i0, i1, i2) => used[i0] || used[i1] || used[i2]);
Result.Integrated.Mesh = mesh;
AddBlendShapesToMesh(mesh);
// skinning
mesh.bindposes = _BindPoses.ToArray();
Result.IntegratedNoBlendShape = new MeshInfo();
var meshWithoutBlendShape = CreateMesh(name + ".no_blendshape", Result.IntegratedNoBlendShape.SubMeshes,
(i0, i1, i2) => !used[i0] && !used[i1] && !used[i2]);
Result.IntegratedNoBlendShape.Mesh = meshWithoutBlendShape;
// skinning
meshWithoutBlendShape.bindposes = _BindPoses.ToArray();
}
else
{
var useBlendShape = op == BlendShapeOperation.Use && BlendShapes.Count > 0;
if (useBlendShape)
{
Result.Integrated = new MeshInfo();
var mesh = CreateMesh(name, Result.Integrated.SubMeshes, null);
Result.Integrated.Mesh = mesh;
AddBlendShapesToMesh(mesh);
// skinning
mesh.bindposes = _BindPoses.ToArray();
}
else
{
Result.IntegratedNoBlendShape = new MeshInfo();
var mesh = CreateMesh(name, Result.IntegratedNoBlendShape.SubMeshes, null);
Result.IntegratedNoBlendShape.Mesh = mesh;
// skinning
mesh.bindposes = _BindPoses.ToArray();
}
}
Result.Bones = _Bones.ToArray();
return Result;
}
}

View File

@@ -191,7 +191,7 @@ namespace UniGLTF.MeshUtility
// Add integrated
foreach (var result in results)
{
{
result.AddIntegratedRendererTo(copy);
}
}

View File

@@ -21,7 +21,7 @@ namespace VRM
{
return clips;
}
var result = results.FirstOrDefault(x => x.MeshMap.Integrated.blendShapeCount > 0);
var result = results.FirstOrDefault(x => x.Integrated.Mesh.blendShapeCount > 0);
if (result == null)
{
return clips;
@@ -32,7 +32,7 @@ namespace VRM
{
rendererDict.Add(x.transform.RelativePathFrom(root.transform), x);
}
var dstPath = result.IntegratedRenderer.transform.RelativePathFrom(root.transform);
var dstPath = result.Integrated.IntegratedRenderer.transform.RelativePathFrom(root.transform);
// copy modify and write
var clipAssetPathList = new List<string>();
@@ -54,7 +54,7 @@ namespace VRM
{
var srcRenderer = rendererDict[val.RelativePath];
var name = srcRenderer.sharedMesh.GetBlendShapeName(val.Index);
var newIndex = result.IntegratedRenderer.sharedMesh.GetBlendShapeIndex(name);
var newIndex = result.Integrated.IntegratedRenderer.sharedMesh.GetBlendShapeIndex(name);
if (newIndex == -1)
{
throw new KeyNotFoundException($"blendshape:{name} not found");

View File

@@ -91,7 +91,7 @@ namespace VRM
[Header("Result")]
[SerializeField]
MeshMap[] integrationResults;
MeshInfo[] integrationResults;
public static void CreateWizard()
{
@@ -279,17 +279,17 @@ namespace VRM
// write mesh asset
foreach (var result in results)
{
var childAssetPath = $"{assetFolder}/{result.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}";
var childAssetPath = $"{assetFolder}/{result.Integrated.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}";
Debug.LogFormat("CreateAsset: {0}", childAssetPath);
AssetDatabase.CreateAsset(result.IntegratedRenderer.sharedMesh, childAssetPath);
AssetDatabase.CreateAsset(result.Integrated.IntegratedRenderer.sharedMesh, childAssetPath);
}
// 統合した結果をヒエラルキーに追加する
foreach (var result in results)
{
if (result.IntegratedRenderer != null)
if (result.Integrated.IntegratedRenderer != null)
{
result.IntegratedRenderer.transform.SetParent(copy.transform, false);
result.Integrated.IntegratedRenderer.transform.SetParent(copy.transform, false);
}
}

View File

@@ -251,7 +251,7 @@ namespace UniVRM10
{
var result = MeshIntegrator.Integrate(group, SplitByBlendShape
? MeshIntegrator.BlendShapeOperation.Split
: MeshIntegrator.BlendShapeOperation.None);
: MeshIntegrator.BlendShapeOperation.Use);
results.Add(result);
result.AddIntegratedRendererTo(empty);
@@ -259,21 +259,13 @@ namespace UniVRM10
if (generateFirstPerson && group.Name == "auto")
{
Debug.Log("generateFirstPerson");
var firstPersonBone = vrmInstance.Humanoid.Head;
var task = VRM10ObjectFirstPerson.CreateErasedMeshAsync(
result.IntegratedRenderer,
firstPersonBone,
new ImmediateCaller());
task.Wait();
if (task.Result != null)
if (result.Integrated.Mesh != null)
{
result.IntegratedRenderer.sharedMesh = task.Result;
result.IntegratedRenderer.name = "auto.headless";
ProcessFirstPerson(vrmInstance, result.Integrated);
}
else
if (result.IntegratedNoBlendShape.Mesh != null)
{
Debug.LogWarning("no result");
ProcessFirstPerson(vrmInstance, result.IntegratedNoBlendShape);
}
}
}
@@ -293,5 +285,25 @@ namespace UniVRM10
MeshIntegrationGroups.Clear();
}
void ProcessFirstPerson(Vrm10Instance vrmInstance, MeshInfo info)
{
var firstPersonBone = vrmInstance.Humanoid.Head;
var task = VRM10ObjectFirstPerson.CreateErasedMeshAsync(
info.IntegratedRenderer,
firstPersonBone,
new ImmediateCaller());
task.Wait();
if (task.Result != null)
{
info.IntegratedRenderer.sharedMesh = task.Result;
info.IntegratedRenderer.name = "auto.headless";
}
else
{
Debug.LogWarning("no result");
}
}
}
}