mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-09-08 20:08:53 -05:00
bake mesh inplace
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user