mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-13 22:09:53 -05:00
Merge pull request #2179 from ousttrue/fix/impl_refactor_mesh_bake
[vrm1.0] impl refactor mesh bake
This commit is contained in:
commit
897d6c7d07
54
Assets/UniGLTF/Runtime/MeshUtility/BlendShapeReport.cs
Normal file
54
Assets/UniGLTF/Runtime/MeshUtility/BlendShapeReport.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF.MeshUtility
|
||||
{
|
||||
class BlendShapeReport
|
||||
{
|
||||
string m_name;
|
||||
int m_count;
|
||||
struct BlendShapeStat
|
||||
{
|
||||
public int Index;
|
||||
public string Name;
|
||||
public int VertexCount;
|
||||
public int NormalCount;
|
||||
public int TangentCount;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}]{1}: {2}, {3}, {4}\n", Index, Name, VertexCount, NormalCount, TangentCount);
|
||||
}
|
||||
}
|
||||
List<BlendShapeStat> m_stats = new List<BlendShapeStat>();
|
||||
public int Count
|
||||
{
|
||||
get { return m_stats.Count; }
|
||||
}
|
||||
public BlendShapeReport(Mesh mesh)
|
||||
{
|
||||
m_name = mesh.name;
|
||||
m_count = mesh.vertexCount;
|
||||
}
|
||||
public void SetCount(int index, string name, int v, int n, int t)
|
||||
{
|
||||
m_stats.Add(new BlendShapeStat
|
||||
{
|
||||
Index = index,
|
||||
Name = name,
|
||||
VertexCount = v,
|
||||
NormalCount = n,
|
||||
TangentCount = t,
|
||||
});
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("NormalizeSkinnedMesh: {0}({1}verts)\n{2}",
|
||||
m_name,
|
||||
m_count,
|
||||
String.Join("", m_stats.Select(x => x.ToString()).ToArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Runtime/MeshUtility/BlendShapeReport.cs.meta
Normal file
11
Assets/UniGLTF/Runtime/MeshUtility/BlendShapeReport.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4ab7e7ef8eaac5b4593a6a1102c15012
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,16 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using VRMShaders;
|
||||
|
||||
|
||||
namespace UniGLTF.MeshUtility
|
||||
{
|
||||
public static class BoneNormalizer
|
||||
{
|
||||
public delegate Avatar CreateAvatarFunc(GameObject original, GameObject normalized, Dictionary<Transform, Transform> boneMap);
|
||||
|
||||
public static (GameObject, Dictionary<Transform, Transform>) CreateNormalizedHierarchy(GameObject go,
|
||||
bool removeScaling = true,
|
||||
bool removeRotation = true)
|
||||
|
|
@ -74,430 +70,6 @@ namespace UniGLTF.MeshUtility
|
|||
}
|
||||
}
|
||||
|
||||
class BlendShapeReport
|
||||
{
|
||||
string m_name;
|
||||
int m_count;
|
||||
struct BlendShapeStat
|
||||
{
|
||||
public int Index;
|
||||
public string Name;
|
||||
public int VertexCount;
|
||||
public int NormalCount;
|
||||
public int TangentCount;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}]{1}: {2}, {3}, {4}\n", Index, Name, VertexCount, NormalCount, TangentCount);
|
||||
}
|
||||
}
|
||||
List<BlendShapeStat> m_stats = new List<BlendShapeStat>();
|
||||
public int Count
|
||||
{
|
||||
get { return m_stats.Count; }
|
||||
}
|
||||
public BlendShapeReport(Mesh mesh)
|
||||
{
|
||||
m_name = mesh.name;
|
||||
m_count = mesh.vertexCount;
|
||||
}
|
||||
public void SetCount(int index, string name, int v, int n, int t)
|
||||
{
|
||||
m_stats.Add(new BlendShapeStat
|
||||
{
|
||||
Index = index,
|
||||
Name = name,
|
||||
VertexCount = v,
|
||||
NormalCount = n,
|
||||
TangentCount = t,
|
||||
});
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("NormalizeSkinnedMesh: {0}({1}verts)\n{2}",
|
||||
m_name,
|
||||
m_count,
|
||||
String.Join("", m_stats.Select(x => x.ToString()).ToArray()));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// index が 有効であれば、setter に weight を渡す。無効であれば setter に 0 を渡す。
|
||||
/// </summary>
|
||||
/// <param name="indexMap"></param>
|
||||
/// <param name="srcIndex"></param>
|
||||
/// <param name="weight"></param>
|
||||
/// <param name="setter"></param>
|
||||
static bool CopyOrDropWeight(int[] indexMap, int srcIndex, float weight, Action<int, float> setter)
|
||||
{
|
||||
if (srcIndex < 0 || srcIndex >= indexMap.Length)
|
||||
{
|
||||
// ありえるかどうかわからないが BoneWeight.boneIndexN に変な値が入っている.
|
||||
setter(0, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
var dstIndex = indexMap[srcIndex];
|
||||
if (dstIndex != -1)
|
||||
{
|
||||
// 有効なindex。weightをコピーする
|
||||
setter(dstIndex, weight);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 無効なindex。0でクリアする
|
||||
setter(0, 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BoneWeight[] src から新しいボーンウェイトを作成する。
|
||||
/// </summary>
|
||||
/// <param name="src">変更前のBoneWeight[]</param>
|
||||
/// <param name="boneMap">新旧のボーンの対応表。新しい方は無効なボーンが除去されてnullの部分がある</param>
|
||||
/// <param name="srcBones">変更前のボーン配列</param>
|
||||
/// <param name="dstBones">変更後のボーン配列。除去されたボーンがある場合、変更前より短い</param>
|
||||
/// <returns></returns>
|
||||
public static BoneWeight[] MapBoneWeight(BoneWeight[] src,
|
||||
Dictionary<Transform, Transform> boneMap,
|
||||
Transform[] srcBones,
|
||||
Transform[] dstBones
|
||||
)
|
||||
{
|
||||
// 処理前後の index の対応表を作成する
|
||||
var indexMap = new int[srcBones.Length];
|
||||
for (int i = 0; i < srcBones.Length; ++i)
|
||||
{
|
||||
var srcBone = srcBones[i];
|
||||
if (srcBone == null)
|
||||
{
|
||||
// 元のボーンが無い
|
||||
indexMap[i] = -1;
|
||||
Debug.LogWarningFormat("bones[{0}] is null", i);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (boneMap.TryGetValue(srcBone, out Transform dstBone))
|
||||
{
|
||||
// 対応するボーンが存在する
|
||||
var dstIndex = Array.IndexOf(dstBones, dstBone);
|
||||
if (dstIndex == -1)
|
||||
{
|
||||
// ありえない。バグ
|
||||
throw new Exception();
|
||||
}
|
||||
indexMap[i] = dstIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 先のボーンが無い
|
||||
indexMap[i] = -1;
|
||||
Debug.LogWarningFormat("{0} is removed", srcBone.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新しいBoneWeightを作成する
|
||||
var newBoneWeights = new BoneWeight[src.Length];
|
||||
for (int i = 0; i < src.Length; ++i)
|
||||
{
|
||||
BoneWeight srcBoneWeight = src[i];
|
||||
|
||||
// 0
|
||||
CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex0, srcBoneWeight.weight0, (newIndex, newWeight) =>
|
||||
{
|
||||
newBoneWeights[i].boneIndex0 = newIndex;
|
||||
newBoneWeights[i].weight0 = newWeight;
|
||||
});
|
||||
// 1
|
||||
CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex1, srcBoneWeight.weight1, (newIndex, newWeight) =>
|
||||
{
|
||||
newBoneWeights[i].boneIndex1 = newIndex;
|
||||
newBoneWeights[i].weight1 = newWeight;
|
||||
});
|
||||
// 2
|
||||
CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex2, srcBoneWeight.weight2, (newIndex, newWeight) =>
|
||||
{
|
||||
newBoneWeights[i].boneIndex2 = newIndex;
|
||||
newBoneWeights[i].weight2 = newWeight;
|
||||
});
|
||||
// 3
|
||||
CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex3, srcBoneWeight.weight3, (newIndex, newWeight) =>
|
||||
{
|
||||
newBoneWeights[i].boneIndex3 = newIndex;
|
||||
newBoneWeights[i].weight3 = newWeight;
|
||||
});
|
||||
}
|
||||
|
||||
return newBoneWeights;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// srcのSkinnedMeshRendererを正規化して、dstにアタッチする
|
||||
/// </summary>
|
||||
/// <param name="src">正規化前のSkinnedMeshRendererのTransform</param>
|
||||
/// <param name="dst">正規化後のSkinnedMeshRendererのTransform</param>
|
||||
/// <param name="boneMap">正規化前のボーンから正規化後のボーンを得る</param>
|
||||
static void NormalizeSkinnedMesh(Transform src, Transform dst, Dictionary<Transform, Transform> boneMap)
|
||||
{
|
||||
var srcRenderer = src.GetComponent<SkinnedMeshRenderer>();
|
||||
if (srcRenderer == null
|
||||
|| !srcRenderer.enabled
|
||||
|| srcRenderer.sharedMesh == null
|
||||
|| srcRenderer.sharedMesh.vertexCount == 0)
|
||||
{
|
||||
// 有効なSkinnedMeshRendererが無かった
|
||||
return;
|
||||
}
|
||||
|
||||
var srcMesh = srcRenderer.sharedMesh;
|
||||
var originalSrcMesh = srcMesh;
|
||||
|
||||
// 元の Transform[] bones から、無効なboneを取り除いて前に詰めた配列を作る
|
||||
var dstBones = srcRenderer.bones
|
||||
.Where(x => x != null && boneMap.ContainsKey(x))
|
||||
.Select(x => boneMap[x])
|
||||
.ToArray();
|
||||
|
||||
var hasBoneWeight = srcRenderer.bones != null && srcRenderer.bones.Length > 0;
|
||||
if (!hasBoneWeight)
|
||||
{
|
||||
// Before bake, bind no weight bones
|
||||
//Debug.LogFormat("no weight: {0}", srcMesh.name);
|
||||
|
||||
srcMesh = srcMesh.Copy(true);
|
||||
var bw = new BoneWeight
|
||||
{
|
||||
boneIndex0 = 0,
|
||||
boneIndex1 = 0,
|
||||
boneIndex2 = 0,
|
||||
boneIndex3 = 0,
|
||||
weight0 = 1.0f,
|
||||
weight1 = 0.0f,
|
||||
weight2 = 0.0f,
|
||||
weight3 = 0.0f,
|
||||
};
|
||||
srcMesh.boneWeights = Enumerable.Range(0, srcMesh.vertexCount).Select(x => bw).ToArray();
|
||||
srcMesh.bindposes = new Matrix4x4[] { Matrix4x4.identity };
|
||||
|
||||
srcRenderer.rootBone = srcRenderer.transform;
|
||||
dstBones = new[] { boneMap[srcRenderer.transform] };
|
||||
srcRenderer.bones = new[] { srcRenderer.transform };
|
||||
srcRenderer.sharedMesh = srcMesh;
|
||||
}
|
||||
|
||||
// BakeMesh
|
||||
var mesh = srcMesh.Copy(false);
|
||||
mesh.name = srcMesh.name + ".baked";
|
||||
srcRenderer.BakeMesh(mesh);
|
||||
|
||||
var blendShapeValues = new Dictionary<int, float>();
|
||||
for (int i = 0; i < srcMesh.blendShapeCount; i++)
|
||||
{
|
||||
var val = srcRenderer.GetBlendShapeWeight(i);
|
||||
if (val > 0) blendShapeValues.Add(i, val);
|
||||
}
|
||||
|
||||
// 新しい骨格のボーンウェイトを作成する
|
||||
mesh.boneWeights = MapBoneWeight(srcMesh.boneWeights, boneMap, srcRenderer.bones, dstBones);
|
||||
|
||||
// recalc bindposes
|
||||
mesh.bindposes = dstBones.Select(x => x.worldToLocalMatrix * dst.transform.localToWorldMatrix).ToArray();
|
||||
|
||||
//var m = src.localToWorldMatrix; // include scaling
|
||||
var m = default(Matrix4x4);
|
||||
m.SetTRS(Vector3.zero, src.rotation, Vector3.one); // rotation only
|
||||
mesh.ApplyMatrix(m);
|
||||
|
||||
//
|
||||
// BlendShapes
|
||||
//
|
||||
|
||||
// clear blendShape always
|
||||
var backcup = new List<float>();
|
||||
for (int i = 0; i < srcMesh.blendShapeCount; ++i)
|
||||
{
|
||||
backcup.Add(srcRenderer.GetBlendShapeWeight(i));
|
||||
srcRenderer.SetBlendShapeWeight(i, 0);
|
||||
}
|
||||
|
||||
var meshVertices = mesh.vertices;
|
||||
var meshNormals = mesh.normals;
|
||||
var meshTangents = Array.Empty<Vector3>();
|
||||
if (Symbols.VRM_NORMALIZE_BLENDSHAPE_TANGENT)
|
||||
{
|
||||
meshTangents = mesh.tangents.Select(x => (Vector3)x).ToArray();
|
||||
}
|
||||
|
||||
var originalBlendShapePositions = new Vector3[meshVertices.Length];
|
||||
var originalBlendShapeNormals = new Vector3[meshVertices.Length];
|
||||
var originalBlendShapeTangents = new Vector3[meshVertices.Length];
|
||||
|
||||
var report = new BlendShapeReport(srcMesh);
|
||||
var blendShapeMesh = new Mesh();
|
||||
for (int i = 0; i < srcMesh.blendShapeCount; ++i)
|
||||
{
|
||||
// check blendShape
|
||||
srcRenderer.sharedMesh.GetBlendShapeFrameVertices(i, 0, originalBlendShapePositions, originalBlendShapeNormals, originalBlendShapeTangents);
|
||||
var hasVertices = originalBlendShapePositions.Count(x => x != Vector3.zero);
|
||||
var hasNormals = originalBlendShapeNormals.Count(x => x != Vector3.zero);
|
||||
var hasTangents = 0;
|
||||
if (Symbols.VRM_NORMALIZE_BLENDSHAPE_TANGENT)
|
||||
{
|
||||
hasTangents = originalBlendShapeTangents.Count(x => x != Vector3.zero);
|
||||
}
|
||||
var name = srcMesh.GetBlendShapeName(i);
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = String.Format("{0}", i);
|
||||
}
|
||||
|
||||
report.SetCount(i, name, hasVertices, hasNormals, hasTangents);
|
||||
|
||||
srcRenderer.SetBlendShapeWeight(i, 100.0f);
|
||||
srcRenderer.BakeMesh(blendShapeMesh);
|
||||
if (blendShapeMesh.vertices.Length != mesh.vertices.Length)
|
||||
{
|
||||
throw new Exception("different vertex count");
|
||||
}
|
||||
|
||||
var value = blendShapeValues.ContainsKey(i) ? blendShapeValues[i] : 0;
|
||||
srcRenderer.SetBlendShapeWeight(i, value);
|
||||
|
||||
Vector3[] vertices = blendShapeMesh.vertices;
|
||||
|
||||
for (int j = 0; j < vertices.Length; ++j)
|
||||
{
|
||||
if (originalBlendShapePositions[j] == Vector3.zero)
|
||||
{
|
||||
vertices[j] = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices[j] = m.MultiplyPoint(vertices[j]) - meshVertices[j];
|
||||
}
|
||||
}
|
||||
|
||||
Vector3[] normals = blendShapeMesh.normals;
|
||||
for (int j = 0; j < normals.Length; ++j)
|
||||
{
|
||||
if (originalBlendShapeNormals[j] == Vector3.zero)
|
||||
{
|
||||
normals[j] = Vector3.zero;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
normals[j] = m.MultiplyVector(normals[j].normalized) - meshNormals[j];
|
||||
}
|
||||
}
|
||||
|
||||
Vector3[] tangents = blendShapeMesh.tangents.Select(x => (Vector3)x).ToArray();
|
||||
if (Symbols.VRM_NORMALIZE_BLENDSHAPE_TANGENT)
|
||||
{
|
||||
for (int j = 0; j < tangents.Length; ++j)
|
||||
{
|
||||
if (originalBlendShapeTangents[j] == Vector3.zero)
|
||||
{
|
||||
tangents[j] = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
tangents[j] = m.MultiplyVector(tangents[j]) - meshTangents[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var frameCount = srcMesh.GetBlendShapeFrameCount(i);
|
||||
for (int f = 0; f < frameCount; f++)
|
||||
{
|
||||
|
||||
var weight = srcMesh.GetBlendShapeFrameWeight(i, f);
|
||||
|
||||
try
|
||||
{
|
||||
mesh.AddBlendShapeFrame(name,
|
||||
weight,
|
||||
vertices,
|
||||
hasNormals > 0 ? normals : null,
|
||||
hasTangents > 0 ? tangents : null
|
||||
);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Debug.LogErrorFormat("fail to mesh.AddBlendShapeFrame {0}.{1}",
|
||||
mesh.name,
|
||||
srcMesh.GetBlendShapeName(i)
|
||||
);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (report.Count > 0)
|
||||
{
|
||||
Debug.LogFormat("{0}", report.ToString());
|
||||
}
|
||||
|
||||
var dstRenderer = dst.gameObject.AddComponent<SkinnedMeshRenderer>();
|
||||
dstRenderer.sharedMaterials = srcRenderer.sharedMaterials;
|
||||
if (srcRenderer.rootBone != null)
|
||||
{
|
||||
if (boneMap.TryGetValue(srcRenderer.rootBone, out Transform found))
|
||||
{
|
||||
dstRenderer.rootBone = found;
|
||||
}
|
||||
}
|
||||
dstRenderer.bones = dstBones;
|
||||
dstRenderer.sharedMesh = mesh;
|
||||
|
||||
if (!hasBoneWeight)
|
||||
{
|
||||
// restore bones
|
||||
srcRenderer.bones = new Transform[] { };
|
||||
srcRenderer.sharedMesh = originalSrcMesh;
|
||||
}
|
||||
// restore blendshape weights
|
||||
for (int i = 0; i < backcup.Count; ++i)
|
||||
{
|
||||
srcRenderer.SetBlendShapeWeight(i, backcup[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <param name="dst"></param>
|
||||
static void NormalizeNoneSkinnedMesh(Transform src, Transform dst)
|
||||
{
|
||||
var srcFilter = src.GetComponent<MeshFilter>();
|
||||
if (srcFilter == null
|
||||
|| srcFilter.sharedMesh == null
|
||||
|| srcFilter.sharedMesh.vertexCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var srcRenderer = src.GetComponent<MeshRenderer>();
|
||||
if (srcRenderer == null || !srcRenderer.enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Meshに乗っているボーンの姿勢を適用する
|
||||
var dstFilter = dst.gameObject.AddComponent<MeshFilter>();
|
||||
|
||||
var dstMesh = srcFilter.sharedMesh.Copy(false);
|
||||
dstMesh.ApplyRotationAndScale(src.localToWorldMatrix);
|
||||
dstFilter.sharedMesh = dstMesh;
|
||||
|
||||
// Materialをコピー
|
||||
var dstRenderer = dst.gameObject.AddComponent<MeshRenderer>();
|
||||
dstRenderer.sharedMaterials = srcRenderer.sharedMaterials;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回転とスケールを除去したヒエラルキーのコピーを作成する(MeshをBakeする)
|
||||
|
|
@ -506,12 +78,16 @@ namespace UniGLTF.MeshUtility
|
|||
/// <param name="bakeCurrentBlendShape">BlendShapeを0クリアするか否か。false の場合 BlendShape の現状を Bake する</param>
|
||||
/// <param name="createAvatar">Avatarを作る関数</param>
|
||||
/// <returns></returns>
|
||||
public static (GameObject, Dictionary<Transform, Transform>) Execute(GameObject go)
|
||||
public static (GameObject, Dictionary<Transform, Transform>) NormalizeHierarchyFreezeMesh(GameObject go,
|
||||
bool removeScaling = true,
|
||||
bool removeRotation = true,
|
||||
bool freezeBlendShape = true
|
||||
)
|
||||
{
|
||||
//
|
||||
// 正規化されたヒエラルキーを作る
|
||||
//
|
||||
var (normalized, boneMap) = CreateNormalizedHierarchy(go);
|
||||
var (normalized, boneMap) = CreateNormalizedHierarchy(go, removeScaling, removeRotation);
|
||||
|
||||
//
|
||||
// 各メッシュから回転・スケールを取り除いてBinding行列を再計算する
|
||||
|
|
@ -524,9 +100,46 @@ namespace UniGLTF.MeshUtility
|
|||
continue;
|
||||
}
|
||||
|
||||
NormalizeSkinnedMesh(src, dst, boneMap);
|
||||
{
|
||||
// SkinnedMeshRenderer
|
||||
var srcRenderer = src.GetComponent<SkinnedMeshRenderer>();
|
||||
var (mesh, dstBones) = MeshFreezer.NormalizeSkinnedMesh(
|
||||
srcRenderer,
|
||||
boneMap,
|
||||
dst.localToWorldMatrix,
|
||||
freezeBlendShape);
|
||||
if (mesh != null)
|
||||
{
|
||||
var dstRenderer = dst.gameObject.AddComponent<SkinnedMeshRenderer>();
|
||||
dstRenderer.sharedMaterials = srcRenderer.sharedMaterials;
|
||||
if (srcRenderer.rootBone != null)
|
||||
{
|
||||
if (boneMap.TryGetValue(srcRenderer.rootBone, out Transform found))
|
||||
{
|
||||
dstRenderer.rootBone = found;
|
||||
}
|
||||
}
|
||||
dstRenderer.bones = dstBones;
|
||||
dstRenderer.sharedMesh = mesh;
|
||||
}
|
||||
}
|
||||
|
||||
NormalizeNoneSkinnedMesh(src, dst);
|
||||
{
|
||||
// MeshRenderer
|
||||
var srcRenderer = src.GetComponent<MeshRenderer>();
|
||||
if (srcRenderer != null)
|
||||
{
|
||||
var dstMesh = MeshFreezer.NormalizeNoneSkinnedMesh(srcRenderer);
|
||||
if (dstMesh != null)
|
||||
{
|
||||
var dstFilter = dst.gameObject.AddComponent<MeshFilter>();
|
||||
dstFilter.sharedMesh = dstMesh;
|
||||
// Materialをコピー
|
||||
var dstRenderer = dst.gameObject.AddComponent<MeshRenderer>();
|
||||
dstRenderer.sharedMaterials = srcRenderer.sharedMaterials;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (normalized, boneMap);
|
||||
|
|
|
|||
391
Assets/UniGLTF/Runtime/MeshUtility/MeshFreezer.cs
Normal file
391
Assets/UniGLTF/Runtime/MeshUtility/MeshFreezer.cs
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using VRMShaders;
|
||||
|
||||
namespace UniGLTF.MeshUtility
|
||||
{
|
||||
public static class MeshFreezer
|
||||
{
|
||||
/// <summary>
|
||||
/// index が 有効であれば、setter に weight を渡す。無効であれば setter に 0 を渡す。
|
||||
/// </summary>
|
||||
/// <param name="indexMap"></param>
|
||||
/// <param name="srcIndex"></param>
|
||||
/// <param name="weight"></param>
|
||||
/// <param name="setter"></param>
|
||||
static bool CopyOrDropWeight(int[] indexMap, int srcIndex, float weight, Action<int, float> setter)
|
||||
{
|
||||
if (srcIndex < 0 || srcIndex >= indexMap.Length)
|
||||
{
|
||||
// ありえるかどうかわからないが BoneWeight.boneIndexN に変な値が入っている.
|
||||
setter(0, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
var dstIndex = indexMap[srcIndex];
|
||||
if (dstIndex != -1)
|
||||
{
|
||||
// 有効なindex。weightをコピーする
|
||||
setter(dstIndex, weight);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 無効なindex。0でクリアする
|
||||
setter(0, 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BoneWeight[] src から新しいボーンウェイトを作成する。
|
||||
/// </summary>
|
||||
/// <param name="src">変更前のBoneWeight[]</param>
|
||||
/// <param name="boneMap">新旧のボーンの対応表。新しい方は無効なボーンが除去されてnullの部分がある</param>
|
||||
/// <param name="srcBones">変更前のボーン配列</param>
|
||||
/// <param name="dstBones">変更後のボーン配列。除去されたボーンがある場合、変更前より短い</param>
|
||||
/// <returns></returns>
|
||||
public static BoneWeight[] MapBoneWeight(BoneWeight[] src,
|
||||
Dictionary<Transform, Transform> boneMap,
|
||||
Transform[] srcBones,
|
||||
Transform[] dstBones
|
||||
)
|
||||
{
|
||||
// 処理前後の index の対応表を作成する
|
||||
var indexMap = new int[srcBones.Length];
|
||||
for (int i = 0; i < srcBones.Length; ++i)
|
||||
{
|
||||
var srcBone = srcBones[i];
|
||||
if (srcBone == null)
|
||||
{
|
||||
// 元のボーンが無い
|
||||
indexMap[i] = -1;
|
||||
Debug.LogWarningFormat("bones[{0}] is null", i);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (boneMap.TryGetValue(srcBone, out Transform dstBone))
|
||||
{
|
||||
// 対応するボーンが存在する
|
||||
var dstIndex = Array.IndexOf(dstBones, dstBone);
|
||||
if (dstIndex == -1)
|
||||
{
|
||||
// ありえない。バグ
|
||||
throw new Exception();
|
||||
}
|
||||
indexMap[i] = dstIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 先のボーンが無い
|
||||
indexMap[i] = -1;
|
||||
Debug.LogWarningFormat("{0} is removed", srcBone.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新しいBoneWeightを作成する
|
||||
var newBoneWeights = new BoneWeight[src.Length];
|
||||
for (int i = 0; i < src.Length; ++i)
|
||||
{
|
||||
BoneWeight srcBoneWeight = src[i];
|
||||
|
||||
// 0
|
||||
CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex0, srcBoneWeight.weight0, (newIndex, newWeight) =>
|
||||
{
|
||||
newBoneWeights[i].boneIndex0 = newIndex;
|
||||
newBoneWeights[i].weight0 = newWeight;
|
||||
});
|
||||
// 1
|
||||
CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex1, srcBoneWeight.weight1, (newIndex, newWeight) =>
|
||||
{
|
||||
newBoneWeights[i].boneIndex1 = newIndex;
|
||||
newBoneWeights[i].weight1 = newWeight;
|
||||
});
|
||||
// 2
|
||||
CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex2, srcBoneWeight.weight2, (newIndex, newWeight) =>
|
||||
{
|
||||
newBoneWeights[i].boneIndex2 = newIndex;
|
||||
newBoneWeights[i].weight2 = newWeight;
|
||||
});
|
||||
// 3
|
||||
CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex3, srcBoneWeight.weight3, (newIndex, newWeight) =>
|
||||
{
|
||||
newBoneWeights[i].boneIndex3 = newIndex;
|
||||
newBoneWeights[i].weight3 = newWeight;
|
||||
});
|
||||
}
|
||||
|
||||
return newBoneWeights;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <param name="boneMap">正規化前のボーンから正規化後のボーンを得る</param>
|
||||
/// <param name="dstLocalToWorldMatrix"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static (Mesh, Transform[]) NormalizeSkinnedMesh(
|
||||
SkinnedMeshRenderer src,
|
||||
Dictionary<Transform, Transform> boneMap,
|
||||
Matrix4x4 dstLocalToWorldMatrix,
|
||||
bool FreezeBlendShape = true)
|
||||
{
|
||||
if (src == null
|
||||
|| !src.enabled
|
||||
|| src.sharedMesh == null
|
||||
|| src.sharedMesh.vertexCount == 0)
|
||||
{
|
||||
// 有効なSkinnedMeshRendererが無かった
|
||||
return default;
|
||||
}
|
||||
|
||||
var srcMesh = src.sharedMesh;
|
||||
var originalSrcMesh = srcMesh;
|
||||
|
||||
// 元の Transform[] bones から、無効なboneを取り除いて前に詰めた配列を作る
|
||||
var dstBones = src.bones
|
||||
.Where(x => x != null && boneMap.ContainsKey(x))
|
||||
.Select(x => boneMap[x])
|
||||
.ToArray();
|
||||
|
||||
var hasBoneWeight = src.bones != null && src.bones.Length > 0;
|
||||
if (!hasBoneWeight)
|
||||
{
|
||||
// Before bake, bind no weight bones
|
||||
//Debug.LogFormat("no weight: {0}", srcMesh.name);
|
||||
|
||||
srcMesh = srcMesh.Copy(true);
|
||||
var bw = new BoneWeight
|
||||
{
|
||||
boneIndex0 = 0,
|
||||
boneIndex1 = 0,
|
||||
boneIndex2 = 0,
|
||||
boneIndex3 = 0,
|
||||
weight0 = 1.0f,
|
||||
weight1 = 0.0f,
|
||||
weight2 = 0.0f,
|
||||
weight3 = 0.0f,
|
||||
};
|
||||
srcMesh.boneWeights = Enumerable.Range(0, srcMesh.vertexCount).Select(x => bw).ToArray();
|
||||
srcMesh.bindposes = new Matrix4x4[] { Matrix4x4.identity };
|
||||
|
||||
src.rootBone = src.transform;
|
||||
dstBones = new[] { boneMap[src.transform] };
|
||||
src.bones = new[] { src.transform };
|
||||
src.sharedMesh = srcMesh;
|
||||
}
|
||||
|
||||
var blendShapeBackup = new List<float>();
|
||||
if (!FreezeBlendShape)
|
||||
{
|
||||
for (int i = 0; i < srcMesh.blendShapeCount; ++i)
|
||||
{
|
||||
blendShapeBackup.Add(src.GetBlendShapeWeight(i));
|
||||
src.SetBlendShapeWeight(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// BakeMesh
|
||||
var mesh = srcMesh.Copy(false);
|
||||
mesh.name = srcMesh.name + ".baked";
|
||||
src.BakeMesh(mesh);
|
||||
|
||||
// 新しい骨格のボーンウェイトを作成する
|
||||
mesh.boneWeights = MapBoneWeight(srcMesh.boneWeights, boneMap, src.bones, dstBones);
|
||||
|
||||
// recalc bindposes
|
||||
mesh.bindposes = dstBones.Select(x => x.worldToLocalMatrix * dstLocalToWorldMatrix).ToArray();
|
||||
|
||||
//var m = src.localToWorldMatrix; // include scaling
|
||||
var m = default(Matrix4x4);
|
||||
m.SetTRS(Vector3.zero, src.transform.rotation, Vector3.one); // rotation only
|
||||
mesh.ApplyMatrix(m);
|
||||
|
||||
//
|
||||
// BlendShapes
|
||||
//
|
||||
CopyBlendShapes(src, srcMesh, mesh, m);
|
||||
|
||||
for (int i = 0; i < blendShapeBackup.Count; ++i)
|
||||
{
|
||||
src.SetBlendShapeWeight(i, blendShapeBackup[i]);
|
||||
}
|
||||
|
||||
if (!hasBoneWeight)
|
||||
{
|
||||
// restore bones
|
||||
src.bones = new Transform[] { };
|
||||
src.sharedMesh = originalSrcMesh;
|
||||
}
|
||||
|
||||
return (mesh, dstBones);
|
||||
}
|
||||
|
||||
private static void CopyBlendShapes(SkinnedMeshRenderer src, Mesh srcMesh, Mesh mesh, Matrix4x4 m)
|
||||
{
|
||||
var blendShapeValues = new Dictionary<int, float>();
|
||||
for (int i = 0; i < srcMesh.blendShapeCount; i++)
|
||||
{
|
||||
var val = src.GetBlendShapeWeight(i);
|
||||
if (val > 0) blendShapeValues.Add(i, val);
|
||||
}
|
||||
|
||||
// clear blendShape always
|
||||
var backcup = new List<float>();
|
||||
for (int i = 0; i < srcMesh.blendShapeCount; ++i)
|
||||
{
|
||||
backcup.Add(src.GetBlendShapeWeight(i));
|
||||
src.SetBlendShapeWeight(i, 0);
|
||||
}
|
||||
|
||||
var meshVertices = mesh.vertices;
|
||||
var meshNormals = mesh.normals;
|
||||
var meshTangents = Array.Empty<Vector3>();
|
||||
if (Symbols.VRM_NORMALIZE_BLENDSHAPE_TANGENT)
|
||||
{
|
||||
meshTangents = mesh.tangents.Select(x => (Vector3)x).ToArray();
|
||||
}
|
||||
|
||||
var originalBlendShapePositions = new Vector3[meshVertices.Length];
|
||||
var originalBlendShapeNormals = new Vector3[meshVertices.Length];
|
||||
var originalBlendShapeTangents = new Vector3[meshVertices.Length];
|
||||
|
||||
var report = new BlendShapeReport(srcMesh);
|
||||
var blendShapeMesh = new Mesh();
|
||||
for (int i = 0; i < srcMesh.blendShapeCount; ++i)
|
||||
{
|
||||
// check blendShape
|
||||
src.sharedMesh.GetBlendShapeFrameVertices(i, 0, originalBlendShapePositions, originalBlendShapeNormals, originalBlendShapeTangents);
|
||||
var hasVertices = originalBlendShapePositions.Count(x => x != Vector3.zero);
|
||||
var hasNormals = originalBlendShapeNormals.Count(x => x != Vector3.zero);
|
||||
var hasTangents = 0;
|
||||
if (Symbols.VRM_NORMALIZE_BLENDSHAPE_TANGENT)
|
||||
{
|
||||
hasTangents = originalBlendShapeTangents.Count(x => x != Vector3.zero);
|
||||
}
|
||||
var name = srcMesh.GetBlendShapeName(i);
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = String.Format("{0}", i);
|
||||
}
|
||||
|
||||
report.SetCount(i, name, hasVertices, hasNormals, hasTangents);
|
||||
|
||||
src.SetBlendShapeWeight(i, 100.0f);
|
||||
src.BakeMesh(blendShapeMesh);
|
||||
if (blendShapeMesh.vertices.Length != mesh.vertices.Length)
|
||||
{
|
||||
throw new Exception("different vertex count");
|
||||
}
|
||||
|
||||
var value = blendShapeValues.ContainsKey(i) ? blendShapeValues[i] : 0;
|
||||
src.SetBlendShapeWeight(i, value);
|
||||
|
||||
Vector3[] vertices = blendShapeMesh.vertices;
|
||||
|
||||
for (int j = 0; j < vertices.Length; ++j)
|
||||
{
|
||||
if (originalBlendShapePositions[j] == Vector3.zero)
|
||||
{
|
||||
vertices[j] = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices[j] = m.MultiplyPoint(vertices[j]) - meshVertices[j];
|
||||
}
|
||||
}
|
||||
|
||||
Vector3[] normals = blendShapeMesh.normals;
|
||||
for (int j = 0; j < normals.Length; ++j)
|
||||
{
|
||||
if (originalBlendShapeNormals[j] == Vector3.zero)
|
||||
{
|
||||
normals[j] = Vector3.zero;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
normals[j] = m.MultiplyVector(normals[j].normalized) - meshNormals[j];
|
||||
}
|
||||
}
|
||||
|
||||
Vector3[] tangents = blendShapeMesh.tangents.Select(x => (Vector3)x).ToArray();
|
||||
if (Symbols.VRM_NORMALIZE_BLENDSHAPE_TANGENT)
|
||||
{
|
||||
for (int j = 0; j < tangents.Length; ++j)
|
||||
{
|
||||
if (originalBlendShapeTangents[j] == Vector3.zero)
|
||||
{
|
||||
tangents[j] = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
tangents[j] = m.MultiplyVector(tangents[j]) - meshTangents[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var frameCount = srcMesh.GetBlendShapeFrameCount(i);
|
||||
for (int f = 0; f < frameCount; f++)
|
||||
{
|
||||
|
||||
var weight = srcMesh.GetBlendShapeFrameWeight(i, f);
|
||||
|
||||
try
|
||||
{
|
||||
mesh.AddBlendShapeFrame(name,
|
||||
weight,
|
||||
vertices,
|
||||
hasNormals > 0 ? normals : null,
|
||||
hasTangents > 0 ? tangents : null
|
||||
);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Debug.LogErrorFormat("fail to mesh.AddBlendShapeFrame {0}.{1}",
|
||||
mesh.name,
|
||||
srcMesh.GetBlendShapeName(i)
|
||||
);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (report.Count > 0)
|
||||
{
|
||||
Debug.LogFormat("{0}", report.ToString());
|
||||
}
|
||||
// restore blendshape weights
|
||||
for (int i = 0; i < backcup.Count; ++i)
|
||||
{
|
||||
src.SetBlendShapeWeight(i, backcup[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static Mesh NormalizeNoneSkinnedMesh(MeshRenderer srcRenderer)
|
||||
{
|
||||
if (srcRenderer == null || !srcRenderer.enabled)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var srcFilter = srcRenderer.GetComponent<MeshFilter>();
|
||||
if (srcFilter == null
|
||||
|| srcFilter.sharedMesh == null
|
||||
|| srcFilter.sharedMesh.vertexCount == 0)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var dstMesh = srcFilter.sharedMesh.Copy(false);
|
||||
// Meshに乗っているボーンの姿勢を適用する
|
||||
dstMesh.ApplyRotationAndScale(srcRenderer.transform.localToWorldMatrix);
|
||||
return dstMesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Runtime/MeshUtility/MeshFreezer.cs.meta
Normal file
11
Assets/UniGLTF/Runtime/MeshUtility/MeshFreezer.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 09547456fd06d7b419d7553beba6f58a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -273,12 +273,16 @@ namespace UniHumanoid
|
|||
}
|
||||
#endif
|
||||
|
||||
public static void AddAnimator(GameObject _src,
|
||||
public static Avatar CreateAvatarForCopyHierarchy(
|
||||
Animator src,
|
||||
GameObject dst,
|
||||
IDictionary<Transform, Transform> boneMap,
|
||||
Action<AvatarDescription> modAvatarDesc = null)
|
||||
{
|
||||
var src = _src.GetComponent<Animator>();
|
||||
if (src == null)
|
||||
{
|
||||
throw new ArgumentNullException("src");
|
||||
}
|
||||
|
||||
var srcHumanBones = CachedEnum.GetValues<HumanBodyBones>()
|
||||
.Where(x => x != HumanBodyBones.LastBone)
|
||||
|
|
@ -292,12 +296,6 @@ namespace UniHumanoid
|
|||
.ToDictionary(x => x.Key, x => boneMap[x.Value])
|
||||
;
|
||||
|
||||
var animator = dst.AddComponent<Animator>();
|
||||
if (animator == null)
|
||||
{
|
||||
animator = dst.AddComponent<Animator>();
|
||||
}
|
||||
|
||||
var avatarDescription = UniHumanoid.AvatarDescription.Create();
|
||||
if (modAvatarDesc != null)
|
||||
{
|
||||
|
|
@ -305,9 +303,8 @@ namespace UniHumanoid
|
|||
}
|
||||
avatarDescription.SetHumanBones(map);
|
||||
var avatar = avatarDescription.CreateAvatar(dst.transform);
|
||||
|
||||
avatar.name = dst.name;
|
||||
animator.avatar = avatar;
|
||||
avatar.name = "created";
|
||||
return avatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniGLTF;
|
||||
using UniGLTF.MeshUtility;
|
||||
using UniGLTF.Utils;
|
||||
using UniHumanoid;
|
||||
|
|
@ -66,10 +67,11 @@ namespace VRM
|
|||
}
|
||||
|
||||
// 正規化されたヒエラルキーを作る
|
||||
var (normalized, bMap) = BoneNormalizer.Execute(go);
|
||||
var (normalized, bMap) = BoneNormalizer.NormalizeHierarchyFreezeMesh(go);
|
||||
|
||||
// 新しいヒエラルキーからAvatarを作る
|
||||
UniHumanoid.AvatarDescription.AddAnimator(go, normalized, bMap, avatarDescription =>
|
||||
var newAvatar = UniHumanoid.AvatarDescription.CreateAvatarForCopyHierarchy(
|
||||
go.GetComponent<Animator>(), normalized, bMap, avatarDescription =>
|
||||
{
|
||||
var vrmHuman = go.GetComponent<VRMHumanoidDescription>();
|
||||
if (vrmHuman != null && vrmHuman.Description != null)
|
||||
|
|
@ -84,6 +86,8 @@ namespace VRM
|
|||
avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF;
|
||||
}
|
||||
});
|
||||
var newAnimator = normalized.GetOrAddComponent<Animator>();
|
||||
newAnimator.avatar = newAvatar;
|
||||
|
||||
CopyVRMComponents(go, normalized, bMap);
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace VRM
|
|||
map.Add(null, new GameObject("null"));
|
||||
// map.Add(new GameObject("c"), null); // ありえないので Exception にしてある
|
||||
var boneWeights = map.CreateBoneWeight(64).ToArray();
|
||||
var newBoneWeight = BoneNormalizer.MapBoneWeight(boneWeights, map.Map,
|
||||
var newBoneWeight = MeshFreezer.MapBoneWeight(boneWeights, map.Map,
|
||||
map.SrcBones.ToArray(), map.DstBones.ToArray());
|
||||
|
||||
// 正常系
|
||||
|
|
@ -75,7 +75,7 @@ namespace VRM
|
|||
map.Add(null, new GameObject("null"));
|
||||
// map.Add(new GameObject("c"), null); // ありえないので Exception にしてある
|
||||
var boneWeights = map.CreateBoneWeight(64).ToArray();
|
||||
var newBoneWeight = BoneNormalizer.MapBoneWeight(boneWeights, map.Map,
|
||||
var newBoneWeight = MeshFreezer.MapBoneWeight(boneWeights, map.Map,
|
||||
map.SrcBones.ToArray(), map.DstBones.ToArray());
|
||||
|
||||
// 4 つめが 0 になる
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ namespace UniVRM10
|
|||
{
|
||||
case Tabs.MeshFreeze:
|
||||
{
|
||||
if (MeshBakeGui())
|
||||
if (MeshFreezeGui())
|
||||
{
|
||||
modified = true;
|
||||
}
|
||||
|
|
@ -135,12 +135,13 @@ namespace UniVRM10
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MeshBakeGui()
|
||||
bool MeshFreezeGui()
|
||||
{
|
||||
var forceUniqueName = ToggleIsModified("ForceUniqueName", ref _meshUtility.ForceUniqueName);
|
||||
var blendShape = ToggleIsModified("BlendShape", ref _meshUtility.FreezeBlendShape);
|
||||
var scale = ToggleIsModified("Scale", ref _meshUtility.FreezeScaling);
|
||||
var rotation = ToggleIsModified("Rotation", ref _meshUtility.FreezeRotation);
|
||||
return blendShape || scale || rotation;
|
||||
return forceUniqueName || blendShape || scale || rotation;
|
||||
}
|
||||
|
||||
bool MeshIntegrateGui()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniGLTF.MeshUtility;
|
||||
|
|
@ -17,6 +18,12 @@ namespace UniVRM10
|
|||
/// </summary>
|
||||
public class Vrm10MeshUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// GameObject 名が重複している場合にリネームする。
|
||||
/// 最初に実行(Avatar生成時のエラーを回避?)
|
||||
/// </summary>
|
||||
public bool ForceUniqueName = false;
|
||||
|
||||
/// <summary>
|
||||
/// Same as VRM-0 normalization
|
||||
/// - Mesh
|
||||
|
|
@ -124,18 +131,37 @@ namespace UniVRM10
|
|||
{
|
||||
// TODO: UNDO
|
||||
|
||||
if (ForceUniqueName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// 正規化されたヒエラルキーを作る
|
||||
var (normalized, boneMap) = BoneNormalizer.CreateNormalizedHierarchy(go,
|
||||
var (normalized, boneMap) = BoneNormalizer.NormalizeHierarchyFreezeMesh(go,
|
||||
removeScaling: FreezeScaling,
|
||||
removeRotation: FreezeRotation);
|
||||
removeRotation: FreezeRotation,
|
||||
freezeBlendShape: FreezeBlendShape);
|
||||
|
||||
// TODO: update: spring
|
||||
// TODO: update: constraint
|
||||
// TODO: update: firstPoint offset
|
||||
|
||||
AvatarDescription.AddAnimator(go, normalized, boneMap);
|
||||
var newAvatar = AvatarDescription.CreateAvatarForCopyHierarchy(go.GetComponent<Animator>(), normalized, boneMap);
|
||||
var newAnimator = normalized.GetOrAddComponent<Animator>();
|
||||
newAnimator.avatar = newAvatar;
|
||||
|
||||
// TODO: write back normalized transform to boneMap
|
||||
|
||||
// TODO: integration
|
||||
foreach (var group in MeshIntegrationGroups)
|
||||
{
|
||||
foreach (var renderer in group.Renderers)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: split
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user