mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-09-26 21:01:15 -05:00
WIP split by blendshape
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace UniGLTF.MeshUtility
|
||||
|
||||
// Add integrated
|
||||
foreach (var result in results)
|
||||
{
|
||||
{
|
||||
result.AddIntegratedRendererTo(copy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user