bake mesh inplace

This commit is contained in:
ousttrue
2023-11-27 20:05:01 +09:00
parent 0f751d0892
commit 4ec5d54588
5 changed files with 63 additions and 184 deletions

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF.Utils;
using UnityEngine;
@@ -8,97 +9,20 @@ namespace UniGLTF.MeshUtility
{
public static class BoneNormalizer
{
public static (GameObject, Dictionary<Transform, Transform>) CreateNormalizedHierarchy(GameObject go,
bool removeScaling = true,
bool removeRotation = true)
private static MeshAttachInfo CreateMeshInfo(Transform src)
{
var boneMap = new Dictionary<Transform, Transform>();
var normalized = new GameObject(go.name + "(normalized)");
normalized.transform.position = go.transform.position;
if (removeScaling && removeRotation)
{
RemoveScaleAndRotationRecursive(go.transform, normalized.transform, boneMap);
}
else if (removeScaling)
{
RemoveScaleAndRotationRecursive(go.transform, normalized.transform, boneMap);
}
else if (removeRotation)
{
throw new NotImplementedException();
}
else
{
throw new ArgumentNullException();
}
return (normalized, boneMap);
}
static void RemoveScaleRecursive(Transform src, Transform dst, Dictionary<Transform, Transform> boneMap)
{
boneMap[src] = dst;
foreach (Transform child in src)
{
if (child.gameObject.activeSelf)
{
var dstChild = new GameObject(child.name);
dstChild.transform.SetParent(dst);
dstChild.transform.position = child.position; // copy world position
dstChild.transform.rotation = child.localToWorldMatrix.rotation; // copy world rotation
// scale is removed
RemoveScaleRecursive(child, dstChild.transform, boneMap);
}
}
}
static void RemoveScaleAndRotationRecursive(Transform src, Transform dst, Dictionary<Transform, Transform> boneMap)
{
boneMap[src] = dst;
foreach (Transform child in src)
{
if (child.gameObject.activeSelf)
{
var dstChild = new GameObject(child.name);
dstChild.transform.SetParent(dst);
dstChild.transform.position = child.position; // copy world position
RemoveScaleAndRotationRecursive(child, dstChild.transform, boneMap);
}
}
}
public static MeshAttachInfo CreateMeshInfo(Transform src, Dictionary<Transform, Transform> boneMap)
{
Transform dst;
if (!boneMap.TryGetValue(src, out dst))
{
return default;
}
// SkinnedMeshRenderer
var smr = src.GetComponent<SkinnedMeshRenderer>();
var mesh = MeshFreezer.NormalizeSkinnedMesh(
smr,
boneMap);
var mesh = MeshFreezer.NormalizeSkinnedMesh(smr);
if (mesh != null)
{
var info = new MeshAttachInfo
return new MeshAttachInfo
{
Mesh = mesh,
Materials = smr.sharedMaterials,
Bones = smr.bones,
RootBone = smr.rootBone,
};
if (smr.rootBone != null)
{
if (boneMap.TryGetValue(smr.rootBone, out Transform found))
{
info.RootBone = found;
}
}
return info;
}
// MeshRenderer
@@ -127,58 +51,68 @@ 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>, Dictionary<Transform, MeshAttachInfo>) NormalizeHierarchyFreezeMesh(
public static Dictionary<Transform, MeshAttachInfo> NormalizeHierarchyFreezeMesh(
GameObject go,
bool removeScaling = true,
bool removeRotation = true
)
{
//
// 正規化されたヒエラルキーを作る
//
var (normalized, boneMap) = CreateNormalizedHierarchy(go, removeScaling, removeRotation);
//
// 各メッシュから回転・スケールを取り除いてBinding行列を再計算する
//
var result = new Dictionary<Transform, MeshAttachInfo>();
foreach (var src in go.transform.Traverse())
{
var info = CreateMeshInfo(src, boneMap);
var info = CreateMeshInfo(src);
if (info != null)
{
result.Add(src, info);
}
}
return (normalized, boneMap, result);
return result;
}
public static void WriteBackResult(GameObject go, GameObject normalized, Dictionary<Transform, Transform> boneMap)
public static void Replace(GameObject go, Dictionary<Transform, MeshAttachInfo> newMesh,
bool FreezeRotation, bool FreezeScaling)
{
Func<Transform, Transform> getSrc = dst =>
var boneMap = go.transform.Traverse().ToDictionary(x => x, x => new EuclideanTransform(x.rotation, x.position));
// Func<Transform, Transform> getSrc = dst =>
// {
// foreach (var (k, v) in boneMap)
// {
// if (v == dst)
// {
// return k;
// }
// }
// throw new NotImplementedException();
// };
foreach (var (src, tr) in boneMap)
{
foreach (var (k, v) in boneMap)
src.position = tr.Translation;
if (FreezeRotation)
{
if (v == dst)
{
return k;
}
src.rotation = Quaternion.identity;
}
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)
else
{
srcR.sharedMesh = dstR.sharedMesh;
srcR.bones = dstR.bones.Select(x => getSrc(x)).ToArray();
src.rotation = tr.Rotation;
}
if (FreezeScaling)
{
src.localScale = Vector3.one;
}
else
{
throw new NotImplementedException();
}
if (newMesh.TryGetValue(src, out var info))
{
info.ReplaceMesh(src.gameObject);
}
}
}
}
}
}

View File

@@ -157,30 +157,17 @@ namespace UniGLTF.MeshUtility
public virtual (List<MeshIntegrationResult>, List<GameObject>) Process(GameObject go)
{
// TODO unpack prefab
// 正規化されたヒエラルキーを作る
if (FreezeBlendShape || FreezeRotation || FreezeScaling)
{
var (normalized, boneMap, newMesh) = BoneNormalizer.NormalizeHierarchyFreezeMesh(go,
// MeshをBakeする
var newMesh = BoneNormalizer.NormalizeHierarchyFreezeMesh(go,
removeScaling: FreezeScaling,
removeRotation: FreezeRotation);
foreach (var (k, v) in newMesh)
{
v.AttachTo(k.gameObject);
}
// write back normalized transform to boneMap
BoneNormalizer.WriteBackResult(go, normalized, boneMap);
if (Application.isPlaying)
{
GameObject.Destroy(normalized);
}
else
{
GameObject.DestroyImmediate(normalized);
}
// - ヒエラルキーから回転・拡縮を除去する
// - BakeされたMeshで置き換える
// - bindPoses を再計算する
BoneNormalizer.Replace(go, newMesh, FreezeRotation, FreezeScaling);
}
var copy = CopyMeshIntegrationGroups();

View File

@@ -9,14 +9,14 @@ namespace UniGLTF.MeshUtility
public Material[] Materials;
public Transform[] Bones;
public Transform RootBone;
public void AttachTo(GameObject dst)
public void ReplaceMesh(GameObject dst)
{
if (Bones != null)
{
// recalc bindposes
Mesh.bindposes = Bones.Select(x => x.worldToLocalMatrix * dst.transform.localToWorldMatrix).ToArray();
var dstRenderer = dst.AddComponent<SkinnedMeshRenderer>();
var dstRenderer = dst.GetComponent<SkinnedMeshRenderer>();
dstRenderer.sharedMesh = Mesh;
dstRenderer.sharedMaterials = Materials;
dstRenderer.bones = Bones;
@@ -24,7 +24,7 @@ namespace UniGLTF.MeshUtility
}
else
{
var dstFilter = dst.AddComponent<MeshFilter>();
var dstFilter = dst.GetComponent<MeshFilter>();
dstFilter.sharedMesh = Mesh;
var dstRenderer = dst.gameObject.AddComponent<MeshRenderer>();
dstRenderer.sharedMaterials = Materials;

View File

@@ -128,9 +128,7 @@ namespace UniGLTF.MeshUtility
/// <param name="boneMap">正規化前のボーンから正規化後のボーンを得る</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static Mesh NormalizeSkinnedMesh(
SkinnedMeshRenderer src,
Dictionary<Transform, Transform> boneMap)
public static Mesh NormalizeSkinnedMesh(SkinnedMeshRenderer src)
{
if (src == null
|| !src.enabled
@@ -144,12 +142,6 @@ namespace UniGLTF.MeshUtility
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)
{
@@ -172,7 +164,6 @@ namespace UniGLTF.MeshUtility
srcMesh.bindposes = new Matrix4x4[] { Matrix4x4.identity };
src.rootBone = src.transform;
dstBones = new[] { boneMap[src.transform] };
src.bones = new[] { src.transform };
src.sharedMesh = srcMesh;
}
@@ -182,8 +173,7 @@ namespace UniGLTF.MeshUtility
mesh.name = srcMesh.name + ".baked";
src.BakeMesh(mesh);
// 新しい骨格のボーンウェイトを作成する
mesh.boneWeights = MapBoneWeight(srcMesh.boneWeights, boneMap, src.bones, dstBones);
mesh.boneWeights = srcMesh.boneWeights;
//var m = src.localToWorldMatrix; // include scaling
var m = default(Matrix4x4);
@@ -195,20 +185,6 @@ namespace UniGLTF.MeshUtility
//
CopyBlendShapes(src, srcMesh, mesh, m);
// var blendShapeBackup = new List<float>();
// if (!FreezeBlendShape)
// {
// for (int i = 0; i < srcMesh.blendShapeCount; ++i)
// {
// blendShapeBackup.Add(src.GetBlendShapeWeight(i));
// src.SetBlendShapeWeight(i, 0);
// }
// }
// for (int i = 0; i < blendShapeBackup.Count; ++i)
// {
// src.SetBlendShapeWeight(i, blendShapeBackup[i]);
// }
if (!hasBoneWeight)
{
// restore bones

View File

@@ -66,37 +66,19 @@ namespace VRM
}
}
// 正規化されたヒエラルキーを作る
var (normalized, bMap, newMesh) = BoneNormalizer.NormalizeHierarchyFreezeMesh(go);
foreach (var (k, v) in newMesh)
{
v.AttachTo(k.gameObject);
}
// Meshの焼きこみ
var newMesh = BoneNormalizer.NormalizeHierarchyFreezeMesh(go);
// 焼いたMeshで置き換える
BoneNormalizer.Replace(go, newMesh, true, true);
// 新しいヒエラルキーからAvatarを作る
var newAvatar = UniHumanoid.AvatarDescription.CreateAvatarForCopyHierarchy(
go.GetComponent<Animator>(), normalized, bMap, avatarDescription =>
{
var vrmHuman = go.GetComponent<VRMHumanoidDescription>();
if (vrmHuman != null && vrmHuman.Description != null)
{
avatarDescription.armStretch = vrmHuman.Description.armStretch;
avatarDescription.legStretch = vrmHuman.Description.legStretch;
avatarDescription.upperArmTwist = vrmHuman.Description.upperArmTwist;
avatarDescription.lowerArmTwist = vrmHuman.Description.lowerArmTwist;
avatarDescription.upperLegTwist = vrmHuman.Description.upperLegTwist;
avatarDescription.lowerLegTwist = vrmHuman.Description.lowerLegTwist;
avatarDescription.feetSpacing = vrmHuman.Description.feetSpacing;
avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF;
}
});
var newAnimator = normalized.GetOrAddComponent<Animator>();
var newAnimator = go.GetComponent<Animator>();
var newAvatar = UniHumanoid.AvatarDescription.RecreateAvatar(newAnimator);
newAnimator.avatar = newAvatar;
CopyVRMComponents(go, normalized, bMap);
// CopyVRMComponents(go, normalized, bMap);
return normalized;
return go;
}
/// <summary>